from enum import Enum from typing import Optional import pendulum from pendulum import Date, Time from pydantic import BaseModel, ConfigDict, Field, field_validator from pydantic_extra_types.pendulum_dt import DateTime class User(BaseModel): login: str password: str = Field(repr=False) club_id: Optional[str] = Field(default=None) class BookingOpening(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) days_before: Optional[int] = Field(default=7, alias="daysBefore") opening_time: Optional[str] = Field(alias="time", default=None, repr=False) time_after_booking: Optional[str] = Field( alias="timeAfterBookingTime", default=None, repr=False ) def __repr__(self): base = super().__repr__() time = f", time: {self.time})" if self.time else "" time_after_booking = ( f", time_after_booking_time: {self.time_after_booking_time})" if self.time_after_booking_time else "" ) return base.removesuffix(")") + time + time_after_booking @property def time(self): return pendulum.parse(self.opening_time).time() @property def time_after_booking_time(self): return ( pendulum.parse(self.time_after_booking).time() if self.time_after_booking else None ) class TotalBookings(BaseModel): peak_hours: int | str = Field(alias="peakHours") off_peak_hours: int | str = Field(alias="offPeakHours") class Court(BaseModel): id: int name: str number: int is_indoor: Optional[bool] = Field(alias="isIndoor") class Sport(BaseModel): name: str id: int duration: int price: int players: int courts: list[Court] class Url(BaseModel): name: str path: str parameter: Optional[str] = Field(default=None) payload_template: Optional[str] = Field(default=None, alias="payloadTemplate") class BookingPlatform(BaseModel): id: str club_id: int = Field(alias="clubId") url: str hours_before_cancellation: int = Field(alias="hoursBeforeCancellation") booking_opening: BookingOpening = Field(alias="bookingOpening") total_bookings: TotalBookings = Field(alias="totalBookings") sports: list[Sport] urls: dict[str, Url] class Club(BaseModel): id: str name: str url: str booking_platform: BookingPlatform = Field(alias="bookingPlatform") class PlatformDefinition(BaseModel): id: str name: str url: str urls: list[Url] class BookingFilter(BaseModel): date: DateTime sport_name: str @field_validator("sport_name", mode="before") @classmethod def to_lower_case(cls, d: str) -> str: return d.lower() class Booking(BaseModel): id: int booking_date: DateTime = Field(alias="dateResa") start_time: DateTime = Field(alias="startTime") sport: str court: str game_creation: Optional[int] = Field(default=None, alias="creaPartie") game_creation_limit: Optional[DateTime] = Field( default=None, alias="limitCreaPartie" ) cancel: Optional[bool] = Field(default=True) block_player_replacement: Optional[int] = Field( default=None, alias="bloquerRemplacementJoueur" ) can_remove_parteners: bool = Field(default=True, alias="canRemovePartners") end_time: Optional[DateTime] = Field(default=None, alias="endTime") day_fr: Optional[str] = Field(default=None, alias="dayFr") live_xperience_code: Optional[str] = Field(default=None, alias="codeLiveXperience") spartime_qr_code: Optional[str] = Field(default=None, alias="qrCodeSpartime") remaining_places: int = Field(default=3, alias="remainingPlaces") is_captain: bool = Field(default=True, alias="isCaptain") dt_start: Optional[DateTime] = Field(default=None, alias="dtStart") credit_card_guaranty: Optional[str] = Field(default=None, alias="garantieCb") certificate_validity_duration: Optional[int] = Field( alias="dureeValidCertif", default=None ) charge_id: Optional[str] = Field(default=None, alias="chargeId") partners: Optional[list] = Field(default=[]) player_status: Optional[int] = Field(default=None, alias="playerStatus") products: Optional[list] = Field(default=[]) @field_validator("booking_date", mode="before") @classmethod def validate_date(cls, d: str) -> DateTime: return pendulum.from_format( d, "DD/MM/YYYY", tz=pendulum.timezone("Europe/Paris") ) @field_validator("start_time", "end_time", mode="before") @classmethod def validate_time(cls, t: str) -> DateTime: return pendulum.from_format(t, "HH:mm", tz=pendulum.timezone("Europe/Paris")) @field_validator("game_creation_limit", mode="before") @classmethod def validate_datetime_add_tz(cls, dt: str) -> DateTime: return pendulum.parse(dt, tz=pendulum.timezone("Europe/Paris")) @field_validator("dt_start", mode="before") @classmethod def validate_datetime(cls, dt: str) -> DateTime: return pendulum.parse(dt) @field_validator("sport", mode="before") @classmethod def to_lower_case(cls, d: str) -> str: return d.lower() def matches(self, booking_filter: BookingFilter) -> bool: """ Check if the booking matches the booking filter :param booking_filter: the conditions the booking should meet :return: true if the booking matches the conditions, false otherwise """ return ( self.is_same_sport(booking_filter.sport_name) and self.is_same_date(booking_filter.date.date()) and self.is_same_time(booking_filter.date.time()) ) def is_same_sport(self, sport: str) -> bool: """ Check if the booking and the booking filter are about the same sport :param sport: the sport to test :return: true if the sport matches booking sport, false otherwise """ return self.sport == sport def is_same_date(self, date: Date) -> bool: """ Check if the booking filter has the same date as the booking :param date: the date to test :return: true if the date matches the booking date, false otherwise """ return self.booking_date.date() == date def is_same_time(self, time: Time) -> bool: """ Check if the booking filter has the same time as the booking :param time: the time to test :return: true if the time matches the booking time, false otherwise """ return self.start_time.time() == time class Action(Enum): BOOK = "book" CANCEL = "cancel" TOURNAMENTS = "tournaments" class Tournament(BaseModel): name: str price: str start_date: DateTime end_date: DateTime gender: str places_left: str | int