gendesign/backend/app/services/analytics_refresh.py
lekss361 f80a2c1d05 fix(scrape): SQLAlchemy text() парсил ':00:' regex как named-param
В 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-запуске
2026-04-30 23:16:11 +03:00

66 lines
2.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Регулярные 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