@dazitech/cli 3.0.8 → 3.1.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/README.md +1 -1
- package/dist/clis/dazi-app.js +1 -1
- package/dist/clis/dazi-flow.js +1 -1
- package/dist/clis/dazi-onto.js +7 -2
- package/dist/clis/dazi.js +1 -1
- package/dist/docs/flow/flow-project-guide.md +1 -1
- package/dist/docs/guides/troubleshooting.md +12 -1
- package/dist/docs/index.json +21 -2
- package/dist/docs/onto/dazi_script_sdk_reference.md +246 -178
- package/dist/docs/onto/function-guide.md +123 -95
- package/dist/docs/onto//346/234/254/344/275/223/345/210/206/347/261/273/350/247/204/345/210/222/344/270/216SDK/346/211/251/345/261/225/346/226/271/346/241/210.md +169 -0
- package/dist/docs/onto//346/234/254/344/275/223/345/221/275/345/220/215/350/247/204/350/214/203_/347/211/251/347/220/206/350/241/250Cube/344/270/216/345/257/271/350/261/241.md +402 -0
- package/dist/docs/onto//346/234/254/344/275/223/345/274/200/345/217/221/344/274/230/345/214/226/346/200/273/347/273/223.md +242 -0
- package/dist/docs/onto//346/234/254/344/275/223/350/204/232/346/234/254/347/274/226/345/206/231/346/214/207/345/215/227.md +339 -249
- package/dist/docs/onto//346/234/254/344/275/223/350/247/204/345/210/222/346/214/207/345/215/227.md +304 -173
- package/dist/docs/onto//350/204/232/346/234/254/350/277/220/350/241/214/345/270/270/350/247/201/351/224/231/350/257/257/345/244/204/347/220/206.md +242 -0
- package/dist/docs/onto//350/247/204/345/210/222/347/244/272/344/276/213_/344/272/247/345/223/201/351/224/200/345/224/256/346/234/254/344/275/223/350/247/204/345/210/222/346/226/271/346/241/210.md +361 -238
- package/dist/docs/onto//350/247/204/345/210/222/347/244/272/344/276/213_/345/210/251/346/266/246/345/210/206/346/236/220/346/234/254/344/275/223/346/226/271/346/241/210.md +385 -300
- package/dist/examples/index.json +22 -16
- package/dist/examples/onto/README.md +13 -5
- package/dist/examples/onto/_templates/ontology_function_template.py +50 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_account_breakdown.py +62 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_budget_vs_actual.py +69 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_cost_center_profit.py +64 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_get_summary.py +61 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_mom_analysis.py +82 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_top_accounts.py +61 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_yoy_analysis.py +79 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/save_test_arguments.ps1 +38 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.account_breakdown.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.budget_vs_actual.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.cost_center_profit.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.get_summary.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.mom_analysis.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.top_accounts.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.yoy_analysis.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_category_mount.py +85 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_ontology_init.py +169 -74
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_seed_data.py +16 -13
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_channel_mix.py +19 -16
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_customer_segmentation.py +48 -50
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_get_summary.py +3 -6
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_mom_analysis.py +11 -12
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_region_breakdown.py +6 -7
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_top_products.py +5 -8
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_yoy_analysis.py +3 -6
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/save_test_arguments.ps1 +32 -19
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.channel_mix.json +3 -6
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.customer_segmentation.json +2 -7
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.get_summary.json +2 -5
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.mom_analysis.json +2 -5
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.region_breakdown.json +2 -5
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.top_products.json +2 -7
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.yoy_analysis.json +2 -5
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_category_mount.py +82 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_ontology_init.py +240 -155
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_seed_data.py +59 -20
- package/dist/prompts/index.json +8 -1
- package/dist/prompts/onto/function-design.md +73 -53
- package/dist/prompts/onto/planning-design.md +104 -0
- package/dist/prompts/onto/script-publish-run.md +229 -194
- package/package.json +1 -1
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_account_breakdown.py +0 -99
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_budget_vs_actual.py +0 -116
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_cost_center_profit.py +0 -85
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_get_summary.py +0 -76
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_mom_analysis.py +0 -86
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_top_accounts.py +0 -103
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_yoy_analysis.py +0 -86
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/save_test_arguments.ps1 +0 -27
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.account_breakdown.json +0 -10
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.budget_vs_actual.json +0 -10
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.cost_center_profit.json +0 -9
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.get_summary.json +0 -9
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.mom_analysis.json +0 -9
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.top_accounts.json +0 -11
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.yoy_analysis.json +0 -9
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/README.md +0 -25
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/README.md +0 -5
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"""利润分析演示数据灌入 —
|
|
1
|
+
"""利润分析演示数据灌入 — space__misc_01
|
|
2
2
|
|
|
3
3
|
前置:先执行 profit_ontology_init.py 建表。
|
|
4
|
-
幂等:
|
|
4
|
+
幂等:fact_gl_journal_entry 已有数据则跳过。
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
发布:dazi onto script publish
|
|
6
|
+
放置:项目/DAZI_TEST/本体/ontos/利润分析示例/setup/profit_seed_data.py
|
|
7
|
+
发布:dazi onto script publish 项目/DAZI_TEST/本体/ontos/利润分析示例/setup/profit_seed_data.py --space space__misc_01 --type data
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import json
|
|
@@ -34,9 +34,11 @@ def _make_entry_line(seq, posting_date, account, cc, amount):
|
|
|
34
34
|
fp = posting_date.month
|
|
35
35
|
entry_id = f"JE{posting_date.strftime('%Y%m%d')}{seq // 10:04d}"
|
|
36
36
|
line_id = f"JL{posting_date.strftime('%Y%m%d')}{seq:05d}"
|
|
37
|
+
date_key = int(posting_date.strftime("%Y%m%d"))
|
|
37
38
|
return {
|
|
38
39
|
"entry_id": entry_id,
|
|
39
40
|
"line_id": line_id,
|
|
41
|
+
"date_key": date_key,
|
|
40
42
|
"posting_date": posting_date,
|
|
41
43
|
"fiscal_year": fy,
|
|
42
44
|
"fiscal_period": fp,
|
|
@@ -62,17 +64,17 @@ def _make_entry_line(seq, posting_date, account, cc, amount):
|
|
|
62
64
|
|
|
63
65
|
|
|
64
66
|
def main():
|
|
65
|
-
space_id = "
|
|
67
|
+
space_id = "space__misc_01"
|
|
66
68
|
s = space.get(space_id)
|
|
67
69
|
|
|
68
70
|
output.print("=== 利润分析演示数据灌入 ===")
|
|
69
71
|
|
|
70
72
|
try:
|
|
71
|
-
n = int(s.sql.query_one("SELECT count() FROM
|
|
73
|
+
n = int(s.sql.query_one("SELECT count() FROM fact_gl_journal_entry") or 0)
|
|
72
74
|
except Exception:
|
|
73
75
|
n = 0
|
|
74
76
|
if n > 0:
|
|
75
|
-
output.print(f"
|
|
77
|
+
output.print(f"fact_gl_journal_entry 已有 {n} 行,跳过灌数")
|
|
76
78
|
output.print("__JSON_SUMMARY__" + json.dumps({"ok": True, "skipped": True, "rows": n}, ensure_ascii=True))
|
|
77
79
|
return
|
|
78
80
|
|
|
@@ -101,8 +103,8 @@ def main():
|
|
|
101
103
|
for c in cost_centers:
|
|
102
104
|
c["created_at"] = _SEED_DT
|
|
103
105
|
|
|
104
|
-
s.sql.insert_rows("
|
|
105
|
-
s.sql.insert_rows("
|
|
106
|
+
s.sql.insert_rows("dim_account", accounts)
|
|
107
|
+
s.sql.insert_rows("dim_cost_center", cost_centers)
|
|
106
108
|
output.print("OK 维表数据")
|
|
107
109
|
|
|
108
110
|
leaf_accounts = [a for a in accounts if a["is_leaf"]]
|
|
@@ -115,7 +117,6 @@ def main():
|
|
|
115
117
|
start = date(2025, 1, 1)
|
|
116
118
|
end = date(2026, 6, 30)
|
|
117
119
|
|
|
118
|
-
# 科目基准金额(月)
|
|
119
120
|
base_amounts = {
|
|
120
121
|
"ACC6001": 800000,
|
|
121
122
|
"ACC6050": 50000,
|
|
@@ -152,7 +153,7 @@ def main():
|
|
|
152
153
|
seq += 1
|
|
153
154
|
d += timedelta(days=1)
|
|
154
155
|
|
|
155
|
-
inserted = s.sql.insert_rows("
|
|
156
|
+
inserted = s.sql.insert_rows("fact_gl_journal_entry", fact_rows)
|
|
156
157
|
output.print(f"OK 实际分录表插入 {inserted} 行")
|
|
157
158
|
|
|
158
159
|
budget_rows = []
|
|
@@ -177,9 +178,11 @@ def main():
|
|
|
177
178
|
if account["account_id"] == "ACC6603" and cc["cost_center_id"] != "CC05":
|
|
178
179
|
continue
|
|
179
180
|
budget_amt = round(base / 6 * random.uniform(0.95, 1.05), 2)
|
|
181
|
+
date_key = int(f"{year}{period:02d}01")
|
|
180
182
|
budget_rows.append({
|
|
181
183
|
"budget_id": f"BUD{year}",
|
|
182
184
|
"line_id": f"BL{year}{period:02d}{bseq:05d}",
|
|
185
|
+
"date_key": date_key,
|
|
183
186
|
"budget_version": version,
|
|
184
187
|
"fiscal_year": year,
|
|
185
188
|
"fiscal_period": period,
|
|
@@ -198,7 +201,7 @@ def main():
|
|
|
198
201
|
})
|
|
199
202
|
bseq += 1
|
|
200
203
|
|
|
201
|
-
binserted = s.sql.insert_rows("
|
|
204
|
+
binserted = s.sql.insert_rows("fact_budget_entry", budget_rows)
|
|
202
205
|
output.print(f"OK 预算表插入 {binserted} 行")
|
|
203
206
|
|
|
204
207
|
summary = {
|
|
@@ -210,4 +213,4 @@ def main():
|
|
|
210
213
|
"budget_inserted": binserted,
|
|
211
214
|
}
|
|
212
215
|
output.success("灌数完成")
|
|
213
|
-
output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=True, default=str))
|
|
216
|
+
output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=True, default=str))
|
|
@@ -1,27 +1,24 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""渠道结构分析 sales.fn.channel_mix
|
|
2
2
|
|
|
3
3
|
参数:start_date, end_date
|
|
4
|
-
|
|
4
|
+
返回:各渠道销售额、销量、订单数及占比
|
|
5
5
|
|
|
6
6
|
发布:
|
|
7
|
-
dazi onto script publish
|
|
8
|
-
--space
|
|
9
|
-
|
|
10
|
-
保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
|
|
11
|
-
见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
|
|
7
|
+
dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_channel_mix.py \
|
|
8
|
+
--space space__misc_01 --register-function-id sales.fn.channel_mix
|
|
12
9
|
"""
|
|
13
10
|
|
|
14
11
|
TEST_ARGUMENTS = {
|
|
15
12
|
"v": 1,
|
|
16
13
|
"arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30"},
|
|
17
|
-
"object_type_code": "
|
|
14
|
+
"object_type_code": "SalesAnalysis",
|
|
18
15
|
}
|
|
19
16
|
|
|
20
17
|
|
|
21
18
|
def _build_where(start_date, end_date):
|
|
22
|
-
clauses = ["
|
|
19
|
+
clauses = ["order_status IN ('已完成', '已发货')"]
|
|
23
20
|
if start_date and end_date:
|
|
24
|
-
clauses.append(f"
|
|
21
|
+
clauses.append(f"order_date >= '{start_date}' AND order_date <= '{end_date}'")
|
|
25
22
|
return "WHERE " + " AND ".join(clauses)
|
|
26
23
|
|
|
27
24
|
|
|
@@ -34,11 +31,13 @@ def _ontology_fn_body(p):
|
|
|
34
31
|
sql = f"""
|
|
35
32
|
SELECT
|
|
36
33
|
f.channel_id,
|
|
37
|
-
any(
|
|
34
|
+
any(dch.channel_name) AS channel_name,
|
|
35
|
+
any(dch.channel_type) AS channel_type,
|
|
38
36
|
sum(f.sales_amount) AS sales_amount,
|
|
37
|
+
sum(f.quantity) AS quantity,
|
|
39
38
|
uniq(f.order_id) AS order_count
|
|
40
|
-
FROM
|
|
41
|
-
LEFT JOIN
|
|
39
|
+
FROM fact_sales_order_line AS f
|
|
40
|
+
LEFT JOIN dim_channel AS dch ON f.channel_id = dch.channel_id
|
|
42
41
|
{where_clause}
|
|
43
42
|
GROUP BY f.channel_id
|
|
44
43
|
ORDER BY sales_amount DESC
|
|
@@ -47,25 +46,29 @@ def _ontology_fn_body(p):
|
|
|
47
46
|
result = p.sql.query(sql)
|
|
48
47
|
if not result:
|
|
49
48
|
return p.function_result(
|
|
50
|
-
columns=["channel_id", "channel_name", "sales_amount", "order_count", "share_pct"],
|
|
49
|
+
columns=["channel_id", "channel_name", "channel_type", "sales_amount", "quantity", "order_count", "share_pct"],
|
|
51
50
|
data=[],
|
|
52
51
|
row_count=0,
|
|
53
52
|
)
|
|
54
53
|
|
|
55
54
|
total = sum(float(r.get("sales_amount", 0) or 0) for r in result)
|
|
55
|
+
|
|
56
56
|
data = []
|
|
57
57
|
for row in result:
|
|
58
58
|
sales_amount = float(row.get("sales_amount", 0) or 0)
|
|
59
|
+
share_pct = sales_amount / total if total > 0 else 0.0
|
|
59
60
|
data.append({
|
|
60
61
|
"channel_id": str(row.get("channel_id", "")),
|
|
61
62
|
"channel_name": str(row.get("channel_name", "")),
|
|
63
|
+
"channel_type": str(row.get("channel_type", "")),
|
|
62
64
|
"sales_amount": round(sales_amount, 2),
|
|
65
|
+
"quantity": int(row.get("quantity", 0) or 0),
|
|
63
66
|
"order_count": int(row.get("order_count", 0) or 0),
|
|
64
|
-
"share_pct": round(
|
|
67
|
+
"share_pct": round(share_pct, 4),
|
|
65
68
|
})
|
|
66
69
|
|
|
67
70
|
return p.function_result(
|
|
68
|
-
columns=["channel_id", "channel_name", "sales_amount", "order_count", "share_pct"],
|
|
71
|
+
columns=["channel_id", "channel_name", "channel_type", "sales_amount", "quantity", "order_count", "share_pct"],
|
|
69
72
|
data=data,
|
|
70
73
|
row_count=len(data),
|
|
71
74
|
)
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""客户分层分析 sales.fn.customer_segmentation
|
|
2
2
|
|
|
3
|
-
参数:metric(sales_amount|quantity), method(quartile|
|
|
4
|
-
|
|
3
|
+
参数:metric(sales_amount|quantity), method(quartile|total), start_date, end_date
|
|
4
|
+
返回:客户分层结果(VIP/战略/普通)
|
|
5
5
|
|
|
6
6
|
发布:
|
|
7
|
-
dazi onto script publish
|
|
8
|
-
--space
|
|
9
|
-
|
|
10
|
-
保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
|
|
11
|
-
见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
|
|
7
|
+
dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_customer_segmentation.py \
|
|
8
|
+
--space space__misc_01 --register-function-id sales.fn.customer_segmentation
|
|
12
9
|
"""
|
|
13
10
|
|
|
14
11
|
TEST_ARGUMENTS = {
|
|
@@ -37,72 +34,73 @@ def _ontology_fn_body(p):
|
|
|
37
34
|
start_date = params.get("start_date", "")
|
|
38
35
|
end_date = params.get("end_date", "")
|
|
39
36
|
where_clause = _build_where(start_date, end_date)
|
|
40
|
-
order_col = "quantity" if metric == "quantity" else "sales_amount"
|
|
41
37
|
|
|
42
38
|
sql = f"""
|
|
43
39
|
SELECT
|
|
44
|
-
customer_id,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
sum(
|
|
48
|
-
|
|
40
|
+
f.customer_id,
|
|
41
|
+
any(dc.customer_name) AS customer_name,
|
|
42
|
+
f.customer_type,
|
|
43
|
+
sum(f.sales_amount) AS sales_amount,
|
|
44
|
+
sum(f.quantity) AS quantity,
|
|
45
|
+
uniq(f.order_id) AS order_count
|
|
46
|
+
FROM fact_sales_order_line AS f
|
|
47
|
+
LEFT JOIN dim_customer AS dc ON f.customer_id = dc.customer_id
|
|
49
48
|
{where_clause}
|
|
50
|
-
GROUP BY customer_id,
|
|
51
|
-
ORDER BY
|
|
49
|
+
GROUP BY f.customer_id, f.customer_type
|
|
50
|
+
ORDER BY sales_amount DESC
|
|
52
51
|
"""
|
|
53
52
|
|
|
54
53
|
result = p.sql.query(sql)
|
|
55
54
|
if not result:
|
|
56
55
|
return p.function_result(
|
|
57
|
-
columns=["customer_id", "
|
|
56
|
+
columns=["customer_id", "customer_name", "customer_type", "sales_amount", "quantity", "order_count", "segment"],
|
|
58
57
|
data=[],
|
|
59
58
|
row_count=0,
|
|
60
59
|
)
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
total =
|
|
64
|
-
if total == 0:
|
|
65
|
-
return p.function_result(
|
|
66
|
-
columns=["customer_id", "customer_region", "sales_amount", "quantity", "segment"],
|
|
67
|
-
data=[],
|
|
68
|
-
row_count=0,
|
|
69
|
-
)
|
|
61
|
+
total_key = "quantity" if metric == "quantity" else "sales_amount"
|
|
62
|
+
total = sum(float(r.get(total_key, 0) or 0) for r in result)
|
|
70
63
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
p80_idx = min(int(total * 0.25), total - 1)
|
|
78
|
-
p50_idx = min(int(total * 0.5), total - 1)
|
|
79
|
-
p20_idx = min(int(total * 0.75), total - 1)
|
|
80
|
-
p80 = sorted_vals[p80_idx]
|
|
81
|
-
p50 = sorted_vals[p50_idx]
|
|
82
|
-
p20 = sorted_vals[p20_idx]
|
|
83
|
-
|
|
84
|
-
def get_segment(val):
|
|
85
|
-
if val >= p80:
|
|
86
|
-
return "VIP"
|
|
87
|
-
if val >= p50:
|
|
88
|
-
return "High"
|
|
89
|
-
if val >= p20:
|
|
90
|
-
return "Medium"
|
|
91
|
-
return "Low"
|
|
64
|
+
if method == "quartile":
|
|
65
|
+
values = sorted([float(r.get(total_key, 0) or 0) for r in result], reverse=True)
|
|
66
|
+
n = len(values)
|
|
67
|
+
p75 = values[min(int(n * 0.25), n - 1)]
|
|
68
|
+
p50 = values[min(int(n * 0.50), n - 1)]
|
|
69
|
+
p25 = values[min(int(n * 0.75), n - 1)]
|
|
92
70
|
|
|
93
71
|
data = []
|
|
94
72
|
for row in result:
|
|
95
|
-
val = float(row.get(
|
|
73
|
+
val = float(row.get(total_key, 0) or 0)
|
|
74
|
+
if method == "quartile":
|
|
75
|
+
if val >= p75:
|
|
76
|
+
segment = "VIP"
|
|
77
|
+
elif val >= p50:
|
|
78
|
+
segment = "战略"
|
|
79
|
+
elif val >= p25:
|
|
80
|
+
segment = "普通"
|
|
81
|
+
else:
|
|
82
|
+
segment = "低价值"
|
|
83
|
+
else:
|
|
84
|
+
share = val / total if total > 0 else 0
|
|
85
|
+
if share >= 0.6:
|
|
86
|
+
segment = "核心"
|
|
87
|
+
elif share >= 0.3:
|
|
88
|
+
segment = "重要"
|
|
89
|
+
else:
|
|
90
|
+
segment = "普通"
|
|
91
|
+
|
|
96
92
|
data.append({
|
|
97
93
|
"customer_id": str(row.get("customer_id", "")),
|
|
98
|
-
"
|
|
94
|
+
"customer_name": str(row.get("customer_name", "")),
|
|
95
|
+
"customer_type": str(row.get("customer_type", "")),
|
|
99
96
|
"sales_amount": round(float(row.get("sales_amount", 0) or 0), 2),
|
|
100
97
|
"quantity": int(row.get("quantity", 0) or 0),
|
|
101
|
-
"
|
|
98
|
+
"order_count": int(row.get("order_count", 0) or 0),
|
|
99
|
+
"segment": segment,
|
|
102
100
|
})
|
|
103
101
|
|
|
104
102
|
return p.function_result(
|
|
105
|
-
columns=["customer_id", "
|
|
103
|
+
columns=["customer_id", "customer_name", "customer_type", "sales_amount", "quantity", "order_count", "segment"],
|
|
106
104
|
data=data,
|
|
107
105
|
row_count=len(data),
|
|
108
106
|
)
|
|
@@ -4,11 +4,8 @@
|
|
|
4
4
|
返回:总销售额、总销量、订单数、客单价、动销 SKU 数
|
|
5
5
|
|
|
6
6
|
发布:
|
|
7
|
-
dazi onto script publish
|
|
8
|
-
--space
|
|
9
|
-
|
|
10
|
-
保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
|
|
11
|
-
见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
|
|
7
|
+
dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_get_summary.py \
|
|
8
|
+
--space space__misc_01 --register-function-id sales.fn.get_summary
|
|
12
9
|
"""
|
|
13
10
|
|
|
14
11
|
TEST_ARGUMENTS = {
|
|
@@ -41,7 +38,7 @@ def _ontology_fn_body(p):
|
|
|
41
38
|
sum(quantity) AS total_quantity,
|
|
42
39
|
uniq(order_id) AS order_count,
|
|
43
40
|
uniq(product_id) AS product_count
|
|
44
|
-
FROM
|
|
41
|
+
FROM fact_sales_order_line
|
|
45
42
|
{where_clause}
|
|
46
43
|
"""
|
|
47
44
|
|
|
@@ -4,11 +4,8 @@
|
|
|
4
4
|
返回:月度销售额、销量、订单数及环比增长率
|
|
5
5
|
|
|
6
6
|
发布:
|
|
7
|
-
dazi onto script publish
|
|
8
|
-
--space
|
|
9
|
-
|
|
10
|
-
保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
|
|
11
|
-
见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
|
|
7
|
+
dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_mom_analysis.py \
|
|
8
|
+
--space space__misc_01 --register-function-id sales.fn.mom_analysis
|
|
12
9
|
"""
|
|
13
10
|
|
|
14
11
|
TEST_ARGUMENTS = {
|
|
@@ -33,20 +30,21 @@ def _ontology_fn_body(p):
|
|
|
33
30
|
|
|
34
31
|
sql = f"""
|
|
35
32
|
SELECT
|
|
36
|
-
|
|
33
|
+
toYear(order_date) AS year,
|
|
34
|
+
toMonth(order_date) AS month,
|
|
37
35
|
sum(sales_amount) AS sales_amount,
|
|
38
36
|
sum(quantity) AS quantity,
|
|
39
37
|
uniq(order_id) AS order_count
|
|
40
|
-
FROM
|
|
38
|
+
FROM fact_sales_order_line
|
|
41
39
|
{where_clause}
|
|
42
|
-
GROUP BY
|
|
43
|
-
ORDER BY
|
|
40
|
+
GROUP BY toYear(order_date), toMonth(order_date)
|
|
41
|
+
ORDER BY year, month
|
|
44
42
|
"""
|
|
45
43
|
|
|
46
44
|
result = p.sql.query(sql)
|
|
47
45
|
if not result:
|
|
48
46
|
return p.function_result(
|
|
49
|
-
columns=["
|
|
47
|
+
columns=["year", "month", "sales_amount", "quantity", "order_count", "mom_growth"],
|
|
50
48
|
data=[],
|
|
51
49
|
row_count=0,
|
|
52
50
|
)
|
|
@@ -60,7 +58,8 @@ def _ontology_fn_body(p):
|
|
|
60
58
|
else:
|
|
61
59
|
mom_growth = 0.0
|
|
62
60
|
data.append({
|
|
63
|
-
"
|
|
61
|
+
"year": int(row.get("year", 0)),
|
|
62
|
+
"month": int(row.get("month", 0)),
|
|
64
63
|
"sales_amount": round(sales_amount, 2),
|
|
65
64
|
"quantity": int(row.get("quantity", 0) or 0),
|
|
66
65
|
"order_count": int(row.get("order_count", 0) or 0),
|
|
@@ -69,7 +68,7 @@ def _ontology_fn_body(p):
|
|
|
69
68
|
prev_sales = sales_amount
|
|
70
69
|
|
|
71
70
|
return p.function_result(
|
|
72
|
-
columns=["
|
|
71
|
+
columns=["year", "month", "sales_amount", "quantity", "order_count", "mom_growth"],
|
|
73
72
|
data=data,
|
|
74
73
|
row_count=len(data),
|
|
75
74
|
)
|
|
@@ -4,11 +4,8 @@
|
|
|
4
4
|
返回:各区域销售额、销量、订单数及占比
|
|
5
5
|
|
|
6
6
|
发布:
|
|
7
|
-
dazi onto script publish
|
|
8
|
-
--space
|
|
9
|
-
|
|
10
|
-
保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
|
|
11
|
-
见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
|
|
7
|
+
dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_region_breakdown.py \
|
|
8
|
+
--space space__misc_01 --register-function-id sales.fn.region_breakdown
|
|
12
9
|
"""
|
|
13
10
|
|
|
14
11
|
TEST_ARGUMENTS = {
|
|
@@ -37,7 +34,7 @@ def _ontology_fn_body(p):
|
|
|
37
34
|
sum(sales_amount) AS sales_amount,
|
|
38
35
|
sum(quantity) AS quantity,
|
|
39
36
|
uniq(order_id) AS order_count
|
|
40
|
-
FROM
|
|
37
|
+
FROM fact_sales_order_line
|
|
41
38
|
{where_clause}
|
|
42
39
|
GROUP BY customer_region
|
|
43
40
|
ORDER BY sales_amount DESC
|
|
@@ -52,15 +49,17 @@ def _ontology_fn_body(p):
|
|
|
52
49
|
)
|
|
53
50
|
|
|
54
51
|
total = sum(float(r.get("sales_amount", 0) or 0) for r in result)
|
|
52
|
+
|
|
55
53
|
data = []
|
|
56
54
|
for row in result:
|
|
57
55
|
sales_amount = float(row.get("sales_amount", 0) or 0)
|
|
56
|
+
share_pct = sales_amount / total if total > 0 else 0.0
|
|
58
57
|
data.append({
|
|
59
58
|
"customer_region": str(row.get("customer_region", "")),
|
|
60
59
|
"sales_amount": round(sales_amount, 2),
|
|
61
60
|
"quantity": int(row.get("quantity", 0) or 0),
|
|
62
61
|
"order_count": int(row.get("order_count", 0) or 0),
|
|
63
|
-
"share_pct": round(
|
|
62
|
+
"share_pct": round(share_pct, 4),
|
|
64
63
|
})
|
|
65
64
|
|
|
66
65
|
return p.function_result(
|
|
@@ -4,11 +4,8 @@
|
|
|
4
4
|
返回:产品排行及销售占比
|
|
5
5
|
|
|
6
6
|
发布:
|
|
7
|
-
dazi onto script publish
|
|
8
|
-
--space
|
|
9
|
-
|
|
10
|
-
保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
|
|
11
|
-
见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
|
|
7
|
+
dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_top_products.py \
|
|
8
|
+
--space space__misc_01 --register-function-id sales.fn.top_products
|
|
12
9
|
"""
|
|
13
10
|
|
|
14
11
|
TEST_ARGUMENTS = {
|
|
@@ -42,12 +39,12 @@ def _ontology_fn_body(p):
|
|
|
42
39
|
sql = f"""
|
|
43
40
|
SELECT
|
|
44
41
|
f.product_id,
|
|
45
|
-
any(
|
|
42
|
+
any(dp.product_name) AS product_name,
|
|
46
43
|
f.product_category,
|
|
47
44
|
sum(f.sales_amount) AS sales_amount,
|
|
48
45
|
sum(f.quantity) AS quantity
|
|
49
|
-
FROM
|
|
50
|
-
LEFT JOIN
|
|
46
|
+
FROM fact_sales_order_line AS f
|
|
47
|
+
LEFT JOIN dim_product AS dp ON f.product_id = dp.product_id
|
|
51
48
|
{where_clause}
|
|
52
49
|
GROUP BY f.product_id, f.product_category
|
|
53
50
|
ORDER BY {order_by}
|
|
@@ -4,11 +4,8 @@
|
|
|
4
4
|
返回:年度销售额、销量、订单数及同比增长率
|
|
5
5
|
|
|
6
6
|
发布:
|
|
7
|
-
dazi onto script publish
|
|
8
|
-
--space
|
|
9
|
-
|
|
10
|
-
保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
|
|
11
|
-
见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
|
|
7
|
+
dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_yoy_analysis.py \
|
|
8
|
+
--space space__misc_01 --register-function-id sales.fn.yoy_analysis
|
|
12
9
|
"""
|
|
13
10
|
|
|
14
11
|
TEST_ARGUMENTS = {
|
|
@@ -37,7 +34,7 @@ def _ontology_fn_body(p):
|
|
|
37
34
|
sum(sales_amount) AS sales_amount,
|
|
38
35
|
sum(quantity) AS quantity,
|
|
39
36
|
uniq(order_id) AS order_count
|
|
40
|
-
FROM
|
|
37
|
+
FROM fact_sales_order_line
|
|
41
38
|
{where_clause}
|
|
42
39
|
GROUP BY toYear(order_date)
|
|
43
40
|
ORDER BY year
|
|
@@ -1,25 +1,38 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
1
|
+
# save_test_arguments.ps1
|
|
2
|
+
# 批量保存各函数的 test_arguments
|
|
3
|
+
# 用法:在 dazi-work 根目录执行 .\项目\DAZI_TEST\本体\ontos\销售本体示例\functions\save_test_arguments.ps1
|
|
3
4
|
|
|
4
|
-
$
|
|
5
|
-
$
|
|
6
|
-
$base = "项目/潘达石化/本体/ontos/产品销售本体方案/functions/test_arguments"
|
|
5
|
+
$spaceId = "space__misc_01"
|
|
6
|
+
$itemPath = "项目/DAZI_TEST/本体/ontos/销售本体示例/functions"
|
|
7
7
|
|
|
8
|
-
$
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
$functions = @(
|
|
9
|
+
@{fn_id="sales.fn.get_summary"; file="sales.fn.get_summary.json"},
|
|
10
|
+
@{fn_id="sales.fn.yoy_analysis"; file="sales.fn.yoy_analysis.json"},
|
|
11
|
+
@{fn_id="sales.fn.mom_analysis"; file="sales.fn.mom_analysis.json"},
|
|
12
|
+
@{fn_id="sales.fn.top_products"; file="sales.fn.top_products.json"},
|
|
13
|
+
@{fn_id="sales.fn.customer_segmentation"; file="sales.fn.customer_segmentation.json"},
|
|
14
|
+
@{fn_id="sales.fn.region_breakdown"; file="sales.fn.region_breakdown.json"},
|
|
15
|
+
@{fn_id="sales.fn.channel_mix"; file="sales.fn.channel_mix.json"}
|
|
16
|
+
)
|
|
11
17
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
# 先获取 function list 获取 ofn_xxx 内部 id
|
|
19
|
+
Write-Host "获取函数列表..."
|
|
20
|
+
$fnList = & dazi onto function list --space $spaceId --output json 2>$null | ConvertFrom-Json
|
|
21
|
+
|
|
22
|
+
foreach ($fn in $functions) {
|
|
23
|
+
$fnId = $fn.fn_id
|
|
24
|
+
$fileName = $fn.file
|
|
25
|
+
$jsonPath = "$PSScriptRoot/test_arguments/$fileName"
|
|
26
|
+
|
|
27
|
+
# 查找 ofn_xxx 内部 id
|
|
28
|
+
$fnInfo = $fnList | Where-Object { $_.function_id -eq $fnId }
|
|
29
|
+
if ($fnInfo) {
|
|
30
|
+
$ofnId = $fnInfo.id
|
|
31
|
+
Write-Host "保存 $fnId (ofnId=$ofnId)..."
|
|
32
|
+
& dazi onto function save-test-arguments $ofnId --space $spaceId --arguments-json-file $jsonPath
|
|
33
|
+
} else {
|
|
34
|
+
Write-Host "函数 $fnId 未找到,请先发布函数"
|
|
19
35
|
}
|
|
20
|
-
Write-Host "save $fid ..."
|
|
21
|
-
# 注意:CLI 须用平台内部 id(ofn_xxx),不能用 function_id 字符串
|
|
22
|
-
dazi onto function save-test-arguments $ofn --space $space --arguments-json-file $file
|
|
23
36
|
}
|
|
24
37
|
|
|
25
|
-
Write-Host "
|
|
38
|
+
Write-Host "完成"
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"v": 1,
|
|
3
|
-
"arguments": {
|
|
4
|
-
"metric": "sales_amount",
|
|
5
|
-
"method": "quartile",
|
|
6
|
-
"start_date": "2025-01-01",
|
|
7
|
-
"end_date": "2026-06-30"
|
|
8
|
-
},
|
|
3
|
+
"arguments": {"metric": "sales_amount", "method": "quartile", "start_date": "2025-01-01", "end_date": "2026-06-30"},
|
|
9
4
|
"object_type_code": "Customer"
|
|
10
|
-
}
|
|
5
|
+
}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"v": 1,
|
|
3
|
-
"arguments": {
|
|
4
|
-
"limit": 5,
|
|
5
|
-
"metric": "sales_amount",
|
|
6
|
-
"start_date": "2025-01-01",
|
|
7
|
-
"end_date": "2026-06-30"
|
|
8
|
-
},
|
|
3
|
+
"arguments": {"limit": 5, "metric": "sales_amount", "start_date": "2025-01-01", "end_date": "2026-06-30"},
|
|
9
4
|
"object_type_code": "Product"
|
|
10
|
-
}
|
|
5
|
+
}
|