Памятка для разработчика админки 2035. Сервис не рисует карту — только отдаёт готовую матрицу. UI, цвета, PNG/PDF — на стороне админки.

Откуда данные

LMS backend  ──POST /api/lms/session-ip-events──► anomaly-service  (логины / IP)
LMS frontend ──sdk.js → /api/sdk/*──────────────► anomaly-service  (activeTime, задания, ИИ)
Админка LMS  ◄──GET /api/admin/heatmap─────────── anomaly-service  (сетка для отрисовки)

Источники: backend LMS, frontend SDK. Markdown: docs/lms-admin-heatmap.md.

1. Auth

Authorization: Bearer <ADMIN_API_KEY>

Ключ выдаёт команда anomaly-service (ADMIN_API_KEY). Не путать с LMS_TO_ANOMALY_API_KEY и SDK_API_KEY.

2. Эндпоинт для отрисовки

GET https://<anomaly-host>/api/admin/heatmap

Query params

Параметр Обязательный Описание
metric нет Показатель. По умолчанию activeTime
granularity нет Ось X. По умолчанию day
from / to нет YYYY-MM-DD. Без них — последние 30 дней
courseId / groupId / moduleId нет Фильтры. Справочники курсов/групп — из LMS
Без from/to API режет окно до 30 дней. Для «весь курс / всё демо» передавайте явный диапазон.

metric

Значение Что в ячейке Источник
logins число входов session_started
activeTime секунды активной работы ActivitySummary.activeSeconds
completedAssignments число сдач заданий assignment_submitted
aiAssistantRequests число обращений к ИИ ai_assistant_request

granularity

Значение Колонки
day один день = одна колонка
week неделя (пн–вс, UTC), ключ = дата понедельника
month месяц, ключ = YYYY-MM-01
module сейчас как day; фильтр модуля — через moduleId

3. Ответ → как мапить в UI

{
  "metric": "activeTime",
  "granularity": "day",
  "from": "2026-07-01",
  "to": "2026-07-31",
  "columns": [
    { "key": "2026-07-01", "label": "01.07" },
    { "key": "2026-07-02", "label": "02.07" }
  ],
  "rows": [
    {
      "studentId": "12345",
      "studentName": "Иванов Иван",
      "groupId": "group-001",
      "groupTitle": "Поток 1",
      "values": { "2026-07-01": 3600, "2026-07-02": 1200 },
      "buckets": { "2026-07-01": 5, "2026-07-02": 2 }
    }
  ],
  "legend": { "min": 0, "max": 3600, "buckets": 5 }
}
Элемент UI Поле ответа
Заголовки колонок (ось X) columns[].label
Ключ ячейки columns[].key
Строки (ученики) rows[]
Подпись строки studentName (fallback: studentId)
Число / tooltip rows[].values[column.key] → иначе 0
Интенсивность цвета buckets[key] (0..5) или value / legend.max

Псевдокод

for (const row of data.rows) {
  for (const col of data.columns) {
    const value = row.values[col.key] ?? 0;
    const bucket = row.buckets[col.key] ?? 0; // 0..5
    // cell background = palette[bucket]
    // cell text / title = value
  }
}
buckets — ступень относительно максимума этой строки (ученика). legend.max — глобальный максимум матрицы. Для единой шкалы на всю карту нормализуйте через legend.max (как на /heatmap.html).

4. Фильтры и пример

UI: курс · группа · модуль · период · гранулярность · метрика. Сетка: строки = ученики, колонки = периоды, ячейки = value + цвет.

GET /api/admin/heatmap?metric=activeTime&granularity=day&from=2026-07-01&to=2026-07-31&courseId=python-basic-2026&groupId=group-001
Authorization: Bearer <ADMIN_API_KEY>

5. Экспорт PNG / PDF

Картинку делайте во фронте админки (html-to-image / html2canvas + jsPDF). Backend отдаёт только JSON.

GET /api/admin/exports/heatmap.json
Authorization: Bearer <ADMIN_API_KEY>

Для экспорта «как на экране» лучше снова вызвать GET /api/admin/heatmap с теми же query и снять DOM.

6. Пустая карта — чек-лист