Skip to content

Prod: add legacy project analytics overview - #736

Merged
TThanos3000 merged 1 commit into
masterfrom
prod/project-analytics-overview
Sep 9, 2026
Merged

TThanos3000 merged 1 commit into
masterfrom
prod/project-analytics-overview

Conversation

@TThanos3000

Copy link
Copy Markdown
Collaborator

Основа

  • Exact origin/master base после merge Prod: expose current project application in program detail #735: d47a194894be1664000a9e94ff8dd758164926bf.
  • Exact final head: b6b3f96d84e92df0cc30aa145c11af5b2e9687a8.
  • Semantic production port DEV #721 и #723, без dev → master merge и без cherry-pick.
  • Для completion взят вариант B: только небольшая финальная семантика #724, без его drilldown API.

Namespace и независимые domains

Добавлен один endpoint: GET /programs/<program_id>/project-analytics/.

DEV namespace /manager-overview/ нельзя использовать на production: он уже описывает настоящий Application/Team/Submission/SubmissionExpertAssignment/Evaluation domain. Его view, service, serializer и URL не изменены.

Новые ProjectAnalyticsAPIView, ProjectAnalyticsSerializer и build_project_analytics вынесены в отдельные файлы. Legacy-аналитика использует PartnerProgramUserProfile, PartnerProgramProject, Project, Collaborator, Expert.programs, ProjectExpertAssignment, Criteria и ProjectScore. Production Application/Submission/Evaluation не входят в её расчёты.

Доступ

Переиспользованы production ProgramPermissionMixin, IsAuthenticated, IsAdminOrManagerOfProgram:

  • manager текущей программы / staff / superuser: 200;
  • participant-only / expert-only / manager другой программы: 403;
  • anonymous: 401; unknown program: 404;
  • POST/PUT/PATCH/DELETE авторизованного manager: 405; стандартные HEAD/OPTIONS сохранены.

Контракт B5

Пример пустой программы; здесь показана одна строка activity для краткости, API всегда возвращает 30 календарных дней:

{
  "summary": {
    "participants": {
      "total": 0
    },
    "projects": {
      "total": 0
    },
    "experts": {
      "total": 0
    },
    "regions": {
      "total": 0,
      "items": []
    },
    "participant_regions": {
      "total": 0,
      "items": []
    }
  },
  "participant_funnel": {
    "registrations": 0,
    "unique_participants": 0,
    "with_team": 0,
    "project_creators": 0,
    "submitted_project_creators": 0
  },
  "solution_funnel": {
    "created": 0,
    "not_submitted": 0,
    "submitted": 0,
    "evaluated": 0
  },
  "evaluation_status": {
    "mode": "open",
    "max_evaluations_per_project": null,
    "assignments": {
      "total": 0,
      "pending": 0,
      "evaluated": 0
    },
    "projects": {
      "submitted": 0,
      "awaiting_evaluation": 0,
      "partially_evaluated": 0,
      "evaluated": 0
    }
  },
  "attention": {
    "participants_without_team": 0,
    "projects_awaiting_evaluation": 0
  },
  "activity": [
    {
      "date": "2026-06-15",
      "registrations": 0,
      "submitted_solutions": 0
    }
  ]
}

Serializer явно проверяет integer counters >= 0, mode open/distributed, nullable max >= 1, region name/count и activity date/counters. Serializer не выполняет SQL.

Семантика метрик

  • summary.participants.total: distinct non-null зарегистрированные user_id текущей программы.
  • summary.projects.total: число текущих PartnerProgramProject links, не глобальных заявок Application. Project в A/B считается отдельно по соответствующей связи.
  • summary.experts.total: authoritative Expert.programs membership, включая экспертов без assignments.
  • Project regions: Project.region, count distinct project.
  • Participant regions: User.city, та же популяция distinct registered users, что participants.
  • Регионы: исключены null/empty/whitespace-only, внешние пробелы удаляются, exact strings группируются и сортируются count DESC/name. Никакой нормализации spelling/case или city → region mapping. Unicode trim обрабатывает также табы/переносы/NBSP; данные в БД не меняются.

Participant funnel: registrations считает profile rows, включая deleted-user rows. Остальные counters считают distinct registered users. with_team требует leader/Collaborator текущей program-linked Project; profile.project сам по себе, foreign-program team и production TeamMember не подходят. project_creators/submitted_project_creators считают лидеров хотя бы одной соответствующей связи.

Solution funnel считает program links: created / submitted=false / submitted=true. Attention содержит только participants_without_team и projects_awaiting_evaluation (awaiting + partial).

Open / Distributed evaluation

max_project_rates возвращается как nullable max_evaluations_per_project: это MAX LIMIT, не required target.

