Two bugs made self-ingested content useless, both silently: - _payload wrote `chapter_number` (int) while math retrieval filters on `chapter` (the full "فصل ۲: کسر" string), so anything we ingest could never match a search — no error, just permanently invisible content. - ingest_pdf flattened the vision extraction into one text blob and threw away type/keywords/generalSolution/problem/solution. LessonChunk is the source of truth, so rebuilding meant paying for vision again — and the problem/solution pairs are the input for building lesson questions. LessonChunk now stores those fields and _payload emits them under the same keys the other team's collections use, so ours and theirs stay one shape. Also fixes a regression from the whitenoise commit: the Manifest storage variant raises when staticfiles.json is absent, which broke all 23 admin rendering tests (Django forces DEBUG=False in tests, so USE_FINDERS is off) and would have taken the admin panel down entirely the day DEBUG=0 ships without a collectstatic run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
80 lines
3.9 KiB
Python
80 lines
3.9 KiB
Python
import uuid
|
|
|
|
from django.db import models
|
|
|
|
|
|
class LessonChunk(models.Model):
|
|
"""
|
|
یک تکهی درسیِ خلاصهشده که هم در همین جدول (منبعِ حقیقت) و هم بهصورتِ
|
|
یک نقطه در Qdrant (فقط index، قابلِ بازسازی) ذخیره میشه.
|
|
|
|
برداشتِ پاکسازیشده از subjects.LessonChunk مخزنِ Rag_backend، با این
|
|
تفاوتها طبق معماریِ این پروژه:
|
|
- هیچ FK بیناپی نداره؛ lesson_id فقط یک ارجاعِ سبک به content.Lesson است
|
|
(دقیقاً مثل Conversation.lesson_id در ai_tutor).
|
|
- subject/grade بهصورتِ رشته/عدد نگهداری میشن چون مدلِ content ما مدلِ
|
|
Subject نداره و نامِ collectionِ تیمِ دیگه بر پایهی همین رشتههاست.
|
|
- embedding_vector هم ذخیره میشه تا collectionِ Qdrant بدونِ فراخوانیِ
|
|
دوبارهی embedding قابلِ بازسازی باشه.
|
|
"""
|
|
|
|
lesson_id = models.IntegerField(db_index=True) # ارجاع سبک به content.Lesson
|
|
grade_id = models.IntegerField(null=True, blank=True) # ارجاع سبک به content.Grade
|
|
subject = models.CharField(max_length=100)
|
|
lesson_number = models.IntegerField()
|
|
chapter = models.IntegerField(null=True, blank=True)
|
|
|
|
# عنوانِ کاملِ فصل، دقیقاً همون رشتهای که در payloadِ تیمِ دیگه هست
|
|
# («فصل ۲: کسر»). retrieval برای ریاضی روی همین مقدار فیلتر میکنه، پس
|
|
# بدونِ این هر چیزی که ingest کنیم در جستجو پیدا نمیشه.
|
|
chapter_title = models.CharField(max_length=255, blank=True)
|
|
grade_title = models.CharField(max_length=100, blank=True)
|
|
|
|
order = models.PositiveIntegerField()
|
|
title = models.CharField(max_length=255)
|
|
original_text = models.TextField()
|
|
summary = models.TextField()
|
|
embedding_vector = models.JSONField(null=True, blank=True)
|
|
|
|
# --- ساختارِ استخراجشده از کتاب (همون قراردادِ ریاضیِ تیمِ دیگه) -------
|
|
# vision یکبار و با هزینه اینها رو درمیآره؛ اگه فقط متنِ صافشده رو
|
|
# نگه داریم، برای هر بازسازی باید دوباره همون هزینه رو بدیم. ضمناً
|
|
# problem/solution ورودیِ مستقیمِ ساختِ سؤالهای مسیرِ درساند.
|
|
problem_type = models.CharField(max_length=255, blank=True)
|
|
keywords = models.JSONField(default=list, blank=True)
|
|
general_solution = models.TextField(blank=True)
|
|
problem = models.TextField(blank=True)
|
|
solution = models.TextField(blank=True)
|
|
|
|
collection = models.CharField(max_length=200, db_index=True)
|
|
qdrant_point_id = models.UUIDField(default=uuid.uuid4, editable=False)
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
ordering = ['lesson_id', 'order']
|
|
constraints = [
|
|
models.UniqueConstraint(
|
|
fields=['collection', 'qdrant_point_id'],
|
|
name='uniq_lesson_chunk_qdrant_point',
|
|
),
|
|
]
|
|
indexes = [
|
|
models.Index(fields=['subject', 'lesson_number']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"chunk-lesson{self.lesson_id}-{self.order} ({self.subject})"
|
|
|
|
|
|
class LessonChunkImage(models.Model):
|
|
"""تصویرِ پیوست به یک chunk (برای محتوای چندوجهیِ آینده)."""
|
|
|
|
chunk = models.ForeignKey(LessonChunk, on_delete=models.CASCADE, related_name='images')
|
|
img_url = models.TextField()
|
|
description = models.TextField(blank=True)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
def __str__(self):
|
|
return f"image-chunk{self.chunk_id}"
|