spb/countryatlas
Public
TypeScript 57%
Python 38.6%
JavaScript 3.6%
CSS 0.6%
1/**2 * TypeScript mirror of the analytics endpoints (docs/API.md § "Analytics endpoints (API 1.1)"). The Python side3 * implements exactly these shapes; keep the two in sync. Base cards live in `./types`.4 */5import type { CountryCard, GroupCard, IndicatorCard, IndicatorSummary, Meta, Provenance } from './types';67export type Direction = 'up' | 'down';8export type Interpretation = 'improvement' | 'deterioration' | null;9export type DirectionSemantics = 'higher_is_better' | 'lower_is_better' | 'neutral';10export type MoverWindow = 1 | 5 | 10;11export type MoverCategory = 'all' | 'economic' | 'demographic' | 'health' | 'energy' | 'climate' | 'digital' | 'housing' | 'labor';12export type MoverKindFilter = 'all' | 'improvement' | 'deterioration' | 'increase' | 'decrease' | 'record' | 'reversal' | 'acceleration' | 'structural';13export type ExtremesWindow = '1' | '5' | '10' | '25' | 'since1990';14export type QualityBadge = 'fresh' | 'historical' | 'sparse' | 'limited-coverage' | 'stale' | 'flagged' | 'forecast';1516export interface PointCountry {17 id: string;18 slug: string | null;19 name: string | null;20 flag: string | null;21 region: string | null;22 income: string | null;23}2425// ---------------------------------------------------------------------------------------------- pulse2627export interface MoverLite {28 country: CountryCard;29 value: number | null;30 ref_value: number | null;31 delta: number | null;32 delta_pct: number | null;33 formatted: string | null;34 year: number | null;35}3637export interface Convergence {38 direction: 'converging' | 'diverging' | 'stable';39 cv_start: number;40 cv_end: number;41 n: number;42 from_year: number;43}4445export interface PulseItem {46 indicator: IndicatorCard;47 year: number;48 n: number;49 n_up: number;50 n_down: number;51 n_flat: number;52 share_up: number;53 share_down: number;54 median_change_abs: number | null;55 median_change_pct: number | null;56 direction_semantics: DirectionSemantics;57 record_highs: number;58 record_lows: number;59 headline: string;60 top_up: MoverLite | null;61 top_down: MoverLite | null;62 convergence: Convergence | null;63 provenance: Provenance | null;64}6566export interface PulseResponse {67 meta: Meta;68 year_reference: number;69 summary: { n_indicators: number; n_countries_reporting: number; n_record_highs: number; n_record_lows: number; n_changes: number };70 items: PulseItem[];71}7273// ---------------------------------------------------------------------------------------------- movers / extremes7475export interface MoverItem {76 country: CountryCard;77 indicator: IndicatorCard;78 kind: string;79 year: number | null;80 ref_year: number | null;81 value: number | null;82 ref_value: number | null;83 delta: number | null;84 delta_pct: number | null;85 formatted: string | null;86 formatted_ref: string | null;87 severity: number | null;88 direction: Direction;89 interpretation: Interpretation;90 headline: string | null;91 provenance: Provenance | null;92}9394export interface MoversResponse {95 meta: Meta;96 window: MoverWindow;97 category: MoverCategory;98 kind: MoverKindFilter;99 min_population: number | null;100 filter_note: string | null;101 categories: MoverCategory[];102 kinds: MoverKindFilter[];103 n: number;104 items: MoverItem[];105}106107export interface ExtremeRow {108 country: CountryCard;109 value_start: number | null;110 value_end: number | null;111 year_start: number | null;112 year_end: number | null;113 delta: number | null;114 delta_pct: number | null;115 formatted_start: string | null;116 formatted_end: string | null;117}118119export interface ExtremeFacet {120 id: string;121 title: string;122 indicator: IndicatorCard;123 direction: Direction;124 metric: 'abs' | 'pct' | 'points';125 rows: ExtremeRow[];126 n: number;127 provenance: Provenance | null;128}129130export interface ExtremesResponse {131 meta: Meta;132 window: ExtremesWindow | string;133 from_year: number | null;134 to_year: number | null;135 min_population: number | null;136 filter_note: string | null;137 facets: ExtremeFacet[];138}139140// ---------------------------------------------------------------------------------------------- scatter / trajectory / peers141142export interface ScatterPointRow extends PointCountry {143 x: number | null;144 y: number | null;145 size: number | null;146 year_x: number | null;147 year_y: number | null;148}149150export interface ScatterStats {151 n: number;152 pearson: number | null;153 spearman: number | null;154 ols: { slope: number; intercept: number; r2: number | null } | null;155 log_x: boolean;156 log_y: boolean;157}158159export interface ScatterResponse {160 meta: Meta;161 x: IndicatorCard;162 y: IndicatorCard;163 size: IndicatorCard | null;164 year: number | null;165 year_used: number | null;166 nearest_years: number;167 group: GroupCard;168 n: number;169 points: ScatterPointRow[];170 stats: ScatterStats;171 note: string;172 provenance?: Provenance[];173}174175export interface TrajectoryResponse {176 meta: Meta;177 x: IndicatorCard;178 y: IndicatorCard;179 size: IndicatorCard | null;180 group: GroupCard;181 years: number[];182 countries: PointCountry[];183 series: Record<string, { x: Array<number | null>; y: Array<number | null>; size: Array<number | null> }>;184 domains: { x: [number, number]; y: [number, number]; size: [number, number] | null };185 log_x: boolean;186 log_y: boolean;187 provenance: Provenance[];188}189190export interface PeerPoint extends PointCountry {191 x: number | null;192 y: number | null;193 expected: number | null;194 residual: number | null;195 residual_z: number | null;196}197198export interface PeersResponse {199 meta: Meta;200 x: IndicatorCard;201 y: IndicatorCard;202 year_used: number | null;203 n: number;204 method: 'theil-sen' | 'ols' | string;205 fit: { slope: number; intercept: number; r2: number | null; log_x: boolean; residual_scale: number | null } | null;206 points: PeerPoint[];207 above: PeerPoint[];208 below: PeerPoint[];209 pairs: Array<{ x: string; y: string; label: string }>;210 note: string;211 methodology: string;212}213214// ---------------------------------------------------------------------------------------------- finder215216export type FinderOp = 'gt' | 'gte' | 'lt' | 'lte' | 'eq' | 'between';217218export interface FinderFilter {219 indicator: IndicatorCard;220 op: FinderOp | string;221 value: number;222 value2: number | null;223 year_used: number | null;224}225226export interface FinderItem {227 country: CountryCard;228 matched: string[];229 values: Record<string, { value: number | null; year: number | null; formatted: string | null; provenance: Provenance | null }>;230}231232export interface FinderResponse {233 meta: Meta;234 mode: 'and' | 'or';235 filters: FinderFilter[];236 n_matching: number;237 n_evaluated: number;238 items: FinderItem[];239}240241// ---------------------------------------------------------------------------------------------- indicator analytics242243export interface RelatedItem {244 indicator: IndicatorCard;245 pearson: number | null;246 spearman: number | null;247 n: number;248 year: number | null;249 log_x: boolean;250 log_y: boolean;251 direction: 'positive' | 'negative';252}253254export interface RelatedResponse {255 meta: Meta;256 indicator: IndicatorCard;257 year_used: number | null;258 n_candidates: number;259 items: RelatedItem[];260 note: string;261}262263export interface Histogram {264 edges: number[];265 counts: number[];266 log: boolean;267}268269export interface DistributionResponse {270 meta: Meta;271 indicator: IndicatorCard;272 year: number | null;273 year_used: number | null;274 n: number;275 log: boolean;276 histogram: Histogram;277 stats: { min: number | null; p10: number | null; p25: number | null; median: number | null; mean: number | null; p75: number | null; p90: number | null; max: number | null };278 highlight: { country: CountryCard; value: number | null; percentile: number | null; rank: number | null; n: number | null; region: GroupCard | null; region_median: number | null; income: GroupCard | null; income_median: number | null } | null;279 by_region: Array<{ group: GroupCard; median: number | null; n: number }>;280 by_income: Array<{ group: GroupCard; median: number | null; n: number }>;281 provenance: Provenance | null;282}283284export interface FramesResponse {285 meta: Meta;286 indicator: IndicatorCard;287 group: GroupCard;288 years: number[];289 values: Record<string, Array<number | null>>;290 legend: { min: number | null; max: number | null; breaks: number[]; n_classes: number };291 n_by_year: number[];292 provenance: Provenance | null;293}294295export interface IndicatorQualityResponse {296 meta: Meta;297 indicator: IndicatorCard;298 n_countries: number;299 n_countries_total: number;300 coverage_pct: number;301 first_year: number | null;302 last_year: number | null;303 latest_common_year: number | null;304 n_years: number;305 years_with_50plus: number;306 median_points_per_country: number | null;307 sparse_countries: number;308 stale_countries: number;309 flagged_values: number;310 sources: Provenance[];311 badges: QualityBadge[];312}313314// ---------------------------------------------------------------------------------------------- race / regions compare315316export interface RaceResponse {317 meta: Meta;318 indicator: IndicatorCard;319 group: GroupCard;320 top: number;321 years: number[];322 frames: Array<{ year: number; rows: Array<{ id: string; value: number; rank: number }> }>;323 countries: Record<string, CountryCard>;324 max_value: number | null;325 provenance: Provenance | null;326}327328export interface RegionCompareResponse {329 meta: Meta;330 groups: [GroupCard, GroupCard];331 rows: Array<{ indicator: IndicatorCard; kind: 'sum' | 'median' | 'weighted_mean' | string; label: string; values: Record<string, { value: number | null; formatted: string | null; n: number | null; year: number | null }> }>;332 shares: Record<string, { population_share_pct: number | null; gdp_share_pct: number | null }>;333 history: Record<string, { years: number[] } & Record<string, Array<number | null> | number[]>>;334}335336// ---------------------------------------------------------------------------------------------- country analytics337338export interface StoryItem {339 indicator: IndicatorCard;340 first: { year: number; value: number; formatted: string | null };341 last: { year: number; value: number; formatted: string | null };342 change_abs: number | null;343 change_pct: number | null;344 cagr: number | null;345 peak: { year: number; value: number } | null;346 trough: { year: number; value: number } | null;347 rank_first: { rank: number; n: number; year: number } | null;348 rank_last: { rank: number; n: number; year: number } | null;349 series: Array<[number, number | null]>;350 text: string;351 provenance: Provenance | null;352}353354export interface StoryResponse {355 meta: Meta;356 country: CountryCard;357 since: number | null;358 items: StoryItem[];359}360361export interface DnaReference {362 kind: 'world' | 'region' | 'income' | 'country' | string;363 id: string | null;364 label: string;365 dims: Record<string, number | null>;366}367368export interface CountryQualityItem {369 indicator: IndicatorCard;370 latest_year: number | null;371 first_year: number | null;372 n_points: number;373 expected_points: number;374 missing_years: number;375 continuity_pct: number | null;376 status: string | null;377 source: string | null;378 source_updated_at: string | null;379 retrieved_at: string | null;380 badges: QualityBadge[];381}382383export interface CountryQualityResponse {384 meta: Meta;385 country: CountryCard;386 summary: { n_indicators: number; n_with_data: number; coverage_pct: number | null; latest_year: number | null; n_fresh: number; n_stale: number; n_sparse: number; n_flagged: number };387 items: CountryQualityItem[];388}389390// ---------------------------------------------------------------------------------------------- updates391392export interface UpdatesSource {393 source: { id: string; name: string | null; organization: string | null; url: string | null; licence: string | null };394 status: 'ok' | 'partial' | 'failed' | 'stale' | 'unknown' | string;395 last_success_at: string | null;396 last_retrieved_at: string | null;397 source_updated_at: string | null;398 n_datasets: number;399 n_indicators: number;400 n_observations: number;401 latest_year: number | null;402 values_changed: number;403 countries_affected: number;404}405406export interface UpdatesResponse {407 meta: Meta;408 snapshot: { run_id: string | null; built_at: string | null; observations: number; indicators: number; countries: number; values_changed: number; values_changed_by_source: Record<string, number> };409 sources: UpdatesSource[];410 recent_runs: Array<{ run_id: string | null; connector: string; dataset: string | null; started_at: string | null; finished_at: string | null; status: string | null; rows_valid: number | null; warnings: number | null; errors: number | null; message: string | null }>;411 indicators_recently_updated: IndicatorSummary[];412}413414/** Search hit for a parsed intent ("compare canada usa", "rank gdp"). */415export interface ActionHit {416 type: 'action';417 action: 'compare' | 'ranking' | 'group_ranking' | 'explore' | string;418 id: string;419 name: string;420 hint: string | null;421 url: string;422 score: number;423}424