"""Project-wide constants and paths (UQO WP36).""" from __future__ import annotations import os from pathlib import Path ROOT = Path(os.environ.get("UCML_ROOT", Path(__file__).resolve().parents[2])) DATA = ROOT / "data" RAW = DATA / "raw" INTERIM = DATA / "interim" PROCESSED = DATA / "processed" SYNTHETIC = DATA / "synthetic" RESULTS = ROOT / "results" FIGURES = ROOT / "figures" TABLES = ROOT / "paper" / "tables" SNAPSHOT = Path(os.environ.get("UCML_SNAPSHOT", RAW / "autoka-snapshot-20261101.db")) SNAPSHOT_DATE = "2026-11-01" # last sync in the snapshot (UTC) — used as the right-censoring point def _manifest_ts() -> tuple[float, float]: """(first sync ts, last sync ts) from data/interim/manifest.json when available.""" import json m = INTERIM / "manifest.json" if m.exists(): d = json.loads(m.read_text()) return float(d["sync_ts_min"]), float(d["sync_ts_max"]) return 1786537835.64518, 1793515782.732466 # 2026-08-12 12:30:35 UTC, 2026-11-01 06:49:42 UTC WINDOW_START_TS, SNAPSHOT_TS = _manifest_ts() # --- Source classification (DECISIONS.md D-003) --------------------------------- SAMPLED_PORTALS = {"autotrader", "cargurus"} # capped sliding windows -> no exit information PRIVATE_MARKETPLACE = {"kijiji"} # private sellers, no VIN FULL_PORTALS = {"otogo", "autousagee"} # full-inventory portals (dealer-identified) BANNERS = {"hgregoire", "automobileendirect", "megacentredeliquidation", "clubautozone", "leprixdugros"} SMALL_PORTALS = {"okaze", "classeauto", "ototr"} FLICKER_OR_FAILED = {"leprixdugros", "albioccasion", "baiecomeaugm", "septilesgm", "stjeromeautodepot", "excelmoto"} UID_COLLISIONS = {"classeauto:-98"} # --- Cleaning thresholds (analysis_plan.md §1-2) -------------------------------- PRICE_MIN, PRICE_MAX = 2_000.0, 250_000.0 KM_MIN, KM_MAX = 1_000.0, 400_000.0 YEAR_MIN, YEAR_MAX = 2005, 2026 TRANSIENT_SECONDS = 48 * 3600 # A->B->A level shorter than this is removed OSCILLATION_MAX_TRANSIENTS = 3 # >= 3 transients -> oscillating uid MARKDOWN_MIN_PCT, MARKDOWN_MIN_ABS = 0.01, 100.0 EXIT_CONFIRM_DAYS = 7 LEFT_CENSOR_CUTOFF_TS = 1786579200.0 # 2026-08-13 00:00:00 UTC: entries before this are the day-1 stock SOURCE_STARTUP_SECONDS = 24 * 3600 # entries within 24 h of a source's first non-empty crawl = start-up stock (D-014) RESTRICTED_COLUMNS = ["description", "images", "url", "carfax_url", "features"]