<commit_before>import {
    QueryParams,
    queryParamsToStr,
    strToQueryParams,
} from "../clientUtils/url"
import { Url } from "../urls/Url"
import { omit, omitUndefinedValues } from "../clientUtils/Util"
import { DefaultPatchGrammar, Patch } from "../patch/Patch"
import { EXPLORERS_ROUTE_FOLDER } from "./ExplorerConstants"
import { legacyToCurrentGrapherUrl } from "../grapher/core/GrapherUrlMigrations"

const legacyIntervalToNewValue = {
    daily: "New per day",
    weekly: "Weekly",
    total: "Cumulative",
    smoothed: "7-day rolling average",
    biweekly: "Biweekly",
    weeklyChange: "Weekly change",
    biweeklyChange: "Biweekly change",
}

const metricFromLegacyQueryParams = (queryParams: QueryParams) => {
    if (queryParams.casesMetric) {
        return "Confirmed cases"
    } else if (queryParams.deathsMetric) {
        return "Confirmed deaths"
    } else if (queryParams.cfrMetric) {
        return "Case fatality rate"
    } else if (queryParams.testsMetric) {
        return "Tests"
    } else if (queryParams.testsPerCaseMetric) {
        return "Tests per confirmed case"
    } else if (queryParams.positiveTestRate) {
        return "Share of positive tests"
    } else if (queryParams.vaccinationsMetric) {
        return "Vaccinations"
    }
    return undefined
}

const intervalFromLegacyQueryParams = (queryParams: QueryParams) => {
    let legacyInterval: string | undefined = undefined

    // Early on, the query string was a few booleans like dailyFreq=true.
    // Now it is a single 'interval'. This transformation is for backward compat.
    if (queryParams.interval) {
        legacyInterval = queryParams.interval
    } else if (queryParams.totalFreq) {
        legacyInterval = "total"
    } else if (queryParams.dailyFreq) {
        legacyInterval = "daily"
    } else if (queryParams.smoothing) {
        legacyInterval = "smoothed"
    }

    if (legacyInterval) {
        return legacyIntervalToNewValue[
            legacyInterval as keyof typeof legacyIntervalToNewValue
        ]
    }

    return undefined
}

const legacyToCurrentCovidQueryParams = (
    queryStr: string,
    baseQueryStr?: string
): QueryParams => {
    const queryParams = strToQueryParams(queryStr)
    const baseQueryParams = strToQueryParams(baseQueryStr)

    const { aligned, perCapita, selection, ...restQueryParams } = omit(
        {
            ...baseQueryParams,
            ...queryParams,
        },
        "casesMetric",
        "deathsMetric",
        "cfrMetric",
        "testsMetric",
        "testsPerCaseMetric",
        "positiveTestRate",
        "vaccinationsMetric",
        "interval",
        "smoothing",
        "totalFreq",
        "dailyFreq"
    ) as QueryParams

    const explorerQueryParams: QueryParams = {
        "Metric Dropdown":
            metricFromLegacyQueryParams(queryParams) ??
            metricFromLegacyQueryParams(baseQueryParams),
        "Interval Dropdown":
            intervalFromLegacyQueryParams(queryParams) ??
            intervalFromLegacyQueryParams(baseQueryParams),
        "Align outbreaks Checkbox": aligned ? "true" : "false",
        "Relative to Population Checkbox": perCapita ? "true" : "false",
    }

    const patch = new Patch(
        omitUndefinedValues({
            ...restQueryParams,
            ...explorerQueryParams,
            // If we don't encode it as an array,
            // Patch will escape the column delimiter.
            selection: selection?.split(DefaultPatchGrammar.columnDelimiter),
        })
    )

    return {
        patch: patch.uriEncodedString,
    }
}

export enum ExplorerUrlMigrationId {
    legacyToGridCovidExplorer = "legacyToGridCovidExplorer",
}

interface ExplorerUrlMigrationSpec {
    explorerSlug: string
    migrateUrl: (url: Url, baseQueryStr: string) => Url
}

export const explorerUrlMigrationsById: Record<
    ExplorerUrlMigrationId,
    ExplorerUrlMigrationSpec
> = {
    legacyToGridCovidExplorer: {
        explorerSlug: "coronavirus-data-explorer",
        migrateUrl: (url, baseQueryStr) => {
            url = legacyToCurrentGrapherUrl(url)
            let baseUrl = legacyToCurrentGrapherUrl(
                Url.fromQueryStr(baseQueryStr)
            )
            url = url.update({
                pathname: `/${EXPLORERS_ROUTE_FOLDER}/coronavirus-data-explorer`,
                queryStr: queryParamsToStr(
                    legacyToCurrentCovidQueryParams(
                        url.queryStr,
                        baseUrl.queryStr
                    )
                ),
            })
            return url
        },
    },
}

/**
 * An object spec that gets encoded into pages that redirect to an explorer.
 *
 * It's encoded as a JSON object on the page in order to avoid storing all redirects
 * in the client-side bundle.
 */
export interface ExplorerPageUrlMigrationSpec {
    explorerUrlMigrationId: ExplorerUrlMigrationId
    baseQueryStr: string
}
<commit_msg>Parse selection param correctly in LegacyCovidExplorer redirects<commit_after>import {
    QueryParams,
    queryParamsToStr,
    strToQueryParams,
} from "../clientUtils/url"
import { Url } from "../urls/Url"
import { omit, omitUndefinedValues } from "../clientUtils/Util"
import { DefaultPatchGrammar, Patch } from "../patch/Patch"
import { EXPLORERS_ROUTE_FOLDER } from "./ExplorerConstants"
import { legacyToCurrentGrapherUrl } from "../grapher/core/GrapherUrlMigrations"
import { EntityUrlBuilder } from "../grapher/core/EntityUrlBuilder"

