- Оспаривания теперь требуют решения админа после 24ч голосования - Можно повторно оспаривать после разрешённых споров - Исправлены бонусные очки при перепрохождении после оспаривания - Сброс серии при невалидном пруфе - Колесо показывает только доступные игры - Rate limiting только через backend (RATE_LIMIT_ENABLED)
105 lines
3.0 KiB
Python
105 lines
3.0 KiB
Python
from datetime import datetime
|
|
from typing import TYPE_CHECKING
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.schemas.user import UserPublic
|
|
from app.schemas.challenge import ChallengeResponse, GameShort
|
|
|
|
if TYPE_CHECKING:
|
|
from app.schemas.game import PlaythroughInfo
|
|
from app.schemas.assignment import BonusAssignmentResponse
|
|
|
|
|
|
class DisputeCreate(BaseModel):
|
|
"""Request to create a dispute"""
|
|
reason: str = Field(..., min_length=10, max_length=1000)
|
|
|
|
|
|
class DisputeCommentCreate(BaseModel):
|
|
"""Request to add a comment to a dispute"""
|
|
text: str = Field(..., min_length=1, max_length=500)
|
|
|
|
|
|
class DisputeVoteCreate(BaseModel):
|
|
"""Request to vote on a dispute"""
|
|
vote: bool # True = valid (proof is OK), False = invalid (proof is not OK)
|
|
|
|
|
|
class DisputeCommentResponse(BaseModel):
|
|
"""Comment in a dispute discussion"""
|
|
id: int
|
|
user: UserPublic
|
|
text: str
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class DisputeVoteResponse(BaseModel):
|
|
"""Vote in a dispute"""
|
|
user: UserPublic
|
|
vote: bool # True = valid, False = invalid
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class DisputeResponse(BaseModel):
|
|
"""Full dispute information"""
|
|
id: int
|
|
raised_by: UserPublic
|
|
reason: str
|
|
status: str # "open", "valid", "invalid"
|
|
comments: list[DisputeCommentResponse]
|
|
votes: list[DisputeVoteResponse]
|
|
votes_valid: int
|
|
votes_invalid: int
|
|
my_vote: bool | None # Current user's vote, None if not voted
|
|
expires_at: datetime
|
|
created_at: datetime
|
|
resolved_at: datetime | None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class AssignmentDetailResponse(BaseModel):
|
|
"""Detailed assignment information with proofs and dispute"""
|
|
id: int
|
|
challenge: ChallengeResponse | None # None for playthrough
|
|
game: GameShort | None = None # For playthrough
|
|
is_playthrough: bool = False
|
|
playthrough_info: dict | None = None # For playthrough (description, points, proof_type, proof_hint)
|
|
participant: UserPublic
|
|
status: str
|
|
proof_url: str | None # External URL (YouTube, etc.)
|
|
proof_image_url: str | None # Uploaded file URL
|
|
proof_comment: str | None
|
|
points_earned: int
|
|
streak_at_completion: int | None
|
|
started_at: datetime
|
|
completed_at: datetime | None
|
|
can_dispute: bool # True if <24h since completion and not own assignment
|
|
dispute: DisputeResponse | None
|
|
bonus_challenges: list[dict] | None = None # For playthrough
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class ReturnedAssignmentResponse(BaseModel):
|
|
"""Returned assignment that needs to be redone"""
|
|
id: int
|
|
challenge: ChallengeResponse | None = None # For challenge assignments
|
|
is_playthrough: bool = False
|
|
game_id: int | None = None # For playthrough assignments
|
|
game_title: str | None = None
|
|
game_cover_url: str | None = None
|
|
original_completed_at: datetime
|
|
dispute_reason: str
|
|
|
|
class Config:
|
|
from_attributes = True
|