avtopost.kultmost.ru/models.py
2026-08-02 12:08:08 -04:00

451 lines
14 KiB
Python

from pydantic import BaseModel, Field, model_validator, validator, ConfigDict
from typing import List, Optional, Literal
from datetime import datetime, date, timezone
from fastapi.security import OAuth2PasswordRequestForm
import json
from fastapi import File, Form, UploadFile
class NotifyItem(BaseModel):
user_id: str
text: str
title: Optional[str] = "Уведомление"
link: str = "/profile"
type_item: str = "c"
class DeliverySendRequest(BaseModel):
sender: str
too: str
item: str
delivery_type: Literal["СДЭК", "Самостоятельно"]
track: Optional[str] = Field(None, description="Трэк-номер для СДЭК")
class DeliveryAcceptRequest(BaseModel):
item: str
class UserRegister(BaseModel):
username: Optional[str]
phone: Optional[str]
email: Optional[str]
# img: Optional[UploadFile]
password: str
client_id: str
roles: List[str] = ["user"]
extra_scopes: List[str] = []
birthday: Optional[date]
grade: Optional[str]
female_name_lastname: Optional[str] = Field(None, alias="Female_Name_Lastame")
living_place: Optional[str]
offer_accepted: Optional[bool] = False
offer_accepted_at: Optional[datetime] = None
@model_validator(mode='after')
def at_least_one_identifier(self):
if not any([self.username, self.phone, self.email]):
raise ValueError("At least one of username, phone or email is required")
return self
model_config = {"validate_by_name": True}
class FormCompetition(BaseModel):
id: str
title: str
address: Optional[str]
topic: Optional[str]
short_desc: Optional[str]
rules_file_id: Optional[str]
poster_file_id: Optional[str]
min_paintings: Optional[int]
max_paintings: Optional[int]
class UserLoginForm(OAuth2PasswordRequestForm):
pass
class LoginRequest(BaseModel):
identifier: str
password: str
client_id: str
class RestoreRequest(BaseModel):
identifier: str
next: str | None = None
class ResetPasswordRequest(BaseModel):
token: str
new_password: str
class AdminLoginRequest(BaseModel):
user_id: str
class PartnercompetitionRequest(BaseModel):
competition_id: str
post_id: str
status: str
class CompetitionArtistRequest(BaseModel):
competition_id: str
user_id: str
class Partnercompetitionstop(BaseModel):
competition_id: str
class PartnerExhibitionDelete(BaseModel):
exhibition_id: str
class PostEditDescription(BaseModel):
post_id: str
description: str
class TokenResponse(BaseModel):
access_token: str
refresh_token: str
token_type: str = "bearer"
class RefreshRequest(BaseModel):
refresh_token: str | None = None
client_id: str | None = None
class CompetitionCreate(BaseModel):
datetime_start: datetime
date_finish: datetime
poster: UploadFile
type_: str
title: str
topic: Optional[str] = None
nominations: List[str] = []
address: str
short_desc: Optional[str] = None
rules_link: Optional[str] = None
rules: Optional[UploadFile] = None
min_paintings: int
max_paintings: int
emailsNotify: bool = False
serviceSocial: bool = False
userSocial: bool = False
@model_validator(mode="after")
def make_aware(cls, values):
tz = timezone.utc
if not values.rules_link and not values.rules:
raise ValueError(
"Укажите хотя бы один из параметров: `rules_link` ИЛИ `rules` (файл)."
)
if values.datetime_start and values.datetime_start.tzinfo is None:
values.datetime_start = values.datetime_start.replace(tzinfo=tz)
elif values.datetime_start:
values.datetime_start = values.datetime_start.astimezone(tz)
if values.date_finish and values.date_finish.tzinfo is None:
values.date_finish = values.date_finish.replace(tzinfo=tz)
elif values.date_finish:
values.date_finish = values.date_finish.astimezone(tz)
return values
@validator("nominations", pre=True, always=True)
def parse_nominations(cls, v):
if v is None:
return None
if isinstance(v, list):
return v
try:
data = json.loads(v)
return data if isinstance(data, list) else [data]
except (json.JSONDecodeError, TypeError):
return [v]
class ExhibitionCreate(BaseModel):
datetime_start: datetime
date_finish: datetime
poster: UploadFile
rules: Optional[UploadFile] = None
rules_link: Optional[str] = None
tizer: Optional[UploadFile] = None
tizer_link: Optional[str] = None
title: str
topic: Optional[str] = None
address: Optional[str] = None
place: Optional[str] = None
@model_validator(mode="after")
def make_aware(cls, values):
tz = timezone.utc
if not values.rules_link and not values.rules:
raise ValueError(
"Укажите хотя бы один из параметров: `rules_link` ИЛИ `rules` (файл)."
)
if not values.tizer_link and not values.tizer:
raise ValueError(
"Укажите хотя бы один из параметров: `tizer_link` ИЛИ `tizer` (файл)."
)
if values.datetime_start and values.datetime_start.tzinfo is None:
values.datetime_start = values.datetime_start.replace(tzinfo=tz)
elif values.datetime_start:
values.datetime_start = values.datetime_start.astimezone(tz)
if values.date_finish and values.date_finish.tzinfo is None:
values.date_finish = values.date_finish.replace(tzinfo=tz)
elif values.date_finish:
values.date_finish = values.date_finish.astimezone(tz)
return values
async def build_payload(
datetime_start: datetime = Form(...),
date_finish: datetime = Form(...),
poster: UploadFile = File(...),
type_: str = Form(...),
title: str = Form(...),
topic: Optional[str] = Form(None),
nominations: Optional[str] = Form(None),
address: str = Form(...),
short_desc: Optional[str] = Form(None),
rules_link: Optional[str] = Form(None),
rules: Optional[UploadFile] = File(None),
min_paintings: int = Form(...),
max_paintings: int = Form(...),
emailsNotify: bool = Form(False),
serviceSocial: bool = Form(False),
userSocial: bool = Form(False),
) -> CompetitionCreate:
return CompetitionCreate(
datetime_start=datetime_start,
date_finish=date_finish,
poster=poster,
type_=type_,
title=title,
topic=topic,
nominations=nominations,
address=address,
short_desc=short_desc,
rules_link=rules_link,
rules=rules,
min_paintings=min_paintings,
max_paintings=max_paintings,
emailsNotify=emailsNotify,
serviceSocial=serviceSocial,
userSocial=userSocial,
)
class CompetitionUpdate(BaseModel):
datetime_start: Optional[datetime] = None
date_finish: Optional[datetime] = None
poster: Optional[UploadFile] = None
type_: Optional[str] = None
title: Optional[str] = None
topic: Optional[str] = None
nominations: Optional[List[str]] = None
rules_link: Optional[str] = None
address: Optional[str] = None
short_desc: Optional[str] = None
rules: Optional[UploadFile] = None
min_paintings: Optional[int] = None
max_paintings: Optional[int] = None
emailsNotify: Optional[bool] = False
serviceSocial: Optional[bool] = False
userSocial: Optional[bool] = False
model_config = ConfigDict(
arbitrary_types_allowed=True,
json_encoders={
datetime: lambda v: v.isoformat()
},
)
@model_validator(mode="after")
def make_aware(cls, values):
tz = timezone.utc
if values.datetime_start and values.datetime_start.tzinfo is None:
values.datetime_start = values.datetime_start.replace(tzinfo=tz)
elif values.datetime_start:
values.datetime_start = values.datetime_start.astimezone(tz)
if values.date_finish and values.date_finish.tzinfo is None:
values.date_finish = values.date_finish.replace(tzinfo=tz)
elif values.date_finish:
values.date_finish = values.date_finish.astimezone(tz)
return values
@validator("nominations", pre=True, always=True)
def parse_nominations(cls, v):
if v is None:
return None
if isinstance(v, list):
return v
try:
data = json.loads(v)
return data if isinstance(data, list) else [data]
except (json.JSONDecodeError, TypeError):
return [v]
async def build_update_payload(
datetime_start: Optional[datetime] = Form(None),
date_finish: Optional[datetime] = Form(None),
poster: Optional[UploadFile] = File(None),
type_: Optional[str] = Form(...),
title: Optional[str] = Form(None),
topic: Optional[str] = Form(None),
nominations: Optional[str] = Form(None),
rules_link: Optional[str] = Form(None),
address: Optional[str] = Form(None),
short_desc: Optional[str] = Form(None),
rules: Optional[UploadFile] = File(None),
min_paintings: Optional[int] = Form(None),
max_paintings: Optional[int] = Form(None),
emailsNotify: Optional[bool] = Form(False),
serviceSocial: Optional[bool] = Form(False),
userSocial: Optional[bool] = Form(False),
) -> CompetitionUpdate:
return CompetitionUpdate(
datetime_start=datetime_start,
date_finish=date_finish,
poster=poster,
type_=type_,
title=title,
topic=topic,
nominations=nominations,
rules_link=rules_link,
address=address,
short_desc=short_desc,
rules=rules,
min_paintings=min_paintings,
max_paintings=max_paintings,
emailsNotify=emailsNotify,
serviceSocial=serviceSocial,
userSocial=userSocial,
)
class CompetitionPublishParams(BaseModel):
competition_id: str
emailsNotify: bool = False
serviceSocial: bool = False
userSocial: bool = False
usersSocial: bool = False
async def build_publish_payload(
competition_id: str = Form(...),
emailsNotify: bool = Form(False),
serviceSocial: bool = Form(False),
userSocial: bool = Form(False),
usersSocial: bool = Form(False),
) -> CompetitionPublishParams:
return CompetitionPublishParams(
competition_id=competition_id,
emailsNotify=emailsNotify,
serviceSocial=serviceSocial,
userSocial=userSocial,
usersSocial=usersSocial,
)
async def build_exhibition_payload(
datetime_start: datetime = Form(...),
date_finish: datetime = Form(...),
poster: Optional[UploadFile] = File(...),
rules: Optional[UploadFile] = File(None),
rules_link: Optional[str] = Form(None),
tizer: Optional[UploadFile] = File(None),
tizer_link: Optional[str] = Form(None),
title: str = Form(...),
topic: Optional[str] = Form(None),
address: Optional[str] = Form(None),
place: Optional[str] = Form(None),
) -> ExhibitionCreate:
return ExhibitionCreate(
datetime_start=datetime_start,
date_finish=date_finish,
poster=poster,
rules=rules,
rules_link=rules_link,
tizer=tizer,
tizer_link=tizer_link,
title=title,
topic=topic,
address=address,
place=place,
)
class ExhibitionUpdate(BaseModel):
datetime_start: Optional[datetime] = None
date_finish: Optional[datetime] = None
poster: Optional[UploadFile] = None
rules: Optional[UploadFile] = None
rules_link: Optional[str] = None
tizer: Optional[UploadFile] = None
tizer_link: Optional[str] = None
title: Optional[str] = None
topic: Optional[str] = None
address: Optional[str] = None
place: Optional[str] = None
short_desc: Optional[str] = None
model_config = ConfigDict(
arbitrary_types_allowed=True,
json_encoders={
datetime: lambda v: v.isoformat()
},
)
@model_validator(mode="after")
def make_aware(cls, values):
tz = timezone.utc
if values.datetime_start and values.datetime_start.tzinfo is None:
values.datetime_start = values.datetime_start.replace(tzinfo=tz)
elif values.datetime_start:
values.datetime_start = values.datetime_start.astimezone(tz)
if values.date_finish and values.date_finish.tzinfo is None:
values.date_finish = values.date_finish.replace(tzinfo=tz)
elif values.date_finish:
values.date_finish = values.date_finish.astimezone(tz)
return values
async def build_update_exhibition_payload(
datetime_start: Optional[datetime] = Form(None),
date_finish: Optional[datetime] = Form(None),
poster: Optional[UploadFile] = File(None),
rules: Optional[UploadFile] = File(None),
rules_link: Optional[str] = Form(None),
tizer: Optional[UploadFile] = File(None),
tizer_link: Optional[str] = Form(None),
title: Optional[str] = Form(None),
topic: Optional[str] = Form(None),
address: Optional[str] = Form(None),
place: Optional[str] = Form(None),
short_desc: Optional[str] = Form(None),
) -> ExhibitionUpdate:
return ExhibitionUpdate(
datetime_start=datetime_start,
date_finish=date_finish,
poster=poster,
rules=rules,
rules_link=rules_link,
tizer=tizer,
tizer_link=tizer_link,
title=title,
topic=topic,
address=address,
place=place,
short_desc=short_desc,
)