В 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-запуске
66 lines
2.2 KiB
Python
66 lines
2.2 KiB
Python
"""Регулярные refresh-задачи для аналитического слоя.
|
||
|
||
Сейчас здесь:
|
||
- refresh_ekb_districts_median: пересчёт median/mean ₽/м² 8 районов ЕКБ из
|
||
ДДУ-сделок rosreestr_deals (окно 24 мес).
|
||
|
||
Запускается через Celery beat (ежемесячно). См. workers/tasks/refresh_analytics.py.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import logging
|
||
from typing import Any
|
||
|
||
from sqlalchemy import text
|
||
from sqlalchemy.orm import Session
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
def refresh_ekb_districts_median(
|
||
db: Session,
|
||
*,
|
||
window_months: int = 24,
|
||
min_deals: int = 50,
|
||
) -> list[dict[str, Any]]:
|
||
"""Пересчитать ekb_districts.median_price_per_m2 + mean_price_per_m2 из
|
||
rosreestr ДДУ-сделок последних `window_months` месяцев.
|
||
|
||
Использует SQL-функцию `refresh_ekb_districts_median(int, int)` из
|
||
миграции 67_refresh_ekb_districts_median.sql. Возвращает список
|
||
обновлённых районов с фактически использованными метриками.
|
||
|
||
Не обновляет района с deals < min_deals (статистически шумно).
|
||
Не трогает 'не определён' (служебная категория).
|
||
"""
|
||
rows = (
|
||
db.execute(
|
||
text(
|
||
"""
|
||
SELECT district_name, deals_used, median_pm, mean_pm
|
||
FROM refresh_ekb_districts_median(:wm, :md)
|
||
"""
|
||
),
|
||
{"wm": window_months, "md": min_deals},
|
||
)
|
||
.mappings()
|
||
.all()
|
||
)
|
||
db.commit()
|
||
out = [
|
||
{
|
||
"district_name": r["district_name"],
|
||
"deals_used": int(r["deals_used"]),
|
||
"median_pm": float(r["median_pm"]) if r["median_pm"] is not None else None,
|
||
"mean_pm": float(r["mean_pm"]) if r["mean_pm"] is not None else None,
|
||
}
|
||
for r in rows
|
||
]
|
||
logger.info(
|
||
"refresh_ekb_districts_median: window=%dmo min_deals=%d updated=%d districts",
|
||
window_months,
|
||
min_deals,
|
||
len(out),
|
||
)
|
||
return out
|