put the whole content pipeline behind the admin panel

سوال‌ها از قبل فله‌ای وارد می‌شدند ولی دو شکاف مانده بود: افزودنِ تکی عملاً
غیرقابلِ استفاده بود، و خودِ برنامه‌ی درسی هیچ راهی جز ترمینالِ سرور نداشت.

افزودنِ تکیِ سوال: انتخابِ درس یک دراپ‌داونِ ۱۳۰۷تایی بود. حالا autocomplete
است و بر اساسِ عنوانِ درس، فصل و پایه جستجو می‌شود.

برنامه‌ی درسی (Grade → Unit → Node → Lesson) حالا از /admin/content/grade/
وارد و خارج می‌شود. تا امروز رساندنش به سرور یعنی اجرای rag_import_collection
و بعد seed_curriculum_from_textbook روی همان سرور — یعنی وابستگی به Qdrant و
به آدمی با دسترسیِ SSH. حالا یک‌بار لوکال ساخته می‌شود و فایلش بالا می‌رود.

هم‌سان‌سازی با عنوان است نه با id: idها بینِ دو دیتابیس یکی نیستند و تکیه بر
آن‌ها یعنی وارد کردن روی سرور محتوای بی‌ربط را بازنویسی می‌کند.

وارد کردنِ برنامه‌ی درسی عمداً چیزی پاک نمی‌کند و idempotent است — یک فایلِ
ناقص نباید محتوای سرور را از بین ببرد، و اجرای دوباره نباید چیزی را دو بار
بسازد. (وارد کردنِ سوال‌ها اما replace دارد، چون جایگزینیِ سوال‌های یک درس
کارِ رایجی است.)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-13 20:46:50 +03:30
parent eaa4b761dd
commit 9cee574fa9
6 changed files with 409 additions and 6 deletions

View File

@@ -0,0 +1,7 @@
{% extends "admin/change_list.html" %}
{% block object-tools-items %}
<li>
<a href="{% url 'admin:content_grade_import_curriculum' %}" class="addlink">وارد کردنِ برنامه‌ی درسی از فایل</a>
</li>
{{ block.super }}
{% endblock %}

View File

@@ -0,0 +1,45 @@
{% extends "admin/base_site.html" %}
{% block content %}
<div dir="rtl" style="max-width:760px">
<h1>وارد کردنِ برنامه‌ی درسی</h1>
<p>کلِ ساختارِ پایه → فصل → درس → نوعِ مسئله را از یک فایلِ JSON می‌سازد.
برای گرفتنِ نمونه، از فهرستِ پایه‌ها یکی را انتخاب کنید و اکشنِ
«دریافتِ فایلِ برنامه‌ی درسی» را بزنید — خروجی دقیقاً همین قالب است.</p>
<pre style="background:#f6f6f6;padding:12px;border-radius:6px;direction:ltr;text-align:left">{
"grades": [
{
"title": "پایه نهم",
"icon": "grade-9",
"color": "#FF9800",
"units": [
{
"title": "فصل ۴: توان و ریشه",
"order": 4,
"nodes": [
{
"title": "توان صحیح",
"order": 1,
"lessons": [
{ "title": "ضرب توان‌ها", "introText": "..." }
]
}
]
}
]
}
]
}</pre>
<p><b>چیزی پاک نمی‌شود.</b> هر چه در فایل هست ساخته یا به‌روز می‌شود؛ هر چه
در فایل نیست دست‌نخورده می‌ماند. اجرای دوباره‌ی همان فایل هم چیزی را دو بار
نمی‌سازد.</p>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<p><input type="file" name="file" accept=".json,application/json" required></p>
<p><input type="submit" value="وارد کن" class="default"></p>
</form>
</div>
{% endblock %}