Files
MathApp/content/migrations/0021_plain_math_everywhere.py

108 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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)]