175 lines
4.6 KiB
Python
175 lines
4.6 KiB
Python
from datetime import datetime
|
||
from pydantic import BaseModel, Field
|
||
from typing import Literal
|
||
|
||
from app.models.event import EventType
|
||
from app.schemas.user import UserPublic
|
||
|
||
|
||
# Event type literals for Pydantic
|
||
EventTypeLiteral = Literal[
|
||
"golden_hour",
|
||
"common_enemy",
|
||
"double_risk",
|
||
"jackpot",
|
||
"swap",
|
||
"game_choice",
|
||
]
|
||
|
||
|
||
class EventCreate(BaseModel):
|
||
type: EventTypeLiteral
|
||
duration_minutes: int | None = Field(
|
||
None,
|
||
description="Duration in minutes. If not provided, uses default for event type."
|
||
)
|
||
challenge_id: int | None = Field(
|
||
None,
|
||
description="For common_enemy event - the challenge everyone will get"
|
||
)
|
||
|
||
|
||
class EventEffects(BaseModel):
|
||
points_multiplier: float = 1.0
|
||
drop_free: bool = False
|
||
special_action: str | None = None # "swap", "game_choice"
|
||
description: str = ""
|
||
|
||
|
||
class EventResponse(BaseModel):
|
||
id: int
|
||
type: EventTypeLiteral
|
||
start_time: datetime
|
||
end_time: datetime | None
|
||
is_active: bool
|
||
created_by: UserPublic | None
|
||
data: dict | None
|
||
created_at: datetime
|
||
|
||
class Config:
|
||
from_attributes = True
|
||
|
||
|
||
class ActiveEventResponse(BaseModel):
|
||
event: EventResponse | None
|
||
effects: EventEffects
|
||
time_remaining_seconds: int | None = None
|
||
|
||
|
||
class SwapRequest(BaseModel):
|
||
target_participant_id: int
|
||
|
||
|
||
class CommonEnemyLeaderboard(BaseModel):
|
||
participant_id: int
|
||
user: UserPublic
|
||
completed_at: datetime | None
|
||
rank: int | None
|
||
bonus_points: int
|
||
|
||
|
||
# Event descriptions and default durations
|
||
EVENT_INFO = {
|
||
EventType.GOLDEN_HOUR: {
|
||
"name": "Золотой час",
|
||
"description": "Все очки x1.5!",
|
||
"default_duration": 45,
|
||
"points_multiplier": 1.5,
|
||
"drop_free": False,
|
||
},
|
||
EventType.COMMON_ENEMY: {
|
||
"name": "Общий враг",
|
||
"description": "Все получают одинаковый челлендж. Первые 3 получают бонус!",
|
||
"default_duration": None, # Until all complete
|
||
"points_multiplier": 1.0,
|
||
"drop_free": False,
|
||
},
|
||
EventType.DOUBLE_RISK: {
|
||
"name": "Безопасная игра",
|
||
"description": "Дропы бесплатны, но очки x0.5",
|
||
"default_duration": 120,
|
||
"points_multiplier": 0.5,
|
||
"drop_free": True,
|
||
},
|
||
EventType.JACKPOT: {
|
||
"name": "Джекпот",
|
||
"description": "Следующий спин — сложный челлендж с x3 очками!",
|
||
"default_duration": None, # 1 spin
|
||
"points_multiplier": 3.0,
|
||
"drop_free": False,
|
||
},
|
||
EventType.SWAP: {
|
||
"name": "Обмен",
|
||
"description": "Можно поменяться заданием с другим участником",
|
||
"default_duration": 60,
|
||
"points_multiplier": 1.0,
|
||
"drop_free": False,
|
||
"special_action": "swap",
|
||
},
|
||
EventType.GAME_CHOICE: {
|
||
"name": "Выбор игры",
|
||
"description": "Выбери игру и один из 3 челленджей. Можно заменить текущее задание без штрафа!",
|
||
"default_duration": 120,
|
||
"points_multiplier": 1.0,
|
||
"drop_free": True, # Free replacement of current assignment
|
||
"special_action": "game_choice",
|
||
},
|
||
}
|
||
|
||
# Bonus points for Common Enemy top 3
|
||
COMMON_ENEMY_BONUSES = {
|
||
1: 50,
|
||
2: 30,
|
||
3: 15,
|
||
}
|
||
|
||
|
||
class SwapCandidate(BaseModel):
|
||
"""Participant available for assignment swap"""
|
||
participant_id: int
|
||
user: UserPublic
|
||
challenge_title: str
|
||
challenge_description: str
|
||
challenge_points: int
|
||
challenge_difficulty: str
|
||
game_title: str
|
||
|
||
|
||
# Two-sided swap confirmation schemas
|
||
SwapRequestStatusLiteral = Literal["pending", "accepted", "declined", "cancelled"]
|
||
|
||
|
||
class SwapRequestCreate(BaseModel):
|
||
"""Request to swap assignment with another participant"""
|
||
target_participant_id: int
|
||
|
||
|
||
class SwapRequestChallengeInfo(BaseModel):
|
||
"""Challenge info for swap request display"""
|
||
title: str
|
||
description: str
|
||
points: int
|
||
difficulty: str
|
||
game_title: str
|
||
|
||
|
||
class SwapRequestResponse(BaseModel):
|
||
"""Response for a swap request"""
|
||
id: int
|
||
status: SwapRequestStatusLiteral
|
||
from_user: UserPublic
|
||
to_user: UserPublic
|
||
from_challenge: SwapRequestChallengeInfo
|
||
to_challenge: SwapRequestChallengeInfo
|
||
created_at: datetime
|
||
responded_at: datetime | None
|
||
|
||
class Config:
|
||
from_attributes = True
|
||
|
||
|
||
class MySwapRequests(BaseModel):
|
||
"""User's incoming and outgoing swap requests"""
|
||
incoming: list[SwapRequestResponse]
|
||
outgoing: list[SwapRequestResponse]
|