#!/usr/bin/env python3 """Batch write all P4 rewrites to bitable - proper version""" 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 from rewrite_p4_all import make_q1_text 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"] result = 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(result.stdout)["tenant_access_token"] def write_and_verify(token, qsid, record_id, fields): """Write and immediately verify one record""" payload = json.dumps({"fields": fields}, ensure_ascii=False) tmp = f"/tmp/p4_write_{record_id}.json" with open(tmp, "w") as f: f.write(payload) result = 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(result.stdout) code = resp.get("code", -1) if code != 0: return False, f"API error: {resp.get('msg','')}" # Read back to verify result = 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(result.stdout) vcode = vdata.get("code", -1) if vcode != 0: return False, f"Verify read error: {vdata.get('msg','')}" rjd = vdata.get("data", {}).get("record", {}).get("fields", {}).get("jsonData", "") if not rjd: return False, "jsonData empty after write" try: parsed = json.loads(rjd) fqs = parsed.get("first", {}).get("questionSet", []) first_ability = fqs[0].get("ability", []) if fqs else [] first_expl = fqs[0].get("explanation", "") if fqs else "" return True, f"Q0 ability={first_ability}, expl_len={len(first_expl)}" except Exception as e: return False, f"jsonData parse error: {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"] # Clean dialogues for q in fb["questionSet"]: q.pop("dialogue", None) if sb and isinstance(sb, dict) and "questionSet" in sb: for q in sb["questionSet"]: q.pop("dialogue", None) json_data = {"first": fb, "second": sb} q1_text = make_q1_text(fqs) q2_text = make_q1_text(sqs) if sqs else "" fields = { "jsonData": json.dumps(json_data, ensure_ascii=False), "题目1": q1_text, "题目2": q2_text, } print(f"\nWriting {qsid} ({record_id})...") ok, msg = write_and_verify(token, qsid, record_id, fields) status = "✅" if ok else "❌" print(f" {status} {msg}") results.append((qsid, ok, msg)) # Small delay to avoid rate limiting time.sleep(0.3) 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: print("\nFailed records:") for qsid, ok, msg in results: if not ok: print(f" ❌ {qsid}: {msg}") if __name__ == "__main__": main()