diff --git a/timetabling/timetable-ics.py b/timetabling/timetable-ics.py index 604eb16694acc7f9b6184b1f084badf5adc00701..493420e6270256e1cb469f867978d5bcf3cd0015 100644 --- a/timetabling/timetable-ics.py +++ b/timetabling/timetable-ics.py @@ -1,5 +1,6 @@ import csv +import pandas as pd import pytz import re import sys @@ -7,15 +8,16 @@ import sys from ics import Calendar, Event from datetime import datetime, timedelta -from typing import Generator +from typing import Dict, Generator BASE_DATE = datetime(2024, 9, 23, 0, 0, 0, 0, pytz.timezone("Europe/London")) BEDFORD = 100 # not a year if len(sys.argv) < 2: print("Usage: timetable-ics.py timetable1.csv [timetable2.csv..]") + print("May also pass excel files instead of csv") -timetable_csvs = sys.argv[1:] +timetable_files = sys.argv[1:] cals = { year: Calendar() for year in range(0, 6) } cals.update({ BEDFORD : Calendar() }) @@ -62,70 +64,80 @@ def get_end_time(start : datetime, duration : str) -> datetime: hours = int(duration.split(":")[0]) return start + timedelta(hours=hours) -for csvfile in timetable_csvs: - with open(csvfile) as f: - for row in csv.DictReader(f): - day = None - start = None - name = row["Name"] - if "Suggested Days" in row: - day = row["Suggested Days"] - elif "Scheduled Days" in row: - day = row["Scheduled Days"] - elif "Day(s)" in row: - day = row["Day(s)"] - if "Suggested Time" in row: - start = row["Suggested Time"] - elif "Scheduled Start Time" in row: - start = row["Scheduled Start Time"] - elif "Start Time" in row: - start = row["Start Time"] - duration_hours = row["Duration"] - weeks = get_weeks(row["Teaching Week Pattern"]) - - location = "" - if "PROVISIONAL Location Name" in row: - location = row["PROVISIONAL Location Name"] - if "Required Location Name" in row: - location = row["PROVISIONAL Location Name"] - - if day is None: - print("Could not find timetabled day column, aborting") - exit() - - if start is None: - print("Could not find timetabled start time column, aborting") - exit() - - if "TUT" in name: - continue - - for week in weeks: - offset = get_day_offset(day) - if offset < 0: - print(f"Warning: ignoring day {day} of {row}") - else: - start_time = get_time(week, offset, start) - end_time = get_end_time(start_time, duration_hours) - event = Event() - event.name = name - event.begin = start_time - event.end = end_time - for year in get_years(name): - cals[year].events.add(event) - if "bedford" in location.lower(): - if "04-06" in location: - event.name = f"ALL {name}" - elif "04" in location: - event.name = f"4 {name}" - elif "05" in location: - event.name = f"5 {name}" - elif "06" in location: - event.name = f"6 {name}" - else: - event.name = f"? {name}" - - cals[BEDFORD].events.add(event) +def iter_rows(ttfile : str) -> Generator[Dict[str, str], None, None]: + """Iterate over rows of csv or xlsx file as dictionary""" + if ttfile.lower().endswith(".csv"): + with open(ttfile) as f: + for row in csv.DictReader(f): + yield row + else: # try any excel + df = pd.read_excel(ttfile, dtype=str, na_filter=False) + for row in df.to_dict(orient="records"): + yield row + +for ttfile in timetable_files: + for row in iter_rows(ttfile): + day = None + start = None + name = row["Name"] + if "Suggested Days" in row: + day = row["Suggested Days"] + elif "Scheduled Days" in row: + day = row["Scheduled Days"] + elif "Day(s)" in row: + day = row["Day(s)"] + if "Suggested Time" in row: + start = row["Suggested Time"] + elif "Scheduled Start Time" in row: + start = row["Scheduled Start Time"] + elif "Start Time" in row: + start = row["Start Time"] + duration_hours = row["Duration"] + weeks = get_weeks(row["Teaching Week Pattern"]) + + location = "" + if "PROVISIONAL Location Name" in row: + location = row["PROVISIONAL Location Name"] + if "Required Location Name" in row: + location = row["PROVISIONAL Location Name"] + + if day is None: + print("Could not find timetabled day column, aborting") + exit() + + if start is None: + print("Could not find timetabled start time column, aborting") + exit() + + if "TUT" in name: + continue + + for week in weeks: + offset = get_day_offset(day) + if offset < 0: + print(f"Warning: ignoring day {day} of {row}") + else: + start_time = get_time(week, offset, start) + end_time = get_end_time(start_time, duration_hours) + event = Event() + event.name = name + event.begin = start_time + event.end = end_time + for year in get_years(name): + cals[year].events.add(event) + if "bedford" in location.lower(): + if "04-06" in location: + event.name = f"ALL {name}" + elif "04" in location: + event.name = f"4 {name}" + elif "05" in location: + event.name = f"5 {name}" + elif "06" in location: + event.name = f"6 {name}" + else: + event.name = f"? {name}" + + cals[BEDFORD].events.add(event) for year in cals: if year != BEDFORD: