15.14 Project Info
We will agree on a class project that uses PySimpleGUI. Useful information for the project will appear in this space.
15.14 Project Info
After brainstorming project ideas, we settled on an Assignment Tracker app. Assignment data will be obtained
from the myPARISH calendar in the form of a webdev:
feed link. Assignments (events in
calendar speak) info will be gleaned from the feed and used to allow the user to view their assignments. The
user will be able to filter the data by date and course.
To facilitate working with webdev:
data, an Assignments
API is provided. Here is Assignments.py
, which defines the API.
from icalevents.icalevents import events
from datetime import datetime, timedelta, date
from pytz import utc, timezone
import html
class Assignments:
"""
Assignments are retrieved from the myPARISH calendar webcal feed. The assumption is made that the calendar is
first limited to only assignments, without other kinds of events. A list is created containing `Assignment` objects.
"""
def __init__(self, start, end, url):
"""
Constructor for `Assignments` class.
:param start: datetime object -> start date for assignments list
:param end: datetime object -> end date for assignments list
:param url: str -> webcal url to myPARISH calendar
"""
self.URL = url
self.start_date = self.normalize_date(start)
self.end_date = self.normalize_date(end)
self.assignments = self.get_assignments()
def __str__(self):
"""
String representation for `Assignments` objects
:return: str
"""
output = ''
for assignment in self.assignments:
output += str(assignment) + '\n'
return output
def get_assignments(self):
"""
Extract assignments from webcal feed and populate a list.
:return: list -> list of `Assignment` objects [`Assignment`, `Assignment`, ...]
"""
es = events(self.URL, start=self.start_date, end=self.end_date, fix_apple=True)
es.sort()
ret_list = []
for event in es:
start_time = self.normalize_date(event.start)
ret_list.append(Assignment(start_time,
html.unescape(self.get_course(event.summary)),
html.unescape(self.get_name(event.summary))
))
return ret_list
@staticmethod
def normalize_date(dt):
"""
Convert `date` or `datetime` to `datetime` with timezone.
:param dt: date to normalize
:return: date as `datetime` with timezone
"""
if type(dt) is date:
dt = datetime(dt.year, dt.month, dt.day, 0, 0)
elif type(dt) is datetime:
dt = dt
else:
raise ValueError("unknown type %s" % type(dt))
if not dt.tzinfo:
dt = utc.localize(dt)
return dt
@staticmethod
def get_course(summary):
"""
Extract course name from `event.summary` property
:param summary: `event.summary`
:return: str -> name of course or `None`
"""
if ':' in summary:
course = summary[:summary.find(':')]
return course.strip()
return None
@staticmethod
def get_name(summary):
"""
Extract assignment name from `event.summary` property
:param summary: `event.summary`
:return: str -> name of assignment or `None`
"""
if ':' in summary:
name = summary[summary.find(':') + 1:]
return name.strip()
return None
class Assignment:
"""
Class for each individual assignment.
"""
def __init__(self, d, c, n):
"""
Constructor for `Assignment` class
:param d: datetime -> due date of assignment
:param c: str -> course name
:param n: str -> assignment name
"""
self.date = d
self.course = c
self.name = n
def __str__(self):
"""
String representation for `Assignment` objects
:return: str
"""
return f'{datetime.date(self.date)} | {self.course} | {self.name}'
def main():
"""
This function executes only if this file is run directly. It is only present to show example usage.
Code that utilizes `Assignments` should be located in other files, not this one.
:return:
"""
# Get URL from user. Possibly store in text file.
url = 'webcal://parish.myschoolapp.com/podium/feed/iCal.aspx?z=PBfWPW%2b7Bf8znQAnWIawKSUMruwMfYfE8DOy5Pq3KcqL5EEpKZ3SaFZKWyirslzw8uqEHM2%2bpImt5ve%2b8ykwEg%3d%3d'
# Also get these from user
start_yr = 2023
start_mth = 3
start_day = 6
end_yr = 2023
end_mth = 3
end_day = 15
# create Assignments object
my_assignments = Assignments(datetime(start_yr, start_mth, start_day), datetime(end_yr, end_mth, end_day), url)
# print assignments in a loop
# selection can be done on assignment properties
print('\nAssignments for Coding for OOP...')
for assignment in my_assignments.assignments:
if 'Coding for OOP' in assignment.course:
print(f'Date: {datetime.date(assignment.date)} | Course: {assignment.course} | Name: {assignment.name}')
# print all assignments
print('\nAll assignments...')
print(my_assignments)
print('Both printouts are limited to the date range given.')
if __name__ == '__main__':
main()
The main()
function at the end shows how one can work with the data.