tmailday 0.1.0__tar.gz
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.
- tmailday-0.1.0/.gitignore +10 -0
- tmailday-0.1.0/.openmcp/connection.json +75 -0
- tmailday-0.1.0/.openmcp/tabs.tmailday.json +85 -0
- tmailday-0.1.0/.openmcp/tabs.untitle.json +4 -0
- tmailday-0.1.0/.python-version +1 -0
- tmailday-0.1.0/AGENTS.md +65 -0
- tmailday-0.1.0/PKG-INFO +9 -0
- tmailday-0.1.0/README.md +0 -0
- tmailday-0.1.0/pyproject.toml +25 -0
- tmailday-0.1.0/tmday/__init__.py +1 -0
- tmailday-0.1.0/tmday/__main__.py +3 -0
- tmailday-0.1.0/tmday/cli.py +56 -0
- tmailday-0.1.0/tmday/core/__init__.py +1 -0
- tmailday-0.1.0/tmday/core/processor.py +74 -0
- tmailday-0.1.0/tmday/core/reader.py +86 -0
- tmailday-0.1.0/tmday/core/schema.py +50 -0
- tmailday-0.1.0/tmday/core/validators.py +208 -0
- tmailday-0.1.0/tmday/core/writer.py +329 -0
- tmailday-0.1.0/tmday/server.py +41 -0
- tmailday-0.1.0/uv.lock +1358 -0
- tmailday-0.1.0//344/272/272/345/267/245/346/223/215/344/275/234/346/274/224/347/244/272.mp4 +0 -0
- tmailday-0.1.0//346/223/215/344/275/234/350/256/260/345/275/225.md +32 -0
- tmailday-0.1.0//346/265/213/350/257/225/346/225/260/346/215/256//343/200/220/347/224/237/346/204/217/345/217/202/350/260/213/345/271/263/345/217/260/343/200/221/345/225/206/345/223/201_/345/205/250/351/203/250_2026-06-11_2026-06-11.xls +0 -0
- tmailday-0.1.0//346/265/213/350/257/225/346/225/260/346/215/256//345/225/206/345/223/201/346/212/245/350/241/250_20260612_173510.zip +0 -0
- tmailday-0.1.0//346/265/213/350/257/225/346/225/260/346/215/256//346/227/245/346/212/245__20260612_0ffef6cae49818857d2d60ca1d0a9632.xlsx +0 -0
- tmailday-0.1.0//346/265/213/350/257/225/346/225/260/346/215/256//346/227/245/346/212/245/346/250/241/346/235/27706/346/234/210.xlsx +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"items": [
|
|
3
|
+
[
|
|
4
|
+
{
|
|
5
|
+
"connectionType": "STDIO",
|
|
6
|
+
"command": "uv",
|
|
7
|
+
"args": [
|
|
8
|
+
"run",
|
|
9
|
+
"tmday",
|
|
10
|
+
"serve"
|
|
11
|
+
],
|
|
12
|
+
"url": "",
|
|
13
|
+
"cwd": "D:/tmailday",
|
|
14
|
+
"oauth": "",
|
|
15
|
+
"clientName": "openmcp.connect.STDIO",
|
|
16
|
+
"clientVersion": "0.0.1",
|
|
17
|
+
"env": {
|
|
18
|
+
"HOME": "C:\\Users\\Administrator",
|
|
19
|
+
"LOGNAME": "Administrator",
|
|
20
|
+
"PATH": "C:\\Program Files\\ShadowBot;C:\\Python314\\Scripts\\;C:\\Python314\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Windows\\System32\\OpenSSH\\;C:\\Program Files\\nodejs\\;C:\\ProgramData\\chocolatey\\bin;C:\\Program Files\\Git\\cmd;C:\\Program Files\\TortoiseGit\\bin;C:\\Program Files\\WireGuard\\;C:\\Program Files\\dotnet\\;C:\\ProgramData\\ComposerSetup\\bin;C:\\Program Files\\Git\\bin;C:\\Program Files\\ShadowBot;C:\\Users\\Administrator\\.local\\bin;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;D:\\VS Code\\bin;C:\\Users\\Administrator\\.lmstudio\\bin;C:\\Users\\Administrator\\AppData\\Local\\Programs\\Ollama;D:\\phpstudy_pro\\Extensions\\php\\php8.0.2nts;C:\\Users\\Administrator\\AppData\\Roaming\\Composer\\vendor\\bin;C:\\Program Files\\Wukong\\0.9.51-26052503\\bin\\dingsync;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Anthropic.ClaudeCode_Microsoft.Winget.Source_8wekyb3d8bbwe;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WinGet\\Links;C:\\Users\\Administrator\\.bun\\bin;D:\\MiniClaude;C:\\Users\\Administrator\\AppData\\Local\\rmux\\bin;",
|
|
21
|
+
"SHELL": "C:\\Windows\\system32\\cmd.exe",
|
|
22
|
+
"TERM": "未设置 (Windows 默认终端)",
|
|
23
|
+
"USER": "Administrator"
|
|
24
|
+
},
|
|
25
|
+
"serverInfo": {
|
|
26
|
+
"name": "tmailday",
|
|
27
|
+
"version": "3.4.2"
|
|
28
|
+
},
|
|
29
|
+
"name": "tmailday",
|
|
30
|
+
"version": "3.4.2"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
{
|
|
35
|
+
"connectionType": "STDIO",
|
|
36
|
+
"name": "STDIO-1781335265906",
|
|
37
|
+
"command": "uv",
|
|
38
|
+
"args": [
|
|
39
|
+
"run",
|
|
40
|
+
"tmday",
|
|
41
|
+
"serve"
|
|
42
|
+
],
|
|
43
|
+
"cwd": "D:\\tmailday",
|
|
44
|
+
"filePath": "D:\\tmailday\\tmday",
|
|
45
|
+
"commandString": "uv run tmday serve"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
[
|
|
49
|
+
{
|
|
50
|
+
"connectionType": "SSE",
|
|
51
|
+
"command": "",
|
|
52
|
+
"args": [],
|
|
53
|
+
"url": "http://127.0.0.1:8000/sse",
|
|
54
|
+
"cwd": "",
|
|
55
|
+
"oauth": "",
|
|
56
|
+
"clientName": "openmcp.connect.SSE",
|
|
57
|
+
"clientVersion": "0.0.1",
|
|
58
|
+
"env": {
|
|
59
|
+
"HOME": "C:\\Users\\Administrator",
|
|
60
|
+
"LOGNAME": "Administrator",
|
|
61
|
+
"PATH": "C:\\Program Files\\ShadowBot;C:\\Python314\\Scripts\\;C:\\Python314\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Windows\\System32\\OpenSSH\\;C:\\Program Files\\nodejs\\;C:\\ProgramData\\chocolatey\\bin;C:\\Program Files\\Git\\cmd;C:\\Program Files\\TortoiseGit\\bin;C:\\Program Files\\WireGuard\\;C:\\Program Files\\dotnet\\;C:\\ProgramData\\ComposerSetup\\bin;C:\\Program Files\\Git\\bin;C:\\Program Files\\ShadowBot;C:\\Users\\Administrator\\.local\\bin;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;D:\\VS Code\\bin;C:\\Users\\Administrator\\.lmstudio\\bin;C:\\Users\\Administrator\\AppData\\Local\\Programs\\Ollama;D:\\phpstudy_pro\\Extensions\\php\\php8.0.2nts;C:\\Users\\Administrator\\AppData\\Roaming\\Composer\\vendor\\bin;C:\\Program Files\\Wukong\\0.9.51-26052503\\bin\\dingsync;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Anthropic.ClaudeCode_Microsoft.Winget.Source_8wekyb3d8bbwe;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WinGet\\Links;C:\\Users\\Administrator\\.bun\\bin;D:\\MiniClaude;C:\\Users\\Administrator\\AppData\\Local\\rmux\\bin;",
|
|
62
|
+
"SHELL": "C:\\Windows\\system32\\cmd.exe",
|
|
63
|
+
"TERM": "未设置 (Windows 默认终端)",
|
|
64
|
+
"USER": "Administrator"
|
|
65
|
+
},
|
|
66
|
+
"serverInfo": {
|
|
67
|
+
"name": "tmailday",
|
|
68
|
+
"version": "3.4.2"
|
|
69
|
+
},
|
|
70
|
+
"name": "tmailday",
|
|
71
|
+
"version": "3.4.2"
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
]
|
|
75
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"clientId": "05615852-5852-515852d450a-815852d450aa0e8-aa0e831a",
|
|
3
|
+
"currentIndex": 1,
|
|
4
|
+
"tabs": [
|
|
5
|
+
{
|
|
6
|
+
"name": "工具",
|
|
7
|
+
"icon": "icon-tool",
|
|
8
|
+
"type": "blank",
|
|
9
|
+
"componentIndex": 2,
|
|
10
|
+
"storage": {
|
|
11
|
+
"activeNames": [
|
|
12
|
+
0
|
|
13
|
+
],
|
|
14
|
+
"autoDetectDiagram": {
|
|
15
|
+
"edges": [],
|
|
16
|
+
"views": []
|
|
17
|
+
},
|
|
18
|
+
"formData": {
|
|
19
|
+
"template_path": "D:\\tmailday\\测试数据\\日报模板06月.xlsx",
|
|
20
|
+
"source_paths": [
|
|
21
|
+
"D:\\tmailday\\测试数据\\日报__20260612_0ffef6cae49818857d2d60ca1d0a9632.xlsx",
|
|
22
|
+
"D:\\tmailday\\测试数据\\【生意参谋平台】商品_全部_2026-06-11_2026-06-11.xls"
|
|
23
|
+
],
|
|
24
|
+
"output_path": ""
|
|
25
|
+
},
|
|
26
|
+
"currentToolName": "process_table_batch",
|
|
27
|
+
"lastToolCallResponse": {
|
|
28
|
+
"_meta": {
|
|
29
|
+
"fastmcp": {
|
|
30
|
+
"wrap_result": true
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"content": [
|
|
34
|
+
{
|
|
35
|
+
"type": "text",
|
|
36
|
+
"text": "处理完成:2 个文件 → D:\\tmailday\\测试数据\\日报模板06月.xlsx"
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"structuredContent": {
|
|
40
|
+
"result": "处理完成:2 个文件 → D:\\tmailday\\测试数据\\日报模板06月.xlsx"
|
|
41
|
+
},
|
|
42
|
+
"isError": false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "工具",
|
|
48
|
+
"icon": "icon-tool",
|
|
49
|
+
"type": "blank",
|
|
50
|
+
"componentIndex": 2,
|
|
51
|
+
"storage": {
|
|
52
|
+
"activeNames": [
|
|
53
|
+
0
|
|
54
|
+
],
|
|
55
|
+
"autoDetectDiagram": {
|
|
56
|
+
"edges": [],
|
|
57
|
+
"views": []
|
|
58
|
+
},
|
|
59
|
+
"formData": {
|
|
60
|
+
"template_path": "D:\\tmailday\\测试数据\\日报模板06月.xlsx",
|
|
61
|
+
"source_path": "D:\\tmailday\\测试数据\\商品报表_20260612_173510.zip",
|
|
62
|
+
"output_path": ""
|
|
63
|
+
},
|
|
64
|
+
"currentToolName": "process_table",
|
|
65
|
+
"lastToolCallResponse": {
|
|
66
|
+
"_meta": {
|
|
67
|
+
"fastmcp": {
|
|
68
|
+
"wrap_result": true
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"content": [
|
|
72
|
+
{
|
|
73
|
+
"type": "text",
|
|
74
|
+
"text": "处理完成:D:\\tmailday\\测试数据\\商品报表_20260612_173510.zip → D:\\tmailday\\测试数据\\日报模板06月.xlsx"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"structuredContent": {
|
|
78
|
+
"result": "处理完成:D:\\tmailday\\测试数据\\商品报表_20260612_173510.zip → D:\\tmailday\\测试数据\\日报模板06月.xlsx"
|
|
79
|
+
},
|
|
80
|
+
"isError": false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
tmailday-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## 项目概述
|
|
4
|
+
|
|
5
|
+
表格数据处理工具。读取导出的数据表格 → 处理数据 → 按数据来源写入模板文件对应工作薄。
|
|
6
|
+
|
|
7
|
+
## 技术栈
|
|
8
|
+
|
|
9
|
+
- **Python >= 3.12**(`.python-version` 锁定 `3.12`)
|
|
10
|
+
- **包管理器**: `uv`(非 pip/poetry)— 见 `pyproject.toml` 和 `uv.lock`
|
|
11
|
+
- **框架**: [FastMCP](https://github.com/jlowin/fastmcp) `>=3.4.2`,用于构建 MCP Server
|
|
12
|
+
- **表格处理**: `openpyxl>=3.1` (.xlsx), `xlrd>=2.0` (.xls)
|
|
13
|
+
- **编码检测**: `chardet>=5.0` (CSV 文件编码自动识别)
|
|
14
|
+
- **内置模块**: `csv` (CSV 读写), `zipfile` (ZIP 解压), `argparse` (CLI)
|
|
15
|
+
|
|
16
|
+
## 常用命令
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv sync # 安装/同步依赖
|
|
20
|
+
uv run tmday --help # 查看 CLI 帮助
|
|
21
|
+
uv run tmday <模板> <数据1> <数据2> ... # 批量处理,原地更新模板
|
|
22
|
+
uv run tmday <模板> <数据> -o <输出> # 处理后输出到指定文件
|
|
23
|
+
uv run tmday serve # 启动 MCP Server (stdio)
|
|
24
|
+
uv run tmday serve --transport sse # 启动 MCP HTTP Server
|
|
25
|
+
uv add <package> # 添加依赖
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 项目结构
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
tmday/ # 主包(CLI 可独立运行,见 pyproject.toml [project.scripts])
|
|
32
|
+
├── __init__.py # 版本号
|
|
33
|
+
├── __main__.py # python -m tmday → CLI
|
|
34
|
+
├── cli.py # CLI 定义 (argparse),子命令 process / serve
|
|
35
|
+
├── server.py # FastMCP 实例 + @mcp.tool() 装饰器
|
|
36
|
+
└── core/ # 核心逻辑(CLI 和 MCP 共享)
|
|
37
|
+
├── reader.py # 读取源表格 → dict
|
|
38
|
+
├── processor.py # 编排:读取 → 处理 → 写入
|
|
39
|
+
└── writer.py # 写入模板工作簿
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 三种入口
|
|
43
|
+
|
|
44
|
+
| 入口 | 命令 | 说明 |
|
|
45
|
+
|------|------|------|
|
|
46
|
+
| CLI | `tmday <模板> [数据...] [-o 输出]` | 批量处理,模板第一个参数 |
|
|
47
|
+
| MCP STDIO | `tmday serve` | MCP stdio 传输(也支持 `uvx tmailday serve`) |
|
|
48
|
+
| MCP HTTP | `tmday serve --transport sse` | MCP HTTP(SSE) 传输 |
|
|
49
|
+
|
|
50
|
+
**CLI 逻辑**:
|
|
51
|
+
- 第一个参数:模板文件
|
|
52
|
+
- 后续参数:数据文件(可选多个),逐个检测类型后处理
|
|
53
|
+
- `-o` 指定输出路径,缺省则覆盖模板文件
|
|
54
|
+
|
|
55
|
+
**MCP tools**:
|
|
56
|
+
- `process_table(template_path, source_path, output_path?)` — SSE 单文件处理
|
|
57
|
+
- `process_table_batch(template_path, source_paths, output_path?)` — 批量处理
|
|
58
|
+
|
|
59
|
+
## 架构约定
|
|
60
|
+
|
|
61
|
+
- **核心逻辑共享**: `cli.py` 和 `server.py` 都调用 `core/` 中的函数,仅入口层不同
|
|
62
|
+
- **表格文件作为参数传入**,不硬编码路径
|
|
63
|
+
- **MCP tools**: 在 `server.py` 用 `@mcp.tool()` 装饰 `core/` 函数
|
|
64
|
+
- **虚拟环境**: `.venv/`(已在 `.gitignore` 排除)
|
|
65
|
+
- **构建**: hatchling,手动指定 `packages = ["tmday"]`(项目名 `tmailday` ≠ 包目录名 `tmday`)
|
tmailday-0.1.0/PKG-INFO
ADDED
tmailday-0.1.0/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tmailday"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "表格数据处理工具 — 读取导出数据,按来源写入模板工作簿"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"chardet>=7.4.3",
|
|
9
|
+
"fastmcp>=3.4.2",
|
|
10
|
+
"openpyxl>=3.1",
|
|
11
|
+
"xlrd>=2.0",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.scripts]
|
|
15
|
+
tmday = "tmday.cli:main"
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["hatchling"]
|
|
19
|
+
build-backend = "hatchling.build"
|
|
20
|
+
|
|
21
|
+
[tool.hatch.build.targets.wheel]
|
|
22
|
+
packages = ["tmday"]
|
|
23
|
+
|
|
24
|
+
[tool.uv]
|
|
25
|
+
package = true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""CLI 入口 — tmailday 表格数据处理工具
|
|
2
|
+
|
|
3
|
+
用法:
|
|
4
|
+
tmday <模板文件> [数据文件...] [-o 输出文件]
|
|
5
|
+
tmday serve [--transport {stdio,sse}]
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main():
|
|
13
|
+
# 特殊处理 serve 子命令
|
|
14
|
+
if len(sys.argv) > 1 and sys.argv[1] == "serve":
|
|
15
|
+
parser = argparse.ArgumentParser(prog="tmday serve", description="启动 MCP Server")
|
|
16
|
+
parser.add_argument(
|
|
17
|
+
"serve", nargs="?", default="serve", help=argparse.SUPPRESS,
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
"--transport", default="stdio", choices=["stdio", "sse"],
|
|
21
|
+
help="传输协议 (default: stdio)",
|
|
22
|
+
)
|
|
23
|
+
args = parser.parse_args(sys.argv[1:])
|
|
24
|
+
from tmday.server import mcp
|
|
25
|
+
|
|
26
|
+
mcp.run(transport=args.transport)
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
parser = argparse.ArgumentParser(
|
|
30
|
+
prog="tmday",
|
|
31
|
+
description="表格数据处理工具 — 将数据文件按类型填入模板对应工作簿",
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument("template", help="模板文件路径 (.xlsx)")
|
|
34
|
+
parser.add_argument("sources", nargs="*", help="数据文件路径(日报/生意参谋/商品报表)")
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
"-o", "--output", default=None,
|
|
37
|
+
help="输出文件路径(默认原地更新模板)",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
args = parser.parse_args(sys.argv[1:])
|
|
41
|
+
|
|
42
|
+
if not args.sources:
|
|
43
|
+
from tmday.core.validators import validate_template
|
|
44
|
+
|
|
45
|
+
validate_template(args.template)
|
|
46
|
+
print(f"模板校验通过: {args.template}")
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
from tmday.core.processor import process_all
|
|
50
|
+
|
|
51
|
+
result = process_all(args.template, args.sources, args.output)
|
|
52
|
+
print(f"处理完成: {len(args.sources)} 个文件 → {result}")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
if __name__ == "__main__":
|
|
56
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""数据处理:按数据来源分配到模板对应工作簿"""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def process_all(
|
|
8
|
+
template_path: str,
|
|
9
|
+
source_paths: list[str],
|
|
10
|
+
output_path: str | None = None,
|
|
11
|
+
) -> str:
|
|
12
|
+
"""统一入口:对模板依次处理多个数据文件。
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
template_path: 模板文件路径
|
|
16
|
+
source_paths: 数据文件路径列表
|
|
17
|
+
output_path: 输出路径,为 None 则原地更新模板
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
实际写入的文件路径
|
|
21
|
+
"""
|
|
22
|
+
from tmday.core.validators import validate_file, validate_template
|
|
23
|
+
|
|
24
|
+
if output_path and Path(output_path) != Path(template_path):
|
|
25
|
+
shutil.copy2(template_path, output_path)
|
|
26
|
+
work_path = output_path
|
|
27
|
+
else:
|
|
28
|
+
work_path = template_path
|
|
29
|
+
|
|
30
|
+
validate_template(work_path)
|
|
31
|
+
|
|
32
|
+
for sp in source_paths:
|
|
33
|
+
file_type = validate_file(sp)
|
|
34
|
+
if file_type == "daily_report":
|
|
35
|
+
process_daily_report(sp, work_path)
|
|
36
|
+
elif file_type == "shengyi_canmou":
|
|
37
|
+
process_shengyi_canmou(sp, work_path)
|
|
38
|
+
elif file_type == "product_report":
|
|
39
|
+
process_product_report(sp, work_path)
|
|
40
|
+
else:
|
|
41
|
+
_not_implemented(file_type)
|
|
42
|
+
|
|
43
|
+
return work_path
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def process_daily_report(source_path: str, template_path: str) -> None:
|
|
47
|
+
"""处理日报:读取 → 排序 → 匹配日期写入 B-日报。"""
|
|
48
|
+
from tmday.core.reader import read_daily_report
|
|
49
|
+
from tmday.core.writer import write_daily_to_template
|
|
50
|
+
|
|
51
|
+
daily_rows = read_daily_report(source_path)
|
|
52
|
+
write_daily_to_template(daily_rows, template_path)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def process_shengyi_canmou(source_path: str, template_path: str) -> None:
|
|
56
|
+
"""处理生意参谋:读取 → 跳过空行 → 匹配日期写入 B-商品。"""
|
|
57
|
+
from tmday.core.reader import read_shengyi_canmou
|
|
58
|
+
from tmday.core.writer import write_shengyi_to_template
|
|
59
|
+
|
|
60
|
+
rows = read_shengyi_canmou(source_path)
|
|
61
|
+
write_shengyi_to_template(rows, template_path)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def process_product_report(source_path: str, template_path: str) -> None:
|
|
65
|
+
"""处理商品报表:读取 zip/csv → 匹配 日期+场景ID 写入 B-无界。"""
|
|
66
|
+
from tmday.core.reader import read_product_report
|
|
67
|
+
from tmday.core.writer import write_product_to_template
|
|
68
|
+
|
|
69
|
+
rows = read_product_report(source_path)
|
|
70
|
+
write_product_to_template(rows, template_path)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _not_implemented(name: str) -> None:
|
|
74
|
+
raise NotImplementedError(f"{name} 处理逻辑尚未实现")
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""读取源数据表格 (.xlsx/.xls/.csv)"""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import openpyxl
|
|
7
|
+
import xlrd
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def read_daily_report(filepath: str) -> list[list[Any]]:
|
|
11
|
+
"""读取日报文件,按统计日期升序排列(远→近)。
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
排序后的行数据列表,每行为完整的一行(不含表头)
|
|
15
|
+
"""
|
|
16
|
+
wb = openpyxl.load_workbook(filepath, data_only=True)
|
|
17
|
+
ws = wb[wb.sheetnames[0]]
|
|
18
|
+
|
|
19
|
+
rows = []
|
|
20
|
+
for row in ws.iter_rows(min_row=2, values_only=True):
|
|
21
|
+
if row[0] is None:
|
|
22
|
+
continue
|
|
23
|
+
date_val = row[0]
|
|
24
|
+
if isinstance(date_val, str):
|
|
25
|
+
date_val = datetime.strptime(date_val, "%Y-%m-%d")
|
|
26
|
+
rows.append((date_val, list(row)))
|
|
27
|
+
|
|
28
|
+
wb.close()
|
|
29
|
+
rows.sort(key=lambda r: r[0])
|
|
30
|
+
return [r[1] for r in rows]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def read_shengyi_canmou(filepath: str) -> list[list[Any]]:
|
|
34
|
+
"""读取生意参谋导出文件 (.xls),跳过空行,返回数据行列表。"""
|
|
35
|
+
wb = xlrd.open_workbook(filepath)
|
|
36
|
+
ws = wb.sheet_by_index(0)
|
|
37
|
+
|
|
38
|
+
header_row = _find_header_row_xls(ws)
|
|
39
|
+
if header_row is None:
|
|
40
|
+
raise ValueError("生意参谋文件中未找到表头行")
|
|
41
|
+
|
|
42
|
+
rows = []
|
|
43
|
+
for i in range(header_row + 1, ws.nrows):
|
|
44
|
+
row = [ws.cell_value(i, j) for j in range(ws.ncols)]
|
|
45
|
+
if any(str(v).strip() for v in row):
|
|
46
|
+
rows.append(row)
|
|
47
|
+
return rows
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def read_product_report(filepath: str) -> list[list[Any]]:
|
|
51
|
+
"""读取商品报表 zip 文件,提取 CSV,解析后返回数据行列表。
|
|
52
|
+
|
|
53
|
+
日期列从 col 0 开始,按日期升序排列。
|
|
54
|
+
"""
|
|
55
|
+
import csv
|
|
56
|
+
import io
|
|
57
|
+
import zipfile
|
|
58
|
+
|
|
59
|
+
import chardet
|
|
60
|
+
|
|
61
|
+
with zipfile.ZipFile(filepath) as zf:
|
|
62
|
+
csv_names = [n for n in zf.namelist() if n.lower().endswith(".csv")]
|
|
63
|
+
if not csv_names:
|
|
64
|
+
raise ValueError("zip 中未找到 csv 文件")
|
|
65
|
+
raw = zf.read(csv_names[0])
|
|
66
|
+
encoding = chardet.detect(raw)["encoding"] or "utf-8"
|
|
67
|
+
text = raw.decode(encoding)
|
|
68
|
+
reader = csv.reader(io.StringIO(text))
|
|
69
|
+
header = next(reader)
|
|
70
|
+
|
|
71
|
+
rows = []
|
|
72
|
+
for row in reader:
|
|
73
|
+
if any(v.strip() for v in row):
|
|
74
|
+
rows.append(row)
|
|
75
|
+
# 按日期升序
|
|
76
|
+
rows.sort(key=lambda r: r[0])
|
|
77
|
+
return rows
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _find_header_row_xls(ws) -> int | None:
|
|
81
|
+
"""在 xls 工作表中查找表头行。"""
|
|
82
|
+
for i in range(ws.nrows):
|
|
83
|
+
vals = [str(ws.cell_value(i, j)).strip() for j in range(min(ws.ncols, 40))]
|
|
84
|
+
if any(vals) and vals[0] == "统计日期":
|
|
85
|
+
return i
|
|
86
|
+
return None
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""各文件类型的表头定义与常量"""
|
|
2
|
+
|
|
3
|
+
TEMPLATE_SHEETS = ["月报", "日报", "B-日报", "B-刷单"]
|
|
4
|
+
|
|
5
|
+
DAILY_REPORT_HEADER = [
|
|
6
|
+
"统计日期", "店铺名称", "访客数", "平均停留时长", "跳失率",
|
|
7
|
+
"商品收藏买家数", "加购人数", "支付金额", "支付买家数",
|
|
8
|
+
"支付子订单数", "支付件数", "老买家支付金额", "全站推广花费",
|
|
9
|
+
"关键词推广花费", "精准人群推广花费", "智能场景花费",
|
|
10
|
+
"淘宝客佣金", "成功退款金额", "支付商品数", "关注店铺人数",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
SHENGYI_CANMOU_HEADER = [
|
|
14
|
+
"统计日期", "商品ID", "商品名称", "主商品ID", "商品类型",
|
|
15
|
+
"货号", "商品状态", "商品标签", "商品访客数", "商品浏览量",
|
|
16
|
+
"平均停留时长", "商品详情页跳出率", "商品收藏人数",
|
|
17
|
+
"商品加购件数", "商品加购人数", "下单买家数", "下单件数",
|
|
18
|
+
"下单金额", "下单转化率", "支付买家数", "支付件数",
|
|
19
|
+
"支付金额", "商品支付转化率", "支付新买家数", "支付老买家数",
|
|
20
|
+
"老买家支付金额", "聚划算支付金额", "访客平均价值",
|
|
21
|
+
"成功退款金额", "竞争力评分", "年累计支付金额",
|
|
22
|
+
"月累计支付金额", "月累计支付件数", "搜索引导支付转化率",
|
|
23
|
+
"搜索引导访客数", "搜索引导支付买家数",
|
|
24
|
+
"结构化详情引导转化率", "结构化详情引导成交占比",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
PRODUCT_REPORT_HEADER = [
|
|
28
|
+
"日期", "场景ID", "场景名字", "原二级场景ID", "原二级场景名字",
|
|
29
|
+
"计划ID", "计划名字", "主体ID", "主体类型", "主体名称",
|
|
30
|
+
"展现量", "点击量", "花费", "点击率", "平均点击花费",
|
|
31
|
+
"千次展现花费", "总预售成交金额", "总预售成交笔数",
|
|
32
|
+
"直接预售成交金额", "直接预售成交笔数", "间接预售成交金额",
|
|
33
|
+
"间接预售成交笔数", "直接成交金额", "间接成交金额",
|
|
34
|
+
"总成交金额", "总成交笔数", "直接成交笔数", "间接成交笔数",
|
|
35
|
+
"点击转化率", "投入产出比", "含预售投产比", "总成交成本",
|
|
36
|
+
"总购物车数", "直接购物车数", "间接购物车数", "加购率",
|
|
37
|
+
"收藏宝贝数", "收藏店铺数", "店铺收藏成本", "总收藏加购数",
|
|
38
|
+
"总收藏加购成本", "宝贝收藏加购数", "宝贝收藏加购成本",
|
|
39
|
+
"总收藏数", "宝贝收藏成本", "宝贝收藏率", "加购成本",
|
|
40
|
+
"拍下订单笔数", "拍下订单金额", "直接收藏宝贝数",
|
|
41
|
+
"间接收藏宝贝数", "优惠券领取量", "购物金充值笔数",
|
|
42
|
+
"购物金充值金额", "旺旺咨询量", "引导访问量", "引导访问人数",
|
|
43
|
+
"引导访问潜客数", "引导访问潜客占比", "入会率", "入会量",
|
|
44
|
+
"引导访问率", "深度访问量", "平均访问页面数", "成交新客数",
|
|
45
|
+
"成交新客占比", "会员首购人数", "会员成交金额", "会员成交笔数",
|
|
46
|
+
"成交人数", "人均成交笔数", "人均成交金额", "自然流量转化金额",
|
|
47
|
+
"自然流量曝光量", "平台助推总成交", "平台助推直接成交",
|
|
48
|
+
"平台助推点击", "平台补贴金额", "补贴引导成交金额",
|
|
49
|
+
"发券补贴商品个数", "补贴引导成交人数",
|
|
50
|
+
]
|