#!/usr/bin/env python3 """ 直接通过Python requests将审校结果写回单元挑战多维表格 """ import json, requests, sys, os APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf" APP_ID = "cli_a931175d41799cc7" APP_SECRET = "Iw2vEfbjT6GtV0GhbxbZqfQ4nAPtbR14" BASE = "https://open.feishu.cn/open-apis/bitable/v1" TABLES = { "听力-P1-图片选择题": "tbliZAhcc9C43B23", "听力-P2-表格填空题": "tblzTLNH7f13uWQN", "听力-P4-短对话选择题": "tblVmeDtBDKsAEfz", "听力-P5-信息匹配题": "tblDssVmhGzc3UKd", "听力-P6-听力选图": "tbloiMcD0sBtGSTq", "听力-P7-听力拖拽": "tbly9SvPEa44k3yX", } KNOWN_ABILITY_LABELS = { "显性事实理解|关键词识别", "显性事实理解|单句信息点抓取", "显性细节理解|数字/时间/地点", "多特征整合", "语用推断", "干扰抑制|多信息筛选", "多句保持|信息整合", "语用推断|否定与纠错", "听觉抓取关键信息", "问题意图识别", "关键细节听辨", "图像语义对齐", "近义改写", "否定与纠错", } def get_token(): r = requests.post("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", json={"app_id": APP_ID, "app_secret": APP_SECRET}) return r.json()["tenant_access_token"] def fetch_records(token, table_id): all_items = [] page_token = None while True: url = f"{BASE}/apps/{APP_TOKEN}/tables/{table_id}/records?page_size=200" if page_token: url += f"&page_token={page_token}" r = requests.get(url, headers={"Authorization": f"Bearer {token}"}) data = r.json() if data.get("code") != 0: print(f" Fetch error: {data}", file=sys.stderr) break all_items.extend(data["data"]["items"]) if not data["data"].get("has_more"): break page_token = data["data"].get("page_token") return all_items def write_record(token, table_id, record_id, result_text): r = requests.put( f"{BASE}/apps/{APP_TOKEN}/tables/{table_id}/records/{record_id}", headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json" }, json={"fields": {"审校结果": result_text}} ) data = r.json() return data.get("code") == 0 def audit_record(rec): issues = [] fields = rec.get("fields", {}) jd_raw = fields.get("jsonData") qs_id = fields.get("题目集合 ID", "") if not jd_raw: return None, False try: parsed = json.loads(jd_raw) except: return "❌ jsonData JSON解析失败", True first = parsed.get("first", {}) second = parsed.get("second", {}) qtype = first.get("type", "unknown") f_qsid = first.get("questionSetID", "") s_qsid = second.get("questionSetID", "") if qs_id and f_qsid and f_qsid != qs_id: issues.append(f" ❌ first questionSetID({f_qsid})与字段({qs_id})不一致") if qs_id and s_qsid and s_qsid != qs_id: issues.append(f" ❌ second questionSetID({s_qsid})与字段({qs_id})不一致") if f_qsid == "000001": issues.append(f" ❌ questionSetID为000001(占位数据)") if qs_id and not qs_id.replace("-","").isdigit() and qs_id != "000001": issues.append(f" ❌ 题目集合 ID异常: '{qs_id}'") for bname, block in [("first", first), ("second", second)]: qs = block.get("questionSet", []) if not isinstance(qs, list) or len(qs) == 0: continue for i, q in enumerate(qs): expl = q.get("explanation", "") if not expl or expl.strip() == "": issues.append(f" ❌ {bname}[{i}]: explanation为空") elif len(expl) < 20: issues.append(f" 🟡 {bname}[{i}]: explanation过短({len(expl)}字)") ability = q.get("ability", []) if not ability: issues.append(f" ❌ {bname}[{i}]: ability为空") else: found_bad_sep = False for a in ability: if isinstance(a, str) and "¥¥" in a: if not found_bad_sep: issues.append(f" ❌ {bname}[{i}]: ability用¥¥分隔(应为逗号)") found_bad_sep = True text1 = None text2 = None for k in ["题目1 完整配置", "题目1", "题目完整配置"]: if fields.get(k): text1 = fields[k] break for k in ["题目2 完整配置", "题目2"]: if fields.get(k): text2 = fields[k] break if not text1: issues.append(f" ❌ 题目1文本字段为空") if second and second.get("questionSet") and not text2: issues.append(f" 🟡 题目2文本字段为空(但jsonData有second)") if not issues: return f"✅ 审校通过\n题型:{qtype} | 题组:first={len(first.get('questionSet',[]))}题 second={len(second.get('questionSet',[]))}题", False else: return f"❌ 审校发现问题({len(issues)}项)\n题型:{qtype} | 题组:first={len(first.get('questionSet',[]))}题 second={len(second.get('questionSet',[]))}题\n" + "\n".join(issues), True def main(): token = get_token() print(f"Token获取成功") total_err = total_ok = total_skip = 0 for tname, tid in TABLES.items(): print(f"\n--- {tname} ---") records = fetch_records(token, tid) for rec in records: rid = rec["record_id"] fields = rec.get("fields", {}) ds = fields.get("dataStatus") if ds != "0" or not fields.get("jsonData"): total_skip += 1 continue result_text, has_err = audit_record(rec) if result_text is None: total_skip += 1 continue if write_record(token, tid, rid, result_text): tag = "🔴" if has_err else "✅" print(f" {tag} {rid} ✓") if has_err: total_err += 1 else: total_ok += 1 else: print(f" ❌ {rid} 写入失败") print(f"\n{'='*40}") print(f"✅通过={total_ok}, 🔴问题={total_err}, ⏭️跳过={total_skip}") if __name__ == "__main__": main()