科技 · 实用指南
将多份 Teams 出席报告合并到一个工作簿
合并文件夹中的英文 Teams 出席 CSV 报告,同时保留参加者姓名、会议详情、原始时长,以及每个输入文件的处理记录。

合并这些报告时,应先取出每份报告的 Participants 表,为其中的每一行附上该报告的 Summary 字段,再将各份报告的记录合在一起。下方的转换脚本会对文件夹中符合要求的英文 Teams CSV 文件执行这一过程,生成一个包含 Participants、Meetings 和 Files 三个工作表的工作簿。
合并结果将姓名、原始加入和离开时间、出席时长及来源文件名保存在一起。脚本还会生成 audit.json,记录原始 Participants 和 Summary 值,方便你对照输入文件检查工作簿。你需要 Python 3.11 或受支持的更新版本,以及电脑上的终端;脚本会在你的电脑上处理已下载的文件。
确认报告符合这一布局
以文本方式打开一份报告,或查看文件但不要保存改动。本版本要求以下三个分区标记依次出现:1. Summary、2. Participants 和 3. In-Meeting Activities。这一布局已与一份独立公开发布的原始报告核对过;它是本工具明确支持的输入格式,并不代表所有 Teams 导出文件都采用相同结构。
Summary 必须包含 Meeting title、Attended participants、Start time、End time、Meeting duration 和 Average attendance time。这些字段的顺序可以不同,额外的 Summary 字段也会保留。Meeting title 可以为空,其余五个必需字段必须有值。
Participants 表头必须恰好包含以下七个字段,且顺序一致:Name、First Join、Last Leave、In-Meeting Duration、Email、Participant ID (UPN)、Role。Email 和 Participant ID (UPN) 可以为空,其余必需的参加者字段必须有值。
Activities 表头必须恰好包含以下六个字段,且顺序一致:Name、Join Time、Leave Time、Duration、Email、Role。脚本会检查这个分区的结构,但不会把其中的时间区间记录追加到 Participants。只要完整结构匹配,脚本支持带字节顺序标记(BOM)的 UTF-8 和 UTF-16 文件,也支持不带 BOM 的 UTF-8 文件;分隔符可以是制表符、逗号或分号。
把准备合并的报告放进一个文件夹
新建一个名为 Teams-Month 的工作文件夹,并在其中新建 reports 子文件夹。把准备合并的 CSV 文件直接放进 reports,不要放在更深一层的子文件夹中,因为脚本不会搜索子文件夹。保留下载的原始文件;用电子表格软件打开并另存,可能在脚本读取之前就改变日期、编码或其他值。
为每次准备纳入的会议选择一份最终报告。Microsoft 会为重复会议的每次举行分别提供报告。如果还缺少文件,而且你有组织者或共同组织者权限,可以打开已结束会议的聊天,选择 Attendance,然后下载 CSV。整理当月资料时,应使用会后报告。
所选文件决定了本次处理的范围。同一场会议的两份报告也可能内容不同,因此文件指纹不同并不能证明它们对应不同场次。脚本不会替你决定应保留报告的哪个版本。
下载并运行转换脚本
如有需要,先从 https://www.python.org/downloads/ 安装受支持的 Python 3 版本。在 Windows 上打开 PowerShell,在 macOS 上打开 Terminal。输入 cd,后面跟上用引号括住的完整路径,切换到 Teams-Month 工作文件夹。例如,Windows 可使用 cd "C:\path\to\Teams-Month",macOS 可使用 cd "/path/to/Teams-Month",请把示例路径替换成你的实际路径。
继续之前先检查解释器:Windows 运行 python --version,macOS 运行 python3 --version。显示的版本应为 Python 3.11 或更新版本。如果找不到命令,请完成官方安装并重新打开终端;缺少解释器与报告格式错误是两类不同的问题。
在 Windows 上,用下面的 PowerShell 命令下载第 1 版脚本。这个命令只保存源文件,不会运行脚本:
Invoke-WebRequest -UseBasicParsing -Uri "https://watchatease.com/guide-tools/merge-teams-attendance-v1.py" -OutFile "merge-teams-attendance-v1.py"
在 macOS 上,用以下命令下载同一个文件:
curl --fail --location "https://watchatease.com/guide-tools/merge-teams-attendance-v1.py" --output "merge-teams-attendance-v1.py"
下载成功后,在 Windows 上运行以下命令。reports 文件夹必须已存在,而 month-output 必须尚不存在:
python "merge-teams-attendance-v1.py" "reports" --output-dir "month-output"
在 macOS 上运行:
python3 "merge-teams-attendance-v1.py" "reports" --output-dir "month-output"
成功后,month-output 中会生成 teams-attendance.xlsx 和 audit.json,终端会显示参加者行数、保留的报告数,以及作为精确副本跳过的文件数。打开工作簿进行检查。再次运行时,请换一个新的输出名称,例如 month-output-2;脚本不会覆盖已有目录。它不需要额外的 Python 软件包,处理报告时也不会发出网络请求。
查看工作簿中的三个工作表
Participants 是合并后的明细表。前七列保留原始参加者字段;Source file 和 Source SHA256 用于识别输入报告,后面以 Summary: 开头的列则保存该报告的会议信息。筛选 Source file 可以查看某一份报告,筛选 Name 或 Email 则可以查看所选报告中与该值相关的记录。
Meetings 为每份保留的报告生成一行,包含来源、参加者行数、计数比较结果和 Summary 值。这个工作表的行数表示保留的报告文件数。在你为每次准备纳入的会议选定一份报告后,它就可以作为会议清单使用。
Files 会记录每个输入 CSV,包括精确副本。Status 的值为 included 或 exact_duplicate_skipped;对于副本,Duplicate of 会指出实际保留的文件。Participants rows 和 Activities rows excluded 显示两个分区分别如何处理。当 Summary 中的 Attended participants 值仅由十进制数字组成时,Count check 会将它与 Participants 行数进行比较。
audit.json 记录相同的文件清单、计数和工作簿指纹,还包含 retained_reports,其中保存原始 Participants 行,以及按原顺序排列的 Summary 键值对。请将它与原始 CSV 一起保留。如果电子表格软件之后改变了某个单元格,或在保存时统一了换行符,audit.json 可以作为输入值的直接记录供你核对。
使用汇总数据前先核对这一批文件
先数一数 reports 中直接存放了多少个 CSV 文件,再与 Files 工作表和 audit.json 中的 input_file_count 比较。每个准备处理的文件都应有对应记录。精确副本会计入 Files,但不会增加 Participants 或 Meetings 的行数。
接着,在 Files 中筛选 included,并在另一份工作副本中将 Participants rows 的计数单元格转换为数字后求和;也可以直接查看 audit.json 中的 participant_rows_written。这个总数应等于 Participants 的数据行数,不包括表头。为保留原值,工作簿最初将所有单元格都写为文本。
对每个 included 来源文件,将其 Summary 字段和一条参加者记录与原始报告核对。尤其要检查姓名、In-Meeting Duration,以及对应的会议开始时间。空的会议标题会保持为空,此时 Source file 可以帮助你确认记录来自哪份报告。
如果 Count check 显示 DIFFERS,工作簿仍然可以打开,数据也会保留,但你需要回到对应的来源报告检查差异。计数相符只表示这两个计数一致,并不证明每一行都代表不同的人,也不证明来源报告完整无缺。
正确理解日期、时长和重复姓名
像 3/04/24 这样的值会原样保留为文本。脚本不会猜测它表示 3 月 4 日还是 4 月 3 日。若要在另一份分析副本中进行日期计算,应先确认导出文件采用的日期顺序,并保留存放原值的列以便对照。
In-Meeting Duration 会按报告中的值保留。脚本不会用 Last Leave 减去 First Join,不会累加活动时间区间、应用出席门槛,也不会计算认证学时。这些都是另行开展的计算,各自涉及不同假设;工作簿提供的是计算所依据的报告字段。
记录不会按姓名、邮箱或 Participant ID (UPN) 合并。同一个显示名称可能出现在不同记录中,空邮箱也会保持为空。同一个人在不同场次中多次出现,可能完全正常。
Microsoft 说明了原始报告的限制,例如未获准从等候区进入会议的人,以及仅查看模式的参加者,不会列入报告。合并下载文件无法补回原本就不存在的信息。这个工作簿是对现有报告的整理,也保留了那些报告本身的局限。
遇到文件被拒绝时
不受支持的表头、缺失的分区、格式错误的引号字段、必需值为空或无法读取的文件,都会触发错误,并指出受影响的文件名。脚本会先检查整批文件,再创建输出;它不会悄悄跳过被拒绝的 CSV,却把剩余数据标为完整结果。
将错误指出的文件与前述三分区布局比较。如果文件曾被意外另存或已经损坏,而你仍能取得原始导出,就换用一份新的原始导出文件。来自其他产品或采用不同布局的报告,应放在本次输入文件夹之外。不要为了通过验证就擅自把不熟悉的列改名,因为相似的标签也可能表示不同的数据。
如果你的真实报告使用另一种语言或结构,本脚本就不支持它。可以考虑为实际布局编写相应的 Power Query 转换。Microsoft 的文件夹合并文档说明了如何将示例文件的转换应用到每个匹配的文件,并保留来源文件名。
遇到输出目录错误,应选择新的目标位置;遇到找不到 Python 的错误,则应解决解释器的安装问题。这两种情况都不需要修改报告内容。在相关问题解决前,请保留错误信息和来源文件。
哪些情况适合其他工作流程
Tom Riha 已有一篇 Power Automate 教程,说明如何在报告上传时提取 Participants,适合持续上传报告的工作流程。获授权的管理员也可以通过报告或审计数据流程收集会议信息。本文的方法从你已有的报告文件夹出发,一次运行即可生成合并后的工作簿。
脚本按所述格式处理文件,并将内容写为纯文本单元格。它使用虚构报告进行检查,覆盖带引号的姓名、空身份字段、多种编码、重复文件和失败情况。这不等于已在你的 Teams 租户中实测,也不保证每一种电子表格软件在另存时都会保留每个字符串。请以原始 CSV 和记录原值的 audit 文件作为对照依据。
