SPB Git forge

spb/countryatlas

Public
20commits 1branches 0releases
268.3 MBsize
maindefault branch
12 days agolast push
TypeScript 57% Python 38.6% JavaScript 3.6% CSS 0.6%
4.8 KB · 134 lines typescript
Raw Blame History
1/**2 * TypeScript mirror of the compare / rankings / regions / indicators response models in3 * `src/countryatlas/api/schemas.py` (CompareResponse, CompareSnapshotResponse, RankingsListResponse,4 * RankHistoryResponse, IndicatorsResponse, RegionsResponse). Kept in its own file so the compare / rankings5 * pages and the indicators / regions pages can be built concurrently without touching `types.ts`.6 */7import type { CountryCard, IndicatorCard, IndicatorSummary, Meta, MetricValue, Series, TopicId } from './types';89// ---------------------------------------------------------------------------------------------- compare1011/** `mode` query of `/compare` (docs/API.md § Compare). */12export type CompareMode = 'absolute' | 'per-capita' | 'index100' | 'pct';13export const COMPARE_MODES: readonly CompareMode[] = ['absolute', 'per-capita', 'index100', 'pct'];14/** UI-only modes computed client-side: `percentile` (world percentile from rank/n) and `change` (value − value at the range start). */15export type CompareUiMode = CompareMode | 'percentile' | 'change';16export const COMPARE_UI_MODES: readonly CompareUiMode[] = ['absolute', 'per-capita', 'index100', 'pct', 'percentile', 'change'];17/** Map a UI mode to the API mode (client-side modes fetch the absolute series). */18export function apiModeOf(mode: CompareUiMode): CompareMode {19  return mode === 'percentile' || mode === 'change' ? 'absolute' : mode;20}2122/** Transformation echo added by the router on every series (`extra="allow"`). */23export interface CompareTransform {24  mode: CompareMode | string;25  base_year?: number | null;26  applied: boolean;27  unit?: string | null;28}2930export interface CompareSeries extends Series {31  transform?: CompareTransform;32}3334export interface CompareResponse {35  meta: Meta;36  mode: CompareMode | string;37  base_year: number | null;38  countries: CountryCard[];39  indicators: IndicatorCard[];40  series: CompareSeries[];41}4243/** One row of `/compare/snapshot`: the indicator, its latest value per country (ISO3 → MetricValue), the best ISO3. */44export interface CompareSnapshotRow {45  indicator: IndicatorCard;46  values: Record<string, MetricValue>;47  /** ISO3 of the best value when `higher_is_better` is known, else null. */48  best: string | null;49}5051export interface CompareSnapshotResponse {52  meta: Meta;53  topic: { id: TopicId | string; name: string; short: string | null; blurb: string | null } | null;54  countries: CountryCard[];55  rows: CompareSnapshotRow[];56}5758// ---------------------------------------------------------------------------------------------- rankings5960/** `/rankings` item — an IndicatorSummary enriched with the latest ranking year and its size. */61export interface RankableIndicator extends IndicatorSummary {62  ranking_year: number | null;63  ranking_n: number | null;64}6566export interface RankingsListResponse {67  meta: Meta;68  n: number;69  items: RankableIndicator[];70}7172export interface RankHistoryPoint {73  year: number;74  rank: number | null;75  n: number | null;76  value: number | null;77  pct_rank: number | null;78}7980export interface RankHistoryResponse {81  meta: Meta;82  indicator: IndicatorCard;83  countries: CountryCard[];84  years: number[];85  /** ISO3 → points (one per year with a rank). */86  series: Record<string, RankHistoryPoint[]>;87}8889// ---------------------------------------------------------------------------------------------- indicators list / regions list9091export interface IndicatorsResponse {92  meta: Meta;93  n: number;94  filters: { topic: string | null; q: string | null; featured: boolean | null; source: string | null };95  items: IndicatorSummary[];96}9798/** `/regions` item (schemas.RegionsResponse.items is `dict[str, Any]`; these are the keys the router emits). */99export interface RegionItem {100  id: string;101  slug: string;102  name: string;103  kind: 'world' | 'region' | 'continent' | 'income' | 'org' | string;104  wb_code: string | null;105  n_members: number | null;106  description: string | null;107  population_latest: number | null;108  gdp_latest: number | null;109}110111export interface RegionsResponse {112  meta: Meta;113  n: number;114  items: RegionItem[];115}116117// ---------------------------------------------------------------------------------------------- UI-side helpers118119/** Minimal country reference passed to client components (kept small: the full CountrySummary list is ~60 kB). */120export interface CountryLite {121  id: string;122  slug: string;123  name: string;124  flag: string | null;125  region: string | null;126  income: string | null;127  /** Optional: latest population (rankings min-population filter). */128  population?: number | null;129}130131export function toCountryLite(c: CountryCard & { population_latest?: number | null }): CountryLite {132  return { id: c.id, slug: c.slug ?? c.id.toLowerCase(), name: c.name ?? c.id, flag: c.flag, region: c.region, income: c.income, population: c.population_latest ?? null };133}134