9a2aea038a
- Add pyproject.toml with uv and ruff configuration - Pin Python version to 3.13 via .python-version - Add Makefile commands: lint, format, fix - Apply ruff formatting to entire codebase - Remove unused imports (base64 in yookassa/simple_subscription) - Update .gitignore for new config files
212 lines
5.4 KiB
Python
212 lines
5.4 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import date, datetime, time
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class ContestTemplateResponse(BaseModel):
|
|
id: int
|
|
name: str
|
|
slug: str
|
|
description: str | None = None
|
|
prize_type: str
|
|
prize_value: str
|
|
max_winners: int
|
|
attempts_per_user: int
|
|
times_per_day: int
|
|
schedule_times: str | None = None
|
|
cooldown_hours: int
|
|
payload: dict[str, Any] = Field(default_factory=dict)
|
|
is_enabled: bool
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class ContestTemplateListResponse(BaseModel):
|
|
items: list[ContestTemplateResponse]
|
|
|
|
|
|
class ContestTemplateUpdateRequest(BaseModel):
|
|
name: str | None = None
|
|
description: str | None = None
|
|
prize_type: str | None = None
|
|
prize_value: str | None = None
|
|
max_winners: int | None = Field(None, ge=1)
|
|
attempts_per_user: int | None = Field(None, ge=1)
|
|
times_per_day: int | None = Field(None, ge=1)
|
|
schedule_times: str | None = None
|
|
cooldown_hours: int | None = Field(None, ge=1)
|
|
payload: dict[str, Any] | None = None
|
|
is_enabled: bool | None = None
|
|
|
|
|
|
class StartRoundRequest(BaseModel):
|
|
starts_at: datetime | None = None
|
|
ends_at: datetime | None = None
|
|
cooldown_hours: int | None = Field(None, ge=1)
|
|
payload: dict[str, Any] | None = None
|
|
force: bool = False
|
|
|
|
|
|
class ContestRoundResponse(BaseModel):
|
|
id: int
|
|
template_id: int
|
|
template_slug: str
|
|
template_name: str | None = None
|
|
starts_at: datetime
|
|
ends_at: datetime
|
|
status: str
|
|
payload: dict[str, Any] = Field(default_factory=dict)
|
|
winners_count: int
|
|
max_winners: int
|
|
attempts_per_user: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class ContestRoundListResponse(BaseModel):
|
|
items: list[ContestRoundResponse]
|
|
total: int
|
|
limit: int
|
|
offset: int
|
|
|
|
|
|
class ContestAttemptUser(BaseModel):
|
|
id: int
|
|
telegram_id: int | None = None
|
|
username: str | None = None
|
|
full_name: str | None = None
|
|
|
|
|
|
class ContestAttemptResponse(BaseModel):
|
|
id: int
|
|
round_id: int
|
|
user: ContestAttemptUser
|
|
answer: str | None = None
|
|
is_winner: bool
|
|
created_at: datetime
|
|
|
|
|
|
class ContestAttemptListResponse(BaseModel):
|
|
items: list[ContestAttemptResponse]
|
|
total: int
|
|
limit: int
|
|
offset: int
|
|
|
|
|
|
class ReferralContestResponse(BaseModel):
|
|
id: int
|
|
title: str
|
|
description: str | None = None
|
|
prize_text: str | None = None
|
|
contest_type: str
|
|
start_at: datetime
|
|
end_at: datetime
|
|
daily_summary_time: time
|
|
daily_summary_times: str | None = None
|
|
timezone: str
|
|
is_active: bool
|
|
last_daily_summary_date: date | None = None
|
|
last_daily_summary_at: datetime | None = None
|
|
final_summary_sent: bool
|
|
created_by: int | None = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class ReferralContestListResponse(BaseModel):
|
|
items: list[ReferralContestResponse]
|
|
total: int
|
|
limit: int
|
|
offset: int
|
|
|
|
|
|
class ReferralContestCreateRequest(BaseModel):
|
|
title: str
|
|
description: str | None = None
|
|
prize_text: str | None = None
|
|
contest_type: str = Field('referral_paid', min_length=1)
|
|
start_at: datetime
|
|
end_at: datetime
|
|
daily_summary_time: time = Field(default=time(hour=12))
|
|
daily_summary_times: str | None = Field(
|
|
default=None, description='Список времён ЧЧ:ММ через запятую (например, 12:00,18:00)'
|
|
)
|
|
timezone: str = Field(default='UTC')
|
|
is_active: bool = True
|
|
created_by: int | None = None
|
|
|
|
|
|
class ReferralContestUpdateRequest(BaseModel):
|
|
title: str | None = None
|
|
description: str | None = None
|
|
prize_text: str | None = None
|
|
contest_type: str | None = Field(None, min_length=1)
|
|
start_at: datetime | None = None
|
|
end_at: datetime | None = None
|
|
daily_summary_time: time | None = None
|
|
daily_summary_times: str | None = Field(default=None, description='Список времён ЧЧ:ММ через запятую')
|
|
timezone: str | None = None
|
|
is_active: bool | None = None
|
|
final_summary_sent: bool | None = None
|
|
created_by: int | None = None
|
|
|
|
|
|
class ReferralContestLeaderboardItem(BaseModel):
|
|
user_id: int
|
|
telegram_id: int | None = None
|
|
username: str | None = None
|
|
full_name: str | None = None
|
|
referrals_count: int
|
|
total_amount_kopeks: int
|
|
total_amount_rubles: float
|
|
|
|
|
|
class ReferralContestDetailResponse(ReferralContestResponse):
|
|
total_events: int | None = None
|
|
leaderboard: list[ReferralContestLeaderboardItem] | None = None
|
|
|
|
|
|
class ReferralContestEventUser(BaseModel):
|
|
id: int
|
|
telegram_id: int | None = None
|
|
username: str | None = None
|
|
full_name: str | None = None
|
|
|
|
|
|
class ReferralContestEventResponse(BaseModel):
|
|
id: int
|
|
contest_id: int
|
|
referrer: ReferralContestEventUser
|
|
referral: ReferralContestEventUser
|
|
event_type: str
|
|
amount_kopeks: int
|
|
amount_rubles: float
|
|
occurred_at: datetime
|
|
|
|
|
|
class ReferralContestEventListResponse(BaseModel):
|
|
items: list[ReferralContestEventResponse]
|
|
total: int
|
|
limit: int
|
|
offset: int
|
|
|
|
|
|
class ReferralContestParticipant(BaseModel):
|
|
referrer_id: int
|
|
full_name: str
|
|
total_referrals: int
|
|
paid_referrals: int
|
|
unpaid_referrals: int
|
|
total_paid_amount: int
|
|
|
|
|
|
class ReferralContestDetailedStatsResponse(BaseModel):
|
|
total_participants: int
|
|
total_invited: int
|
|
total_paid_amount: int
|
|
total_unpaid: int
|
|
participants: list[ReferralContestParticipant]
|