stats: /stats/countries comparison and /stats/countries/[iso3] profiles (Curaçao via World Bank), more WB indicators, Countries + Curaçao cards, nav/sitemap/start links
All checks were successful
Deploy production / deploy (push) Successful in 1m13s

This commit is contained in:
2026-10-25 21:22:20 +00:00
parent d19313035f
commit 2a4466cb82
12 changed files with 887 additions and 5 deletions

View File

@@ -25,6 +25,8 @@ export type StatSeries = {
region: string | null;
sourceId: string;
points: StatPoint[];
/** First forecast year (IMF WEO vintages); points from that year on are projections. */
forecastFrom?: number;
};
export type SeriesRequest = {
id: string;
@@ -48,8 +50,10 @@ export async function loadSeries(
freq: string;
region_code: string | null;
source_id: string;
forecast_from: string | null;
}[]
>`select series_id, title, unit, freq, region_code, source_id from stats.series
>`select series_id, title, unit, freq, region_code, source_id, meta->>'forecastFrom' as forecast_from
from stats.series
where series_id = any(${reqs.map((r) => r.id)})`;
const out: Record<string, StatSeries> = {};
await Promise.all(
@@ -72,6 +76,7 @@ export async function loadSeries(
freq: m.freq,
region: m.region_code,
sourceId: m.source_id,
...(m.forecast_from ? { forecastFrom: Number(m.forecast_from) } : {}),
points: rows.map((r) => [r.period, Math.round(r.value * 1e6) / 1e6])
};
})
@@ -181,3 +186,15 @@ export async function loadCbsCross(
}
return [...byCode.values()].sort((a, b) => a.code.localeCompare(b.code));
}
export type LatestValue = { period: string; value: number };
/** Latest observed (non-forecast) value per series; missing ids are absent. */
export async function loadLatest(ids: string[]): Promise<Record<string, LatestValue>> {
if (!ids.length) return {};
const rows = await client<{ series_id: string; period: string; value: number }[]>`
select distinct on (series_id) series_id, to_char(period, 'YYYY-MM-DD') as period, value
from stats.obs
where series_id = any(${ids}) and value is not null and status is distinct from 'forecast'
order by series_id, period desc`;
return Object.fromEntries(rows.map((r) => [r.series_id, { period: r.period, value: r.value }]));
}