在华为云 AI DevSpace 写一个 CSV 工时汇总脚本

举报
yd_49100212 发表于 2026/10/28 00:39:43 2026/10/28
【摘要】 用三条虚构记录写一个按项目汇总工时的小工具,放进 AI DevSpace 练习。附完整 Python 脚本,处理 CSV 编码、非法工时和重复运行时的文件覆盖。

周报里的工时经常来自几份表格:同一个项目出现好几次,项目名末尾还可能多一个空格。每次手动筛选、求和,到了下周又得重复一遍。这类工作适合写成一个小脚本,输入原始 CSV,输出按项目汇总的结果。

华为云 AI DevSpace 提供 AI 对话、命令行和文件管理入口,可以用来编写这类办公工具。下面用三条虚构记录演示,脚本只依赖 Python 标准库。文中的汇总结果和错误处理已在本地 Python 环境验证。

先把输入和输出说清楚

进入 AI DevSpace 时,页面会让用户选择体验版或持久化版。我这次选择的体验版标明“数据不保留”,进入空间后能看到剩余时长。试写脚本时,记得在体验结束前把代码和结果另行保存。

在 AI Shell 里,可以把需求写得具体一些:“读取 project,hours 两列的 UTF-8 CSV,按项目累计工时。项目名去掉首尾空格,每条记录允许 0 到 24 小时、最多两位小数。遇到坏数据说明行号并停止,输出文件已存在时不要覆盖。”这几句话决定了脚本遇到空值、错误数字和重复运行时该怎么处理。

创建 hours.csv,放入下面三条记录。这里的每一行代表一条工时登记,24 小时的限制针对单条记录,同一个项目的累计值可以超过 24 小时。

project,hours
文档整理,1.25
接口联调,2.50
文档整理,0.75

再创建 summarize_hours.py,内容如下。运行前在命令行执行 python3 --version,确认环境里有 Python 3。

import argparse
import csv
from collections import defaultdict
from decimal import Decimal, InvalidOperation
from pathlib import Path


def summarize(source, target):
    if source.resolve() == target.resolve():
        raise ValueError("输入和输出不能使用同一个文件")
    totals = defaultdict(Decimal)
    with source.open(encoding="utf-8-sig", newline="") as stream:
        reader = csv.DictReader(stream)
        if reader.fieldnames != ["project", "hours"]:
            raise ValueError("表头必须依次为 project,hours")
        for row in reader:
            line = reader.line_num
            if None in row or any(value is None for value in row.values()):
                raise ValueError(f"第 {line} 行:列数不正确")
            project = row["project"].strip()
            if not project:
                raise ValueError(f"第 {line} 行:项目名为空")
            try:
                hours = Decimal(row["hours"].strip())
            except InvalidOperation:
                raise ValueError(f"第 {line} 行:工时不是数字") from None
            if not hours.is_finite() or not Decimal(0) <= hours <= Decimal(24):
                raise ValueError(f"第 {line} 行:工时须为 0 到 24 的有限数值")
            if hours != hours.quantize(Decimal("0.01")):
                raise ValueError(f"第 {line} 行:工时最多保留两位小数")
            totals[project] += hours
    with target.open("x", encoding="utf-8-sig", newline="") as stream:
        writer = csv.writer(stream)
        writer.writerow(["project", "total_hours"])
        for project, hours in sorted(totals.items()):
            writer.writerow([project, f"{hours:.2f}"])


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="按项目汇总 CSV 工时")
    parser.add_argument("source", type=Path)
    parser.add_argument("target", type=Path)
    args = parser.parse_args()
    try:
        summarize(args.source, args.target)
    except (ValueError, OSError) as error:
        parser.exit(1, f"错误:{error}\n")

这段代码先读完并检查所有记录,再创建输出文件。第三行工时填错时,程序会报错退出,目录里不会多出一份只算了前两行的汇总表。输出采用 x 模式创建,重复运行时如果目标文件已经存在,会提示文件存在,原来的结果会保留。

CSV 用 utf-8-sig 编码读取,可以处理带 UTF-8 BOM 的表头;newline=“” 让 csv 模块处理换行。工时从文本直接转换成 Decimal,按十进制累加。表头、列数和项目名也一并检查,避免少了一列后把错误带到合计里。

用几条数据检查脚本

在两个文件所在目录运行:

python3 summarize_hours.py hours.csv summary.csv

summary.csv 的内容如下。输出按项目名排序,工时统一保留两位小数。

project,total_hours
接口联调,2.50
文档整理,2.00

先算得对,再看看填错时会怎样。把其中一条工时改成 abc,换一个尚不存在的输出文件名运行,程序会指出对应行的工时不是数字。填入 -1、25 或 0.001,也会被拒绝。NaN 和 Infinity 虽然能被 Decimal 解析,但不能作为工时参与汇总,因此代码另行检查了数值是否有限。

本地检查覆盖了这六种非法输入,以及正常求和、带 BOM 的输入和输出文件已存在的情况。每种非法输入都在创建输出前停止;重复运行也没有改动已有结果。要调整工时范围,可以修改代码中的 24;如果登记单位改成分钟,就需要同时调整字段名、单位和测试数据。

项目名合并还有一个业务问题:去掉首尾空格后,“接口联调”和“接口联调 ”会归为同一项,但“接口联调”和“接口对接”仍是两项。需要合并别名时,可以先维护一张明确的名称对照表。仅凭名字相似就合并,容易把不同项目的工时算到一起。

脚本跑通后,把 summarize_hours.py、hours.csv 样例和运行命令放在一起保存。下周收到新表格,先检查列名和工时单位,再换一个输出文件名运行;拿到汇总表后抽查一两个项目,确认原始记录与合计能对上。

CSV 字段读取的行为可参阅 Python 官方文档中的 csv 模块章节。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。