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:
2026-09-23 22:47:02 +03:30
parent d70737dddf
commit 0c6b08c9ff
13 changed files with 292 additions and 12 deletions

View File

@@ -1,6 +1,6 @@
from django.core.cache import cache
from django.db import IntegrityError, transaction
from django.db.models import Count
from django.db.models import Count, F
from django.http import Http404
import random
@@ -87,10 +87,10 @@ def _grades_payload() -> dict:
# هنوز خالی است و کاربر صفحه‌ی خالی می‌بیند.
grades = Grade.objects.annotate(
_lessons=Count('units__nodes__lessons', distinct=True),
).order_by('id')
).order_by(F('number').asc(nulls_last=True), 'id')
return {'grades': [
{'id': g.id, 'title': g.title, 'icon': g.icon, 'color': g.color,
'lessonCount': g._lessons}
{'id': g.id, 'number': g.number, 'title': g.title, 'icon': g.icon,
'color': g.color, 'lessonCount': g._lessons}
for g in grades
]}