@wubaoqi/guancopilot 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.md +17 -0
- package/README.md +146 -2
- package/assets/coreutils.wasm +0 -0
- package/assets/find.wasm +0 -0
- package/assets/guanbi.wasm +4 -0
- package/assets/guancli.wasm +0 -0
- package/assets/guanhelm.wasm +0 -0
- package/assets/jq.wasm +0 -0
- package/assets/openskills.wasm +0 -0
- package/assets/rg.wasm +0 -0
- package/assets/sed.wasm +0 -0
- package/bin/guancopilot +0 -0
- package/bin/guanvm +0 -0
- package/bin/platforms.cjs +6 -0
- package/bin/run.cjs +39 -0
- package/bin/runtime.cjs +44 -0
- package/licenses/Go-LICENSE +27 -0
- package/licenses/browser-_speed-highlight_core-LICENSE +121 -0
- package/licenses/browser-_speed-highlight_core-package.json +14 -0
- package/licenses/browser-dompurify-LICENSE +202 -0
- package/licenses/browser-dompurify-LICENSE-MPL +373 -0
- package/licenses/browser-dompurify-package.json +10 -0
- package/licenses/browser-marked-LICENSE +44 -0
- package/licenses/browser-marked-package.json +10 -0
- package/licenses/coreutils-LICENSE +18 -0
- package/licenses/findutils-LICENSE +18 -0
- package/licenses/github.com_BurntSushi_toml_v1.6.0-COPYING +21 -0
- package/licenses/github.com_dustin_go-humanize_v1.0.1-LICENSE +21 -0
- package/licenses/github.com_google_jsonschema-go_v0.4.3-LICENSE +21 -0
- package/licenses/github.com_google_uuid_v1.6.0-LICENSE +27 -0
- package/licenses/github.com_jackc_pgpassfile_v1.0.0-LICENSE +22 -0
- package/licenses/github.com_jackc_pgservicefile_v0.0.0-20240606120523-5a60cdf6a761-LICENSE +22 -0
- package/licenses/github.com_jackc_pgx_v5_v5.11.0-LICENSE +22 -0
- package/licenses/github.com_jackc_puddle_v2_v2.2.2-LICENSE +22 -0
- package/licenses/github.com_mattn_go-isatty_v0.0.20-LICENSE +9 -0
- package/licenses/github.com_modelcontextprotocol_go-sdk_v1.8.0-LICENSE +216 -0
- package/licenses/github.com_ncruces_go-strftime_v1.0.0-LICENSE +21 -0
- package/licenses/github.com_remyoudompheng_bigfft_v0.0.0-20230129092748-24d4a6f8daec-LICENSE +27 -0
- package/licenses/github.com_santhosh-tekuri_jsonschema_v6_v6.0.3-LICENSE +175 -0
- package/licenses/github.com_segmentio_asm_v1.1.3-LICENSE +21 -0
- package/licenses/github.com_segmentio_encoding_v0.5.4-LICENSE +21 -0
- package/licenses/github.com_tetratelabs_wazero_v1.12.0-LICENSE +201 -0
- package/licenses/github.com_tetratelabs_wazero_v1.12.0-NOTICE +2 -0
- package/licenses/github.com_yosida95_uritemplate_v3_v3.0.2-LICENSE +25 -0
- package/licenses/golang.org_x_image_v0.44.0-LICENSE +27 -0
- package/licenses/golang.org_x_net_v0.58.0-LICENSE +27 -0
- package/licenses/golang.org_x_oauth2_v0.36.0-LICENSE +27 -0
- package/licenses/golang.org_x_sync_v0.22.0-LICENSE +27 -0
- package/licenses/golang.org_x_sys_v0.47.0-LICENSE +27 -0
- package/licenses/golang.org_x_text_v0.41.0-LICENSE +27 -0
- package/licenses/golang.org_x_time_v0.15.0-LICENSE +27 -0
- package/licenses/guanbi-skills-LICENSE +133 -0
- package/licenses/guanbi-skills-LICENSE.README.md +28 -0
- package/licenses/guanbi-skills-LICENSE.zh-CN +82 -0
- package/licenses/jq-COPYING +176 -0
- package/licenses/modernc.org_libc_v1.73.4-LICENSE +27 -0
- package/licenses/modernc.org_libc_v1.73.4-LICENSE-3RD-PARTY.md +305 -0
- package/licenses/modernc.org_mathutil_v1.7.1-LICENSE +27 -0
- package/licenses/modernc.org_memory_v1.11.0-LICENSE +27 -0
- package/licenses/modernc.org_memory_v1.11.0-LICENSE-GO +27 -0
- package/licenses/modernc.org_memory_v1.11.0-LICENSE-LOGO +1 -0
- package/licenses/modernc.org_memory_v1.11.0-LICENSE-MMAP-GO +25 -0
- package/licenses/modernc.org_sqlite_v1.53.0-LICENSE +26 -0
- package/licenses/mvdan.cc_sh_v3_v3.13.1-LICENSE +27 -0
- package/licenses/ripgrep-COPYING +3 -0
- package/licenses/ripgrep-LICENSE-MIT +21 -0
- package/licenses/sed-LICENSE +21 -0
- package/manifest.json +972 -0
- package/package.json +30 -4
- package/plugin/guancopilot.js +193 -0
- package/skills-bundle/.guanvm-skills-bundle +0 -0
- package/skills-bundle/_recipes/dashboard/gates/page_check.js +16 -0
- package/skills-bundle/_recipes/dashboard/gates/report_check.js +4 -0
- package/skills-bundle/_recipes/dashboard/knowledge/01.md +1 -0
- package/skills-bundle/_recipes/dashboard/knowledge/02.md +1 -0
- package/skills-bundle/_recipes/dashboard/knowledge/03.md +1 -0
- package/skills-bundle/_recipes/dashboard/knowledge/04.md +1 -0
- package/skills-bundle/_recipes/dashboard/knowledge/05.md +1 -0
- package/skills-bundle/_recipes/dashboard/knowledge/06.md +1 -0
- package/skills-bundle/_recipes/dashboard/template.json +41 -0
- package/skills-bundle/_recipes/etl/gates/etl_live_check.js +10 -0
- package/skills-bundle/_recipes/etl/gates/etl_report_check.js +3 -0
- package/skills-bundle/_recipes/etl/knowledge/01.md +1 -0
- package/skills-bundle/_recipes/etl/knowledge/02.md +9 -0
- package/skills-bundle/_recipes/etl/knowledge/03.md +1 -0
- package/skills-bundle/_recipes/etl/knowledge/04.md +1 -0
- package/skills-bundle/_recipes/etl/knowledge/05.md +1 -0
- package/skills-bundle/_recipes/etl/template.json +39 -0
- package/skills-bundle/_recipes/hints.json +46 -0
- package/skills-bundle/guancli/.commandface.json +155 -0
- package/skills-bundle/guancli/SKILL.md +342 -0
- package/skills-bundle/guancli/evals/evals.json +156 -0
- package/skills-bundle/guancli/evals/fixtures/metric-candidate-empty-search.json +13 -0
- package/skills-bundle/guancli/evals/fixtures/metric-candidate-prior-request.json +13 -0
- package/skills-bundle/guancli/evals/fixtures/metric-consumption-columns.json +46 -0
- package/skills-bundle/guancli/evals/fixtures/metric-consumption-metadata.json +36 -0
- package/skills-bundle/guancli/evals/fixtures/metric-consumption-query.json +18 -0
- package/skills-bundle/guancli/references/analysis-primitives.md +95 -0
- package/skills-bundle/guancli/references/auth-and-config.md +131 -0
- package/skills-bundle/guancli/references/examples/metric-date-in.json +7 -0
- package/skills-bundle/guancli/references/examples/metric-query-rows.py +83 -0
- package/skills-bundle/guancli/references/examples/metric-query.sh +6 -0
- package/skills-bundle/guancli/references/form-chatbi.md +199 -0
- package/skills-bundle/guancli/references/gateway.md +148 -0
- package/skills-bundle/guancli/references/metric-batch-query.md +82 -0
- package/skills-bundle/guancli/references/metric-commands.md +562 -0
- package/skills-bundle/guancli/references/metric-generic-query.md +215 -0
- package/skills-bundle/guancli/references/metric-json-migration.md +18 -0
- package/skills-bundle/guancli/references/metric-query-recipes.md +358 -0
- package/skills-bundle/guancli/references/preview-and-output.md +192 -0
- package/skills-bundle/guancli/references/resource-query.md +101 -0
- package/skills-bundle/guands/SKILL.md +281 -0
- package/skills-bundle/guands/references/calc-fields-boundaries-and-pitfalls.md +160 -0
- package/skills-bundle/guands/references/command-reference.md +267 -0
- package/skills-bundle/guands/references/incremental-and-schedule.md +157 -0
- package/skills-bundle/guands/references/workflows.md +256 -0
- package/skills-bundle/guanetl/SKILL.md +391 -0
- package/skills-bundle/guanetl/evals/evals.json +41 -0
- package/skills-bundle/guanetl/references/ETL_AI_DEVELOP.md +456 -0
- package/skills-bundle/guanmetric/SKILL.md +144 -0
- package/skills-bundle/guanmetric/evals/evals.json +117 -0
- package/skills-bundle/guanmetric/evals/fixtures/atomic-detail-read-model.json +18 -0
- package/skills-bundle/guanmetric/evals/fixtures/confirmed-template-summary.json +12 -0
- package/skills-bundle/guanmetric/evals/fixtures/create-result-unknown-state.json +13 -0
- package/skills-bundle/guanmetric/evals/forward-eval-2026-09-21.json +62 -0
- package/skills-bundle/guanmetric/references/metric-api-contract-examples.json +690 -0
- package/skills-bundle/guanmetric/references/metric-api-contract.schema.json +349 -0
- package/skills-bundle/guanmetric/references/metric-api-contracts.md +436 -0
- package/skills-bundle/guanmetric/references/metric-atomic-mutation.md +238 -0
- package/skills-bundle/guanmetric/references/metric-batch-status.md +71 -0
- package/skills-bundle/guanmetric/references/metric-business-dictionary-api-contract-examples.json +53 -0
- package/skills-bundle/guanmetric/references/metric-business-dictionary-api-contract.schema.json +26 -0
- package/skills-bundle/guanmetric/references/metric-business-dictionary.md +76 -0
- package/skills-bundle/guanmetric/references/metric-common-fields.md +246 -0
- package/skills-bundle/guanmetric/references/metric-composite-mutation.md +165 -0
- package/skills-bundle/guanmetric/references/metric-delete-mutation.md +70 -0
- package/skills-bundle/guanmetric/references/metric-derived-mutation.md +453 -0
- package/skills-bundle/guanmetric/references/metric-excel-template.md +365 -0
- package/skills-bundle/guanmetric/references/metric-expression-rules.md +146 -0
- package/skills-bundle/guanmetric/references/metric-flow-generate-example.json +79 -0
- package/skills-bundle/guanmetric/references/metric-flow-generate.schema.json +93 -0
- package/skills-bundle/guanmetric/references/metric-flow.md +218 -0
- package/skills-bundle/guanmetric/references/metric-hierarchy-api-contract-examples.json +171 -0
- package/skills-bundle/guanmetric/references/metric-hierarchy-api-contract.schema.json +167 -0
- package/skills-bundle/guanmetric/references/metric-hierarchy-api-contracts.md +193 -0
- package/skills-bundle/guanmetric/references/metric-mutation.md +155 -0
- package/skills-bundle/guanmetric/references/metric-public-dimension-api-contract-examples.json +62 -0
- package/skills-bundle/guanmetric/references/metric-public-dimension-api-contract.schema.json +47 -0
- package/skills-bundle/guanmetric/references/metric-public-dimension.md +155 -0
- package/skills-bundle/guanmetric/references/metric-tree-api-contract-examples.json +277 -0
- package/skills-bundle/guanmetric/references/metric-tree-api-contract.schema.json +263 -0
- package/skills-bundle/guanmetric/references/metric-tree-api-contracts.md +53 -0
- package/skills-bundle/guanmetric/references/metric-tree-mutation.md +500 -0
- package/skills-bundle/guanmetric/references/metric-validation-ownership.md +74 -0
- package/skills-bundle/guanvis/SKILL.md +333 -0
- package/skills-bundle/guanvis/evals/china_map/card_01_basic_map.js +13 -0
- package/skills-bundle/guanvis/evals/china_map/card_02_world_map.js +10 -0
- package/skills-bundle/guanvis/evals/china_map/card_03_point_map.js +12 -0
- package/skills-bundle/guanvis/evals/china_map/page.js +10 -0
- package/skills-bundle/guanvis/evals/china_map/schema.js +10 -0
- package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/README.md +30 -0
- package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/card_01_comparative_mvp.js +72 -0
- package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/page.js +6 -0
- package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/schema.js +22 -0
- package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/verify_default_roundtrip.sh +92 -0
- package/skills-bundle/guanvis/evals/complex_report_pro/card_01_monthly.js +63 -0
- package/skills-bundle/guanvis/evals/complex_report_pro/page.js +6 -0
- package/skills-bundle/guanvis/evals/complex_report_pro/schema.js +6 -0
- package/skills-bundle/guanvis/evals/custom_chart_echarts/card_01_bar.js +13 -0
- package/skills-bundle/guanvis/evals/custom_chart_echarts/card_02_pie.js +13 -0
- package/skills-bundle/guanvis/evals/custom_chart_echarts/charts/bar.js +11 -0
- package/skills-bundle/guanvis/evals/custom_chart_echarts/charts/pie.js +15 -0
- package/skills-bundle/guanvis/evals/custom_chart_echarts/page.js +5 -0
- package/skills-bundle/guanvis/evals/custom_chart_echarts/schema.js +9 -0
- package/skills-bundle/guanvis/evals/custom_chart_sdk/card_01_level_nodes.js +19 -0
- package/skills-bundle/guanvis/evals/custom_chart_sdk/charts/level_nodes.css +51 -0
- package/skills-bundle/guanvis/evals/custom_chart_sdk/charts/level_nodes.html +1 -0
- package/skills-bundle/guanvis/evals/custom_chart_sdk/charts/level_nodes.js +28 -0
- package/skills-bundle/guanvis/evals/custom_chart_sdk/page.js +5 -0
- package/skills-bundle/guanvis/evals/custom_chart_sdk/schema.js +7 -0
- package/skills-bundle/guanvis/evals/dynamic_fields/card_01_dynamic_dataset.js +20 -0
- package/skills-bundle/guanvis/evals/dynamic_fields/card_02_dynamic_metric_chart.js +16 -0
- package/skills-bundle/guanvis/evals/dynamic_fields/metrics.js +19 -0
- package/skills-bundle/guanvis/evals/dynamic_fields/page.js +9 -0
- package/skills-bundle/guanvis/evals/dynamic_fields/schema.js +7 -0
- package/skills-bundle/guanvis/evals/dynamic_fields/verify_default_roundtrip.sh +137 -0
- package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/README.md +26 -0
- package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/card_01_percentage_advcalc.js +44 -0
- package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/card_02_scroll_percentage.js +12 -0
- package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/page.js +6 -0
- package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/schema.js +22 -0
- package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/README.md +28 -0
- package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/card_01_rank_advcalc.js +59 -0
- package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/page.js +6 -0
- package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/schema.js +22 -0
- package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/verify_default_roundtrip.sh +135 -0
- package/skills-bundle/guanvis/evals/running_total_advcalc_roundtrip/README.md +15 -0
- package/skills-bundle/guanvis/evals/running_total_advcalc_roundtrip/card_01_running_total_advcalc.js +74 -0
- package/skills-bundle/guanvis/evals/running_total_advcalc_roundtrip/page.js +6 -0
- package/skills-bundle/guanvis/evals/running_total_advcalc_roundtrip/schema.js +22 -0
- package/skills-bundle/guanvis/evals/sales_dashboard/card_01_revenue_column.js +10 -0
- package/skills-bundle/guanvis/evals/sales_dashboard/card_02_trend_line.js +9 -0
- package/skills-bundle/guanvis/evals/sales_dashboard/card_03_kpi.js +6 -0
- package/skills-bundle/guanvis/evals/sales_dashboard/card_04_pie.js +9 -0
- package/skills-bundle/guanvis/evals/sales_dashboard/card_05_calc_field_test.js +12 -0
- package/skills-bundle/guanvis/evals/sales_dashboard/page.js +11 -0
- package/skills-bundle/guanvis/evals/sales_dashboard/schema.js +14 -0
- package/skills-bundle/guanvis/evals/sales_dashboard/selector_01_region.js +10 -0
- package/skills-bundle/guanvis/evals/split_charts/card_01_column_split.js +10 -0
- package/skills-bundle/guanvis/evals/split_charts/card_02_line_split.js +10 -0
- package/skills-bundle/guanvis/evals/split_charts/card_03_combo_split.js +11 -0
- package/skills-bundle/guanvis/evals/split_charts/page.js +10 -0
- package/skills-bundle/guanvis/evals/split_charts/schema.js +14 -0
- package/skills-bundle/guanvis/evals/tab_layout/card_00_total_revenue.js +6 -0
- package/skills-bundle/guanvis/evals/tab_layout/card_01_revenue.js +10 -0
- package/skills-bundle/guanvis/evals/tab_layout/card_02_trend.js +9 -0
- package/skills-bundle/guanvis/evals/tab_layout/card_03_story.js +11 -0
- package/skills-bundle/guanvis/evals/tab_layout/page.js +23 -0
- package/skills-bundle/guanvis/evals/tab_layout/schema.js +11 -0
- package/skills-bundle/guanvis/evals/tab_layout/selector_01_region.js +8 -0
- package/skills-bundle/guanvis/references/api-reference.md +476 -0
- package/skills-bundle/guanvis/references/builder-reference.md +1676 -0
- package/skills-bundle/guanvis/references/chart-properties.md +2101 -0
- package/skills-bundle/guanvis/references/checkout-editing.md +53 -0
- package/skills-bundle/guanvis/references/complex-report-pro-patterns.md +398 -0
- package/skills-bundle/guanvis/references/dir-and-page-management.md +48 -0
- package/skills-bundle/guanvis/references/metric-chart-reference.md +258 -0
- package/skills-bundle/guanvis/references/publish-and-constraints.md +60 -0
- package/skills-bundle/guanvis/references/selector-reference.md +431 -0
- package/skills-bundle/guanvis/references/tableau-migration.md +73 -0
- package/skills-bundle/guanvis/references/theme.md +123 -0
- package/skills-bundle/guanvis/references/troubleshooting.md +25 -0
- package/skills-bundle/guanvis/references/validation-and-chart-patterns.md +205 -0
- package/skills-bundle/guanwf/SKILL.md +543 -0
- package/skills-bundle/guanwf/references/ETL_AI_DEVELOP.md +456 -0
- package/skills-bundle/guanwf/references/TASK_NODE_CONTRACTS.md +64 -0
- package/skills-bundle/guanwf/references/WORKFLOW_DSL.md +536 -0
- package/skills-bundle/guanwf/references/WORKFLOW_NODES.md +166 -0
- package/skills-bundle/js/SKILL.md +145 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
5
|
+
EVAL_DIR="$ROOT_DIR/evals/comparative_mvp_roundtrip"
|
|
6
|
+
OUT_DIR="$EVAL_DIR/.roundtrip"
|
|
7
|
+
|
|
8
|
+
mkdir -p "$OUT_DIR"
|
|
9
|
+
|
|
10
|
+
cd "$ROOT_DIR"
|
|
11
|
+
|
|
12
|
+
go run ./cmd/guanvis preview "$EVAL_DIR" > "$OUT_DIR/preview_payload.json"
|
|
13
|
+
go run ./cmd/guanvis publish "$EVAL_DIR" > "$OUT_DIR/publish.log"
|
|
14
|
+
|
|
15
|
+
guancli card get p3bc51ab664e095ab2dff402 --profile default --raw > "$OUT_DIR/raw_normal.json"
|
|
16
|
+
guancli card get o360e50afb73bcc998353c13 --profile default --raw > "$OUT_DIR/raw_filter_based.json"
|
|
17
|
+
guancli card preview p3bc51ab664e095ab2dff402 --profile default --limit 50 -f json > "$OUT_DIR/preview_normal.json"
|
|
18
|
+
guancli card preview o360e50afb73bcc998353c13 --profile default --limit 50 -f json > "$OUT_DIR/preview_filter_based.json"
|
|
19
|
+
|
|
20
|
+
python3 - "$OUT_DIR" <<'PY'
|
|
21
|
+
import json
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
out = Path(sys.argv[1])
|
|
26
|
+
|
|
27
|
+
cases = [
|
|
28
|
+
{
|
|
29
|
+
"kind": "normal",
|
|
30
|
+
"raw": out / "raw_normal.json",
|
|
31
|
+
"preview": out / "preview_normal.json",
|
|
32
|
+
"expected_aliases": [
|
|
33
|
+
"月同比对比值",
|
|
34
|
+
"季同比增长值",
|
|
35
|
+
"年同比增长率",
|
|
36
|
+
"上月末增长率",
|
|
37
|
+
"上季度末增长值",
|
|
38
|
+
"上年末对比值",
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"kind": "filter_based",
|
|
43
|
+
"raw": out / "raw_filter_based.json",
|
|
44
|
+
"preview": out / "preview_filter_based.json",
|
|
45
|
+
"expected_aliases": [
|
|
46
|
+
"日环比增长值",
|
|
47
|
+
"周同比增长率",
|
|
48
|
+
"默认年同比增长率",
|
|
49
|
+
"年同比按周对比值",
|
|
50
|
+
"自定义后移增长率",
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
summary = {"cards": []}
|
|
56
|
+
|
|
57
|
+
for case in cases:
|
|
58
|
+
raw_obj = json.loads(case["raw"].read_text())
|
|
59
|
+
response = raw_obj.get("response") or raw_obj.get("data") or raw_obj
|
|
60
|
+
zone_data = response["content"]["meta"]["chartMain"]["zoneData"]
|
|
61
|
+
metrics = zone_data.get("metric", [])
|
|
62
|
+
by_alias = {m.get("alias"): m for m in metrics if m.get("alias")}
|
|
63
|
+
rows = json.loads(case["preview"].read_text())
|
|
64
|
+
|
|
65
|
+
card_summary = {
|
|
66
|
+
"kind": case["kind"],
|
|
67
|
+
"cardId": response.get("cdId") or response.get("id"),
|
|
68
|
+
"name": response.get("name"),
|
|
69
|
+
"previewRows": len(rows),
|
|
70
|
+
"aliases": {},
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for alias in case["expected_aliases"]:
|
|
74
|
+
metric = by_alias.get(alias)
|
|
75
|
+
if metric is None:
|
|
76
|
+
raise SystemExit(f"{case['kind']}: missing metric alias {alias}")
|
|
77
|
+
if "formula" in metric:
|
|
78
|
+
raise SystemExit(f"{case['kind']}: {alias} degraded to formula")
|
|
79
|
+
adv_calc = metric.get("advCalc")
|
|
80
|
+
if not adv_calc:
|
|
81
|
+
raise SystemExit(f"{case['kind']}: {alias} missing advCalc")
|
|
82
|
+
if adv_calc.get("advType") != "COMPARATIVE":
|
|
83
|
+
raise SystemExit(f"{case['kind']}: {alias} advType is not COMPARATIVE")
|
|
84
|
+
if not any(isinstance(row, dict) and row.get(alias) not in ("", None) for row in rows):
|
|
85
|
+
raise SystemExit(f"{case['kind']}: {alias} has no non-empty preview value")
|
|
86
|
+
card_summary["aliases"][alias] = adv_calc["advValue"]
|
|
87
|
+
|
|
88
|
+
summary["cards"].append(card_summary)
|
|
89
|
+
|
|
90
|
+
(out / "summary.json").write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
91
|
+
print(json.dumps(summary, ensure_ascii=False, indent=2))
|
|
92
|
+
PY
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var salesView = createCard(ChartType.DATA_GRID, "sales")
|
|
2
|
+
.setId("crpchildsales00000000001")
|
|
3
|
+
.bindDataset(DS)
|
|
4
|
+
.addRow(f("区域"))
|
|
5
|
+
.addRow(f("门店"))
|
|
6
|
+
.addMetric(f("销售额", { aggrType: AggrType.SUM }))
|
|
7
|
+
.addMetric(f("利润", { aggrType: AggrType.SUM }));
|
|
8
|
+
|
|
9
|
+
var workbook = createReportWorkbook()
|
|
10
|
+
.addSheet("经营月报", function(sheet) {
|
|
11
|
+
sheet.setColumnWidth("A", 18)
|
|
12
|
+
.setColumnWidth("B", 18)
|
|
13
|
+
.setColumnWidth("C", 16)
|
|
14
|
+
.setColumnWidth("D", 16)
|
|
15
|
+
.setRowHeight(1, 28)
|
|
16
|
+
.merge("A1:D1")
|
|
17
|
+
.setValue("A1", "门店经营月报", {
|
|
18
|
+
fontSize: 16,
|
|
19
|
+
bold: true,
|
|
20
|
+
horizontalAlignment: "center",
|
|
21
|
+
verticalAlignment: "center",
|
|
22
|
+
backgroundColor: "#D9EAF7",
|
|
23
|
+
borderColor: "#8AA8BF"
|
|
24
|
+
})
|
|
25
|
+
.setValue("A2", "区域", { bold: true, backgroundColor: "#EEF5FA", borderColor: "#8AA8BF" })
|
|
26
|
+
.setValue("B2", "门店", { bold: true, backgroundColor: "#EEF5FA", borderColor: "#8AA8BF" })
|
|
27
|
+
.setValue("C2", "销售额", { bold: true, backgroundColor: "#EEF5FA", borderColor: "#8AA8BF" })
|
|
28
|
+
.setValue("D2", "利润", { bold: true, backgroundColor: "#EEF5FA", borderColor: "#8AA8BF" })
|
|
29
|
+
.bindDimension("A3", reportField("sales", "区域"), {
|
|
30
|
+
expansion: "vertical",
|
|
31
|
+
group: "merge",
|
|
32
|
+
style: { borderColor: "#D0D7DE" }
|
|
33
|
+
})
|
|
34
|
+
.bindDimension("B3", reportField("sales", "门店"), {
|
|
35
|
+
expansion: "vertical",
|
|
36
|
+
context: "A3",
|
|
37
|
+
style: { borderColor: "#D0D7DE" }
|
|
38
|
+
})
|
|
39
|
+
.bindMetric("C3", reportField("sales", "销售额"), {
|
|
40
|
+
aggregate: "SUM",
|
|
41
|
+
expansion: "vertical",
|
|
42
|
+
context: "B3",
|
|
43
|
+
numberFormat: "#,##0.00",
|
|
44
|
+
style: { borderColor: "#D0D7DE" }
|
|
45
|
+
})
|
|
46
|
+
.bindMetric("D3", reportField("sales", "利润"), {
|
|
47
|
+
aggregate: "SUM",
|
|
48
|
+
expansion: "vertical",
|
|
49
|
+
context: "B3",
|
|
50
|
+
numberFormat: "#,##0.00",
|
|
51
|
+
style: { borderColor: "#D0D7DE" }
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
var report = createComplexReportPro("门店经营月报 Pro")
|
|
56
|
+
.setId("crpparentmonthly00000001")
|
|
57
|
+
.setDescription("GuanVis 结构化工作簿 DSL 生成的复杂报表 Pro")
|
|
58
|
+
.setWorkbook(workbook)
|
|
59
|
+
.addDataView("sales", salesView)
|
|
60
|
+
.setPagination(false)
|
|
61
|
+
.setColumnWidthStretch(true);
|
|
62
|
+
|
|
63
|
+
registerComplexReportPro(report.build());
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
defineDataset("complex_report_sales", [
|
|
2
|
+
{ fdId: "region_fd", name: "区域", fdType: "STRING", metaType: "DIM" },
|
|
3
|
+
{ fdId: "store_fd", name: "门店", fdType: "STRING", metaType: "DIM" },
|
|
4
|
+
{ fdId: "sales_fd", name: "销售额", fdType: "DOUBLE", metaType: "METRIC" },
|
|
5
|
+
{ fdId: "profit_fd", name: "利润", fdType: "DOUBLE", metaType: "METRIC" }
|
|
6
|
+
], { displayType: "CSV" });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
var dataView = createCard(ChartType.DATA_GRID, "区域营收数据")
|
|
2
|
+
.setId("a000000000000000000child")
|
|
3
|
+
.bindDataset(DS)
|
|
4
|
+
.addRow(f("区域"))
|
|
5
|
+
.addMetric(f("营收", { aggrType: AggrType.SUM }));
|
|
6
|
+
|
|
7
|
+
var chart = createCustomChart("区域营收柱状图")
|
|
8
|
+
.setId("a000000000000000000chart")
|
|
9
|
+
.setSubType(CustomChartSubType.ECHARTS_LITE)
|
|
10
|
+
.loadContent("charts/bar")
|
|
11
|
+
.addDataView(dataView);
|
|
12
|
+
|
|
13
|
+
registerCustomChart(chart.build());
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
var dataView = createCard(ChartType.DATA_GRID, "产品成本数据")
|
|
2
|
+
.setId("b000000000000000000child")
|
|
3
|
+
.bindDataset(DS)
|
|
4
|
+
.addRow(f("产品"))
|
|
5
|
+
.addMetric(f("成本", { aggrType: AggrType.SUM }));
|
|
6
|
+
|
|
7
|
+
var chart = createCustomChart("产品成本饼图")
|
|
8
|
+
.setId("b000000000000000000chart")
|
|
9
|
+
.setSubType(CustomChartSubType.ECHARTS_LITE)
|
|
10
|
+
.loadContent("charts/pie")
|
|
11
|
+
.addDataView(dataView);
|
|
12
|
+
|
|
13
|
+
registerCustomChart(chart.build());
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var cols = data[0] || [];
|
|
2
|
+
var categories = (cols[0] || {}).data || [];
|
|
3
|
+
var values = (cols[1] || {}).data || [];
|
|
4
|
+
|
|
5
|
+
option = {
|
|
6
|
+
tooltip: { trigger: "axis" },
|
|
7
|
+
xAxis: { type: "category", data: categories },
|
|
8
|
+
yAxis: { type: "value" },
|
|
9
|
+
series: [{ type: "bar", data: values }],
|
|
10
|
+
color: config.colors
|
|
11
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
var cols = data[0] || [];
|
|
2
|
+
var names = (cols[0] || {}).data || [];
|
|
3
|
+
var values = (cols[1] || {}).data || [];
|
|
4
|
+
|
|
5
|
+
option = {
|
|
6
|
+
tooltip: { trigger: "item" },
|
|
7
|
+
legend: { bottom: 10 },
|
|
8
|
+
series: [{
|
|
9
|
+
type: "pie",
|
|
10
|
+
radius: ["30%", "60%"],
|
|
11
|
+
data: names.map(function(name, i) { return { name: name, value: values[i] }; }),
|
|
12
|
+
label: { formatter: "{b}: {d}%" }
|
|
13
|
+
}],
|
|
14
|
+
color: config.colors
|
|
15
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Auto-generated by guanvis schema command
|
|
2
|
+
// DO NOT EDIT — regenerate with: guanvis schema s9ded2338f43807b095fbb4f
|
|
3
|
+
|
|
4
|
+
defineDataset("s9ded2338f43807b095fbb4f", [
|
|
5
|
+
{ fdId: "jb16f22f6c1c21c3f6e8e293", name: "区域", fdType: "STRING", metaType: "DIM" },
|
|
6
|
+
{ fdId: "o7093aaf2b2f32c9e964c41a", name: "产品", fdType: "STRING", metaType: "DIM" },
|
|
7
|
+
{ fdId: "lb1c31b4dc86511a27049a98", name: "营收", fdType: "DOUBLE", metaType: "METRIC" },
|
|
8
|
+
{ fdId: "aece10cd6806966be56f52ff", name: "成本", fdType: "DOUBLE", metaType: "METRIC" }
|
|
9
|
+
], { displayType: "CSV" });
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// 自定义图表 SDK 模式最小示例:横向等级进度节点图(自定义 HTML/CSS + GDPlugin)。
|
|
2
|
+
// 适用场景:内置图表做不出来的自定义 DOM 布局(节点图/进度轴/卡片流等);
|
|
3
|
+
// 纯 ECharts 需求改用 CustomChartSubType.ECHARTS_LITE(见 evals/custom_chart_echarts/)。
|
|
4
|
+
var dataView = createCard(ChartType.DATA_GRID, "等级数据视图")
|
|
5
|
+
.setId("a000000000000000sdkchild")
|
|
6
|
+
.bindDataset(DS)
|
|
7
|
+
.addRow(f("等级"))
|
|
8
|
+
.addMetric(f("占比", { aggrType: AggrType.SUM }))
|
|
9
|
+
// 节点顺序必须显式排序,否则依赖查询返回顺序;数据集有序号字段时优先按序号排
|
|
10
|
+
.addSort(f("等级", { sortType: SortOrder.ASC }));
|
|
11
|
+
|
|
12
|
+
var chart = createCustomChart("等级进度节点图")
|
|
13
|
+
.setId("a000000000000000sdkchart")
|
|
14
|
+
.setSubType(CustomChartSubType.SDK)
|
|
15
|
+
.loadContent("charts/level_nodes")
|
|
16
|
+
.addDataView(dataView);
|
|
17
|
+
|
|
18
|
+
// 自定义图表必须用 registerCustomChart;registerCard 会在 preview 时报类型错配。
|
|
19
|
+
registerCustomChart(chart.build());
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/* 横向节点+连接线布局范式:
|
|
2
|
+
* 圆点和连接线放进同一个固定高度的 .circle-area,连接线绝对定位、top 对准圆心。
|
|
3
|
+
* 把连接线做成与节点同级的独立 flex 项无法与圆心垂直对齐(客户实测踩坑)。 */
|
|
4
|
+
#container {
|
|
5
|
+
width: 100%;
|
|
6
|
+
height: 100%;
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
11
|
+
}
|
|
12
|
+
.node {
|
|
13
|
+
flex: 1;
|
|
14
|
+
max-width: 160px;
|
|
15
|
+
text-align: center;
|
|
16
|
+
}
|
|
17
|
+
.node .name {
|
|
18
|
+
font-size: 14px;
|
|
19
|
+
font-weight: 600;
|
|
20
|
+
margin-bottom: 8px;
|
|
21
|
+
}
|
|
22
|
+
.node .circle-area {
|
|
23
|
+
position: relative;
|
|
24
|
+
height: 16px; /* 与圆点直径一致,连接线以此垂直居中 */
|
|
25
|
+
}
|
|
26
|
+
.node .dot {
|
|
27
|
+
width: 16px;
|
|
28
|
+
height: 16px;
|
|
29
|
+
border-radius: 50%;
|
|
30
|
+
background: #4575b4;
|
|
31
|
+
margin: 0 auto;
|
|
32
|
+
position: relative;
|
|
33
|
+
z-index: 1;
|
|
34
|
+
}
|
|
35
|
+
/* 连接线:从本节点圆心向右延伸到下一节点圆心 */
|
|
36
|
+
.node .connector {
|
|
37
|
+
position: absolute;
|
|
38
|
+
top: 7px; /* (circle-area 高 16 - 线高 2) / 2 */
|
|
39
|
+
left: 50%;
|
|
40
|
+
width: 100%;
|
|
41
|
+
height: 2px;
|
|
42
|
+
background: #c3d4e8;
|
|
43
|
+
}
|
|
44
|
+
.node:last-child .connector {
|
|
45
|
+
display: none;
|
|
46
|
+
}
|
|
47
|
+
.node .value {
|
|
48
|
+
font-size: 13px;
|
|
49
|
+
color: #666;
|
|
50
|
+
margin-top: 8px;
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<div id="container"></div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// SDK 模式渲染代码:实现 renderChart 并用 new GDPlugin().init(renderChart) 挂载。
|
|
2
|
+
// data[i] 对应第 i 个数据视图的列数据: [{name:"字段名", data:[...]}, ...]
|
|
3
|
+
// config.colors 是主题色数组。联动回传(仅在 dataView 配置了 .linkTo() 时需要):
|
|
4
|
+
// clickFunc({ clickedItems: [{ idx: [viewIndex, columnIndex], colName: "列名", value: ["选中值"] }] })
|
|
5
|
+
function renderChart(data, clickFunc, config) {
|
|
6
|
+
var cols = data[0] || [];
|
|
7
|
+
var names = (cols[0] || {}).data || [];
|
|
8
|
+
var values = (cols[1] || {}).data || [];
|
|
9
|
+
var color = (config && config.colors && config.colors[0]) || "#4575b4";
|
|
10
|
+
|
|
11
|
+
var container = document.getElementById("container");
|
|
12
|
+
container.innerHTML = "";
|
|
13
|
+
for (var i = 0; i < names.length; i++) {
|
|
14
|
+
var node = document.createElement("div");
|
|
15
|
+
node.className = "node";
|
|
16
|
+
// 量纲以 guancli ds preview 看到的真实数据为准:0~1 小数才需要 ×100
|
|
17
|
+
var pct = (Number(values[i]) * 100).toFixed(1) + "%";
|
|
18
|
+
node.innerHTML =
|
|
19
|
+
'<div class="name"></div>' +
|
|
20
|
+
'<div class="circle-area"><div class="connector"></div><div class="dot"></div></div>' +
|
|
21
|
+
'<div class="value"></div>';
|
|
22
|
+
node.querySelector(".name").textContent = String(names[i]);
|
|
23
|
+
node.querySelector(".dot").style.background = color;
|
|
24
|
+
node.querySelector(".value").textContent = pct;
|
|
25
|
+
container.appendChild(node);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
new GDPlugin().init(renderChart);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Auto-generated by guanvis schema command
|
|
2
|
+
// DO NOT EDIT — regenerate with: guanvis schema <dsId>
|
|
3
|
+
|
|
4
|
+
defineDataset("s0000000000000000000demo", [
|
|
5
|
+
{ fdId: "f0000000000000000000lvl1", name: "等级", fdType: "STRING", metaType: "DIM" },
|
|
6
|
+
{ fdId: "f0000000000000000000rat2", name: "占比", fdType: "DOUBLE", metaType: "METRIC" }
|
|
7
|
+
], { displayType: "CSV" });
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
var region = f("区域");
|
|
2
|
+
var city = f("城市");
|
|
3
|
+
var sales = f("销售额", { aggrType: AggrType.SUM });
|
|
4
|
+
var profit = f("利润", { aggrType: AggrType.SUM });
|
|
5
|
+
|
|
6
|
+
var card = createCard(ChartType.GROUPED_COLUMN, "动态维度与动态数值")
|
|
7
|
+
.setId("dfcarddataset00000000000")
|
|
8
|
+
.bindDataset(DS)
|
|
9
|
+
.addDynamicRow("分析维度", [region, city], {
|
|
10
|
+
defaultValue: [region],
|
|
11
|
+
multiSelect: false
|
|
12
|
+
})
|
|
13
|
+
.addDynamicMetric("分析数值", [sales, profit], {
|
|
14
|
+
defaultValue: [sales],
|
|
15
|
+
multiSelect: true,
|
|
16
|
+
orderType: DynamicFieldOrder.CLICK
|
|
17
|
+
})
|
|
18
|
+
.setShowLegend(true, "bottom");
|
|
19
|
+
|
|
20
|
+
registerCard(card.build());
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
var region = metricDim("销售额", "区域");
|
|
2
|
+
var city = metricDim("销售额", "城市");
|
|
3
|
+
var sales = metric("销售额");
|
|
4
|
+
var profit = metric("利润");
|
|
5
|
+
|
|
6
|
+
var card = createMetricChart(ChartType.PIVOT_TABLE, "指标平台动态维度与动态指标")
|
|
7
|
+
.setId("dfmetriccard000000000000")
|
|
8
|
+
.addDynamicRow("分析维度", [region, city], {
|
|
9
|
+
defaultValue: [region]
|
|
10
|
+
})
|
|
11
|
+
.addDynamicMetric("分析指标", [sales, profit], {
|
|
12
|
+
defaultValue: [sales],
|
|
13
|
+
multiSelect: true
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
registerMetricChart(card.build());
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
defineMetric({
|
|
2
|
+
id: "metric_sales_eval_000001",
|
|
3
|
+
name: "销售额",
|
|
4
|
+
subType: "ATOMIC",
|
|
5
|
+
applicableDims: [
|
|
6
|
+
{ fdId: "region_fd", dsId: "ds_dynamic_fields_eval", name: "区域", fdType: "STRING", metaType: "DIM" },
|
|
7
|
+
{ fdId: "city_fd", dsId: "ds_dynamic_fields_eval", name: "城市", fdType: "STRING", metaType: "DIM" }
|
|
8
|
+
]
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
defineMetric({
|
|
12
|
+
id: "metric_profit_eval_00001",
|
|
13
|
+
name: "利润",
|
|
14
|
+
subType: "ATOMIC",
|
|
15
|
+
applicableDims: [
|
|
16
|
+
{ fdId: "region_fd", dsId: "ds_dynamic_fields_eval", name: "区域", fdType: "STRING", metaType: "DIM" },
|
|
17
|
+
{ fdId: "city_fd", dsId: "ds_dynamic_fields_eval", name: "城市", fdType: "STRING", metaType: "DIM" }
|
|
18
|
+
]
|
|
19
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
defineDataset("ds_dynamic_fields_eval", [
|
|
2
|
+
{ fdId: "region_fd", name: "区域", fdType: "STRING", metaType: "DIM" },
|
|
3
|
+
{ fdId: "city_fd", name: "城市", fdType: "STRING", metaType: "DIM" },
|
|
4
|
+
{ fdId: "month_fd", name: "月份", fdType: "DATE", metaType: "DIM" },
|
|
5
|
+
{ fdId: "sales_fd", name: "销售额", fdType: "DOUBLE", metaType: "METRIC" },
|
|
6
|
+
{ fdId: "profit_fd", name: "利润", fdType: "DOUBLE", metaType: "METRIC" }
|
|
7
|
+
], { displayType: "CSV" });
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
5
|
+
EVAL_DIR="$ROOT_DIR/evals/dynamic_fields"
|
|
6
|
+
OUT_DIR="$EVAL_DIR/.roundtrip"
|
|
7
|
+
|
|
8
|
+
mkdir -p "$OUT_DIR"
|
|
9
|
+
|
|
10
|
+
cd "$ROOT_DIR"
|
|
11
|
+
|
|
12
|
+
go run ./cmd/guanvis preview "$EVAL_DIR" > "$OUT_DIR/preview_payload.json"
|
|
13
|
+
go run ./cmd/guanvis pack "$EVAL_DIR" -o "$OUT_DIR/dynamic_fields_package.zip" > "$OUT_DIR/pack.log"
|
|
14
|
+
|
|
15
|
+
if [[ "${PUBLISH:-0}" == "1" ]]; then
|
|
16
|
+
go run ./cmd/guanvis publish "$EVAL_DIR" > "$OUT_DIR/publish.log"
|
|
17
|
+
guancli card get dfcarddataset00000000000 --raw > "$OUT_DIR/raw_dataset_card.json"
|
|
18
|
+
guancli card get dfmetriccard000000000000 --raw > "$OUT_DIR/raw_metric_card.json"
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
python3 - "$OUT_DIR" <<'PY'
|
|
22
|
+
import json
|
|
23
|
+
import sys
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
out = Path(sys.argv[1])
|
|
27
|
+
|
|
28
|
+
expected = {
|
|
29
|
+
"dfcarddataset00000000000": {
|
|
30
|
+
"name": "动态维度与动态数值",
|
|
31
|
+
"mappings": {
|
|
32
|
+
"分析维度": {"zoneId": "row", "metaType": "DIM", "multiSelect": False},
|
|
33
|
+
"分析数值": {"zoneId": "metric", "metaType": "METRIC", "multiSelect": True},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
"dfmetriccard000000000000": {
|
|
37
|
+
"name": "指标平台动态维度与动态指标",
|
|
38
|
+
"mappings": {
|
|
39
|
+
"分析维度": {"zoneId": "row", "metaType": "DIM", "multiSelect": False},
|
|
40
|
+
"分析指标": {"zoneId": "metric", "metaType": "METRIC", "multiSelect": True},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
def load_json(path):
|
|
46
|
+
text = path.read_text(encoding="utf-8")
|
|
47
|
+
start = text.find("{")
|
|
48
|
+
if start > 0:
|
|
49
|
+
text = text[start:]
|
|
50
|
+
return json.loads(text)
|
|
51
|
+
|
|
52
|
+
def normalize_card(raw):
|
|
53
|
+
obj = raw.get("response") or raw.get("data") or raw
|
|
54
|
+
if "card" in obj:
|
|
55
|
+
card = obj["card"]
|
|
56
|
+
meta = json.loads(card["meta"]) if isinstance(card.get("meta"), str) else card.get("meta")
|
|
57
|
+
return {"id": card.get("cdId"), "name": card.get("name"), "meta": meta}
|
|
58
|
+
content = obj.get("content") or obj
|
|
59
|
+
return {"id": obj.get("cdId") or obj.get("id"), "name": obj.get("name"), "meta": content.get("meta")}
|
|
60
|
+
|
|
61
|
+
def preview_cards():
|
|
62
|
+
payload = load_json(out / "preview_payload.json")
|
|
63
|
+
cards = []
|
|
64
|
+
for item in payload.get("cards", []):
|
|
65
|
+
card = item.get("card", {})
|
|
66
|
+
meta = json.loads(card["meta"]) if isinstance(card.get("meta"), str) else card.get("meta")
|
|
67
|
+
cards.append({"id": card.get("cdId"), "name": card.get("name"), "meta": meta})
|
|
68
|
+
return cards
|
|
69
|
+
|
|
70
|
+
def assert_dynamic_card(card):
|
|
71
|
+
card_id = card["id"]
|
|
72
|
+
if card_id not in expected:
|
|
73
|
+
return None
|
|
74
|
+
spec = expected[card_id]
|
|
75
|
+
chart_main = (card.get("meta") or {}).get("chartMain") or {}
|
|
76
|
+
zone_data = chart_main.get("zoneData") or {}
|
|
77
|
+
dz_info = chart_main.get("dynamicZoneInfo") or {}
|
|
78
|
+
if dz_info.get("hidden") is not False:
|
|
79
|
+
raise SystemExit(f"{card_id}: dynamicZoneInfo.hidden={dz_info.get('hidden')!r}, want false")
|
|
80
|
+
mappings = dz_info.get("dzMappings") or []
|
|
81
|
+
by_name = {m.get("name"): m for m in mappings}
|
|
82
|
+
if set(by_name) != set(spec["mappings"]):
|
|
83
|
+
raise SystemExit(f"{card_id}: dzMappings names={sorted(by_name)}, want {sorted(spec['mappings'])}")
|
|
84
|
+
|
|
85
|
+
summary = {"cardId": card_id, "name": card.get("name"), "mappings": {}}
|
|
86
|
+
for name, want in spec["mappings"].items():
|
|
87
|
+
mapping = by_name[name]
|
|
88
|
+
for key, want_value in want.items():
|
|
89
|
+
got = mapping.get(key)
|
|
90
|
+
if got != want_value:
|
|
91
|
+
raise SystemExit(f"{card_id}: mapping {name} {key}={got!r}, want {want_value!r}")
|
|
92
|
+
if mapping.get("selectType") != "SEARCHBOX":
|
|
93
|
+
raise SystemExit(f"{card_id}: mapping {name} selectType={mapping.get('selectType')!r}, want SEARCHBOX")
|
|
94
|
+
if mapping.get("orderType") not in ("PRESET", "CLICK"):
|
|
95
|
+
raise SystemExit(f"{card_id}: mapping {name} invalid orderType={mapping.get('orderType')!r}")
|
|
96
|
+
dz_id = mapping.get("dzId")
|
|
97
|
+
defaults = mapping.get("defaultValue") or []
|
|
98
|
+
if not dz_id:
|
|
99
|
+
raise SystemExit(f"{card_id}: mapping {name} missing dzId")
|
|
100
|
+
if not defaults:
|
|
101
|
+
raise SystemExit(f"{card_id}: mapping {name} defaultValue is empty")
|
|
102
|
+
zone_fields = zone_data.get(mapping["zoneId"]) or []
|
|
103
|
+
candidate_keys = {f.get("key") for f in zone_fields if f.get("dzId") == dz_id}
|
|
104
|
+
if not candidate_keys:
|
|
105
|
+
raise SystemExit(f"{card_id}: mapping {name} has no zoneData candidates for dzId={dz_id}")
|
|
106
|
+
missing = [key for key in defaults if key not in candidate_keys]
|
|
107
|
+
if missing:
|
|
108
|
+
raise SystemExit(f"{card_id}: mapping {name} defaultValue keys {missing} not found in zoneData.{mapping['zoneId']}")
|
|
109
|
+
summary["mappings"][name] = {
|
|
110
|
+
"dzId": dz_id,
|
|
111
|
+
"zoneId": mapping["zoneId"],
|
|
112
|
+
"metaType": mapping["metaType"],
|
|
113
|
+
"multiSelect": mapping["multiSelect"],
|
|
114
|
+
"defaultValue": defaults,
|
|
115
|
+
"candidateCount": len(candidate_keys),
|
|
116
|
+
}
|
|
117
|
+
return summary
|
|
118
|
+
|
|
119
|
+
summaries = []
|
|
120
|
+
for card in preview_cards():
|
|
121
|
+
result = assert_dynamic_card(card)
|
|
122
|
+
if result:
|
|
123
|
+
summaries.append(result)
|
|
124
|
+
|
|
125
|
+
if len(summaries) != len(expected):
|
|
126
|
+
got = sorted(item["cardId"] for item in summaries)
|
|
127
|
+
raise SystemExit(f"preview dynamic card count={len(summaries)}, got {got}, want {sorted(expected)}")
|
|
128
|
+
|
|
129
|
+
if (out / "raw_dataset_card.json").exists() and (out / "raw_metric_card.json").exists():
|
|
130
|
+
for raw_name in ("raw_dataset_card.json", "raw_metric_card.json"):
|
|
131
|
+
raw_summary = assert_dynamic_card(normalize_card(load_json(out / raw_name)))
|
|
132
|
+
summaries.append({**raw_summary, "source": raw_name})
|
|
133
|
+
|
|
134
|
+
summary = {"cards": summaries}
|
|
135
|
+
(out / "summary.json").write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
136
|
+
print(json.dumps(summary, ensure_ascii=False, indent=2))
|
|
137
|
+
PY
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Percentage Advanced Calc Round-Trip
|
|
2
|
+
|
|
3
|
+
This eval verifies the L3 native BI `LEVEL_PERCENTAGE` advanced calculation builder against a real `default` BI environment.
|
|
4
|
+
|
|
5
|
+
It covers:
|
|
6
|
+
|
|
7
|
+
- 按列:`SUM`
|
|
8
|
+
- 按行:`SUM` with a column dimension, matching the UI-created path
|
|
9
|
+
- 指定行维度:`SUM`
|
|
10
|
+
- 指定列维度:`SUM`
|
|
11
|
+
- 去重计数:`CNT_DISTINCT`
|
|
12
|
+
- 平均值:`AVG`, covering the backend SUM/CNT auxiliary denominator path
|
|
13
|
+
|
|
14
|
+
Local payload preview from `guanvis/`:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
guanvis preview ./evals/percentage_advcalc_roundtrip
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Default-environment publish/readback commands intentionally live in the project golden-answer note rather than this PR, because they publish fixed resource IDs to the shared `default` environment. The manual verification checks:
|
|
21
|
+
|
|
22
|
+
- `advCalc` is preserved in `zoneData.metric[*].advCalc`.
|
|
23
|
+
- No metric silently degrades to a formula.
|
|
24
|
+
- Each expected `measureOnDimRow` / `tfdId` / `auto` field round-trips.
|
|
25
|
+
- Percentage number format is present with two decimal places.
|
|
26
|
+
- `card preview` succeeds and percentage aliases have non-empty values.
|
package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/card_01_percentage_advcalc.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
var region = f("地区");
|
|
2
|
+
var province = f("省/自治区");
|
|
3
|
+
var category = f("类别");
|
|
4
|
+
var subcategory = f("子类别");
|
|
5
|
+
|
|
6
|
+
var percentageCard = createCard(ChartType.PIVOT_TABLE, "L3 占比高级计算")
|
|
7
|
+
.setId("pct01a02b03c04d05e06f07g")
|
|
8
|
+
.bindDataset(DS)
|
|
9
|
+
.addRow(region)
|
|
10
|
+
.addRow(province)
|
|
11
|
+
.addColumn(category)
|
|
12
|
+
.addColumn(subcategory)
|
|
13
|
+
.addMetric(f("销售额", {
|
|
14
|
+
alias: "按列SUM占比",
|
|
15
|
+
aggrType: AggrType.SUM,
|
|
16
|
+
advCalc: percentage.byColumn()
|
|
17
|
+
}))
|
|
18
|
+
.addMetric(f("销售额", {
|
|
19
|
+
alias: "按行SUM占比",
|
|
20
|
+
aggrType: AggrType.SUM,
|
|
21
|
+
advCalc: percentage.byRow()
|
|
22
|
+
}))
|
|
23
|
+
.addMetric(f("销售额", {
|
|
24
|
+
alias: "地区层级SUM占比",
|
|
25
|
+
aggrType: AggrType.SUM,
|
|
26
|
+
advCalc: percentage.byField(region)
|
|
27
|
+
}))
|
|
28
|
+
.addMetric(f("销售额", {
|
|
29
|
+
alias: "类别层级SUM占比",
|
|
30
|
+
aggrType: AggrType.SUM,
|
|
31
|
+
advCalc: percentage.byField(category)
|
|
32
|
+
}))
|
|
33
|
+
.addMetric(f("客户 ID", {
|
|
34
|
+
alias: "客户去重按列占比",
|
|
35
|
+
aggrType: AggrType.COUNT_DISTINCT,
|
|
36
|
+
advCalc: percentage.byColumn()
|
|
37
|
+
}))
|
|
38
|
+
.addMetric(f("折扣", {
|
|
39
|
+
alias: "折扣AVG地区占比",
|
|
40
|
+
aggrType: AggrType.AVG,
|
|
41
|
+
advCalc: percentage.byField(region)
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
registerCard(percentageCard.build());
|