@yionr/plan-kit 1.0.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.
Files changed (28) hide show
  1. package/cli.mjs +175 -0
  2. package/package.json +21 -0
  3. package/scripts/collect_monthly_data.py +126 -0
  4. package/scripts/config.py +38 -0
  5. package/scripts/create_next_week.py +85 -0
  6. package/scripts/generate_monthly_md.py +91 -0
  7. package/scripts/get_current_week.py +79 -0
  8. package/scripts/get_user_info.py +85 -0
  9. package/scripts/query_weekly_records.py +90 -0
  10. package/scripts/update_completion.py +69 -0
  11. package/skills/sab-monthly-summary/SKILL.md +85 -0
  12. package/skills/sab-monthly-summary/scripts/collect_monthly_data.py +126 -0
  13. package/skills/sab-monthly-summary/scripts/config.py +41 -0
  14. package/skills/sab-monthly-summary/scripts/create_next_week.py +85 -0
  15. package/skills/sab-monthly-summary/scripts/generate_monthly_md.py +91 -0
  16. package/skills/sab-monthly-summary/scripts/get_current_week.py +79 -0
  17. package/skills/sab-monthly-summary/scripts/get_user_info.py +85 -0
  18. package/skills/sab-monthly-summary/scripts/query_weekly_records.py +90 -0
  19. package/skills/sab-monthly-summary/scripts/update_completion.py +69 -0
  20. package/skills/sab-weekly-plan/SKILL.md +109 -0
  21. package/skills/sab-weekly-plan/scripts/collect_monthly_data.py +126 -0
  22. package/skills/sab-weekly-plan/scripts/config.py +41 -0
  23. package/skills/sab-weekly-plan/scripts/create_next_week.py +85 -0
  24. package/skills/sab-weekly-plan/scripts/generate_monthly_md.py +91 -0
  25. package/skills/sab-weekly-plan/scripts/get_current_week.py +79 -0
  26. package/skills/sab-weekly-plan/scripts/get_user_info.py +85 -0
  27. package/skills/sab-weekly-plan/scripts/query_weekly_records.py +90 -0
  28. package/skills/sab-weekly-plan/scripts/update_completion.py +69 -0
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env python3
2
+ """查部门人员表,返回当前用户的姓名、open_id、所属组。
3
+
4
+ 流程:
5
+ 1. lark-cli contact +get-user --as user → 获取当前用户 open_id
6
+ 2. lark-cli base +record-search → 在部门人员表按人员字段筛选
7
+ 3. 返回 {open_id, name, group}
8
+
9
+ 用法: python3 get_user_info.py
10
+ 输出: {"open_id": "ou_xxx", "name": "张三", "group": "移动"}
11
+ """
12
+
13
+ import json
14
+ import subprocess
15
+ import sys
16
+ import os
17
+
18
+ # 允许脚本从上级目录 import config
19
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
20
+ import config
21
+
22
+
23
+ def run_lark(args):
24
+ """执行 lark-cli 命令并返回 JSON 结果。失败时退出。"""
25
+ result = subprocess.run(
26
+ ["lark-cli"] + args,
27
+ capture_output=True,
28
+ text=True,
29
+ timeout=30,
30
+ )
31
+ try:
32
+ data = json.loads(result.stdout)
33
+ except json.JSONDecodeError:
34
+ print(json.dumps({"error": "parse_failed", "stdout": result.stdout[:500], "stderr": result.stderr[:500]}, ensure_ascii=False))
35
+ sys.exit(1)
36
+
37
+ if not data.get("ok"):
38
+ print(json.dumps({"error": "lark_cli_failed", "detail": data.get("error", {})}, ensure_ascii=False))
39
+ sys.exit(1)
40
+
41
+ return data
42
+
43
+
44
+ def main():
45
+ # 1. 获取当前用户
46
+ user_data = run_lark(["contact", "+get-user", "--as", "user"])
47
+ user = user_data["data"]["user"]
48
+ open_id = user["open_id"]
49
+ name = user["name"]
50
+
51
+ # 2. 在部门人员表中查找
52
+ record_data = run_lark([
53
+ "base", "+record-list",
54
+ "--base-token", config.BASE_TOKEN,
55
+ "--table-id", config.TABLE_IDS["部门人员"],
56
+ "--filter-json", json.dumps(
57
+ {"logic": "and", "conditions": [
58
+ ["人员", "intersects", [{"id": open_id}]],
59
+ ]},
60
+ ensure_ascii=False
61
+ ),
62
+ "--as", "user",
63
+ "--format", "json",
64
+ ])
65
+
66
+ records = record_data["data"]["data"]
67
+ if not records:
68
+ print(json.dumps({"error": "user_not_found", "open_id": open_id, "name": name}, ensure_ascii=False))
69
+ sys.exit(1)
70
+
71
+ # fields 顺序: [人名, 人员, 所属组, 推送日期]
72
+ record = records[0]
73
+ group_arr = record[2] # 所属组
74
+ group = group_arr[0] if group_arr else ""
75
+
76
+ result = {
77
+ "open_id": open_id,
78
+ "name": name,
79
+ "group": group,
80
+ }
81
+ print(json.dumps(result, ensure_ascii=False))
82
+
83
+
84
+ if __name__ == "__main__":
85
+ main()
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env python3
2
+ """按用户和月周查询周计划记录。
3
+
4
+ 用法: python3 query_weekly_records.py --open-id ou_xxx --week "7月第4周"
5
+ 输出: JSON 数组
6
+ """
7
+
8
+ import json
9
+ import subprocess
10
+ import sys
11
+ import argparse
12
+ import os
13
+
14
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
15
+ import config
16
+
17
+
18
+ def run_lark(args):
19
+ result = subprocess.run(
20
+ ["lark-cli"] + args,
21
+ capture_output=True,
22
+ text=True,
23
+ timeout=30,
24
+ )
25
+ data = json.loads(result.stdout)
26
+ if not data.get("ok"):
27
+ print(json.dumps({"error": "lark_cli_failed", "detail": data.get("error", {})}, ensure_ascii=False))
28
+ sys.exit(1)
29
+ return data
30
+
31
+
32
+ def main():
33
+ parser = argparse.ArgumentParser()
34
+ parser.add_argument("--open-id", required=True)
35
+ parser.add_argument("--week", required=True)
36
+ args = parser.parse_args()
37
+
38
+ open_id = args.open_id
39
+ week = args.week
40
+
41
+ data = run_lark([
42
+ "base", "+record-list",
43
+ "--base-token", config.BASE_TOKEN,
44
+ "--table-id", config.TABLE_IDS["周计划"],
45
+ "--filter-json", json.dumps(
46
+ {"logic": "and", "conditions": [
47
+ ["月周", "intersects", [week]],
48
+ ["责任人", "intersects", [{"id": open_id}]],
49
+ ]},
50
+ ensure_ascii=False
51
+ ),
52
+ "--as", "user",
53
+ "--format", "json",
54
+ ])
55
+
56
+ records = data["data"]["data"]
57
+ field_ids = data["data"]["field_id_list"]
58
+
59
+ idx_content = field_ids.index("fldjdmxh69")
60
+ idx_completion = field_ids.index("fldF9ebqDi")
61
+ idx_remark = field_ids.index("fldu5uAqGU")
62
+ idx_hours = field_ids.index("fldQXhORVG")
63
+ idx_week = field_ids.index("fldAivwku1")
64
+
65
+ result = []
66
+ for i, rec in enumerate(records):
67
+ def safe_val(arr, default=""):
68
+ if isinstance(arr, list):
69
+ return arr[0] if arr else default
70
+ if arr is None:
71
+ return default
72
+ return arr
73
+
74
+ record_id = (
75
+ rec.get("_record_id") if isinstance(rec, dict) else None
76
+ ) or (data["data"].get("record_id_list") or [None])[i] or "rec_{}".format(i)
77
+ result.append({
78
+ "record_id": record_id,
79
+ "content": safe_val(rec[idx_content]),
80
+ "completion_status": safe_val(rec[idx_completion]),
81
+ "remark": safe_val(rec[idx_remark]),
82
+ "hours": safe_val(rec[idx_hours], None),
83
+ "week": safe_val(rec[idx_week]),
84
+ })
85
+
86
+ print(json.dumps(result, ensure_ascii=False))
87
+
88
+
89
+ if __name__ == "__main__":
90
+ main()
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env python3
2
+ """批量更新周计划记录的完成情况和备注。
3
+
4
+ 用法: python3 update_completion.py --records '<json_array>'
5
+ json_array 格式: [{"record_id": "rec_xxx", "completion": "完成", "remark": "..."}]
6
+
7
+ 输出: {"ok": true, "updated": N}
8
+ """
9
+
10
+ import json
11
+ import subprocess
12
+ import sys
13
+ import argparse
14
+ import os
15
+
16
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
17
+ import config
18
+
19
+
20
+ def main():
21
+ parser = argparse.ArgumentParser()
22
+ parser.add_argument("--records", required=True)
23
+ args = parser.parse_args()
24
+
25
+ try:
26
+ records = json.loads(args.records)
27
+ except json.JSONDecodeError:
28
+ print(json.dumps({"ok": False, "error": "invalid_json"}, ensure_ascii=False))
29
+ sys.exit(1)
30
+
31
+ update_records = {}
32
+ for r in records:
33
+ rid = r["record_id"]
34
+ fields = {}
35
+ if "completion" in r and r["completion"]:
36
+ fields["完成情况"] = r["completion"]
37
+ if "remark" in r:
38
+ fields["备注"] = r["remark"] if r["remark"] else ""
39
+ update_records[rid] = fields
40
+
41
+ payload = json.dumps({"update_records": update_records}, ensure_ascii=False)
42
+
43
+ result = subprocess.run(
44
+ [
45
+ "lark-cli", "base", "+record-batch-update",
46
+ "--base-token", config.BASE_TOKEN,
47
+ "--table-id", config.TABLE_IDS["周计划"],
48
+ "--json", payload,
49
+ "--as", "user",
50
+ ],
51
+ capture_output=True,
52
+ text=True,
53
+ timeout=30,
54
+ )
55
+
56
+ try:
57
+ data = json.loads(result.stdout)
58
+ except json.JSONDecodeError:
59
+ data = json.loads(result.stderr) if result.stderr else {"ok": False}
60
+
61
+ if data.get("ok"):
62
+ print(json.dumps({"ok": True, "updated": len(update_records)}, ensure_ascii=False))
63
+ else:
64
+ print(json.dumps({"ok": False, "error": data.get("error", {})}, ensure_ascii=False))
65
+ sys.exit(1)
66
+
67
+
68
+ if __name__ == "__main__":
69
+ main()
@@ -0,0 +1,85 @@
1
+ ---
2
+ name: sab-monthly-summary
3
+ description: >-
4
+ 读取上月飞书多维表格"周计划"数据,按模板格式生成飞书云文档月总结。
5
+ 当用户说"生成本月总结"、"写月总结"、"生成X月工作总结"时就使用此skill。
6
+ ---
7
+
8
+ # SAB 月总结生成
9
+
10
+ ## 前置依赖
11
+
12
+ - `lark-cli` 已安装并认证
13
+ - Python 脚本位于本 skill 目录下的 `scripts/` 中
14
+ - 依赖 `lark-doc` skill 用于创建飞书文档
15
+
16
+ ## 重要规则
17
+
18
+ 1. **只汇总用户自己的记录**
19
+ 2. **所有数据采集通过固化脚本**,AI 负责生成章节二、三、四的分析内容
20
+ 3. **章节一表格严格来自数据**,不要增删或篡改
21
+ 4. **脚本路径**:本 skill 的 SKILL.md 所在目录下的 `scripts/`
22
+
23
+ ## 流程
24
+
25
+ ### 步骤 1:定位用户
26
+
27
+ ```bash
28
+ python3 scripts/get_user_info.py
29
+ ```
30
+
31
+ ### 步骤 2:确定目标月份
32
+
33
+ 如果用户没说具体月份,默认为上个月。
34
+
35
+ ### 步骤 3:汇总数据
36
+
37
+ ```bash
38
+ python3 scripts/collect_monthly_data.py --open-id <open_id> --month <month>
39
+ ```
40
+
41
+ 输出 JSON,含 `records`(所有周计划记录)和 `temp_work`(从备注提取的临时工作)。
42
+
43
+ ### 步骤 4:生成 Markdown
44
+
45
+ ```bash
46
+ python3 scripts/generate_monthly_md.py --data '<json_from_step3>'
47
+ ```
48
+
49
+ 输出包含完整章节骨架的 Markdown:
50
+ - **章节一** 的表格已完整填充(数据驱动,包含临时工作)
51
+ - **章节二、三、四** 标注了原材料数据和 AI 生成提示
52
+
53
+ ### 步骤 5:AI 补充分析内容
54
+
55
+ 基于 Markdown 中的原材料,生成分析文字:
56
+
57
+ - **章节二 重点工作回顾**:从已完成项中挑选 1-3 项最有价值的,写背景、过程和成果
58
+ - **章节三 存在问题及分析**:从备注/未完成原因中汇总共性问题
59
+ - **章节四 下月工作重点**:列出未完成项 + 跟进措施
60
+
61
+ ### 步骤 6:创建飞书文档
62
+
63
+ 使用 `lark-doc` skill 创建飞书云文档:
64
+
65
+ - **标题**: `信息技术科YYYY年M月工作总结--{姓名}`
66
+ - **内容**: 补全后的 Markdown
67
+
68
+ ## 文档格式模板
69
+
70
+ ```
71
+ # 信息技术科 YYYY年M月工作总结
72
+ {姓名}
73
+
74
+ ## 一、工作计划完成情况(包括计划外完成)
75
+ (表格)
76
+
77
+ ## 二、重点工作回顾
78
+ (AI 生成)
79
+
80
+ ## 三、存在问题及分析
81
+ (AI 生成)
82
+
83
+ ## 四、下月工作重点
84
+ (AI 生成)
85
+ ```
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env python3
2
+ """汇总指定月份所有周计划记录。
3
+
4
+ 用法: python3 collect_monthly_data.py --open-id ou_xxx --month 7
5
+ 输出: JSON
6
+ """
7
+
8
+ import json
9
+ import subprocess
10
+ import sys
11
+ import argparse
12
+ import os
13
+
14
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
15
+ import config
16
+
17
+
18
+ def run_lark(args):
19
+ result = subprocess.run(
20
+ ["lark-cli"] + args,
21
+ capture_output=True,
22
+ text=True,
23
+ timeout=30,
24
+ )
25
+ try:
26
+ data = json.loads(result.stdout)
27
+ except json.JSONDecodeError:
28
+ print(json.dumps({"error": "parse_failed", "stdout": result.stdout[:500], "stderr": result.stderr[:500]}, ensure_ascii=False))
29
+ sys.exit(1)
30
+
31
+ if not data.get("ok"):
32
+ print(json.dumps({"error": "lark_cli_failed", "detail": data.get("error", {})}, ensure_ascii=False))
33
+ sys.exit(1)
34
+ return data
35
+
36
+
37
+ def safe_val(arr, default=""):
38
+ if isinstance(arr, list):
39
+ return arr[0] if arr else default
40
+ if arr is None:
41
+ return default
42
+ return arr
43
+
44
+
45
+ def main():
46
+ parser = argparse.ArgumentParser()
47
+ parser.add_argument("--open-id", required=True)
48
+ parser.add_argument("--month", required=True, type=int)
49
+ args = parser.parse_args()
50
+
51
+ month = args.month
52
+
53
+ # 先查所有记录(按责任人筛选,内存中再按月份过滤)
54
+ data = run_lark([
55
+ "base", "+record-list",
56
+ "--base-token", config.BASE_TOKEN,
57
+ "--table-id", config.TABLE_IDS["周计划"],
58
+ "--filter-json", json.dumps(
59
+ {"logic": "and", "conditions": [
60
+ ["责任人", "intersects", [{"id": args.open_id}]],
61
+ ]},
62
+ ensure_ascii=False
63
+ ),
64
+ "--as", "user",
65
+ "--format", "json",
66
+ ])
67
+
68
+ records = data["data"]["data"]
69
+ field_ids = data["data"]["field_id_list"]
70
+
71
+ idx_content = field_ids.index("fldjdmxh69")
72
+ idx_completion = field_ids.index("fldF9ebqDi")
73
+ idx_remark = field_ids.index("fldu5uAqGU")
74
+ idx_hours = field_ids.index("fldQXhORVG")
75
+ idx_week = field_ids.index("fldAivwku1")
76
+
77
+ all_records = []
78
+ seen_ids = set()
79
+
80
+ for i, rec in enumerate(records):
81
+ record_id = rec.get("_record_id") if isinstance(rec, dict) else None
82
+ record_id = record_id or (data["data"].get("record_id_list") or [None])[i] or "rec_{}".format(i)
83
+ if record_id in seen_ids:
84
+ continue
85
+ seen_ids.add(record_id)
86
+
87
+ week_val = safe_val(rec[idx_week])
88
+ # 过滤:只保留目标月份的记录
89
+ if week_val.startswith("{}月".format(month)):
90
+ all_records.append({
91
+ "record_id": record_id,
92
+ "content": safe_val(rec[idx_content]),
93
+ "completion_status": safe_val(rec[idx_completion]),
94
+ "remark": safe_val(rec[idx_remark]),
95
+ "hours": safe_val(rec[idx_hours], None),
96
+ "week": week_val,
97
+ })
98
+
99
+ # 提取临时工作(从备注中识别)
100
+ temp_work = []
101
+ skip_keywords = ["未完成", "延期", "搁置", "等待"]
102
+ for rec in all_records:
103
+ remark = rec.get("remark", "")
104
+ if remark and len(remark) > 10 and not any(k in remark for k in skip_keywords):
105
+ temp_work.append({
106
+ "record_id": rec["record_id"],
107
+ "content": remark,
108
+ "source_week": rec["week"],
109
+ })
110
+
111
+ user_data = run_lark(["contact", "+get-user", "--as", "user"])
112
+ name = user_data["data"]["user"]["name"]
113
+
114
+ result = {
115
+ "name": name,
116
+ "month": month,
117
+ "records": all_records,
118
+ "temp_work": temp_work,
119
+ "total_records": len(all_records),
120
+ "temp_work_count": len(temp_work),
121
+ }
122
+ print(json.dumps(result, ensure_ascii=False))
123
+
124
+
125
+ if __name__ == "__main__":
126
+ main()
@@ -0,0 +1,41 @@
1
+ """共享配置"""
2
+
3
+ BASE_TOKEN = "HBR3bvERVaooCUsFfxcc0fYLndd"
4
+
5
+ TABLE_IDS = {
6
+ "部门人员": "tblxsJA46bTLlUEE",
7
+ "周计划": "tblN6nUNSwah6j2v",
8
+ "月计划": "tblLb0SsT38HFf8X",
9
+ }
10
+
11
+ DEFAULT_DEPT = "信息技术"
12
+
13
+ # 人员字段映射
14
+ PERSONNEL_FIELDS = {
15
+ "人员": "fldbGCCdNV",
16
+ "所属组": "flddUR4K4c",
17
+ "人名": "fldrI663hx",
18
+ }
19
+
20
+ # 周计划可写字段
21
+ WEEKLY_WRITABLE_FIELDS = [
22
+ "责任人", # fldOavYzSr, user
23
+ "月周", # fldAivwku1, select
24
+ "工作计划内容", # fldjdmxh69, text
25
+ "工时", # fldQXhORVG, number
26
+ "完成情况", # fldF9ebqDi, select
27
+ "备注", # fldu5uAqGU, text
28
+ "计划来源", # fldIvgrltV, select
29
+ "业务组", # fldgFNrrqP, select
30
+ ]
31
+
32
+ # 周计划只读字段(不要写)
33
+ WEEKLY_READONLY_FIELDS = [
34
+ "创建时间", # fldJrGqC4Y, created_at
35
+ "修改人", # fldMZjLGsr, updated_by
36
+ "创建人", # fldnYVOlV6, created_by
37
+ "最后更新时间", # fldzy7XDqO, updated_at
38
+ ]
39
+
40
+ # 脚本可被 sab-weekly-plan 和 sab-monthly-summary 两个 skill 引用
41
+ # SKILL.md 中的引用路径: ../sab-shared/scripts/
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env python3
2
+ """创建下周周计划记录:迁移未完成项 + 新建用户口述的计划。
3
+
4
+ 用法:
5
+ python3 create_next_week.py \
6
+ --open-id ou_xxx \
7
+ --group 移动 \
8
+ --next-week "7月第5周" \
9
+ --migrated '[{"content":"xxx","hours":8}]' \
10
+ --new '[{"content":"yyy","hours":4}]'
11
+ """
12
+
13
+ import json
14
+ import subprocess
15
+ import sys
16
+ import argparse
17
+ import os
18
+
19
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
20
+ import config
21
+
22
+
23
+ def main():
24
+ parser = argparse.ArgumentParser()
25
+ parser.add_argument("--open-id", required=True)
26
+ parser.add_argument("--group", required=True)
27
+ parser.add_argument("--next-week", required=True)
28
+ parser.add_argument("--migrated", default="[]")
29
+ parser.add_argument("--new", default="[]")
30
+ args = parser.parse_args()
31
+
32
+ try:
33
+ migrated = json.loads(args.migrated)
34
+ new_items = json.loads(args.new)
35
+ except json.JSONDecodeError as e:
36
+ print(json.dumps({"ok": False, "error": "invalid_json", "detail": str(e)}, ensure_ascii=False))
37
+ sys.exit(1)
38
+
39
+ all_items = migrated + new_items
40
+ if not all_items:
41
+ print(json.dumps({"ok": True, "created": 0}, ensure_ascii=False))
42
+ return
43
+
44
+ fields = ["责任人", "月周", "工作计划内容", "工时", "业务组", "计划来源"]
45
+ rows = []
46
+ for item in all_items:
47
+ row = [
48
+ [{"id": args.open_id}],
49
+ args.next_week,
50
+ item.get("content", ""),
51
+ item.get("hours"),
52
+ args.group,
53
+ item.get("source", "新增计划"),
54
+ ]
55
+ rows.append(row)
56
+
57
+ payload = json.dumps({"fields": fields, "rows": rows}, ensure_ascii=False)
58
+
59
+ result = subprocess.run(
60
+ [
61
+ "lark-cli", "base", "+record-batch-create",
62
+ "--base-token", config.BASE_TOKEN,
63
+ "--table-id", config.TABLE_IDS["周计划"],
64
+ "--json", payload,
65
+ "--as", "user",
66
+ ],
67
+ capture_output=True,
68
+ text=True,
69
+ timeout=30,
70
+ )
71
+
72
+ try:
73
+ data = json.loads(result.stdout)
74
+ except json.JSONDecodeError:
75
+ data = json.loads(result.stderr) if result.stderr else {"ok": False}
76
+
77
+ if data.get("ok"):
78
+ print(json.dumps({"ok": True, "created": len(rows)}, ensure_ascii=False))
79
+ else:
80
+ print(json.dumps({"ok": False, "error": data.get("error", {})}, ensure_ascii=False))
81
+ sys.exit(1)
82
+
83
+
84
+ if __name__ == "__main__":
85
+ main()
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env python3
2
+ """按模板生成月总结 Markdown。
3
+
4
+ 用法: python3 generate_monthly_md.py --data '<json_from_collect>'
5
+ 输出: Markdown 文本到 stdout
6
+ """
7
+
8
+ import json
9
+ import sys
10
+ import argparse
11
+
12
+
13
+ def main():
14
+ parser = argparse.ArgumentParser()
15
+ parser.add_argument("--data", required=True)
16
+ args = parser.parse_args()
17
+
18
+ try:
19
+ data = json.loads(args.data)
20
+ except json.JSONDecodeError as e:
21
+ print("ERROR: invalid JSON: {}".format(e), file=sys.stderr)
22
+ sys.exit(1)
23
+
24
+ name = data.get("name", "")
25
+ month = data.get("month", "")
26
+ records = data.get("records", [])
27
+ temp_work = data.get("temp_work", [])
28
+ year = 2026
29
+
30
+ lines = []
31
+ lines.append("# 信息技术科 {}年{}月工作总结\n".format(year, month))
32
+ lines.append(name + "\n")
33
+
34
+ # 一、工作计划完成情况
35
+ lines.append("## 一、工作计划完成情况(包括计划外完成)\n")
36
+
37
+ table_rows = []
38
+ idx = 0
39
+
40
+ for rec in records:
41
+ idx += 1
42
+ completion = rec.get("completion_status", "")
43
+ remark = rec.get("remark", "")
44
+ follow_up = ""
45
+ if completion == "未完成":
46
+ follow_up = "移至下月继续跟进"
47
+
48
+ table_rows.append("| {} | {} | {} | {} | {} |".format(
49
+ idx, rec.get("content", ""), completion, remark, follow_up
50
+ ))
51
+
52
+ # 临时工作也插入表格
53
+ for tw in temp_work:
54
+ idx += 1
55
+ table_rows.append("| {} | {} | {} | {} | {} |".format(
56
+ idx, tw.get("content", ""), "(临时工作)", tw.get("content", "")[:50], "-"
57
+ ))
58
+
59
+ if table_rows:
60
+ lines.append("| 序号 | 内容 | 完成情况 | 原因说明 | 跟进措施 |")
61
+ lines.append("|------|------|---------|---------|---------|")
62
+ lines.extend(table_rows)
63
+ lines.append("")
64
+
65
+ # 二、重点工作回顾
66
+ lines.append("## 二、重点工作回顾\n")
67
+ completed = [r for r in records if r.get("completion_status") == "完成"]
68
+ if completed:
69
+ lines.append("<!-- AI: 从以下已完成项中提炼1-3个重点工作 -->")
70
+ for r in completed[:5]:
71
+ lines.append("<!-- - {} -->".format(r.get("content", "")))
72
+ lines.append("(待 AI 根据上述材料生成)\n")
73
+
74
+ # 三、存在问题及分析
75
+ lines.append("## 三、存在问题及分析\n")
76
+ undone = [r for r in records if r.get("completion_status") == "未完成"]
77
+ if undone:
78
+ lines.append("<!-- AI: 从以下未完成项汇总共性问题 -->")
79
+ for r in undone:
80
+ lines.append("<!-- - {}: {} -->".format(r.get("content", ""), r.get("remark", "")))
81
+ lines.append("(待 AI 根据上述材料生成)\n")
82
+
83
+ # 四、下月工作重点
84
+ lines.append("## 四、下月工作重点\n")
85
+ lines.append("(待 AI 根据上述材料生成)\n")
86
+
87
+ print("\n".join(lines))
88
+
89
+
90
+ if __name__ == "__main__":
91
+ main()