Изменение логики любимых и основных фракций #17

Closed
opened 2026-09-07 14:59:03 +03:00 by NotBigGhost · 2 comments
Owner

Изменение логики:
Любимые фракции: самая частая фракция, выбираемая игроком -> личный выбор игрока в меню кастомизации профиля из выпадающего списка. Выпадающий список из шаблонов сайта.
Основная фракция: Переименовать в "Чаще всего играет на", логика подбора - частота по всей истории игр независимо от условий выбора фракции, сама фракция в предложном падеже (необходимо составить словарь).

Изменение логики: Любимые фракции: самая частая фракция, выбираемая игроком -> личный выбор игрока в меню кастомизации профиля из выпадающего списка. Выпадающий список из шаблонов сайта. Основная фракция: Переименовать в "Чаще всего играет на", логика подбора - частота по всей истории игр независимо от условий выбора фракции, сама фракция в предложном падеже (необходимо составить словарь).
NotBigGhost added this to the v1.35 - промежуточная полировка версии 1.3 milestone 2026-09-07 14:59:03 +03:00
NotBigGhost added the Kind/Enhancement label 2026-09-07 14:59:15 +03:00
Agent self-assigned this 2026-09-07 16:04:44 +03:00
Agent added the
Reviewed
Confirmed
1
label 2026-09-07 16:04:45 +03:00
NotBigGhost added reference dev 2026-09-07 16:07:54 +03:00
Collaborator

План выполнения

Находка, которая упрощает половину задачи: логику «основной фракции» менять не нужно. В stats_service.py она уже считается как самая игранная по всем партиям, включая рандомные раздачи, — ровно как требует ТЗ. Меняются только подпись и падеж.

Основная работа — превращение «любимой фракции» из вычисляемой в поле профиля.

Бэкенд

  1. models.py — User.favorite_faction_id, FK на factions с ON DELETE SET NULL; миграция 0010_user_favorite_faction.py по идемпотентному шаблону 0009.
  2. services/faction_service.py — словарь предложного падежа code → форма с fallback на именительный, чтобы фракция, добавленная админом мимо словаря, не роняла вывод: Орках, Ультрамаринах, Эльдарах, Хаоситах, Имперской гвардии, Тау, Некронах, Тиранидах, Инквизиции, Сёстрах битвы, Друкхари, Адептус Механикус.
  3. services/user_service.py — update_favorite_faction рядом с update_bio: null очищает выбор, несуществующий id → ошибка валидации.
  4. schemas/api.py — UserRead.favorite_faction_id, ProfileUpdate.favorite_faction_id, FactionStat.name_ru_prepositional; в ProfileStats most_played_faction заменяется на favorite_faction.
  5. routers/users.py — update_my_profile переводится на model_dump(exclude_unset=True): сейчас он всегда пишет bio, и без этого сохранение одного «О себе» обнуляло бы выбранную фракцию.
  6. services/stats_service.py — убрать вычисление старой «любимой» вместе со служебным chosen_games, отдавать favorite_faction из профиля.

Публичный профиль правок не требует — PublicProfile.stats это ProfileStats, новое поле приедет туда само.

Фронтенд
7. hooks/reference.ts — useFactions() для уже существующего GET /api/factions.
8. hooks/auth.ts — useUpdateFavoriteFaction() по образцу useUpdateBio.
9. pages/AccountPage.tsx — блок «Любимая фракция» на готовом PickerSelect, все 12 фракций. Компонент не умеет группирующие заголовки, поэтому дополнение показываю прямо в строке опции приглушённым текстом (Орки · Базовая игра) — без правки общего компонента; сброс — кнопкой «Убрать» рядом.
10. components/ProfileStatsCard.tsx — «Любимая фракция» из нового поля, «Основная фракция» → «Чаще всего играет на» в предложном падеже.
11. npm run gen:api — контракт меняется, схема пересобирается с живого бэкенда.

Тесты. Два теста в test_profile.py проверяют отменяемую логику — их проверки main_faction сохраню, утверждения про most_played_faction уберу. Добавлю тесты на установку, сброс, отказ на несуществующем id и на то, что сохранение «О себе» не сбрасывает фракцию.

Критерии готовности

  • Любимая фракция задаётся из списка всех 12 фракций, сохраняется, сбрасывается и переживает сохранение «О себе».
  • «Чаще всего играет на» показывает самую игранную по всей истории фракцию в предложном падеже — одинаково на своём и чужом профиле.
  • Фракция вне словаря выводится в именительном, без ошибки.
  • alembic upgrade head проходит на существующей БД и идемпотентен на свежей.
  • pytest и npm run build зелёные.

Ветка: issue-17-faction-favorite-and-most-played от dev (поле ref у задачи пустое — базовую ветку подтвердили отдельно)

