ai_member_xiaoyan/scripts/batch_reading_pic_judge.py

266 lines
13 KiB
Python

#!/usr/bin/env python3
"""Produce reading_pic_judge for 4 IDs (121301-121601), L1 A级."""
import json, requests, time
CRED_FILE = "/root/.openclaw/credentials/xiaoyan/config.json"
APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf"
TABLE_ID = "tblJc60aO0T163MJ"
# ============================================================
# ALL QUESTIONS — L1 A级, 3-4词极简句, L1词汇
# ============================================================
ALL_DATA = {}
# ---- 121301 ----
ALL_DATA["121301"] = {
"first": [
{"qi": "121301-00.png", "q": "These are houses.", "a": "Yes",
"x": "图上有三栋房子,句子说「这些是房子」,匹配。"},
{"qi": "121301-01.png", "q": "This is a home.", "a": "No",
"x": "图上是一个橱柜,不是家(home),句子与图片不匹配。"},
{"qi": "121301-02.png", "q": "This is a sofa.", "a": "Yes",
"x": "图上是一张沙发,句子说「这是一张沙发」,匹配。"},
{"qi": "121301-03.png", "q": "These are cupboards.", "a": "No",
"x": "图上是一间卧室放着一张床,句子说「这些是橱柜」,与图片不匹配。"},
{"qi": "121301-04.png", "q": "This is a bedroom.", "a": "Yes",
"x": "图上是一间有床的卧室,句子说「这是一间卧室」,匹配。"},
],
"second": [
{"qi": "121301-05.png", "q": "This is a zoo.", "a": "Yes",
"x": "图上是动物园入口,能看到动物,句子说「这是一个动物园」,匹配。"},
{"qi": "121301-06.png", "q": "This is a hippo.", "a": "Yes",
"x": "图上是一只河马在水里,句子说「这是一只河马」,匹配。"},
{"qi": "121301-07.png", "q": "This is a bear.", "a": "No",
"x": "图上是一条牛仔裤,不是熊(bear),句子与图片不匹配。"},
{"qi": "121301-08.png", "q": "These are jeans.", "a": "Yes",
"x": "图上是一条牛仔裤,句子说「这些是牛仔裤」,匹配。"},
{"qi": "121301-09.png", "q": "This is a sock.", "a": "No",
"x": "图上是一只大棕熊在森林里,不是袜子(sock),句子与图片不匹配。"},
],
}
# ---- 121401 ----
ALL_DATA["121401"] = {
"first": [
{"qi": "121401-00.png", "q": "These are students.", "a": "Yes",
"x": "图上是几个学生坐在教室里,句子说「这些是学生」,匹配。"},
{"qi": "121401-01.png", "q": "This is a school.", "a": "Yes",
"x": "图上是一所学校建筑,句子说「这是一所学校」,匹配。"},
{"qi": "121401-02.png", "q": "This is a teacher.", "a": "No",
"x": "图上是几支蜡笔,不是老师(teacher),句子与图片不匹配。"},
{"qi": "121401-03.png", "q": "These are crayons.", "a": "Yes",
"x": "图上是几支彩色蜡笔放在桌上,句子说「这些是蜡笔」,匹配。"},
{"qi": "121401-04.png", "q": "These are boards.", "a": "No",
"x": "图上是一位老师在讲桌前,不是黑板(board),句子与图片不匹配。"},
],
"second": [
{"qi": "121401-05.png", "q": "This is a mat.", "a": "Yes",
"x": "图上是一张地板上的垫子,句子说「这是一张垫子」,匹配。"},
{"qi": "121401-06.png", "q": "This is a letter.", "a": "No",
"x": "图上是一张课桌上面有台灯,不是信(letter),句子与图片不匹配。"},
{"qi": "121401-07.png", "q": "This is a desk.", "a": "Yes",
"x": "图上是一张课桌,句子说「这是一张课桌」,匹配。"},
{"qi": "121401-08.png", "q": "This is a schoolbag.", "a": "No",
"x": "图上是一个女士手提包,不是书包(schoolbag),句子与图片不匹配。"},
{"qi": "121401-09.png", "q": "This is a handbag.", "a": "Yes",
"x": "图上是一个手提包,句子说「这是一个手提包」,匹配。"},
],
}
# ---- 121501 ----
ALL_DATA["121501"] = {
"first": [
{"qi": "121501-00.png", "q": "This is a body.", "a": "Yes",
"x": "图上是一个孩子的全身,句子说「这是一个身体」,匹配。"},
{"qi": "121501-01.png", "q": "This is a head.", "a": "No",
"x": "图上是一张嘴的特写,不是头(head),句子与图片不匹配。"},
{"qi": "121501-02.png", "q": "These are mouths.", "a": "No",
"x": "图上是两个孩子在玩耍(朋友),不是嘴(mouth),句子与图片不匹配。"},
{"qi": "121501-03.png", "q": "This is an ear.", "a": "Yes",
"x": "图上是一只耳朵的特写,句子说「这是一只耳朵」,匹配。"},
{"qi": "121501-04.png", "q": "These are friends.", "a": "Yes",
"x": "图上是两个小朋友在一起玩,句子说「这些是朋友」,匹配。"},
],
"second": [
{"qi": "121501-05.png", "q": "This is a camera.", "a": "Yes",
"x": "图上是一台相机,句子说「这是一台相机」,匹配。"},
{"qi": "121501-06.png", "q": "This is a boy.", "a": "Yes",
"x": "图上是一个小男孩,句子说「这是一个男孩」,匹配。"},
{"qi": "121501-07.png", "q": "This is a girl.", "a": "No",
"x": "图上是一个滑板,不是女孩(girl),句子与图片不匹配。"},
{"qi": "121501-08.png", "q": "These are skateboards.", "a": "No",
"x": "图上是一个女孩在滑滑板,句子说「这些是滑板」但图中只有一个,且重点是人在滑板动作,与句子描述不符。"},
{"qi": "121501-09.png", "q": "This is a skateboard.", "a": "Yes",
"x": "图上是一个滑板,句子说「这是一个滑板」,匹配。"},
],
}
# ---- 121601 ----
ALL_DATA["121601"] = {
"first": [
{"qi": "121601-00.png", "q": "It is summer.", "a": "Yes",
"x": "图上是阳光海滩夏天场景,句子说「现在是夏天」,匹配。"},
{"qi": "121601-01.png", "q": "It is autumn.", "a": "No",
"x": "图上是冬天白雪覆盖的场景,不是秋天(autumn),句子与图片不匹配。"},
{"qi": "121601-02.png", "q": "It is winter.", "a": "Yes",
"x": "图上是一片雪景,句子说「现在是冬天」,匹配。"},
{"qi": "121601-03.png", "q": "It is cold.", "a": "Yes",
"x": "图上是一个孩子穿着厚棉衣冬天场景,句子说「天气冷」,匹配。"},
{"qi": "121601-04.png", "q": "It is spring.", "a": "No",
"x": "图上是秋天落叶满地,不是春天(spring),句子与图片不匹配。"},
],
"second": [
{"qi": "121601-05.png", "q": "This is a bus.", "a": "Yes",
"x": "图上是一辆公交车,句子说「这是一辆公交车」,匹配。"},
{"qi": "121601-06.png", "q": "This is a taxi.", "a": "No",
"x": "图上是一条长裤,不是出租车(taxi),句子与图片不匹配。"},
{"qi": "121601-07.png", "q": "These are trousers.", "a": "Yes",
"x": "图上是一条长裤,句子说「这些是长裤」,匹配。"},
{"qi": "121601-08.png", "q": "These are shorts.", "a": "No",
"x": "图上是白雪皑皑的下雪场景,不是短裤(shorts),句子与图片不匹配。"},
{"qi": "121601-09.png", "q": "This is snow.", "a": "Yes",
"x": "图上是一片雪景,句子说「这是雪」,匹配。"},
],
}
# ============================================================
# Build JSON
# ============================================================
def build_json(qid, groups):
return {
"first": {
"category": "reading",
"type": "reading_pic_judge",
"questionSetID": qid,
"textTitle": "Look and read. Put a tick (Yes) or a cross (No) in the box.",
"questionSet": [
{
"questionImage": item["qi"],
"question": item["q"],
"answerText": item["a"],
"ability": ["图文匹配"],
"explanation": item["x"],
}
for item in groups["first"]
],
},
"second": {
"category": "reading",
"type": "reading_pic_judge",
"questionSetID": qid,
"textTitle": "Look and read. Put a tick (Yes) or a cross (No) in the box.",
"questionSet": [
{
"questionImage": item["qi"],
"question": item["q"],
"answerText": item["a"],
"ability": ["图文匹配"],
"explanation": item["x"],
}
for item in groups["second"]
],
},
}
# ============================================================
# Get token & find existing records
# ============================================================
with open(CRED_FILE) as f:
cred = json.load(f)
app_id = cred["apps"][0]["appId"]
app_secret = cred["apps"][0]["appSecret"]
token_resp = requests.post(
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
json={"app_id": app_id, "app_secret": app_secret},
)
token = token_resp.json()["tenant_access_token"]
# Find existing records
resp = requests.get(
f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records?page_size=20",
headers={"Authorization": f"Bearer {token}"},
)
record_map = {}
for item in resp.json()["data"]["items"]:
fid = item["fields"].get("题目集合 ID", "")
if fid in ALL_DATA:
record_map[fid] = item["record_id"]
print(f"Found: {fid}{item['record_id']}")
# ============================================================
# Update each record with real jsonData
# ============================================================
for qid in sorted(ALL_DATA.keys()):
rid = record_map.get(qid)
if not rid:
print(f"{qid}: no existing record, creating new...")
resp = requests.post(
f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json={"fields": {"题目集合 ID": qid, "dataStatus": "0"}},
)
rid = resp.json()["data"]["record"]["record_id"]
print(f" Created: {rid}")
time.sleep(1.0)
json_data = build_json(qid, ALL_DATA[qid])
json_str = json.dumps(json_data, ensure_ascii=False)
# Verify balance
for grp in ("first", "second"):
qs = json_data[grp]["questionSet"]
yc = sum(1 for q in qs if q["answerText"] == "Yes")
nc = sum(1 for q in qs if q["answerText"] == "No")
print(f" {qid} {grp}: {yc}Y/{nc}N, json={len(json_str)}chars")
# Update
resp = requests.put(
f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{rid}",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json={"fields": {"jsonData": json_str}},
)
code = resp.json().get("code")
print(f" Update: code={code}")
time.sleep(0.5)
# ============================================================
# Wait & Verify
# ============================================================
print("\nWaiting 15s for verification...")
time.sleep(15)
token_resp = requests.post(
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
json={"app_id": app_id, "app_secret": app_secret},
)
token = token_resp.json()["tenant_access_token"]
print("\n=== Final Verification ===")
all_ok = True
for qid in sorted(ALL_DATA.keys()):
rid = record_map.get(qid)
resp = requests.get(
f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{rid}",
headers={"Authorization": f"Bearer {token}"},
)
jd = resp.json()["data"]["record"]["fields"].get("jsonData", "")
try:
j = json.loads(jd)
fq = len(j["first"]["questionSet"])
sq = len(j["second"]["questionSet"])
if fq == 5 and sq == 5:
fq1 = j["first"]["questionSet"][0]["question"]
sq1 = j["second"]["questionSet"][0]["question"]
fy = sum(1 for q in j["first"]["questionSet"] if q["answerText"] == "Yes")
fn = sum(1 for q in j["first"]["questionSet"] if q["answerText"] == "No")
print(f"{qid}: first={fq}q({fy}Y{fn}N) second={sq}q | Q1='{fq1}' | SQ1='{sq1}'")
else:
print(f"{qid}: first={fq}q second={sq}q | jd={len(jd)}chars")
all_ok = False
except Exception as e:
print(f"{qid}: PARSE_ERROR {e} | jd={len(jd)}chars")
all_ok = False
print(f"\n{'✅ ALL VERIFIED' if all_ok else '❌ SOME CORRUPTED'}")