Исправлены ошибки Wild Card и skip-assignment
- Wild Card: исправлен game.name → game.title - Wild Card: добавлена поддержка игр типа playthrough - points.py: добавлена проверка на None для challenge_points - PlaythroughInfo: поля сделаны Optional (description, points, proof_type) - organizer_skip_assignment: добавлен фильтр is_event_assignment Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1049,7 +1049,7 @@ async def organizer_skip_assignment(
|
||||
if not participant:
|
||||
raise HTTPException(status_code=404, detail="Participant not found")
|
||||
|
||||
# Get active assignment
|
||||
# Get active assignment (exclude event assignments)
|
||||
result = await db.execute(
|
||||
select(Assignment)
|
||||
.options(
|
||||
@@ -1059,6 +1059,7 @@ async def organizer_skip_assignment(
|
||||
.where(
|
||||
Assignment.participant_id == participant.id,
|
||||
Assignment.status == AssignmentStatus.ACTIVE.value,
|
||||
Assignment.is_event_assignment == False,
|
||||
)
|
||||
)
|
||||
assignment = result.scalar_one_or_none()
|
||||
|
||||
@@ -87,7 +87,7 @@ class GameResponse(GameBase):
|
||||
|
||||
class PlaythroughInfo(BaseModel):
|
||||
"""Информация о прохождении для игр типа playthrough"""
|
||||
description: str
|
||||
points: int
|
||||
proof_type: str
|
||||
description: str | None = None
|
||||
points: int | None = None
|
||||
proof_type: str | None = None
|
||||
proof_hint: str | None = None
|
||||
|
||||
@@ -20,7 +20,7 @@ from sqlalchemy.orm import selectinload
|
||||
from app.models import (
|
||||
User, Participant, Marathon, Assignment, AssignmentStatus,
|
||||
ShopItem, UserInventory, ConsumableUsage, ConsumableType, Game, Challenge,
|
||||
BonusAssignment, ExiledGame
|
||||
BonusAssignment, ExiledGame, GameType
|
||||
)
|
||||
|
||||
|
||||
@@ -262,11 +262,15 @@ class ConsumablesService:
|
||||
game_id: int,
|
||||
) -> dict:
|
||||
"""
|
||||
Use Wild Card - choose a game and get a random challenge from it.
|
||||
Use Wild Card - choose a game and switch to it.
|
||||
|
||||
- Current assignment is replaced
|
||||
For challenges game type:
|
||||
- New challenge is randomly selected from the chosen game
|
||||
- Game must be in the marathon
|
||||
- Assignment becomes a regular challenge
|
||||
|
||||
For playthrough game type:
|
||||
- Assignment becomes a playthrough of the chosen game
|
||||
- Bonus assignments are created from game's challenges
|
||||
|
||||
Returns: dict with new assignment info
|
||||
|
||||
@@ -279,9 +283,10 @@ class ConsumablesService:
|
||||
if assignment.status != AssignmentStatus.ACTIVE.value:
|
||||
raise HTTPException(status_code=400, detail="Can only use wild card on active assignments")
|
||||
|
||||
# Verify game is in this marathon
|
||||
# Verify game is in this marathon and load challenges
|
||||
result = await db.execute(
|
||||
select(Game)
|
||||
.options(selectinload(Game.challenges))
|
||||
.where(
|
||||
Game.id == game_id,
|
||||
Game.marathon_id == marathon.id,
|
||||
@@ -292,31 +297,52 @@ class ConsumablesService:
|
||||
if not game:
|
||||
raise HTTPException(status_code=400, detail="Game not found in this marathon")
|
||||
|
||||
# Get random challenge from this game
|
||||
result = await db.execute(
|
||||
select(Challenge)
|
||||
.where(Challenge.game_id == game_id)
|
||||
.order_by(func.random())
|
||||
.limit(1)
|
||||
)
|
||||
new_challenge = result.scalar_one_or_none()
|
||||
|
||||
if not new_challenge:
|
||||
raise HTTPException(status_code=400, detail="No challenges available for this game")
|
||||
# Store old assignment info for logging
|
||||
old_game_id = assignment.game_id
|
||||
old_challenge_id = assignment.challenge_id
|
||||
old_is_playthrough = assignment.is_playthrough
|
||||
|
||||
# Consume wild card from inventory
|
||||
item = await self._consume_item(db, user, ConsumableType.WILD_CARD.value)
|
||||
|
||||
# Store old assignment info for logging
|
||||
old_game_id = assignment.game_id
|
||||
old_challenge_id = assignment.challenge_id
|
||||
# Delete existing bonus assignments if any
|
||||
if assignment.bonus_assignments:
|
||||
for ba in assignment.bonus_assignments:
|
||||
await db.delete(ba)
|
||||
|
||||
# Update assignment with new challenge
|
||||
new_challenge_id = None
|
||||
new_challenge_title = None
|
||||
|
||||
if game.game_type == GameType.PLAYTHROUGH.value:
|
||||
# Switch to playthrough mode
|
||||
assignment.game_id = game_id
|
||||
assignment.challenge_id = new_challenge.id
|
||||
# Reset timestamps since it's a new challenge
|
||||
assignment.challenge_id = None
|
||||
assignment.is_playthrough = True
|
||||
|
||||
# Create bonus assignments from game's challenges
|
||||
for ch in game.challenges:
|
||||
bonus = BonusAssignment(
|
||||
main_assignment_id=assignment.id,
|
||||
challenge_id=ch.id,
|
||||
)
|
||||
db.add(bonus)
|
||||
|
||||
else:
|
||||
# Switch to challenge mode - get random challenge
|
||||
if not game.challenges:
|
||||
raise HTTPException(status_code=400, detail="No challenges available for this game")
|
||||
|
||||
new_challenge = random.choice(game.challenges)
|
||||
new_challenge_id = new_challenge.id
|
||||
new_challenge_title = new_challenge.title
|
||||
|
||||
assignment.game_id = game_id
|
||||
assignment.challenge_id = new_challenge_id
|
||||
assignment.is_playthrough = False
|
||||
|
||||
# Reset timestamps since it's a new assignment
|
||||
assignment.started_at = datetime.utcnow()
|
||||
assignment.deadline = None # Will be recalculated if needed
|
||||
assignment.deadline = None
|
||||
|
||||
# Log usage
|
||||
usage = ConsumableUsage(
|
||||
@@ -328,8 +354,10 @@ class ConsumablesService:
|
||||
"type": "wild_card",
|
||||
"old_game_id": old_game_id,
|
||||
"old_challenge_id": old_challenge_id,
|
||||
"old_is_playthrough": old_is_playthrough,
|
||||
"new_game_id": game_id,
|
||||
"new_challenge_id": new_challenge.id,
|
||||
"new_challenge_id": new_challenge_id,
|
||||
"new_is_playthrough": game.game_type == GameType.PLAYTHROUGH.value,
|
||||
},
|
||||
)
|
||||
db.add(usage)
|
||||
@@ -337,9 +365,11 @@ class ConsumablesService:
|
||||
return {
|
||||
"success": True,
|
||||
"game_id": game_id,
|
||||
"game_name": game.name,
|
||||
"challenge_id": new_challenge.id,
|
||||
"challenge_title": new_challenge.title,
|
||||
"game_name": game.title,
|
||||
"game_type": game.game_type,
|
||||
"is_playthrough": game.game_type == GameType.PLAYTHROUGH.value,
|
||||
"challenge_id": new_challenge_id,
|
||||
"challenge_title": new_challenge_title,
|
||||
}
|
||||
|
||||
async def use_lucky_dice(
|
||||
|
||||
@@ -66,7 +66,7 @@ class PointsService:
|
||||
def calculate_drop_penalty(
|
||||
self,
|
||||
consecutive_drops: int,
|
||||
challenge_points: int,
|
||||
challenge_points: int | None,
|
||||
event: Event | None = None
|
||||
) -> int:
|
||||
"""
|
||||
@@ -80,6 +80,10 @@ class PointsService:
|
||||
Returns:
|
||||
Penalty points to subtract
|
||||
"""
|
||||
# No penalty if no points defined
|
||||
if challenge_points is None:
|
||||
return 0
|
||||
|
||||
# Double risk event = free drops
|
||||
if event and event.type == EventType.DOUBLE_RISK.value:
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user