aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2024-09-10 14:08:23 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2024-09-10 14:08:23 +0200
commit39ac356e5e410fe5378d9a728f5979c8d6e6b1dc (patch)
tree033d3b9df0f96b84d31b5e0ecbabd4bc645f998f
parent11923676c8477c4c522535d5c2431667de125a21 (diff)
downloadavail-lessons-39ac356e5e410fe5378d9a728f5979c8d6e6b1dc.tar.gz
avail-lessons-39ac356e5e410fe5378d9a728f5979c8d6e6b1dc.zip
support building a timetable without untis specifying the lesson's periods
Move all untis related configuration into the webuntis section.
-rwxr-xr-xlessons.py130
1 files changed, 100 insertions, 30 deletions
diff --git a/lessons.py b/lessons.py
index 71a90e6..6ac20ea 100755
--- a/lessons.py
+++ b/lessons.py
@@ -17,8 +17,21 @@ import toml
import webuntis
+def die(msg: str, status=1) -> t.NoReturn:
+ """print a message and exit"""
+ print(msg, file=sys.stderr)
+ sys.exit(status)
+
+
+@dataclass
+class Period:
+ """Data representing a period where a lesson could take place"""
+ start: datetime.datetime
+ code = None
+
+
@dataclass
-class SessionCredentials:
+class WebuntisSessionCredentials:
"""Data required to create a new webuntis session"""
school: str
server: str
@@ -142,6 +155,46 @@ def filter_periods(timetable: webuntis.objects.PeriodList,
]
+DAYS_MAP = {'Mo': 1, 'Di': 2, 'Mi': 3, 'Do': 4, 'Fr': 5}
+
+TIME_SLOTS = {
+ '1': datetime.timedelta(hours=8),
+ '2': datetime.timedelta(hours=8, minutes=45),
+ '3': datetime.timedelta(hours=9, minutes=45),
+ '4': datetime.timedelta(hours=10, minutes=30),
+ '5': datetime.timedelta(hours=11, minutes=30),
+ '6': datetime.timedelta(hours=12, minutes=15),
+ '7': datetime.timedelta(hours=13),
+ '8': datetime.timedelta(hours=13, minutes=45),
+ '9': datetime.timedelta(hours=14, minutes=30),
+ '10': datetime.timedelta(hours=15, minutes=20),
+ '11': datetime.timedelta(hours=16),
+ '12': datetime.timedelta(hours=16, minutes=45),
+}
+
+
+def build_periods(config: str, start: datetime.datetime,
+ end: datetime.datetime) -> list[Period]:
+ """Build a list of periods from a configuration string"""
+
+ periods = []
+ week = start.isocalendar().week
+ while True:
+ year = start.year if week >= start.isocalendar().week else end.year
+ for period in config.split(';'):
+ weekday, slot = period.split(':')
+ period_day = datetime.datetime.fromisocalendar(
+ year=year, week=week, day=DAYS_MAP[weekday])
+ period_start = period_day + TIME_SLOTS[slot]
+ periods.append(Period(period_start))
+
+ week = week % 52 + 1
+ if week > end.isocalendar().week and year == end.isocalendar().year:
+ break
+
+ return periods
+
+
def main():
"""Programm entry point loading data from webuntis and printing the remaining lessons"""
parser = argparse.ArgumentParser()
@@ -156,6 +209,10 @@ def main():
parser.add_argument('--subject',
help='The subject abbreviation used in webuntis')
parser.add_argument('--class', help='The class name')
+ parser.add_argument('--periods',
+ help=('The periods when you teach the class.'
+ 'Format: <Day>:<Slot>[;<Day><Slot>]*'),
+ type=str)
parser.add_argument('--show-events',
action='store_true',
help='Show ICS calendar events during the lessons')
@@ -168,35 +225,48 @@ def main():
config.update({k: v for k, v in args.__dict__.items() if v})
if 'class' not in config or 'subject' not in config:
- print('Class and subject must be provided')
- sys.exit(1)
-
- # use the webuntis username as teacher fallback
- if 'teacher' not in config:
- config['teacher'] = config['username']
-
- session_info = SessionCredentials(config)
- with webuntis.Session(useragent="muhq's avail-lessons script",
- **session_info.__dict__).login() as session:
-
- # Unfortunately pylint does not understand the type (_itemclass)
- # of the webuntis ListResults, therfore we supress the errors when
- # accessing members of the _itemclass.
- year = [
- y for y in session.schoolyears()
- if y.start <= config['start'] <= y.end # pylint: disable=no-member
- ][0]
-
- if 'end' not in config:
- config['end'] = year.end
-
- klasse = session.klassen().filter(name=config['class'])[0] # pylint: disable=no-member
-
- timetable = session.timetable_extended(klasse=klasse,
- start=config['start'],
- end=config['end'])
-
- periods = filter_periods(timetable, config)
+ die('Class and subject must be provided')
+
+ periods = None
+ if 'webuntis' in config:
+ # use the webuntis username as teacher fallback
+ if 'teacher' not in config:
+ config['teacher'] = config['webuntis']['username']
+
+ session_info = WebuntisSessionCredentials(config['webuntis'])
+ with webuntis.Session(useragent="muhq's avail-lessons script",
+ **session_info.__dict__).login() as session:
+
+ # Unfortunately pylint does not understand the type (_itemclass)
+ # of the webuntis ListResults, therfore we supress the errors when
+ # accessing members of the _itemclass.
+ year = [
+ y for y in session.schoolyears()
+ if y.start <= config['start'] <= y.end # pylint: disable=no-member
+ ]
+ if not year:
+ die('No school year is active')
+ year = year[0]
+
+ if 'end' not in config:
+ config['end'] = year.end
+
+ klasse = session.klassen().filter(name=config['class'])[0] # pylint: disable=no-member
+
+ timetable = session.timetable_extended(klasse=klasse,
+ start=config['start'],
+ end=config['end'])
+
+ periods = filter_periods(timetable, config)
+ elif args.periods:
+ start = config.setdefault(
+ 'start', datetime.datetime.fromisocalendar(2023, 37, 2))
+ end = config.setdefault('end',
+ datetime.datetime.fromisocalendar(2024, 30, 5))
+ periods = build_periods(args.periods, start, end)
+
+ if not periods:
+ die('Webuntis credentials or manual periods must be provided')
# load ICS events
evs = load_events(config)