Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions docs/modules/prod-program-analytics-project-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# PROD: выборочный перенос аналитики и стандартной обложки

База: `4c49f5918e571708f872c532b1f5154fc2a03428`.
Ветка: `release/prod-program-analytics-project-cover`.

Перенесены только DEV merge commits:

- #742: `8ed5670d2123dd1f1b53668567ad60bde8641bcc`;
- #743: `dd3d48b121a1fd0668857c9b8766bb588be2360b`.

## Различия с DEV и сохранение PROD

Конфликт detail программы разрешён вручную: существующий
`current_project_application` сохранён с прежней семантикой лидера.
DEV-поле `current_application` не добавлено и публичный контракт не переименован.
Из #742 добавлен `is_user_expert`, необходимый для отображения секции эксперту
без member-роли. Это добавление вызвало описанное ниже падение строгого
регрессионного теста набора полей ответа.

PROD уже использует `project_analytics` и `project_assignment_analytics`.
Новый сервис виджета вызывает существующие `_participant_metrics`,
`participants_without_team_rows`, `_annotated_solution_rows` и
`annotated_assignment_queryset`. Количество проектов и отправленных решений
считается SQL aggregate по тем же связям программы; большая аналитика не
сериализуется. Дополнительные DEV commits не переносились.

Endpoint reset-cover использует существующие permissions ProjectDetail.
Глобальное удаление файлов, модели, миграции, правила оценки и сдачи,
React-контракты, зависимости, CI/Docker/workflows не менялись.

## Локальные проверки 18.09.2026

Python 3.11, PostgreSQL 18 на localhost, изолированные временные БД.
Рабочие DEV/PROD БД не использовались. Настройки основаны на
`procollab.settings_ci`, включён NEXTGEN_SURFACE_ENABLED для регрессии контрактов.

| Проверка | Результат |
| --- | --- |
| Targeted: widget, reset-cover, Project permissions, program detail/fields, legacy analytics, оценивание | 200 тестов, OK, 57,708 с |
| `python manage.py test --verbosity 1 --keepdb` | 1554 теста, failures=3, skipped=3, 811,056 с, exit 1 |
| `python manage.py check` | 0 issues |
| `python manage.py check --tag models` | 0 issues |
| `python manage.py makemigrations --check --dry-run` | No changes detected |
| Black изменённых Python-файлов | PASS |
| flake8 всех tracked Python-файлов | PASS |
| `git diff --check` | PASS |

Все три падения полного прогона — subtests
`ProgramCurrentProjectApplicationTests.test_existing_production_detail_contract_is_preserved`:
анонимный пользователь, посторонний пользователь и участник-организатор.
Тест сравнивает точный набор полей detail с serializer и не ожидает добавленный
`is_user_expert: false`. Сам `current_project_application` исключён из этого
сравнения; его selector и семантика не менялись. Не следует трактовать это
падение как доказательство изменения прав или выбора проекта.

Тест не ослаблялся, полный прогон не объявляется зелёным. По STOP-условию задачи
релиз остановлен до PR, snapshot, merge и deployment. Angular full suite также
завершился с exit 1 из-за необработанного teardown-таймера ngx-autosize.

## Порядок будущего релиза

После устранения блокеров и нового полного зелёного прогона нужны отдельные
PROD PR и свежий PROD snapshot. Backend развёртывается первым, Angular после
подтверждённой работоспособности новых API. Этот документ не подтверждает
развёртывание и не заменяет фактические runtime receipt и smoke.
99 changes: 99 additions & 0 deletions docs/modules/program-role-widget.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Read-only виджет программы для Angular

PROD-port backend #742 основан на master `4c49f5918e571708f872c532b1f5154fc2a03428`.
Сохранены namespace `project-analytics` и поле `current_project_application`.
Отсутствующие на PROD DEV-имена helpers не добавляются как копии: виджет использует
существующие селекторы legacy Project; React manager-overview не участвует.
Результаты нового production-прогона фиксируются в PR; исторические DEV-проверки
ниже не заменяют проверку PROD-ветки.

## Контракт

