Исправлены ошибки 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:
|
if not participant:
|
||||||
raise HTTPException(status_code=404, detail="Participant not found")
|
raise HTTPException(status_code=404, detail="Participant not found")
|
||||||
|
|
||||||
# Get active assignment
|
# Get active assignment (exclude event assignments)
|
||||||
result = await db.execute(
|
result = await db.execute(
|
||||||
select(Assignment)
|
select(Assignment)
|
||||||
.options(
|
.options(
|
||||||
@@ -1059,6 +1059,7 @@ async def organizer_skip_assignment(
|
|||||||
.where(
|
.where(
|
||||||
Assignment.participant_id == participant.id,
|
Assignment.participant_id == participant.id,
|
||||||
Assignment.status == AssignmentStatus.ACTIVE.value,
|
Assignment.status == AssignmentStatus.ACTIVE.value,
|
||||||
|
Assignment.is_event_assignment == False,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assignment = result.scalar_one_or_none()
|
assignment = result.scalar_one_or_none()
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class GameResponse(GameBase):
|
|||||||
|
|
||||||
class PlaythroughInfo(BaseModel):
|
class PlaythroughInfo(BaseModel):
|
||||||
"""Информация о прохождении для игр типа playthrough"""
|
"""Информация о прохождении для игр типа playthrough"""
|
||||||
description: str
|
description: str | None = None
|
||||||
points: int
|
points: int | None = None
|
||||||
proof_type: str
|
proof_type: str | None = None
|
||||||
proof_hint: str | None = None
|
proof_hint: str | None = None
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from sqlalchemy.orm import selectinload
|
|||||||
from app.models import (
|
from app.models import (
|
||||||
User, Participant, Marathon, Assignment, AssignmentStatus,
|
User, Participant, Marathon, Assignment, AssignmentStatus,
|
||||||
ShopItem, UserInventory, ConsumableUsage, ConsumableType, Game, Challenge,
|
ShopItem, UserInventory, ConsumableUsage, ConsumableType, Game, Challenge,
|
||||||
BonusAssignment, ExiledGame
|
BonusAssignment, ExiledGame, GameType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -262,11 +262,15 @@ class ConsumablesService:
|
|||||||
game_id: int,
|
game_id: int,
|
||||||
) -> dict:
|
) -> 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
|
- 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
|
Returns: dict with new assignment info
|
||||||
|
|
||||||
@@ -279,9 +283,10 @@ class ConsumablesService:
|
|||||||
if assignment.status != AssignmentStatus.ACTIVE.value:
|
if assignment.status != AssignmentStatus.ACTIVE.value:
|
||||||
raise HTTPException(status_code=400, detail="Can only use wild card on active assignments")
|
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(
|
result = await db.execute(
|
||||||
select(Game)
|
select(Game)
|
||||||
|
.options(selectinload(Game.challenges))
|
||||||
.where(
|
.where(
|
||||||
Game.id == game_id,
|
Game.id == game_id,
|
||||||
Game.marathon_id == marathon.id,
|
Game.marathon_id == marathon.id,
|
||||||
@@ -292,31 +297,52 @@ class ConsumablesService:
|
|||||||
if not game:
|
if not game:
|
||||||
raise HTTPException(status_code=400, detail="Game not found in this marathon")
|
raise HTTPException(status_code=400, detail="Game not found in this marathon")
|
||||||
|
|
||||||
# Get random challenge from this game
|
# Store old assignment info for logging
|
||||||
result = await db.execute(
|
old_game_id = assignment.game_id
|
||||||
select(Challenge)
|
old_challenge_id = assignment.challenge_id
|
||||||
.where(Challenge.game_id == game_id)
|
old_is_playthrough = assignment.is_playthrough
|
||||||
.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")
|
|
||||||
|
|
||||||
# Consume wild card from inventory
|
# Consume wild card from inventory
|
||||||
item = await self._consume_item(db, user, ConsumableType.WILD_CARD.value)
|
item = await self._consume_item(db, user, ConsumableType.WILD_CARD.value)
|
||||||
|
|
||||||
# Store old assignment info for logging
|
# Delete existing bonus assignments if any
|
||||||
old_game_id = assignment.game_id
|
if assignment.bonus_assignments:
|
||||||
old_challenge_id = assignment.challenge_id
|
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.game_id = game_id
|
||||||
assignment.challenge_id = new_challenge.id
|
assignment.challenge_id = None
|
||||||
# Reset timestamps since it's a new challenge
|
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.started_at = datetime.utcnow()
|
||||||
assignment.deadline = None # Will be recalculated if needed
|
assignment.deadline = None
|
||||||
|
|
||||||
# Log usage
|
# Log usage
|
||||||
usage = ConsumableUsage(
|
usage = ConsumableUsage(
|
||||||
@@ -328,8 +354,10 @@ class ConsumablesService:
|
|||||||
"type": "wild_card",
|
"type": "wild_card",
|
||||||
"old_game_id": old_game_id,
|
"old_game_id": old_game_id,
|
||||||
"old_challenge_id": old_challenge_id,
|
"old_challenge_id": old_challenge_id,
|
||||||
|
"old_is_playthrough": old_is_playthrough,
|
||||||
"new_game_id": game_id,
|
"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)
|
db.add(usage)
|
||||||
@@ -337,9 +365,11 @@ class ConsumablesService:
|
|||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"game_id": game_id,
|
"game_id": game_id,
|
||||||
"game_name": game.name,
|
"game_name": game.title,
|
||||||
"challenge_id": new_challenge.id,
|
"game_type": game.game_type,
|
||||||
"challenge_title": new_challenge.title,
|
"is_playthrough": game.game_type == GameType.PLAYTHROUGH.value,
|
||||||
|
"challenge_id": new_challenge_id,
|
||||||
|
"challenge_title": new_challenge_title,
|
||||||
}
|
}
|
||||||
|
|
||||||
async def use_lucky_dice(
|
async def use_lucky_dice(
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class PointsService:
|
|||||||
def calculate_drop_penalty(
|
def calculate_drop_penalty(
|
||||||
self,
|
self,
|
||||||
consecutive_drops: int,
|
consecutive_drops: int,
|
||||||
challenge_points: int,
|
challenge_points: int | None,
|
||||||
event: Event | None = None
|
event: Event | None = None
|
||||||
) -> int:
|
) -> int:
|
||||||
"""
|
"""
|
||||||
@@ -80,6 +80,10 @@ class PointsService:
|
|||||||
Returns:
|
Returns:
|
||||||
Penalty points to subtract
|
Penalty points to subtract
|
||||||
"""
|
"""
|
||||||
|
# No penalty if no points defined
|
||||||
|
if challenge_points is None:
|
||||||
|
return 0
|
||||||
|
|
||||||
# Double risk event = free drops
|
# Double risk event = free drops
|
||||||
if event and event.type == EventType.DOUBLE_RISK.value:
|
if event and event.type == EventType.DOUBLE_RISK.value:
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
Reference in New Issue
Block a user