diff --git a/timetabling/timetable-ics.py b/timetabling/timetable-ics.py index a3de9d3fcf634249de99b93f57f49e37d331b007..51083c32f65f88d23184162432989aad87f231c1 100644 --- a/timetabling/timetable-ics.py +++ b/timetabling/timetable-ics.py @@ -6,6 +6,7 @@ import re import sys from ics import Calendar, Event +from ics.grammar.parse import ContentLine from datetime import datetime, timedelta from typing import Dict, Generator @@ -13,11 +14,21 @@ from typing import Dict, Generator BASE_DATE = datetime(2024, 9, 23, 0, 0, 0, 0, pytz.timezone("Europe/London")) BEDFORD = 100 # not a year +CAL_DAYS = [ "MO", "TU", "WE", "TH", "FR", "SA", "SU" ] + if len(sys.argv) < 2: - print("Usage: timetable-ics.py timetable1.csv [timetable2.csv..]") + print("Usage: timetable-ics.py [-expand] timetable1.csv [timetable2.csv..]") print("May also pass excel files instead of csv") + print( + "Add -expand as first arg to make individual events for each", + "lecture, instead of using recurring events." + ) timetable_files = sys.argv[1:] +expand_events = False +if sys.argv[1].lower() == "-expand": + expand_events = True + del timetable_files[0] cals = { year: Calendar() for year in range(0, 6) } cals.update({ BEDFORD : Calendar() }) @@ -93,7 +104,7 @@ for ttfile in timetable_files: elif "Start Time" in row: start = row["Start Time"] duration_hours = row["Duration"] - weeks = get_weeks(row["Teaching Week Pattern"]) + weeks = list(get_weeks(row["Teaching Week Pattern"])) location = "" if "PROVISIONAL Location Name" in row: @@ -112,20 +123,64 @@ for ttfile in timetable_files: if "TUT" in name: continue - for week in weeks: + if expand_events: + 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.location = location + event.begin = start_time + event.end = end_time + for year in get_years(name): + cals[year].events.add(event) + if "bedford" in location.lower(): + event = event.clone() + 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) + elif len(weeks) > 0: offset = get_day_offset(day) if offset < 0: print(f"Warning: ignoring day {day} of {row}") else: - start_time = get_time(week, offset, start) + start_time = get_time(weeks[0], offset, start) end_time = get_end_time(start_time, duration_hours) + + year_weeks = ",".join(map( + lambda w: str(get_time(w, offset, start).isocalendar()[1]), + weeks + )) + event = Event() event.name = name event.location = location event.begin = start_time event.end = end_time + event.extra.append(ContentLine( + name="RRULE", + value="FREQ=YEARLY;" + + f"COUNT={len(weeks)};" + + f"BYWEEKNO={year_weeks};" + + f"BYDAY={CAL_DAYS[offset]}" + )) + for year in get_years(name): cals[year].events.add(event) + if "bedford" in location.lower(): event = event.clone() if "04-06" in location: @@ -141,6 +196,7 @@ for ttfile in timetable_files: cals[BEDFORD].events.add(event) + for year in cals: if year != BEDFORD: with open(f"Year{year}.ics", 'w') as icsfile: