207 lines
5.4 KiB
Python
207 lines
5.4 KiB
Python
"""
|
|
Pydantic schemas for Shop system
|
|
"""
|
|
from datetime import datetime
|
|
from pydantic import BaseModel, Field
|
|
from typing import Any
|
|
|
|
|
|
# === Shop Items ===
|
|
|
|
class ShopItemBase(BaseModel):
|
|
"""Base schema for shop items"""
|
|
item_type: str
|
|
code: str
|
|
name: str
|
|
description: str | None = None
|
|
price: int
|
|
rarity: str = "common"
|
|
asset_data: dict | None = None
|
|
|
|
|
|
class ShopItemCreate(ShopItemBase):
|
|
"""Schema for creating a shop item (admin)"""
|
|
is_active: bool = True
|
|
available_from: datetime | None = None
|
|
available_until: datetime | None = None
|
|
stock_limit: int | None = None
|
|
|
|
|
|
class ShopItemUpdate(BaseModel):
|
|
"""Schema for updating a shop item (admin)"""
|
|
name: str | None = None
|
|
description: str | None = None
|
|
price: int | None = Field(None, ge=1)
|
|
rarity: str | None = None
|
|
asset_data: dict | None = None
|
|
is_active: bool | None = None
|
|
available_from: datetime | None = None
|
|
available_until: datetime | None = None
|
|
stock_limit: int | None = None
|
|
|
|
|
|
class ShopItemResponse(ShopItemBase):
|
|
"""Schema for shop item response"""
|
|
id: int
|
|
is_active: bool
|
|
available_from: datetime | None
|
|
available_until: datetime | None
|
|
stock_limit: int | None
|
|
stock_remaining: int | None
|
|
created_at: datetime
|
|
is_available: bool # Computed property
|
|
is_owned: bool = False # Set by API based on user
|
|
is_equipped: bool = False # Set by API based on user
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# === Inventory ===
|
|
|
|
class InventoryItemResponse(BaseModel):
|
|
"""Schema for user inventory item"""
|
|
id: int
|
|
item: ShopItemResponse
|
|
quantity: int
|
|
equipped: bool
|
|
purchased_at: datetime
|
|
expires_at: datetime | None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# === Purchases ===
|
|
|
|
class PurchaseRequest(BaseModel):
|
|
"""Schema for purchase request"""
|
|
item_id: int
|
|
quantity: int = Field(default=1, ge=1, le=10)
|
|
|
|
|
|
class PurchaseResponse(BaseModel):
|
|
"""Schema for purchase response"""
|
|
success: bool
|
|
item: ShopItemResponse
|
|
quantity: int
|
|
total_cost: int
|
|
new_balance: int
|
|
message: str
|
|
|
|
|
|
# === Consumables ===
|
|
|
|
class UseConsumableRequest(BaseModel):
|
|
"""Schema for using a consumable"""
|
|
item_code: str # 'skip', 'boost', 'wild_card', 'lucky_dice', 'copycat', 'undo'
|
|
marathon_id: int
|
|
assignment_id: int | None = None # Required for skip, wild_card, copycat
|
|
game_id: int | None = None # Required for wild_card
|
|
target_participant_id: int | None = None # Required for copycat
|
|
|
|
|
|
class UseConsumableResponse(BaseModel):
|
|
"""Schema for consumable use response"""
|
|
success: bool
|
|
item_code: str
|
|
remaining_quantity: int
|
|
effect_description: str
|
|
effect_data: dict | None = None
|
|
|
|
|
|
# === Equipment ===
|
|
|
|
class EquipItemRequest(BaseModel):
|
|
"""Schema for equipping an item"""
|
|
inventory_id: int
|
|
|
|
|
|
class EquipItemResponse(BaseModel):
|
|
"""Schema for equip response"""
|
|
success: bool
|
|
item_type: str
|
|
equipped_item: ShopItemResponse | None
|
|
message: str
|
|
|
|
|
|
# === Coins ===
|
|
|
|
class CoinTransactionResponse(BaseModel):
|
|
"""Schema for coin transaction"""
|
|
id: int
|
|
amount: int
|
|
transaction_type: str
|
|
description: str | None
|
|
reference_type: str | None
|
|
reference_id: int | None
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class CoinsBalanceResponse(BaseModel):
|
|
"""Schema for coins balance with recent transactions"""
|
|
balance: int
|
|
recent_transactions: list[CoinTransactionResponse]
|
|
|
|
|
|
class AdminCoinsRequest(BaseModel):
|
|
"""Schema for admin coin operations"""
|
|
amount: int = Field(..., ge=1)
|
|
reason: str = Field(..., min_length=1, max_length=500)
|
|
|
|
|
|
# === User Cosmetics ===
|
|
|
|
class UserCosmeticsResponse(BaseModel):
|
|
"""Schema for user's equipped cosmetics"""
|
|
frame: ShopItemResponse | None = None
|
|
title: ShopItemResponse | None = None
|
|
name_color: ShopItemResponse | None = None
|
|
background: ShopItemResponse | None = None
|
|
|
|
|
|
# === Certification ===
|
|
|
|
class CertificationRequestSchema(BaseModel):
|
|
"""Schema for requesting marathon certification"""
|
|
pass # No fields needed for now
|
|
|
|
|
|
class CertificationReviewRequest(BaseModel):
|
|
"""Schema for admin reviewing certification"""
|
|
approve: bool
|
|
rejection_reason: str | None = Field(None, max_length=1000)
|
|
|
|
|
|
class CertificationStatusResponse(BaseModel):
|
|
"""Schema for certification status"""
|
|
marathon_id: int
|
|
certification_status: str
|
|
is_certified: bool
|
|
certification_requested_at: datetime | None
|
|
certified_at: datetime | None
|
|
certified_by_nickname: str | None = None
|
|
rejection_reason: str | None = None
|
|
|
|
|
|
# === Consumables Status ===
|
|
|
|
class ConsumablesStatusResponse(BaseModel):
|
|
"""Schema for participant's consumables status in a marathon"""
|
|
skips_available: int # From inventory
|
|
skips_used: int # In this marathon
|
|
skips_remaining: int | None # Based on marathon limit
|
|
boosts_available: int # From inventory
|
|
has_active_boost: bool # Currently activated (one-time for current assignment)
|
|
boost_multiplier: float | None # 1.5 if boost active
|
|
wild_cards_available: int # From inventory
|
|
lucky_dice_available: int # From inventory
|
|
has_lucky_dice: bool # Currently activated
|
|
lucky_dice_multiplier: float | None # Rolled multiplier if active
|
|
copycats_available: int # From inventory
|
|
undos_available: int # From inventory
|
|
can_undo: bool # Has drop data to undo
|