Compare commits

...

2 Commits

Author SHA1 Message Date
Stijn De Clercq 1857bdefe9 Rework schedule a bit to make it more maintainable & easier to code 2021-08-06 23:47:50 +02:00
Stijn De Clercq 54d31c943a Support to show the schedule for a requested day AFTER holidays 2021-08-06 20:52:56 +02:00
7 changed files with 288 additions and 65 deletions

View File

@ -1,12 +1,12 @@
import random import random
from data import constants from data import constants, schedule
from decorators import help from decorators import help
import discord import discord
from discord.ext import commands from discord.ext import commands
from enums.courses import years from enums.courses import years
from enums.help_categories import Category from enums.help_categories import Category
from functions import checks, eten, les from functions import checks, eten, les, les_rework
import json import json
@ -44,7 +44,9 @@ class School(commands.Cog):
@commands.command(name="Les", aliases=["Class", "Classes", "Sched", "Schedule"], usage="[Jaargang]* [Dag]*") @commands.command(name="Les", aliases=["Class", "Classes", "Sched", "Schedule"], usage="[Jaargang]* [Dag]*")
# @commands.check(checks.allowedChannels) # @commands.check(checks.allowedChannels)
@help.Category(category=Category.School) @help.Category(category=Category.School)
async def les(self, ctx, *day): async def les(self, ctx, day=None):
date = les_rework.find_target_date(day)
s = schedule.Schedule(date, day is not None)
return return
# parsed = les.parseArgs(day) # parsed = les.parseArgs(day)
# #

View File

