Add command to generate course guides, add autocompletion for some commands, create json file with course information & abbreviations

pull/105/head
Stijn De Clercq 2022-02-11 22:11:20 +01:00
parent 6d7b47fee0
commit c6958d22f3
3 changed files with 223 additions and 9 deletions

View File

@ -1,7 +1,8 @@
from discord.ext import commands
from discord.commands import slash_command, ApplicationContext, Option
from discord.commands import slash_command, ApplicationContext, Option, AutocompleteContext
from data import schedule
from data.courses import load_courses, find_course_from_name
from data.embeds.food import Menu
from data.embeds.deadlines import Deadlines
from functions import les, config
@ -10,13 +11,26 @@ from functions.timeFormatters import skip_weekends
from startup.didier import Didier
# Preload autocomplete constants to allow for smoother results
courses = load_courses()
days = ["Morgen", "Overmorgen", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag"]
def day_autocomplete(ctx: AutocompleteContext) -> list[str]:
return [day for day in days if day.lower().startswith(ctx.value.lower())]
def course_autocomplete(ctx: AutocompleteContext) -> list[str]:
return [course for course in courses if course.lower().startswith(ctx.value.lower())]
class SchoolSlash(commands.Cog):
def __init__(self, client: Didier):
self.client: Didier = client
@slash_command(name="eten", description="Menu in de UGent resto's op een bepaalde dag")
async def _food_slash(self, ctx: ApplicationContext,
dag: Option(str, description="Dag", required=False, default=None)
dag: Option(str, description="Dag", required=False, default=None, autocomplete=day_autocomplete)
):
embed = Menu(dag).to_embed()
await ctx.respond(embed=embed)
@ -28,21 +42,21 @@ class SchoolSlash(commands.Cog):
@slash_command(name="les", description="Lessenrooster voor [Dag] (default vandaag)",)
async def _schedule_slash(self, ctx: ApplicationContext,
day: Option(str, description="Dag", required=False, default=None)
dag: Option(str, description="Dag", required=False, default=None, autocomplete=day_autocomplete)
):
"""It's late and I really don't want to refactor the original right now"""
if day is not None:
day = day.lower()
if dag is not None:
dag = dag.lower()
date = les.find_target_date(day)
date = les.find_target_date(dag)
# Person explicitly requested a weekend-day
if day is not None and day.lower() in ("morgen", "overmorgen") and date.weekday() > 4:
return await ctx.respond(f"{capitalize(day)} is het weekend.", ephemeral=True)
if dag is not None and dag.lower() in ("morgen", "overmorgen") and date.weekday() > 4:
return await ctx.respond(f"{capitalize(dag)} is het weekend.", ephemeral=True)
date = skip_weekends(date)
s = schedule.Schedule(date, int(config.get("year")), int(config.get("semester")), day is not None)
s = schedule.Schedule(date, int(config.get("year")), int(config.get("semester")), dag is not None)
if s.semester_over:
return await ctx.respond("Het semester is afgelopen.", ephemeral=True)
@ -54,6 +68,21 @@ class SchoolSlash(commands.Cog):
return await ctx.respond(embed=s.create_schedule().to_embed())
@slash_command(name="fiche", description="Zoek de studiefiche voor een vak.")
async def _study_guide_slash(self, ctx: ApplicationContext,
vak: Option(str, description="Naam van het vak. Afkortingen werken ook, maar worden niet ge-autocompletet.",
required=True, autocomplete=course_autocomplete)):
# Find code corresponding to the search query
course = find_course_from_name(vak, courses)
# Code not found
if course is None:
return await ctx.respond(f"Onbekend vak: \"{vak}\".", ephemeral=True)
# Get the guide for the current year
year = 2018 + int(config.get("year"))
return await ctx.respond(f"https://studiekiezer.ugent.be/studiefiche/nl/{course.code}/{year}")
def setup(client: Didier):
client.add_cog(SchoolSlash(client))

64
data/courses.py 100644
View File

@ -0,0 +1,64 @@
from dataclasses import dataclass
from typing import Optional
import dacite
import json
@dataclass
class Course:
abbreviations: list[str]
code: str
name: str
year: int
alt: Optional[str] = None
def load_courses() -> dict[str, Course]:
"""Create a list of all courses"""
with open("files/courses.json", "r") as file:
data = json.load(file)
courses = {}
for course_name in data:
# Add name into the dict to allow flexibility
course_data = data[course_name]
course_data["name"] = course_name
courses[course_name] = dacite.from_dict(data_class=Course, data=course_data)
return courses
def find_course_from_name(name: str, courses: Optional[dict[str, Course]] = None, case_insensitive: bool = True) -> Optional[Course]:
# Allow passing a course dict in to avoid having to create it all the time
if courses is None:
courses = load_courses()
if case_insensitive:
name = name.lower()
def _perhaps_lower(inp: str) -> str:
"""Cast a string to lowercase if necessary"""
if case_insensitive:
return inp.lower()
return inp
# Iterate over all courses to look for a match
for course_name, course in courses.items():
# Check name first
if _perhaps_lower(course_name) == name:
return course
# Then abbreviations
for abbreviation in course.abbreviations:
if _perhaps_lower(abbreviation) == name:
return course
# Finally alternative names
if course.alt is not None and _perhaps_lower(course.alt) == name:
return course
return None

121
files/courses.json 100644
View File

@ -0,0 +1,121 @@
{
"Algoritmen en Datastructuren 2": {
"abbreviations": ["AD2"],
"code": "C003777",
"year": 2
},
"Algoritmen en Datastructuren 3": {
"abbreviations": ["AD3"],
"code": "C003782",
"year": 3
},
"Artificiële Intelligentie": {
"abbreviations": ["AI"],
"code": "C003756",
"year": 3
},
"Automaten, Berekenbaarheid en Complexiteit": {
"abbreviations": ["ABC"],
"code": "C003785",
"year": 3
},
"Besturingssystemen": {
"abbreviations": ["BS"],
"code": "E019010",
"year": 3
},
"Communicatienetwerken": {
"abbreviations": ["Comnet"],
"code": "E008620",
"year": 2
},
"Computationele Biologie": {
"abbreviations": ["Compbio"],
"code": "C003789",
"year": 3
},
"Computerarchitectuur": {
"abbreviations": ["CA", "Comparch"],
"code": "E034110",
"year": 2
},
"Functioneel Programmeren": {
"abbreviations": ["FP", "Funcprog"],
"code": "C003775",
"year": 2
},
"Informatiebeveiliging": {
"abbreviations": ["Infosec"],
"alt": "Information Security",
"code": "E019400",
"year": 3
},
"Inleiding tot Elektrotechniek": {
"abbreviations": [],
"alt": "Elektrotechniek",
"code": "C003806",
"year": 3
},
"Inleiding tot Telecommunicatie": {
"abbreviations": ["Telecom"],
"code": "C003787",
"year": 3
},
"Logisch Programmeren": {
"abbreviations": ["LP", "Logprog", "Prolog"],
"code": "C003783",
"year": 3
},
"Modelleren en Simuleren": {
"abbreviations": ["Modsim"],
"code": "C003786",
"year": 3
},
"Multimedia": {
"abbreviations": ["MM"],
"code": "C002126",
"year": 2
},
"Parallelle Computersystemen": {
"abbreviations": ["PCS"],
"alt": "Parallel Computer Systems",
"code": "E034140",
"year": 3
},
"Statistiek en Probabiliteit": {
"abbreviations": ["Stat","Statistiek", "Statprob"],
"code": "C003778",
"year": 2
},
"Software Engineering Lab 1": {
"abbreviations": ["SEL1"],
"code": "C003780",
"year": 2
},
"Software Engineering Lab 2": {
"abbreviations": ["SEL2"],
"code": "C003784",
"year": 3
},
"Systeemprogrammeren": {
"abbreviations": ["Sysprog"],
"code": "C003776",
"year": 2
},
"Webdevelopment": {
"abbreviations": ["Webdev"],
"code": "C003779",
"year": 2
},
"Wetenschappelijk Rekenen": {
"abbreviations": ["Wetrek"],
"code": "C001521",
"year": 2
},
"Wiskundige Modellering in de Ingenieurswetenschappen": {
"abbreviations": ["Wimo"],
"alt": "Wiskundige Modellering",
"code": "C003788",
"year": 3
}
}