make every grade's id its class number and sort grades by it everywhere
Grade ids were the order the books were imported in (ninth=1, eighth=5), so gradeId: 5 meant eighth grade and the admin, the question filter and the API all listed grades out of order. Grade.number (1-12) is now read from the title, new grades take it as their id, and migration 0008 moves existing ids plus every reference to them: Unit, the loose grade_id in accounts/ai_tutor/rag, and the grade target inside campaign JSON. Unnumbered demo grades move above 100 and the id sequence is raised past 12 so they never collide with a real grade. The frontend needs no change: it always reads gradeId from the server. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
82
content/tests_grade_number.py
Normal file
82
content/tests_grade_number.py
Normal file
@@ -0,0 +1,82 @@
|
||||
import importlib
|
||||
from types import SimpleNamespace
|
||||
|
||||
from django.apps import apps
|
||||
from django.db import connection
|
||||
from django.test import TestCase
|
||||
|
||||
from accounts.models import User
|
||||
from ai_tutor.models import Conversation
|
||||
from campaigns.models import MessageCampaign
|
||||
from content.models import Grade, Unit, grade_number_from_title
|
||||
|
||||
migration = importlib.import_module('content.migrations.0008_grade_id_equals_number')
|
||||
|
||||
|
||||
class GradeNumberTests(TestCase):
|
||||
def test_number_is_read_from_title(self):
|
||||
self.assertEqual(grade_number_from_title('پایه هشتم'), 8)
|
||||
self.assertEqual(grade_number_from_title('پایه دوازدهم'), 12)
|
||||
self.assertEqual(grade_number_from_title('پایهٔ یازدهم'), 11)
|
||||
self.assertIsNone(grade_number_from_title('[SEED] پایه سوم'))
|
||||
self.assertIsNone(grade_number_from_title('ریاضی'))
|
||||
|
||||
def test_new_grade_takes_its_number_as_id(self):
|
||||
# بدونِ این، «پایه چهارم»ی که بعداً اضافه شود id=13 میگرفت.
|
||||
grade = Grade.objects.create(title='پایه چهارم', icon='g', color='#000')
|
||||
self.assertEqual((grade.number, grade.pk), (4, 4))
|
||||
|
||||
def test_unnumbered_grade_stays_out_of_one_to_twelve(self):
|
||||
grade = Grade.objects.create(title='[DEMO] نمونه', icon='g', color='#000')
|
||||
self.assertIsNone(grade.number)
|
||||
self.assertGreater(grade.pk, 12)
|
||||
# و بعدش هنوز جا برای پایهی واقعی هست
|
||||
self.assertEqual(Grade.objects.create(title='پایه نهم', icon='g', color='#000').pk, 9)
|
||||
|
||||
def test_duplicate_title_does_not_break_save(self):
|
||||
Grade.objects.create(title='پایه دهم', icon='g', color='#000')
|
||||
second = Grade.objects.create(title='پایه دهم', icon='g', color='#000')
|
||||
self.assertIsNone(second.number)
|
||||
|
||||
def test_grades_api_is_ordered_by_number(self):
|
||||
for title in ('پایه نهم', 'پایه سوم', 'پایه دوازدهم', 'پایه هشتم'):
|
||||
Grade.objects.create(title=title, icon='g', color='#000')
|
||||
rows = self.client.get('/api/grades').json()['grades']
|
||||
self.assertEqual([r['number'] for r in rows], [3, 8, 9, 12])
|
||||
self.assertEqual([r['id'] for r in rows], [3, 8, 9, 12])
|
||||
|
||||
|
||||
class GradeRenumberMigrationTests(TestCase):
|
||||
"""همان وضعیتِ سرور پیش از migration: idها به ترتیبِ واردشدنِ کتابها."""
|
||||
|
||||
def _grade(self, pk, title):
|
||||
# save() را دور میزنیم تا idِ قدیمی و بیشماره بسازیم، مثلِ دیتای واقعی
|
||||
Grade.objects.bulk_create([Grade(pk=pk, title=title, icon='g', color='#000')])
|
||||
return pk
|
||||
|
||||
def test_ids_become_numbers_and_every_reference_follows(self):
|
||||
old = { # id قدیمی → عنوان (ترتیبِ سرورِ واقعی)
|
||||
1: 'پایه نهم', 2: 'پایه ششم', 5: 'پایه هشتم', 8: 'پایه دوازدهم', 9: '[DEMO] نمونه',
|
||||
}
|
||||
for pk, title in old.items():
|
||||
self._grade(pk, title)
|
||||
Unit.objects.create(grade_id=pk, title=f'فصلِ {title}')
|
||||
student = User.objects.create(grade_id=5) # هشتم
|
||||
conversation = Conversation.objects.create(user_id=student.id, grade_id=8) # دوازدهم
|
||||
campaign = MessageCampaign.objects.create(
|
||||
title='t', body='b', channel='sms', target_filter={'type': 'grade', 'grade_id': 1}) # نهم
|
||||
|
||||
migration.forwards(apps, SimpleNamespace(connection=connection))
|
||||
|
||||
by_title = {g.title: g for g in Grade.objects.all()}
|
||||
self.assertEqual({t: (g.pk, g.number) for t, g in by_title.items() if g.number}, {
|
||||
'پایه نهم': (9, 9), 'پایه ششم': (6, 6), 'پایه هشتم': (8, 8), 'پایه دوازدهم': (12, 12),
|
||||
})
|
||||
self.assertGreater(by_title['[DEMO] نمونه'].pk, 12)
|
||||
for grade in by_title.values():
|
||||
self.assertEqual(list(grade.units.values_list('title', flat=True)), [f'فصلِ {grade.title}'])
|
||||
student.refresh_from_db()
|
||||
conversation.refresh_from_db()
|
||||
campaign.refresh_from_db()
|
||||
self.assertEqual((student.grade_id, conversation.grade_id), (8, 12))
|
||||
self.assertEqual(campaign.target_filter, {'type': 'grade', 'grade_id': 9})
|
||||
Reference in New Issue
Block a user