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>
120 lines
8.1 KiB
Markdown
120 lines
8.1 KiB
Markdown
# ساختن و ویرایشِ محتوا
|
||
|
||
هدفِ این مسیر یک جمله است: **محتوا باید بدونِ توسعهدهنده قابلِ ویرایش باشد.**
|
||
اجرای یک management command روی SSH آخرین راهِ چاره است، نه روالِ کار.
|
||
|
||
## محتوا کجا زندگی میکند
|
||
|
||
`content/seed/` کلِ برنامهی درسی و بانکِ سوال را بهصورتِ JSONِ کامیتشده نگه
|
||
میدارد:
|
||
|
||
| فایل | محتوا |
|
||
|---|---|
|
||
| `curriculum.json` | ۸ پایه، ۵۸ فصل، ۲۶۳ نود، ۲٬۶۱۷ درس |
|
||
| `questions.json` | ۲٬۴۶۳ سوالِ چندگزینهای |
|
||
|
||
`deploy.sh` در هر انتشار `seed_content` را اجرا میکند، پس یک سرورِ تازه خودش
|
||
را با یک فرمان پر میکند — **بدونِ Qdrant، بدونِ اعتبارِ API، بدونِ نشستِ SSH.**
|
||
|
||
> **کد و محتوا دو مسیرِ جدا دارند.** `db.sqlite3` در gitignore است. سوالی که
|
||
> فقط در دیتابیسِ محلیِ شماست به سرور نمیرسد مگر از راهِ
|
||
> `seed_content --export --only questions` و کامیتِ `content/seed/questions.json`.
|
||
> پوش کردنِ کد بهتنهایی محتوا را نمیبرد.
|
||
|
||
دو رفتارِ `seed_content` که دانستنشان لازم است:
|
||
|
||
- **idempotent است و هرگز چیزی پاک نمیکند.** هر چه در فایل هست ساخته یا
|
||
بهروز میشود؛ هر چه نیست دستنخورده میماند. پس یک فایلِ ناقص محتوای سرور را
|
||
از بین نمیبرد.
|
||
- **سوالها با مسیرِ عنوان به درس وصل میشوند** (پایه › فصل › نود › درس)، نه با
|
||
کلیدِ اصلی. شناسهها بینِ هر دو دیتابیس فرق دارند و اتصال با id، سوالها را
|
||
بیصدا به درسِ اشتباه میچسباند.
|
||
|
||
`seed_content --export` فایلها را از دیتابیسِ محلی بازنویسی میکند. اگر
|
||
دیتابیس سوالی نداشته باشد از بازنویسیِ یک `questions.json` غیرخالی خودداری
|
||
میکند (این قبلاً فایل را بیصدا پاک میکرد)؛ وقتی فقط برنامهی درسی را
|
||
میخواهید `--export --only curriculum` بزنید.
|
||
|
||
## از پنلِ ادمین (روالِ عادی)
|
||
|
||
| کار | کجا |
|
||
|---|---|
|
||
| افزودن یا ویرایشِ **یک** سوال | `/admin/content/question/` → «افزودن» (انتخابِ درس با جستجو) |
|
||
| وارد کردنِ **فلهای** سوال | همان صفحه → «وارد کردنِ سوالها از فایل» |
|
||
| گرفتنِ خروجیِ سوالها | چند سوال را انتخاب کنید → اکشنِ «دریافتِ فایل» |
|
||
| وارد کردنِ **برنامهی درسی** | `/admin/content/grade/` → «وارد کردنِ برنامهی درسی از فایل» |
|
||
| گرفتنِ خروجیِ برنامهی درسی | پایهها را انتخاب کنید → اکشنِ «دریافتِ فایلِ برنامهی درسی» |
|
||
| افزودنِ یک **پایهی تازه** | `/admin/content/grade/add/` — عنوان به شکلِ «پایه چهارم»؛ شماره و id خودکار ۴ میشوند |
|
||
|
||
خروجیِ هر کدام **دقیقاً** همان قالبیست که ورودیاش میپذیرد، پس چرخهی
|
||
«بگیر → ویرایش کن → برگردان» بسته است و برای دیدنِ یک نمونه لازم نیست کسی
|
||
مستندات بخواند.
|
||
|
||
دو تضمین:
|
||
|
||
- **سوالها:** فایلِ نامعتبر هیچ چیزی نمینویسد — یا همه وارد میشوند یا هیچکدام.
|
||
- **برنامهی درسی:** چیزی پاک نمیشود، به همان دلیلِ بالا.
|
||
|
||
## ساختِ انبوهِ سوال از یک کتابِ تازه
|
||
|
||
وقتی کتابی تازه روی Qdrant آمده، مسیرِ رایگان این است:
|
||
|
||
```bash
|
||
# اسکلتِ برنامهی درسی از فهرستِ خودِ کتاب (بدونِ هزینه)
|
||
python manage.py seed_curriculum_from_textbook --subject ریاضی --grade نهم --dry-run
|
||
python manage.py seed_curriculum_from_textbook --subject ریاضی --grade نهم
|
||
|
||
# جفتهای (درس، مسئلهی تأییدشده) را برای سوالنویسی بیرون بده (بدونِ هزینه)
|
||
python manage.py generate_questions_from_chunks --subject ریاضی --grade نهم \
|
||
--export /tmp/batches
|
||
```
|
||
|
||
`--dry-run` روی هر دو کار میکند و پیش از هر نوشتنی نشان میدهد چه ساخته خواهد
|
||
شد.
|
||
|
||
خروجی چند فایلِ JSON است بههمراهِ یک `*-RULES.txt` که **عینِ همان پرامپتی** را
|
||
دارد که به مدلِ پولی میرفت — تا کیفیتِ دو مسیر از هم جدا نیفتد. سوالها را با
|
||
آن قواعد بنویسید و از «وارد کردنِ فلهای سوال» در جدولِ بالا واردشان کنید.
|
||
|
||
همان فرمان بدونِ `--export` کار را به مدلِ پولی میسپارد. **این کار را نکنید
|
||
مگر دلیلِ خاصی داشته باشید**: اندازهگیریشده، گرانتر است و کیفیتِ بهتری
|
||
نمیدهد. جزئیات و هزینههای واقعی در [`extraction-playbook.md`](extraction-playbook.md).
|
||
|
||
## کیفیتِ محتوا
|
||
|
||
کتابها با یک مدلِ **ویژن از تصویرِ صفحه** خوانده میشوند، پس خطای استخراج از
|
||
همانجا وارد میشود: رقمِ بدخواندهشده، جداکنندهی اعشارِ افتاده، راهحلی که به
|
||
مسئلهی بغلی چسبیده. اینها در بازیابی دیده نمیشوند و فقط وقتی لو میروند که
|
||
چیزی به متن اعتماد کند.
|
||
|
||
`rag_verify_solutions` هر مسئلهی استخراجشده را مستقلاً دوباره حل میکند و
|
||
نتیجه را مقایسه میکند. `LessonChunk.solution_verdict` سهحالته است —
|
||
`agrees` / `disagrees` / `unjudgeable`. **ساختِ سوال فقط از chunkهای `agrees`
|
||
میخواند.**
|
||
|
||
دو درسی که گران تمام شدند و حالا در پرامپتها کدگذاری شدهاند:
|
||
|
||
- **رأیِ دوحالته غلط است.** با دو حالت، هر «نمیتوانم قضاوت کنم» تبدیل به
|
||
«غلط» میشود و نرخِ خطا را حدودِ سه برابرِ واقعیت گزارش میکند. پیش از
|
||
اعتماد به هر اجرای انبوه، چند نمونه را با چشم بخوانید.
|
||
- `۳/۱۰۴` در این کتابها عددِ اعشاریِ ۳٫۱۰۴ است، نه کسرِ ۳ تقسیم بر ۱۰۴.
|
||
|
||
خودِ `solution` هرگز بازنویسی نمیشود — متنِ استخراجشده بهعنوانِ سند میماند و
|
||
پاسخِ مستقل جداگانه در `checked_answer` ذخیره میشود.
|
||
|
||
`solution_verdict='agrees'` تضمینِ کامل نیست، فقط فیلترِ اول: چند بار خطای
|
||
حسابِ خودِ کتاب در همین مرحله گرفته شده است.
|
||
|
||
## دادهی ساختگی برای توسعه
|
||
|
||
`seed_test_data` یک دیتاستِ کوچک و idempotent میسازد که همهی اپها را پوشش
|
||
میدهد. **هرگز روی سروری که کاربرِ واقعی دارد اجرا نشود** — ردیفهایی با
|
||
پیشوندِ `[SEED]`/`[DEMO]` میسازد که در API دیده میشوند. محتوای واقعی از
|
||
مسیرِ بالا میرود، نه از این فرمان.
|
||
|
||
```bash
|
||
python manage.py seed_test_data # فقط با DEBUG=True اجرا میشود
|
||
python manage.py seed_test_data --force # staging، با DEBUG=False
|
||
python manage.py seed_test_data --reset # پاک کردنِ دادهی seedِ قبلی و ساختِ دوباره
|
||
```
|