Subjects per grade (math, calculus, geometry, stats, discrete), 5 new textbooks, 202 new islands, long islands split, hand-written figures and lesson texts for the new books

- 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>
This commit is contained in:
2026-10-12 05:35:18 +03:30
parent 3d51b3b83a
commit 8cb1ecb85e
32 changed files with 81218 additions and 4486 deletions

View File

@@ -24,6 +24,7 @@ import threading
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
import openai
from django.core.management.base import BaseCommand, CommandError
from content.models import Lesson
@@ -109,6 +110,7 @@ class Command(BaseCommand):
def add_arguments(self, p):
p.add_argument('--grade', type=int, help='بدونِ آن همه‌ی پایه‌ها')
p.add_argument('--subject', help='عنوانِ کتاب (مثلاً هندسه)؛ بدونِ آن همه‌ی کتاب‌ها')
p.add_argument('--limit', type=int, default=4)
p.add_argument('--lesson', type=int, help='فقط همین شناسه')
p.add_argument('--redo', action='store_true', help='درس‌هایی که شکل دارند هم')
@@ -128,6 +130,8 @@ class Command(BaseCommand):
'node__unit__grade__number', 'node__unit__order', 'node__order', 'id')
if o['grade']:
qs = qs.filter(node__unit__grade__number=o['grade'])
if o['subject']:
qs = qs.filter(node__unit__subject__title=o['subject'])
if o['lesson']:
qs = qs.filter(pk=o['lesson'])
if not o['redo']:
@@ -179,7 +183,8 @@ class Command(BaseCommand):
return
try:
items = _json_list(ask(PROMPT + spec, text))
except (ValueError, KeyError) as e:
# خطای شبکه/درگاه یک درس را رد می‌کند، نه کلِ اجرا را؛ در ledger ثبت نمی‌شود تا دفعه‌ی بعد دوباره برود.
except (ValueError, KeyError, openai.OpenAIError) as e:
with lock:
stats['invalid'] += 1
self.stderr.write(f'درس {lesson.id}: خروجیِ نامعتبر ({e}); ذخیره نشد.')
@@ -218,7 +223,7 @@ class Command(BaseCommand):
ok = {v.get('i'): v for v in verdicts if isinstance(v, dict)}
kept = [f for i, f in enumerate(figures) if ok.get(i, {}).get('ok') is True]
notes = [f"{i}: {v.get('why', '')}" for i, v in ok.items() if v.get('ok') is not True]
except (ValueError, KeyError, AttributeError) as e:
except (ValueError, KeyError, AttributeError, openai.OpenAIError) as e:
kept, notes = [], [f'وارسی ناموفق: {e}']
with lock:
stats['rejected'] += len(figures) - len(kept)