|
|
@@ -1,19 +1,13 @@
|
|
|
"""
|
|
|
-定时任务:从 ODPS 同步策略需求天级表到 MySQL,并对新词做归属分类与热度统计。
|
|
|
-
|
|
|
-流程:
|
|
|
-1. 比对当天 ODPS / MySQL 行数,相同则跳过写入
|
|
|
-2. 有差异时拉取 ODPS,按 (strategy, demand_id) 只插入缺失、删除多余,并回填已有行 video_list
|
|
|
-3. video_list 每条最多保留前 10 个 video_id,video_count 与之保持一致
|
|
|
-4. 拉取近 7 日 rov_diff/vov_diff,按特征值匹配回填 real_rov_7d / real_vov_7d
|
|
|
-5. 查询当天全部 demand_name,按空格分词写入 set
|
|
|
-6. 过滤 demand_belong_category 中已存在的词
|
|
|
-7. 剩余词按 100 词一批调用 demand_belong_category_agent
|
|
|
-8. 遍历 demand_belong_category 全部词,按策略与 rov_diff/vov_diff 写入 demand_popularity_stats(avg/count)
|
|
|
-9. 基于三表计算整棵类目树节点加权平均分,写入 category_tree_weight
|
|
|
-10. 四维热度全局排名归一化打分,写入各维 score 与 total_score
|
|
|
-11. 增量同步 multi_demand_video_detail(全表 video_list → ODPS 昨天分区最终选题)
|
|
|
-12. 补充 demand_belong_pool_rel 匹配边,并回填词级 video_list(最多 10 个)
|
|
|
+Demand pool ODPS → MySQL sync stages used by the durable pipeline.
|
|
|
+
|
|
|
+Stages owned by this module:
|
|
|
+1. source row sync (`_sync_pool_rows`)
|
|
|
+2. demand word classification (`_classify_words`)
|
|
|
+3. real rov/vov enrichment (`enrich_real_rov_vov_7d`)
|
|
|
+4. popularity stats (`compute_popularity_stats`)
|
|
|
+
|
|
|
+Related pipeline stages live in sibling modules (belong_rel / tree_weight / videos).
|
|
|
"""
|
|
|
from __future__ import annotations
|
|
|
|
|
|
@@ -21,7 +15,7 @@ import json
|
|
|
import logging
|
|
|
from datetime import datetime, timedelta
|
|
|
from decimal import Decimal
|
|
|
-from typing import Any, Callable
|
|
|
+from typing import Any
|
|
|
|
|
|
from agents.demand_belong_category_agent.run import main as classify_demand_words
|
|
|
from supply_infra.db.repositories.demand_belong_category_repo import (
|
|
|
@@ -33,11 +27,6 @@ from supply_infra.db.repositories.demand_popularity_stats_repo import (
|
|
|
from supply_infra.db.repositories.multi_demand_pool_di_repo import MultiDemandPoolDiRepository
|
|
|
from supply_infra.db.session import get_session
|
|
|
from supply_infra.odps.client import get_odps_client
|
|
|
-from supply_infra.scheduler.jobs.demand_pool.belong_rel import sync_demand_belong_pool_rel
|
|
|
-from supply_infra.scheduler.jobs.demand_pool.tree_weight import (
|
|
|
- compute_category_tree_weight,
|
|
|
-)
|
|
|
-from supply_infra.scheduler.jobs.demand_pool.videos import sync_multi_demand_videos
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@@ -502,77 +491,3 @@ def _sync_pool_rows(partition_date: str) -> dict[str, Any]:
|
|
|
"mysql_count": mysql_count,
|
|
|
**_sync_diff(partition_date),
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
-def _run_sync_stage(
|
|
|
- stage_name: str,
|
|
|
- action: Callable[[], dict[str, Any]],
|
|
|
-) -> tuple[dict[str, Any], str | None]:
|
|
|
- """执行需求池同步子阶段,错误转为结构化结果并允许后续阶段继续。"""
|
|
|
- try:
|
|
|
- payload = action()
|
|
|
- except Exception as exc:
|
|
|
- logger.exception("Multi demand pool stage failed; continue: stage=%s", stage_name)
|
|
|
- return {"success": False, "error": str(exc)}, str(exc)
|
|
|
-
|
|
|
- if payload.get("success") is False:
|
|
|
- error = str(payload.get("error") or f"{stage_name} returned success=False")
|
|
|
- logger.error(
|
|
|
- "Multi demand pool stage reported failure; continue: stage=%s error=%s",
|
|
|
- stage_name,
|
|
|
- error,
|
|
|
- )
|
|
|
- return payload, error
|
|
|
- return payload, None
|
|
|
-
|
|
|
-
|
|
|
-def sync_multi_demand_pool_odps_to_mysql(partition_date: str | None = None) -> dict:
|
|
|
- """
|
|
|
- 从 ODPS 增量同步策略需求天级数据到 MySQL,并对新词做归属分类与热度统计。
|
|
|
-
|
|
|
- Args:
|
|
|
- partition_date: 分区日期 (YYYYMMDD),默认当天
|
|
|
- """
|
|
|
- if partition_date is None:
|
|
|
- partition_date = datetime.now().strftime("%Y%m%d")
|
|
|
-
|
|
|
- logger.info(
|
|
|
- "Starting multi demand pool ODPS → MySQL sync for partition: %s",
|
|
|
- partition_date,
|
|
|
- )
|
|
|
-
|
|
|
- stage_definitions: list[tuple[str, Callable[[], dict[str, Any]]]] = [
|
|
|
- ("source_sync", lambda: _sync_pool_rows(partition_date)),
|
|
|
- ("classify", lambda: _classify_words(partition_date)),
|
|
|
- ("belong_pool_rel", sync_demand_belong_pool_rel),
|
|
|
- ("real_metrics", lambda: enrich_real_rov_vov_7d(partition_date)),
|
|
|
- ("popularity", lambda: compute_popularity_stats(partition_date)),
|
|
|
- ("tree_weight", lambda: compute_category_tree_weight(partition_date)),
|
|
|
- ("videos", lambda: sync_multi_demand_videos(limit=None)),
|
|
|
- ]
|
|
|
- stage_results: dict[str, dict[str, Any]] = {}
|
|
|
- errors: list[dict[str, str]] = []
|
|
|
- for stage_name, action in stage_definitions:
|
|
|
- payload, error = _run_sync_stage(stage_name, action)
|
|
|
- stage_results[stage_name] = payload
|
|
|
- if error:
|
|
|
- errors.append({"stage": stage_name, "error": error})
|
|
|
-
|
|
|
- sync_stats = stage_results["source_sync"]
|
|
|
-
|
|
|
- result = {
|
|
|
- "success": not errors,
|
|
|
- "partition_date": partition_date,
|
|
|
- **({} if errors and sync_stats.get("success") is False else sync_stats),
|
|
|
- "source_sync": sync_stats,
|
|
|
- "real_metrics": stage_results["real_metrics"],
|
|
|
- "classify": stage_results["classify"],
|
|
|
- "belong_pool_rel": stage_results["belong_pool_rel"],
|
|
|
- "popularity": stage_results["popularity"],
|
|
|
- "tree_weight": stage_results["tree_weight"],
|
|
|
- "videos": stage_results["videos"],
|
|
|
- "errors": errors,
|
|
|
- "synced_at": datetime.now().isoformat(),
|
|
|
- }
|
|
|
- logger.info("Multi demand pool sync completed: %s", result)
|
|
|
- return result
|