Added docstrings

This commit is contained in:
Stanislas Jouffroy 2024-02-18 09:16:11 +01:00
parent 5434a74d0f
commit ccd019eb4c
4 changed files with 174 additions and 21 deletions

View file

@ -14,6 +14,14 @@ LOGGER = logging.getLogger(__name__)
def wait_until_booking_time(club: Club, booking_filter: BookingFilter):
"""
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 club: the club where to book a court
:param booking_filter: the booking information
"""
LOGGER.info("Waiting booking time")
booking_datetime = build_booking_datetime(booking_filter, club)
now = pendulum.now()
@ -23,6 +31,15 @@ def wait_until_booking_time(club: Club, booking_filter: BookingFilter):
def build_booking_datetime(booking_filter: BookingFilter, club: Club) -> 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_filter: the booking information
:param club: the club where to book a court
:return: the date and time when the booking is open
"""
date_to_book = booking_filter.date
booking_date = date_to_book.subtract(days=club.booking_open_days_before)
@ -33,15 +50,28 @@ def build_booking_datetime(booking_filter: BookingFilter, club: Club) -> DateTim
async def book(club: Club, user: User, booking_filter: BookingFilter) -> int | None:
"""
Book a court for a user to a club following a booking filter
:param club: the club where to book a court
:param user: the user information
:param booking_filter: the information related to the booking
:return: the id of the booked court, or None if no court was booked
"""
async with ClientSession() as session:
platform = GestionSportsConnector(session, club.url)
await platform.connect()
await platform.land()
await platform.login(user, club)
wait_until_booking_time(club, booking_filter)
return await platform.book(booking_filter, club)
def main() -> int | None:
"""
Main function used to book a court
:return: the id of the booked court, or None if no court was booked
"""
user = config.get_user()
booking_filter = config.get_booking_filter()
club = config.get_club()

View file

@ -13,6 +13,12 @@ load_dotenv()
def get_club() -> Club:
"""
Read the environment variables related to the current club
and build the Club object
:return: the club
"""
club_url = os.environ.get("CLUB_URL")
court_ids_tmp = os.environ.get("COURT_IDS") or ""
court_ids = (
@ -34,6 +40,12 @@ def get_club() -> Club:
def get_booking_filter() -> BookingFilter:
"""
Read the environment variables related to the current booking filter
and build the BookingFilter object
:return: the club
"""
sport_id_tmp = os.environ.get("SPORT_ID")
sport_id = int(sport_id_tmp) if sport_id_tmp else None
date_time_tmp = os.environ.get("DATE_TIME")
@ -42,12 +54,24 @@ def get_booking_filter() -> BookingFilter:
def get_user() -> User:
"""
Read the environment variables related to the current user
and build the User object
:return: the club
"""
login = os.environ.get("LOGIN")
password = os.environ.get("PASSWORD")
return User(login=login, password=password)
def get_post_headers(platform_id: str) -> dict:
"""
Get the headers for the POST endpoint related to a specific booking platform
:param platform_id: the platform to which the headers apply
:return: the headers as a dictionary
"""
root_path = Path(__file__).parent
headers_file = Path(root_path, "resources", platform_id, "post-headers.json")
with headers_file.open(mode="r", encoding="utf-8") as f:
@ -57,6 +81,9 @@ def get_post_headers(platform_id: str) -> dict:
def init_log_config():
"""
Read the logging.yaml file to initialize the logging configuration
"""
root_dir = os.path.realpath(os.path.dirname(__file__))
logging_file = root_dir + "/logging.yaml"

View file

@ -18,6 +18,9 @@ POST_HEADERS = config.get_post_headers("gestion-sports")
class GestionSportsConnector:
"""
Handle the specific booking requests to Gestion-Sports
"""
def __init__(self, session: ClientSession, url: str):
LOGGER.info("Initializing connection to GestionSports API")
@ -26,24 +29,49 @@ class GestionSportsConnector:
self.payload_builder = GestionSportsPayloadBuilder()
@property
def connection_url(self) -> str:
def landing_url(self) -> str:
"""
Get the URL to the landing page of Gestion-Sports
:return: the URL to the landing page
"""
return urljoin(self.url, "/connexion.php?")
@property
def login_url(self) -> str:
"""
Get the URL to the connection login of Gestion-Sports
:return: the URL to the login page
"""
return urljoin(self.url, "/connexion.php?")
@property
def booking_url(self) -> str:
"""
Get the URL to the booking page of Gestion-Sports
:return: the URL to the booking page
"""
return urljoin(self.url, "/membre/reservation.html?")
async def connect(self) -> ClientResponse:
async def land(self) -> ClientResponse:
"""
Perform the request to the landing page in order to get the cookie PHPSESSIONID
:return: the response from the landing page
"""
LOGGER.info("Connecting to GestionSports API")
async with self.session.get(self.connection_url) as response:
async with self.session.get(self.landing_url) as response:
await response.text()
return response
async def login(self, user: User, club: Club) -> ClientResponse:
"""
Perform the request to the log in the user
:return: the response from the login
"""
payload = (
self.payload_builder.login(user.login)
.password(user.password)
@ -58,6 +86,15 @@ class GestionSportsConnector:
return response
async def book(self, booking_filter: BookingFilter, club: Club) -> int | 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 booking_filter: the booking information
:param club: the club where to book the court
:return: the booked court, or None if no court was booked
"""
# use asyncio to request a booking on every court
# the gestion-sports backend is able to book only one court for a user
bookings = await asyncio.gather(