#!/usr/bin/env python3 """ P4 rewrite with automation-safe approach: - jsonData: use ability=['听觉抓取关键信息'], explanation='' (automation-compatible) - 题目1/题目2: include full ability + explanation info in text """ import json, subprocess, sys, os, time AT = "CMHSbUUjka3TrUsaxxEc297ongf" TID = "tblVmeDtBDKsAEfz" BASE = os.path.expanduser("~/.openclaw/workspace-xiaoyan") CRED_FILE = os.path.expanduser("~/.openclaw/credentials/xiaoyan/config.json") sys.path.insert(0, os.path.join(BASE, "scripts")) import rewrite_p4_all as rw RECORD_IDS = { "021301": "recvjufKJO8d0O", "021401": "recvjufM763ijb", "021501": "recvjufM76lEsW", "021601": "recvjufM76hNv5", "021701": "recvjufM76eMKs", "021801": "recvjufM76lRHQ", "022101": "recvjufM76o6of", "032501": "recvjufM76k4dx", "032901": "recvjufM76frUP", } def get_token(): with open(CRED_FILE) as f: cred = json.load(f) app_id = cred["apps"][0]["appId"] app_secret = cred["apps"][0]["appSecret"] r = subprocess.run(["curl","-s","-X","POST", "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", "-H","Content-Type: application/json", "-d",json.dumps({"app_id":app_id,"app_secret":app_secret})], capture_output=True, text=True) return json.loads(r.stdout)["tenant_access_token"] def make_qtext_with_info(questions): """Generate 题目 text with dialogue, ability and explanation info""" lines = [] for i, q in enumerate(questions, 1): lines.append(f"{i}.") lines.append(f"【描述】{q.get('questionDesc','')}") lines.append(f"【听力文本】") if 'dialogue' in q: lines.append(q['dialogue']) lines.append(f"【题目】") lines.append(q['question']) for j, opt in enumerate(q.get('options',[])): marker = "(正确)" if j == q.get('answer',[-1])[0] else "" lines.append(f"{chr(65+j)}. {opt}{marker}") # Add ability info ability = q.get('new_ability', q.get('ability', ['听力理解'])) lines.append(f"【能力标签】{'|'.join(ability) if isinstance(ability,list) else ability}") # Add explanation lines.append(f"【解析】{q.get('explanation','')}") lines.append("") return "\n".join(lines) def sanitize_json(json_data): """Replace ability/explanation with automation-safe values""" for qs in json_data.get("first", {}).get("questionSet", []): qs["ability"] = ["听觉抓取关键信息"] qs["explanation"] = "" second = json_data.get("second", {}) if second and isinstance(second, dict) and "questionSet" in second: for qs in second.get("questionSet", []): qs["ability"] = ["听觉抓取关键信息"] qs["explanation"] = "" return json_data def write_one(token, qsid, record_id, json_data, q1_text, q2_text): """Write one record and verify""" json_str = json.dumps(json_data, ensure_ascii=False) fields = {"jsonData": json_str, "题目1": q1_text, "题目2": q2_text} payload = json.dumps({"fields": fields}, ensure_ascii=False) tmp = f"/tmp/p4_safe_{record_id}.json" with open(tmp, "w") as f: f.write(payload) r = subprocess.run(["curl","-s","-X","PUT", f"https://open.feishu.cn/open-apis/bitable/v1/apps/{AT}/tables/{TID}/records/{record_id}", "-H",f"Authorization: Bearer {token}", "-H","Content-Type: application/json", "-d",f"@{tmp}"], capture_output=True, text=True, timeout=30) os.remove(tmp) resp = json.loads(r.stdout) if resp.get("code") != 0: return False, f"API error: {resp.get('msg','')}" # Verify r = subprocess.run(["curl","-s","-X","GET", f"https://open.feishu.cn/open-apis/bitable/v1/apps/{AT}/tables/{TID}/records/{record_id}", "-H",f"Authorization: Bearer {token}"], capture_output=True, text=True, timeout=30) vdata = json.loads(r.stdout) if vdata.get("code") != 0: return False, f"Verify error: {vdata.get('msg','')}" rec = vdata.get("data",{}).get("record",{}) jd = rec.get("fields",{}).get("jsonData","") q1 = rec.get("fields",{}).get("题目1","") or "" if not jd: return False, "jsonData empty" try: parsed = json.loads(jd) fqs = parsed.get("first",{}).get("questionSet",[]) if not fqs: return False, "no first questions" q0_question = fqs[0].get("question","") q0_ability = fqs[0].get("ability","") return True, f"Qs={len(fqs)}, q0='{q0_question[:30]}', ability={q0_ability}, Q1_len={len(q1)}" except Exception as e: return False, f"parse: {e}" def main(): token = get_token() print(f"Token: {token[:15]}...") results = [] for qsid, record_id in RECORD_IDS.items(): data = rw.RECORD_DATA.get(qsid) if not data: continue fb = data["first_block"] sb = data["second_block"] fqs = data["first_qs_list"] sqs = data["second_qs_list"] # Build jsonData - keep original dialogue but not in final jsonData json_data = {"first": fb, "second": sb} # Build Q1/Q2 text with full info q1_text = make_qtext_with_info(fqs) q2_text = make_qtext_with_info(sqs) if sqs else "" # Clean dialogues from jsonData and sanitize for q in fb["questionSet"]: q.pop("dialogue", None) q.pop("new_ability", None) if sb and isinstance(sb, dict) and "questionSet" in sb: for q in sb["questionSet"]: q.pop("dialogue", None) q.pop("new_ability", None) json_data = sanitize_json(json_data) print(f"\nWriting {qsid} ({record_id})...") ok, msg = write_one(token, qsid, record_id, json_data, q1_text, q2_text) status = "✅" if ok else "❌" print(f" {status} {msg}") results.append((qsid, ok, msg)) time.sleep(0.5) success = sum(1 for r in results if r[1]) fails = sum(1 for r in results if not r[1]) print(f"\n{'='*60}") print(f"FINAL: {success} succeeded, {fails} failed") if fails: for qsid, ok, msg in results: if not ok: print(f" ❌ {qsid}: {msg}") if __name__ == "__main__": main()