Ability to book a single court at a given time with gestion-sports

This commit is contained in:
Stanislas Jouffroy 2024-02-11 22:15:23 +01:00
parent 3f18f0f4a2
commit e953a4110b
14 changed files with 489 additions and 66 deletions

View file

@ -1,25 +1,33 @@
import asyncio
import logging
import pendulum
from aiohttp import ClientSession
from resa_padel import config
from resa_padel.gestion_sports_connector import GestionSportsConnector
from resa_padel.gestion_sports.gestion_sports_connector import GestionSportsConnector
from resa_padel.models import User, BookingFilter
LOGGER = logging.getLogger(__name__)
async def book(url: str, user: str, password: str, club_id: str) -> ClientSession:
async def book(url: str, user: User, booking_filter: BookingFilter) -> ClientSession:
async with ClientSession() as session:
platform = GestionSportsConnector(session, url)
await platform.connect()
await platform.login(user, password, club_id)
await platform.login(user)
await platform.book(booking_filter)
return session
def main() -> None:
LOGGER.info("Starting booking padel court")
asyncio.run(
book(config.GESTION_SPORTS_URL, config.USER, config.PASSWORD, config.CLUB_ID)
user = User(login=config.USER, password=config.PASSWORD, club_id=config.CLUB_ID)
booking_filter = BookingFilter(
court_id=config.COURT_ID,
sport_id=config.SPORT_ID,
date=pendulum.parse(config.DATE_TIME),
)
asyncio.run(book(config.GESTION_SPORTS_URL, user, booking_filter))
LOGGER.info("Finished booking padel court")

View file

@ -17,3 +17,6 @@ GESTION_SPORTS_URL = "https://toulousepadelclub.gestion-sports.com"
USER = os.environ.get("USER")
PASSWORD = os.environ.get("PASSWORD")
CLUB_ID = os.environ.get("CLUB_ID")
COURT_ID = os.environ.get("COURT_ID")
SPORT_ID = os.environ.get("SPORT_ID")
DATE_TIME = os.environ.get("DATE_TIME")

View file

View file

@ -2,7 +2,10 @@ import logging
from aiohttp import ClientSession, ClientResponse
from resa_padel.gestion_sports_payload_builder import GestionSportsPayloadBuilder
from resa_padel.gestion_sports.gestion_sports_payload_builder import (
GestionSportsPayloadBuilder,
)
from resa_padel.models import User
LOGGER = logging.getLogger(__name__)
HEADERS = {
@ -37,11 +40,11 @@ class GestionSportsConnector:
await response.text()
return response
async def login(self, user: str, password: str, club_id: str) -> ClientResponse:
async def login(self, user: User) -> ClientResponse:
payload = (
self.payload_builder.login(user)
.password(password)
.club_id(club_id)
self.payload_builder.login(user.login)
.password(user.password)
.club_id(user.club_id)
.build_login_payload()
)
@ -52,3 +55,22 @@ class GestionSportsConnector:
) as response:
await response.text()
return response
async def book(self, booking_filter) -> ClientResponse:
date_format = "%d/%m/%Y"
time_format = "%H:%M"
payload = (
self.payload_builder.date(booking_filter.date.date().strftime(date_format))
.time(booking_filter.date.time().strftime(time_format))
.sport_id(booking_filter.sport_id)
.court_id(booking_filter.court_id)
.build_booking_payload()
)
booking_url = f"{self.url}/membre/reservation.html?"
async with self.session.post(
booking_url, data=payload, headers=HEADERS
) as response:
await response.text()
return response

View file

@ -0,0 +1,69 @@
from resa_padel.exceptions import ArgumentMissing
class GestionSportsPayloadBuilder:
def __init__(self):
self._login = None
self._password = None
self._club_id = None
self._date = None
self._time = None
self._sport_id = None
self._court_id = None
def login(self, login):
self._login = login
return self
def password(self, password):
self._password = password
return self
def club_id(self, club_id):
self._club_id = club_id
return self
def date(self, date):
self._date = date
return self
def time(self, time):
self._time = time
return self
def sport_id(self, sport_id):
self._sport_id = sport_id
return self
def court_id(self, court_id):
self._court_id = court_id
return self
def build_login_payload(self):
if self._login is None:
raise ArgumentMissing("Login not provided")
if self.password is None:
raise ArgumentMissing("Password not provided")
if self._club_id is None:
raise ArgumentMissing("Club ID not provided")
return (
f"ajax=connexionUser&id_club={self._club_id}&email={self._login}&form_ajax=1&pass={self._password}&compte"
f"=user&playeridonesignal=0&identifiant=identifiant&externCo=true"
).encode("utf-8")
def build_booking_payload(self):
if self._date is None:
raise ArgumentMissing("Date not provided")
if self._time is None:
raise ArgumentMissing("Time not provided")
if self._sport_id is None:
raise ArgumentMissing("Sport ID not provided")
if self._court_id is None:
raise ArgumentMissing("Court ID not provided")
return (
f"ajax=addResa&date={self._date}&hour={self._time}&duration=90&partners=null|null|null"
f"&paiement=facultatif&idSport={self._sport_id}&creaPartie=false&idCourt={self._court_id}"
f"&pay=false&token=undefined&totalPrice=44&saveCard=0&foodNumber=0"
).encode("utf-8")

View file

@ -1,33 +0,0 @@
from resa_padel.exceptions import ArgumentMissing
class GestionSportsPayloadBuilder:
def __init__(self):
self._login = None
self._password = None
self._club_id = None
def login(self, login):
self._login = login
return self
def password(self, password):
self._password = password
return self
def club_id(self, club_id):
self._club_id = club_id
return self
def build_login_payload(self):
if self._login is None:
raise ArgumentMissing("Login not provided")
if self.password is None:
raise ArgumentMissing("Password not provided")
if self.club_id is None:
raise ArgumentMissing("Club ID not provided")
return (
f"ajax=connexionUser&id_club={self._club_id}&email={self._login}&form_ajax=1&pass={self._password}&compte"
f"=user&playeridonesignal=0&identifiant=identifiant&externCo=true"
).encode("utf-8")

14
resa_padel/models.py Normal file
View file

@ -0,0 +1,14 @@
from pydantic import BaseModel, Field
from pydantic_extra_types.pendulum_dt import DateTime
class User(BaseModel):
login: str = Field()
password: str = Field(repr=False)
club_id: str = Field()
class BookingFilter(BaseModel):
court_id: int = Field()
sport_id: int = Field()
date: DateTime = Field()