Open: первая ProjectScore по критерию текущей программы для сданной связи означает evaluated, assignment не обязателен.

Distributed: считаются только ProjectExpertAssignment текущей программы. Assignment завершён, когда current-program link submitted, критерии непусты и назначенный эксперт имеет scores по всем текущим критериям, включая автоматически создаваемый production-критерий «Комментарий». Не при первой score. Добавление критерия может снова сделать assignment pending. Считается наличие score rows, не truthiness значений.

Без назначений / без completed assignments: awaiting. Часть completed: partial. Все completed: evaluated, даже если назначений меньше maximum limit. Zero criteria и unsubmitted link не завершают assignment. Scores другого эксперта/проекта/программы не учитываются. Assignment counters используют эту семантику в обоих режимах; open project status по-прежнему определяется первой score. Production Evaluation model не используется.

Activity

Непрерывные 30 локальных календарных дней, от today - 29 до today включительно, с нулевыми днями. Registrations берутся из profile.datetime_created; submitted_solutions из submitted links.datetime_submitted. Проверены active timezone и границы local day, null timestamps, future/out-of-window и foreign-program события.

Совместимость и isolation

  • URL reverse/resolve двух namespace ведёт к разным view classes.
  • Production-only fixture с Application/Team/Submission/SubmissionExpertAssignment/Evaluation оставляет legacy project counters нулевыми. Регистрации и Expert.programs являются общей исходной популяцией.
  • Legacy-only Project/ProjectScore не меняют production manager-overview.
  • При наличии обоих domains старый manager response побайтово неизменен; добавление production entities при неизменной общей популяции не меняет legacy response.
  • До изменений и на final head отдельно снят response snapshot старого manager-overview на одном и том же complete fixture: byte-for-byte equal, SHA-256 7db60065e9b3f762dbc8b87a5c3f3facf9ee8f08410a6b2bb733cc710239a205.
  • Multi-program тесты проверяют независимые project counts/submitted/regions, team membership, foreign criteria scores и assignments.
  • Service test подтверждает отсутствие SQL к production domain tables; приватное содержимое пользователя/проекта в агрегаты не попадает.

SQL budget

Измерение HTTP GET с реальной production permission stack и force-authenticated manager:

Participants / Projects / Regions / Assignments Open Distributed
0 / 0 / 0 / 0 10 10
1 / 1 / 1 / 1 10 10
31 / 31 / 31 / 31 10 10

Service отдельно: 8 SELECTs. SQL count фиксирован, нет N+1 и project-list/pagination scan. Коррелированные subqueries и агрегаты работают в SQL; объём обработанных строк/region results и память естественно растут с данными, constant memory не заявляется.

Scope

Шесть файлов: три новых runtime modules, отдельный regression test module (29 tests), документация docs/project-analytics-api.md, один additive URL.

Не изменены #732 project access/expert filters, #733 case lifecycle/canonical fields/admin freeze, #734 submission metadata, #735 current_project_application. Не изменены apply/submit, fields GET/PUT, permissions, models/migrations, Angular/React, dependencies, Docker/workflows/deploy.

B6 не включён: нет delayed_experts, cases, projects_not_submitted object, assignments/score drilldowns или attention detail endpoints.

Проверки exact final head

Локально Windows / Python 3.11.15 / PostgreSQL 18.1, существующее окружение и отдельная test DB; зависимости не менялись.

  • Snapshot base → head: PASS, отдельный тест, response bytes совпали.
  • Targeted new analytics + manager overview + Application/Submission/Evaluation + project_rates + Prod: harden program project access and expert filters #732-Prod: expose current project application in program detail #735: 572 tests, OK, exit 0.
  • manage.py test projects partner_programs --verbosity 1 --noinput: 841 tests, OK, exit 0.
  • Full backend manage.py test --verbosity 1 --noinput --keepdb: 1410 tests, OK, exit 0; выделенная test DB удалена после окончания процесса.
  • manage.py check: PASS.
  • manage.py check --tag models: PASS.
  • manage.py makemigrations --check --dry-run: PASS, No changes detected.
  • Scoped Black всех пяти изменённых Python-файлов: PASS.
  • Full Flake8: PASS.
  • git diff --check HEAD^ HEAD: PASS.
  • Ordinary CI, 34385081756: SUCCESS: Lint + Tests; 1410 tests, OK (skipped=5), exact head выше.
  • PostgreSQL CI, 34385083340: SUCCESS: 86 locking/constraint tests + 1410 full-suite tests, без skips, workflow_dispatch feature-ветки на том же exact head.

Draft только для review. Merge и deploy не выполнялись.

@TThanos3000
TThanos3000 marked this pull request as ready for review September 9, 2026 20:11
@TThanos3000
TThanos3000 merged commit f0fd3e6 into master Sep 9, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants