import logging import time import pendulum from aiohttp import ClientSession from connectors import GestionSportsConnector from models import BookingFilter, BookingOpening, Club, Court, User from pendulum import DateTime LOGGER = logging.getLogger(__name__) class GestionSportsServices: @staticmethod async def book( club: Club, user: User, booking_filter: BookingFilter ) -> Court | None: """ Perform a request for each court at the same time to increase the chances to get a booking. The gestion-sports backend does not allow several bookings at the same time so there is no need to make each request one after the other :param club: the club in which the booking will be made :param user: the user that wants to book the court :param booking_filter: the booking conditions to meet :return: the booked court, or None if no court was booked """ connector = GestionSportsConnector(club) LOGGER.info( "Booking any available court from GestionSports API at %s", connector.booking_url, ) async with ClientSession() as session: # use asyncio to request a booking on every court # the gestion-sports backend is able to book only one court for a user await connector.land(session) await connector.login(session, user) booking_opening = club.booking_platform.booking_opening GestionSportsServices.wait_until_booking_time( booking_filter, booking_opening ) bookings = await connector.book_any_court(session, booking_filter) LOGGER.debug("Booking results:\n'%s'", bookings) return connector.get_booked_court(bookings, booking_filter.sport_name) @staticmethod async def has_user_available_slots(user: User, club: Club) -> bool: connector = GestionSportsConnector(club) async with ClientSession() as session: await connector.land(session) await connector.login(session, user) bookings = await connector.get_ongoing_bookings(session) return bool(bookings) @staticmethod async def cancel_booking(user: User, club: Club, booking_filter: BookingFilter): connector = GestionSportsConnector(club) async with ClientSession() as session: await connector.land(session) await connector.login(session, user) await connector.cancel_booking(session, booking_filter) @staticmethod async def cancel_booking_id(user: User, club: Club, booking_id: int): connector = GestionSportsConnector(club) async with ClientSession() as session: await connector.land(session) await connector.login(session, user) await connector.cancel_booking_id(session, booking_id) @staticmethod def wait_until_booking_time( booking_filter: BookingFilter, booking_opening: BookingOpening ) -> None: """ Wait until the booking is open. The booking filter contains the date and time of the booking. The club has the information about when the booking is open for that date. :param booking_opening: :param booking_filter: the booking information """ LOGGER.info("Waiting for booking time") booking_datetime = GestionSportsServices.build_booking_datetime( booking_filter, booking_opening ) now = pendulum.now() duration_until_booking = booking_datetime - now LOGGER.debug(f"Current time: {now}, Datetime to book: {booking_datetime}") LOGGER.debug( f"Time to wait before booking: {duration_until_booking.hours:0>2}" f":{duration_until_booking.minutes:0>2}" f":{duration_until_booking.seconds:0>2}" ) while now < booking_datetime: time.sleep(1) now = pendulum.now() LOGGER.info("It's booking time!") @staticmethod def build_booking_datetime( booking_filter: BookingFilter, booking_opening: BookingOpening ) -> DateTime: """ Build the date and time when the booking is open for a given match date. The booking filter contains the date and time of the booking. The club has the information about when the booking is open for that date. :param booking_opening:the booking opening conditions :param booking_filter: the booking information :return: the date and time when the booking is open """ date_to_book = booking_filter.date booking_date = date_to_book.subtract(days=booking_opening.days_before) opening_time = pendulum.parse(booking_opening.opening_time) booking_hour = opening_time.hour booking_minute = opening_time.minute return booking_date.at(booking_hour, booking_minute)