Commit Graph

13 Commits

Author SHA1 Message Date
Maxim Dolgolyov a096f3bcd9 feat(exam): Phase 4 — контроллер использует task-level textbook_slug/paragraph
- SELECT добавляет t.textbook_slug, t.textbook_paragraph во все 7 запросов
  (getVariantTasks, practiceRandom, practiceUnsolved, pickRandomByDifficulty,
   topicTasksUnsolved/Any, weakBatchTasks, getTasksByIds)
- shapeTask() предпочитает task-level ссылку, fallback на refMap subtopic
- /variants/:n/tasks аналогично использует per-task поля
- getParaTitle() строит map chapter:para -> title из g9_textbook_sections.json

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 16:18:41 +03:00
Maxim Dolgolyov 471171b77c feat(access): доступ к учебникам и экзаменам по классам/ученикам из админ-панели
Модель allowlist (закрыто по умолчанию), правило ученика важнее класса.
Управляют админ (все) и учителя (свои классы/ученики).

- миграция 040: таблица content_access + непрерывный переход
  (всем существующим классам открыт текущий контент)
- сервис contentAccess: резолвинг доступа, главы наследуют хаб
- API /api/access (catalog/targets/rules) для admin+teacher
- гейты: каталог учебников, router.param slug/examKey, фильтр tracks
- клиентские редиректы на /403 (textbook-tracker, exam-prep boot)
- раздел админки «Доступ к учебникам»: классы + ученики (tri-state)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 12:33:05 +03:00
Maxim Dolgolyov 268ea31bb8 feat(gamification): Phase 4 — standalone coin events + coin_log
Coins were always 1:10 of XP. Now they have their own event log + a
helper that dedups by reason within a configurable window.

Backend:
  • migration 032 creates coin_log (user_id, amount, reason, created_at)
    with indices for the 'fired today?' check
  • awardCoins now records into coin_log on every call (reason defaults
    to 'xp_bonus' for the legacy XP-proportional path)
  • awardCoinsOnce(userId, amount, reason, window) — fires the bonus
    only if no row matches in the window:
      'day'     → DATE(created_at) = today
      'week'    → ISO week match
      'forever' → never twice

Wired events (Phase 4 subset of the plan):
  • Daily login — 10 coins, once/day. Hooked in updateStreak so the
    bonus rides on the existing 'daily_activity' XP trigger.
  • Daily goal completion — 15/25/40 coins (easy/medium/hard), once/day.
    Sits next to the existing tier XP bonus in updateDailyGoal.
  • Variant clear — 30 coins, once per (user, variant) forever. Fires
    from the exam-prep attempts endpoint when the user's final correct
    answer fills out a math9 variant.

Deferred (need invasive trigger hooks): weekly goal, paragraph close,
boss defeated, referral.

Verified end-to-end: awardCoinsOnce returns true→false on repeated
calls, coin_log records the first, coins balance moves once.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 20:30:14 +03:00
Maxim Dolgolyov c3d6af8757 fix(exam-prep): normalize LaTeX in dashboard preview text
Recent-attempts widget on /exam-prep/:examKey was showing raw LaTeX
like '\dfrac{7}{9}' because stripPreview only removed HTML tags.
Now it also converts common LaTeX to readable unicode (fractions →
a/b, \sqrt → √, \cdot → ·, comparisons → ≤≥≠, Greek letters, etc.)
before truncating.

KaTeX rendering would be overkill for a 100-char preview row; this
just makes the existing text legible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 16:45:59 +03:00
Maxim Dolgolyov 3cc52e21b0 feat(exam9): link tasks to textbook + difficulty-ordered random + topic exclusion
Practice (random) now picks tasks by ascending difficulty so the first
slot is always level 1 and the session ramps up. Adds ?exclude= to drop
specific subtopics from the random pool, with a per-section checkbox
modal in the UI.

Each task carries a topic_ref (textbook chapter + paragraph) shown as
a 'Учить тему · §N' button next to the solution, deep-linking to the
right section of /textbook/<slug>. Mapping seeded for all 15 math9
subtopics in migration 028.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 14:55:47 +03:00
Maxim Dolgolyov 54b8d06c61 feat(exam-prep F8): слабые темы на дашборде + strategy=weak в тренажёре 2026-05-29 11:38:55 +03:00
Maxim Dolgolyov fe7d44aa83 feat(exam-prep F7): карта тем + тематический тренажёр (API /topics + /topics/:slug/tasks + UI) 2026-05-29 11:35:28 +03:00
Maxim Dolgolyov c590c32b41 feat(exam-prep F10): план по дате экзамена — виджет на дашборде + модалка + GET/PUT/DELETE /plan 2026-05-29 11:17:28 +03:00
Maxim Dolgolyov 294b3622b5 feat(exam-prep F4): живой дашборд — streak + последние попытки + точность 7д + хитмап активности + пробники 2026-05-29 11:12:23 +03:00
Maxim Dolgolyov cfcb233b6c feat(exam-prep F9): пробный экзамен — setup/active/result + таймер + балл по сетке + серверный чекер 2026-05-29 11:06:57 +03:00
Maxim Dolgolyov 4652f9a73d feat(exam-prep F5): тренажёр случайных задач + /practice/next API (random|unsolved) 2026-05-29 10:57:22 +03:00
Maxim Dolgolyov 7c33d4ce11 feat(exam-prep F2): порт браузера вариантов + API /variants + POST /attempts + редирект /exam9 2026-05-29 10:43:10 +03:00
Maxim Dolgolyov 1b79965fce feat(geom9 ch3 wave2 + final): §12 «Герон» + Финал Главы 3 2026-05-29 10:13:29 +03:00