Справочник API
Курсы, уроки и шаги
Создание структуры курса, копирование материалов, режимы доступа к урокам и загрузка медиафайлов.
Маршруты раздела
| Метод | Маршрут | Доступ | Назначение |
|---|---|---|---|
GET |
/api/courses |
Зависит от фильтра | Получить доступные курсы или рабочий список преподавателя. |
POST |
/api/courses |
Преподаватель | Создать черновик курса. |
GET |
/api/courses/[id] |
По доступу к курсу | Получить курс по идентификатору или адресу. |
PUT |
/api/courses/[id] |
Редактирование курса | Изменить настройки или опубликовать курс. |
DELETE |
/api/courses/[id] |
Владелец курса | Переместить курс в удалённые. |
POST |
/api/courses/[id]/duplicate |
Управление курсом | Создать независимую копию курса. |
PATCH |
/api/instructor/courses/[id]/content/reorder |
Редактирование курса | Атомарно применить полный порядок модулей, уроков или шагов. |
GET |
/api/sections?courseId=[id] |
Чтение курса | Получить модули курса. |
POST |
/api/sections |
Редактирование курса | Создать модуль. |
GET |
/api/sections/[id] |
Чтение курса | Получить один модуль. |
PUT |
/api/sections/[id] |
Редактирование курса | Изменить или переместить модуль. |
DELETE |
/api/sections/[id] |
Редактирование курса | Удалить модуль. |
GET |
/api/lessons?sectionId=[id] |
Чтение курса | Получить уроки модуля. |
POST |
/api/lessons |
Редактирование курса | Создать урок. |
GET |
/api/lessons/[id] |
Чтение курса | Получить урок. |
PUT |
/api/lessons/[id] |
Редактирование курса | Изменить урок и срок выполнения. |
DELETE |
/api/lessons/[id] |
Редактирование курса | Удалить урок. |
POST |
/api/lessons/[id]/duplicate |
Редактирование курса | Скопировать урок в выбранный модуль. |
GET |
/api/steps?lessonId=[id] |
Редактирование курса | Получить шаги урока для редактора. |
POST |
/api/steps |
Редактирование курса | Создать шаг. |
GET |
/api/steps/[id] |
Редактирование курса | Получить шаг для редактора. |
PUT |
/api/steps/[id] |
Редактирование курса | Изменить шаг или его положение. |
DELETE |
/api/steps/[id] |
Редактирование курса | Удалить шаг. |
POST |
/api/uploads/course-cover |
Преподаватель | Загрузить обложку курса. |
POST |
/api/uploads/lesson-media |
Преподаватель | Загрузить изображение, аудио или WebM для урока. |
Иерархия материалов
Курс состоит из модулей, модуль — из уроков, урок — из шагов:
Курс → Модуль → Урок → Шаг
При создании и изменении поддерживаются как прямой JSON, так и именованная оболочка: { "course": { ... } }, { "lesson": { ... } } и аналогичные варианты.
Как менять порядок без конфликтов
Не отправляйте серию PUT /api/lessons/[id] или PUT /api/steps/[id] с разными position: промежуточные позиции могут конфликтовать. Для сортировки используйте один атомарный запрос PATCH /api/instructor/courses/[id]/content/reorder с полным снимком порядка.
Шаги одного урока:
{ "kind": "STEPS", "lessonId": "lesson-id", "orderedStepIds": ["step-2", "step-1"] }
Все уроки курса, включая перенос между модулями:
{
"kind": "LESSONS",
"sections": [
{ "sectionId": "section-1", "orderedLessonIds": ["lesson-2"] },
{ "sectionId": "section-2", "orderedLessonIds": ["lesson-1"] }
]
}
Модули курса передают и исходный, и новый полный порядок:
{
"kind": "SECTIONS",
"expectedSectionIds": ["section-1", "section-2"],
"orderedSectionIds": ["section-2", "section-1"]
}
Нужен scope CONTENT_WRITE; для PAT курс также должен оставаться в courseIds, а у владельца — актуальное право content:write. Неполный или устаревший снимок возвращает 409 без частичной записи.
Создание курса
Минимальный запрос:
{
"title": "Основы Python",
"description": "Практический курс для начинающих",
"price": 0
}
Новый курс сохраняется черновиком. Перед публикацией сервер строго проверяет структуру и блоки всех шагов.
Режимы доступа к уроку
Поле accessMode принимает:
PUBLIC— урок открывается без регистрации;REGISTERED— нужен аккаунт, но покупка не требуется;PAID— нужен доступ к курсу.
Поле isDemo оставлено для совместимости. В новых интеграциях записывайте accessMode.
Типы шагов
Режим PROJECT — многофайловый HTTP-сервис. Профили: project-go-127 (Go), project-python-312 (Python/FastAPI), project-rust-198 (Rust 1.98/Axum/Tokio). Поле project содержит files: [{path, content, editable}], entrypoint (./directory, main:app или фиксированный src/main.rs для Rust), readinessPath, postgres: {enabled, seedSql} и checks. До 12 файлов, 20 000 символов на файл и 80 000 суммарно; относительные ASCII-пути без скрытых файлов и ..; go.mod/go.sum и Cargo.toml/Cargo.lock задаёт платформа, build.rs и переопределения toolchain запрещены. Rust-файлы находятся внутри src/, зависимости уже собраны, Cargo работает offline. Ученик отправляет в code JSON-строку с files: [{path, content}] — ровно все редактируемые файлы. Остальные файлы добавляет сервер.
В PROJECT можно включить redis.enabled; подключение доступно как REDIS_URL. Для самостоятельной HTTP-песочницы задаётся отдельный публичный postgres.playgroundSeedSql: приватный seedSql туда не копируется. Проверки поддерживают faultBefore (NONE, REDIS_STOP, REDIS_START, REDIS_RESTART, POSTGRES_STOP, POSTGRES_START), waitBeforeMs (0–5000, суммарно до 20 секунд) и parallelRequests (1–5 одинаковых запросов). Для зачёта группы должны пройти все ответы; переменные берутся из первого. Redis после перезапуска пустой, PostgreSQL сохраняет данные. Песочница открывается только в кабинете ученика и не меняет оценку; PAT/MCP к сессиям доступа не имеют.
Проверки проекта — до 20 последовательных HTTP-запросов: method, path, body, expectedStatus, expectedBody, bodyMatch (JSON/TEXT/IGNORE), name, points, hidden, learnerFailureMessage, restartBefore, capture: [{name, pointer}]. JSON Pointer сохраняет значение из ответа; следующие запросы используют {{id}} в пути или {"$ref":"id"} в JSON. Тела ограничены 8000 символов, JSON — 24 уровнями и точными конечными числами. База новая на каждый запуск, перезапуск сервиса внутри сценария её сохраняет. После ошибки остальные запросы пропускаются, баллы уже пройденных остаются. Скрытые сценарии не раскрывают сырой HTTP-вывод. Этапы подготовки, сборки и запуска возвращаются отдельно в projectPhases. Нужен настроенный исполнитель проектов.
Go-функции используют режим GO_FUNCTION, язык go, профиль go-127-modal и goFunction: {name: "Sum"}. Ученик пишет функцию в package main, без main(). В testCases поле input — JSON-массив аргументов ([[1,2,3]] для одного среза), output — ожидаемый результат JSON. Несколько возвращаемых значений передаются массивом; error — строкой или null. Порядок ключей объекта не важен, порядок массива и типы значений важны. Большие целые вне точного диапазона JavaScript передавайте строками. До 20 тестов, до 8000 символов на ввод/результат, глубина JSON до 32; лимит одной проверки 1–15 секунд, общий до 60 секунд, память 64–256 МиБ. Доступна стандартная библиотека. Поддерживаются названия тестов, баллы, сообщения и подсказки, описанные ниже. Скрытые данные не входят в программу ученика; проверка результата идёт на сервере. Собственный запуск принимает аргументы без выставления оценки. Этот режим не является запуском произвольных _test.go или многофайлового проекта.
Практика командной строки использует отдельные CODE-режимы. SHELL_SCRIPT (язык bash, профиль modal-devtools-debian-12) запускает ровно bash solution.sh в одноразовом workspace без сети; GIT_FIXTURE проверяет финальное состояние локального репозитория — branch, HEAD message, commit count, clean state, branches и файлы. DOCKERFILE (язык docker, профиль modal-docker-27-vm) собирает и запускает Dockerfile в одноразовой Modal VM без сети. Разрешены только предзагруженные alpine:3.20 и busybox:1.36; host mounts, registries и learner-controlled daemon commands недоступны. Конфигурация хранится в devtools.workspace, devtools.git или devtools.docker и devtools.assertions; полный JSON приведён в docs/API_IMPORT.md. Если MODAL_DEVTOOLS_EXECUTION_URL не настроен или провайдер вернул некорректный ответ, проверка завершается fail closed.
Для структурного сравнения JSON в обычном STDIO также доступно outputComparison: {mode: "JSON"}.
C/C++: режим NATIVE_FUNCTION, профили native-c-17 / native-cpp-20, язык c / cpp. Поле nativeFunction: {header, driver, checkMemory}: открытые объявления и код main(), вызывающий функции или классы ученика; checkMemory по умолчанию true. Оба текста до 20 000 символов; driver обязателен. Ожидаемые ответы — только в testCases, не в driver. До 10 тестов, 8000 символов на ввод/вывод, 1–3 секунды и 64–256 МиБ (по умолчанию 2/256). Доступны баллы, подсказки и outputComparison (по умолчанию TRIM). Clang 14, C17/C++20; отдельный запуск ASan/UBSan выявляет ошибки памяти. Каждый тест изолирован, сравнение — на сервере. Скрытый тест показывает вердикт MEMORY_ERROR, но не диагностический вывод. Запуск на своих данных не создаёт попытку; PAT/MCP к исполнителю доступа не имеют.
Для CODE с режимом STDIO можно задать outputComparison: mode — EXACT (точно, с нормализацией переносов строк), TRIM (без пробелов по краям), TOKENS (без различий в разделяющих пробелах), NUMERIC (числовые значения с абсолютной погрешностью). Для NUMERIC поле tolerance — от 0 до 0.01, по умолчанию 0.000001. Без этой настройки сохраняются прежние правила задания. Настройка доступна через обычные права записи содержания и видна ученику.
В STDIO и PYTHON_TEST_CODE у теста или набора можно указать points — целое число от 1 до 1000, по умолчанию 1. Частичный результат — доля набранных баллов; для полного зачёта нужны все проверки. Поле learnerFailureMessage (до 500 символов) задаёт сообщение при неверном ответе или ошибке, в том числе у скрытого теста. Закрытые данные и технический вывод при этом не раскрываются.
Поле name у теста STDIO или набора Python задаёт необязательное название до 120 символов. Без него показывается «Тест 1», «Тест 2» и так далее. Название меняется прямо в заголовке выбранного теста; название скрытой проверки ученику не передаётся.
В этих режимах поле hints содержит до 10 подсказок: {afterAttempts: 3, text: "Проверьте граничный случай"}. Порог — от 1 до 100 неудачных отправок, текст — от 1 до 2000 символов. Считаются сохранённые неверные решения, ошибки и превышения времени; самостоятельные запуски не учитываются. До открытия текст подсказки не передаётся ученику. Эти поля сохраняются через обычные права записи содержания курса.
Учащийся может запускать собственный Python-код интерактивно: программа сохраняет состояние, принимает строки и показывает вывод до завершения. Для этого требуется подключённый интерактивный исполнитель. Авторские тесты и preCode в собственном запуске не исполняются, оценка и прогресс не меняются. Это отдельная возможность кабинета учащегося, недоступная через PAT/MCP; автору не нужно менять формат задания.
Поддерживаются TEXT, VIDEO, CODE, CHOICE, NUMBER, FREE_TEXT, SORTING, MATCHING и REVIEW. В штатном редакторе тип выбирается при создании. Низкоуровневый PUT /api/steps/[id] также принимает новый type, но вместе с ним обязательно нужно передать новый совместимый block.
Текстовая задача FREE_TEXT принимает многострочный answer.value. Каждый элемент answers — целый допустимый ответ. ignoreCase управляет регистром, trimWhitespace — пробелами по краям, matchSubstring — поиском ответа внутри текста. Для своей проверки задайте pythonChecker с функцией check(reply): результат — True/False, число от 0 до 1 или пара (балл, комментарий). В этом режиме answers пуст, acceptAny и acceptRegex выключены. Код скрыт от ученика, выполняется без сети с лимитом 3 секунды и 128 МБ; комментарий показывается после проверки. Авторский API возвращает код только в рамках прав на содержание курса.
Пример числовой задачи:
{
"lessonId": "lesson-id",
"type": "NUMBER",
"block": {
"text": "Сколько будет 6 × 7?",
"answer": 42,
"tolerance": 0,
"correctFeedback": "Верно",
"incorrectFeedback": "Проверьте умножение"
}
}
Правильные ответы, скрытые тесты и подсказки проверяющему возвращаются только в преподавательских ответах.