const legacyIntervalToNewValue = {
    daily: "New per day",
    weekly: "Weekly",
    total: "Cumulative",
    smoothed: "7-day rolling average",
    biweekly: "Biweekly",
    weeklyChange: "Weekly change",
    biweeklyChange: "Biweekly change",
}

const metricFromLegacyQueryParams = (queryParams: QueryParams) => {
    if (queryParams.casesMetric) {
        return "Confirmed cases"
    } else if (queryParams.deathsMetric) {
        return "Confirmed deaths"
    } else if (queryParams.cfrMetric) {
        return "Case fatality rate"
    } else if (queryParams.testsMetric) {
        return "Tests"
    } else if (queryParams.testsPerCaseMetric) {
        return "Tests per confirmed case"
    } else if (queryParams.positiveTestRate) {
        return "Share of positive tests"
    } else if (queryParams.vaccinationsMetric) {
        return "Vaccinations"
    }
    return undefined
}

const intervalFromLegacyQueryParams = (queryParams: QueryParams) => {
    let legacyInterval: string | undefined = undefined

    // Early on, the query string was a few booleans like dailyFreq=true.
    // Now it is a single 'interval'. This transformation is for backward compat.
    if (queryParams.interval) {
        legacyInterval = queryParams.interval
    } else if (queryParams.totalFreq) {
        legacyInterval = "total"
    } else if (queryParams.dailyFreq) {
        legacyInterval = "daily"
    } else if (queryParams.smoothing) {
        legacyInterval = "smoothed"
    }

    if (legacyInterval) {
        return legacyIntervalToNewValue[
            legacyInterval as keyof typeof legacyIntervalToNewValue
        ]
    }

    return undefined
}

const legacyToCurrentCovidQueryParams = (
    queryStr: string,
    baseQueryStr?: string
): QueryParams => {
    const queryParams = strToQueryParams(queryStr)
    const baseQueryParams = strToQueryParams(baseQueryStr)

    const { aligned, perCapita, selection, ...restQueryParams } = omit(
        {
            ...baseQueryParams,
            ...queryParams,
        },
        "casesMetric",
        "deathsMetric",
        "cfrMetric",
        "testsMetric",
        "testsPerCaseMetric",
        "positiveTestRate",
        "vaccinationsMetric",
        "interval",
        "smoothing",
        "totalFreq",
        "dailyFreq"
    ) as QueryParams

    const explorerQueryParams: QueryParams = {
        "Metric Dropdown":
            metricFromLegacyQueryParams(queryParams) ??
            metricFromLegacyQueryParams(baseQueryParams),
        "Interval Dropdown":
            intervalFromLegacyQueryParams(queryParams) ??
            intervalFromLegacyQueryParams(baseQueryParams),
        "Align outbreaks Checkbox": aligned ? "true" : "false",
        "Relative to Population Checkbox": perCapita ? "true" : "false",
    }

    const patch = new Patch(
        omitUndefinedValues({
            ...restQueryParams,
            ...explorerQueryParams,
            // If we don't encode it as an array,
            // Patch will escape the column delimiter.
            selection: selection
                ? EntityUrlBuilder.queryParamToEntityNames(selection)
                : undefined,
        })
    )

    return {
        patch: patch.uriEncodedString,
    }
}

export enum ExplorerUrlMigrationId {
    legacyToGridCovidExplorer = "legacyToGridCovidExplorer",
}

interface ExplorerUrlMigrationSpec {
    explorerSlug: string
    migrateUrl: (url: Url, baseQueryStr: string) => Url
}

export const explorerUrlMigrationsById: Record<
    ExplorerUrlMigrationId,
    ExplorerUrlMigrationSpec
> = {
    legacyToGridCovidExplorer: {
        explorerSlug: "coronavirus-data-explorer",
        migrateUrl: (url, baseQueryStr) => {
            url = legacyToCurrentGrapherUrl(url)
            let baseUrl = legacyToCurrentGrapherUrl(
                Url.fromQueryStr(baseQueryStr)
            )
            url = url.update({
                pathname: `/${EXPLORERS_ROUTE_FOLDER}/coronavirus-data-explorer`,
                queryStr: queryParamsToStr(
                    legacyToCurrentCovidQueryParams(
                        url.queryStr,
                        baseUrl.queryStr
                    )
                ),
            })
            return url
        },
    },
}

/**
 * An object spec that gets encoded into pages that redirect to an explorer.
 *
 * It's encoded as a JSON object on the page in order to avoid storing all redirects
 * in the client-side bundle.
 */
export interface ExplorerPageUrlMigrationSpec {
    explorerUrlMigrationId: ExplorerUrlMigrationId
    baseQueryStr: string
}

{'fraction_non_alphanumeric': 0.08982630272952853, 'fraction_numerical': 0.00019851116625310174, 'mean_word_length': 2.514475061039414, 'pattern_counts': {'":': 8, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2955637', 'n_tokens_mistral': 2865, 'n_tokens_neox': 2807, 'n_words': 672}