refactor: Split code into many files
This commit is contained in:
295
session_manager.py
Normal file
295
session_manager.py
Normal file
@@ -0,0 +1,295 @@
|
||||
# Native modules
|
||||
import logging
|
||||
import pytz
|
||||
import time
|
||||
from datetime import date
|
||||
from typing import List, Dict, Optional, Any
|
||||
from datetime import datetime, timedelta, date
|
||||
|
||||
# Third-party modules
|
||||
import requests
|
||||
from dateutil.parser import parse
|
||||
|
||||
# Import the preferred sessions from the session_config module
|
||||
from session_config import PREFERRED_SESSIONS
|
||||
|
||||
# Import the AuthHandler class
|
||||
from auth import AuthHandler
|
||||
|
||||
class SessionManager:
|
||||
"""
|
||||
A class for managing CrossFit sessions.
|
||||
This class handles session availability checking, booking,
|
||||
and session-related operations.
|
||||
"""
|
||||
|
||||
def __init__(self, auth_handler: AuthHandler) -> None:
|
||||
"""
|
||||
Initialize the SessionManager with necessary attributes.
|
||||
|
||||
Args:
|
||||
auth_handler (AuthHandler): AuthHandler instance for authentication.
|
||||
"""
|
||||
self.auth_handler = auth_handler
|
||||
self.session = requests.Session()
|
||||
self.base_headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:140.0) Gecko/20100101 Firefox/140.0",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Nubapp-Origin": "user_apps",
|
||||
}
|
||||
self.session.headers.update(self.base_headers)
|
||||
|
||||
# Define mandatory parameters for API calls
|
||||
self.mandatory_params = {
|
||||
"app_version": "5.09.21",
|
||||
"device_type": "3",
|
||||
"id_application": "81560887",
|
||||
"id_category_activity": "677"
|
||||
}
|
||||
|
||||
def get_auth_headers(self) -> Dict[str, str]:
|
||||
"""
|
||||
Return headers with authorization from the AuthHandler.
|
||||
|
||||
Returns:
|
||||
Dict[str, str]: Headers dictionary with authorization if available.
|
||||
"""
|
||||
return self.auth_handler.get_auth_headers()
|
||||
|
||||
def get_available_sessions(self, start_date: date, end_date: date) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Fetch available sessions from the API.
|
||||
|
||||
Args:
|
||||
start_date (date): Start date for fetching sessions.
|
||||
end_date (date): End date for fetching sessions.
|
||||
|
||||
Returns:
|
||||
Optional[Dict[str, Any]]: Dictionary containing available sessions if successful, None otherwise.
|
||||
"""
|
||||
if not self.auth_handler.auth_token or not self.auth_handler.user_id:
|
||||
logging.error("Authentication required - missing token or user ID")
|
||||
return None
|
||||
|
||||
url = "https://sport.nubapp.com/api/v4/activities/getActivitiesCalendar.php"
|
||||
|
||||
# Prepare request with mandatory parameters
|
||||
request_data = self.mandatory_params.copy()
|
||||
request_data.update({
|
||||
"id_user": self.auth_handler.user_id,
|
||||
"start_timestamp": start_date.strftime("%d-%m-%Y"),
|
||||
"end_timestamp": end_date.strftime("%d-%m-%Y")
|
||||
})
|
||||
|
||||
# Add retry logic
|
||||
for retry in range(3):
|
||||
try:
|
||||
response = self.session.post(
|
||||
url,
|
||||
headers=self.get_auth_headers(),
|
||||
data=request_data,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
elif response.status_code == 401:
|
||||
logging.error("401 Unauthorized - token may be expired or invalid")
|
||||
return None
|
||||
elif 500 <= response.status_code < 600:
|
||||
logging.error(f"Server error {response.status_code}")
|
||||
raise requests.exceptions.ConnectionError(f"Server error {response.status_code}")
|
||||
else:
|
||||
logging.error(f"Unexpected status code: {response.status_code} - {response.text[:100]}")
|
||||
return None
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
if retry == 2:
|
||||
logging.error(f"Final retry failed: {str(e)}")
|
||||
raise
|
||||
wait_time = 1 * (2 ** retry)
|
||||
logging.warning(f"Request failed (attempt {retry+1}/3): {str(e)}. Retrying in {wait_time}s...")
|
||||
time.sleep(wait_time)
|
||||
|
||||
return None
|
||||
|
||||
def book_session(self, session_id: str) -> bool:
|
||||
"""
|
||||
Book a specific session.
|
||||
|
||||
Args:
|
||||
session_id (str): ID of the session to book.
|
||||
|
||||
Returns:
|
||||
bool: True if booking is successful, False otherwise.
|
||||
"""
|
||||
url = "https://sport.nubapp.com/api/v4/activities/bookActivityCalendar.php"
|
||||
data = {
|
||||
**self.mandatory_params,
|
||||
"id_activity_calendar": session_id,
|
||||
"id_user": self.auth_handler.user_id,
|
||||
"action_by": self.auth_handler.user_id,
|
||||
"n_guests": "0",
|
||||
"booked_on": "1",
|
||||
"device_type": self.mandatory_params["device_type"],
|
||||
"token": self.auth_handler.auth_token
|
||||
}
|
||||
|
||||
for retry in range(3):
|
||||
try:
|
||||
response = self.session.post(
|
||||
url,
|
||||
headers=self.get_auth_headers(),
|
||||
data=data,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
json_response = response.json()
|
||||
if json_response.get("success", False):
|
||||
logging.info(f"Successfully booked session {session_id}")
|
||||
return True
|
||||
else:
|
||||
logging.error(f"API returned success:false: {json_response} - Session ID: {session_id}")
|
||||
return False
|
||||
|
||||
logging.error(f"HTTP {response.status_code}: {response.text[:100]}")
|
||||
return False
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
if retry == 2:
|
||||
logging.error(f"Final retry failed: {str(e)}")
|
||||
raise
|
||||
wait_time = 1 * (2 ** retry)
|
||||
logging.warning(f"Request failed (attempt {retry+1}/3): {str(e)}. Retrying in {wait_time}s...")
|
||||
time.sleep(wait_time)
|
||||
|
||||
logging.error(f"Failed to complete request after 3 attempts")
|
||||
return False
|
||||
|
||||
def get_booked_sessions(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get a list of booked sessions.
|
||||
|
||||
Returns:
|
||||
A list of dictionaries containing information about the booked sessions.
|
||||
"""
|
||||
url = "https://sport.nubapp.com/api/v4/activities/getBookedActivities.php"
|
||||
data = {
|
||||
**self.mandatory_params,
|
||||
"id_user": self.auth_handler.user_id,
|
||||
"action_by": self.auth_handler.user_id
|
||||
}
|
||||
|
||||
for retry in range(3):
|
||||
try:
|
||||
response = self.session.post(
|
||||
url,
|
||||
headers=self.get_auth_headers(),
|
||||
data=data,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
json_response = response.json()
|
||||
if json_response.get("success", False):
|
||||
return json_response.get("data", [])
|
||||
logging.error(f"API returned success:false: {json_response}")
|
||||
return []
|
||||
|
||||
logging.error(f"HTTP {response.status_code}: {response.text[:100]}")
|
||||
return []
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
if retry == 2:
|
||||
logging.error(f"Final retry failed: {str(e)}")
|
||||
raise
|
||||
wait_time = 1 * (2 ** retry)
|
||||
logging.warning(f"Request failed (attempt {retry+1}/3): {str(e)}. Retrying in {wait_time}s...")
|
||||
time.sleep(wait_time)
|
||||
|
||||
logging.error(f"Failed to complete request after 3 attempts")
|
||||
return []
|
||||
|
||||
def is_session_bookable(self, session: Dict[str, Any], current_time: datetime) -> bool:
|
||||
"""
|
||||
Check if a session is bookable based on user_info.
|
||||
|
||||
Args:
|
||||
session (Dict[str, Any]): Session data.
|
||||
current_time (datetime): Current time for comparison.
|
||||
|
||||
Returns:
|
||||
bool: True if the session is bookable, False otherwise.
|
||||
"""
|
||||
user_info = session.get("user_info", {})
|
||||
|
||||
# First check if can_join is true (primary condition)
|
||||
if user_info.get("can_join", False):
|
||||
return True
|
||||
|
||||
# Default case: not bookable
|
||||
return False
|
||||
|
||||
def matches_preferred_session(self, session: Dict[str, Any], current_time: datetime) -> bool:
|
||||
"""
|
||||
Check if session matches one of your preferred sessions with exact matching.
|
||||
|
||||
Args:
|
||||
session (Dict[str, Any]): Session data.
|
||||
current_time (datetime): Current time for comparison.
|
||||
|
||||
Returns:
|
||||
bool: True if the session matches a preferred session, False otherwise.
|
||||
"""
|
||||
try:
|
||||
session_time = parse(session["start_timestamp"])
|
||||
if not session_time.tzinfo:
|
||||
session_time = pytz.timezone("Europe/Paris").localize(session_time)
|
||||
|
||||
day_of_week = session_time.weekday()
|
||||
session_time_str = session_time.strftime("%H:%M")
|
||||
session_name = session.get("name_activity", "").upper()
|
||||
|
||||
for preferred_day, preferred_time, preferred_name in PREFERRED_SESSIONS:
|
||||
# Exact match
|
||||
if (day_of_week == preferred_day and
|
||||
session_time_str == preferred_time and
|
||||
preferred_name in session_name):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to check session: {str(e)} - Session: {session}")
|
||||
return False
|
||||
|
||||
def display_upcoming_sessions(self, sessions: List[Dict[str, Any]], current_time: datetime) -> None:
|
||||
"""
|
||||
Display upcoming sessions with ID, name, date, and time.
|
||||
|
||||
Args:
|
||||
sessions (List[Dict[str, Any]]): List of session data.
|
||||
current_time (datetime): Current time for comparison.
|
||||
"""
|
||||
if not sessions:
|
||||
logging.info("No sessions to display")
|
||||
return
|
||||
|
||||
logging.info("Upcoming sessions:")
|
||||
logging.info("ID\t\tName\t\tDate\t\tTime")
|
||||
logging.info("="*50)
|
||||
|
||||
for session in sessions:
|
||||
session_time = parse(session["start_timestamp"])
|
||||
if not session_time.tzinfo:
|
||||
session_time = pytz.timezone("Europe/Paris").localize(session_time)
|
||||
|
||||
# Format session details
|
||||
session_id = session.get("id_activity_calendar", "N/A")
|
||||
session_name = session.get("name_activity", "N/A")
|
||||
session_date = session_time.strftime("%Y-%m-%d")
|
||||
session_time_str = session_time.strftime("%H:%M")
|
||||
|
||||
# Display session details
|
||||
logging.info(f"{session_id}\t{session_name}\t{session_date}\t{session_time_str}")
|
||||
Reference in New Issue
Block a user