我写了一个“自动写周报”的脚本,结果被领导表扬了
我是豪彡,从建筑设计院跑路转行 C# 上位机开发。这篇文章记录我写“自动写周报”脚本的真实经历——不聊虚的,直接上代码,顺便说说我踩过的坑,以及这个脚本最后是怎么“倒逼”我把团队 Git 提交规范建立起来的。
一、先说一个让我头疼的事
我们公司有个规矩:每周五下午五点之前,须交周报。
周报的格式是这样的:
本周工作总结:
1. 完成了XX设备的通信调试
2. 修复了XX模块的3个bug
3. 配合测试完成了XX功能的验证
下周工作计划:
1. 继续调试XX设备
2. 优化XX模块的性能
3. 完成XX功能的测试
遇到的问题:
1. XX设备通信不稳定,需要进一步排查
2. XX模块在高并发下偶发超时
说实话,写周报这件事本身不复杂,但烦。
烦在哪?不是写不出来,是每周都要把做过的事情重新回忆一遍。我这周改了哪些 bug、调了哪些设备、写了哪些功能——到了周五下午,我得翻 Git 提交记录、翻任务管理工具、翻聊天记录,才能凑出一份像样的周报。
有一次,我实在想不起来周一干了什么,就翻了翻 Git log。发现那天我提交了一个“修复设备状态显示异常”的 commit。但我已经完全忘了那个 bug 是什么了。
那一刻我意识到:我的工作记录,比我的记忆更可靠。
于是我想:能不能写个脚本,自动从我的工作痕迹里提取信息,帮我生成周报草稿?
二、第一版:从 Git log 里“捞”内容
我最先想到的是 Git。
因为我的代码提交记录里,其实已经包含了很多信息。每个 commit 的 message,就是我当时对“做了什么”的描述。
# 看看我上周提交了什么
git log --since="1 week ago" --pretty=format:"%s" --author="haoshan"
输出大概是这样的:
修复设备状态显示异常
优化PLC通信超时处理
新增温度监控页面
修复报警记录导出格式问题
调整设备列表排序逻辑
这些 commit message,其实已经是一份“粗糙的周报”了。只是它们太零散,需要整理。
我写了一个 Python 脚本,把这些 commit message 分类整理:
import subprocess
import re
from datetime import datetime, timedelta
def get_git_log(days=7, author=None):
since = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
cmd = ['git', 'log', f'--since={since}', '--pretty=format:%s|%ad', '--date=short']
if author:
cmd.insert(2, f'--author={author}')
result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
return result.stdout.strip().split('\n')
def categorize_commits(commits):
categories = {
'修复': [],
'新增': [],
'优化': [],
'其他': []
}
for commit in commits:
if not commit:
continue
message = commit.split('|')[0]
if message.startswith('修复') or 'fix' in message.lower():
categories['修复'].append(message)
elif message.startswith('新增') or message.startswith('添加') or 'add' in message.lower():
categories['新增'].append(message)
elif message.startswith('优化') or message.startswith('调整') or 'optimize' in message.lower():
categories['优化'].append(message)
else:
categories['其他'].append(message)
return categories
def generate_weekly_report():
commits = get_git_log(days=7)
categories = categorize_commits(commits)
report = "本周工作总结:\n"
if categories['新增']:
report += "\n【新增功能】\n"
for item in categories['新增']:
report += f"- {item}\n"
if categories['修复']:
report += "\n【修复问题】\n"
for item in categories['修复']:
report += f"- {item}\n"
if categories['优化']:
report += "\n【优化改进】\n"
for item in categories['优化']:
report += f"- {item}\n"
if categories['其他']:
report += "\n【其他工作】\n"
for item in categories['其他']:
report += f"- {item}\n"
report += "\n下周工作计划:\n"
report += "- (待补充)\n"
report += "\n遇到的问题:\n"
report += "- (待补充)\n"
return report
if __name__ == '__main__':
print(generate_weekly_report())
跑了一下,输出大概是:
本周工作总结:
【修复问题】
- 修复设备状态显示异常
- 修复报警记录导出格式问题
【新增功能】
- 新增温度监控页面
【优化改进】
- 优化PLC通信超时处理
- 调整设备列表排序逻辑
下周工作计划:
- (待补充)
遇到的问题:
- (待补充)
看到这个输出的时候,我有点感动。 因为它真的把我这周做过的事情,整理成了一份“周报草稿”。虽然还需要手动补充“下周计划”和“遇到的问题”,但至少“本周工作总结”这部分,已经不用我从头回忆了。
三、第二版:加上“任务管理工具”的数据
Git log 只能反映“代码层面”的工作。但很多时候,我的工作不只是写代码——还有开会、写文档、调试设备、和同事沟通。
这些信息,散落在各种地方。我用的任务管理工具是 Jira(我们公司用的),上面有我的任务状态和工时记录。
我加了一个从 Jira API 拉取数据的模块:
import requests
from requests.auth import HTTPBasicAuth
def get_jira_issues(base_url, email, api_token, project_key, days=7):
since = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
url = f"{base_url}/rest/api/2/search"
query = {
'jql': f'project={project_key} AND assignee=currentUser() AND updated>={since}',
'fields': 'summary,status,issuetype',
'maxResults': 50
}
response = requests.get(
url,
params=query,
auth=HTTPBasicAuth(email, api_token),
headers={'Accept': 'application/json'}
)
if response.status_code != 200:
print(f"Jira请求失败: {response.status_code}")
return []
data = response.json()
issues = []
for issue in data.get('issues', []):
fields = issue['fields']
issues.append({
'key': issue['key'],
'summary': fields['summary'],
'status': fields['status']['name'],
'type': fields['issuetype']['name']
})
return issues
这个模块的输出大概是:
本周Jira任务:
【已完成】
- PROJ-123: 设备状态显示异常修复
- PROJ-125: 报警记录导出格式调整
- PROJ-128: 温度监控页面开发
【进行中】
- PROJ-130: PLC通信超时优化
【待处理】
- PROJ-132: 设备列表排序逻辑调整
把这部分和 Git log 整合在一起,周报的内容就更完整了。
四、第三版:加上“AI 润色”
到这里,周报的“素材”已经有了,但还比较粗糙。比如:
- 修复设备状态显示异常
- 优化PLC通信超时处理
这些是“技术描述”,但周报是给领导看的。领导想看到的不是“你做了什么”,而是“你做的事情对项目有什么价值”。
我加了一个“AI 润色”模块,用大模型 API 把技术描述转成“业务语言”:
def polish_with_ai(raw_report, api_key):
prompt = f"""
请把下面的技术周报润色成更专业、更简洁的版本。
要求:
1. 保留技术细节,但用更正式的语言表达
2. 突出工作成果和价值
3. 不要夸大,不要编造
原始内容:
{raw_report}
"""
response = requests.post(
'https://api.openai.com/v1/chat/completions',
headers={
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
},
json={
'model': 'gpt-3.5-turbo',
'messages': [{'role': 'user', 'content': prompt}],
'temperature': 0.5
}
)
data = response.json()
return data['choices'][0]['message']['content']
润色之后的输出大概是:
本周工作总结:
1. 完成设备状态显示异常的修复,解决了界面刷新不及时的问题
2. 优化PLC通信超时处理逻辑,提升了设备通信的稳定性
3. 新增温度监控页面,支持实时数据展示和历史曲线查看
4. 调整报警记录导出格式,使导出文件更符合下游系统的对接要求
这个版本,已经能直接交上去了。
五、第四版:让脚本真正“好用”——参数化与一键运行
脚本在我自己电脑上能跑之后,我发现了一个新问题:能跑 ≠ 好用。
真实用了两周,冒出来一堆小毛病:
- 脚本放在
G:\item\py git\,但我要统计的项目在E:\User_Guo\...\DisassemblyDrawing,不在 git 仓库目录里运行就什么都捞不到; - 默认只看 7 天,碰上国庆长假回来,我想看“最近 30 天”甚至“今年全部提交”,没法选时间;
- 结果只打印在黑窗口里,关掉就没了,还得手动复制;
- 我电脑装了 Python 但没配 PATH,每次要敲一长串解释器路径;
- 不是 git 仓库时脚本不报错,静默输出一个空报告,我还以为脚本坏了。
于是有了第四版,改动不大,但每一条都是被真实使用“逼”出来的。
5.1 支持传入任意仓库路径
用 subprocess.run(..., cwd=repo_path) 指定 git 的工作目录,这样脚本不用放在项目里,传一个路径参数就能分析任何仓库——而且传子目录也可以,git 会自己向上找到仓库根目录:
def get_git_log(days=7, author=None, repo_path=None):
cmd = ['git', 'log']
if days: # days 为 None 或 0 时统计全部历史
since = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
cmd.append(f'--since={since}')
cmd += ['--pretty=format:%s|%ad', '--date=short']
if author:
cmd.insert(2, f'--author={author}')
result = subprocess.run(cmd, capture_output=True, text=True,
encoding='utf-8', cwd=repo_path, errors='replace')
if result.returncode != 0:
raise RuntimeError(
f'git log 执行失败(路径:{repo_path or os.getcwd()})\n{result.stderr.strip()}'
)
return result.stdout.strip().split('\n')
注意两点:一是 returncode != 0 时直接抛异常,不再吞掉错误;二是加了 errors='replace',防止仓库里有非 UTF-8 的提交信息时整个脚本崩掉。
5.2 时间范围可选:一周、两周、一个月、全部
用 argparse 加了三个参数:
parser.add_argument('path', nargs='?', default=None,
help='Git 仓库路径(默认为当前目录)')
parser.add_argument('-d', '--days', type=int, default=7,
help='统计最近多少天的提交,默认 7 天;设为 0 表示全部历史')
parser.add_argument('--all', action='store_true',
help='统计仓库的全部提交记录(不限时间)')
parser.add_argument('-a', '--author', default=None,
help='只统计指定作者,支持姓名/邮箱关键字')
parser.add_argument('-o', '--output', default=None,
help='报告保存路径(默认保存在脚本所在目录)')
命令行下随便选:
python weekly_report.py "E:\...\DisassemblyDrawing" # 最近7天
python weekly_report.py "E:\...\DisassemblyDrawing" -d 14 # 最近2周
python weekly_report.py "E:\...\DisassemblyDrawing" -d 30 # 最近30天
python weekly_report.py "E:\...\DisassemblyDrawing" --all # 全部历史
python weekly_report.py "E:\...\DisassemblyDrawing" -a 豪彡 # 只看我自己的
报告标题会标明实际时间范围,防止交周报时自己都搞混:
工作总结(最近 30 天,2026-09-08 至今):
5.3 自动存成 Markdown 文件
结果除了打印,还会按日期自动落盘,不同时间范围互不覆盖:
weekly_report_2026-10-08.md ← 默认7天
weekly_report_2026-10-08_30d.md ← 最近30天
weekly_report_2026-10-08_all.md ← 全部历史
5.4 双击就能用:写了个 .bat 启动器
让同事也能用,最忌讳的就是让人家敲命令行。我写了个 生成周报.bat,双击后先选时间,再把仓库文件夹拖进窗口就行:
@echo off
setlocal
set "PY=C:\Users\Administrator\.workbuddy\binaries\python\versions\3.13.12\python.exe"
set "SCRIPT=%~dp0weekly_report.py"
echo Select time range:
echo 1 = Last 7 days (this week)
echo 2 = Last 14 days (2 weeks)
echo 3 = Last 30 days (1 month)
echo 4 = All commits (entire history)
set "CHOICE=1"
set /p "CHOICE=Enter choice [1]: "
if "%CHOICE%"=="2" (set "RANGE=-d 14") else if "%CHOICE%"=="3" (set "RANGE=-d 30") else if "%CHOICE%"=="4" (set "RANGE=--all") else (set "RANGE=-d 7")
if "%~1"=="" (
set /p "REPOPATH=Drag a git repo folder into this window, then press Enter: "
) else (
set "REPOPATH=%~1"
)
"%PY%" "%SCRIPT%" %RANGE% %REPOPATH%
pause
endlocal
(bat 里的提示我特意用英文写,是因为 cmd 默认 GBK 编码,中文 echo 在部分机器上会乱码,这是踩过的坑。)
5.5 在真实项目上验证
拿我手头那个 AutoCAD 拆图插件的仓库跑了一遍,全部历史一共九十多 KB 的提交,从 Initial commit 到最新的功能,全被分门别类捞了出来。我以为我这个月只干了三四件事,翻完报告才发现,我干了二十多件。
六、结果:被领导表扬了
第一次用这个脚本生成的周报交上去,我心里还有点虚。因为周报里有一半的内容是脚本生成的,我怕领导看出来“这不是你写的”。
结果周五下午,领导在群里 @ 我:
“豪彡这周的周报写得不错,内容清晰,重点突出。特别是‘优化 PLC 通信超时处理’这一条,把技术价值和业务影响都说清楚了。大家参考一下。”
我当时看到这条消息,心情很复杂。
一方面,确实被表扬了,开心。另一方面,我心里清楚,这份周报的“骨架”是脚本生成的,我只是做了最后的润色和补充。
但仔细想想,我其实并没有“偷懒”。 因为:
- 脚本只是帮我“整理素材”,核心的工作内容还是我自己做的;
- 我花在“优化脚本”上的时间,最终转化成了更高效的周报产出;
- 周报本身的质量确实提升了——因为脚本不会漏掉任何一条 commit,而我手动回忆的时候经常会漏。
换句话说:我把“写周报”这件事,从“回忆 + 整理 + 润色”变成了“确认 + 补充 + 提交”。
七、真正的关键:你的 Git 提交信息,值得认真写
用了一个月脚本,我发现一个扎心的事实:
脚本分类准不准,完全取决于 commit 写得规不规范。
看看我仓库里这些真实的提交信息:
update
update1
update2
update3
backup:重构业务代码备份代码1
配置更改测试
测试
fix
更新维度数据及相关文件
这些提交,脚本只能一股脑扔进【其他工作】。update 到底更新了啥?半年后的我自己都不记得。更要命的是,这种提交写周报时没法用,出了问题想回溯也没法查。
所以这一章,我想认真聊聊 Git 提交规范。这是这个脚本带给我的最大副产品。
7.1 业界主流:Conventional Commits(约定式提交)
目前最通用的是 Conventional Commits 规范,Angular 团队的规范是它的事实源头。结构长这样:
<类型>[可选范围]: <主题>
[可选正文]
[可选脚注]
一个典型例子:
fix(parser): 修复温度值超过1000时解析崩溃的问题
PLC返回的温度寄存器值为无符号整型,原解析逻辑按有符号短整型处理,
导致超过32767的值变成负数并触发格式异常。
改为按 ushort 解析并增加范围校验。
Closes #128
类型(type) 是规范的核心,常用的就这 10 个:
| 类型 | 含义 | 周报里对应 |
|---|---|---|
feat | 新功能 | 【新增功能】 |
fix | 修复 bug | 【修复问题】 |
perf | 性能优化(不改功能,只变快/省资源) | 【优化改进】 |
refactor | 重构(既不修 bug 也不加功能) | 【优化改进】 |
docs | 只改文档 | 【其他工作】 |
style | 格式调整(空格、缩进、分号,不改逻辑) | 一般不进周报 |
test | 新增/修改测试 | 一般不进周报 |
build | 构建系统、依赖、NuGet 包变动 | 【其他工作】 |
ci | CI/CD 流水线变动 | 一般不进周报 |
chore | 杂项(不影响源码和测试) | 一般不进周报 |
revert | 回滚某次提交 | 【修复问题】或单独列 |
范围(scope) 建议写模块名,比如 fix(plc)、feat(export),以后想按模块汇总周报特别方便。
破坏性变更在类型/范围后加 !,或者在脚注写 BREAKING CHANGE::
feat(api)!: 统一设备通信接口返回格式,旧版 DataTable 返回方式移除
7.2 给团队定的“中英双语”落地规范
纯英文 type 有个现实问题:组里有同事不习惯,而且周报最终是中文的。所以我在团队里推的是**“英文类型 + 中文主题”**,类型词和中文动作一一对应,脚本两边都能识别:
| 规范类型 | 中文动作为备选 | 什么时候用 | C#/上位机场景示例 |
|---|---|---|---|
feat | 新增 / 添加 | 写了新功能、新命令、新页面、新接口 | feat(export): 新增尺寸标注导出Excel功能 |
fix | 修复 | 修了一个明确的缺陷 | fix(comm): 修复PLC偶发断连后不自动重连的问题 |
perf | 优化(性能) | 同样的结果,但更快/更省内存 | perf(hatch): 大批量剖面线填充耗时从8s降到1.2s |
refactor | 重构 / 调整 | 内部结构调整,外部行为不变 | refactor(dimension): 抽取标注修复公共基类 |
docs | 文档 | README、需求文档、注释文档 | docs: 更新避让算法的配置项说明 |
style | 格式 | using 排序、换行、命名统一 | style: 统一 GlobalUsings 引用 |
test | 测试 | 单元测试、测试命令 | test: 补充圆形序号识别的边界用例 |
build | 构建/依赖 | csproj、NuGet、引用库 | build: 升级 AutoCAD.EntityTool 到 1.0.8.8 |
chore | 杂项 | 不属于以上任何一类的琐事 | chore: 整理测试用 dwg 目录 |
主题部分的规矩只有五条,很好记:
- 一句话说清“做了什么”,动宾结构开头("修复……问题""新增……功能"),不要用"update""改了点东西";
- 结尾不加句号,控制在 50 个字符左右;
- 说"为什么"和"怎么做"放正文,主题只回答"是什么";
- 一个 commit 只干一件事,别把修 bug、加功能、改格式揉在一起——否则脚本没法分类,code review 也没法看;
- 关联任务号,在脚注写上 Jira/工单编号,周报和任务系统就能对上:
Refs PROJ-130。
7.3 反面教材 vs 正确示范
# ❌ 无法分类、无法回溯
git commit -m "update"
git commit -m "改"
git commit -m "修复" # 修了啥?
git commit -m "备份代码1" # 备份不是版本控制的用法
git commit -m "新增功能,顺便修了几个bug,还调了下格式" # 一个commit干三件事
# ✅ 清晰、可分类、可回溯
git commit -m "feat(monitor): 新增温度监控实时曲线页面"
git commit -m "fix(comm): 修复PLC通信超时后状态未复位的问题"
git commit -m "perf(export): 尺寸导出改为批量写入,减少NPOI开销"
git commit -m "refactor(service): 抽取避让计算与标注匹配的公共逻辑"
git commit -m "build: 引入 Newtonsoft.Json 并新增 JSON 导出配置"
7.4 让规范不依赖“自觉”:配一个 commit 模板
规范定了,怎么保证大家照做?靠自觉通常撑不过两周。可以给团队仓库配一个提交模板:
在仓库根目录建 .gitmessage.txt:
<type>(<scope>): <subject>
# type 可选: feat/fix/perf/refactor/docs/style/test/build/ci/chore/revert
# subject: 动宾结构,50字以内,结尾不加句号
# body: 为什么做这个改动?关键实现思路?(可选)
# footer: Refs PROJ-xxx / Closes #xxx(可选)
然后启用:
git config commit.template .gitmessage.txt
以后执行 git commit(不加 -m)就会自动带出这个模板,照着填空即可。配合 commitlint / husky(Windows 下也能用)或服务端钩子,还能在提交时直接拦截不符合规范的 message。
7.5 脚本和规范是“双向奔赴”
规范建立起来之后,脚本的分类规则可以从"模糊的关键词匹配"升级为"精确读 type",一行正则就够:
import re
PATTERN = re.compile(r'^(?P<type>feat|fix|perf|refactor|docs|style|test|build|ci|chore|revert)'
r'(?:\((?P<scope>[^)]+)\))?(?P<bang>!)?:\s*(?P<subject>.+)$')
TYPE_TO_CATEGORY = {
'feat': '新增',
'fix': '修复', 'revert': '修复',
'perf': '优化', 'refactor': '优化',
'docs': '其他', 'build': '其他', 'chore': '其他',
'style': None, 'test': None, 'ci': None, # 噪音提交,默认不进周报
}
def categorize_commits(commits):
categories = {'修复': [], '新增': [], '优化': [], '其他': []}
for commit in commits:
if not commit:
continue
message = commit.split('|')[0]
if message.startswith('Merge '): # 合并提交不进周报
continue
m = PATTERN.match(message)
if m:
category = TYPE_TO_CATEGORY.get(m.group('type'))
text = f"[{m.group('scope')}] {m.group('subject')}" if m.group('scope') else m.group('subject')
else:
category, text = '其他', message # 老提交/未规范提交走兜底
if category:
categories[category].append(text)
return categories
顺手还解决了三个老问题:合并提交自动排除、style/test 这类噪音不再污染周报、范围可以直接显示在条目里。
迁移建议:老仓库不用纠结历史提交,从定规范那天起,新提交照规范写就行;脚本保留中文关键词兜底,过渡期两边都认。
八、其他踩坑记录
如果你也想做一个类似的脚本,这几点可以注意:
1. Git log 的日期筛选,要注意时区
git log --since="1 week ago" 用的是本地时区,但如果你的服务器是 UTC 时间,可能会漏掉一些 commit。建议用 --since="2026-09-28" --until="2026-10-08" 这种明确日期。脚本里我用 Python 算出日期字符串再传进去,就是这个原因。
2. 不要漏掉 --author 和合并提交
多人仓库里不加作者过滤,会把别人的提交也写进你的周报。建议默认带上 --author;另外合并提交(message 以 Merge 开头)要么在脚本里过滤,要么 git log 直接加 --no-merges。
3. Windows 上的三个经典坑
- Python 没配 PATH:
python命令报“不是内部或外部命令”,要么用完整路径,要么用py启动器,最省心的是 bat 里写死解释器路径; - 控制台编码:git log 输出中文在老版本 Windows 上可能乱码,
subprocess.run里显式指定encoding='utf-8', errors='replace'; - 不在仓库目录运行:用
cwd=参数或git -C <路径>指定,别依赖“先 cd 过去”;传子目录也行,git 会自己找仓库根目录。
4. 多仓库 / 子模块结构
有的解决方案根目录不是 git 仓库,代码分散在多个子模块里(各自独立的 .git)。这种情况要对每个子模块分别取 log,再合并结果——别在根目录跑一次就以为数据全了。
5. Jira API 的认证方式
Jira Cloud 用的是 email + api_token 的 Basic Auth,Jira Server 用的是 username + password。自建 Jira 还要确认 API 版本(v2 还是 v3),路径可能不一样。
6. 大模型 API 的调用成本与脱敏
GPT-3.5 润色一份周报大概 500 个 token,成本很低。周报很长时可以换更便宜的模型,或对重复 commit message 做缓存。更重要的是脱敏:如果提交记录或任务里有客户名称、项目代号,发给外部 API 前用正则替换成“客户 A / 客户 B”,涉密单位直接走内网模型或本地模型。
7. 不要完全依赖脚本
脚本生成的是“草稿”,不是“终稿”。最终提交前一定要人工过一遍,补充“下周计划”和“遇到的问题”——这两部分脚本暂时替代不了。
九、最后说一句
这个脚本不是什么“黑科技”,它只是把“写周报”这件事,从“凭记忆回忆”变成了“从数据里提取”。
我用了三个月,最大的收获不是“省了多少时间”,而是两件事:
- 我发现我的工作记录比我的记忆更可靠。 我以为我这周只干了三件事,翻完 Git log 才发现,我其实干了八件事;
- 一个小小的周报脚本,顺理成章地把团队的提交规范立了起来。 当“写好 commit”能直接变成“周报少加班”,规范就不再是领导嘴上的要求,而是每个人自己的需求——这大概是推动规范最顺滑的方式。
知道这件事,比不知道好。
我是豪彡,从建筑设计院跑路转行 C# 上位机开发。这里没有套路,只有我一路走来踩过的坑和学到的东西。
感兴趣的话,咱们慢慢聊。
一起加油。💪