В nspd_kn.py:get_pending_cads и admin_scrape.py:nspd_coverage regex-фильтры quarter_cad_number !~ '^00:00:' и !~ ':0000000$' падали с psycopg.errors. DataError — двоеточие в text() трактуется как ':param'. Заменил на NOT LIKE с bindparams (быстрее regex и без issue с парсингом). Эффект на проде: - /api/v1/admin/scrape/nspd/coverage возвращал 500 → теперь 200 - Кнопка «Запустить sweep» больше не дизейбленa (pending=964 теперь видно) - Сам scraper тоже бы валился на этом же баге при beat-запуске
54 lines
3 KiB
PL/PgSQL
54 lines
3 KiB
PL/PgSQL
-- Пересчёт ekb_districts.median_price_per_m2 + mean_price_per_m2 из rosreestr_deals.
|
||
--
|
||
-- Зачем: для половины районов ЕКБ были NULL (импорт Антона из SQLite не покрыл),
|
||
-- из-за чего recommend_mix давал district_factor=1.0 без учёта района и
|
||
-- cadastre_vs_market_pct = NULL (нет district_median для сравнения с кадастром).
|
||
-- Старые значения для Октябрьского/Орджоникидзевского (191K/205K) выглядят как
|
||
-- yandex/domrf-листинг, не реальные сделки — перезаписываем для консистентности.
|
||
--
|
||
-- Исключаем 'не определён' (служебная категория, не настоящий район).
|
||
--
|
||
-- Идемпотентно — можно перезапускать. Окно 24 мес даёт устойчивые медианы.
|
||
-- Если в районе <50 сделок за 24 мес — оставляем NULL (warning в recommend_mix).
|
||
|
||
CREATE OR REPLACE FUNCTION refresh_ekb_districts_median(window_months int DEFAULT 24, min_deals int DEFAULT 50)
|
||
RETURNS TABLE(district_name text, deals_used bigint, median_pm numeric, mean_pm numeric)
|
||
LANGUAGE plpgsql AS $$
|
||
BEGIN
|
||
RETURN QUERY
|
||
WITH per_district AS (
|
||
SELECT d.district_name,
|
||
COUNT(*)::bigint AS deals,
|
||
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY rd.price_per_sqm)::numeric AS p_median,
|
||
AVG(rd.price_per_sqm)::numeric AS p_mean
|
||
FROM rosreestr_deals rd
|
||
JOIN cad_quarters_geom cq ON cq.cad_number = rd.quarter_cad_number
|
||
JOIN ekb_districts_geom d ON ST_Intersects(d.geom, cq.geom)
|
||
WHERE rd.region_code = 66
|
||
AND rd.doc_type = 'ДДУ'
|
||
AND rd.realestate_type_code = '002001003000'
|
||
AND rd.area BETWEEN 15 AND 200
|
||
AND rd.price_per_sqm BETWEEN 30000 AND 1000000
|
||
AND rd.period_start_date >= NOW() - (window_months || ' months')::interval
|
||
GROUP BY d.district_name
|
||
),
|
||
upd AS (
|
||
UPDATE ekb_districts e
|
||
SET median_price_per_m2 = ROUND(pd.p_median, 0),
|
||
mean_price_per_m2 = ROUND(pd.p_mean, 0)
|
||
FROM per_district pd
|
||
WHERE e.district_name = pd.district_name
|
||
AND e.district_name <> 'не определён'
|
||
AND pd.deals >= min_deals
|
||
RETURNING e.district_name, pd.deals, pd.p_median, pd.p_mean
|
||
)
|
||
SELECT u.district_name::text, u.deals, ROUND(u.p_median, 0), ROUND(u.p_mean, 0)
|
||
FROM upd u
|
||
ORDER BY u.district_name;
|
||
END;
|
||
$$;
|
||
|
||
COMMENT ON FUNCTION refresh_ekb_districts_median(int, int) IS
|
||
'Refresh ekb_districts.median_price_per_m2 + mean_price_per_m2 из ДДУ-сделок rosreestr_deals '
|
||
'через spatial-join cad_quarters_geom + ekb_districts_geom. Окно по умолчанию 24 мес, '
|
||
'минимум 50 сделок на район. Запускается Celery beat раз в месяц.';
|