我写了一个“自动写周报”的脚本,结果被领导表扬了

1,595 阅读19分钟

我写了一个“自动写周报”的脚本,结果被领导表扬了

我是豪彡,从建筑设计院跑路转行 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. 调整报警记录导出格式,使导出文件更符合下游系统的对接要求

这个版本,已经能直接交上去了。

五、第四版:让脚本真正“好用”——参数化与一键运行

脚本在我自己电脑上能跑之后,我发现了一个新问题:能跑 ≠ 好用。

真实用了两周,冒出来一堆小毛病:

  1. 脚本放在 G:\item\py git\,但我要统计的项目在 E:\User_Guo\...\DisassemblyDrawing,不在 git 仓库目录里运行就什么都捞不到;
  2. 默认只看 7 天,碰上国庆长假回来,我想看“最近 30 天”甚至“今年全部提交”,没法选时间;
  3. 结果只打印在黑窗口里,关掉就没了,还得手动复制;
  4. 我电脑装了 Python 但没配 PATH,每次要敲一长串解释器路径;
  5. 不是 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 通信超时处理’这一条,把技术价值和业务影响都说清楚了。大家参考一下。”

我当时看到这条消息,心情很复杂。

一方面,确实被表扬了,开心。另一方面,我心里清楚,这份周报的“骨架”是脚本生成的,我只是做了最后的润色和补充。

但仔细想想,我其实并没有“偷懒”。 因为:

  1. 脚本只是帮我“整理素材”,核心的工作内容还是我自己做的;
  2. 我花在“优化脚本”上的时间,最终转化成了更高效的周报产出;
  3. 周报本身的质量确实提升了——因为脚本不会漏掉任何一条 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 包变动【其他工作】
ciCI/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 目录

主题部分的规矩只有五条,很好记:

  1. 一句话说清“做了什么”,动宾结构开头("修复……问题""新增……功能"),不要用"update""改了点东西";
  2. 结尾不加句号,控制在 50 个字符左右;
  3. 说"为什么"和"怎么做"放正文,主题只回答"是什么";
  4. 一个 commit 只干一件事,别把修 bug、加功能、改格式揉在一起——否则脚本没法分类,code review 也没法看;
  5. 关联任务号,在脚注写上 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. 不要完全依赖脚本

脚本生成的是“草稿”,不是“终稿”。最终提交前一定要人工过一遍,补充“下周计划”和“遇到的问题”——这两部分脚本暂时替代不了。

九、最后说一句

这个脚本不是什么“黑科技”,它只是把“写周报”这件事,从“凭记忆回忆”变成了“从数据里提取”。

我用了三个月,最大的收获不是“省了多少时间”,而是两件事:

  1. 我发现我的工作记录比我的记忆更可靠。 我以为我这周只干了三件事,翻完 Git log 才发现,我其实干了八件事;
  2. 一个小小的周报脚本,顺理成章地把团队的提交规范立了起来。 当“写好 commit”能直接变成“周报少加班”,规范就不再是领导嘴上的要求,而是每个人自己的需求——这大概是推动规范最顺滑的方式。

知道这件事,比不知道好。

我是豪彡,从建筑设计院跑路转行 C# 上位机开发。这里没有套路,只有我一路走来踩过的坑和学到的东西。

感兴趣的话,咱们慢慢聊。

一起加油。💪