#!/usr/bin/env python3 """Generate explanations for 171 empty speaking-P1 questions, then write back.""" import json, requests, time APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf" APP_ID = "cli_a931175d41799cc7" APP_SECRET = "Iw2vEfbjT6GtV0GhbxbZqfQ4nAPtbR14" TABLE = "tblRGv7k4WH58Jgq" 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}, timeout=10) return r.json()["tenant_access_token"] # ── Explanation generator ── # Map ability → template function that takes (question_content, ability_list) → explanation def gen_expl(q, abilities): """Generate a natural Chinese explanation for a speaking question.""" content = q.get('content', '') or q.get('question', '') ab = abilities[0] if abilities else '通用问答' # ── Pattern-based explanations ── # 基础信息表达|个人信息问答 if '个人信息' in ab: if 'name' in content.lower() or "what's your name" in content.lower(): return "本题考察基础个人信息表达能力。回答时直接说出自己的名字,如 'My name is Tom.' 注意使用完整的介绍句式,发音清晰。" if 'old' in content.lower() and 'how' in content.lower(): return "本题考察年龄表达。回答时用 'I am ... years old.' 的句式,说清楚数字。也可以补充生日信息使回答更丰富。" if 'from' in content.lower(): return "本题考察国籍与家乡表达。回答时用 'I am from ...' 或 'I come from ...' 的句式,可以说国家或城市名称。" if 'live' in content.lower(): return "本题考察家庭生活信息表达。回答时用 'I live with my ...' 描述同住的家人。注意家庭成员词汇的准确使用。" if 'hair' in content.lower() or 'look like' in content.lower() or 'wearing' in content.lower() or 'favourite clothes' in content.lower(): return "本题考察人物外貌与穿着描述能力。回答时使用 'He/She has...' 描述发型发色,用 'He/She is wearing...' 描述衣着,注意颜色和衣物词汇的准确使用。" if 'schoolbag' in content.lower() or 'in your' in content.lower() and ('schoolbag' in content.lower() or 'desk' in content.lower() or 'classroom' in content.lower()): return "本题考察物品列举与位置描述能力。回答时使用 'There is/are ... in my ...' 句式,逐一说出物品名称,注意单复数一致。" if 'tall' in content.lower(): return "本题考察比较级描述能力。回答时使用 '... is taller than me.' 的句式,说出比你高的人的名字。注意比较级的正确使用。" if 'dinosaur' in content.lower() or 'cloud' in content.lower() or 'moon' in content.lower(): return "本题考察想象与外观描述能力。回答时使用形容词描述事物的样子,如 'It is big and green.' 或 'It looks like ...',发挥想象力用英语表达。" if 'pet' in content.lower() and ('love' in content.lower() or 'like' in content.lower()): return "本题考察宠物喜好表达。回答时说出你喜欢的宠物,如 'I love dogs.' 并尝试说明喜欢的原因。注意动物名称词汇的准确使用。" if 'toy' in content.lower() or 'toys' in content.lower(): return "本题考察个人物品表达。回答时列举你拥有的玩具,用 'I have ...' 句式。注意玩具词汇和复数形式的正确使用。" if 'book' in content.lower() and 'where' in content.lower(): return "本题考察物品位置表达。回答时使用方位介词描述书的位置,如 'It is on the desk.' 或 'It is in my schoolbag.'" if 'pen' in content.lower() and 'yours' in content.lower(): return "本题考察物品归属表达。肯定回答用 'Yes, it is mine.',否定回答用 'No, it is not mine.' 注意物主代词的区分。" if 'goal' in content.lower(): return "本题考察目标与计划表达能力。回答时说出本学期的目标,如 'My goal is to read 10 English books.' 注意使用完整句子表达决心。" if 'keep healthy' in content.lower() or 'health' in content.lower(): return "本题考察健康建议表达能力。回答时列举保持健康的方法,如 'We can eat vegetables and exercise every day.' 注意使用情态动词 can/should。" # fallback for 个人信息 return f"本题考察基础个人信息表达能力。回答时需要围绕「{content[:20]}」这个主题用完整的英语句子进行描述或说明,注意信息清晰、表达自然。" # 表达喜好与理由 if '喜好' in ab: if 'favorite' in content.lower() or 'favourite' in content.lower(): return "本题考察喜好表达与理由说明能力。回答时先明确说出喜好选择,如 'My favorite ... is ...',然后用 'because' 或 'I like it because...' 说明理由。" if 'like' in content.lower() and 'music' in content.lower(): return "本题考察音乐喜好表达。回答时说出你喜欢的音乐类型,如 'I like pop music.' 再说明原因如 'because it makes me happy.' 也可以举一首喜欢的歌为例。" if 'like' in content.lower() and 'read' in content.lower(): return "本题考察阅读喜好表达。回答时说明你喜欢读什么类型的书,如 'I like story books.' 并补充阅读的感受或最喜欢的书籍。" if 'like' in content.lower() and 'internet' in content.lower(): return "本题考察网络活动喜好表达。回答时说出你在网上喜欢做什么,如 'I like watching videos and playing games online.' 用 and 连接多项活动。" if 'like' in content.lower() and 'cat' in content.lower(): return "本题考察原因解释能力。回答时用 'because' 引出原因,如 'I like cats because they are cute and soft.' 至少给出一个具体理由。" if 'hobby' in content.lower(): return "本题考察爱好表达。回答时说出你的爱好,如 'My hobby is drawing.' 或 'I like playing football.' 可以补充做这个爱好的频率和感受。" if 'think of' in content.lower() or 'think about' in content.lower(): return "本题考察观点表达能力。回答时用 'I think ...' 开头表达你的看法,然后简单说明原因。鼓励表达真实想法而不仅仅是正确回答。" if 'plan' in content.lower() or 'going to' in content.lower(): return "本题考察未来计划表达能力。回答时用 'I plan to ...' 或 'I am going to ...' 的句式描述计划,注意将来时的正确使用。" if 'want to' in content.lower() and 'do' in content.lower(): return "本题考察意愿与计划表达。回答时用 'I want to ...' 表达你想做的事,如果涉及周末计划可用 'I am going to ...'。" if 'dad' in content.lower() and ('unhappy' in content.lower() or 'say' in content.lower()): return "本题考察家庭情感与沟通表达。回答时描述爸爸的言行或情绪,如 'He said I should finish my homework first.' 注意引述和描述的准确性。" if 'happy' in content.lower() or 'glad' in content.lower(): return "本题考察积极情感表达能力。回答时用 'I am happy/glad that ...' 的句式表达喜悦,并具体说出让你开心的事情。" if 'afraid' in content.lower(): return "本题考察恐惧情感表达能力。回答时用 'I am afraid of ...' 说出害怕的动物或事物,可以简单说明原因如 'because it is scary.'" if 'need' in content.lower() or 'need to do' in content.lower(): return "本题考察需求表达能力。回答时用 'I need to ...' 或 'We need ...' 说明需要做的事情或物品。注意 need 后接动词原形或名词。" if 'colour' in content.lower() or 'color' in content.lower(): return "本题考察颜色偏好表达。回答时说出你想要的颜色,如 'I want the picture in blue.' 或 'I like red best.' 可以补充为什么喜欢这个颜色。" if 'whose' in content.lower(): return "本题考察物品归属判断与表达。回答时用 'I think it is ...'s.' 或 'It might be ...' 的句式表达推断。注意名词所有格的正确使用。" if 'exam' in content.lower(): return "本题考察学习准备与计划表达。回答时列举考前需做的事,如 'I need to review my notes and do more practice.' 注意使用 need to 表达必要性。" if 'wrong' in content.lower() and 'radio' in content.lower(): return "本题考察推断与猜测表达能力。回答时用 'Maybe it is ...' 或 'It might be ...' 表达对故障原因的推测。注意情态动词 might/maybe 的使用。" return f"本题考察表达喜好与理由的能力。回答时先表明喜好态度或观点,再用 'because' 等连接词说明理由,注意使用完整的英语句子。" # 互动应答|问答交流 if '互动应答' in ab or '问答交流' in ab: if 'help' in content.lower() and 'say' in content.lower(): return "本题考察礼貌应答能力。回答时给出得体的感谢用语,如 'Thank you so much!' 或 'That's very kind of you.' 注意感谢的真诚性表达。" if 'sorry' in content.lower() or 'lose' in content.lower() and 'book' in content.lower(): return "本题考察道歉表达与情境应对。回答时用诚恳的道歉语如 'I am so sorry. I will help you find it.' 同时给出弥补方案,体现责任感。" if 'library' in content.lower() and 'should' in content.lower(): return "本题考察规则表达能力。回答时使用情态动词如 'We shouldn't talk loudly.' 或 'We must be quiet.' 表达图书馆的行为规范。" if 'park' in content.lower() and ('must' in content.lower() or 'should' in content.lower()): return "本题考察规则与义务表达能力。回答时用 'We must not ...' 表达禁止行为,如 'We must not pick flowers or litter.' 注意 must 的使用。" if 'ill' in content.lower() or 'sad' in content.lower(): return "本题考察共情与关怀表达能力。回答时说出关怀的话语,如 'I hope you feel better soon.' 或 'Don't be sad, let me help you.' 体现同理心。" if 'leave' in content.lower(): return "本题考察礼貌告别用语。回答时说出得体的道别方式,如 'Goodbye!' 或 'See you later!' 也可以说 'It was nice meeting you.'" if 'friend comes' in content.lower() or 'friend came' in content.lower() or 'come to your home' in content.lower(): return "本题考察接待与欢迎表达能力。回答时说出欢迎用语,如 'Welcome! Come in, please.' 或 'I'm so glad you came!' 注意热情友好的语气。" if 'introduce' in content.lower(): return "本题考察介绍他人的能力。回答时使用 'This is my friend ...' 的句式介绍朋友,可以说出对方的名字和一两个特点。" if 'shop' in content.lower() or 'shopping' in content.lower(): return "本题考察购物场景交流能力。回答时模拟店员用语如 'Can I help you?' 或 'What would you like to buy?' 注意服务场景的礼貌表达。" if 'swim' in content.lower() or 'sports' in content.lower(): return "本题考察能力询问与应答。肯定回答用 'Yes, I can swim.',否定回答用 'No, I can't swim.' 或补充 'But I can run fast.'" if 'can you help' in content.lower(): return "本题考察请求帮助的表达与应答。回答时可以表示愿意帮助 'Sure, I will help you.' 或说明无法帮助的原因 'Sorry, my hands are full.'" if 'say when' in content.lower() and 'can' in content.lower() and 'find' in content.lower(): return "本题考察失物求助表达。回答时说出寻找物品的请求语,如 'I can't find my pen. Can you help me?' 或 'Have you seen my pen?'" if 'say when' in content.lower() and 'ruler' in content.lower(): return "本题考察借物请求表达。回答时说出礼貌的借物请求,如 'May I borrow your ruler, please?' 注意使用 May I 或 Can I 的礼貌表达。" if 'draw' in content.lower() and 'together' in content.lower(): return "本题考察邀请与提议表达能力。回答时使用 'Let's draw together!' 或 'Shall we draw a picture?' 表达邀请,语气友好自然。" if 'loud' in content.lower(): return "本题考察请求与协商表达能力。回答时礼貌地提出降低音量的请求,如 'Could you please turn it down?' 或 'It's a bit too loud.' 注意礼貌用语。" if 'rabbit' in content.lower() and 'eat' in content.lower(): return "本题考察观察与描述应答。回答时描述兔子正在吃什么,如 'The rabbit is eating a carrot.' 注意现在进行时的正确使用。" if 'see in the sky' in content.lower(): return "本题考察观察与描述应答。回答时描述天空中看到的事物,如 'I can see some birds and clouds.' 或 'I can see the sun.' 注意自然观察词汇。" if 'page' in content.lower(): return "本题考察课堂指令应答能力。回答时说出应该翻到的页码,如 'Let's turn to page 10.' 或 'Please turn to page 10.'" if 'animal' in content.lower() and 'look at' in content.lower(): return "本题考察选择与提议应答。回答时提出想看的动物,如 'Let's look at the elephants.' 或 'I want to see the monkeys.'" if 'car' in content.lower() and 'faster' in content.lower(): return "本题考察比较与判断应答。回答时使用比较级说出哪辆车更快,如 'The red car is faster.' 注意比较级 -er 或 more 的使用。" if 'gift' in content.lower() or 'present' in content.lower(): return "本题考察礼物相关表达能力。回答时描述想要的礼物或礼物送给谁,如 'I want a toy car for my birthday.' 或 'This gift is for my mum.'" if 'picnic' in content.lower(): return "本题考察准备与计划描述能力。回答时列举野餐需要带的东西,如 'We need some sandwiches, fruit and drinks for the picnic.' 注意物品词汇的准确使用。" return f"本题考察互动应答能力。回答时需要根据「{content[:20]}」的情境,给出得体的英文回应,注意使用礼貌用语和完整句子。" # 信息交换|双向问答 if '信息交换' in ab: if 'lunch' in content.lower() or 'eat' in content.lower(): return "本题考察饮食内容交流能力。回答时用 'Let's have ...' 提出午餐建议,或说出想吃的食物。注意食物名称词汇的准确使用,可以说明选择理由使交流更自然。" return "本题考察信息交换与双向交流能力。回答时不仅给出自己的答案,还可以反问对方以延续对话,注意问答之间的自然过渡和对话的互动性。" # 过去经历描述 if '过去经历' in ab or 'Past' in ab: if 'see' in content.lower() and ('park' in content.lower() or 'weekend' in content.lower()): return "本题考察过去经历描述能力。回答时使用过去时描述你在公园看到的事物,如 'I saw many beautiful flowers and some ducks in the pond.' 注意过去式动词的正确使用。" if 'help' in content.lower(): return "本题考察过去行为描述能力。回答时说出你帮助了谁以及怎么帮的,如 'I helped my mum clean the house.' 注意 help 后接动词原形。" if 'weekend' in content.lower(): return "本题考察过去活动描述能力。回答时描述周末发生的事情,用过去时讲述活动和感受,如 'Last weekend, I went to the zoo with my family.'" return f"本题考察过去经历描述能力。回答时使用过去时态描述「{content[:20]}」相关经历,注意动词过去式的变化,包含时间、地点和感受。" # Fallback return f"本题考察英语口语应答能力。请围绕「{content[:30]}」用完整句子进行回答,注意发音清晰、语法正确、表达自然。" # ── Main ── def main(): token = get_token() print("Token OK") with open('/root/.openclaw/workspace-xiaoyan/tmp/empty_expl_data.json') as f: records = json.load(f) print(f"Processing {len(records)} records, 171 questions...") updated = 0 for rec in records: rid = rec['rid'] jd = rec['jd'] qsid = rec['qsid'] modified = False for bn in ['first', 'second']: block = jd.get(bn, {}) if not block: continue qs = block.get('questionSet', []) for qi, q in enumerate(qs): expl = q.get('explanation', '') if expl == 'PATCH' or not expl or len(expl.strip()) < 5: abilities = q.get('ability', []) new_expl = gen_expl(q, abilities) q['explanation'] = new_expl modified = True updated += 1 if modified: # Write back new_jd_str = json.dumps(jd, ensure_ascii=False) r = requests.put( f'https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE}/records/{rid}', headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'}, json={'fields': {'jsonData': new_jd_str}}, timeout=15) code = r.json().get('code') if code == 0: print(f' ✅ {qsid} ({rid[:12]}...)') else: print(f' ❌ {qsid}: {r.json().get("msg")}') time.sleep(0.25) print(f'\nDone: {updated} explanations generated across {len(records)} records') if __name__ == '__main__': main()