Commit Graph

12 Commits

Author SHA1 Message Date
99a0cafe24 serve what the frontend screens actually need
صفحه‌های فرانت خوانده شدند و با API مقایسه شدند. ساختارِ ما از کتابِ واقعی
درآمده و درست‌تر است، پس مدل عوض نشد — ولی چند چیز کم داشت.

مهم‌ترینش یک باگ بود: /api/home پارامتر را `grade` می‌خواند و کلاینتی که به
فرانت فرستادیم `gradeId` می‌فرستد. پارامترِ ناشناخته بی‌صدا نادیده گرفته
می‌شد، یعنی کاربر پایه‌ای را انتخاب می‌کرد و نقشه‌ی پایه‌ی دیگری می‌دید
بدونِ هیچ خطایی. حالا هر دو نام پذیرفته می‌شوند (`gradeId` نامِ درست است،
با بقیه‌ی API یکی). تستِ سرتاسریِ قبلی این را نگرفته بود چون home را بدونِ
آرگومان صدا زده بود.

/api/grades حالا lessonCount می‌دهد. فهرستِ پایه‌های فرانت تا دوازدهم است
ولی محتوای ما تا نهم؛ بدونِ این عدد، کاربر پایه‌ی خالی را انتخاب می‌کند و
صفحه‌ی خالی می‌بیند بی‌آنکه بفهمد چرا.

GET /api/nodes/{id}/lessons اضافه شد. /api/home فقط شناسه‌ی درس‌ها را
می‌دهد، ولی نقشه‌ی مهارت برای هر قدم عنوان، نوع، زیرنویس و «انجام شد» لازم
دارد — بدونش فرانت باید برای هر درس یک intro جدا می‌گرفت.

Lesson حالا kind (آزمون/ویدیو) و متنِ سه‌تکه دارد: توضیح، مثال، حلِ
گام‌به‌گام. ویدیو از قبل سوالِ بازِ محصول بود و فرانت آیتمش را دارد؛ حالا
API می‌تواند بیانش کند، حتی اگر محتوایش بعداً بیاید. Unit یک blurb گرفت و
Node آیکون و رنگ، تا فرانت رنگ‌ها را هاردکد نکند. هیچ‌کدام اجباری نیست، پس
محتوای موجود دست‌نخورده کار می‌کند.

PATCH /api/user/profile حالا age را هم می‌پذیرد (صفحه‌ی ثبت‌نامِ فرانت سن
می‌پرسد). ذخیره‌سازی همچنان birthYear است چون کهنه نمی‌شود؛ این فقط ورودی
را ترجمه می‌کند تا دو طرف حسابِ متفاوت درنیاورند.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 01:42:24 +03:30
5f3aae0756 cover the new paths, especially the ones that fail silently
خطاهای این پروژه خاموش‌اند: نامِ اشتباهِ collection یا فیلترِ نخورده نتیجه‌ی
خالی می‌دهد نه استثنا، و دستیار فقط بدتر جواب می‌دهد. این تست‌ها نگهبانِ
همان‌جاهایند.

TextbookLayoutTests بررسی می‌کند رشته‌ی هر فصل بینِ ingest و retrieval یکی
باشد و بازه‌ی صفحه‌ها پیوسته بماند. IngestBookResumeTests مطمئن می‌شود
صفحه‌ی بی‌مسئله دوبار پول نمی‌گیرد، صفحه‌ی خطادار در اجرای بعد دوباره
سراغش می‌روند، و upsertِ شکست‌خورده rollback می‌شود.
LessonContextResolutionTests نگهبانِ نرمال‌سازیِ «پایه نهم» → «ریاضی نهم»
است و اینکه مسیرِ gradeId و مسیرِ lessonId به یک collection برسند.
GradeAwarePromptTests جلوی برگشتنِ لحنِ دبستانی برای پایه‌های بالا را
می‌گیرد. FollowUpStaysGroundedTests پیامِ دومِ یک گفتگو را هم مستند نگه
می‌دارد.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 19:01:24 +03:30
ac1b1af398 open the API up to the frontend
سینیور خواست زودتر به فرانت‌اند وصل شویم تا از آنجا هم تست شود. این‌ها
سمتِ ما لازم بودند:

