Рейтинг: партии без соперников не считаются, понятная причина недоступной партии
В профиле Ivan висели две «партии» с одним участником, которые не открывались. Dev-удаление аккаунта (dev_admin) вычёркивает игрока из партий, не трогая сами партии, и 17.06 в dev-БД так опустели партии 2, 3, 6 и 7. Они засчитывались оставшемуся в число игр с ΔR 0.0, показывались в истории и списке группы, а бэкфилл 0014 проставил им last_standing. Партия меньше чем с двумя участниками больше не считается игрой: её нет в рейтинге и счётчиках (load_history), в истории игрока и в списке партий группы; админка её по-прежнему видит. Бэкфилл last_standing в 0014 теперь требует хотя бы двух участников (на уже мигрированных БД такие партии и так скрыты). «Партия не найдена» при переходе из чужого профиля в партию группы, где зритель не состоит, — это 403; страница партии теперь так и пишет. Тест: партия, опустевшая после dev-удаления, не попадает в историю, список и счётчик группы, рейтинг считается только по настоящей партии. #80 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
This commit is contained in:
@@ -26,11 +26,28 @@ from app.services.scoring import (
|
||||
)
|
||||
|
||||
|
||||
MIN_PARTICIPANTS = 2 # партия, где осталось меньше участников, партией не считается
|
||||
|
||||
|
||||
def _playable_match_ids():
|
||||
"""Подзапрос id партий, в которых не меньше MIN_PARTICIPANTS участников.
|
||||
|
||||
Партия может «опустеть» в деве: жёсткое удаление аккаунта (routers/dev_admin.py)
|
||||
вычёркивает игрока из партий и не пересчитывает их. Партия с одним участником —
|
||||
уже не игра: её нет ни в рейтинге, ни в историях и списках (админка её видит)."""
|
||||
return (
|
||||
select(MatchParticipant.match_id)
|
||||
.group_by(MatchParticipant.match_id)
|
||||
.having(func.count() >= MIN_PARTICIPANTS)
|
||||
)
|
||||
|
||||
|
||||
def load_history(session: Session) -> list[RatedMatch]:
|
||||
"""Все завершённые партии в порядке проигрывания: дата игры, момент завершения, id.
|
||||
|
||||
Один запрос на партии с участниками. In_progress в рейтинг не входят: мест у них нет.
|
||||
Срез группы — _for_group по этой же истории: рейтинг считается только целиком."""
|
||||
Один запрос на партии с участниками. In_progress в рейтинг не входят: мест у них нет,
|
||||
партии меньше чем с двумя участниками — тоже (_playable_match_ids). Срез группы —
|
||||
_for_group по этой же истории: рейтинг считается только целиком."""
|
||||
stmt = (
|
||||
select(Match, MatchParticipant)
|
||||
.join(MatchParticipant, MatchParticipant.match_id == Match.id)
|
||||
@@ -43,7 +60,7 @@ def load_history(session: Session) -> list[RatedMatch]:
|
||||
seats: list[RatedSeat] = []
|
||||
|
||||
def flush() -> None:
|
||||
if current is not None and seats:
|
||||
if current is not None and len(seats) >= MIN_PARTICIPANTS:
|
||||
history.append(
|
||||
RatedMatch(
|
||||
seats=tuple(seats),
|
||||
@@ -411,12 +428,11 @@ def _match_items(
|
||||
|
||||
|
||||
def group_match_list(session: Session, group_id: int, limit: int = 20, offset: int = 0) -> dict:
|
||||
total = session.exec(
|
||||
select(func.count()).select_from(Match).where(Match.group_id == group_id)
|
||||
).one()
|
||||
where = (Match.group_id == group_id, Match.id.in_(_playable_match_ids()))
|
||||
total = session.exec(select(func.count()).select_from(Match).where(*where)).one()
|
||||
matches = session.exec(
|
||||
select(Match)
|
||||
.where(Match.group_id == group_id)
|
||||
.where(*where)
|
||||
.order_by(Match.played_at.desc(), Match.id.desc())
|
||||
.offset(offset)
|
||||
.limit(limit)
|
||||
@@ -456,7 +472,11 @@ def user_match_list(
|
||||
"offset": 0,
|
||||
}
|
||||
|
||||
where = (MatchParticipant.user_id == user_id, Match.status == "finished")
|
||||
where = (
|
||||
MatchParticipant.user_id == user_id,
|
||||
Match.status == "finished",
|
||||
Match.id.in_(_playable_match_ids()),
|
||||
)
|
||||
total = session.exec(
|
||||
select(func.count())
|
||||
.select_from(Match)
|
||||
|
||||
Reference in New Issue
Block a user