Skip to content

Commit

Permalink
Allow overriding display settings of indicators in mdims (#4460)
Browse files Browse the repository at this point in the history
We now normalize the indicators to a list of objects, which can have an
optional `display` property. See `IndicatorConfig`.
  • Loading branch information
rakyi authored Jan 20, 2025
1 parent dca43cb commit 437e7c6
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 47 deletions.
74 changes: 57 additions & 17 deletions adminSiteServer/multiDim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ import {
DbPlainMultiDimXChartConfig,
DbRawChartConfig,
GrapherInterface,
IndicatorConfig,
IndicatorEntryBeforePreProcessing,
IndicatorsBeforePreProcessing,
MultiDimDataPageConfigEnriched,
MultiDimDataPageConfigPreProcessed,
MultiDimDataPageConfigRaw,
MultiDimDataPagesTableName,
MultiDimDimensionChoices,
MultiDimXChartConfigsTableName,
View,
} from "@ourworldindata/types"
import {
mergeGrapherConfigs,
Expand Down Expand Up @@ -51,19 +54,39 @@ function dimensionsToViewId(dimensions: MultiDimDimensionChoices) {
.toLowerCase()
}

function catalogPathFromIndicatorEntry(
entry: IndicatorEntryBeforePreProcessing
): string | undefined {
if (typeof entry === "string") return entry
if (typeof entry === "object" && "catalogPath" in entry) {
return entry.catalogPath
}
return undefined
}

function getAllCatalogPaths(views: View<IndicatorsBeforePreProcessing>[]) {
const paths = []
for (const view of views) {
const { y, x, size, color } = view.indicators
if (y) {
if (Array.isArray(y)) {
paths.push(...y.map(catalogPathFromIndicatorEntry))
} else {
paths.push(catalogPathFromIndicatorEntry(y))
}
}
for (const entry of [x, size, color]) {
if (entry) paths.push(catalogPathFromIndicatorEntry(entry))
}
}
return paths.filter((path) => path !== undefined)
}

async function resolveMultiDimDataPageCatalogPathsToIndicatorIds(
knex: db.KnexReadonlyTransaction,
rawConfig: MultiDimDataPageConfigRaw
): Promise<MultiDimDataPageConfigPreProcessed> {
const allCatalogPaths = rawConfig.views
.flatMap((view) =>
Object.values(view.indicators).flatMap((indicatorOrIndicators) =>
Array.isArray(indicatorOrIndicators)
? indicatorOrIndicators
: [indicatorOrIndicators]
)
)
.filter((indicator) => typeof indicator === "string")
const allCatalogPaths = getAllCatalogPaths(rawConfig.views)

const catalogPathToIndicatorIdMap = await getVariableIdsByCatalogPath(
allCatalogPaths,
Expand All @@ -84,12 +107,29 @@ async function resolveMultiDimDataPageCatalogPathsToIndicatorIds(
)
}

function resolveSingleField(indicator?: IndicatorEntryBeforePreProcessing) {
if (typeof indicator === "string") {
const indicatorId = catalogPathToIndicatorIdMap.get(indicator)
return indicatorId ?? undefined
function resolveSingleField(
indicator?: IndicatorEntryBeforePreProcessing
): IndicatorConfig | undefined {
switch (typeof indicator) {
case "number":
return { id: indicator }
case "string": {
const id = catalogPathToIndicatorIdMap.get(indicator)
return id ? { id } : undefined
}
case "object": {
if ("id" in indicator) return indicator
if ("catalogPath" in indicator) {
const id = catalogPathToIndicatorIdMap.get(
indicator.catalogPath
)
return id ? { ...indicator, id } : undefined
}
return undefined
}
default:
return undefined
}
return indicator
}

function resolveSingleOrArrayField(
Expand Down Expand Up @@ -202,7 +242,7 @@ export async function createMultiDimConfig(
)
const variableConfigs = await getMergedGrapherConfigsForVariables(
knex,
uniq(config.views.map((view) => view.indicators.y[0]))
uniq(config.views.map((view) => view.indicators.y[0].id))
)
const existingViewIdsToChartConfigIds = await getViewIdToChartConfigIdMap(
knex,
Expand All @@ -215,7 +255,7 @@ export async function createMultiDimConfig(

const enrichedViews = await Promise.all(
config.views.map(async (view) => {
const variableId = view.indicators.y[0]
const variableId = view.indicators.y[0].id
// Main config for each view.
const mainGrapherConfig: GrapherInterface = {
$schema: defaultGrapherConfig.$schema,
Expand Down Expand Up @@ -283,7 +323,7 @@ export async function createMultiDimConfig(
await upsertMultiDimXChartConfigs(knex, {
multiDimId,
viewId: dimensionsToViewId(view.dimensions),
variableId: view.indicators.y[0],
variableId: view.indicators.y[0].id,
chartConfigId: view.fullConfigId,
})
}
Expand Down
2 changes: 1 addition & 1 deletion baker/MultiDimBaker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
const getRelevantVariableIds = (config: MultiDimDataPageConfigPreProcessed) => {
// A "relevant" variable id is the first y indicator of each view
const allIndicatorIds = config.views
.map((view) => view.indicators.y?.[0])
.map((view) => view.indicators.y?.[0]?.id)
.filter((id) => id !== undefined)

return new Set(allIndicatorIds)
Expand Down
2 changes: 2 additions & 0 deletions packages/@ourworldindata/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -729,8 +729,10 @@ export {
} from "./domainTypes/Author.js"

export type {
IndicatorConfig,
IndicatorEntryBeforePreProcessing,
IndicatorsAfterPreProcessing,
IndicatorsBeforePreProcessing,
MultiDimDataPageConfigEnriched,
MultiDimDataPageConfigPreProcessed,
MultiDimDataPageConfigRaw,
Expand Down
20 changes: 17 additions & 3 deletions packages/@ourworldindata/types/src/siteTypes/MultiDimDataPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,23 @@ import {
OwidVariableWithSource,
} from "../OwidVariable.js"

// Indicator ID, catalog path, or maybe an array of those
export type IndicatorEntryBeforePreProcessing = string | number
export type IndicatorEntryAfterPreProcessing = number // catalog paths have been resolved to indicator IDs
export type IndicatorConfig = Pick<OwidVariableWithSource, "id" | "display">
type IndicatorConfigByPath = Pick<IndicatorConfig, "display"> & {
catalogPath: string
}

type IndicatorConfigBeforePreProcessing =
| IndicatorConfig
| IndicatorConfigByPath

// Indicator ID, catalog path or a an object
export type IndicatorEntryBeforePreProcessing =
| string
| number
| IndicatorConfigBeforePreProcessing

// Catalog paths have been resolved to indicator IDs
export type IndicatorEntryAfterPreProcessing = IndicatorConfig

type Metadata = Omit<OwidVariableWithSource, "id">

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const CONFIG: MultiDimDataPageConfigEnriched = {
interval: "yearly",
},
indicators: {
y: [111, 222],
y: [{ id: 111 }, { id: 222 }],
},
fullConfigId: uuidv7(),
},
Expand All @@ -61,7 +61,7 @@ const CONFIG: MultiDimDataPageConfigEnriched = {
interval: "yearly",
},
indicators: {
y: [819727],
y: [{ id: 819727 }],
},
fullConfigId: uuidv7(),
},
Expand Down
52 changes: 33 additions & 19 deletions packages/@ourworldindata/utils/src/MultiDimDataPageConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,27 +156,41 @@ export class MultiDimDataPageConfig {
}

static viewToDimensionsConfig(
view: View<IndicatorsAfterPreProcessing> | undefined
view?: View<IndicatorsAfterPreProcessing>
): OwidChartDimensionInterface[] {
if (!view?.indicators) return []

return Object.entries(view.indicators)
.flatMap(([property, variableIdOrIds]) => {
if (Array.isArray(variableIdOrIds)) {
return variableIdOrIds.map((variableId) => ({
property: property as DimensionProperty,
variableId,
}))
} else {
return [
{
property: property as DimensionProperty,
variableId: variableIdOrIds,
},
]
}
const dimensions: OwidChartDimensionInterface[] = []
if (!view?.indicators) return dimensions
if (view.indicators.y) {
dimensions.push(
...view.indicators.y.map(({ id, display }) => ({
property: DimensionProperty.y,
variableId: id,
display,
}))
)
}
if (view.indicators.x) {
dimensions.push({
property: DimensionProperty.x,
variableId: view.indicators.x.id,
display: view.indicators.x.display,
})
}
if (view.indicators.size) {
dimensions.push({
property: DimensionProperty.size,
variableId: view.indicators.size.id,
display: view.indicators.size.display,
})
.filter((dim) => dim.variableId !== undefined)
}
if (view.indicators.color) {
dimensions.push({
property: DimensionProperty.color,
variableId: view.indicators.color.id,
display: view.indicators.color.display,
})
}
return dimensions
}
}

Expand Down
7 changes: 2 additions & 5 deletions site/multiDim/MultiDimDataPageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,7 @@ const useVarDatapageData = (
setGrapherConfigIsReady(false)
setGrapherConfig(null)
setVarDatapageData(null)
const yIndicatorOrIndicators = currentView?.indicators?.["y"]
const variableId = Array.isArray(yIndicatorOrIndicators)
? yIndicatorOrIndicators[0]
: yIndicatorOrIndicators
const variableId = currentView?.indicators?.["y"]?.[0]?.id
if (!variableId) return

const datapageDataPromise = cachedGetVariableMetadata(variableId).then(
Expand Down Expand Up @@ -297,7 +294,7 @@ export const MultiDimDataPageContent = ({
const variables = currentView?.indicators?.["y"]
const editUrl =
variables?.length === 1
? `variables/${variables[0]}/config`
? `variables/${variables[0].id}/config`
: undefined
return {
...varGrapherConfig,
Expand Down

0 comments on commit 437e7c6

Please sign in to comment.