#!/usr/bin/env python3 """批量更新 L2 和共用题型 SKILL.md 中的难度对应关系表""" import os import re BASE = "/root/.openclaw/workspace-xiaoyan/business_production/单元挑战/skills/unit_challenge/questions" # New L2 8-stage difficulty table L2_NEW_TABLE = """## 难度对应关系(与教研标准对齐) | 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | |------|---------|---------|------| """ # Store the A/B/C/D rows with the new mapping - 通用声明式,实际难度描述(最后一列)保留原样 L2_NEW_HEADER = "| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 |" # Maps for old->new grade columns L2_GRADE_MAP = { "A级": "| A级 | U1-U12(阶段1-2) | Flyers入门→基础 |", "B级": "| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 |", "C级": "| C级 | U25-U36(阶段5-6) | KET基础→强化 |", "D级": "| D级 | U37-U48(阶段7-8) | KET高位→达标 |", } def update_l2_exclusive(filepath): """Update L2专属题型: replace entire difficulty table with 8-stage version""" with open(filepath, 'r', encoding='utf-8') as f: content = f.read() original = content # ---- Part 1: Replace the difficulty table ---- # Find the section from "## 难度对应关系" to next heading/separator pattern = r'(## 难度对应关系.*?\n)\| 难度等级 \|.*?\n\|[-|\s]*\n((?:\| [ABCD]级 \|.*?\n)+)' def replace_table(match): section_title = match.group(1) old_rows = match.group(2) new_rows = [] for old_row in old_rows.strip().split('\n'): # Extract the last column (难度描述) from old row parts = old_row.split('|') if len(parts) >= 4: desc = parts[-2].strip() if len(parts) > 4 else parts[-1].strip() # Determine which grade for grade, new_prefix in L2_GRADE_MAP.items(): if grade in old_row: new_rows.append(f"{new_prefix} {desc} |") break else: new_rows.append(old_row) return section_title + L2_NEW_HEADER + '\n' + '\n'.join(new_rows) + '\n' content = re.sub(pattern, replace_table, content, flags=re.DOTALL) # ---- Part 2: Update config field description for 难度等级 ---- # Pattern: | 难度等级 | ✅ | `A级` / `...` / `...` | ... | # Replace with new references def replace_config(match): full_line = match.group(0) # Replace old stage/cambridge refs with new ones new_line = re.sub( r'`[ABCD]级` / `(?:L[12])(?:第)?[\d-]+阶段` / `(?:Starters|Movers|Flyers)[\s\d-]*`', lambda m: m.group(0).replace( m.group(0), re.sub( r'`([ABCD]级)` / `.*?` / `.*?`', lambda g: { 'A级': '`A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础`', 'B级': '`B级` / `U13-U24(阶段3-4)` / `Flyers达标→KET入门`', 'C级': '`C级` / `U25-U36(阶段5-6)` / `KET基础→强化`', 'D级': '`D级` / `U37-U48(阶段7-8)` / `KET高位→达标`', }[g.group(1)], m.group(0) ) ), full_line ) # Also update the note column new_line = new_line.replace('难度等级或对应阶段', '难度等级(L2用Unit范围)') return new_line content = re.sub( r'\| 难度等级 \| ✅ \|.*?\|.*?\|', replace_config, content ) if content != original: with open(filepath, 'w', encoding='utf-8') as f: f.write(content) return True return False def update_common(filepath): """Update L1&L2共用题型: add L2 dimension after L1 table""" with open(filepath, 'r', encoding='utf-8') as f: content = f.read() original = content # Check if already has L2 dimension if 'L2 维度' in content or 'L2阶段(Unit范围)' in content: return False # Find end of L1 difficulty table (## 难度对应关系 ... through last D级 row) pattern = r'(## 难度对应关系.*?\n(?:\| .*?\n)*)' def add_l2_dimension(match): table_block = match.group(1) l2_note = ( "\n> **L2 难度维度(对标 Flyers/KET,每2阶段为1个难度等级):**\n" "> - A级 = U1-U12(阶段1-2):Flyers入门→Flyers基础\n" "> - B级 = U13-U24(阶段3-4):Flyers达标→KET入门\n" "> - C级 = U25-U36(阶段5-6):KET基础→KET强化\n" "> - D级 = U37-U48(阶段7-8):KET高位→KET达标\n" ) return table_block + l2_note content = re.sub(pattern, add_l2_dimension, content, flags=re.DOTALL) # Also update config row if it references Starters/Movers content = re.sub( r'\| 难度等级 \| ✅ \|.*?\| 难度等级或对应阶段 \|', lambda m: m.group(0).replace('难度等级或对应阶段', '难度等级(L1用Starters/Movers,L2用Unit范围)'), content ) if content != original: with open(filepath, 'w', encoding='utf-8') as f: f.write(content) return True return False def main(): L2_FILES = [ "listening/L2/listening_short_conversation/SKILL.md", "listening/L2/listening_long_conversation/SKILL.md", "listening/L2/listening_form_fill/SKILL.md", "listening/L2/listening-choicePic/SKILL.md", "listening/L2/listening_info_match/SKILL.md", "reading/reading_info_match/SKILL.md", "reading/reading_paragraph_match/SKILL.md", "reading/reading_long_passage/SKILL.md", "reading/reading_cloze/SKILL.md", "reading/reading_open_fill/SKILL.md", "writing/writing_email_reply/SKILL.md", "writing/writing_picture_writing/SKILL.md", "speaking/L2/speaking_topic_discussion/SKILL.md", ] COMMON_FILES = [ "reading/common/reading_pic_qa/SKILL.md", "reading/common/reading_pic_judge/SKILL.md", "speaking/common/speaking_daily_qa/SKILL.md", "writing/common/writing_pic_qa/SKILL.md", ] updated = 0 print("=== L2 专属题型 ===") for rel_path in L2_FILES: full = os.path.join(BASE, rel_path) if not os.path.exists(full): print(f" [MISS] {rel_path}") continue if update_l2_exclusive(full): print(f" [OK] {rel_path}") updated += 1 else: print(f" [NO CHANGE] {rel_path}") print("\n=== L1&L2 共用题型 ===") for rel_path in COMMON_FILES: full = os.path.join(BASE, rel_path) if not os.path.exists(full): print(f" [MISS] {rel_path}") continue if update_common(full): print(f" [OK] {rel_path}") updated += 1 else: print(f" [NO CHANGE] {rel_path}") print(f"\n=== Done: {updated} files updated ===") if __name__ == '__main__': main()