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>
This commit is contained in:
93
content/tests_lesson_texts.py
Normal file
93
content/tests_lesson_texts.py
Normal file
@@ -0,0 +1,93 @@
|
||||
"""متنهای درس و فصل: پر شدن از کتاب، و اینکه دیپلوی ویرایشِ ادمین را بازنویسی نکند."""
|
||||
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, 'ادمین')
|
||||
Reference in New Issue
Block a user