spb/qwhpi Public
QHPI — Quebec Housing Price Index: quality-adjusted, hierarchically pooled housing price indexes.
Python 63.9%
TypeScript 25.4%
CSS 5.5%
TeX 3.5%
SQL 0.8%
Makefile 0.5%
Dockerfile 0.5%
1/**2 * =============================================================================3 * QWHPI — Quebec Weekly Housing Price Index4 * Author : Simon-Pierre Boucher5 * Contact : contact@spboucher.ai6 * File : web/lib/api.ts7 * Purpose : Typed API client for the QHPI monthly FastAPI service.8 * =============================================================================9 */1011export const API_BASE =12 process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8080";1314export interface Observation {15 period: string;16 index: number | null;17 index_smoothed: number;18 representative_value: number | null;19 transactions: number;20 effective_sample_size: number | null;21 monthly_pct: number | null;22 three_month_pct: number | null;23 six_month_pct: number | null;24 yoy_pct: number | null;25 lower_95: number;26 upper_95: number;27 reliability_grade: string;28 shrinkage_weight: number | null;29 is_partial_month: boolean;30}3132export interface SeriesResponse {33 geography: string;34 geography_name: string;35 geography_level: string;36 property_type: string;37 model_version: string;38 data_vintage: string;39 total_observations: number;40 observations: Observation[];41}4243export interface LatestResponse {44 geography: string;45 geography_name: string;46 property_type: string;47 period: string;48 latest_index: number;49 representative_value: number | null;50 monthly_change: number | null;51 three_month_change: number | null;52 yoy_change: number | null;53 transactions: number;54 reliability: string;55 lower_95: number;56 upper_95: number;57 is_partial_month: boolean;58 data_vintage: string;59}6061export interface GeographyNode {62 geography_id: string;63 geography_name: string;64 geography_level: string;65 property_types: string[];66}6768export interface MapCell {69 geography_id: string;70 geography_name: string;71 value: number | null;72 reliability_grade: string;73 transactions: number;74}7576async function get<T>(path: string): Promise<T> {77 const res = await fetch(`${API_BASE}${path}`, {78 headers: { Accept: "application/json" },79 });80 if (!res.ok) throw new Error(`${res.status} ${res.statusText} — ${path}`);81 return res.json() as Promise<T>;82}8384export const fetchSeries = (g: string, t: string) =>85 get<SeriesResponse>(`/v1/index?geography=${g}&type=${t}`);8687export const fetchLatest = (g: string, t: string) =>88 get<LatestResponse>(`/v1/index/latest?geography=${g}&type=${t}`);8990export const fetchGeographies = () =>91 get<{ published_series: GeographyNode[]; data_vintage: string }>(92 "/v1/geographies",93 );9495export const fetchMap = (metric: string, type: string, period?: string) =>96 get<{ metric: string; period: string; cells: MapCell[] }>(97 `/v1/map?metric=${metric}&level=region&type=${type}` +98 (period ? `&period=${period}` : ""),99 );100101export const csvUrl = (g: string, t: string) =>102 `${API_BASE}/v1/index?geography=${g}&type=${t}&format=csv`;103104export interface SeriesStats {105 geography: string;106 property_type: string;107 period: string;108 index: number;109 representative_value: number | null;110 lower_95: number;111 upper_95: number;112 reliability_grade: string;113 monthly_pct: number | null;114 three_month_pct: number | null;115 six_month_pct: number | null;116 yoy_pct: number | null;117 since_2021_pct: number;118 cagr_pct: number;119 peak_index: number;120 peak_period: string;121 drawdown_pct: number;122 months_since_peak: number;123 at_record_high: boolean;124 volatility_12m_pct: number;125 momentum_3m_ann_pct: number;126 yoy_rank: number | null;127 yoy_rank_of: number;128 volume_12m: number;129 volume_yoy_pct: number | null;130 dollar_volume_12m: number;131 assessment_gap: number | null;132 effective_sample_size: number | null;133 spark: number[];134}135136export interface OverviewRow {137 geography_id: string;138 geography_name: string;139 period: string;140 index: number;141 monthly_pct: number | null;142 three_month_pct: number | null;143 six_month_pct: number | null;144 yoy_pct: number | null;145 since_2021_pct: number;146 drawdown_pct: number;147 at_record_high: boolean;148 volume_12m: number;149 representative_value: number | null;150 reliability_grade: string;151 spark: number[];152}153154export const fetchStats = (g: string, t: string) =>155 get<SeriesStats>(`/v1/stats?geography=${g}&type=${t}`);156157export const fetchOverview = (level: string, t: string) =>158 get<{ period: string; rows: OverviewRow[] }>(159 `/v1/stats/overview?level=${level}&type=${t}`,160 );161162export const reportUrl = (specs: string[]) =>163 `${API_BASE}/v1/report?series=${specs.join(",")}`;164165export const marketReportUrl = (t: string) =>166 `${API_BASE}/v1/report/market?type=${t}`;167