"""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()