Массовая проверка лабораторных из админки - #53
Open
markpolyak wants to merge 4 commits into
Open
Conversation
markpolyak
force-pushed
the
feature/bulk-grading
branch
from
September 7, 2026 11:45
b864f74 to
fb6dd88
Compare
Grade every student of a group for one lab in a single run, with the same checks as a self-submitted work. Two modes. With a name file, every repository of the lab in the course organization is inspected: the first line of that file gives the student's full name, which resolves their spreadsheet row and records their GitHub username. Without one, only students who already have a username in the sheet are graded. grade_lab mixed the HTTP layer, per-cell Sheets reads and the grading logic in one 240-line function, so the bulk run could not reuse it. grading/bulk.py:evaluate_student now holds that decision and receives what it needs from the spreadsheet through a lazily-invoked provider, so grade_lab still answers repository and CI errors without opening a Sheets connection. Characterization tests pass unchanged. The run is a background job with the same machinery as propagate: an in-memory store under a lock, one job per (course, group, lab), 202 with a job_id, polling and 409 on a second start. The worksheet is read once with get_all_values() and grades are flushed in batches of 10 - the per-cell helpers spend ~6 Sheets requests per student, which for a group of 30 exceeds the 60 reads/minute quota. Adds GitHubClient.get_file_content and grid-based counterparts of the per-cell sheet helpers; reuses the existing list_org_repos. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- tests/test_bulk_grading.py: repo prefix filtering (including the os-task1 / os-task10 collision), name file parsing, name normalization and matching, the GitHub-cell write policy, grid helpers, the shared evaluate_student decision, the job store and the orchestrator (batched writes, dry run, cell protection, cancellation, per-student error isolation, unavailable org repos). - tests/test_github_client.py: get_file_content decoding, BOM, missing file, directory, size limit, non-UTF-8 and encoding "none". - tests/test_admin_endpoints.py: the three bulk routes join PROTECTED_ROUTES, plus 202/409/mode-selection/cancel coverage for the endpoint, and the bulk job store joins the clean_job_store fixture. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The teacher is already picking a course and a lab there for template updates, so the run starts from the same table: a second action per row opens a dialog that asks for the group (labs are per course, the sheet is per group) and the file holding the student's full name, with the lab's required files offered as suggestions and student-name-file preselected. The dialog then shows progress and the per-student report, polling the job every 2s like the propagate dialog does, with a stop button while the run is going. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- docs/PROJECT_DESCRIPTION.md: the two modes, name matching rules, the GitHub-cell write policy, cell protection, dry run, and the job/quota details; the three new routes join the admin table. - docs/COURSE_CONFIG.md: the student-name-file lab key. - CLAUDE.md: where the shared grading decision lives and the endpoints. - docs/BULK_GRADING_PLAN.md: rewritten as a record of the decisions behind the feature rather than a work plan, and trimmed to what PROJECT_DESCRIPTION does not already say. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
markpolyak
force-pushed
the
feature/bulk-grading
branch
from
September 8, 2026 04:32
fb6dd88 to
f8f1cc9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Проверка всей группы по одной лабораторной за один запуск. Разбор принятых решений — docs/BULK_GRADING_PLAN.md, поведение и API — docs/PROJECT_DESCRIPTION.md.
Как это выглядит
Кнопка «Заполнить таблицу» добавлена в строку каждой лабы на существующей странице
/admin/courses/{course_id}/labs— там, где уже запускается рассылка обновлений шаблона. В диалоге выбирается группа (список лаб общий для курса, а лист у каждой группы свой) и, при необходимости, файл с ФИО; дальше — прогресс и отчёт по каждому студенту.Два режима
Переключаются полем «Файл с ФИО студента»:
GitHub, затем работа проверяетсяПодсказки в поле берутся из
filesлабы; новый необязательный ключstudent-name-fileпредзаполняет его.Проверка — тот же пайплайн, что и при самостоятельной отправке студентом: обязательные файлы, workflows, коммиты, запрещённые модификации, CI, номер варианта, баллы из логов, штраф за просрочку.
Ключевые решения
Одно ядро для одиночной и массовой проверки.
grade_lab— 240 строк, где перемешаны HTTP-слой, поячеечное чтение таблицы и логика оценки. Решение об оценке вынесено вgrading/bulk.py:evaluate_student,grade_labпереписан в тонкую обёртку. Контекст таблицы передаётся не значением, а функцией, вызываемой лениво — только когда CI дал результат, который есть смысл записывать. Без этого потерялось бы свойствоgrade_labвозвращать ошибки репозитория и CI, не открывая соединение с Google Sheets. Характеризационные тесты проходят без изменений.Чтение таблицы одним запросом. Поячеечные хелперы тратят ~6 обращений к Sheets API на студента — на группе из 30 это ~180 запросов при квоте 60/мин. Массовый режим читает лист один раз через
get_all_values()и ищет по сетке в памяти; запись — пакетами по 10 ячеек по ходу работы, чтобы падение или отмена не теряли уже проставленные оценки.Сопоставление ФИО — только точное после нормализации (пробелы, регистр,
ё/е). Нечёткий подбор не делается: цена ошибки — оценка не тому студенту. Несопоставленные и неоднозначные ФИО уходят в отчёт с прочитанным значением.Конфликт логинов. Если в строке студента указан другой логин, репозиторий не проверяется ни под одним из них и попадает в отчёт: молча перезаписать логин — потерять факт самостоятельной регистрации, проверить под логином из строки — проверить чужую работу.
Защита оценок
can_overwrite_cellдействует и здесь; флага принудительной перезаписи нет.Пробный запуск выполняет все проверки и строит полный отчёт, не записывая ни оценок, ни логинов.
Фоновая работа повторяет устройство
grading/propagate.py: хранилище в памяти под блокировкой,202сjob_id, опрос статуса,409на повторный запуск для той же тройки курс/группа/лаба. Переиспользованы существующиеrequire_admin,find_lab_config,GitHubClient.list_org_repos; из нового в клиенте GitHub — толькоget_file_content.Новые эндпоинты
/admin/courses/{course_id}/groups/{group_id}/labs/{lab_id}/bulk-grade/admin/bulk-grade-jobs/{job_id}/admin/bulk-grade-jobs/{job_id}/cancelGET /admin/courses/{course_id}/labsдополнен полямиfilesиname_file.Тесты
349 → 461. Новое:
tests/test_bulk_grading.py(фильтрация репозиториев с коллизиейos-task1/os-task10, разбор и сопоставление ФИО, политика записи логина, grid-хелперы, общее ядро проверки, хранилище работ, оркестратор — пакетная запись, пробный запуск, защита ячеек, отмена, изоляция ошибок по студентам, недоступный список репозиториев),get_file_contentвtests/test_github_client.py, а вtests/test_admin_endpoints.pyтри новых маршрута добавлены вPROTECTED_ROUTESплюс покрытие202/409/выбора режима/отмены.Проверка
461 тест, сборка фронтенда, ESLint. Интерфейс проверен вручную в браузере целиком — на backend с заглушками Google Sheets и GitHub прогнаны оба режима: подстановка подсказок файла из конфига, отчёт по группе, статусы «зачтено», «уже проверено», «ошибка», «конфликт логинов», «ФИО не сопоставлено», отметка «логин записан», исключение репозиториев чужого префикса.
🤖 Generated with Claude Code