CORS با allowlist صریح (نه ALLOW_ALL): احرازِ هویتِ ما توکنِ Bearer در هدر
است، پس origin ناشناس نباید از مرورگرِ کاربر به API ما بزند.
CORS_ALLOW_CREDENTIALS عمداً خاموش می‌ماند — کوکی‌ای در کار نیست و روشن
کردنش فقط سطحِ حمله اضافه می‌کند. CorsMiddleware بالاتر از CommonMiddleware
گذاشته شد وگرنه ریدایرکتِ APPEND_SLASH و خطای ۴۰۰ هدرِ CORS نمی‌گیرند و در
مرورگر به‌شکلِ خطای مبهمِ شبکه دیده می‌شوند.

PATCH /api/user/profile اضافه شد؛ تا پیش از این هیچ راهی برای ثبتِ نامِ
کاربر نبود. GET /api/home بدونِ پارامتر حالا پایه‌ی ذخیره‌شده‌ی کاربر را
برمی‌گرداند و POST /api/ai/ask فقط با lessonId کار می‌کند — هر دو برای
اینکه فرانت لازم نباشد پایه را خودش نگه دارد. questionCount به
lessons/{id}/intro اضافه شد.

docs/frontend/api.ts و README.md کلاینتِ تایپ‌دار و راهنمای اتصال‌اند، از
روی OpenAPI واقعیِ همین سرویس تولید شده‌اند.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 19:01:06 +03:30
37a1741e0e add gamification characters (Duolingo-style mascot catalog)
New progress.Character model: an admin-managed catalog (same pattern as
subscription.Plan) of selectable mascots, each unlocked once the user's
XP crosses unlock_xp. No separate ownership table yet — unlocking is a
pure function of UserStats.xp, kept intentionally minimal since this is
the first of an incremental rollout the senior asked for.

- GET /api/characters — catalog with per-user unlocked/selected flags
- POST /api/characters/select — picks an active character, rejecting
  ones the user hasn't unlocked yet
- UserStats.selected_character_id tracks the pick
- seed_test_data now seeds 4 sample characters (owl/fox/turtle/lion)
  for local/staging testing

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 13:53:17 +03:30
d25ecab0b3 optional short-TTL cache for the auth user row
TadrisinoTokenAuthentication ran one SELECT on accounts_user for every
authenticated request. Off by default (AUTH_USER_CACHE_TTL=0) so behaviour
is unchanged until a deployment opts in.

accounts/authentication.py
  - _get_request_user(): when AUTH_USER_CACHE_TTL>0, cache all concrete
    field values under auth:user:<id> and rebuild a full User instance
    (so save(update_fields=[...]) stays safe); otherwise load from DB as
    before. invalidate_user_cache() / _load_user_or_fail() helpers.
accounts/receivers.py (new)
  - post_save/post_delete on User -> invalidate_user_cache(pk); covers
    every in-app write path (SetUserGrade, login, refresh, last_activity).
accounts/apps.py
  - AccountsConfig.ready() imports receivers.
accounts/tests.py
  - AuthUserRowCacheTests: disabled -> loads every request; enabled ->
    loads once then cache; User.save() invalidates; SetUserGradeView still
    persists and the next request sees the new grade.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 08:46:11 +03:30
683bd40169 cleanup: trim bloated multi-line comments across the project 2026-08-26 03:22:20 +03:30
05ab6ffc36 admin & data: correct admin action redirect, add SQLite retry, seed_test_data command 2026-08-26 03:22:20 +03:30
a9200772a3 Add campaigns/ Add supervisor/ Add messaging features/ Separate SMS 2026-08-25 03:39:41 +03:30
80298b47b1 throttling, abuse-protection & logging/monitoring pass 2026-08-23 23:44:00 +03:30
cb18c93a3e Harden auth, AI tutor, payments, and subscriptions 2026-08-22 04:42:36 +03:30
f945f960c9 Update project 2026-08-19 17:15:55 +03:30
331148037e Initial commit - clean project structure 2026-08-18 16:48:15 +03:30