"""Scheduler adapter for the Yahoo public-offer reconciliation lane.""" from __future__ import annotations import logging import os from datetime import datetime, timedelta, timezone TAIPEI_TZ = timezone(timedelta(hours=8)) def run_scheduled_pchome_growth_yahoo_backfill(*, save_stats): """Bounded Yahoo public-offer reconciliation with canary and readback.""" try: from config import DATABASE_PATH from sqlalchemy import create_engine from services.yahoo_shopping_public_offer_service import ( run_pchome_growth_yahoo_backfill, ) now_str = datetime.now(TAIPEI_TZ).strftime("%Y-%m-%d %H:%M") limit = max( 1, min(int(os.getenv("PCHOME_GROWTH_YAHOO_BACKFILL_LIMIT", "8")), 20), ) logging.info( "[Scheduler] [PChomeGrowthYahooBackfill] starting | %s | limit=%s", now_str, limit, ) engine = create_engine(DATABASE_PATH) try: result = run_pchome_growth_yahoo_backfill(engine, limit=limit) finally: engine.dispose() data = result.get("data") or {} sync_result = data.get("external_offer_sync") or {} post_verifier = data.get("post_verifier") or {} activation = data.get("source_activation") or {} artifact = data.get("artifact_receipt") or {} identity = data.get("identity") or {} terminal_status = str(data.get("terminal_status") or "") stats = { "status": ( "Success" if result.get("success") and terminal_status.startswith("verified") else "Degraded" if terminal_status.startswith(("degraded", "partial")) else "Failed" if not result.get("success") else "Skipped" ), "trace_id": identity.get("trace_id"), "run_id": identity.get("run_id"), "work_item_id": identity.get("work_item_id"), "terminal_status": terminal_status, "next_machine_action": data.get("next_machine_action"), "scanned_products": data.get("scanned_products", 0), "candidate_count": data.get("candidate_count", 0), "auto_compare_count": data.get("auto_compare_count", 0), "review_count": data.get("review_count", 0), "written_count": sync_result.get("written_count", 0), "readback_pass_count": post_verifier.get("readback_pass_count", 0), "post_verifier_status": post_verifier.get("status"), "source_activation_status": activation.get("status"), "source_activated": bool(activation.get("applied")), "artifact_status": artifact.get("status"), "artifact_sha256": artifact.get("receipt_sha256"), "message": result.get("message"), } logging.info( "[Scheduler] [PChomeGrowthYahooBackfill] completed | " "terminal=%s scanned=%s candidates=%s verified=%s written=%s " "readback=%s activated=%s artifact=%s", stats["terminal_status"], stats["scanned_products"], stats["candidate_count"], stats["auto_compare_count"], stats["written_count"], stats["readback_pass_count"], stats["source_activated"], stats["artifact_status"], ) save_stats("pchome_growth_yahoo_backfill", stats) return result except Exception as error: import traceback as _tb logging.error( "[Scheduler] [PChomeGrowthYahooBackfill] task failed | Error: %s", error, exc_info=True, ) save_stats( "pchome_growth_yahoo_backfill", {"status": "Failed", "error": str(error)}, ) try: from services.event_router import notify_failure notify_failure( task_name="run_pchome_growth_yahoo_backfill_task", error=error, source="Scheduler.PChomeGrowthYahooBackfill", event_type="pchome_growth_yahoo_backfill_failure", priority="P2", title="PChome 高業績商品 Yahoo 公開來源補齊異常", trace=_tb.format_exc(), ) except Exception as router_error: logging.error( "[Scheduler] [PChomeGrowthYahooBackfill] event_router failed: %s", router_error, ) return { "success": False, "message": str(error), "data": {"terminal_status": "failed_with_safe_retry"}, } __all__ = ["run_scheduled_pchome_growth_yahoo_backfill"]