Files
MathApp/tools/figures/batch.py

130 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
ابزارِ سوالِ شکل‌دار: `export` درس‌های یک فصل را برای نویسنده بیرون می‌دهد، `check` خروجیِ او را
سخت‌گیرانه می‌سنجد (validate بی‌صدا آیتمِ خراب را حذف می‌کند؛ اینجا حذف یعنی رد).
python tools/figures/batch.py spec # کاتالوگِ کاملِ نوع‌های شکل
python tools/figures/batch.py export "پایه دهم" "فصل ۱" هندسه [--out f.json]
python tools/figures/batch.py check batch.json # بدونِ نوشتن در seed
python tools/figures/batch.py merge batch.json # افزودن به content/seed/questions.json
آزموده نشده: درستیِ جوابِ سوال‌ها را این ابزار نمی‌سنجد؛ آن را re-solveِ مستقل می‌سنجد.
"""
import json
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
import django # noqa: E402
django.setup()
from rag.services import diagram # noqa: E402
SEED = os.path.join(os.path.dirname(__file__), '..', '..', 'content', 'seed')
def _curriculum():
return json.load(open(os.path.join(SEED, 'curriculum.json')))['grades']
def _questions():
return json.load(open(os.path.join(SEED, 'questions.json')))['questions']
def export(grade, unit_prefix, subject=None):
qs = _questions()
rows = []
for g in _curriculum():
if g['title'] != grade:
continue
for u in g['units']:
if not u['title'].startswith(unit_prefix + ':') or (subject and u['subject'] != subject):
continue
for n in u['nodes']:
for l in n['lessons']:
path = [g['title'], u['title'], n['title'], l['title']]
have = [q for q in qs if q['path'] == path]
rows.append({
'path': path, 'subject': u['subject'], 'intro': l.get('introText', '')[:400],
'figuredAlready': sum(1 for q in have if q.get('figure')),
'existingPrompts': [q['prompt'][:110] for q in have[:3]],
})
return rows
def _strict(row, i):
errs = []
path = row.get('path')
if not (isinstance(path, list) and len(path) == 4):
errs.append('path باید چهار عنوان باشد')
ch = row.get('choices')
if not (isinstance(ch, list) and len(ch) == 4 and len(set(map(str, ch))) == 4):
errs.append('choices باید چهار گزینه‌ی متفاوت باشد')
if row.get('correct') not in (0, 1, 2, 3):
errs.append('correct باید ۰ تا ۳ باشد')
if row.get('type') != 'multiple_choice':
errs.append('type باید multiple_choice باشد')
fig = row.get('figure')
if not fig:
errs.append('figure خالی است')
else:
clean = diagram.validate(fig)
if clean is None:
errs.append('figure نامعتبر')
elif json.dumps(clean, sort_keys=True) != json.dumps(diagram.validate(clean), sort_keys=True):
errs.append('figure پایدار نیست')
else:
n_in = len(fig.get('items') or []) + len(fig.get('points') or {})
n_out = len(clean.get('items') or []) + len(clean.get('points') or {})
if n_out < n_in:
errs.append(f'validate {n_in - n_out} آیتم/نقطه را حذف کرد')
p = row.get('prompt') or ''
if any(t in p for t in ('$', '\\frac', '\\(', '**')):
errs.append('LaTeX/markdown در prompt')
return [f'ردیف {i}: {e}' for e in errs]
def check(path):
rows = json.load(open(path))
rows = rows['questions'] if isinstance(rows, dict) else rows
curr = {tuple([g['title'], u['title'], n['title'], l['title']])
for g in _curriculum() for u in g['units'] for n in u['nodes'] for l in n['lessons']}
errs = []
for i, r in enumerate(rows):
errs += _strict(r, i)
if tuple(r.get('path') or ()) not in curr:
errs.append(f'ردیف {i}: درس در برنامه‌ی درسی نیست')
print(f'{len(rows)} سوال، {len(errs)} خطا')
print('\n'.join(errs[:40]))
return not errs
def merge(path):
if not check(path):
sys.exit('خطا دارد؛ ادغام نشد')
rows = json.load(open(path))
rows = rows['questions'] if isinstance(rows, dict) else rows
p = os.path.join(SEED, 'questions.json')
data = json.load(open(p))
have = {(tuple(q['path']), q['prompt'].strip()) for q in data['questions']}
new = [r for r in rows if (tuple(r['path']), r['prompt'].strip()) not in have]
data['questions'] += new
json.dump(data, open(p, 'w'), ensure_ascii=False, indent=1)
print(f'{len(new)} سوال افزوده شد')
if __name__ == '__main__':
cmd = sys.argv[1]
if cmd == 'spec':
print(diagram.DIAGRAM_SPEC)
elif cmd == 'export':
out = json.dumps(export(sys.argv[2], sys.argv[3], sys.argv[4] if len(sys.argv) > 4 and not sys.argv[4].startswith('--') else None), ensure_ascii=False, indent=1)
if '--out' in sys.argv:
open(sys.argv[sys.argv.index('--out') + 1], 'w').write(out)
else:
print(out)
elif cmd == 'check':
sys.exit(0 if check(sys.argv[2]) else 1)
elif cmd == 'merge':
merge(sys.argv[2])