## План выполнения **Находка, которая упрощает половину задачи:** логику «основной фракции» менять не нужно. В `stats_service.py` она уже считается как самая игранная по всем партиям, включая рандомные раздачи, — ровно как требует ТЗ. Меняются только подпись и падеж. Основная работа — превращение «любимой фракции» из вычисляемой в поле профиля. **Бэкенд** 1. `models.py` — `User.favorite_faction_id`, FK на `factions` с `ON DELETE SET NULL`; миграция `0010_user_favorite_faction.py` по идемпотентному шаблону `0009`. 2. `services/faction_service.py` — словарь предложного падежа `code → форма` с fallback на именительный, чтобы фракция, добавленная админом мимо словаря, не роняла вывод: Орках, Ультрамаринах, Эльдарах, Хаоситах, Имперской гвардии, Тау, Некронах, Тиранидах, Инквизиции, Сёстрах битвы, Друкхари, Адептус Механикус. 3. `services/user_service.py` — `update_favorite_faction` рядом с `update_bio`: `null` очищает выбор, несуществующий id → ошибка валидации. 4. `schemas/api.py` — `UserRead.favorite_faction_id`, `ProfileUpdate.favorite_faction_id`, `FactionStat.name_ru_prepositional`; в `ProfileStats` `most_played_faction` заменяется на `favorite_faction`. 5. `routers/users.py` — `update_my_profile` переводится на `model_dump(exclude_unset=True)`: сейчас он всегда пишет `bio`, и без этого сохранение одного «О себе» обнуляло бы выбранную фракцию. 6. `services/stats_service.py` — убрать вычисление старой «любимой» вместе со служебным `chosen_games`, отдавать `favorite_faction` из профиля. Публичный профиль правок не требует — `PublicProfile.stats` это `ProfileStats`, новое поле приедет туда само. **Фронтенд** 7. `hooks/reference.ts` — `useFactions()` для уже существующего `GET /api/factions`. 8. `hooks/auth.ts` — `useUpdateFavoriteFaction()` по образцу `useUpdateBio`. 9. `pages/AccountPage.tsx` — блок «Любимая фракция» на готовом `PickerSelect`, все 12 фракций. Компонент не умеет группирующие заголовки, поэтому дополнение показываю прямо в строке опции приглушённым текстом (`Орки · Базовая игра`) — без правки общего компонента; сброс — кнопкой «Убрать» рядом. 10. `components/ProfileStatsCard.tsx` — «Любимая фракция» из нового поля, «Основная фракция» → «Чаще всего играет на» в предложном падеже. 11. `npm run gen:api` — контракт меняется, схема пересобирается с живого бэкенда. **Тесты.** Два теста в `test_profile.py` проверяют отменяемую логику — их проверки `main_faction` сохраню, утверждения про `most_played_faction` уберу. Добавлю тесты на установку, сброс, отказ на несуществующем id и на то, что сохранение «О себе» не сбрасывает фракцию. **Критерии готовности** - Любимая фракция задаётся из списка всех 12 фракций, сохраняется, сбрасывается и переживает сохранение «О себе». - «Чаще всего играет на» показывает самую игранную по всей истории фракцию в предложном падеже — одинаково на своём и чужом профиле. - Фракция вне словаря выводится в именительном, без ошибки. - `alembic upgrade head` проходит на существующей БД и идемпотентен на свежей. - `pytest` и `npm run build` зелёные. **Ветка:** `issue-17-faction-favorite-and-most-played` от `dev` (поле `ref` у задачи пустое — базовую ветку подтвердили отдельно)
Agent added the
Status
In Review
label 2026-09-07 16:18:22 +03:00
Collaborator

Работа выполнена, открыт PR: #20

Итог: любимая фракция стала полем профиля (users.favorite_faction_id, миграция 0010) и выбирается игроком в кастомизации из всех 12 фракций; «Основная фракция» переименована в «Чаще всего играет на» и печатается в предложном падеже из словаря в faction_service. Логику подсчёта основной менять не пришлось — она и так считала по всем партиям, включая рандом.

Попутно нашлась и закрыта потенциальная бага: update_my_profile всегда писал bio, поэтому сохранение одного «о себе» обнулило бы выбранную фракцию. Роутер переведён на exclude_unset, на это есть отдельный тест.

Проверки: alembic upgrade head — накатилась; pytest — 69 passed (было 65, два теста старой логики переписаны, четыре добавлены); npm run gen:api + npm run build — зелёные. Визуальная проверка не выполнена: dev-стенд остановлен, расширение Claude in Chrome к сессии не подключено.

Статус: Status/In Review
Осталось за вами: ревью и мёрж PR — задача закроется автоматически.

Работа выполнена, открыт PR: https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/pulls/20 **Итог:** любимая фракция стала полем профиля (`users.favorite_faction_id`, миграция `0010`) и выбирается игроком в кастомизации из всех 12 фракций; «Основная фракция» переименована в «Чаще всего играет на» и печатается в предложном падеже из словаря в `faction_service`. Логику подсчёта основной менять не пришлось — она и так считала по всем партиям, включая рандом. Попутно нашлась и закрыта потенциальная бага: `update_my_profile` всегда писал `bio`, поэтому сохранение одного «о себе» обнулило бы выбранную фракцию. Роутер переведён на `exclude_unset`, на это есть отдельный тест. **Проверки:** `alembic upgrade head` — накатилась; `pytest` — 69 passed (было 65, два теста старой логики переписаны, четыре добавлены); `npm run gen:api` + `npm run build` — зелёные. Визуальная проверка не выполнена: dev-стенд остановлен, расширение Claude in Chrome к сессии не подключено. **Статус:** `Status/In Review` **Осталось за вами:** ревью и мёрж PR — задача закроется автоматически.
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: NotBigGhost/ForbiddenStarsApp#17