@pn-data/pn-data-analysis 0.0.2 → 0.0.4

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 CHANGED
@@ -1,4 +1,4 @@
1
- # @pn/pn-data-analysis
1
+ # PN中台 AI数据分析
2
2
 
3
3
  Cursor Agent Skill — 通过 pn_mid_v2 中台 API 查询项目的**流水、活跃、留存、来源**数据。
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pn-data/pn-data-analysis",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Cursor Agent Skill for querying pn_mid_v2 project data",
5
5
  "keywords": [
6
6
  "cursor",
@@ -3,6 +3,18 @@ name: pn-data-analysis
3
3
  description: Analyzes project data on the middle platform (pn_mid_v2). Use when querying 收入, 流水, 商店流水, 活跃, 留存, 来源, project revenue, activity, retention, or source data by project and month.
4
4
  ---
5
5
 
6
+ > Skill 版本:v0.0.4
7
+
8
+ # Skill 更新
9
+
10
+ ```bash
11
+ npm view @pn-data/pn-data-analysis version --registry https://registry.npmjs.org/
12
+ ```
13
+
14
+ 与本文件顶部 `Skill 版本` 对比,不同则更新本 Skill
15
+
16
+ 更新后**重新读取** `<SKILL_DIR>/SKILL.md`,以新版本为准。告知用户"中台数据分析 Skill 已更新到 vX.X.X"。
17
+
6
18
  # 中台项目数据分析
7
19
 
8
20
  通过 `pn_query.py` 远程查询 pn_mid_v2 中台各项目数据,支持**流水、活跃、留存、来源**四类查询。
@@ -13,10 +25,21 @@ description: Analyzes project data on the middle platform (pn_mid_v2). Use when
13
25
  - **关键词**:用pn、用中台
14
26
  - **数据类型词**:收入、流水、商店流水、活跃、留存、来源
15
27
 
16
- ## 项目名称解析
28
+ ## 执行顺序:先取 listProject 学习 id 与名称
29
+
30
+ **在跑任何按项目查询(pay/activity/retention/source)之前**,先取一次项目列表,建立「项目 id ↔ 名称」对应关系:
31
+
32
+ 1. **先执行**:`python3 <SKILL_DIR>/pn_query.py projects`(或 `--json` 取原始列表)
33
+ 2. **从返回中学习**:每条为 `id | name | tag`,记下 id、name、tag 的对应关系
34
+ 3. **再解析用户请求**:用户提到「项目名」「项目X」「tag」或数字 id 时,用该列表解析出**真实的项目 id (pid)**,再调用 `pn_query.py <pid> <dataType> <YYYY-MM>`
35
+
36
+ 未先取 listProject 时,不要猜测 pid;名称/别称与 id 的对应关系以当次 listProject 结果为准。
17
37
 
18
- - 项目 ID 直接使用(如「2023项目」→ pid=2023)
19
- - 「项目10」→ pid=2023
38
+ ## 项目名称解析(依赖上面 listProject 结果)
39
+
40
+ - 用户说**数字 id**(如 2023、2088)→ 直接作为 pid
41
+ - 用户说**项目名称或 tag** → 用已获取的项目列表按 name/tag 匹配,取对应 id 作为 pid
42
+ - 仅当列表中存在明确对应时再发起数据查询;无法匹配时提示用户说明项目 id 或名称
20
43
 
21
44
  ---
22
45
 
@@ -34,10 +57,23 @@ description: Analyzes project data on the middle platform (pn_mid_v2). Use when
34
57
 
35
58
  ## 查询命令
36
59
 
37
- 脚本位置:`.cursor/skills/pn-data-analysis/pn_query.py`
60
+ 脚本位置:`<SKILL_DIR>/pn_query.py`
61
+
62
+ ### 获取项目列表(id、name、tag)
63
+
64
+ 无需 pid/月份,直接获取当前密钥对应用户可访问的项目列表,用于学习 **id、name、tag** 的对应关系:
38
65
 
39
66
  ```bash
40
- python3 .cursor/skills/pn-data-analysis/pn_query.py <pid> <dataType> <YYYY-MM> [选项]
67
+ python3 <SKILL_DIR>/pn_query.py projects
68
+ python3 <SKILL_DIR>/pn_query.py projects --json
69
+ ```
70
+
71
+ 输出格式:`id | name | tag`,每行一个项目。
72
+
73
+ ### 按项目与月份查询数据
74
+
75
+ ```bash
76
+ python3 <SKILL_DIR>/pn_query.py <pid> <dataType> <YYYY-MM> [选项]
41
77
  ```
42
78
 
43
79
  ### 四类数据
@@ -71,23 +107,26 @@ python3 .cursor/skills/pn-data-analysis/pn_query.py <pid> <dataType> <YYYY-MM> [
71
107
  ## 示例
72
108
 
73
109
  ```bash
110
+ # 获取项目列表
111
+ python3 <SKILL_DIR>/pn_query.py projects
112
+
74
113
  # 流水
75
- python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 pay 2026-01
114
+ python3 <SKILL_DIR>/pn_query.py 2023 pay 2026-01
76
115
 
77
116
  # 活跃(全平台)
78
- python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 activity 2026-01
117
+ python3 <SKILL_DIR>/pn_query.py 2023 activity 2026-01
79
118
 
80
119
  # 留存(全平台)
81
- python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 retention 2026-01
120
+ python3 <SKILL_DIR>/pn_query.py 2023 retention 2026-01
82
121
 
83
122
  # 来源
84
- python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 source 2026-01
123
+ python3 <SKILL_DIR>/pn_query.py 2023 source 2026-01
85
124
 
86
125
  # 仅 iOS 活跃
87
- python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 activity 2026-01 --appPlatform=1
126
+ python3 <SKILL_DIR>/pn_query.py 2023 activity 2026-01 --appPlatform=1
88
127
 
89
128
  # 原始 JSON
90
- python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 retention 2026-01 --json
129
+ python3 <SKILL_DIR>/pn_query.py 2023 retention 2026-01 --json
91
130
  ```
92
131
 
93
132
  ## 默认行为
@@ -2,9 +2,10 @@
2
2
  """
3
3
  pn_query.py - 远程查询 pn_mid_v2 中台数据
4
4
 
5
- 通过 HTTP API 调用 pnDataAnalysis 接口,查询项目的流水、活跃、留存、来源数据。
5
+ 通过 HTTP API 调用 pnDataAnalysis 接口,查询项目的流水、活跃、留存、来源数据,或获取项目列表。
6
6
 
7
7
  用法:
8
+ python3 pn_query.py projects # 获取项目列表
8
9
  python3 pn_query.py <pid> <dataType> <YYYY-MM> [选项]
9
10
  python3 pn_query.py 2023 pay 2026-01
10
11
  python3 pn_query.py 2023 activity 2026-01
@@ -14,6 +15,7 @@ pn_query.py - 远程查询 pn_mid_v2 中台数据
14
15
  选项:
15
16
  --appPlatform=N 0=全平台(默认) 1=iOS 2=Android
16
17
  --group=N 分组类型,默认2(日期)。留存默认8(账号)
18
+ --json 输出原始 JSON
17
19
  """
18
20
 
19
21
  import argparse
@@ -30,6 +32,42 @@ API_PATH = "/service/pn_data_analysis"
30
32
  ENV_TOKEN = "PN_DATA_ANALYSIS_TOKEN"
31
33
 
32
34
 
35
+ def list_projects() -> list:
36
+ """获取当前用户可访问的项目列表。"""
37
+ token = os.environ.get(ENV_TOKEN)
38
+ if not token:
39
+ raise SystemExit(
40
+ f"未配置 {ENV_TOKEN}。请在中台右上角 -> 生成数据分析密钥 获取后写入 shell 配置(如 ~/.zshrc)并 source 生效。"
41
+ )
42
+ url = f"{API_BASE}{API_PATH}"
43
+ payload = {"dataType": "listProject"}
44
+ headers = {
45
+ "Content-Type": "application/json;charset=UTF-8",
46
+ "X-Pn-Token": token,
47
+ }
48
+ req = urllib.request.Request(
49
+ url,
50
+ data=json.dumps(payload).encode("utf-8"),
51
+ headers=headers,
52
+ method="POST",
53
+ )
54
+ with urllib.request.urlopen(req, timeout=30) as resp:
55
+ out = json.loads(resp.read().decode("utf-8"))
56
+ return out if isinstance(out, list) else out.get("list", out)
57
+
58
+
59
+ def format_projects(data: list) -> str:
60
+ if not data:
61
+ return "无项目数据"
62
+ lines = ["项目列表 (id | name | tag):"]
63
+ for p in data:
64
+ pid = p.get("id", "")
65
+ name = p.get("name", "")
66
+ tag = p.get("tag", "")
67
+ lines.append(f" {pid} | {name} | {tag}")
68
+ return "\n".join(lines)
69
+
70
+
33
71
  def query(pid: int, data_type: str, month: str, app_platform: int = 0, group: int = 2) -> dict:
34
72
  token = os.environ.get(ENV_TOKEN)
35
73
  if not token:
@@ -126,19 +164,36 @@ def format_source(data: list) -> str:
126
164
 
127
165
  def main():
128
166
  parser = argparse.ArgumentParser(description="pn_mid_v2 远程数据查询")
129
- parser.add_argument("pid", type=int, help="项目 ID")
130
- parser.add_argument("dataType", choices=["pay", "activity", "retention", "source"], help="数据类型")
131
- parser.add_argument("month", help="月份 YYYY-MM")
167
+ parser.add_argument("pid", nargs="?", help="项目 ID,或写 projects 获取项目列表")
168
+ parser.add_argument("dataType", nargs="?", choices=["pay", "activity", "retention", "source"], help="数据类型")
169
+ parser.add_argument("month", nargs="?", help="月份 YYYY-MM")
132
170
  parser.add_argument("--appPlatform", type=int, default=0, help="0=全平台 1=iOS 2=Android (默认0)")
133
171
  parser.add_argument("--group", type=int, default=None, help="分组类型,默认2(日期),留存默认8(账号)")
134
172
  parser.add_argument("--json", action="store_true", help="输出原始 JSON")
135
173
  args = parser.parse_args()
136
174
 
175
+ # 获取项目列表
176
+ if (args.pid or "").lower() in ("projects", "listproject", "list"):
177
+ try:
178
+ result = list_projects()
179
+ except Exception as e:
180
+ print(f"查询失败: {e}", file=sys.stderr)
181
+ sys.exit(1)
182
+ if args.json:
183
+ print(json.dumps(result, ensure_ascii=False, indent=2))
184
+ else:
185
+ print(format_projects(result))
186
+ return
187
+
188
+ if args.pid is None or args.dataType is None or args.month is None:
189
+ parser.error("查询数据时需指定 pid dataType month,或使用 pid=projects 获取项目列表")
190
+
191
+ pid = int(args.pid)
137
192
  if args.group is None:
138
193
  args.group = 8 if args.dataType == "retention" else 2
139
194
 
140
195
  try:
141
- result = query(args.pid, args.dataType, args.month, args.appPlatform, args.group)
196
+ result = query(pid, args.dataType, args.month, args.appPlatform, args.group)
142
197
  except Exception as e:
143
198
  print(f"查询失败: {e}", file=sys.stderr)
144
199
  sys.exit(1)