`GET /programs/{id}/analytics-widget/`, обязательная аутентификация.
Параметры `role`, `user_id` и `project_id` не используются для авторизации или выбора проекта.
GET не изменяет бизнес-данные. Ответ содержит только ветвь разрешённой роли:

```json
{
"program_id": 12,
"role": "participant",
"is_competitive": true,
"participant": {
"participant_project": { "id": 5, "name": "StudyFlow", "program_link_id": 34 },
"case_provided": true,
"case_name": "Цифровой сервис",
"stage": "review",
"submission_open": true
}
}
```

Пример иллюстрирует форму, значения не являются данными DEV. Остальные ветви:

- `organizer`: `participants`, `projects`, `submitted_solutions`, `participants_without_project`.
- `expert`: `mode` (`distributed` / `open`), `assigned`, `remaining`, `evaluation_ends` (ISO datetime либо null).
- `participant_project` и `case_name` могут быть null. `stage`: `none`, `not_submitted`, `submitted`, `review`, `evaluated`, `not_applicable`.
- Неприменимые счётчики — null: open assigned/remaining; неконкурсные submitted_solutions и персональные distributed-счётчики. Реальный срок оценивания передаётся также для open.

401 — нет входа; 403 — нет роли в этой программе; 404 — программа отсутствует; 409 — противоречивые связи команды, не выбор первого проекта. Angular не отображает raw response body и не подменяет ошибку нулями.

## Источники и переиспользование

| Показатель | Источник и правило | Права / изменение |
| --- | --- | --- |
| Роль | `program.is_manager(user)` → `program.experts` → `PartnerProgramUserProfile` | Только текущая программа; global staff/user_type, наличие назначений и клиентские claims не дают роль |
| Проект команды | PartnerProgramProject текущей программы; Project.leader либо Exists Collaborator | Новый локальный selector; максимум две строки для обнаружения несоответствия правилу одной команды. Один проект без выбора |
| Кейс | `get_program_case_field`: точный name="case"; PartnerProgramFieldValue текущей связи | Нет fallback по label, первому option, глобальному Project или другой программе |
| Участники и без проектов | `_participant_metrics` и `participants_without_team_rows` из `project_analytics` | Distinct user; без проекта = нет лидерства и Collaborator на проекте программы; черновик учитывается |
| Проекты и решения | SQL aggregate над `_annotated_solution_rows` из `project_analytics` | Единица учёта — PartnerProgramProject; отправленные — submitted-связи, не пользователи. Большая аналитика не сериализуется |
| Этап проекта | `_annotated_solution_rows` основной legacy-аналитики | Неподанный → not_submitted; submitted без начала → submitted; назначение/начатая оценка → review; общая итоговая классификация → evaluated |
| Остаток эксперта | `annotated_assignment_queryset`, фильтр текущих program/expert, SQL aggregate | Незавершённые назначения, включая not_ready, а не число незаполненных критериев |
| Срок | `PartnerProgram.datetime_evaluation_ends` | Не срок подачи или окончания программы |

Distributed completion остаётся общим: submitted-связь, ненулевой набор критериев и заполнение всех критериев назначения; для проекта должны завершиться все назначения. Записанная допустимая оценка 0 считается заполненной. Неотправленный проект не завершён даже при существующих оценках.

В open сохранено существующее правило основной аналитики: ProjectScore по критерию программы даёт проекту evaluated. Это не новая формула виджета. Назначения и персональный остаток в open не выдумываются; frontend сохраняет переход в оценивание и однократно показывает реальный срок.

Дедлайн не участвует в определении оценённости. Баллы, личности экспертов и закрытые результаты участнику не сериализуются. Неконкурсная программа не получает искусственную обязательную сдачу.

## Изоляция контрактов

Используются только Angular-модели Project, Collaborator, PartnerProgramProject, PartnerProgramUserProfile, Expert, ProjectExpertAssignment, Criteria, ProjectScore и существующие поля программы.

