194 lines
7.2 KiB
Python
194 lines
7.2 KiB
Python
#!/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()
|