89 lines
15 KiB
Markdown
89 lines
15 KiB
Markdown
# План: сравнение двух запусков в разделе «Аналитика»
|
||||
|
|
|
|||
|
|
Статус: реализовано, см. [итог](2026-10-04_10-08_analytics-run-compare-summary.md).
|
|||
|
|
|
|||
|
|
## 1. Что нужно
|
|||
|
|
|
|||
|
|
Администратор выбирает два завершённых запуска и видит динамику между ними по семи индикаторам страницы «Аналитика»: `pass`, `partial`, `fail`, «Egress https: есть провалы», «Egress https: все провалены», «Ingress ssh: есть провалы», «Ingress ssh: все провалены». Отдельно выделяются три группы адресов:
|
|||
|
|
|
|||
|
|
1. **Новые**: есть в новом запуске, в старом не было. Нужны, чтобы понять, какие адреса прибыли в проект и подключились к анализу.
|
|||
|
|
2. **Изменившиеся**: есть в обоих запусках, но состояние по проверкам разное. Для них показывается подробная сводка, что именно изменилось.
|
|||
|
|
3. **Выбывшие**: были в старом запуске, в новом их нет. Нужны, чтобы понять, какие адреса вышли из состава проекта.
|
|||
|
|
|
|||
|
|
Адреса, которые есть в обоих запусках и не изменились, только считаются (список доступен, но не выделяется).
|
|||
|
|
|
|||
|
|
## 2. Основные решения
|
|||
|
|
|
|||
|
|
- **Отдельная страница `/analytics/compare`**, а не режим текущей: страница одного запуска остаётся как есть («данные других запусков на странице не участвуют»). На `/analytics` добавляется кнопка «Сравнить с другим запуском», она ведёт на сравнение с текущим запуском в роли нового.
|
|||
|
|
- **Выбор запусков.** Два списка: «Запуск A (старый)» и «Запуск B (новый)» и кнопка «Поменять местами». По умолчанию B — последний завершённый запуск, A — предыдущий. Порядок не навязывается: «новые» всегда означает «есть в B, нет в A». Доступны только завершённые запуски с адресами (как в текущем выборе). Одинаковые A и B — ошибка `400`.
|
|||
|
|
- **Адрес — это IP.** В запуске он присутствует, если у него есть итог, отличный от `cancelled`. Отменённые адреса из сравнения исключаются (в обоих запусках), их число показывается примечанием, чтобы они не выглядели как «новые» или «выбывшие».
|
|||
|
|
- **Состояние адреса** — принадлежность к семи индикаторам по последнему циклу адреса в запуске (те же правила, что в отчёте запуска; один адрес может входить в несколько, вердикт — ровно в один из трёх). Адрес **изменился**, если принадлежность хотя бы к одному индикатору в A и B разная. Адрес с теми же индикаторами, но другим набором проваленных целей или площадок считается неизменившимся (см. риски).
|
|||
|
|
- **Расчёт на control-api** по двум уже существующим `Analysis` (кэш завершённых запусков переиспользуется). Новых таблиц, миграций и новых данных нет.
|
|||
|
|
|
|||
|
|
## 3. Решение
|
|||
|
|
|
|||
|
|
### 3.1. control-api (`internal/analytics/compare.go`, новый)
|
|||
|
|
|
|||
|
|
- Таблица семи индикаторов (ключ, название, предикат по адресу): ключи совпадают с видами списков `verdict_pass`, `verdict_partial`, `verdict_fail`, `egress_https_any`, `egress_https_all`, `ingress_ssh_any`, `ingress_ssh_all`. Предикаты повторяют условия `Compute`/`List`; тест проверяет, что число адресов по каждому индикатору в каждом запуске равно соответствующему полю `summary`.
|
|||
|
|
- `Compare(a, b *Analysis) *Comparison`:
|
|||
|
|
- группы: `new`, `left`, `common`, `changed`, `same` (числа; `common = changed + same`);
|
|||
|
|
- по каждому индикатору: `base`, `target`, `delta`, `new` (новые адреса в индикаторе), `left` (выбывшие, были в индикаторе), `entered` (общие адреса, вошедшие в индикатор), `exited` (общие адреса, вышедшие из индикатора). Инвариант: `delta = new − left + entered − exited` (проверяется тестом);
|
|||
|
|
- матрица переходов вердикта для общих адресов (3×3), плюс строка «нет в A» (новые) и столбец «нет в B» (выбывшие);
|
|||
|
|
- число отменённых адресов в каждом запуске.
|
|||
|
|
- `(*Comparison) List(group, filter)`: таблица адресов. Группы: `new`, `left`, `changed`, `same`, `entered`, `exited`. Фильтры: `indicator` (для `new`/`left` — адрес входит в индикатор в своём запуске; для `changed`/`same` — принадлежность этому индикатору; для `entered`/`exited` обязателен), `from`+`to` (вердикт в A и в B, для ячейки матрицы). Порядок — по числовому адресу. Неверная группа или индикатор — ошибка (`404`).
|
|||
|
|
- Столбцы списков:
|
|||
|
|
- `new`: Адрес, Подсеть, Вердикт, Egress, Ingress, Индикаторы (в B);
|
|||
|
|
- `left`: те же столбцы по запуску A;
|
|||
|
|
- `changed`, `entered`, `exited`, `same`: Адрес, Подсеть, Вердикт (A → B), Egress (A → B), Ingress (A → B), **Что изменилось**. «Что изменилось» — текст по шагам: «вердикт partial → pass», «вошёл в: Egress https: все провалены», «вышел из: Ingress ssh: есть провалы», «https: провалены цели +a.test −b.test», «ssh: площадки −rxmsk», «валидатор v3 → v12». У `same` — «без изменений».
|
|||
|
|
- Для текста нужны из `addr` уже имеющиеся поля (`https.failedTargets`, `ssh.sites`, `https.validator`, статистики egress/ingress); новых вычислений в `Compute` нет.
|
|||
|
|
|
|||
|
|
### 3.2. API (`internal/httpapi/handlers_analytics.go`)
|
|||
|
|
|
|||
|
|
- `GET /api/v1/admin/analytics/compare?base=A&target=B` → отчёт сравнения (`runs`: сведения об обоих запусках, `groups`, `indicators`, `transitions`, `cancelled`).
|
|||
|
|
- `GET /api/v1/admin/analytics/compare/lists/{group}?base=A&target=B[&indicator=…][&from=…&to=…][&format=csv]` → таблица или CSV (UTF-8 с BOM; имя `compare_<group>[_<indicator>]_run<A>-<B>.csv`).
|
|||
|
|
- Ошибки: `400` (нет или неверные id, `base = target`), `404` (запуска нет, неизвестная группа/индикатор), `409` (запуск ещё идёт).
|
|||
|
|
- Загрузка `Analysis` по id выносится из `analysisFor` в общую функцию, чтобы сравнение и страница одного запуска пользовались одним кэшем.
|
|||
|
|
|
|||
|
|
### 3.3. Дашборд
|
|||
|
|
|
|||
|
|
- Страница `/analytics/compare?base=A&target=B` (`handlers_analytics.go`, `templates/analytics_compare.html`, `static/analytics-compare.js`), прокси списков и CSV: `/analytics/compare/lists/{group}` и `/analytics/compare/csv/{group}`. Состояние страницы целиком в адресе (можно отправить ссылку).
|
|||
|
|
- Содержимое страницы сверху вниз:
|
|||
|
|
1. Выбор A и B, «Поменять местами», «Сравнить»; под ним примечание: даты, тип и размер обоих запусков, число отменённых.
|
|||
|
|
2. Карточки: «Новые», «Выбывшие», «Общие», «Изменились», «Без изменений». Кликабельны, открывают диалог со списком.
|
|||
|
|
3. «Динамика по индикаторам»: строка на индикатор, столбцы A, B, Δ (рост `pass` и падение остальных — зелёным, обратное — красным), Новые, Выбывшие, Вошли, Вышли. Ненулевые числа кликабельны (список с соответствующими `group` и `indicator`).
|
|||
|
|
4. «Переходы вердикта»: матрица 3×3 по общим адресам + «нет в A» / «нет в B»; ячейки кликабельны.
|
|||
|
|
5. Диалог со списком, «Скачать CSV», «Копировать» — тот же, что на странице запуска.
|
|||
|
|
- Код диалога (открытие, заполнение, CSV, копирование, подсказки) выносится из `analytics.js` в общий `analytics-dialog.js`, разметка диалога — в общий шаблон; страница одного запуска переходит на него без изменения поведения.
|
|||
|
|
- На `/analytics` добавляется кнопка «Сравнить с другим запуском».
|
|||
|
|
|
|||
|
|
## 4. Файлы
|
|||
|
|
|
|||
|
|
- `internal/analytics/compare.go` (новый), `internal/analytics/compare_test.go` (новый).
|
|||
|
|
- `internal/httpapi/handlers_analytics.go` (общий загрузчик, два новых обработчика), `routes`.
|
|||
|
|
- `internal/dashboard`: `handlers_analytics.go` (страница и прокси), `client.go`, `routes.go`, `templates/analytics_compare.html`, `templates/analytics.html` (кнопка, общий диалог), `static/analytics.js`, `static/analytics-dialog.js` (новый), `static/analytics-compare.js` (новый), `static/analytics.css`.
|
|||
|
|
- Документы: `docs/API.md`, `docs/USAGE.md` («Аналитика запусков»), `docs/DASHBOARD.md`, `README.md`, итог `docs/changes/…-summary.md`.
|
|||
|
|
|
|||
|
|
## 5. Тесты
|
|||
|
|
|
|||
|
|
- `analytics`: два запуска с новыми, выбывшими, изменившимися и неизменившимися адресами, с отменёнными; числа групп; инвариант `delta = new − left + entered − exited` по каждому индикатору; число адресов по индикатору равно `summary` каждого запуска; матрица переходов; списки каждой группы и фильтры (`indicator`, `from`/`to`); текст «Что изменилось» (вердикт, вход/выход из индикатора, цели, площадки, валидатор); порядок по адресу; пустые группы.
|
|||
|
|
- `httpapi`: отчёт и списки (JSON, CSV с именем файла), `400` на одинаковые и неверные id, `404` на неизвестные запуск/группу/индикатор, `409` на незавершённый запуск.
|
|||
|
|
- `dashboard`: страница с запуском по умолчанию, с явным `base`/`target`, предупреждение на неверные id, прокси списка и CSV; страница одного запуска после выноса диалога по-прежнему отдаёт данные и скрипты.
|
|||
|
|
- `gofmt -l`, `go build ./... && go vet ./... && go test ./...`, проверка синтаксиса JS.
|
|||
|
|
|
|||
|
|
## 6. Выкладка и проверка
|
|||
|
|
|
|||
|
|
Меняются `control-api` и `admin-dashboard` (статика встроена); агенты, prober, миграции без изменений. Порядок: проверка пустой очереди, тег отката образов, пересборка, перезапуск. Проверка на стенде: сравнение запусков 1 и 2 через API (сумма групп равна числу адресов, инвариант по индикаторам), затем страница в браузере: выбор запусков, обмен местами, открытие диалогов из карточек, таблицы и матрицы, CSV, а также что страница одного запуска работает как раньше.
|
|||
|
|
|
|||
|
|
## 7. Риски
|
|||
|
|
|
|||
|
|
- **Что считается изменением.** Только принадлежность к семи индикаторам. Адрес, который остался в «Egress https: есть провалы», но у которого поменялись проваленные цели, попадает в «без изменений»; в списке `same` его сводка «без изменений» это не показывает. Если такие случаи важны, следующим шагом можно считать изменением и смену наборов целей и площадок.
|
|||
|
|
- **Размер списков.** Группа новых или `same` может содержать тысячи строк (диалог показывает все, как и другие списки); CSV полный.
|
|||
|
|
- **Разный состав запусков.** Если запуск 1 был частичным (например, перепроверка), то в сравнении будет много «новых» и «выбывших»: это отражает состав запусков, а не динамику. Размеры обоих запусков показаны в примечании над таблицами.
|
|||
|
|
- Вердикт — оценка системы при агрегации, а не итог всех проверок (см. «Качество данных»); сравнение вердикта это не меняет, столбцы Egress и Ingress показывают фактические проверки.
|
|||
|
|
- Вынос кода диалога затрагивает работающую страницу; закрывается тестами и ручной проверкой п. 6.
|
|||
|
|
|
|||
|
|
## 8. Не входит в доработку
|
|||
|
|
|
|||
|
|
Сравнение более чем двух запусков, графики динамики по времени, разбивка новых и выбывших по подсетям, сравнение по другим показателям (классы ошибок, площадки, валидаторы), автоматический выбор «интересных» пар запусков.
|
|||
|
|
|
|||
|
|
Открытых вопросов нет. Жду команды начать реализацию.
|