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%
12.6 KB · 424 lines typescript
Raw Blame History
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