- content.Subject + Unit.subject, /api/home?subject=, tutor grounded in the chosen book - 5 new books ingested; ~1150 hand-written questions, islands named by hand - node_split: islands over 7 lessons split into parts of <=6 (lesson ids and progress kept) - Lesson.figures hand-authored and visually checked for 653 lessons of the new books; gen_lesson_figures got --subject and survives network errors - seed files re-exported Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
94 lines
5.7 KiB
Python
94 lines
5.7 KiB
Python
"""متنهای درس و فصل: پر شدن از کتاب، و اینکه دیپلوی ویرایشِ ادمین را بازنویسی نکند."""
|
||
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': [{
|
||
'subject': 'ریاضی', '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, 'ادمین')
|