Изменение логики:
Любимые фракции: самая частая фракция, выбираемая игроком -> личный выбор игрока в меню кастомизации профиля из выпадающего списка. Выпадающий список из шаблонов сайта.
Основная фракция: Переименовать в "Чаще всего играет на", логика подбора - частота по всей истории игр независимо от условий выбора фракции, сама фракция в предложном падеже (необходимо составить словарь).
Изменение логики:
Любимые фракции: самая частая фракция, выбираемая игроком -> личный выбор игрока в меню кастомизации профиля из выпадающего списка. Выпадающий список из шаблонов сайта.
Основная фракция: Переименовать в "Чаще всего играет на", логика подбора - частота по всей истории игр независимо от условий выбора фракции, сама фракция в предложном падеже (необходимо составить словарь).
NotBigGhost
added this to the v1.35 - промежуточная полировка версии 1.3 milestone 2026-09-07 14:59:03 +03:00
Находка, которая упрощает половину задачи: логику «основной фракции» менять не нужно. В stats_service.py она уже считается как самая игранная по всем партиям, включая рандомные раздачи, — ровно как требует ТЗ. Меняются только подпись и падеж.
Основная работа — превращение «любимой фракции» из вычисляемой в поле профиля.
Бэкенд
models.py — User.favorite_faction_id, FK на factions с ON DELETE SET NULL; миграция 0010_user_favorite_faction.py по идемпотентному шаблону 0009.
services/faction_service.py — словарь предложного падежа code → форма с fallback на именительный, чтобы фракция, добавленная админом мимо словаря, не роняла вывод: Орках, Ультрамаринах, Эльдарах, Хаоситах, Имперской гвардии, Тау, Некронах, Тиранидах, Инквизиции, Сёстрах битвы, Друкхари, Адептус Механикус.
services/user_service.py — update_favorite_faction рядом с update_bio: null очищает выбор, несуществующий id → ошибка валидации.
schemas/api.py — UserRead.favorite_faction_id, ProfileUpdate.favorite_faction_id, FactionStat.name_ru_prepositional; в ProfileStatsmost_played_faction заменяется на favorite_faction.
routers/users.py — update_my_profile переводится на model_dump(exclude_unset=True): сейчас он всегда пишет bio, и без этого сохранение одного «О себе» обнуляло бы выбранную фракцию.
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` у задачи пустое — базовую ветку подтвердили отдельно)
Итог: любимая фракция стала полем профиля (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 — задача закроется автоматически.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Изменение логики:
Любимые фракции: самая частая фракция, выбираемая игроком -> личный выбор игрока в меню кастомизации профиля из выпадающего списка. Выпадающий список из шаблонов сайта.
Основная фракция: Переименовать в "Чаще всего играет на", логика подбора - частота по всей истории игр независимо от условий выбора фракции, сама фракция в предложном падеже (необходимо составить словарь).
План выполнения
Находка, которая упрощает половину задачи: логику «основной фракции» менять не нужно. В
stats_service.pyона уже считается как самая игранная по всем партиям, включая рандомные раздачи, — ровно как требует ТЗ. Меняются только подпись и падеж.Основная работа — превращение «любимой фракции» из вычисляемой в поле профиля.
Бэкенд
models.py—User.favorite_faction_id, FK наfactionsсON DELETE SET NULL; миграция0010_user_favorite_faction.pyпо идемпотентному шаблону0009.services/faction_service.py— словарь предложного падежаcode → формас fallback на именительный, чтобы фракция, добавленная админом мимо словаря, не роняла вывод: Орках, Ультрамаринах, Эльдарах, Хаоситах, Имперской гвардии, Тау, Некронах, Тиранидах, Инквизиции, Сёстрах битвы, Друкхари, Адептус Механикус.services/user_service.py—update_favorite_factionрядом сupdate_bio:nullочищает выбор, несуществующий id → ошибка валидации.schemas/api.py—UserRead.favorite_faction_id,ProfileUpdate.favorite_faction_id,FactionStat.name_ru_prepositional; вProfileStatsmost_played_factionзаменяется наfavorite_faction.routers/users.py—update_my_profileпереводится наmodel_dump(exclude_unset=True): сейчас он всегда пишетbio, и без этого сохранение одного «О себе» обнуляло бы выбранную фракцию.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 и на то, что сохранение «О себе» не сбрасывает фракцию.Критерии готовности
alembic upgrade headпроходит на существующей БД и идемпотентен на свежей.pytestиnpm run buildзелёные.Ветка:
issue-17-faction-favorite-and-most-playedотdev(полеrefу задачи пустое — базовую ветку подтвердили отдельно)Работа выполнена, открыт 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 — задача закроется автоматически.