feat(scripts): 支持动态指标和维度参数配置

为 build_studio_urls.py 添加指标和维度的可配置支持,包括:
- 新增 metrics.json 和 dimensions.json 映射文件加载
- 支持中文/英文别名映射及指标代码透传
- 列别名扩展以识别"指标"和"维度"列
- 空值时自动回退到 CONFIG 默认值
This commit is contained in:
2026-08-24 15:28:48 +08:00
parent ba00651fa9
commit b8bd749f43
16 changed files with 996 additions and 36 deletions

View File

@@ -19,7 +19,8 @@ ASSET_XLSX = ROOT / "assets" / "需求输入示例.xlsx"
HEADERS = ["所有者名称", "所有者ID", "实体类型", "实体名称", "实体ID", "数据周期", "国家"]
OUTPUT_COLUMNS = ["所有者名称", "所有者ID", "实体类型", "实体名称", "实体ID",
"数据周期", "国家", "国家代码", "开始时间戳", "结束时间戳", "URL"]
"数据周期", "国家", "国家代码", "指标", "指标代码", "维度", "维度代码",
"开始时间戳", "结束时间戳", "URL"]
def run_builder(args):
@@ -183,6 +184,123 @@ class TestHappyPath:
assert len(read_output(out)) == 1
# ---------------------------------------------------------------------------
# 指标(每行可选)
# ---------------------------------------------------------------------------
class TestMetricColumn:
def test_metric_selected_and_default(self, tmp_path):
"""指标列:一行选"观看时长",一行留空走 CONFIG 默认。"""
src = tmp_path / "需求.csv"
pd.DataFrame([
{"所有者ID": "MC123", "实体ID": "G001", "数据周期": "2026.07.01-2026.08.01",
"指标": "观看时长"},
{"所有者ID": "MC123", "实体ID": "G002", "数据周期": "2026.07.01-2026.08.01",
"指标": ""},
]).to_csv(src, index=False, encoding="utf-8-sig")
out = tmp_path / "out.csv"
r = run_builder(["-i", str(src), "-o", str(out)])
assert r.returncode == 0, r.stderr
df = read_output(out)
assert list(df.columns) == OUTPUT_COLUMNS
assert df.loc[0, "指标"] == "观看时长"
assert df.loc[0, "指标代码"] == "EXTERNAL_WATCH_TIME"
assert "metric=EXTERNAL_WATCH_TIME" in df.loc[0, "URL"]
assert "o_column=EXTERNAL_WATCH_TIME" in df.loc[0, "URL"]
# 空白行:回退 CONFIG 默认
assert df.loc[1, "指标代码"] == "SUBSCRIBERS_NET_CHANGE"
assert "metric=SUBSCRIBERS_NET_CHANGE" in df.loc[1, "URL"]
def test_unknown_metric_exits_2(self, tmp_path):
src = tmp_path / "需求.csv"
src.write_text(
"所有者ID,实体ID,数据周期,指标\nMC123,G001,2026.07.01-2026.08.01,神秘指标\n",
encoding="utf-8-sig",
)
out = tmp_path / "out.csv"
r = run_builder(["-i", str(src), "-o", str(out)])
assert r.returncode == 2
assert "未识别的指标" in r.stderr
assert "第2行" in r.stderr
def test_metric_code_passthrough(self, tmp_path):
src = tmp_path / "需求.csv"
src.write_text(
"所有者ID,实体ID,数据周期,指标\nMC123,G001,2026.07.01-2026.08.01,external_views\n",
encoding="utf-8-sig",
)
out = tmp_path / "out.csv"
r = run_builder(["-i", str(src), "-o", str(out)])
assert r.returncode == 0, r.stderr
df = read_output(out)
assert df.loc[0, "指标代码"] == "EXTERNAL_VIEWS"
assert "metric=EXTERNAL_VIEWS" in df.loc[0, "URL"]
def test_metric_alias_tax_revenue(self, tmp_path):
"""「税前收益/税前收入」别名 -> TOTAL_ESTIMATED_EARNINGS。"""
src = tmp_path / "需求.csv"
src.write_text(
"所有者ID,实体ID,数据周期,指标\nMC123,G001,2026.07.01-2026.08.01,税前收益\n",
encoding="utf-8-sig",
)
out = tmp_path / "out.csv"
r = run_builder(["-i", str(src), "-o", str(out)])
assert r.returncode == 0, r.stderr
df = read_output(out)
assert df.loc[0, "指标代码"] == "TOTAL_ESTIMATED_EARNINGS"
assert "metric=TOTAL_ESTIMATED_EARNINGS" in df.loc[0, "URL"]
# ---------------------------------------------------------------------------
# 维度(每行可选)
# ---------------------------------------------------------------------------
class TestDimensionColumn:
def test_dimension_selected_and_default(self, tmp_path):
"""维度列:一行选"地理位置",一行留空走 CONFIG 默认。"""
src = tmp_path / "需求.csv"
pd.DataFrame([
{"所有者ID": "MC123", "实体ID": "G001", "数据周期": "2026.07.01-2026.08.01",
"维度": "地理位置"},
{"所有者ID": "MC123", "实体ID": "G002", "数据周期": "2026.07.01-2026.08.01",
"维度": ""},
]).to_csv(src, index=False, encoding="utf-8-sig")
out = tmp_path / "out.csv"
r = run_builder(["-i", str(src), "-o", str(out)])
assert r.returncode == 0, r.stderr
df = read_output(out)
assert list(df.columns) == OUTPUT_COLUMNS
assert df.loc[0, "维度"] == "地理位置"
assert df.loc[0, "维度代码"] == "COUNTRY"
assert "dimension=COUNTRY" in df.loc[0, "URL"]
# 空白行:回退 CONFIG 默认
assert df.loc[1, "维度代码"] == "USER"
assert "dimension=USER" in df.loc[1, "URL"]
def test_unknown_dimension_exits_2(self, tmp_path):
src = tmp_path / "需求.csv"
src.write_text(
"所有者ID,实体ID,数据周期,维度\nMC123,G001,2026.07.01-2026.08.01,神秘维度\n",
encoding="utf-8-sig",
)
out = tmp_path / "out.csv"
r = run_builder(["-i", str(src), "-o", str(out)])
assert r.returncode == 2
assert "未识别的维度" in r.stderr
assert "第2行" in r.stderr
def test_dimension_code_passthrough(self, tmp_path):
src = tmp_path / "需求.csv"
src.write_text(
"所有者ID,实体ID,数据周期,维度\nMC123,G001,2026.07.01-2026.08.01,video\n",
encoding="utf-8-sig",
)
out = tmp_path / "out.csv"
r = run_builder(["-i", str(src), "-o", str(out)])
assert r.returncode == 0, r.stderr
df = read_output(out)
assert df.loc[0, "维度代码"] == "VIDEO"
assert "dimension=VIDEO" in df.loc[0, "URL"]
# ---------------------------------------------------------------------------
# 失败行与退出码
# ---------------------------------------------------------------------------