Skip to content
This repository has been archived by the owner on Feb 15, 2024. It is now read-only.

Commit

Permalink
Fix typos and add logging statements
Browse files Browse the repository at this point in the history
  • Loading branch information
ThePhaseless committed Nov 26, 2023
1 parent 76a4620 commit 2d9e19e
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 44 deletions.
14 changes: 10 additions & 4 deletions lib/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def changeClassroom(id: str, classroom: Location):
raise Exception("ERROR_CHANGING_CLASSROOM")

def addClassroom(classroom: Location):
classroomID = requests.post(api_url + "/Classrooms", headers=header, json=json.dumps(classroom.to_map()))
classroomID = requests.post(api_url + "/Classrooms", headers=header, json=classroom.to_map())
if classroomID.status_code != 200:
raise Exception("ERROR_POSTING_CLASSROOM")
classroomID = findClassroomID(classroom)
Expand Down Expand Up @@ -108,7 +108,7 @@ def changeSchedule(id: str, schedule: Schedule):

def findScheduleID(schedule: Schedule):
for scheduleJSON in getSchedules():
if scheduleJSON["name"] == schedule.name:
if scheduleJSON["name"] == schedule.name():
return scheduleJSON["id"]
raise Exception("ERROR_FINDING_SCHEDULE_ID")

Expand All @@ -129,7 +129,7 @@ def getLecturers():
def changeLecturer(id: str, lecturer: str):
lecturerJson = {"name": lecturer}
lecturerJson["id"] = id
with requests.put(api_url + "/Lecturers", headers=header, json=json.dumps(lecturerJson)) as req:
with requests.put(api_url + "/Lecturers", headers=header, json=lecturerJson) as req:
if req.status_code != 200:
raise Exception("ERROR_CHANGING_LECTURER")

Expand All @@ -140,7 +140,7 @@ def findLecturerID(lecturer: Lecturer):
raise Exception("ERROR_FINDING_LECTURER_ID")

