@dazitech/cli 3.0.7 → 3.0.8
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 +73 -22
- package/dist/clis/dazi.js +266 -171
- package/dist/docs/flow/flow-project-guide.md +1 -1
- package/dist/docs/guides/quickstart.md +18 -4
- package/dist/docs/guides/troubleshooting.md +1 -1
- package/dist/docs/guides/workspace-v3.md +43 -23
- package/dist/docs/index.json +9 -3
- package/dist/docs/onto/action-guide.md +3 -3
- package/dist/docs/onto/dazi_script_sdk_reference.md +178 -174
- package/dist/docs/onto/dazi_script_seed_data_guide.md +158 -155
- package/dist/docs/onto/function-guide.md +37 -10
- package/dist/docs/onto/space-management.md +3 -1
- 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 +138 -34
- package/dist/docs/onto//346/234/254/344/275/223/350/247/204/345/210/222/346/214/207/345/215/227.md +73 -31
- 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 +497 -0
- 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 +597 -541
- package/dist/examples/index.json +202 -22
- package/dist/examples/onto/README.md +43 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_account_breakdown.py +99 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_budget_vs_actual.py +116 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_cost_center_profit.py +85 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_get_summary.py +76 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_mom_analysis.py +86 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_top_accounts.py +103 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_yoy_analysis.py +86 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/save_test_arguments.ps1 +27 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.account_breakdown.json +10 -0
- 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 +10 -0
- 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 +9 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.get_summary.json +9 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.mom_analysis.json +9 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.top_accounts.json +11 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.yoy_analysis.json +9 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_ontology_init.py +521 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_seed_data.py +213 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/README.md +25 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_channel_mix.py +86 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_customer_segmentation.py +123 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_get_summary.py +81 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_mom_analysis.py +90 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_region_breakdown.py +85 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_top_products.py +101 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_yoy_analysis.py +90 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/save_test_arguments.ps1 +25 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.channel_mix.json +8 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.customer_segmentation.json +10 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.get_summary.json +8 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.mom_analysis.json +8 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.region_breakdown.json +8 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.top_products.json +10 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.yoy_analysis.json +8 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/README.md +5 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_ontology_init.py +403 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_seed_data.py +124 -0
- package/dist/prompts/index.json +2 -2
- package/dist/prompts/onto/action-design.md +4 -1
- package/dist/prompts/onto/function-design.md +9 -2
- package/dist/prompts/onto/rule-seed.md +5 -1
- package/dist/prompts/onto/script-publish-run.md +72 -24
- package/package.json +1 -1
- package/dist/examples/onto/function/profit_fn_customer_segmentation.py +0 -117
- package/dist/examples/onto/function/profit_fn_mom_analysis.py +0 -89
- package/dist/examples/onto/function/profit_fn_top_products.py +0 -89
- package/dist/examples/onto/function/profit_fn_yoy_analysis.py +0 -89
- package/dist/examples/onto/setup/profit_ontology_init.py +0 -388
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
你是搭子平台 **dazi-vscode v3** 本体实施助手。用户已完成或正在完成
|
|
9
|
+
你是搭子平台 **dazi-vscode v3** 本体实施助手。用户已完成或正在完成 `项目/<业务名>/本体/ontos/<实现名>/plans/` 中的方案,你的任务是编写 `setup/`、`functions/` 下 Python,并**用正确命令**发布与运行。
|
|
10
10
|
|
|
11
11
|
## 强制规则(违反会导致 CommandNotFound 或 404)
|
|
12
12
|
|
|
@@ -17,20 +17,35 @@
|
|
|
17
17
|
- **仅开发仓库**:`node bundled/clis/dazi.js onto ...`(维护方打包用;客户环境用 `dazi.ps1`)
|
|
18
18
|
- **完整路径**:`node <搭子仓库>/dazi/dazi-vscode/bundled/clis/dazi.js onto <子命令> ...`,并设置环境变量 `DAZI_BUNDLED_DIR=<同目录 bundled/clis>`
|
|
19
19
|
4. **工作目录**必须是搭子工作区根(含 `项目/` 目录),例如 `dazi-work`。
|
|
20
|
-
5. **`space_id`** 只从
|
|
20
|
+
5. **`space_id`** 只从 `项目/<业务名>/本体/ontos/<实现名>/README.md` 的「数据空间 ID」读取,禁止猜测。
|
|
21
21
|
|
|
22
22
|
## 工作区目录(v3)
|
|
23
23
|
|
|
24
24
|
```text
|
|
25
25
|
<工作区根>/
|
|
26
|
-
|
|
27
|
-
README.md
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
项目/<业务名>/
|
|
27
|
+
README.md ← 业务项目元信息
|
|
28
|
+
本体/
|
|
29
|
+
README.md ← 容器说明(onto-assets + ontos 索引)
|
|
30
|
+
onto-assets/ ← 平台元数据缓存(业务项目级,侧栏懒拉取)
|
|
31
|
+
objects/ functions/ actions/ rules/
|
|
32
|
+
ontos/
|
|
33
|
+
<实现名>/ ← ★ 本体实现工作单元
|
|
34
|
+
README.md ← space_id(权威)
|
|
35
|
+
快速启动_<实现名>.md ← 动态生成,含本实现命令模板
|
|
36
|
+
plans/ ← 规划 Markdown(本阶段不 publish)
|
|
37
|
+
setup/ ← 初始化、灌数(先 init 后 seed)
|
|
38
|
+
functions/ ← 本体函数
|
|
39
|
+
test_arguments/ ← 各 function_id 的默认测试入参 JSON
|
|
40
|
+
save_test_arguments.ps1 ← 可选:批量 save-test-arguments
|
|
41
|
+
流程/
|
|
42
|
+
plans/
|
|
43
|
+
flows/<流程名>/...
|
|
44
|
+
应用/
|
|
45
|
+
plans/
|
|
46
|
+
apps/<组件名>/...
|
|
47
|
+
资源/docs/ ← dazi docs sync 后的指南
|
|
48
|
+
scripts/ ← dazi.ps1 / dazi.cmd 包装(客户工作区模板应包含)
|
|
34
49
|
```
|
|
35
50
|
|
|
36
51
|
本地开发路径 **不是** `onto/<space_id>/editorial/`;发布时 CLI 自动映射为平台路径
|
|
@@ -49,24 +64,25 @@ dazi auth login
|
|
|
49
64
|
|
|
50
65
|
## 标准发布与运行顺序
|
|
51
66
|
|
|
52
|
-
将
|
|
67
|
+
将 `<业务名>`、`<实现名>`、`<空间>` 替换为实际值,例如 `潘达石化`、`本体01`、`space__0519`。
|
|
68
|
+
脚本路径前缀记为 `<项目路径>` = `项目/<业务名>/本体/ontos/<实现名>`。
|
|
53
69
|
|
|
54
70
|
### 1. 预检(可选)
|
|
55
71
|
|
|
56
72
|
```powershell
|
|
57
|
-
dazi onto script publish-preview
|
|
73
|
+
dazi onto script publish-preview <项目路径>/setup/xxx_ontology_init.py --space <空间> --type setup
|
|
58
74
|
```
|
|
59
75
|
|
|
60
76
|
### 2. 发布初始化脚本(setup)
|
|
61
77
|
|
|
62
78
|
```powershell
|
|
63
|
-
dazi onto script publish
|
|
79
|
+
dazi onto script publish <项目路径>/setup/xxx_ontology_init.py --space <空间> --type setup
|
|
64
80
|
```
|
|
65
81
|
|
|
66
82
|
### 3. 发布灌数脚本(data)
|
|
67
83
|
|
|
68
84
|
```powershell
|
|
69
|
-
dazi onto script publish
|
|
85
|
+
dazi onto script publish <项目路径>/setup/xxx_seed_data.py --space <空间> --type data
|
|
70
86
|
```
|
|
71
87
|
|
|
72
88
|
### 4. 发布本体函数(**必须** `--register-function-id`,否则函数列表看不到)
|
|
@@ -76,7 +92,7 @@ dazi onto script publish <项目路径>/脚本/setup/xxx_seed_data.py --space <
|
|
|
76
92
|
发布后必须确认输出含 `functionId` / `function_registration.ok`,并执行 `function list` 能看到该 id。
|
|
77
93
|
|
|
78
94
|
```powershell
|
|
79
|
-
dazi onto script publish
|
|
95
|
+
dazi onto script publish <项目路径>/functions/xxx_fn_yyy.py --space <空间> --register-function-id <domain>.fn.<name>
|
|
80
96
|
dazi onto function list --space <空间>
|
|
81
97
|
```
|
|
82
98
|
|
|
@@ -84,8 +100,8 @@ dazi onto function list --space <空间>
|
|
|
84
100
|
|
|
85
101
|
```powershell
|
|
86
102
|
# 按 publish 返回的 scriptId,或:
|
|
87
|
-
dazi onto script run --file
|
|
88
|
-
dazi onto script run --file
|
|
103
|
+
dazi onto script run --file <项目路径>/setup/xxx_ontology_init.py --space <空间>
|
|
104
|
+
dazi onto script run --file <项目路径>/setup/xxx_seed_data.py --space <空间>
|
|
89
105
|
```
|
|
90
106
|
|
|
91
107
|
**顺序**:先 run init,再 run seed,再发布/运行函数。
|
|
@@ -103,13 +119,44 @@ $env:DAZI_PARAMS='{"start_date":"2025-01-01","end_date":"2026-12-31"}'
|
|
|
103
119
|
dazi onto function run <function_id> --space <空间> --params $env:DAZI_PARAMS
|
|
104
120
|
```
|
|
105
121
|
|
|
122
|
+
### 7. 保存测试参数(test_arguments,**发布后必做**)
|
|
123
|
+
|
|
124
|
+
`publish` + `function run` **不会**自动写入测试参数。侧栏 **Onto → 运行函数** 依赖函数定义上的 **`test_arguments`** 预填表单;未保存时侧栏参数为空。
|
|
125
|
+
|
|
126
|
+
**本地约定**(与 `plans/` 规划文档一致):
|
|
127
|
+
|
|
128
|
+
1. 每个函数一份 JSON:`<项目路径>/functions/test_arguments/<function_id>.json`
|
|
129
|
+
2. 脚本内常量 `TEST_ARGUMENTS` 与 JSON **保持同步**
|
|
130
|
+
3. JSON 格式:`{"v":1,"arguments":{...},"object_type_code":"<ObjectTypeCode>"}`
|
|
131
|
+
|
|
132
|
+
**保存到平台**(`function run` 验证通过后):
|
|
133
|
+
|
|
134
|
+
```powershell
|
|
135
|
+
# 推荐:批量脚本(解析 function list,用内部 id ofn_xxx 调用 PATCH)
|
|
136
|
+
.\<项目路径>\functions\save_test_arguments.ps1
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
单条保存(**须用平台内部 id `ofn_xxx`**,先 `dazi onto function list --space <空间>` 查看;直接传 `sales.fn.xxx` 等 function_id 字符串可能 **404**):
|
|
140
|
+
|
|
141
|
+
```powershell
|
|
142
|
+
dazi onto function save-test-arguments <ofn_internal_id> --space <空间> `
|
|
143
|
+
--arguments-json-file <项目路径>/functions/test_arguments/<function_id>.json
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
验收:
|
|
147
|
+
|
|
148
|
+
```powershell
|
|
149
|
+
dazi onto function get <function_id> --space <空间>
|
|
150
|
+
# 确认 test_arguments 非 null,且 arguments 与 JSON 一致
|
|
151
|
+
```
|
|
152
|
+
|
|
106
153
|
## 脚本类型与路径推断
|
|
107
154
|
|
|
108
|
-
| 本地路径特征
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
155
|
+
| 本地路径特征 | `--type` | 平台目录 |
|
|
156
|
+
| ------------------------- | -------------------------- | --------------------- |
|
|
157
|
+
| `.../setup/*init*.py` | `setup` | `setup/` |
|
|
158
|
+
| `.../setup/*seed*.py` | `data` | `data/` |
|
|
159
|
+
| `.../functions/*.py` | (默认 ontology_function) | `ontology_functions/` |
|
|
113
160
|
|
|
114
161
|
## 编写脚本时注意
|
|
115
162
|
|
|
@@ -122,7 +169,7 @@ dazi onto function run <function_id> --space <空间> --params $env:DAZI_PARAMS
|
|
|
122
169
|
```powershell
|
|
123
170
|
$env:DAZI_BUNDLED_DIR = "<绝对路径>/dazi/dazi-vscode/bundled/clis"
|
|
124
171
|
cd <工作区根>
|
|
125
|
-
node "<绝对路径>/dazi/dazi-vscode/bundled/clis/dazi.js" onto script publish
|
|
172
|
+
node "<绝对路径>/dazi/dazi-vscode/bundled/clis/dazi.js" onto script publish 项目/<业务名>/本体/ontos/<实现名>/setup/xxx_ontology_init.py --space <空间> --type setup
|
|
126
173
|
```
|
|
127
174
|
|
|
128
175
|
VS Code 用户也可在侧栏 **搭子 → Onto 本体** 使用「发布脚本」「运行函数」,无需终端。
|
|
@@ -141,6 +188,7 @@ VS Code 用户也可在侧栏 **搭子 → Onto 本体** 使用「发布脚本
|
|
|
141
188
|
|
|
142
189
|
**用户补充上下文(可选)**:
|
|
143
190
|
|
|
144
|
-
-
|
|
191
|
+
- 业务项目:{{business_name}}
|
|
192
|
+
- 本体实现:{{item_name}}
|
|
145
193
|
- 数据空间 ID:{{space_id}}
|
|
146
194
|
- 当前任务:{{task}}(如:发布 training_ontology_init 并执行)
|
package/package.json
CHANGED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
"""Customer Segmentation Function
|
|
2
|
-
|
|
3
|
-
功能:基于利润贡献对客户进行分层
|
|
4
|
-
参数:metric, method, start_date, end_date
|
|
5
|
-
返回:客户分层数据(VIP/High/Medium/Low)
|
|
6
|
-
|
|
7
|
-
放置位置:spaces/space__profit0520/editorial/scripts/ontology_functions/profit_fn_customer_segmentation.py
|
|
8
|
-
检索关键字:profit customer_segmentation
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _ontology_fn_body(p):
|
|
13
|
-
params = dict(p.get_params() or {})
|
|
14
|
-
|
|
15
|
-
metric = params.get("metric", "profit")
|
|
16
|
-
method = params.get("method", "quartile")
|
|
17
|
-
start_date = params.get("start_date", "")
|
|
18
|
-
end_date = params.get("end_date", "")
|
|
19
|
-
|
|
20
|
-
where_clause = ""
|
|
21
|
-
if start_date and end_date:
|
|
22
|
-
where_clause = f"WHERE order_date >= '{start_date}' AND order_date <= '{end_date}'"
|
|
23
|
-
|
|
24
|
-
sql = f"""
|
|
25
|
-
SELECT
|
|
26
|
-
customer_id,
|
|
27
|
-
customer_region,
|
|
28
|
-
sum(revenue) as revenue,
|
|
29
|
-
sum(cost) as cost,
|
|
30
|
-
sum(revenue) - sum(cost) as profit
|
|
31
|
-
FROM profit_analysis_fact
|
|
32
|
-
{where_clause}
|
|
33
|
-
GROUP BY customer_id, customer_region
|
|
34
|
-
ORDER BY profit DESC
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
result = p.sql.query(sql)
|
|
38
|
-
|
|
39
|
-
if not result:
|
|
40
|
-
return p.function_result(
|
|
41
|
-
columns=["customer_id", "customer_region", "revenue", "cost", "profit", "segment"],
|
|
42
|
-
data=[],
|
|
43
|
-
row_count=0
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
profits = [float(row.get("profit", 0) or 0) for row in result]
|
|
47
|
-
total = len(profits)
|
|
48
|
-
|
|
49
|
-
if total == 0:
|
|
50
|
-
return p.function_result(
|
|
51
|
-
columns=["customer_id", "customer_region", "revenue", "cost", "profit", "segment"],
|
|
52
|
-
data=[],
|
|
53
|
-
row_count=0
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
if method == "percentile":
|
|
57
|
-
sorted_profits = sorted(profits, reverse=True)
|
|
58
|
-
p80_idx = min(int(total * 0.8), total - 1)
|
|
59
|
-
p50_idx = min(int(total * 0.5), total - 1)
|
|
60
|
-
p20_idx = min(int(total * 0.2), total - 1)
|
|
61
|
-
p80 = sorted_profits[p80_idx]
|
|
62
|
-
p50 = sorted_profits[p50_idx]
|
|
63
|
-
p20 = sorted_profits[p20_idx]
|
|
64
|
-
else:
|
|
65
|
-
sorted_profits = sorted(profits, reverse=True)
|
|
66
|
-
q1_idx = min(int(total * 0.25), total - 1)
|
|
67
|
-
q2_idx = min(int(total * 0.5), total - 1)
|
|
68
|
-
q3_idx = min(int(total * 0.75), total - 1)
|
|
69
|
-
p80 = sorted_profits[q1_idx]
|
|
70
|
-
p50 = sorted_profits[q2_idx]
|
|
71
|
-
p20 = sorted_profits[q3_idx]
|
|
72
|
-
|
|
73
|
-
def get_segment(profit_val):
|
|
74
|
-
if profit_val >= p80:
|
|
75
|
-
return "VIP"
|
|
76
|
-
elif profit_val >= p50:
|
|
77
|
-
return "High"
|
|
78
|
-
elif profit_val >= p20:
|
|
79
|
-
return "Medium"
|
|
80
|
-
else:
|
|
81
|
-
return "Low"
|
|
82
|
-
|
|
83
|
-
data = []
|
|
84
|
-
for row in result:
|
|
85
|
-
profit = float(row.get("profit", 0) or 0)
|
|
86
|
-
data.append({
|
|
87
|
-
"customer_id": str(row.get("customer_id", "")),
|
|
88
|
-
"customer_region": str(row.get("customer_region", "")),
|
|
89
|
-
"revenue": round(float(row.get("revenue", 0) or 0), 2),
|
|
90
|
-
"cost": round(float(row.get("cost", 0) or 0), 2),
|
|
91
|
-
"profit": round(profit, 2),
|
|
92
|
-
"segment": get_segment(profit),
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
return p.function_result(
|
|
96
|
-
columns=["customer_id", "customer_region", "revenue", "cost", "profit", "segment"],
|
|
97
|
-
data=data,
|
|
98
|
-
row_count=len(data)
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def main():
|
|
103
|
-
s = space.get(ctx.space_id or "")
|
|
104
|
-
|
|
105
|
-
_Ports = type(
|
|
106
|
-
"_Ports",
|
|
107
|
-
(),
|
|
108
|
-
{
|
|
109
|
-
"get_params": lambda self: dict(ctx.params or {}),
|
|
110
|
-
"function_result": lambda self, **kw: onto.function_result(**kw),
|
|
111
|
-
},
|
|
112
|
-
)
|
|
113
|
-
p = _Ports()
|
|
114
|
-
p.space_id = str(ctx.space_id or "")
|
|
115
|
-
p.sql = s.sql
|
|
116
|
-
|
|
117
|
-
return _ontology_fn_body(p)
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"""MoM (Month-over-Month) Analysis Function
|
|
2
|
-
|
|
3
|
-
功能:计算指定时间范围内的月度环比分析
|
|
4
|
-
参数:start_date, end_date
|
|
5
|
-
返回:月度利润数据及环比增长率
|
|
6
|
-
|
|
7
|
-
放置位置:spaces/space__profit0520/editorial/scripts/ontology_functions/profit_fn_mom_analysis.py
|
|
8
|
-
检索关键字:profit mom month-over-month
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _ontology_fn_body(p):
|
|
13
|
-
params = dict(p.get_params() or {})
|
|
14
|
-
|
|
15
|
-
start_date = params.get("start_date", "")
|
|
16
|
-
end_date = params.get("end_date", "")
|
|
17
|
-
|
|
18
|
-
where_clause = ""
|
|
19
|
-
if start_date and end_date:
|
|
20
|
-
where_clause = f"WHERE order_date >= '{start_date}' AND order_date <= '{end_date}'"
|
|
21
|
-
|
|
22
|
-
sql = f"""
|
|
23
|
-
SELECT
|
|
24
|
-
formatDateTime(order_date, '%Y-%m') as year_month,
|
|
25
|
-
sum(revenue) as revenue,
|
|
26
|
-
sum(cost) as cost,
|
|
27
|
-
sum(revenue) - sum(cost) as profit,
|
|
28
|
-
if(sum(revenue) > 0, (sum(revenue) - sum(cost)) / sum(revenue), 0) as profit_margin
|
|
29
|
-
FROM profit_analysis_fact
|
|
30
|
-
{where_clause}
|
|
31
|
-
GROUP BY formatDateTime(order_date, '%Y-%m')
|
|
32
|
-
ORDER BY year_month
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
result = p.sql.query(sql)
|
|
36
|
-
|
|
37
|
-
if not result:
|
|
38
|
-
return p.function_result(
|
|
39
|
-
columns=["year_month", "revenue", "cost", "profit", "profit_margin", "mom_growth"],
|
|
40
|
-
data=[],
|
|
41
|
-
row_count=0
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
data = []
|
|
45
|
-
prev_profit = None
|
|
46
|
-
|
|
47
|
-
for row in result:
|
|
48
|
-
year_month = str(row.get("year_month", ""))
|
|
49
|
-
profit = float(row.get("profit", 0) or 0)
|
|
50
|
-
|
|
51
|
-
if prev_profit is not None and prev_profit != 0:
|
|
52
|
-
mom_growth = (profit - prev_profit) / prev_profit
|
|
53
|
-
else:
|
|
54
|
-
mom_growth = 0.0
|
|
55
|
-
|
|
56
|
-
data.append({
|
|
57
|
-
"year_month": year_month,
|
|
58
|
-
"revenue": round(float(row.get("revenue", 0) or 0), 2),
|
|
59
|
-
"cost": round(float(row.get("cost", 0) or 0), 2),
|
|
60
|
-
"profit": round(profit, 2),
|
|
61
|
-
"profit_margin": round(float(row.get("profit_margin", 0) or 0), 4),
|
|
62
|
-
"mom_growth": round(mom_growth, 4),
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
prev_profit = profit
|
|
66
|
-
|
|
67
|
-
return p.function_result(
|
|
68
|
-
columns=["year_month", "revenue", "cost", "profit", "profit_margin", "mom_growth"],
|
|
69
|
-
data=data,
|
|
70
|
-
row_count=len(data)
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def main():
|
|
75
|
-
s = space.get(ctx.space_id or "")
|
|
76
|
-
|
|
77
|
-
_Ports = type(
|
|
78
|
-
"_Ports",
|
|
79
|
-
(),
|
|
80
|
-
{
|
|
81
|
-
"get_params": lambda self: dict(ctx.params or {}),
|
|
82
|
-
"function_result": lambda self, **kw: onto.function_result(**kw),
|
|
83
|
-
},
|
|
84
|
-
)
|
|
85
|
-
p = _Ports()
|
|
86
|
-
p.space_id = str(ctx.space_id or "")
|
|
87
|
-
p.sql = s.sql
|
|
88
|
-
|
|
89
|
-
return _ontology_fn_body(p)
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"""Top Products Profit Ranking Function
|
|
2
|
-
|
|
3
|
-
功能:按利润或利润率排序,获取Top N产品
|
|
4
|
-
参数:limit, metric, start_date, end_date
|
|
5
|
-
返回:产品利润排行数据
|
|
6
|
-
|
|
7
|
-
放置位置:spaces/space__profit0520/editorial/scripts/ontology_functions/profit_fn_top_products.py
|
|
8
|
-
检索关键字:profit top_products ranking
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _ontology_fn_body(p):
|
|
13
|
-
params = dict(p.get_params() or {})
|
|
14
|
-
|
|
15
|
-
limit = int(params.get("limit", 10) or 10)
|
|
16
|
-
metric = params.get("metric", "profit")
|
|
17
|
-
start_date = params.get("start_date", "")
|
|
18
|
-
end_date = params.get("end_date", "")
|
|
19
|
-
|
|
20
|
-
where_clause = ""
|
|
21
|
-
if start_date and end_date:
|
|
22
|
-
where_clause = f"WHERE order_date >= '{start_date}' AND order_date <= '{end_date}'"
|
|
23
|
-
|
|
24
|
-
if metric == "margin":
|
|
25
|
-
order_by = "profit_margin DESC"
|
|
26
|
-
else:
|
|
27
|
-
order_by = "profit DESC"
|
|
28
|
-
|
|
29
|
-
sql = f"""
|
|
30
|
-
SELECT
|
|
31
|
-
product_id,
|
|
32
|
-
product_category,
|
|
33
|
-
sum(revenue) as revenue,
|
|
34
|
-
sum(cost) as cost,
|
|
35
|
-
sum(revenue) - sum(cost) as profit,
|
|
36
|
-
if(sum(revenue) > 0, (sum(revenue) - sum(cost)) / sum(revenue), 0) as profit_margin
|
|
37
|
-
FROM profit_analysis_fact
|
|
38
|
-
{where_clause}
|
|
39
|
-
GROUP BY product_id, product_category
|
|
40
|
-
ORDER BY {order_by}
|
|
41
|
-
LIMIT {limit}
|
|
42
|
-
"""
|
|
43
|
-
|
|
44
|
-
result = p.sql.query(sql)
|
|
45
|
-
|
|
46
|
-
if not result:
|
|
47
|
-
return p.function_result(
|
|
48
|
-
columns=["rank", "product_id", "product_category", "revenue", "cost", "profit", "profit_margin"],
|
|
49
|
-
data=[],
|
|
50
|
-
row_count=0
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
data = []
|
|
54
|
-
rank = 1
|
|
55
|
-
for row in result:
|
|
56
|
-
data.append({
|
|
57
|
-
"rank": rank,
|
|
58
|
-
"product_id": str(row.get("product_id", "")),
|
|
59
|
-
"product_category": str(row.get("product_category", "")),
|
|
60
|
-
"revenue": round(float(row.get("revenue", 0) or 0), 2),
|
|
61
|
-
"cost": round(float(row.get("cost", 0) or 0), 2),
|
|
62
|
-
"profit": round(float(row.get("profit", 0) or 0), 2),
|
|
63
|
-
"profit_margin": round(float(row.get("profit_margin", 0) or 0), 4),
|
|
64
|
-
})
|
|
65
|
-
rank += 1
|
|
66
|
-
|
|
67
|
-
return p.function_result(
|
|
68
|
-
columns=["rank", "product_id", "product_category", "revenue", "cost", "profit", "profit_margin"],
|
|
69
|
-
data=data,
|
|
70
|
-
row_count=len(data)
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def main():
|
|
75
|
-
s = space.get(ctx.space_id or "")
|
|
76
|
-
|
|
77
|
-
_Ports = type(
|
|
78
|
-
"_Ports",
|
|
79
|
-
(),
|
|
80
|
-
{
|
|
81
|
-
"get_params": lambda self: dict(ctx.params or {}),
|
|
82
|
-
"function_result": lambda self, **kw: onto.function_result(**kw),
|
|
83
|
-
},
|
|
84
|
-
)
|
|
85
|
-
p = _Ports()
|
|
86
|
-
p.space_id = str(ctx.space_id or "")
|
|
87
|
-
p.sql = s.sql
|
|
88
|
-
|
|
89
|
-
return _ontology_fn_body(p)
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"""YoY (Year-over-Year) Analysis Function
|
|
2
|
-
|
|
3
|
-
功能:计算指定时间范围内的年度同比分析
|
|
4
|
-
参数:start_date, end_date
|
|
5
|
-
返回:年度利润数据及同比增长率
|
|
6
|
-
|
|
7
|
-
放置位置:spaces/space__profit0520/editorial/scripts/ontology_functions/profit_fn_yoy_analysis.py
|
|
8
|
-
检索关键字:profit yoy year-over-year
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _ontology_fn_body(p):
|
|
13
|
-
params = dict(p.get_params() or {})
|
|
14
|
-
|
|
15
|
-
start_date = params.get("start_date", "")
|
|
16
|
-
end_date = params.get("end_date", "")
|
|
17
|
-
|
|
18
|
-
where_clause = ""
|
|
19
|
-
if start_date and end_date:
|
|
20
|
-
where_clause = f"WHERE order_date >= '{start_date}' AND order_date <= '{end_date}'"
|
|
21
|
-
|
|
22
|
-
sql = f"""
|
|
23
|
-
SELECT
|
|
24
|
-
toYear(order_date) as year,
|
|
25
|
-
sum(revenue) as revenue,
|
|
26
|
-
sum(cost) as cost,
|
|
27
|
-
sum(revenue) - sum(cost) as profit,
|
|
28
|
-
if(sum(revenue) > 0, (sum(revenue) - sum(cost)) / sum(revenue), 0) as profit_margin
|
|
29
|
-
FROM profit_analysis_fact
|
|
30
|
-
{where_clause}
|
|
31
|
-
GROUP BY toYear(order_date)
|
|
32
|
-
ORDER BY year
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
result = p.sql.query(sql)
|
|
36
|
-
|
|
37
|
-
if not result:
|
|
38
|
-
return p.function_result(
|
|
39
|
-
columns=["year", "revenue", "cost", "profit", "profit_margin", "yoy_growth"],
|
|
40
|
-
data=[],
|
|
41
|
-
row_count=0
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
data = []
|
|
45
|
-
prev_profit = None
|
|
46
|
-
|
|
47
|
-
for row in result:
|
|
48
|
-
year = int(row.get("year", 0))
|
|
49
|
-
profit = float(row.get("profit", 0) or 0)
|
|
50
|
-
|
|
51
|
-
if prev_profit is not None and prev_profit != 0:
|
|
52
|
-
yoy_growth = (profit - prev_profit) / prev_profit
|
|
53
|
-
else:
|
|
54
|
-
yoy_growth = 0.0
|
|
55
|
-
|
|
56
|
-
data.append({
|
|
57
|
-
"year": year,
|
|
58
|
-
"revenue": round(float(row.get("revenue", 0) or 0), 2),
|
|
59
|
-
"cost": round(float(row.get("cost", 0) or 0), 2),
|
|
60
|
-
"profit": round(profit, 2),
|
|
61
|
-
"profit_margin": round(float(row.get("profit_margin", 0) or 0), 4),
|
|
62
|
-
"yoy_growth": round(yoy_growth, 4),
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
prev_profit = profit
|
|
66
|
-
|
|
67
|
-
return p.function_result(
|
|
68
|
-
columns=["year", "revenue", "cost", "profit", "profit_margin", "yoy_growth"],
|
|
69
|
-
data=data,
|
|
70
|
-
row_count=len(data)
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def main():
|
|
75
|
-
s = space.get(ctx.space_id or "")
|
|
76
|
-
|
|
77
|
-
_Ports = type(
|
|
78
|
-
"_Ports",
|
|
79
|
-
(),
|
|
80
|
-
{
|
|
81
|
-
"get_params": lambda self: dict(ctx.params or {}),
|
|
82
|
-
"function_result": lambda self, **kw: onto.function_result(**kw),
|
|
83
|
-
},
|
|
84
|
-
)
|
|
85
|
-
p = _Ports()
|
|
86
|
-
p.space_id = str(ctx.space_id or "")
|
|
87
|
-
p.sql = s.sql
|
|
88
|
-
|
|
89
|
-
return _ontology_fn_body(p)
|