# cis-manager **Repository Path**: Ixchitl/cis-manager ## Basic Information - **Project Name**: cis-manager - **Description**: cis-manager - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-03 - **Last Updated**: 2026-09-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # CMT 会议投稿统计与同步 按需从 Microsoft CMT 拉取投稿数据,生成本地 JSON 与 Excel 报表,并同步到飞书多维表格。 没有后台服务、不做轮询:**只有你执行命令(或让 AI 调用技能)时才会去拉数据**。 面向 AI agent 设计,但每条命令都可以手动执行。本文件是给人看的;`AGENTS.md`、`skills/` 下的文档是给 AI 读的,保持英文。 --- ## 一、快速上手 先创建环境并安装 skill: ```bash conda env create -f environment.yml # 只需一次 conda run -n cmt-reporting python install_skills.py conda run -n cmt-reporting python install_skills.py --check ``` 安装后,在项目根目录打开支持 skill 的 AI 编码工具,直接用自然语言即可,例如: ```text 更新一下 CMT 信息 把最新投稿同步到飞书 ``` AI 会自动调用 `cmt-reporting` skill,拉取最新投稿、先预览变更,再按安全规则同步;不需要记命令或手动指定 skill。 把拿到的 `config.json` 放到**项目根目录**(与 `cli.py` 同级),即 `D:\cmt-automation\config.json`。 然后确认环境和配置都就绪: ```bash conda run -n cmt-reporting python cli.py doctor ``` `ready` 里的 `cmt` 和 `feishu` 都是 `true` 就可以开始用了。唯一建议改的一项是把更新记录归到自己名下: ```bash python cli.py config set feishu.operator "<你的名字>" ``` 如果需要改其他配置项,见第六节。 ### 关于配置文件 `config.json` 里包含**明文的 CMT 密码和飞书 App Secret**,所以: - 它在 `.gitignore` 里,**永远不要提交**,把这个文件本身当成密码看待。 - 所有命令输出都会把密钥打码成 `***set***`,所以命令结果可以放心粘贴。 - 万一泄露:飞书后台可以重置 App Secret,且应用只能访问被授权的那张表,影响面有限。 - 需要临时覆盖时,环境变量 `CMT_PASSWORD`、`FEISHU_APP_SECRET` 优先于文件。 ### 飞书侧的一次性准备 只有新建飞书应用或换表时才需要做:创建企业自建应用 → 开通 `bitable:app` 与记录读写权限 → 发布版本 → **把应用添加为目标多维表格的协作者(可编辑)**。 最后一步最容易漏:飞书规定应用访问不了没被授权的文档。好处是它也碰不到其他任何文档。 --- ## 二、设计理念 ### 靠编号认行,不靠位置 CMT 每篇投稿有个编号,填在表里的 `EasyID` 列,**所有对应关系都靠它**。你在飞书里排序、插行、筛选都不会错乱;但**这一列不要改**,改了就认不出来了。 ### 表里的列分三类 | 类别 | 包含 | 谁来填 | | --- | --- | --- | | 机器直抄 | `EasyID`、`Title`、`Feild`、`CMT状态`、`AEmail`、`A国籍(英文)`、`accept/reject`、`更新人`、`更新时间` | 程序,无需判断 | | 需要判断 | `Authors`、`A机构(英文)`、`Information1`、`Information2`、`PaperID`、`Corresponding Author名字/邮箱` | AI 润色 | | 你们自己填 | 缴费情况与备注、参会形式与人数、报告人信息、审稿意见与备注、终稿提交情况、基础备注 | 人工,**程序永不触碰** | 为什么要分:CMT 里的原始数据很乱——作者名有中文、有全小写、有全大写;机构有人写缩写(`szu`)、有人写整个院系(`School of Mathematics and Statistics, Changchun University`)。机器直接抄过来没法用,必须理解后重写。 ### 快照签名:判断"CMT 那边变没变" 每行存一个 `同步指纹`,由这行在 CMT 里的原始内容(标题、每位作者的姓名/邮箱/机构/国籍)计算而来。 原始内容一字未变,签名就一样;变了任何一处,签名就不同。 **签名对得上 → 这行的润色还新鲜,跳过;对不上 → CMT 动过了,需要重新润色。** 关键:只有 AI 完成润色并写入时才更新签名,机械同步故意不写。所以没润色过的行会一直出现在待办里,不会漏。这也是"不用每次让 AI 全过一遍"的实现方式。 ### 三条底线 1. **只删 CMT 里确实被删除的行,且必须显式加 `--delete-removed`**——其他任何"表里多出来"的行只报告,绝不自动删。 2. **人填的值优先**——发现冲突只报告,不覆盖。 3. **默认只预演**——同步和润色都要显式加 `--apply` 才真写。 取舍是程序偏保守:宁可多报告让你决策,也不自动做可能出错的事。代价是有些情况需要你确认一下。 --- ## 三、三个工作流(怎么用技能) 对 AI 说人话即可,它会挑对应的流程。**不要在只想看数字的时候跑全量润色。** ### 1. 同步 —— 机械,不做判断 对 AI 说:"更新一下 CMT 数据" / "把最新投稿同步到飞书" ```bash python cli.py collect python cli.py publish # 预演,什么都不写 python cli.py publish --apply --allow-create # 真写 ``` 只写"机器直抄"那一类,**绝不改写姓名**。跑完会报告三件需要你决策的事:冲突、消失的行、待润色的行。 ### 2. 润色 —— AI 规范化,增量 对 AI 说:"把作者姓名和机构补全/规范化" ```bash python cli.py polish-tasks # 导出 output/polish-tasks.json # AI 读该文件 → 生成 patch.json → 应用 python cli.py publish --patch patch.json --apply ``` 任务文件只包含**签名对不上的行**,且内容很精简(每位作者的原始姓名、拆开的姓与名、邮箱、原始机构、国籍)。首次全量跑,之后每次只跑变动的那几篇。 `patch.json` 按 `EasyID` 索引: ```json { "14": { "Authors1": "Taiyu Zhu", "A1机构(英文)": "Southwest Petroleum University", "PaperID": "CI26010845", "Information1": "Taiyu Zhu, Optimizing Task Scheduling in ...", "Information2": "Taiyu Zhu(Southwest Petroleum University,China)", "Corresponding Author名字": "Taiyu Zhu", "Corresponding Author邮箱": "someone@example.edu" } } ``` ### 3. 核对 —— 只读 对 AI 说:"表和 CMT 一致吗" / "截稿前帮我核对一遍" ```bash python cli.py collect python cli.py publish # 预演 python cli.py summary --top 10 ``` 什么都不写,只列出四种情况各有多少、具体哪些行。 --- ## 四、增删改逻辑 | 情况 | 表现 | 处理 | | --- | --- | --- | | CMT 有、表里没有 | `planned_creates` + `needs_polish` 标 `new row` | 同步建行(`--allow-create`),再润色 | | CMT 内容变了 | `needs_polish` 标 `CMT source changed` | 同步,然后只重润色这几行 | | 只有机械值变了 | `planned_updates` | 同步即可,无需润色 | | 表里已有不同的值 | `conflicts` | **保留人工值**,报告差异,征得同意才 `--overwrite` | | 状态改为 Withdrawn / Revision | 下次同步自动更新 `CMT状态` 列 | 无需决策。这列由 CMT 独占,总是覆盖 | | 在 CMT 里被删除 | `Deleted` 表 + `removed_in_cmt` | 先报告要删哪几行,再加 `--delete-removed` 删除。**不可撤销** | | 因其他原因表里多出的行 | `stale_rows` | 只报告,**绝不自动删**(被过滤的测试稿会落在这里) | | 测试投稿 | `excluded_submissions` | 说明跳过了哪些 | | 作者超过 9 位 | `author_overflow` | 停下来问怎么记 | | 两行同一个 `EasyID` | `duplicate_keys` | 请你删掉重复行,程序不猜 | 测试投稿的过滤规则:`cmt.exclude_title_pattern` 默认只匹配"整个标题就是 test"的情况(如 `test1`),不会误伤 `Testing Deep Learning...` 这类真实论文;另可用 `cmt.exclude_submission_ids` 点名排除。被排除的会记入报表的 `Excluded` 表。 --- ## 五、命名规范 写进表里的内容必须是英文。 **姓名** - 名在前、姓在后,Title Case:`Zhenrong Deng` - 顺序取自 CMT 的 FirstName/LastName 字段,不靠猜 - 中文名转汉语拼音,无声调无连字符:`太宇 朱` → `Taiyu Zhu` - 全大写形式(`SAREN GAOWA`)仅在作者确实那样写时保留,否则 `RAJESHRAM V` → `Rajeshram V` - 缩写保持原样:`N. F. S. Md Sazihan` - 拉丁字母姓名只修大小写,不改拼写和词序:`han cong` → `Han Cong` - 去掉 `Dr.`、`Prof.` 这类头衔 **机构** - 只到院校级的官方英文名,去掉院系与实验室:`School of Mathematics and Statistics, Changchun University` → `Changchun University` - 缩写展开:`szu` → `Shenzhen University` - 中文机构翻译:`西南石油大学` → `Southwest Petroleum University` - 缩写有歧义时说明,不要猜 `CMT状态` 记录 CMT 的原始状态(含 `Withdrawn`、`Revision`),因为 `accept/reject` 那一列只有两个选项装不下。这列由 CMT 独占、人不会去编辑,所以总是覆盖——否则撤稿永远反映不到表里。 **国籍**来自 CMT,本身是英文。为空时不允许静默推断——要么说明推断依据,要么问你。 **拼接列** - `PaperID` = 领域前缀 + (基数 + EasyID)。2026 年基数是 `26010831`;换年份必须重新确认。前缀:`AP` = Applications,`CI` = Computational Intelligence,`IS` = Information Security。 - `Information1` = 规范化后的作者名以 `, ` 连接,再接标题;标题按每词首字母大写、四字母以下的冠词/连词/介词小写;用英文标点;通讯作者不加 `*`。 - `Information2` = `姓名(机构,国籍)` 逐位作者以 `, ` 连接。 - 通讯作者取自 CMT 标记的 primary 作者。注意这是**联系人标记,不等于学术通讯作者**,而且不一定是第一作者——AI 会说明用了哪位,并在与表里已有值不一致时提醒。 --- ## 六、需要更新时怎么做 都是一条命令,对 AI 说人话它也会照做。改完跑一下它提示的验证命令。 | 变化 | 命令 | 之后验证 | | --- | --- | --- | | 会议换年份(拿到新链接) | `config set cmt.conference_url "<新链接>"` | `probe` | | 只知道会议名 | `config set cmt.conference "CISconf2027"` | `probe` | | 主席账号变了 | `config set cmt.email "<邮箱>"` | `probe` | | CMT 密码改了 | `config set cmt.password "<新密码>"` | `probe` | | 换飞书表格 | `config set feishu.table_url "<新链接>"` | `setup-table` | | 换飞书应用 | `config set feishu.app_id ""` 再 `config set feishu.app_secret ""` | `setup-table` | | 换记录的操作人 | `config set feishu.operator "<名字>"` | — | | 排除某篇测试稿 | `config set cmt.exclude_submission_ids "12"` | `collect` | | 不再过滤测试稿 | `config set cmt.exclude_title_pattern ""` | `collect` | 换到新表、新增列或迁移旧字段名后,先跑 `setup-table` 预演,确认要创建和重命名的字段,再 `--apply`。旧字段 `1、PaperID` 会原地重命名为 `PaperID`,已有数据保持不变;如果新旧字段同时存在,程序不会自动合并。表里缺字段不会导致报错,程序会在 `missing_fields` 里列出来。 改动本项目代码后,跑这两条自检: ```bash conda run -n cmt-reporting python skills/cmt-reporting/scripts/smoke.py conda run -n cmt-reporting python install_skills.py --check ``` --- ## 七、输出文件 `collect` 写入 `output/`: - `cmt-report.json` —— 完整数据:投稿、作者、已删除、被排除、各类分布 - `cmt-report.xlsx` —— 分表 `Summary`、`Submissions`、`Authors`、`Deleted`、`Excluded`、`Statuses`、`Tracks`、`Subjects`、`Institutions`、`Countries` `Submissions` 含 CMT 返回的全部标量字段(117 个,含问卷答案);`Authors` 每位作者一行;`Deleted` 是 CMT 主列表不返回的撤稿/删除投稿。 `polish-tasks` 写入 `output/polish-tasks.json`,只含待润色的行。 两份报表都包含作者姓名和邮箱,已在 `.gitignore` 中,请保留在本地。 --- ## 八、给 AI 用(技能安装) 两个入口,都不绑定特定工具: - 根目录 `AGENTS.md` —— 跨工具通用约定,多数编码 agent 会读 - `skills/cmt-reporting/` —— 技能本体(唯一来源) - `SKILL.md` —— 三个工作流、命名规范、增删改逻辑、失败处理 - `references/fields.md` —— CMT 全字段目录及真实覆盖率 - `references/summary-table-mapping.md` —— 逐列映射、需要组装的列、真实脏数据清单 - `scripts/smoke.py` —— 端到端自检 没有一个目录是所有 agent 都读的,所以 `skills/` 作为唯一来源,用安装器分发: ```bash python install_skills.py # 全部目标 python install_skills.py --targets claude cursor python install_skills.py --check # 漂移检测,不一致退出 1 python install_skills.py --dry-run ``` | 目标 | 位置 | 形式 | | --- | --- | --- | | `claude` | `.claude/skills//` | 完整复制 | | `qoder` | `.qoder/skills//` | 完整复制 | | `cursor` | `.cursor/rules/.mdc` | 指针,指向唯一来源 | | `windsurf` | `.windsurf/rules/.md` | 指针 | | `copilot` | `.github/instructions/.instructions.md` | 指针 | 规则型工具只生成小小的指针文件,正文永远只有一份,天然不会漂移。完整复制的那两个可能漂移,所以 `--check` 会按内容哈希比对。 生成出来的目录都在 `.gitignore` 里。克隆后建环境、跑安装器即可。**只改 `skills/` 下的文件,不要改安装出来的副本**,改完重跑安装器。 --- ## 九、命令参考 | 命令 | 用途 | | --- | --- | | `doctor` | 配置与环境自检,不联网。出问题先跑这个。 | | `config show` | 全部配置项,密钥已打码 | | `config set <键> <值>` | 改一项配置,自动同步派生项 | | `probe` | CMT 登录连通性检查,`--fields` 列出全部字段名 | | `collect` | 拉取全部数据并生成两份报表,`--limit N` 快速冒烟 | | `summary` | 上次报表的统计,`--top N` 控制每类行数 | | `setup-table` | 在飞书表建缺失字段,`--apply` 才写 | | `polish-tasks` | 导出待润色的行 | | `publish` | 同步到飞书,`--apply` 才写;`--allow-create` 允许新增行;`--delete-removed` 删除 CMT 已删除的行(不可撤销);`--patch` 提交润色值;`--overwrite` 强制覆盖 | 所有命令:正常结果走 stdout(JSON),错误走 stderr(JSON,含 `error_code`、`error` 和下一步 `remedy`),进度信息走 stderr。退出码:`0` 成功、`1` 未知错误、`2` 配置问题、`3` CMT 鉴权被拒、`4` 网络、`5` CMT 或飞书接口错误。 `collect` 采用**快速失败**:某篇作者详情拉取失败会中止整批,避免生成一份看起来完整实则缺数据的报表。确实需要容错时加 `--continue-on-error`,失败的编号会列在 `detail_failures`。 --- ## 十、目录结构 ``` cli.py 命令入口 cmt_reporting/client.py CMT 登录、OData 查询、重试 cmt_reporting/config.py 单一明文配置(含凭据) cmt_reporting/feishu.py 飞书多维表格访问(应用身份) cmt_reporting/publish.py 表结构定义、写入计划、快照签名 cmt_reporting/reports.py 数据规范化、JSON 与 Excel 输出 environment.yml conda 环境 cmt-reporting config.example.json config.json 模板 skills/cmt-reporting/ 技能本体(唯一来源) install_skills.py 把技能分发到各 agent AGENTS.md 跨工具入口 ```