@ -1,39 +1,37 @@
import json import dacite
from dacite import from_dict from dacite import from_dict
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime, timedelta from datetime import datetime, timedelta
from enums.platforms import Platforms from enums.platform import Platform, get_platform
from functions.config import get from functions.config import get
from functions.timeFormatters import fromArray, forward_to_weekday, intToWeekday
import json
from typing import Dict, Optional, List from typing import Dict, Optional, List
from functions.timeFormatters import fromArray
@dataclass @dataclass
class Holiday: class Holiday:
start_list: List[int] start_date: List[int]
end_list: List[int] end_date: List[int]
start_date: datetime = field(init=False) start_date_parsed: datetime = field(init=False)
end_date: datetime = field(init=False) end_date_parsed: datetime = field(init=False)
duration: timedelta = field(init=False) duration: timedelta = field(init=False)
def __post_init__(self): def __post_init__(self):
self.start_date = fromArray(self.start_list) self.start_date_parsed = fromArray(self.start_date)
self.end_date = fromArray(self.end_list) self.end_date_parsed = fromArray(self.end_date)
self.duration = self.end_date - self.start_date self.duration = self.end_date_parsed - self.start_date_parsed
def has_passed(self, current_day: datetime) -> bool: def has_passed(self, current_day: datetime) -> bool:
""" """
Check if a holiday has passed already Check if a holiday has passed already
""" """
return current_day > self.end_date return current_day > self.end_date_parsed
@dataclass @dataclass
class Course: class Course:
day: str name: str
week: int
course_dict: Dict
@dataclass @dataclass
@ -52,18 +50,49 @@ class Timeslot:
is_special: bool = False is_special: bool = False
location: Optional[Location] = None location: Optional[Location] = None
online_link: Optional[str] = None online_link: Optional[str] = None
online_platform: Optional[Platforms] = None online_platform: Optional[Platform] = None
@staticmethod
def from_slot_dict(slot_dict: Dict, course_dict: Dict, current_week: int):
"""
Construct a Timeslot from a dict of data
"""
if "weeks" in slot_dict and str(current_week) in slot_dict["weeks"]:
return Timeslot.special_from_dict(slot_dict, course_dict, str(current_week))
course = Course(course_dict["course"])
start_time = slot_dict["time"]["start"]
end_time = slot_dict["time"]["end"]
# Location can be none if a class is online-only
location = dacite.from_dict(Location, slot_dict["location"]) if "location" in slot_dict else None
# Find platform & link if this class is online
online_platform: Platform = get_platform(slot_dict.get("online", None))
online_link = course_dict["online_links"][Platform.value["rep"]] if online_platform is not None else None
return Timeslot(course=course, start_time=start_time, end_time=end_time, canceled=False, is_special=False,
location=location, online_platform=online_platform, online_link=online_link)
@staticmethod
def special_from_dict(slot_dict: Dict, course_dict: Dict, current_week: str):
"""
Create a SPECIAL Timeslot from a dict and data
"""
course = Course(course_dict["course"])
# TODO
@dataclass @dataclass
class Schedule: class Schedule:
day: datetime day: datetime
targetted_weekday: bool = False
schedule_dict: Dict = field(init=False) schedule_dict: Dict = field(init=False)
start_date: datetime start_date: datetime = field(init=False)
end_date: datetime end_date: datetime = field(init=False)
semester_over: bool = False semester_over: bool = False
holiday_offset: int = 0 holiday_offset: int = 0
current_holiday: Optional[Holiday] = None current_holiday: Optional[Holiday] = None
_weekday_str: str = field(init=False)
def __post_init__(self): def __post_init__(self):
self.schedule_dict: Dict = self.load_schedule_file() self.schedule_dict: Dict = self.load_schedule_file()
@ -77,9 +106,23 @@ class Schedule:
self.check_holidays() self.check_holidays()
# Show schedule for after holidays # TODO show a custom embed when no class instead of fast-forwarding
if self.current_holiday is not None: # # Store the target weekday (in case it exists) so we can ask for the next
self.day = self.current_holiday.end_date + timedelta(days=1) # # friday after the holiday, for example
# target_weekday = -1 if not self.targetted_weekday else self.day.weekday()
#
# # Show schedule for after holidays
# if self.current_holiday is not None:
# # Set day to day after holiday
# self.day = self.current_holiday.end_date_parsed + timedelta(days=1)
#
# # Find the next [DAY] after the holidays
# if target_weekday != -1:
# self.day = forward_to_weekday(self.day, target_weekday)
self._weekday_str = intToWeekday(self.day.weekday())
print(self.day)
def check_holidays(self): def check_holidays(self):
""" """
@ -89,23 +132,23 @@ class Schedule:
holiday: Holiday = from_dict(Holiday, hol_entry) holiday: Holiday = from_dict(Holiday, hol_entry)
# Hasn't happened yet, don't care # Hasn't happened yet, don't care
if holiday.start_date > self.day: if holiday.start_date_parsed > self.day:
continue continue
# In the past: add the offset # In the past: add the offset
if holiday.has_passed(self.day): if holiday.has_passed(self.day):
self.holiday_offset += (self.day - holiday.end_date) // 7 self.holiday_offset += (self.day - holiday.end_date_parsed) // 7
elif holiday.start_date <= self.day <= holiday.end_date: elif holiday.start_date_parsed <= self.day <= holiday.end_date_parsed:
self.current_holiday = holiday self.current_holiday = holiday
def load_schedule_file(self) -> Dict: def load_schedule_file(self) -> Dict:
""" """
Load the schedule from the JSON file Load the schedule from the JSON file
""" """
semester = get("semester") semester = get_platform("semester")
year = get("year") year = get_platform("year")
with open(f"files/{year}{semester}.json", "r") as fp: with open(f"files/schedules/{year}{semester}.json", "r") as fp:
return json.load(fp) return json.load(fp)
def get_week(self) -> int: def get_week(self) -> int:
@ -121,8 +164,29 @@ class Schedule:
# Add +1 at the end because week 1 would be 0 as it's not over yet # Add +1 at the end because week 1 would be 0 as it's not over yet
return (diff.days // 7) + self.holiday_offset + 1 return (diff.days // 7) + self.holiday_offset + 1
def find_slots_for_course(self, course_dict: Dict, current_week: int) -> List[Timeslot]:
"""
Create time timeslots for a course
"""
slots_today = []
# First create a list of all slots of today
for slot in course_dict["slots"]:
# This slot is for a different day
if slot["time"]["day"] != self._weekday_str.lower():
continue
slots_today.append(slot)
# Create Timeslots
slots_today = list(map(lambda x: Timeslot.from_slot_dict(x, course_dict, current_week), slots_today))
return slots_today
def create_schedule(self): def create_schedule(self):
""" """
Create the schedule for the current week Create the schedule for the current week
""" """
week: int = self.get_week() week: int = self.get_week()
slots: List[List[Timeslot]] = [self.find_slots_for_course(course, week) for course in self.schedule_dict["schedule"]]
slots_flattened = [item for sublist in slots for item in sublist]

View File

@ -1,7 +1,8 @@
from enum import Enum from enum import Enum
from typing import Optional
class Platforms(Enum): class Platform(Enum):
""" """
An Enum to represent online class platforms An Enum to represent online class platforms
Name: The name of the platform Name: The name of the platform
@ -11,3 +12,17 @@ class Platforms(Enum):
MSTeams = {"name": "MS Teams", "rep": "msteams"} MSTeams = {"name": "MS Teams", "rep": "msteams"}
Ufora = {"name": "Ufora", "rep": "ufora"} Ufora = {"name": "Ufora", "rep": "ufora"}
Zoom = {"name": "Zoom", "rep": "zoom"} Zoom = {"name": "Zoom", "rep": "zoom"}
def get_platform(rep: Optional[str]) -> Optional[Platform]:
"""
Find the platform that corresponds to the given name
"""
if rep is None:
return None
for platform in Platform:
if platform.value["rep"] == rep:
return platform
return None

View File

@ -1 +1 @@
{"semester": "2", "year": "2", "years": 2, "jpl": 161733, "jpl_day": 24} {"semester": "1", "year": "3", "years": 3, "jpl": 161733, "jpl_day": 24}

View File

@ -0,0 +1,139 @@
{
"semester_start": [1, 7, 2021],
"semester_end": [16, 8, 2021],
"holidays": [
{
"start_date": [2, 7, 2021],
"end_date": [10, 8, 2021]
}
],
"schedule": [
{
"course": "Computerarchitectuur",
"online_links": {
"zoom": "https://ufora.ugent.be/d2l/ext/rp/228912/lti/framedlaunch/556e197e-e87b-4c27-be5d-53adc7a41826",
"msteams": "https://teams.microsoft.com/l/team/19%3ad7295f0bc4634a61b461504d4a7134b3%40thread.tacv2/conversations?groupId=8755cb96-1ef5-4ea3-b806-eeebf8a85ae8&tenantId=d7811cde-ecef-496c-8f91-a1786241b99c"
},
"slots": [
]
},
{
"course": "Multimedia",
"online_links": {
"zoom": "https://ugent-be.zoom.us/j/94248831947?pwd=ZCt4UnBLSzViZnFEQmkzWE5SYnF2QT09"
},
"slots": [
{
"location": {
"campus": "Sterre",
"building": "S9",
"room": "A3"
},
"time": {
"day": "woensdag",
"start": 1130,
"end": 1330
}
},
{
"online": "ZOOM",
"time": {
"day": "vrijdag",
"start": 1300,
"end": 1530
}
}
]
},
{
"course": "Wetenschappelijk Rekenen",
"online_links": {
"zoom": "https://ufora.ugent.be/d2l/ext/rp/236404/lti/framedlaunch/556e197e-e87b-4c27-be5d-53adc7a41826"
},
"slots": [
{
"online": "ZOOM",
"time": {
"day": "dinsdag",
"start": 1130,
"end": 1300
}
},
{
"online": "ZOOM",
"time": {
"day": "woensdag",
"start": 1500,
"end": 1800
}
},
{
"online": "ZOOM",
"time": {
"day": "donderdag",
"start": 830,
"end": 1000
}
}
]
},
{
"course": "Software Engineering Lab 1",
"online_links": {
"zoom": "https://ufora.ugent.be/d2l/ext/rp/235800/lti/framedlaunch/556e197e-e87b-4c27-be5d-53adc7a41826",
"msteams": "https://teams.microsoft.com/l/team/19%3a4dfd5b2fb1ae4aa9b72706aa3a0d6867%40thread.tacv2/conversations?groupId=256d5c58-5d53-43f5-9436-497b0c852c75&tenantId=d7811cde-ecef-496c-8f91-a1786241b99c"
},
"slots": [
{
"online": "MS Teams",
"time": {
"day": "dinsdag",
"start": 1430,
"end": 1700
}
},
{
"online": "MS Teams",
"time": {
"day": "vrijdag",
"start": 830,
"end": 1130
}
}
]
},
{
"course": "Webdevelopment",
"online_links": {
"zoom": "https://ugent-be.zoom.us/j/93166767783?pwd=MWdvb1BnNnlPSnAyNk52QmRzdjcwdz09"
},
"slots": [
{
"weeks": {
"1": {
"canceled": true
}
},
"location": {
"campus": "Sterre",
"building": "S9",
"room": "A3"
},
"time": {
"day": "woensdag",
"start": 900,
"end": 1100
}
},
{
"online": "ZOOM",
"time": {
"day": "donderdag",
"start": 1000,
"end": 1300
}
}
]
}
]
}

View File

@ -1,5 +1,5 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from timeFormatters import dateTimeNow, weekdayToInt from functions.timeFormatters import dateTimeNow, weekdayToInt, forward_to_weekday, skip_weekends
from typing import Optional from typing import Optional
@ -24,35 +24,8 @@ def find_target_date(arg: Optional[str]) -> datetime:
elif arg.lower() == "overmorgen": # Day after tomorrow's schedule elif arg.lower() == "overmorgen": # Day after tomorrow's schedule
day += timedelta(days=2) day += timedelta(days=2)
# TODO show a different embed when "(over)morgen" is requested & it lands on a weekend
# Don't land on a weekend # Don't land on a weekend
day = skip_weekends(day) day = skip_weekends(day)
return day return day
def skip_weekends(day: datetime) -> datetime:
"""
Increment the current date if it's not a weekday
"""
weekday = day.weekday()
# Friday is weekday 4
if weekday > 4:
return day + timedelta(days=(7 - weekday))
return day
def forward_to_weekday(day: datetime, weekday: int) -> datetime:
"""
Increment a date until the weekday is the same as the one provided
Finds the "next" [weekday]
"""
current = day.weekday()
# This avoids negative numbers below, and shows
# next week in case the days are the same
if weekday >= current:
weekday += 7
return day + timedelta(days=(weekday - current))

View File

@ -138,13 +138,15 @@ def getPlural(amount, unit):
return dic[unit.lower()]["s" if amount == 1 else "p"] return dic[unit.lower()]["s" if amount == 1 else "p"]
def weekdayToInt(day) -> int: def weekdayToInt(day: str) -> int:
days = {"maandag": 0, "dinsdag": 1, "woensdag": 2, "donderdag": 3, "vrijdag": 4, "zaterdag": 5, "zondag": 6} days = {"maandag": 0, "dinsdag": 1, "woensdag": 2, "donderdag": 3, "vrijdag": 4, "zaterdag": 5, "zondag": 6}
if day.lower() not in days: # Allow abbreviations
return -1 for d, i in days.items():
if d.startswith(day):
return i
return days[day.lower()] return -1
def intToWeekday(day): def intToWeekday(day):
@ -164,3 +166,31 @@ def fromArray(data: List[int]) -> datetime:
year = str(data[2]) year = str(data[2])
return fromString(f"{day}/{month}/{year}") return fromString(f"{day}/{month}/{year}")
def skip_weekends(day: datetime) -> datetime:
"""
Increment the current date if it's not a weekday
"""
weekday = day.weekday()
# Friday is weekday 4
if weekday > 4:
return day + datetime.timedelta(days=(7 - weekday))
return day
def forward_to_weekday(day: datetime, weekday: int) -> datetime:
"""
Increment a date until the weekday is the same as the one provided
Finds the "next" [weekday]
"""
current = day.weekday()
# This avoids negative numbers below, and shows
# next week in case the days are the same
if weekday <= current:
weekday += 7
return day + datetime.timedelta(days=(weekday - current))