mirror of
https://github.com/Code-For-Groningen/temmies.git
synced 2025-03-15 15:10:15 +01:00
Used the API for year logic
This commit is contained in:
parent
f6e6bc28d2
commit
da4705b56a
@ -1,9 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Main class for the Themis API
|
Main class for the Themis API using the new JSON endpoints.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import urllib3
|
|
||||||
import keyring
|
import keyring
|
||||||
import getpass
|
import getpass
|
||||||
from requests import Session
|
from requests import Session
|
||||||
@ -11,41 +9,49 @@ from bs4 import BeautifulSoup
|
|||||||
from .year import Year
|
from .year import Year
|
||||||
from .exceptions.illegal_action import IllegalAction
|
from .exceptions.illegal_action import IllegalAction
|
||||||
|
|
||||||
|
|
||||||
# Disable warnings
|
|
||||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
||||||
|
|
||||||
|
|
||||||
class Themis:
|
class Themis:
|
||||||
"""
|
"""
|
||||||
login: Login to Themis
|
Main class for interacting with Themis.
|
||||||
get_year: Get a year object
|
- login: Login to Themis
|
||||||
all_years: Get all years
|
- get_year: Get a year object
|
||||||
|
- all_years: Get all years
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, user: str):
|
def __init__(self, user: str):
|
||||||
|
"""
|
||||||
|
Initialize Themis object, logging in with the given user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user (str): Username to login with.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
user (str): Username.
|
||||||
|
password (str): Password, retrieved from keyring.
|
||||||
|
base_url (str): Base URL of the Themis website.
|
||||||
|
session (requests.Session): Authenticated session.
|
||||||
|
"""
|
||||||
self.user = user
|
self.user = user
|
||||||
self.password = self.__get_password()
|
self.password = self.__get_password()
|
||||||
self.session = self.login(user, self.password)
|
self.base_url = "https://themis.housing.rug.nl"
|
||||||
|
self.session = self.login(self.user, self.password)
|
||||||
def __get_password(self) -> str:
|
def __get_password(self) -> str:
|
||||||
"""
|
"""
|
||||||
Retrieve the password from the keyring, prompting the user if not found.
|
Retrieve the password from the keyring, prompting the user if not found.
|
||||||
"""
|
"""
|
||||||
password = keyring.get_password(f'{self.user}-temmies', self.user)
|
password = keyring.get_password(f"{self.user}-temmies", self.user)
|
||||||
if not password:
|
if not password:
|
||||||
print(f"Password for user '{self.user}' not found in keyring.")
|
print(f"Password for user '{self.user}' not found in keyring.")
|
||||||
password = getpass.getpass(prompt=f"Enter password for {self.user}: ")
|
password = getpass.getpass(prompt=f"Enter password for {self.user}: ")
|
||||||
keyring.set_password(f'{self.user}-temmies', self.user, password)
|
keyring.set_password(f"{self.user}-temmies", self.user, password)
|
||||||
print("Password saved securely in keyring.")
|
print("Password saved securely in keyring.")
|
||||||
return password
|
return password
|
||||||
|
|
||||||
def login(self, user: str, passwd: str) -> Session:
|
def login(self, user: str, passwd: str) -> Session:
|
||||||
"""
|
"""
|
||||||
login(self, user: str, passwd: str) -> Session
|
Login to Themis using the original method, parsing CSRF token from the login page.
|
||||||
Login to Themis
|
|
||||||
Set user to your student number and passwd to your password
|
|
||||||
"""
|
"""
|
||||||
|
session = Session()
|
||||||
|
login_url = f"{self.base_url}/log/in"
|
||||||
|
|
||||||
user_agent = (
|
user_agent = (
|
||||||
"Mozilla/5.0 (X11; Linux x86_64) "
|
"Mozilla/5.0 (X11; Linux x86_64) "
|
||||||
@ -57,52 +63,52 @@ class Themis:
|
|||||||
|
|
||||||
data = {"user": user, "password": passwd, "null": None}
|
data = {"user": user, "password": passwd, "null": None}
|
||||||
|
|
||||||
with Session() as s:
|
# Get login page to retrieve CSRF token
|
||||||
url = "https://themis.housing.rug.nl/log/in"
|
response = session.get(login_url, headers=headers, verify=False)
|
||||||
r = s.get(url, headers=headers, verify=False)
|
if response.status_code != 200:
|
||||||
soup = BeautifulSoup(r.text, "lxml")
|
raise ConnectionError("Failed to connect to Themis login page.")
|
||||||
|
|
||||||
# get the csrf token and add it to payload
|
# Parse CSRF token from login page
|
||||||
csrf_token = soup.find("input", attrs={"name": "_csrf"})["value"]
|
soup = BeautifulSoup(response.text, "lxml")
|
||||||
|
csrf_input = soup.find("input", attrs={"name": "_csrf"})
|
||||||
|
if not csrf_input or not csrf_input.get("value"):
|
||||||
|
raise ValueError("Unable to retrieve CSRF token.")
|
||||||
|
csrf_token = csrf_input["value"]
|
||||||
data["_csrf"] = csrf_token
|
data["_csrf"] = csrf_token
|
||||||
data["sudo"] = user.lower()
|
data["sudo"] = user.lower()
|
||||||
|
|
||||||
# Login
|
# Attempt login
|
||||||
r = s.post(url, data=data, headers=headers)
|
response = session.post(login_url, data=data, headers=headers)
|
||||||
|
if "Invalid credentials" in response.text:
|
||||||
# check if login was successful
|
|
||||||
log_out = "Welcome, logged in as" in r.text
|
|
||||||
if "Invalid credentials" in r.text:
|
|
||||||
# Prompt for password again
|
# Prompt for password again
|
||||||
print("Invalid credentials. Please try again.")
|
print("Invalid credentials. Please try again.")
|
||||||
passwd = getpass.getpass(prompt="Enter password: ")
|
passwd = getpass.getpass(prompt="Enter password: ")
|
||||||
keyring.set_password(f'{self.user}-temmies', self.user, passwd)
|
keyring.set_password(f'{self.user}-temmies', self.user, passwd)
|
||||||
return self.login(user, passwd)
|
return self.login(user, passwd)
|
||||||
|
elif "Welcome, logged in as" not in response.text:
|
||||||
|
raise ValueError("Login failed for an unknown reason.")
|
||||||
|
|
||||||
return s
|
return session
|
||||||
|
|
||||||
def get_year(self, start: int, end: int) -> Year:
|
def get_year(self, year_path: str) -> Year:
|
||||||
"""
|
"""
|
||||||
get_year(self, start: int, end: int) -> Year
|
Gets a Year object using the year path (e.g., '2023-2024').
|
||||||
Gets a year object
|
|
||||||
Set start to the start year and end to the end year (e.g. 2023-2024)
|
|
||||||
"""
|
"""
|
||||||
return Year(self.session, start, end)
|
return Year(self.session, year_path)
|
||||||
|
|
||||||
def all_years(self) -> list[Year]:
|
def all_years(self) -> list:
|
||||||
"""
|
"""
|
||||||
get_years(self, start: int, end: int) -> list[Year]
|
Gets all visible years as Year objects.
|
||||||
Gets all visible years
|
|
||||||
"""
|
"""
|
||||||
# All of them are in a big ul at the beginning of the page
|
navigation_url = f"{self.base_url}/api/navigation/"
|
||||||
r = self.session.get(self.url)
|
response = self.session.get(navigation_url)
|
||||||
soup = BeautifulSoup(r.text, "lxml")
|
if response.status_code != 200:
|
||||||
ul = soup.find("ul", class_="round")
|
raise ConnectionError("Failed to retrieve years from Themis API.")
|
||||||
lis = ul.find_all("li", class_="large")
|
|
||||||
|
years_data = response.json()
|
||||||
years = []
|
years = []
|
||||||
for li in lis:
|
for year_info in years_data:
|
||||||
# format: 2019-2020
|
if year_info.get("visible", False):
|
||||||
year = li.a.text.split("-")
|
year_path = year_info["path"].strip("/")
|
||||||
years.append(Year(self.session, int(year[0]), int(year[1])))
|
years.append(Year(self.session, year_path))
|
||||||
|
return years
|
||||||
return years # Return a list of year objects
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user