Серия sale-share data-quality (по находкам юзера): - house_dedup_merge: cluster_key → tradein_canon_addr(COALESCE(short_address, full_address, address)) — ловит дубли с район-noise в address (Вайнера 66). - zhkh_flats_loader: cadastre-fallback (searchByAddress по cadastreNumber, guid-независимо) — резолвит дома где gar_house_guid ≠ ЖКХ houseGuid (Переходный 6: guid→0, cadastre→165). CLI --cadastre. - мигр.153: плаузибилити-гард по медиане этажности листингов — denom >= GREATEST(total_floors, median(listings.total_floors), 8); отсекает gar-недосчёт башен без ЖКХ (gar=9 vs листинги 19-26 эт → NULL). ruff clean, pytest 36 green.
113 lines
4.4 KiB
Python
113 lines
4.4 KiB
Python
"""CLI: загрузка ЖКХ-знаменателя «квартир на дом» → houses.zhkh_* (мигр. 146/149).
|
||
|
||
Тянет точный счёт квартир из публичного API ГИС ЖКХ (dom.gosuslugi.ru) и проставляет
|
||
`houses.zhkh_flat_count` / `zhkh_house_guid` / `zhkh_matched_at` / `zhkh_year` /
|
||
`zhkh_floors_raw`. Матч ТРИВИАЛЬНЫЙ: `houses.gar_house_guid` — это И ЕСТЬ ЖКХ `houseGuid`
|
||
(проверено), поэтому ищем дом по нему, без нормализации/канона адреса.
|
||
|
||
Запуск из контейнера tradein-backend (у прода есть интернет к dom.gosuslugi.ru):
|
||
|
||
python -m app.tasks.zhkh_flats_load # полный прогон (по gar_house_guid)
|
||
python -m app.tasks.zhkh_flats_load --limit 300 --dry-run # сэмпл, без записи
|
||
python -m app.tasks.zhkh_flats_load --cadastre # fallback по кадастру листингов
|
||
|
||
В режиме --dry-run выборка/фетч происходят, но НИ ОДНОЙ записи в БД не делается.
|
||
В режиме --cadastre резолвим дома, чей gar_house_guid ≠ ЖКХ houseGuid, по
|
||
listings.building_cadastral_number и UPDATE'им строго по houses.id.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import logging
|
||
|
||
from app.core.db import SessionLocal
|
||
from app.services.zhkh_flats_loader import (
|
||
match_houses_to_zhkh,
|
||
match_houses_to_zhkh_by_cadastre,
|
||
)
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
def build_parser() -> argparse.ArgumentParser:
|
||
"""Парсер CLI (вынесен для тестируемости флагов без запуска main)."""
|
||
parser = argparse.ArgumentParser(
|
||
description="ГИС ЖКХ loader: квартир на дом → houses.zhkh_* по gar_house_guid"
|
||
)
|
||
parser.add_argument(
|
||
"--region", default="66", help="код региона (информационно, по умолчанию 66)"
|
||
)
|
||
parser.add_argument(
|
||
"--limit", type=int, default=None, help="ограничить число домов (сэмпл); по умолчанию все"
|
||
)
|
||
parser.add_argument(
|
||
"--dry-run", action="store_true", help="без записи в БД — только фетч и подсчёт"
|
||
)
|
||
parser.add_argument(
|
||
"--sleep", type=float, default=0.4, help="пауза между батчами, сек (по умолчанию 0.4)"
|
||
)
|
||
parser.add_argument(
|
||
"--cadastre",
|
||
action="store_true",
|
||
help="cadastre-fallback: резолв ЖКХ по listings.building_cadastral_number для домов, "
|
||
"чей gar_house_guid ≠ ЖКХ houseGuid; UPDATE по houses.id",
|
||
)
|
||
return parser
|
||
|
||
|
||
def main() -> None:
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
||
)
|
||
parser = build_parser()
|
||
args = parser.parse_args()
|
||
|
||
db = SessionLocal()
|
||
try:
|
||
if args.cadastre:
|
||
cad_counts = match_houses_to_zhkh_by_cadastre(
|
||
db,
|
||
limit=args.limit,
|
||
dry_run=args.dry_run,
|
||
sleep_sec=args.sleep,
|
||
)
|
||
if not args.dry_run:
|
||
db.commit()
|
||
logger.info(
|
||
"zhkh_flats_load (cadastre) DONE: limit=%s dry_run=%s seen=%d resolved=%d "
|
||
"updated=%d",
|
||
args.limit,
|
||
args.dry_run,
|
||
cad_counts["houses_seen"],
|
||
cad_counts["resolved"],
|
||
cad_counts["updated"],
|
||
)
|
||
else:
|
||
counts = match_houses_to_zhkh(
|
||
db,
|
||
region_code=args.region,
|
||
limit=args.limit,
|
||
dry_run=args.dry_run,
|
||
sleep_sec=args.sleep,
|
||
)
|
||
if not args.dry_run:
|
||
db.commit()
|
||
logger.info(
|
||
"zhkh_flats_load DONE: region=%s limit=%s dry_run=%s seen=%d resolved=%d "
|
||
"with_flatcount=%d updated=%d",
|
||
args.region,
|
||
args.limit,
|
||
args.dry_run,
|
||
counts["houses_seen"],
|
||
counts["zhkh_resolved"],
|
||
counts["zhkh_with_flatcount"],
|
||
counts["houses_updated"],
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|