odps_demand_pool.py 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. """从 ODPS 需求池拉取当下供需 gap 数据。"""
  2. from __future__ import annotations
  3. import re
  4. from app.aliyun_odps.client import get_odps_client
  5. from app.gap_script_demand.exceptions import GapScriptDemandError
  6. IDENTIFIER_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?$")
  7. def _safe_identifier(name: str) -> str:
  8. value = name.strip()
  9. if not IDENTIFIER_RE.match(value):
  10. raise GapScriptDemandError(f"invalid sql identifier: {name}")
  11. return value
  12. def _escape_sql_string(value: str) -> str:
  13. return value.replace("'", "''")
  14. def _normalize_demand_key(value: str) -> str:
  15. return "".join(value.split())
  16. def _dedupe_demand_names(demand_names: list[str]) -> list[str]:
  17. deduped: list[str] = []
  18. seen: set[str] = set()
  19. for raw_name in demand_names:
  20. demand_name = str(raw_name).strip()
  21. if not demand_name:
  22. continue
  23. keys = {demand_name, _normalize_demand_key(demand_name)}
  24. if keys & seen:
  25. continue
  26. seen.update(keys)
  27. deduped.append(demand_name)
  28. return deduped
  29. def fetch_demand_names(
  30. *,
  31. partition_dt: str,
  32. strategy: str,
  33. source_table: str,
  34. ) -> list[str]:
  35. """按分区日期与 strategy 从 ODPS 需求池拉取 demand_name。"""
  36. table = _safe_identifier(source_table)
  37. dt = _escape_sql_string(partition_dt.strip())
  38. strategy_value = _escape_sql_string(strategy.strip())
  39. if not dt:
  40. raise GapScriptDemandError("partition_dt is required")
  41. if not strategy_value:
  42. raise GapScriptDemandError("strategy is required")
  43. sql = f"""
  44. SELECT TRIM(demand_name) AS demand_name
  45. FROM {table}
  46. WHERE dt = '{dt}'
  47. AND strategy = '{strategy_value}'
  48. AND demand_name IS NOT NULL
  49. AND TRIM(demand_name) <> ''
  50. """
  51. odps_client = get_odps_client()
  52. instance = odps_client.execute_sql(sql)
  53. raw_demand_names: list[str] = []
  54. with instance.open_reader(tunnel=True) as reader:
  55. for record in reader:
  56. demand_name = str(record["demand_name"] or "").strip()
  57. if demand_name:
  58. raw_demand_names.append(demand_name)
  59. return _dedupe_demand_names(raw_demand_names)