157 lines
11 KiB
Python
157 lines
11 KiB
Python
"""
|
||
最终修复:P1/P3/P4/P5 的能力标签+解析,使用直接 curl(tempfile 入参,无 shell 转义风险)
|
||
"""
|
||
import json, subprocess
|
||
|
||
cred_file = '/root/.openclaw/credentials/xiaoyan/config.json'
|
||
with open(cred_file) as f:
|
||
cfg = json.load(f)
|
||
app_id = cfg['apps'][0]['appId']
|
||
app_secret = cfg['apps'][0]['appSecret']
|
||
body = json.dumps({'app_id': app_id, 'app_secret': app_secret})
|
||
r = subprocess.run(f"curl -s -X POST 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal' -H 'Content-Type: application/json' -d '{body}'", shell=True, capture_output=True, text=True, timeout=10)
|
||
token = json.loads(r.stdout)['tenant_access_token']
|
||
|
||
APP_TOKEN = 'CMHSbUUjka3TrUsaxxEc297ongf'
|
||
|
||
def fetch(tid):
|
||
r = subprocess.run(f"curl -s -X GET 'https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{tid}/records?page_size=500' -H 'Authorization: Bearer {token}'", shell=True, capture_output=True, text=True, timeout=15)
|
||
return json.loads(r.stdout)
|
||
|
||
def update(tid, rid, fields):
|
||
tmpfile = '/tmp/bitable_fix.json'
|
||
with open(tmpfile, 'w') as f:
|
||
json.dump({"fields": fields}, f, ensure_ascii=False)
|
||
r = subprocess.run(f"curl -s -X PUT 'https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{tid}/records/{rid}' -H 'Authorization: Bearer {token}' -H 'Content-Type: application/json' -d @{tmpfile}", shell=True, capture_output=True, text=True, timeout=15)
|
||
return json.loads(r.stdout)
|
||
|
||
# ============ P1 032501 ============
|
||
data = fetch('tblCgfYDnnqwLfgH')
|
||
for item in data['data']['items']:
|
||
if item['record_id'] == 'recvjt0HzBBhYu' and item['fields'].get('题目集合 ID','') == '032501':
|
||
jd = json.loads(item['fields']['jsonData'])
|
||
for q in jd['first']['questionSet']:
|
||
ab = [{'信息定位与提取':'扫读定位|信息匹配','细节理解':'细节理解|事实信息提取'}.get(a,a) for a in q['ability']]
|
||
q['ability'] = ab
|
||
resp = update('tblCgfYDnnqwLfgH', 'recvjt0HzBBhYu', {'jsonData': json.dumps(jd, ensure_ascii=False)})
|
||
print(f"P1 032501: {'✅' if resp.get('code')==0 else '❌'+str(resp.get('msg',''))}")
|
||
|
||
# ============ P3 032601 ============
|
||
data = fetch('tbl4q0ZUV3HB54t1')
|
||
for item in data['data']['items']:
|
||
if item['record_id'] == 'recvjug1hWz2oG':
|
||
jd = json.loads(item['fields']['jsonData'])
|
||
TAG = {
|
||
'信息定位':'扫读定位|信息匹配','细节理解':'细节理解|事实信息提取',
|
||
'因果推断':'推理判断|原因/结果','主旨归纳':'主旨理解|段落/文本大意',
|
||
'推理判断':'推理判断|原因/结果',
|
||
}
|
||
# Also fix any corrupted tags by looking at original context
|
||
# Q0: what did they plan → 细节理解|事实信息提取
|
||
# Q1: why late → 推理判断|原因/结果
|
||
# Q2: what did they do → 细节理解|事实信息提取
|
||
# Q3: what was favorite → 细节理解|事实信息提取
|
||
# Q4: what lesson → 主旨理解|段落/文本大意
|
||
CORRECT = [
|
||
['扫读定位|信息匹配', '细节理解|事实信息提取'],
|
||
['扫读定位|信息匹配', '推理判断|原因/结果'],
|
||
['扫读定位|信息匹配', '细节理解|事实信息提取'],
|
||
['扫读定位|信息匹配', '细节理解|事实信息提取'],
|
||
['主旨理解|段落/文本大意', '推理判断|原因/结果'],
|
||
]
|
||
for qi, q in enumerate(jd['first']['questionSet']):
|
||
q['ability'] = CORRECT[qi]
|
||
resp = update('tbl4q0ZUV3HB54t1', 'recvjug1hWz2oG', {'jsonData': json.dumps(jd, ensure_ascii=False)})
|
||
print(f"P3 032601: {'✅' if resp.get('code')==0 else '❌'+str(resp.get('msg',''))}")
|
||
|
||
# ============ P4 032701 & 032801 ============
|
||
EXPL_032701 = [
|
||
"空白处需要表示赚钱的名词。get some cash 是地道表达,cash(现金)最符合学生售卖手链的情境。cheque(支票)语义过正式,bracelet(手链)是销售的商品而非获取的目标。考查名词在语境中的最佳语义选择。",
|
||
"修饰可数名词bracelets应用many(许多)。much修饰不可数名词,only(只有)在此处语义不流畅,不符合带了一些手链去给朋友看的语境。考查可数名词数量修饰词的语法规则。",
|
||
"表语位置需形容词。They were very popular(它们很受欢迎)才是完整句子。cash和sell均非形容词,不能直接作表语。考查词性辨别和上下文语义连贯。",
|
||
"students是可数名词复数,需用many修饰表示很多学生。much用于不可数名词,only在so many结构后不合适。考查可数名词数量表达。",
|
||
"bracelets是可数名词复数,需用many表示数量。didn't have many bracelets表示手链不够多,与后文promised to make more呼应。考查上下文逻辑与可数名词修饰。",
|
||
"空白处需动词作谓语。She could sell a few(她能卖出几条)中sell是唯一动词。cheque和popular均为非动词。考查句子主干成分(谓语动词)的语法识别。",
|
||
]
|
||
EXPL_032801 = [
|
||
"根据后文a local band was playing music和it was very noisy,可推断是一场派对。party最能描述这种热闹场景,car和sleep与乐队演奏、噪音等语境不符。考查名词的语境语义选择。",
|
||
"I tried to sleep, but the sound was too loud——噪音太大导致睡不着,sleep是最合理的选择。drive和sing都偏离语境。考查动词的语义逻辑匹配。",
|
||
"描述派对参与者身份,adult people(成年人)与午夜派对的情境相符,也为后文decided to be more understanding提供合理性。考查修饰词的语境判断。",
|
||
"The party went on until midnight——派对持续到午夜是最符合逻辑的时间终点。morning和afternoon时间太早,不符合嘈杂派对的情境。考查时间名词的合理推断。",
|
||
"前后分句存在转折关系:I was annoyed because I couldn't rest, but then I remembered...。but引导转折,because表示原因,so表示结果。考查连词的逻辑关系。",
|
||
"此处需引导原因的连词。because the band members cleaned up解释为什么第二天心情好转。although表示让步,until表示时间。考查原因连词的选择。",
|
||
]
|
||
|
||
data = fetch('tblzKVm1FEukPgnN')
|
||
for sid, rid, expls in [('032701','recvjueHm15HPu',EXPL_032701),('032801','recvjueN5QT1c5',EXPL_032801)]:
|
||
for item in data['data']['items']:
|
||
if item['record_id'] == rid:
|
||
jd = json.loads(item['fields']['jsonData'])
|
||
t1 = item['fields'].get('题目1','') or ''
|
||
for qi, q in enumerate(jd['first']['questionSet']):
|
||
q['ability'] = ['语法结构识别|完形填空']
|
||
q['explanation'] = expls[qi]
|
||
# Build 题目1 with explanations
|
||
if '【解析】' not in t1:
|
||
t1_lines = [f"{qi+1}. {expls[qi]}" for qi in range(len(expls))]
|
||
t1_new = t1 + '\n\n【解析】\n' + '\n'.join(t1_lines)
|
||
else:
|
||
t1_new = t1
|
||
update_fields = {
|
||
'jsonData': json.dumps(jd, ensure_ascii=False),
|
||
'题目1': t1_new,
|
||
}
|
||
resp = update('tblzKVm1FEukPgnN', rid, update_fields)
|
||
print(f"P4 {sid}: {'✅' if resp.get('code')==0 else '❌'+str(resp.get('msg',''))}")
|
||
|
||
# ============ P5 032901 ============
|
||
EXPL_032901_first = [
|
||
"前后是因果关系:去野生动物园是因为想看小象。because引导原因状语从句最合适。but表示转折,so表示结果,均不符合语义。考查原因连词。",
|
||
"had just been born是被动语态,表示刚刚出生。born是bear的过去分词,与a few weeks ago的时间状语呼应。grow up(长大)和top(顶端)语义不通。考查词义辨析与被动语态。",
|
||
"reach the top of a tall tree——大象想够到高树的顶端。top表示顶部位置,与tall tree形成语义对应。bottom与reach矛盾,nose不是位置。考查方位词的选择。",
|
||
"大象的长鼻子是标志性特征。nose是正确选项,banana和bottom明显不符。考查动物特征相关的核心词汇。",
|
||
"could only touch the bottom——虽然用长鼻子去够,但只碰到了底部。bottom与top形成对比,体现够不到的落差感。考查反义词对(top↔bottom)的理解。",
|
||
"小象吃黄色的香蕉是最自然的食物搭配。banana是常见动物投喂食物,elephant和nose均不符合吃的语义。考查动物食物相关词汇。",
|
||
"when I grow up(等我长大)是固定表达。grow up表示成长、长大,born与when从句时态矛盾,bottom不相关。考查固定短语的掌握。",
|
||
"根据上下文描述的美好回忆,bottom指从心底的比喻义。from the bottom of my heart是常见表达。top与情感表达不相配,nose属于干扰项。考查固定搭配和比喻义。",
|
||
]
|
||
EXPL_032901_second = [
|
||
"前后分句是因果关系:姐姐喜欢美丽的东西,所以总戴首饰。so引导结果,because引导原因,but表示转折。考查结果连词。",
|
||
"lost one of her favourite silver earrings——在首饰语境中,银色的earrings(耳环)是最典型的可丢失物品。painting和language与silver修饰和wear搭配都不符。考查语境词义推断。",
|
||
"couldn't find it anywhere else——在否定句中else表示别的地方/其他任何地方,except含义不符,again重复逻辑不通。考查否定句中的词汇用法。",
|
||
"I decided to make..., although I'm not very good——前后存在让步转折关系,虽然不擅长但还是决定做。although正确,because和so均表示因果。考查让步连词。",
|
||
"She showed me how to use small silver pieces——朋友擅长的是首饰制作(jewellery),与silver pieces和earrings主题一致。painting和language偏离主题。考查上下文主题关联。",
|
||
"I made a mistake, so I had to start again——犯错后重新开始,again表示再一次。else用于否定句表另外,except表除了。考查副词选择。",
|
||
]
|
||
|
||
data = fetch('tblLmUxzzUDe0QAJ')
|
||
for item in data['data']['items']:
|
||
if item['record_id'] == 'recvjueULrufNg':
|
||
jd = json.loads(item['fields']['jsonData'])
|
||
t1 = item['fields'].get('题目1','') or ''
|
||
t2 = item['fields'].get('题目2','') or ''
|
||
|
||
for qi, q in enumerate(jd['first']['questionSet']):
|
||
q['ability'] = ['语法结构识别|完形填空']
|
||
q['explanation'] = EXPL_032901_first[qi]
|
||
for qi, q in enumerate(jd['second']['questionSet']):
|
||
q['ability'] = ['语法结构识别|完形填空']
|
||
q['explanation'] = EXPL_032901_second[qi]
|
||
|
||
if '【解析】' not in t1:
|
||
t1_new = t1 + '\n\n【解析】\n' + '\n'.join(f"{i+1}. {e}" for i,e in enumerate(EXPL_032901_first))
|
||
else:
|
||
t1_new = t1
|
||
if '【解析】' not in t2:
|
||
t2_new = t2 + '\n\n【解析】\n' + '\n'.join(f"{i+1}. {e}" for i,e in enumerate(EXPL_032901_second))
|
||
else:
|
||
t2_new = t2
|
||
|
||
update_fields = {
|
||
'jsonData': json.dumps(jd, ensure_ascii=False),
|
||
'题目1': t1_new,
|
||
'题目2': t2_new,
|
||
}
|
||
resp = update('tblLmUxzzUDe0QAJ', 'recvjueULrufNg', update_fields)
|
||
print(f"P5 032901: {'✅' if resp.get('code')==0 else '❌'+str(resp.get('msg',''))}")
|