test: make activity index deterministic
This commit is contained in:
+11
-11
@@ -34,17 +34,17 @@
|
||||
- [x] Добавить удаление пользовательского сообщения администратором с аудитом действия (обезличивание записи, удаление объекта MinIO, миграция `0006`).
|
||||
- [x] Заменить in-memory rate limit на общее хранилище, пригодное для нескольких API-процессов и перезапусков (PostgreSQL, HMAC-отпечаток без хранения исходного IP, миграция `0007`).
|
||||
- [x] Валидировать одновременно содержимое, MIME, расширение и лимит изображения; добавить тесты каждого отказа.
|
||||
- [ ] Добавить сквозной тест: отправка → pending → модерация → появление одобренного улова в публичной статистике.
|
||||
- [ ] Добавить понятные состояния успеха и ошибок загрузки в форму, включая отдельную ошибку скриншота без потери уже созданной заявки.
|
||||
- [x] Добавить сквозной тест: отправка → pending → модерация → появление одобренного улова в публичной статистике (Compose/Playwright: `2 passed`).
|
||||
- [x] Добавить понятные состояния успеха и ошибок загрузки в форму, включая отдельную ошибку скриншота без потери уже созданной заявки (повторная загрузка по ID сохранённой заявки).
|
||||
|
||||
Критерий готовности: полный пользовательский сценарий проходит через браузер, а модератору не нужен ручной вызов API.
|
||||
|
||||
## Этап 4 — индекс клёва
|
||||
|
||||
- [ ] Сверить текущую формулу активности и уверенности с разделом 9 спецификации и зафиксировать формулу в `docs/activity-index.md`.
|
||||
- [ ] Покрыть unit-тестами затухание по свежести, вес официальных и пользовательских источников, дубликаты и вклад разных игроков.
|
||||
- [ ] Не учитывать pending/rejected записи и доказать это тестами.
|
||||
- [ ] Добавить детерминированные агрегаты для окон 6, 12, 24 и 72 часа.
|
||||
- [x] Сверить текущую формулу активности и уверенности с разделом 11 спецификации и зафиксировать формулу в `docs/activity-index.md`.
|
||||
- [x] Покрыть unit-тестами затухание по свежести, доверие к официальным и пользовательским источникам, повторные сообщения одного игрока и вклад разных игроков.
|
||||
- [x] Не учитывать pending/rejected/удалённые записи и доказать это тестами.
|
||||
- [x] Добавить детерминированные агрегаты для окон 6, 12, 24 и 72 часа.
|
||||
- [ ] На карточке и странице точки показывать человекочитаемое объяснение оценки и объём данных, на котором она основана.
|
||||
- [ ] Реализовать и проверить состояния «данных мало», «данных нет», «источник недоступен» и ошибки валидации фильтров.
|
||||
- [ ] Проверить фильтры главной страницы сквозным тестом на desktop и mobile.
|
||||
@@ -75,12 +75,12 @@
|
||||
|
||||
## Ближайший рабочий пакет
|
||||
|
||||
Следующим завершается этап 3:
|
||||
Этап 3 завершён. Следующий пакет продолжает этап 4:
|
||||
|
||||
1. сквозной тест `отправка → pending → модерация → публичная статистика`;
|
||||
2. раздельные состояния успеха, ошибки создания заявки и ошибки загрузки скриншота без потери заявки;
|
||||
3. финальная проверка этапа 3 в Docker и браузере на desktop и 390 px;
|
||||
4. переход к документации и тестам индекса клёва из этапа 4.
|
||||
1. человекочитаемые состояния и объём данных на карточках этапа 4;
|
||||
2. состояния «данных мало», «данных нет», «источник недоступен» и ошибки фильтров;
|
||||
3. сквозная проверка фильтров главной страницы на desktop и mobile;
|
||||
4. переход к health/readiness и структурированным логам подготовки MVP.
|
||||
|
||||
После каждого пункта необходимо:
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Индекс активности RF4 Spotter
|
||||
|
||||
Индекс — сравнительная оценка свежести подтверждённых наблюдений, а не вероятность поклёвки. Он рассчитывается отдельно для комбинации «водоём + точка + рыба» в выбранном окне 6, 12, 24 или 72 часа.
|
||||
|
||||
## Какие данные участвуют
|
||||
|
||||
В расчёт входят только одобренные и не удалённые записи с координатами точки. `pending`, `rejected`, удалённые записи и официальные рекорды без координат не участвуют. Фильтры водоёма, рыбы и способа ловли применяются до агрегации.
|
||||
|
||||
Официальный импорт устраняет точные дубликаты по `source_external_id`. Несколько пользовательских сообщений остаются отдельными наблюдениями, но одинаковое имя игрока после удаления пробелов и приведения регистра учитывается в `unique_players` только один раз. Пустое имя игроком не считается. Поэтому один игрок может увеличить объём и свежесть данных, но не может имитировать несколько независимых источников.
|
||||
|
||||
## Формула активности
|
||||
|
||||
Для каждого сообщения с возрастом `age_hours` и доверием к источнику `source_confidence` от 0 до 100:
|
||||
|
||||
```text
|
||||
freshness_i = exp(-age_hours_i / 18)
|
||||
weighted_reports = sum(freshness_i * source_confidence_i / 100)
|
||||
unique_players = количество уникальных непустых player_name
|
||||
trophy_bonus = min(1, trophy_count / 3)
|
||||
|
||||
activity_score = round(
|
||||
55 * min(1, weighted_reports / 12)
|
||||
+ 25 * min(1, unique_players / 6)
|
||||
+ 20 * trophy_bonus
|
||||
)
|
||||
```
|
||||
|
||||
Период полураспада вклада по свежести равен примерно 12,5 часа. Трофей определяется по порогу `trophy_weight_g` конкретного вида рыбы.
|
||||
|
||||
## Формула уверенности
|
||||
|
||||
```text
|
||||
confidence_score = round(
|
||||
45 * min(1, approved_reports / 10)
|
||||
+ 35 * min(1, unique_players / 5)
|
||||
+ 20 * average_source_confidence / 100
|
||||
)
|
||||
```
|
||||
|
||||
Обе оценки ограничены диапазоном 0–100 составляющими формулы. На карточке рядом с числом показываются число уловов, число уникальных игроков, время последнего подтверждения и пометка «Данных мало» при числе наблюдений меньше трёх.
|
||||
|
||||
## Воспроизводимость и ограничения
|
||||
|
||||
Расчёт использует время сервера в UTC; в тестах опорное время задаётся явно. Граница окна включительна: запись ровно на границе входит в расчёт. Формула соответствует разделу 11 `RF4_MVP_SPEC.md` и должна быть откалибрована после накопления реальных данных. Она пока не выявляет семантически похожие пользовательские сообщения от разных имён и не заменяет модерацию.
|
||||
Reference in New Issue
Block a user