На PROD существующее поле `current_project_application` остаётся legacy-контрактом лидера с прежней семантикой. DEV-имя `current_application` не добавляется и не переименовывает PROD-контракт. Для члена команды null в этом поле не влияет на новый `participant_project`. Просмотр виджета не даёт права редактировать поля или отправлять проект вместо лидера.

React-модели Application, Team, Submission, SubmissionExpertAssignment, Evaluation, их serializers/services/endpoints не используются и не изменяются. Менеджерские API не открываются другим ролям.

В существующий program detail добавлен только контекстный boolean `is_user_expert`; он не раскрывает description/links незарегистрированному эксперту. Правила регистрации, выбора/сохранения case, сдачи, назначений, завершения и публикации сохранены. Нет миграций, зависимостей или изменений CI/Docker/workflows.

Сервис использует общие SQL-агрегации. Не сериализует большую аналитику и не загружает проекты/регистрации в браузер. Тест проверяет постоянное число SQL-запросов при росте числа проектов и отсутствие INSERT/UPDATE/DELETE.

## Исторические проверки DEV 14.09.2026

Python 3.11, существующее Poetry-окружение. Локальные SQLite и PostgreSQL 18; production/DEV базы не использовались.

```sh
DEBUG=True python manage.py test partner_programs.tests.test_role_widget_api partner_programs.tests.test_program_detail partner_programs.tests.test_program_link_fields_api partner_programs.tests.test_manager_analytics_api partner_programs.tests.test_assignment_analytics_api project_rates.tests --noinput
```

Targeted: 120 тестов PASS. Новые 15 тестов проверяют роли и приоритет, эксперта без регистрации/назначений, чужую программу, manager-only API, лидера/члена команды, несколько программ с разными case/status, неизменность current_application и прав сдачи, integrity 409, точное поле case, distributed/open, 0/partial/full/not_ready, черновики и командную метрику, изоляцию назначений, реальный дедлайн, bounded SQL/read-only.

Уникальность регистраций уже обеспечена схемой: тест проверяет отклонение дубликата и единственный учёт пользователя. Ограничения БД ради теста не менялись; общая аналитика сохраняет Count(distinct user).

| Команда | Фактический результат |
| --- | --- |
| `DEBUG=True python manage.py test --noinput` (SQLite) | 849 тестов, OK (skipped=4), 570,439 с. После тестов exit 1: WinError 32 при удалении test_db.sqlite3 |
| `python manage.py test --noinput` с временным settings на основе `procollab.settings_ci` (PostgreSQL) | 849 тестов, 1 failure: `feed.tests.test_feed_api.FeedAPITests.test_feed_returns_project_news_as_news_content`, ожидалось news, получено project. Teardown также встретил 21 незакрытое соединение; после завершения процесса временная БД удалена |
| Тот же feed-тест отдельно на исходном `ed5244bd...`, PostgreSQL | PASS. Причина сбоя полного прогона не установлена; полный suite не объявляется успешным, feed-код не менялся |
| `python -m flake8` всех tracked Python и отдельно новых файлов | PASS |
| `python -m black --check` новых файлов и urls | PASS |
| `DEBUG=True python manage.py check` | 0 issues |
| `DEBUG=True python manage.py makemigrations --check --dry-run` | No changes detected |
| `python -m mypy partner_programs/services/role_analytics.py partner_programs/serializers/role_analytics.py partner_programs/widget_views.py` | Не выполнен typecheck: существующий mypy.ini объединяет две строки plugins в один import (`mypy_django_plugin.main\nmypy_drf_plugin.main`), exit 2. Конфигурация не менялась |
| `git diff --check` | PASS |

Скриншоты трёх ролей/нулевых состояний и сравнение исходной геометрии находятся в парном Angular PR, `docs/program-role-widget/README.md`. Браузерный smoke использует реальные Angular-компоненты и локальные fixtures; живой DEV вход/сдача/оценивание в браузере не проверялись.

## Зависимость развёртывания

Сначала backend-контракт и detail-флаг, затем Angular-виджет. Изменение read-only, миграций нет. Фактическое состояние релиза и новый полный прогон PROD-ветки фиксируются отдельно в PR и отчёте развёртывания.
Loading
Loading