def addLecturer(lecturer: Lecturer):
lecturerID = requests.post(api_url + "/Lecturers", headers=header, json=json.dumps(lecturer.to_map()))
lecturerID = requests.post(api_url + "/Lecturers", headers=header, json=lecturer.to_map())
if lecturerID.status_code != 200:
raise Exception("ERROR_POSTING_LECTURER")
lecturerID = findLecturerID(lecturer)
Expand Down Expand Up @@ -177,8 +177,14 @@ def addLesson(lesson: Lesson, scheduleID: str, groupID: str, classroomID: str, l
return lessonID

def changePlanData(schedules: List[Schedule]):
from lib.logger import logger
# Update schedules
for schedule in schedules:
logger.info("")
logger.info("Updating plan data for")
logger.info(schedule.name())
logger.info("Group " + schedule.group.name)
logger.info("Day " + schedule.lessons[0].day.name)
try:
scheduleID = findScheduleID(schedule)
except:
Expand Down
29 changes: 20 additions & 9 deletions lib/email_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from imapclient import IMAPClient
from imapclient.response_types import SearchIds
from typing import Any, Callable, List
from typing import Callable, List
from models.emailMessage import EmailMessage
from lib.logger import logger

# Get username and password from environment variables
import os
Expand All @@ -14,40 +15,50 @@
raise Exception("USERNAME_OR_PASSWORD_NOT_SET")

def getEmails():
messages:SearchIds = client.search()
messages:SearchIds = fetcher.search()
emails:List[EmailMessage] = []
for emailID in messages:
raw_email:bytes = client.fetch(emailID, ["RFC822"])[emailID][b"RFC822"]
raw_email:bytes = fetcher.fetch(emailID, ["RFC822"])[emailID][b"RFC822"]
emailMessage = EmailMessage.from_raw_email(raw_email)
emails.append(emailMessage)
return emails

def getNLatestEmail(n: int):
messages = client.search()
logger.info("Getting latest " + str(n+1) + ". email...")
messages = fetcher.search()
if len(messages) == 0:
logger.info("No new emails, skipping...")
return None
# Check if there any mail doesn't have the flag \
emailID = messages[-n-1]
raw_email = client.fetch(emailID, ["RFC822"])[emailID][b"RFC822"]
raw_email = fetcher.fetch(emailID, ["RFC822"])[emailID][b"RFC822"]
fetcher.set_flags(emailID, [b"\\Seen"])
emailMessage = EmailMessage.from_raw_email(raw_email)
return emailMessage

def listenForEmails(execOnNewEmail: Callable[[int], None]):
# Start an IDLE (IMAP IDLE) command - allows the client to listen for changes
client.idle()

print("Listening for new emails...")
logger.info("Listening for new emails...")
while True:
try:
pass
except Exception as e:
print("Stopping due to exception: " + str(e))
break
responses:List[Any] = client.idle_check(timeout=timeout)
if responses:
responses = client.idle_check(timeout=timeout)
if len(responses) > 0:
logger.info("Got event")
i: int=0
while i < len(responses):
execOnNewEmail(i)
i += 1
client.idle_done()

client = IMAPClient(imap_server)
fetcher = IMAPClient(imap_server)
client.login(username, password)
client.select_folder("INBOX")
fetcher.login(username, password)
client.select_folder("INBOX")
fetcher.select_folder("INBOX")
9 changes: 9 additions & 0 deletions lib/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
logger.info("Logger initialized")
24 changes: 17 additions & 7 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from io import BytesIO
import lib.email_client as email_client
import lib.api_client as api_client
from models.emailMessage import EmailMessage
from models.schedule import Schedule
from lib.logger import logger


def processAttachmentsForNLastestEmail(i: int):
# Get allowed senders from env
Expand All @@ -15,7 +16,10 @@ def processAttachmentsForNLastestEmail(i: int):
email_prefix += "+"

# Get latest email
latestEmail:EmailMessage = email_client.getNLatestEmail(i) # 0 = latest email, 1 = second latest email, etc.
latestEmail = email_client.getNLatestEmail(0) # 0 = latest email, 1 = second latest email, etc.

if latestEmail == None:
return

# Check if email send to email with prefix
if not latestEmail.recipient.startswith(email_prefix):
Expand All @@ -24,22 +28,28 @@ def processAttachmentsForNLastestEmail(i: int):

# Check if sender is allowed
if latestEmail.sender.split("<")[1].strip(">") not in allowedSenders:
print("Sender " + latestEmail.sender + "not allowed, skipping...")
logger.info("Sender " + latestEmail.sender + "not allowed, skipping...")
return

if len(latestEmail.attachments) == 0:
print("No attachments, skipping...")
logger.info("No attachments, skipping...")
return
for attachment in latestEmail.attachments:
if attachment.name.endswith(".xlsx"):
print("Found Excel file, processing...")
logger.info("Found Excel file, processing...")
timetable = Schedule.get_timetables_from_xlsx_data_openpyxl(BytesIO(attachment.content))
logger.info("Sending data to API...")
api_client.changePlanData(timetable)
logger.info("Done")
elif attachment.name.endswith(".txt"):
print("Found text file, processing...")
logger.info("Found text file, processing...")
# Text is in UTF-8 with BOM
timetable = Schedule.get_timetable_from_txt_data(attachment.content.decode("utf-8-sig").splitlines())
logger.info("Sending data to API...")
api_client.changePlanData([timetable])
logger.info("Done")
else:
print("Unknown file type, skipping...")
logger.warning("Unknown file type, skipping...")
# Process attachments for latest unread email, if script wasnt run before
processAttachmentsForNLastestEmail(0)
email_client.listenForEmails(processAttachmentsForNLastestEmail)
4 changes: 2 additions & 2 deletions models/lesson.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@

class LessonType(Enum):
LABORATORIES = "Laboratories"
PRACTICAL_CLASSES = "PracticalClasses"
PracticalClasses = "PracticalClasses"
LECTURE = "Lecture"
UNKNOWN = "Unknown"

def strToType(string:str):
if "lab" in string.lower():
return LessonType.LABORATORIES
elif "ćw" in string.lower():
return LessonType.PRACTICAL_CLASSES
return LessonType.PracticalClasses
elif "w" in string.lower():
return LessonType.LECTURE
else:
Expand Down
64 changes: 42 additions & 22 deletions models/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,18 @@ def __init__(self, faculty:str="", field:str="", degree:str="", mode:str="", yea
if lessons == []:
lessons = []
self.faculty = faculty
self.name = field + " R" + str(year) + "S" + str(semester)
self.field = field
self.degree = degree
self.mode = mode
self.year = year
self.semester = semester
self.group = group
self.group:Group = group
self.lessons: List[Lesson] = lessons

def name(self):
return self.field + " R" + str(self.year) + "S" + str(self.semester)


@classmethod
def get_timetables_from_xlsx_data_openpyxl(cls, data: BytesIO):
from openpyxl import load_workbook, cell
Expand All @@ -52,9 +55,14 @@ def parse_sheet(sheet: Worksheet):
year = schedule_data[2].split("Rok")[1].strip()
semester = schedule_data[3].split("Semestr")[1].strip()

time_row = sheet[3]
max_row = sheet.max_row
for i in range(4, max_row + 1):
time_row:tuple[Cell,...] = ()
i:int = 2
while sheet[i+1][1].value != None and sheet[i+1][1].value != "":
i += 1
# Check if this is time row
if sheet[i][1].value == "Grupy":
time_row = sheet[i]
continue
group_row = sheet[i]
schedule = parse_row(group_row, time_row)

Expand All @@ -68,7 +76,17 @@ def parse_sheet(sheet: Worksheet):
schedule.mode = "ZO"
schedule.faculty = "WZIM"

schedules.append(schedule)
# Check if schedule already exists
skip = False
for existing_schedule in schedules:
if existing_schedule.name() == schedule.name():
if existing_schedule.group.name == schedule.group.name:
existing_schedule.lessons.extend(schedule.lessons)
skip = True
break
if not skip:
schedules.append(schedule)


return schedules

Expand All @@ -80,7 +98,7 @@ def parse_row(row:tuple[Cell | MergedCell,...], time_row:tuple[Cell,...]):
if groupName == "Grupy":
return schedule

schedule.group = groupName
schedule.group = Group(groupName)

dayWithInfo = str(row[0].value).strip()
if "Piątek" in dayWithInfo:
Expand All @@ -99,7 +117,7 @@ def parse_row(row:tuple[Cell | MergedCell,...], time_row:tuple[Cell,...]):
if lessonRaw == "None":
continue

lesson.startTime = datetime.strptime(str(time_row[j].value), "%H:%M").time()
lesson.startTime = time_row[j].value

# Set j at the next Cell (not MergedCell)
while(j+1 < max_col):
Expand All @@ -109,7 +127,7 @@ def parse_row(row:tuple[Cell | MergedCell,...], time_row:tuple[Cell,...]):
else:
break

lesson.endTime = datetime.strptime(str(time_row[j].value), "%H:%M").time()
lesson.endTime = time_row[j].value

lessonRaw = lessonRaw.split(",")
lesson.name = lessonRaw[0].split("(")[0].strip()
Expand Down Expand Up @@ -138,7 +156,7 @@ def parse_row(row:tuple[Cell | MergedCell,...], time_row:tuple[Cell,...]):
lesson.lecturer.name = " ".join(teacherData[0:-2])
lesson.lecturer.surname = teacherData[-1].strip()

if not "]" in lessonRaw[-1]:
if not "]" in lessonRaw[-1] and len(lessonRaw) > 1:
lesson.comment = lessonRaw[-1].strip().strip(".")

schedule.lessons.append(lesson)
Expand All @@ -158,18 +176,19 @@ def get_timetable_from_txt_data(cls, data: List[str]):
# 10.10.2023 22:34
# WZIM, 2023, Jesień , ST, Inf, inż, R4, S7, gr1, ISI-1 ;
lessons:List[Lesson] = []
schedule = Schedule()
# Get name
scheduleInfo = data[1].strip().split(",")
faculty = scheduleInfo[0].strip() # WZIM
year = scheduleInfo[1].strip() # 2023
mode = scheduleInfo[3].strip() # ST
fieldOfStudent = scheduleInfo[4].strip() # Inf
if fieldOfStudent == "Inf":
fieldOfStudent = "Informatyka"
degree = scheduleInfo[5].strip() # inż
year = int(scheduleInfo[6].strip().removeprefix("R").strip()) # R4
semester = int(scheduleInfo[7].strip().removeprefix("S").strip()) # S7
group = scheduleInfo[8].strip().removeprefix("gr").strip() # gr1
schedule.faculty = scheduleInfo[0].strip() # WZIM
schedule.year = scheduleInfo[1].strip() # 2023
schedule.mode = scheduleInfo[3].strip() # ST
schedule.field = scheduleInfo[4].strip() # Inf
if schedule.field == "Inf":
schedule.field = "Informatyka"
schedule.degree = scheduleInfo[5].strip() # inż
schedule.year = int(scheduleInfo[6].strip().removeprefix("R").strip()) # R4
schedule.semester = int(scheduleInfo[7].strip().removeprefix("S").strip()) # S7
schedule.group = Group(scheduleInfo[8].strip().removeprefix("gr").strip()) # gr1

split_filter = "------------------------------------------------"
blocks: List[List[str]] = []
Expand Down Expand Up @@ -228,7 +247,8 @@ def get_timetable_from_txt_data(cls, data: List[str]):

# Add lesson to lessons list
lessons.append(lesson)
return cls(faculty, fieldOfStudent, degree, mode, year, semester, Group(group), lessons)
schedule.lessons = lessons
return schedule

@classmethod
def get_timetables_from_file(cls, filename: str) -> List["Schedule"]:
Expand All @@ -244,7 +264,7 @@ def get_timetables_from_file(cls, filename: str) -> List["Schedule"]:

def to_map(self):
return {
"name": secureString(self.name)
"name": secureString(self.name())
}

def __str__(self):
Expand Down

0 comments on commit 2d9e19e

Please sign in to comment.