|
|
@@ -0,0 +1,150 @@
|
|
|
+"""视频点位解析与序列化 — decode_result / JSON 字段 ↔ 行记录。"""
|
|
|
+from __future__ import annotations
|
|
|
+
|
|
|
+import json
|
|
|
+from typing import Any
|
|
|
+
|
|
|
+from supply_infra.db.models.multi_demand_video_point import (
|
|
|
+ POINT_TYPE_INSPIRATION,
|
|
|
+ POINT_TYPE_KEY,
|
|
|
+ POINT_TYPE_PURPOSE,
|
|
|
+)
|
|
|
+
|
|
|
+_POINT_KEY = "点"
|
|
|
+_POINT_DESC_KEY = "点描述"
|
|
|
+
|
|
|
+# decode_result 中的 key → point_type
|
|
|
+DECODE_RESULT_KEY_TO_POINT_TYPE = {
|
|
|
+ "灵感点": POINT_TYPE_INSPIRATION,
|
|
|
+ "目的点": POINT_TYPE_PURPOSE,
|
|
|
+ "关键点": POINT_TYPE_KEY,
|
|
|
+}
|
|
|
+
|
|
|
+# 原 JSON 列名 → point_type
|
|
|
+JSON_FIELD_TO_POINT_TYPE = {
|
|
|
+ "inspiration_points_json": POINT_TYPE_INSPIRATION,
|
|
|
+ "purpose_points_json": POINT_TYPE_PURPOSE,
|
|
|
+ "key_points_json": POINT_TYPE_KEY,
|
|
|
+}
|
|
|
+
|
|
|
+# point_type → 原 JSON 列名(API 兼容)
|
|
|
+POINT_TYPE_TO_JSON_FIELD = {v: k for k, v in JSON_FIELD_TO_POINT_TYPE.items()}
|
|
|
+
|
|
|
+
|
|
|
+def _item_to_row(
|
|
|
+ video_id: str, point_type: str, item: dict[str, Any]
|
|
|
+) -> dict[str, str | None] | None:
|
|
|
+ point = item.get(_POINT_KEY)
|
|
|
+ point_desc = item.get(_POINT_DESC_KEY)
|
|
|
+ if point is None and point_desc is None:
|
|
|
+ return None
|
|
|
+ return {
|
|
|
+ "video_id": video_id,
|
|
|
+ "point_type": point_type,
|
|
|
+ "point_data": str(point) if point is not None else None,
|
|
|
+ "point_desc": str(point_desc) if point_desc is not None else None,
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+def points_from_decode_payload(
|
|
|
+ video_id: str, payload: dict[str, Any]
|
|
|
+) -> list[dict[str, str | None]]:
|
|
|
+ """从 decode_result 提取全部点位行。"""
|
|
|
+ rows: list[dict[str, str | None]] = []
|
|
|
+ for decode_key, point_type in DECODE_RESULT_KEY_TO_POINT_TYPE.items():
|
|
|
+ items = payload.get(decode_key)
|
|
|
+ if not isinstance(items, list):
|
|
|
+ continue
|
|
|
+ for item in items:
|
|
|
+ if not isinstance(item, dict):
|
|
|
+ continue
|
|
|
+ row = _item_to_row(video_id, point_type, item)
|
|
|
+ if row:
|
|
|
+ rows.append(row)
|
|
|
+ return rows
|
|
|
+
|
|
|
+
|
|
|
+def points_from_json_fields(
|
|
|
+ video_id: str,
|
|
|
+ *,
|
|
|
+ inspiration_points_json: str | None = None,
|
|
|
+ purpose_points_json: str | None = None,
|
|
|
+ key_points_json: str | None = None,
|
|
|
+) -> list[dict[str, str | None]]:
|
|
|
+ """从 multi_demand_video_detail 的三个 JSON 列提取点位行。"""
|
|
|
+ field_values = {
|
|
|
+ "inspiration_points_json": inspiration_points_json,
|
|
|
+ "purpose_points_json": purpose_points_json,
|
|
|
+ "key_points_json": key_points_json,
|
|
|
+ }
|
|
|
+ rows: list[dict[str, str | None]] = []
|
|
|
+ for field, point_type in JSON_FIELD_TO_POINT_TYPE.items():
|
|
|
+ text = field_values.get(field)
|
|
|
+ if not text:
|
|
|
+ continue
|
|
|
+ try:
|
|
|
+ items = json.loads(text)
|
|
|
+ except json.JSONDecodeError:
|
|
|
+ continue
|
|
|
+ if not isinstance(items, list):
|
|
|
+ continue
|
|
|
+ for item in items:
|
|
|
+ if not isinstance(item, dict):
|
|
|
+ continue
|
|
|
+ row = _item_to_row(video_id, point_type, item)
|
|
|
+ if row:
|
|
|
+ rows.append(row)
|
|
|
+ return rows
|
|
|
+
|
|
|
+
|
|
|
+def json_fields_from_point_rows(
|
|
|
+ rows: list[dict[str, Any]],
|
|
|
+) -> dict[str, str | None]:
|
|
|
+ """将点位行重组为三个 JSON 列(保持 API 兼容)。"""
|
|
|
+ grouped: dict[str, list[dict[str, str | None]]] = {
|
|
|
+ POINT_TYPE_INSPIRATION: [],
|
|
|
+ POINT_TYPE_PURPOSE: [],
|
|
|
+ POINT_TYPE_KEY: [],
|
|
|
+ }
|
|
|
+ for row in rows:
|
|
|
+ point_type = row.get("point_type")
|
|
|
+ if point_type not in grouped:
|
|
|
+ continue
|
|
|
+ grouped[point_type].append(
|
|
|
+ {
|
|
|
+ _POINT_KEY: row.get("point_data"),
|
|
|
+ _POINT_DESC_KEY: row.get("point_desc"),
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+ result: dict[str, str | None] = {}
|
|
|
+ for point_type, field in POINT_TYPE_TO_JSON_FIELD.items():
|
|
|
+ items = grouped[point_type]
|
|
|
+ result[field] = (
|
|
|
+ json.dumps(items, ensure_ascii=False) if items else None
|
|
|
+ )
|
|
|
+ return result
|
|
|
+
|
|
|
+
|
|
|
+def extract_points_json_from_decode(payload: dict[str, Any]) -> dict[str, str | None]:
|
|
|
+ """从 decode_result 提取灵感点/目的点/关键点 JSON 列(原逻辑)。"""
|
|
|
+ result: dict[str, str | None] = {}
|
|
|
+ for decode_key, field in {
|
|
|
+ k: POINT_TYPE_TO_JSON_FIELD[v]
|
|
|
+ for k, v in DECODE_RESULT_KEY_TO_POINT_TYPE.items()
|
|
|
+ }.items():
|
|
|
+ items = payload.get(decode_key)
|
|
|
+ if not isinstance(items, list):
|
|
|
+ result[field] = None
|
|
|
+ continue
|
|
|
+ points: list[dict[str, Any]] = []
|
|
|
+ for item in items:
|
|
|
+ if not isinstance(item, dict):
|
|
|
+ continue
|
|
|
+ point = item.get(_POINT_KEY)
|
|
|
+ point_desc = item.get(_POINT_DESC_KEY)
|
|
|
+ if point is None and point_desc is None:
|
|
|
+ continue
|
|
|
+ points.append({_POINT_KEY: point, _POINT_DESC_KEY: point_desc})
|
|
|
+ result[field] = json.dumps(points, ensure_ascii=False) if points else None
|
|
|
+ return result
|