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# QWHPI — Quebec Weekly Housing Price Index3# Author : Simon-Pierre Boucher4# Contact : contact@spboucher.ai5# File : api/app/deps.py6# Purpose : Shared dependencies — series validation, ETag and CSV helpers.7# =============================================================================8"""Shared FastAPI dependencies and response helpers."""910from __future__ import annotations1112import hashlib1314import polars as pl15from fastapi import HTTPException, Request, Response1617from app.services import data1819VALID_TYPES = {"all", "unifamilial", "condo", "plex"}202122def validate_series(geography: str, property_type: str) -> None:23 if property_type not in VALID_TYPES:24 raise HTTPException(422, f"property_type must be one of {sorted(VALID_TYPES)}")25 if geography not in data.geography_ids():26 raise HTTPException(404, f"unknown geography '{geography}' — see /v1/geographies")272829def etag_for(request: Request, response: Response, *parts: str) -> bool:30 """Set the ETag; return True when the client's copy is fresh (304)."""31 raw = "|".join([data.data_vintage(), data.model_version(),32 str(request.url), *parts])33 tag = '"' + hashlib.sha256(raw.encode()).hexdigest()[:32] + '"'34 response.headers["ETag"] = tag35 response.headers["Cache-Control"] = "public, max-age=300"36 return request.headers.get("if-none-match") == tag373839def csv_response(df: pl.DataFrame) -> Response:40 return Response(41 content=df.write_csv(),42 media_type="text/csv",43 headers={"X-Author": "Simon-Pierre Boucher <contact@spboucher.ai>"},44 )45