Common enemy rework

This commit is contained in:
2025-12-15 23:03:59 +07:00
parent 9a037cb34f
commit 07e02ce32d
11 changed files with 731 additions and 29 deletions

View File

@@ -38,7 +38,14 @@ async def get_participant_or_403(db, user_id: int, marathon_id: int) -> Particip
return participant
async def get_active_assignment(db, participant_id: int) -> Assignment | None:
async def get_active_assignment(db, participant_id: int, is_event: bool = False) -> Assignment | None:
"""Get active assignment for participant.
Args:
db: Database session
participant_id: Participant ID
is_event: If True, get event assignment (Common Enemy). If False, get regular assignment.
"""
result = await db.execute(
select(Assignment)
.options(
@@ -47,6 +54,7 @@ async def get_active_assignment(db, participant_id: int) -> Assignment | None:
.where(
Assignment.participant_id == participant_id,
Assignment.status == AssignmentStatus.ACTIVE.value,
Assignment.is_event_assignment == is_event,
)
)
return result.scalar_one_or_none()
@@ -66,8 +74,8 @@ async def spin_wheel(marathon_id: int, current_user: CurrentUser, db: DbSession)
participant = await get_participant_or_403(db, current_user.id, marathon_id)
# Check no active assignment
active = await get_active_assignment(db, participant.id)
# Check no active regular assignment (event assignments are separate)
active = await get_active_assignment(db, participant.id, is_event=False)
if active:
raise HTTPException(status_code=400, detail="You already have an active assignment")
@@ -77,7 +85,7 @@ async def spin_wheel(marathon_id: int, current_user: CurrentUser, db: DbSession)
game = None
challenge = None
# Handle special event cases
# Handle special event cases (excluding Common Enemy - it has separate flow)
if active_event:
if active_event.type == EventType.JACKPOT.value:
# Jackpot: Get hard challenge only
@@ -90,17 +98,7 @@ async def spin_wheel(marathon_id: int, current_user: CurrentUser, db: DbSession)
game = result.scalar_one_or_none()
# Consume jackpot (one-time use)
await event_service.consume_jackpot(db, active_event.id)
elif active_event.type == EventType.COMMON_ENEMY.value:
# Common enemy: Everyone gets same challenge (if not already completed)
event_data = active_event.data or {}
completions = event_data.get("completions", [])
already_completed = any(c["participant_id"] == participant.id for c in completions)
if not already_completed:
challenge = await event_service.get_common_enemy_challenge(db, active_event)
if challenge:
game = challenge.game
# Note: Common Enemy is handled separately via event-assignment endpoints
# Normal random selection if no special event handling
if not game or not challenge:
@@ -192,9 +190,9 @@ async def spin_wheel(marathon_id: int, current_user: CurrentUser, db: DbSession)
@router.get("/marathons/{marathon_id}/current-assignment", response_model=AssignmentResponse | None)
async def get_current_assignment(marathon_id: int, current_user: CurrentUser, db: DbSession):
"""Get current active assignment"""
"""Get current active regular assignment (not event assignments)"""
participant = await get_participant_or_403(db, current_user.id, marathon_id)
assignment = await get_active_assignment(db, participant.id)
assignment = await get_active_assignment(db, participant.id, is_event=False)
if not assignment:
return None
@@ -237,7 +235,7 @@ async def complete_assignment(
comment: str | None = Form(None),
proof_file: UploadFile | None = File(None),
):
"""Complete an assignment with proof"""
"""Complete a regular assignment with proof (not event assignments)"""
# Get assignment
result = await db.execute(
select(Assignment)
@@ -258,6 +256,10 @@ async def complete_assignment(
if assignment.status != AssignmentStatus.ACTIVE.value:
raise HTTPException(status_code=400, detail="Assignment is not active")
# Event assignments should be completed via /event-assignments/{id}/complete
if assignment.is_event_assignment:
raise HTTPException(status_code=400, detail="Use /event-assignments/{id}/complete for event assignments")
# Need either file or URL
if not proof_file and not proof_url:
raise HTTPException(status_code=400, detail="Proof is required (file or URL)")