Files
MathApp/content/tests_lesson_texts.py
Zareian 0917ab487f diagrams for every grade, sectioned prompt, repair of real model slips; fill lesson texts and thin islands
Diagrams: 14 kinds drawn by the app (plane with constructed points, graphs with
computed roots/extrema/tangents, number lines with intervals, solids, charts,
tables, Venn, mapping, trees, fractions, grids, groups, clock). The catalog is
split by topic and only the matching sections go next to a question (200-1073
tokens instead of 1854). parse() repairs slips seen on 96 real book problems:
Persian digits in JSON numbers, 2*pi, // comments, bare items, Venn with
Persian set names. Measured: 45/48 valid on the second round, 0/8 on plain
arithmetic; visual review of 83: 64 right, 3 weak, 16 wrong.

Content: 82 new questions so no island has fewer than four; fill_lesson_texts
writes Unit.blurb (58) and Lesson.example/steps (590) from verified book
problems, never the lesson's own quiz problem. seed_content only fills empty
fields, so admin edits survive a deploy. Tedi unlocks at 500 xp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 23:22:13 +03:30

94 lines
5.7 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.
"""متن‌های درس و فصل: پر شدن از کتاب، و اینکه دیپلوی ویرایشِ ادمین را بازنویسی نکند."""
import json
from django.core.management import call_command
from django.test import TestCase
from content.curriculum_io import export_curriculum, import_curriculum, parse
from content.management.commands.fill_lesson_texts import related, split_steps, unit_blurb
from content.models import Choice, Grade, Lesson, Node, Question, Unit
from rag.models import LessonChunk
def _curriculum(blurb='', example='', steps=None):
return json.dumps({'grades': [{'title': 'پایه آزمون', 'units': [{
'title': 'فصل ۱', 'blurb': blurb, 'nodes': [{'title': 'جزیره', 'lessons': [
{'title': 'درس', 'introText': 'توضیح', 'example': example, 'steps': steps or []},
]}],
}]}]}).encode()
class SeedKeepsAdminEditsTests(TestCase):
def test_empty_fields_are_filled_from_the_file(self):
import_curriculum(parse(_curriculum()))
import_curriculum(parse(_curriculum('در این فصل: جزیره.', 'مثال', ['قدم ۱'])))
lesson = Lesson.objects.get(title='درس')
self.assertEqual((lesson.example, lesson.steps), ('مثال', ['قدم ۱']))
self.assertEqual(Unit.objects.get(title='فصل ۱').blurb, 'در این فصل: جزیره.')
def test_text_written_in_admin_survives_the_next_deploy(self):
import_curriculum(parse(_curriculum()))
Lesson.objects.filter(title='درس').update(example='نوشته‌ی ادمین', steps=['قدمِ ادمین'])
Unit.objects.filter(title='فصل ۱').update(blurb='توضیحِ ادمین')
import_curriculum(parse(_curriculum('از فایل', 'از فایل', ['از فایل'])))
lesson = Lesson.objects.get(title='درس')
self.assertEqual((lesson.example, lesson.steps), ('نوشته‌ی ادمین', ['قدمِ ادمین']))
self.assertEqual(Unit.objects.get(title='فصل ۱').blurb, 'توضیحِ ادمین')
def test_export_round_trips(self):
import_curriculum(parse(_curriculum('توضیح', 'مثال', ['الف', 'ب'])))
out = export_curriculum(Grade.objects.filter(title='پایه آزمون'))
unit = out['grades'][0]['units'][0]
self.assertEqual(unit['blurb'], 'توضیح')
self.assertEqual(unit['nodes'][0]['lessons'][0]['steps'], ['الف', 'ب'])
class HelpersTests(TestCase):
def test_blurb_lists_islands_and_stays_short(self):
grade = Grade.objects.create(title='پایه تست')
unit = Unit.objects.create(grade=grade, title='فصل', order=1)
for i, t in enumerate(['عددهای صحیح', 'عددهای گویا', 'جمع و تفریق']):
Node.objects.create(unit=unit, title=t, order=i)
self.assertEqual(unit_blurb(unit), 'در این فصل: عددهای صحیح، عددهای گویا و جمع و تفریق.')
for i in range(20):
Node.objects.create(unit=unit, title=f'جزیره‌ی بسیار طولانیِ شماره‌ی {i}', order=10 + i)
self.assertLessEqual(len(unit_blurb(unit)), 120)
def test_steps_drop_their_own_numbering(self):
self.assertEqual(split_steps('گام 1: ضرایب\nگام 2: توان‌ها\n3) نتیجه'), ['ضرایب', 'توان‌ها', 'نتیجه'])
def test_relevance_needs_two_real_words(self):
chunk = LessonChunk(problem_type='معادله‌ی خط', problem='خط y=2x+3 را رسم کن و شیب خط را بگو')
self.assertTrue(related('رسم خط با معادله و شیب', chunk))
self.assertFalse(related('محاسبه تعداد مثلث‌های ممکن با شرایط داده شده', chunk))
class FillCommandTests(TestCase):
"""مثالِ درس از دومین مسئله‌ی تأییدشده، هرگز همان مسئله‌ی آزمون."""
def setUp(self):
grade = Grade.objects.create(title='پایه هشتم', number=8)
unit = Unit.objects.create(grade=grade, title='فصل ۱', order=1)
node = Node.objects.create(unit=unit, title='جزیره', order=1)
self.lesson = Lesson.objects.create(node=node, title='جمع کسرها', intro_text='x')
q = Question.objects.create(lesson=self.lesson, type='multiple_choice', prompt='۱/۲ + ۱/۴ = ?')
Choice.objects.create(question=q, value='3/4', is_correct=True)
common = {'collection': 'ریاضی هشتم', 'problem_type': 'جمع کسرها', 'solution_verdict': 'agrees',
'grade_title': 'هشتم', 'lesson_number': 1, 'chapter': 1, 'subject': 'ریاضی'}
LessonChunk.objects.create(order=1, problem='اولی: ۱/۲ + ۱/۴', solution='مخرج مشترک ۴\nجواب ۳/۴', checked_answer='3/4', **common)
LessonChunk.objects.create(order=2, problem='دومی با همان جواب', solution='...\n۳/۴', checked_answer='3 / 4', **common)
LessonChunk.objects.create(order=3, problem='سومی: ۱/۳ + ۱/۶', solution='مخرج مشترک ۶\n۲/۶ + ۱/۶ = ۳/۶', checked_answer='1/2', **common)
def test_example_skips_the_quiz_problem_and_its_answer(self):
call_command('fill_lesson_texts', verbosity=0)
self.lesson.refresh_from_db()
self.assertTrue(self.lesson.example.startswith('سومی'))
self.assertEqual(self.lesson.steps[-1], '✅ جواب: 1/2')
def test_second_run_changes_nothing(self):
call_command('fill_lesson_texts', verbosity=0)
Lesson.objects.filter(pk=self.lesson.pk).update(example='ادمین')
call_command('fill_lesson_texts', verbosity=0)
self.lesson.refresh_from_db()
self.assertEqual(self.lesson.example, 'ادمین')