"""Celery task: refresh mv_quarter_price_per_m2 → mv_quarter_price_index chain. Scheduled via hardcoded beat entry in workers/beat_schedule.py: 'refresh-quarter-price-index' — monthly on the 5th at 05:00 MSK (02:00 UTC). Runs after 'refresh-ekb-districts-medians' (04:00 MSK same day) so that deals data is settled before the price index is recomputed. Issue: #762. """ from __future__ import annotations import logging from typing import Any from app.core.db import SessionLocal from app.services.site_finder.quarter_price_index_refresh import refresh_quarter_price_index from app.workers.celery_app import celery_app logger = logging.getLogger(__name__) @celery_app.task( bind=True, name="tasks.refresh_quarter_price_index.refresh_quarter_price_index_chain", max_retries=2, ) def refresh_quarter_price_index_chain(self: Any) -> dict[str, Any]: """Refresh mv_quarter_price_per_m2 then mv_quarter_price_index in sequence. Both MVs are refreshed CONCURRENTLY (non-blocking). Falls back to non-concurrent if the MV is found unpopulated (edge case on first run). Returns result dict for Celery task result store / logging. """ db = SessionLocal() try: count = refresh_quarter_price_index(db, concurrently=True) logger.info("refresh_quarter_price_index_chain: completed, index rows=%d", count) return {"status": "ok", "mv_quarter_price_index_rows": count} except Exception as e: logger.exception("refresh_quarter_price_index_chain failed: %s", e) raise finally: db.close()