All methods are in the right class
This commit is contained in:
parent
0d541e82a5
commit
7f59443b64
12 changed files with 585 additions and 1729 deletions
|
@ -6,16 +6,24 @@ import pendulum
|
|||
from aiohttp import ClientSession
|
||||
from bs4 import BeautifulSoup
|
||||
from gestion_sport_connector import GestionSportsConnector
|
||||
from models import BookingFilter, BookingOpening, Club, Court, Tournament, User
|
||||
from models import (
|
||||
Booking,
|
||||
BookingFilter,
|
||||
BookingOpening,
|
||||
Club,
|
||||
Court,
|
||||
Sport,
|
||||
Tournament,
|
||||
User,
|
||||
)
|
||||
from pendulum import DateTime
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GestionSportsServices:
|
||||
@staticmethod
|
||||
async def book(
|
||||
club: Club, user: User, booking_filter: BookingFilter
|
||||
self, 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
|
||||
|
@ -45,37 +53,76 @@ class GestionSportsServices:
|
|||
booking_filter, booking_opening
|
||||
)
|
||||
|
||||
bookings = await connector.book_any_court(session, booking_filter)
|
||||
bookings = await connector.send_all_booking_requests(
|
||||
session, booking_filter
|
||||
)
|
||||
|
||||
LOGGER.debug("Booking results:\n'%s'", bookings)
|
||||
return connector.get_booked_court(bookings, booking_filter.sport_name)
|
||||
|
||||
sport = club.sports.get(booking_filter.sport_name)
|
||||
|
||||
return self.get_booked_court(bookings, sport)
|
||||
|
||||
def get_booked_court(
|
||||
self, bookings: list[tuple[int, dict]], sport: Sport
|
||||
) -> Court | None:
|
||||
"""
|
||||
Parse the booking list and return the court that was booked
|
||||
|
||||
:param bookings: a list of bookings
|
||||
:param sport: the sport of the club and all the courts it has
|
||||
:return: the id of the booked court if any, None otherwise
|
||||
"""
|
||||
for court_id, response in bookings:
|
||||
if self.is_booking_response_status_ok(response):
|
||||
LOGGER.debug("Court %d is booked", court_id)
|
||||
court_booked = self.find_court(court_id, sport)
|
||||
LOGGER.info("Court '%s' is booked", court_booked.name)
|
||||
return court_booked
|
||||
LOGGER.debug("No booked court found")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
async def has_user_available_slots(user: User, club: Club) -> bool:
|
||||
def is_booking_response_status_ok(response: dict) -> bool:
|
||||
"""
|
||||
Check if the booking response is OK
|
||||
|
||||
:param response: the response as a string
|
||||
:return: true if the status is ok, false otherwise
|
||||
"""
|
||||
return response["status"] == "ok"
|
||||
|
||||
@staticmethod
|
||||
def find_court(court_id: int, sport: Sport) -> Court:
|
||||
"""
|
||||
Get all the court information based on the court id and the sport name
|
||||
|
||||
:param court_id: the court id
|
||||
:param sport: the sport
|
||||
:return: the court that has the given id and sport name
|
||||
"""
|
||||
for court in sport.courts:
|
||||
if court.id == court_id:
|
||||
return court
|
||||
|
||||
async def has_user_available_slots(self, user: User, club: Club) -> bool:
|
||||
"""
|
||||
Checks if a user has available booking slot.
|
||||
If a user already has an ongoing booking, it is considered as no slot is
|
||||
available
|
||||
|
||||
:param user: The user to check the booking availability
|
||||
:param club: The club of the user
|
||||
:return: True if the user has no ongoing booking, False otherwise
|
||||
"""
|
||||
connector = GestionSportsConnector(club)
|
||||
async with ClientSession() as session:
|
||||
await connector.land(session)
|
||||
await connector.login(session, user)
|
||||
bookings = await connector.get_ongoing_bookings(session)
|
||||
bookings = await self.get_ongoing_bookings(session, connector)
|
||||
|
||||
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
|
||||
|
@ -128,6 +175,71 @@ class GestionSportsServices:
|
|||
|
||||
return booking_date.at(booking_hour, booking_minute)
|
||||
|
||||
async def cancel_booking(
|
||||
self, user: User, club: Club, booking_filter: BookingFilter
|
||||
):
|
||||
connector = GestionSportsConnector(club)
|
||||
async with ClientSession() as session:
|
||||
await connector.land(session)
|
||||
await connector.login(session, user)
|
||||
|
||||
bookings = await self.get_ongoing_bookings(session, connector)
|
||||
for booking in bookings:
|
||||
if booking.matches(booking_filter):
|
||||
return await self.cancel_booking_id(session, connector, booking.id)
|
||||
|
||||
async def get_ongoing_bookings(
|
||||
self, session: ClientSession, connector: GestionSportsConnector
|
||||
) -> list[Booking]:
|
||||
"""
|
||||
Get the list of all ongoing bookings of a user.
|
||||
The steps to perform this are to get the user's bookings page and get a hidden
|
||||
property in the HTML to get a hash that will be used in the payload of the
|
||||
POST request (sic) to get the user's bookings.
|
||||
Gestion sports is really a mess!!
|
||||
|
||||
:param session: the client session shared among all connections
|
||||
:param connector: the connector used to send the requests
|
||||
:return: the list of all ongoing bookings of a user
|
||||
"""
|
||||
response = await connector.send_hash_request(session)
|
||||
hash_value = self.get_hash_input(await response.text())
|
||||
LOGGER.debug(f"Hash value: {hash_value}")
|
||||
response = await connector.send_user_bookings_request(session, hash_value)
|
||||
return [Booking(**booking) for booking in json.loads(await response.text())]
|
||||
|
||||
@staticmethod
|
||||
def get_hash_input(html_doc: str) -> str:
|
||||
"""
|
||||
There is a secret hash generated by Gestion sports that is reused when trying to get
|
||||
users bookings. This hash is stored in a hidden input with name "mesresas-hash"
|
||||
|
||||
:param html_doc: the html document when getting the page mes-resas.html
|
||||
:return: the value of the hash in the page
|
||||
"""
|
||||
soup = BeautifulSoup(html_doc, "html.parser")
|
||||
inputs = soup.find_all("input")
|
||||
for input_tag in inputs:
|
||||
if input_tag.get("name") == "mesresas-hash":
|
||||
return input_tag.get("value").strip()
|
||||
|
||||
async def cancel_booking_id(
|
||||
self, session: ClientSession, connector: GestionSportsConnector, booking_id: int
|
||||
) -> None:
|
||||
"""
|
||||
Send the HTTP request to cancel the booking
|
||||
|
||||
:param session: the client session shared among all connections
|
||||
:param connector: the connector used to send the requests
|
||||
:param booking_id: the id of the booking to cancel
|
||||
:return: the response from the client
|
||||
"""
|
||||
response = await connector.send_hash_request(session)
|
||||
hash_value = self.get_hash_input(await response.text())
|
||||
LOGGER.debug(f"Hash value: {hash_value}")
|
||||
|
||||
await connector.send_cancellation_request(session, booking_id, hash_value)
|
||||
|
||||
@staticmethod
|
||||
async def get_all_tournaments(user: User, club: Club) -> list[Tournament]:
|
||||
connector = GestionSportsConnector(club)
|
||||
|
@ -135,7 +247,7 @@ class GestionSportsServices:
|
|||
await connector.land(session)
|
||||
await connector.login(session, user)
|
||||
|
||||
session_html = await connector.send_tournaments_sessions_request(session)
|
||||
session_html = await connector.send_session_request(session)
|
||||
tournaments_id = GestionSportsServices.retrieve_tournament_session(
|
||||
await session_html.text()
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue