""" اصلاحِ سوال‌های شکل‌دارِ از قبل منتشرشده (seed هیچ‌وقت حذف یا بازنویسی نمی‌کند، پس اصلاح یک ابزارِ جدا می‌خواهد). python tools/figures/apply_fixups.py fixups.json # فقط فایل‌های seed محلی venv/bin/python tools/figures/apply_fixups.py --db fixups.json # دیتابیسِ همین ماشین (روی سرور، پس از pg_dump) fixups.json: {"delete": [[path, prompt], ...], "blank_title": [[path, prompt], ...]} delete سوال را کامل پاک می‌کند (پاسخ‌های ثبت‌شده‌اش هم می‌رود؛ فقط برای سوالِ تازه‌منتشرشده) blank_title عنوانِ زیرِ شکل را خالی می‌کند (عنوان گاهی جوابِ سوال را لو می‌داد) سوال با (path, prompt) شناخته می‌شود؛ هر کدام پیدا نشود فهرست می‌شود، بی‌صدا رد نمی‌شود. """ import json import os import sys ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(0, ROOT) def key(path, prompt): return (tuple(path), prompt.strip()) def apply_seed(fx): p = os.path.join(ROOT, 'content', 'seed', 'questions.json') data = json.load(open(p)) dele = {key(*x) for x in fx.get('delete', [])} blank = {key(*x) for x in fx.get('blank_title', [])} seen_d, seen_b, out = set(), set(), [] for q in data['questions']: k = key(q['path'], q['prompt']) if k in dele: seen_d.add(k) continue if k in blank and q.get('figure'): q['figure']['title'] = '' seen_b.add(k) out.append(q) data['questions'] = out json.dump(data, open(p, 'w'), ensure_ascii=False, indent=1) return dele - seen_d, blank - seen_b def apply_db(fx): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') import django django.setup() from content.models import Question lessons = {} from content.models import Lesson for l in Lesson.objects.select_related('node__unit__grade'): lessons[(l.node.unit.grade.title, l.node.unit.title, l.node.title, l.title)] = l.pk miss_d, miss_b = set(), set() for path, prompt in fx.get('delete', []): lid = lessons.get(tuple(path)) qs = Question.objects.filter(lesson_id=lid, prompt=prompt.strip()) if lid else Question.objects.none() if not qs.exists(): miss_d.add(key(path, prompt)) else: qs.delete() for path, prompt in fx.get('blank_title', []): lid = lessons.get(tuple(path)) qs = list(Question.objects.filter(lesson_id=lid, prompt=prompt.strip())) if lid else [] if not qs: miss_b.add(key(path, prompt)) for q in qs: if q.figure: q.figure = {**q.figure, 'title': ''} q.save(update_fields=['figure']) return miss_d, miss_b if __name__ == '__main__': args = [a for a in sys.argv[1:] if a != '--db'] fx = json.load(open(args[0])) miss_d, miss_b = (apply_db if '--db' in sys.argv else apply_seed)(fx) print(f'deleted/blanked ok; not found: delete={len(miss_d)} blank_title={len(miss_b)}') for k in list(miss_d)[:5] + list(miss_b)[:5]: print(' missing:', k[1][:60])