resa-padel/resa_padel/gestion_sports_services.py

127 lines
4.9 KiB
Python

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)