Seed: grade 3 rebuilt (every lesson 5+ questions, wrong figures/lesson text fixed), Persian digits and plain fractions across all grades, apply_fixups gains lesson/question_set, ledger save chunked for SQLite

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-16 21:27:22 +03:30
parent 5fbbd371fe
commit f57deb9c1d
9 changed files with 35779 additions and 11725 deletions

View File

@@ -0,0 +1,61 @@
"""tools/figures/apply_fixups.py --db: اصلاحِ محتوای منتشرشده روی دیتابیس، بدون از دست رفتنِ پاسخ‌های ثبت‌شده."""
import importlib.util
import os
from django.conf import settings
from django.test import TestCase
from content.models import Choice, Grade, Lesson, Node, Question, Unit, UserAnswer
_spec = importlib.util.spec_from_file_location(
'apply_fixups', os.path.join(settings.BASE_DIR, 'tools', 'figures', 'apply_fixups.py'))
apply_fixups = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(apply_fixups)
PATH = ['پایه سوم', 'فصل ۱', 'جزیره', 'درس']
class ApplyFixupsDbTests(TestCase):
def setUp(self):
g = Grade.objects.create(title='پایه سوم', icon='', color='')
u = Unit.objects.create(grade=g, title='فصل ۱', order=1)
n = Node.objects.create(unit=u, title='جزیره', order=1)
self.lesson = Lesson.objects.create(node=n, title='درس', intro_text='متن', example='مثالِ غلط',
steps=['گام'], figures=[{'where': 'example', 'diagram': {}}])
self.q = Question.objects.create(lesson=self.lesson, type='multiple_choice', prompt='(۱)/(۲) کدام است؟')
self.c = [Choice.objects.create(question=self.q, value=v, is_correct=(i == 0))
for i, v in enumerate(['(۱)/(۲)', '۲', '۳', '۴'])]
self.answer = UserAnswer.objects.create(user_id=7, question=self.q, choice=self.c[0], correct=True)
self.dup = Question.objects.create(lesson=self.lesson, type='multiple_choice', prompt='تکراری')
def test_question_set_keeps_rows_and_answers(self):
problems = apply_fixups.apply_db({'question_set': [[PATH, '(۱)/(۲) کدام است؟', {
'prompt': '۱/۲ کدام است؟', 'choices': ['۱/۲', '۲', '۳', '۴'], 'correct': 0}]]})
self.assertEqual(problems, [])
self.q.refresh_from_db()
self.assertEqual(self.q.prompt, '۱/۲ کدام است؟')
self.assertEqual([c.value for c in self.q.choices.order_by('id')], ['۱/۲', '۲', '۳', '۴'])
self.assertEqual([c.pk for c in self.q.choices.order_by('id')], [c.pk for c in self.c])
self.assertTrue(UserAnswer.objects.filter(pk=self.answer.pk, choice=self.c[0]).exists())
def test_choice_count_mismatch_is_reported_not_applied(self):
problems = apply_fixups.apply_db({'question_set': [[PATH, '(۱)/(۲) کدام است؟', {'choices': ['۱', '۲']}]]})
self.assertEqual(len(problems), 1)
self.assertEqual(self.c[0].__class__.objects.get(pk=self.c[0].pk).value, '(۱)/(۲)')
def test_lesson_fields_and_delete(self):
problems = apply_fixups.apply_db({
'lesson': [[PATH, {'example': 'مثالِ درست', 'figures': []}]],
'delete': [[PATH, 'تکراری']]})
self.assertEqual(problems, [])
self.lesson.refresh_from_db()
self.assertEqual((self.lesson.example, self.lesson.figures, self.lesson.steps), ('مثالِ درست', [], ['گام']))
self.assertFalse(Question.objects.filter(pk=self.dup.pk).exists())
def test_missing_targets_are_reported(self):
problems = apply_fixups.apply_db({'delete': [[PATH, 'نیست']], 'lesson': [[['x', 'y', 'z', 'w'], {'steps': []}]]})
self.assertEqual(len(problems), 2)
def test_unknown_field_stops(self):
with self.assertRaises(SystemExit):
apply_fixups.apply_db({'lesson': [[PATH, {'title': 'نه'}]]})