From b075b93f2a3acc750793738a54444baadae5171f Mon Sep 17 00:00:00 2001 From: Zareian Date: Mon, 12 Oct 2026 12:18:46 +0330 Subject: [PATCH] Tutor/answers: bare subscripts (S_n, a_1) become Unicode; migration 0022 re-cleans DB text Co-Authored-By: Claude Sonnet 5.5 --- ai_tutor/tests_plain_history.py | 8 ++++++++ .../migrations/0022_plain_math_messages_again.py | 16 ++++++++++++++++ content/plain_math.py | 3 ++- content/seed/curriculum.json | 4 ++-- rag/services/mathtext.py | 4 +++- 5 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 content/migrations/0022_plain_math_messages_again.py diff --git a/ai_tutor/tests_plain_history.py b/ai_tutor/tests_plain_history.py index f5b9c9f..9c80cac 100644 --- a/ai_tutor/tests_plain_history.py +++ b/ai_tutor/tests_plain_history.py @@ -23,3 +23,11 @@ class HistoryNeverShowsLatexTests(TestCase): self.assertNotIn('^', t) self.assertNotIn('$', t) self.assertIn('x²', texts[0]) + + +class ToPlainSubscriptTests(TestCase): + def test_bare_subscripts_are_unicode(self): + from rag.services.mathtext import to_plain + out = to_plain('S_n = n/2 × (a_1 + a_n)') + self.assertNotIn('_', out) + self.assertIn('Sₙ', out) diff --git a/content/migrations/0022_plain_math_messages_again.py b/content/migrations/0022_plain_math_messages_again.py new file mode 100644 index 0000000..29a6cba --- /dev/null +++ b/content/migrations/0022_plain_math_messages_again.py @@ -0,0 +1,16 @@ +""" +0021 پیش از تکمیلِ تبدیل (اندیسِ ساده‌ی S_n، a_1) اجرا شد؛ پیام‌های دستیار و متنِ سوال/درس را با نسخه‌ی کامل دوباره +می‌گذراند. همان تبدیل، idempotent. `0021.forwards` عیناً دوباره اجرا می‌شود (تکراریِ سوال را هم پاک می‌کند). +""" +import importlib + +from django.db import migrations + + +def forwards(apps, schema_editor): + importlib.import_module('content.migrations.0021_plain_math_everywhere').forwards(apps, schema_editor) + + +class Migration(migrations.Migration): + dependencies = [('content', '0021_plain_math_everywhere')] + operations = [migrations.RunPython(forwards, migrations.RunPython.noop)] diff --git a/content/plain_math.py b/content/plain_math.py index cecae54..fdcccf4 100644 --- a/content/plain_math.py +++ b/content/plain_math.py @@ -99,7 +99,8 @@ def convert(text: str) -> str: s = re.sub(r'\^\(((?:[^()]|\([^()]*\))*)\)', lambda m: _try(m[1], _SUP) or f' به توانِ ({m[1].strip()}) ', s) s = re.sub(r'(?<=[A-Za-z0-9۰-۹\)₀-₉ₐ-ₜᵢⱼ])\^([A-Za-zα-ωΑ-Ω□▢])(?![A-Za-z0-9۰-۹])', lambda m: _try(m[1], _SUP) or f' به توانِ {m[1]} ', s) s = re.sub(r'\^([+\-−]?[0-9۰-۹]+)', lambda m: _try(m[1], _SUP) or m[0], s) - s = s = re.sub(r'(?<=[A-Za-z\)∫Σ∏])_([0-9۰-۹]+|[A-Za-z]{1,4}[0-9۰-۹]?)', lambda m: ''.join(_SUB.get(ch, ch) for ch in m[1]), s) + s = s = re.sub(r'((? str: text = _strip_markdown(text) text = re.sub(r'[ \t]+\n', '\n', text) text = re.sub(r'\n{3,}', '\n\n', text) - return text.strip() + # S_n، a_1، x^2 و توانِ پیچیده که بالا رد شد: همان تبدیلِ seed/دیتابیس (یک رفتار در همه‌جا). + from content.plain_math import convert as _plain + return _plain(text).strip() class StreamSanitizer: