108 lines
4.6 KiB
Python
108 lines
4.6 KiB
Python
"""
|
||
LaTeX و نشانهگذاریِ خام (`^(−۴)`، `**…**`، `\\vec`) را از همهی متنهایی که دانشآموز میبیند پاک میکند:
|
||
سوالها و گزینهها، متنِ درسها و گفتگوهای دستیار. فایلهای seed همین تبدیل را دارند؛ بدونِ این migration
|
||
`seed_content` سوالِ تمیز را «تازه» میدید (صورتش با دیتابیس فرق دارد) و کنارِ نسخهی قدیمی دوباره میساخت.
|
||
|
||
تبدیل `content/plain_math.py::convert` است (idempotent). معکوس ندارد. پاسخِ دستیار با
|
||
`rag.services.mathtext.to_plain` (همان مسیرِ زندهی پاسخها).
|
||
"""
|
||
import hashlib
|
||
|
||
from django.db import migrations
|
||
|
||
from content.plain_math import convert, convert_figures
|
||
|
||
|
||
def _h(text):
|
||
return hashlib.sha1((text or '').encode('utf-8')).hexdigest()
|
||
|
||
|
||
def _key(*parts):
|
||
return hashlib.sha1('\x1f'.join(('intro', *map(str, parts))).encode('utf-8')).hexdigest()
|
||
|
||
|
||
def forwards(apps, schema_editor):
|
||
Question = apps.get_model('content', 'Question')
|
||
Choice = apps.get_model('content', 'Choice')
|
||
Lesson = apps.get_model('content', 'Lesson')
|
||
SeedRecord = apps.get_model('content', 'SeedRecord')
|
||
|
||
batch = []
|
||
for q in Question.objects.all().iterator(chunk_size=1000):
|
||
new = convert(q.prompt)
|
||
if new != q.prompt:
|
||
q.prompt = new
|
||
batch.append(q)
|
||
if len(batch) >= 500:
|
||
Question.objects.bulk_update(batch, ['prompt'])
|
||
batch = []
|
||
if batch:
|
||
Question.objects.bulk_update(batch, ['prompt'])
|
||
|
||
batch = []
|
||
for c in Choice.objects.all().iterator(chunk_size=2000):
|
||
new = convert(c.value)
|
||
if new != c.value:
|
||
c.value = new
|
||
batch.append(c)
|
||
if len(batch) >= 500:
|
||
Choice.objects.bulk_update(batch, ['value'])
|
||
batch = []
|
||
if batch:
|
||
Choice.objects.bulk_update(batch, ['value'])
|
||
|
||
# بعد از تبدیل ممکن است دو سوالِ همصورت در یک درس بماند: نسخهی قدیمیِ LaTeXدار و نسخهای که seed
|
||
# پیش از این migration با متنِ تمیز ساخته بود. یکی نگه داشته میشود: آنکه پاسخِ بیشتری دارد، وگرنه قدیمیتر.
|
||
from django.db.models import Count
|
||
UserAnswer = apps.get_model('content', 'UserAnswer')
|
||
groups = {}
|
||
for qid, lid, prompt in Question.objects.values_list('id', 'lesson_id', 'prompt').iterator(chunk_size=2000):
|
||
groups.setdefault((lid, prompt), []).append(qid)
|
||
drop = []
|
||
for ids in groups.values():
|
||
if len(ids) > 1:
|
||
answered = dict(UserAnswer.objects.filter(question_id__in=ids).values_list('question_id').annotate(n=Count('id')))
|
||
keep = max(ids, key=lambda i: (answered.get(i, 0), -i))
|
||
drop.extend(i for i in ids if i != keep)
|
||
if drop:
|
||
Question.objects.filter(id__in=drop).delete()
|
||
|
||
for lesson in Lesson.objects.select_related('node__unit__grade').iterator(chunk_size=500):
|
||
new = {
|
||
'title': convert(lesson.title),
|
||
'intro_text': convert(lesson.intro_text),
|
||
'example': convert(lesson.example),
|
||
'steps': [convert(s) for s in (lesson.steps or [])],
|
||
'figures': convert_figures(lesson.figures or []),
|
||
}
|
||
changed = [f for f, v in new.items() if v != getattr(lesson, f)]
|
||
if not changed:
|
||
continue
|
||
old_intro, old_title = lesson.intro_text, lesson.title
|
||
for f in changed:
|
||
setattr(lesson, f, new[f])
|
||
lesson.save(update_fields=changed)
|
||
if 'intro_text' in changed:
|
||
n = lesson.node
|
||
key = _key(n.unit.grade.title, n.unit.title, n.title, old_title)
|
||
SeedRecord.objects.filter(key=key, value=_h(old_intro)).update(value=_h(new['intro_text']))
|
||
|
||
try:
|
||
Message = apps.get_model('ai_tutor', 'Message')
|
||
except LookupError:
|
||
return
|
||
from rag.services.mathtext import to_plain
|
||
batch = []
|
||
for m in Message.objects.all().iterator(chunk_size=500):
|
||
new = to_plain(m.text) if m.role == 'assistant' else convert(m.text)
|
||
if new != m.text:
|
||
m.text = new
|
||
batch.append(m)
|
||
if batch:
|
||
Message.objects.bulk_update(batch, ['text'])
|
||
|
||
|
||
class Migration(migrations.Migration):
|
||
dependencies = [('content', '0020_plain_math_lesson_texts'), ('ai_tutor', '0008_alter_message_created_at')]
|
||
operations = [migrations.RunPython(forwards, migrations.RunPython.noop)]
|