Data-layer impl of #887 (steps 1-3+5; client-visible estimator time-adjust step 4 deferred to backtest-gated follow-up — estimator.py untouched). - migration 092: sber_price_index table (additive, CREATE TABLE IF NOT EXISTS) - migration 093: scrape_schedules seed (source=sber_index_pull, ON CONFLICT DO NOTHING) - sber_index.py: base64 /api/sowa route builder + decoder + async fetch + idempotent upsert ON CONFLICT (city,period_month,dashboard) - task wrapper + scheduler trigger + dispatch branch (monthly); 3 dashboards (deals/hedonic/asking) REALTY=2; per-series graceful errors; asking benchmark log NOTE: REF_AREA codes EKB/MSK unresolved (sberindex TLS unreachable from CI; 643=РФ only) — TODO in SBER_REF_AREAS. 20 tests pass. Refs #887, #794, #652
70 lines
3.6 KiB
PL/PgSQL
70 lines
3.6 KiB
PL/PgSQL
-- 092_sber_price_index.sql
|
||
-- Issue #887 — city-level secondary-housing price index from СберИндекс (sberindex.ru).
|
||
--
|
||
-- Context:
|
||
-- СберИндекс publishes monthly city/region-level secondary-housing price statistics
|
||
-- across three dashboards:
|
||
-- * real_estate_deals — hedonic sold-price index (руб/м²)
|
||
-- * residential_real_estate_prices — repeat-sales / asking-adjusted hedonic
|
||
-- * dinamika-tsen-obyavlenii — asking-price benchmark (объявления, руб/м²)
|
||
--
|
||
-- The pull service (app/services/sber_index.py) fetches all three series for configured
|
||
-- cities (initially РФ-643, with EKB/MSK codes TBD — see TODO in sber_index.py) via the
|
||
-- /api/sowa POST endpoint (reverse-engineered in #794 research note) and upserts rows here.
|
||
--
|
||
-- The table feeds the estimator time-adjust coefficient (step 4, #887 out-of-scope —
|
||
-- backtest-gated, separate follow-up).
|
||
--
|
||
-- Schema notes:
|
||
-- * city — REF_AREA label as returned by the API (e.g. «Россия», «Екатеринбург»).
|
||
-- * period_month — first day of the reported month (normalised from API timestamp).
|
||
-- * segment — realty type label from API (e.g. «Вторичный»).
|
||
-- * dashboard — slug used in the route (e.g. 'real_estate_deals').
|
||
-- * PRIMARY KEY (city, period_month, dashboard) — one value per city×month×series;
|
||
-- upsert on conflict keeps the freshest fetched_at.
|
||
--
|
||
-- Idempotent: CREATE TABLE IF NOT EXISTS — safe to re-apply. ADDITIVE only, no drops.
|
||
--
|
||
-- Dependencies: none (standalone table).
|
||
--
|
||
-- Deploy order: apply before deploying app/services/sber_index.py +
|
||
-- app/tasks/sber_index_pull.py + scheduler.py changes.
|
||
|
||
BEGIN;
|
||
|
||
CREATE TABLE IF NOT EXISTS sber_price_index (
|
||
city text NOT NULL,
|
||
period_month date NOT NULL,
|
||
segment text NOT NULL DEFAULT '',
|
||
dashboard text NOT NULL,
|
||
index_value_rub_m2 double precision NOT NULL,
|
||
source text NOT NULL DEFAULT 'sberindex',
|
||
fetched_at timestamptz NOT NULL DEFAULT now(),
|
||
PRIMARY KEY (city, period_month, dashboard)
|
||
);
|
||
|
||
COMMENT ON TABLE sber_price_index IS
|
||
'Monthly city-level secondary-housing price index from СберИндекс (sberindex.ru). '
|
||
'Three series: real_estate_deals (hedonic sold), residential_real_estate_prices '
|
||
'(repeat-sales hedonic), dinamika-tsen-obyavlenii (asking benchmark). '
|
||
'Populated by app/tasks/sber_index_pull.py via in-app scheduler (#887). '
|
||
'Estimator time-adjust wire is a separate backtest-gated follow-up (step 4, #887).';
|
||
|
||
COMMENT ON COLUMN sber_price_index.city IS
|
||
'REF_AREA label from the API response (e.g. «Россия», «Екатеринбург»).';
|
||
COMMENT ON COLUMN sber_price_index.period_month IS
|
||
'First day of the reported month (normalised from API ISO timestamp).';
|
||
COMMENT ON COLUMN sber_price_index.segment IS
|
||
'Realty type label from API (e.g. «Вторичный»). Empty string when absent.';
|
||
COMMENT ON COLUMN sber_price_index.dashboard IS
|
||
'Dataset slug used in the /dataset/v1/<dashboard> route.';
|
||
COMMENT ON COLUMN sber_price_index.index_value_rub_m2 IS
|
||
'Price index in rubles per square metre.';
|
||
COMMENT ON COLUMN sber_price_index.fetched_at IS
|
||
'Wall-clock timestamp when this row was last written/updated.';
|
||
|
||
-- Index for time-series queries (latest N months per city × dashboard)
|
||
CREATE INDEX IF NOT EXISTS idx_sber_price_index_city_dashboard_period
|
||
ON sber_price_index (city, dashboard, period_month DESC);
|
||
|
||
COMMIT;
|