02-school-admin

02. Руководство school_admin

Аудитория. Администратор одной школы. Отвечает за преподавателей, каталог курсов, биллинг школы и мониторинг обучения.


Что видит и что делает

Чего не делает:


Доступ

Учётка в dev:
school_admin@local / change_me_locally (демо-школа, id a7e42330-b775-4041-a57b-9e0ee9ea7ed9).

Фронт: http://127.0.0.1:3500/login.


Главные страницы

URL Что там
/dashboard Обзор школы, видимые школы (1), методик, ссылка на разделы
/courses Каталог курсов школы (published + drafts)
/courses/[id] Просмотр последней версии курса
/courses/[id]/edit Редактор курса (автосейв в course_drafts)
/courses/[id]/v/[N] Историческая версия курса (read-only)
/billing Оплата / выбор провайдера
/billing/success, /billing/cancel Возврат от провайдера
/analytics Completion rate + самые трудные блоки (по course_id)
/demo/media Демонстрация генерации изображений/аудио/видео

Типовые сценарии

1. Выдать/отозвать роль teacher

UI для этого сейчас нет. Единственные пути:

  1. Пересоздать dev-учётки.venv/bin/python -m scripts.bootstrap создаёт demo-school + admin@local, school_admin@local, teacher@local, student@local.
  2. Руками в БД (для выдачи роли реальному пользователю):
    INSERT INTO user_memberships (user_id, school_id, role_code)
    VALUES ('<user-uuid>', '<your-school-uuid>', 'teacher');
    
    -- Отозвать
    DELETE FROM user_memberships
    WHERE user_id = '<user-uuid>' AND school_id = '<your-school-uuid>' AND role_code = 'teacher';
    

Web-форма / email-приглашения — в roadmap (Phase 6).

2. Купить пакет занятий (биллинг, demo-режим)

  1. /billing → выбор провайдера из списка (stripe / vipps / paypal / mock).
  2. Жмёшь Pay → переход на /billing/mock-checkout (эмулятор hosted-page).
  3. Три кнопки: Pay / Fail / Cancel → каждая шлёт webhook на /billing/webhooks/{provider}.
  4. При успехе — редирект на /billing/success. При отмене — /billing/cancel.

Webhook идемпотентен: повторная доставка вернёт {status:"duplicate", ack:true} и не создаст второй payment_event.

3. Посмотреть аналитику курса

  1. /dashboard → ссылка «Course completion + hardest blocks».
  2. На /analytics вбить course_id (как он записан в published_courses.course_id) → «Показать».
  3. Получаешь 2 карточки:
    • Сводка — всего событий, учеников, прохождений, попыток, ошибок, completion_rate.
    • Самые трудные блоки — топ-10 блоков по fail_rate (ORDER BY fails/attempts DESC). Блоки с fail_rate ≥ 50% помечаются баннером rework.

Под капотом:

POST /learner/events                  (записывает события от студента)
GET  /learner/analytics/{course_id}   (агрегация, доступ только school_admin / teacher / platform_admin)

Никакие имена студентов в аналитике не раскрываются — только счётчики.

4. Посмотреть LLM-затраты школы

/dashboard → «LLM cost (last 30 days)» → компонент CostReport.

Фильтр по school_id применяется автоматически благодаря RLS (таблица llm_cost_entries).

5. Клонировать курс внутри школы

curl -X POST "http://127.0.0.1:55400/courses/<src_course_id>/copy" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"new_course_id":"clone-001","new_title":"Клон курса"}'

Ответ 201 при успехе, 409 если new_course_id уже занят, 404 если исходник не найден в вашей школе, 422 если новый id совпадает с исходным или пустой.

Полная семантика deep_copy — см. docs/PROJECT_MANIFEST.md §14.

6. Откатиться к предыдущей версии курса

Каждая публикация создаёт новую строку в published_courses с инкрементом version. Перейти на историческую версию можно по URL /courses/[id]/v/[N] — это read-only view.

Если нужно сделать N снова «активной» — опубликовать её ещё раз через редактор / скрипт (обычно teacher).


Что контролировать ежедневно


Чего НЕ нужно делать


Ссылки