Skip to content

Commit

Permalink
Complete all inputs, fix logic and add improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
georgi-m-iliev committed Feb 22, 2024
1 parent 5cc31ea commit ddec7e7
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 48 deletions.
6 changes: 1 addition & 5 deletions .env
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
DEBUG=
USER=
PASSWORD=
LOGIN_URL='https://susi.uni-sofia.bg/ISSU/forms/Login.aspx'
ELECTIVES_URL='https://susi.uni-sofia.bg/ISSU/forms/students/ElectiveDisciplinesSubscribe.aspx'
PREFETCH_ELECTIVES_URL='https://susi.uni-sofia.bg/ISSU/forms/students/AllElectiveDisciplines.aspx'
HEADERS='{"sec-ch-ua": "\"Google Chrome\";v=\"117\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"117\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "Windows", "Upgrade-Insecure-Requests": "1", "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "host": "susi.uni-sofia.bg"}'
CAMPAIGN_START_DATE='2023-10-16 00:00:00'
CAMPAIGN_START_DATE='2024-02-23'
56 changes: 43 additions & 13 deletions actions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import os
import json
import pickle
Expand All @@ -7,18 +8,44 @@
from functions import *
from models import ElectivesCategory

LOGIN_URL = 'https://susi.uni-sofia.bg/ISSU/forms/Login.aspx'
ELECTIVES_URL = 'https://susi.uni-sofia.bg/ISSU/forms/students/ElectiveDisciplinesSubscribe.aspx'
PREFETCH_ELECTIVES_URL = 'https://susi.uni-sofia.bg/ISSU/forms/students/AllElectiveDisciplines.aspx'
HEADERS = {"sec-ch-ua": "\"Google Chrome\";v=\"117\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"117\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "Windows", "Upgrade-Insecure-Requests": "1", "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "host": "susi.uni-sofia.bg"}


def authenticate(session: requests.Session, progress_iter):
progress_iter = iter(progress_iter)
vstate_eventvalidation_request = session.get(os.getenv('LOGIN_URL'))

if os.path.exists('session'):
# there is session file
with open('session', 'rb') as f:
# read timestamp
try:
data = pickle.load(f)
# check if the session is older than 30 minutes
if datetime.datetime.now().timestamp() - data['timestamp'] > 600:
# if it is, delete the file and authenticate
os.unlink('session')
else:
# if it is not, load the session and return
session.cookies.update(data['session'])
print('Session loaded from file!')
for _ in progress_iter:
pass
return
except EOFError:
print('Session file is corrupted!')

vstate_eventvalidation_request = session.get(LOGIN_URL)
next(progress_iter)

vstate_eventvalidation_page_soup = BeautifulSoup(vstate_eventvalidation_request.text, 'html.parser')
vstate, eventvalidation = extract_vstate_eventvalidation(vstate_eventvalidation_page_soup)
next(progress_iter)

login_response = session.post(
url=os.getenv('LOGIN_URL'),
url=LOGIN_URL,
data={
'__EVENTARGUMENT': '',
'__EVENTTARGET': '',
Expand All @@ -29,7 +56,7 @@ def authenticate(session: requests.Session, progress_iter):
'txtUserName': os.getenv('USER'),
'txtPassword': os.getenv('PASSWORD')
},
headers=json.loads(os.getenv('HEADERS')),
headers=HEADERS,
)
next(progress_iter)

Expand All @@ -39,24 +66,27 @@ def authenticate(session: requests.Session, progress_iter):
next(progress_iter)
except StopIteration:
pass

# Save session to file
with open('session', 'wb') as f:
pickle.dump(session.cookies, f)
file = open('session', 'wb')
pickle.dump({'session': session.cookies, 'timestamp': datetime.datetime.now().timestamp()}, file)
file.close()

return login_response


def fetch_available_electives(session: requests.Session, categories: ElectivesCategory, progress_iter) -> list:
""" Returns a list of all electives names from queries tab. """
progress_iter = iter(progress_iter)

vstate_eventvalidation_request = session.get(os.getenv('PREFETCH_ELECTIVES_URL'))
vstate_eventvalidation_request = session.get(PREFETCH_ELECTIVES_URL)
vstate, eventvalidation = extract_vstate_eventvalidation(
BeautifulSoup(vstate_eventvalidation_request.text, 'html.parser')
)
next(progress_iter)

electives_names_request = session.post(
os.getenv('PREFETCH_ELECTIVES_URL'),
PREFETCH_ELECTIVES_URL,
data={
'SelectSemesterSessionMultiple1:cboSemester': categories.semester.value,
'SelectSemesterSessionMultiple1:cboYearSemester': categories.year,
Expand All @@ -73,7 +103,7 @@ def fetch_available_electives(session: requests.Session, categories: ElectivesCa
'disciplineType': categories.discipline_type.value,
'includedInEducationPlan': categories.plan_type.value,
},
headers=json.loads(os.getenv('HEADERS')),
headers=HEADERS,
)
try:
next(progress_iter)
Expand All @@ -84,10 +114,10 @@ def fetch_available_electives(session: requests.Session, categories: ElectivesCa


def fetch_electives_ids(session: requests.Session, categories: ElectivesCategory):
electives_request = session.get(os.getenv('ELECTIVES_URL'))
electives_request = session.get(ELECTIVES_URL)
vstate, eventvalidation = extract_vstate_eventvalidation(BeautifulSoup(electives_request.text, 'html.parser'))
electives_request = session.post(
url=os.getenv('ELECTIVES_URL'),
url=ELECTIVES_URL,
data={
'SelectSemesterSessionMultiple1:cboSemester': categories.semester.value,
'SelectSemesterSessionMultiple1:cboYearSemester': categories.year,
Expand All @@ -103,17 +133,17 @@ def fetch_electives_ids(session: requests.Session, categories: ElectivesCategory
'disciplineType': categories.discipline_type.value,
'includedInEducationPlan': categories.plan_type.value,
},
headers=json.loads(os.getenv('HEADERS')),
headers=HEADERS,
)

return extract_electives_ids(electives_request)


def enroll_selected_electives(session: requests.Session, categories: ElectivesCategory, wanted_electives: list, available_electives_ids: dict):
enrollment_request = session.post(
url=os.getenv('ELECTIVES_URL'),
url=ELECTIVES_URL,
data=prepare_electives_form_data(session, categories, wanted_electives, available_electives_ids),
headers=json.loads(os.getenv('HEADERS')),
headers=HEADERS,
)

return enrollment_request
16 changes: 14 additions & 2 deletions functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

from models import ElectivesCategory

ELECTIVES_URL = 'https://susi.uni-sofia.bg/ISSU/forms/students/ElectiveDisciplinesSubscribe.aspx'


def visualize_response(response: requests.models.Response):
""" Creates a temporary file with the response and opens it in the default browser. """
Expand Down Expand Up @@ -62,14 +64,14 @@ def extract_electives_ids(electives_request: requests.Response) -> dict:
elective_id = None
else:
elective_id = row.find('select')['name']
print(f'Found {elective_name}: {elective_id}')
# print(f'Found {elective_name}: {elective_id}')
electives_ids[elective_name] = elective_id

return electives_ids


def prepare_electives_form_data(session: requests.Session, categories: ElectivesCategory, wanted_electives: list, available_electives: dict) -> dict:
vstate_eventvalidation_request = session.get(os.getenv('ELECTIVES_URL'))
vstate_eventvalidation_request = session.get(ELECTIVES_URL)
vstate, eventvalidation = extract_vstate_eventvalidation(
BeautifulSoup(vstate_eventvalidation_request.text, 'html.parser')
)
Expand All @@ -94,3 +96,13 @@ def prepare_electives_form_data(session: requests.Session, categories: Electives
form_data[available_electives[elective]] = '1'

return form_data


def save_to_env(key, value):
from dotenv import set_key
from pathlib import Path

env_file_path = Path('.env')
env_file_path.touch(mode=0o600, exist_ok=False)
# Save some values to the file.
set_key(dotenv_path=env_file_path, key_to_set=key, value_to_set=value)
46 changes: 22 additions & 24 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,35 @@


def setup():
pass
disclaimer()

if not os.getenv('USERNAME') or not os.getenv('PASSWORD'):
get_credentials()

def run():
disclaimer()
get_credentials()

def run():
session = requests.Session()
retries = Retry(total=100,
backoff_factor=0.1,
status_forcelist=[500, 502, 503, 504])
session.mount('https://', HTTPAdapter(max_retries=retries))

if os.path.exists('session'):
with open('session', 'rb') as f:
session.cookies.update(pickle.load(f))
with ProgressBar() as pb:
progress_iter = pb(range(3), label='Вписване...')
authenticate(session, progress_iter)
print('Успешно вписан!\n')

if session.cookies:
print('Успешно вписан!')
if not os.getenv('CAMPAIGN_START_DATE'):
campaign_start = get_campaign_start_date()
else:
with ProgressBar() as pb:
progress_iter = pb(range(3), label='Вписване...')
authenticate(session, progress_iter)
print('Успешно вписан!\n')
electives_categories = ElectivesCategory(
discipline_type=ElectivesCategory.ElectiveType.ELECTIVE,
plan_type=ElectivesCategory.ElectivePlanType.INCLUDED_IN_EDUCATION_PLAN_OTHERS,
semester=ElectivesCategory.Semester.SUMMER,
year=2023
)
campaign_start = datetime.datetime.strptime(os.getenv('CAMPAIGN_START_DATE'), '%Y-%m-%d')
electives_categories = select_electives_categories()

with ProgressBar() as pb:
progress_iter = pb(range(1), label='Извличане на избираеми...')

available_electives = fetch_available_electives(session, electives_categories, progress_iter)
print('\nИзбираемите са извлечени успешно!')
# print('\nИзбираемите са извлечени успешно!')
time.sleep(2)

# sort electives by length
Expand Down Expand Up @@ -75,6 +69,10 @@ def run():
if not last_confirmation_before_post_result:
continue

countdown_to_campaign(campaign_start)
# wait 5 secs before posting
time.sleep(5)

# clear console
print("\033[H\033[J")
if last_confirmation_before_post_result:
Expand All @@ -88,16 +86,16 @@ def run():
pass
progress_iter = iter(pb(range(2), label='Записване за избираеми...'))
next(progress_iter)
enroll_selected_electives(session, electives_categories, selected_electives, electives_ids)
result = enroll_selected_electives(session, electives_categories, selected_electives, electives_ids)
try:
next(progress_iter)
except StopIteration:
pass
success_message(selected_electives)
#TODO: add visualization of
if success_message(selected_electives):
visualize_response(result)

else:
print('Край!')
print('Чао!')


def main():
Expand Down
28 changes: 28 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import datetime
from enum import Enum
from dataclasses import dataclass

from prompt_toolkit.validation import Validator, ValidationError



@dataclass
class ElectivesCategory:
Expand All @@ -21,3 +25,27 @@ class Semester(Enum):
plan_type: ElectivePlanType
semester: Semester
year: int


class DateValidator(Validator):
def validate(self, document):
text = document.text

try:
test = datetime.datetime.strptime(text, '%Y-%m-%d')
except ValueError:
raise ValidationError(message='Грешен формат на датата', cursor_position=len(text))

if test < datetime.datetime.now():
raise ValidationError(message='Дата не може да бъде в миналото', cursor_position=len(text))


class YearValidator(Validator):
def validate(self, document):
text = document.text

if not text.isdigit():
raise ValidationError(message='Годината трябва да бъде число!', cursor_position=len(text))

if len(text) != 4:
raise ValidationError(message='Годината трябва да бъде четирицифрено число!', cursor_position=len(text))
Loading

0 comments on commit ddec7e7

Please sign in to comment.