"""Shared helpers.""" import re from datetime import date, datetime, timedelta DAY_NAMES = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"] MONTHS = ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"] def week_start(d: date) -> date: return d - timedelta(days=d.weekday()) def week_label(d: date) -> str: iso = d.isocalendar() return f"KW {iso[1]}" def fmt_day(d: date) -> str: return f"{d.day:02d}.{MONTHS[d.month - 1]}" def fmt_range(d1: date, d2: date) -> str: return f"{fmt_day(d1)} - {fmt_day(d2)} {d2.year}" def current_semester(semesters: list[dict], now: datetime | None = None) -> str: now = now or datetime.now() ts = now.replace(tzinfo=None).timestamp() for s in semesters: if s["begin"] <= ts <= s["ende"]: return s["label"] # fallback: most recent past semester past = [s for s in semesters if s["ende"] >= ts] if past: return past[0]["label"] return semesters[0]["label"] if semesters else "" def decode_matrikel(m: str, stgs: list[dict]) -> dict | None: """Decode FH-Burgenland matrikel: YY + startcode + StG(3) + nr(3).""" m = m.strip() if not re.fullmatch(r"\d{9,10}", m): return None stg_code = m[-6:-3] nr = m[-3:] year = m[:2] startcode = m[2:4] if len(m) == 10 else "" start = None if startcode == "10": start = f"WS 20{year}" elif startcode == "20": start = f"SS 20{year}" stg = next((s for s in stgs if s["kz"] == stg_code), None) return { "matrikel": m, "start": start or f"20{year}", "stg_kz": stg_code, "stg_code": stg["code"] if stg else None, "stg_name": stg["name"] if stg else None, "nr": int(nr), } def lv_color(lv: str) -> str: """Stable hue for an LV code.""" h = sum(ord(c) for c in lv) * 47 hue = h % 360 return f"hsl({hue}, 55%, 45%)" def events_to_json(events) -> list[dict]: return [{ "lv": e.lv, "lektor": e.lektor, "gruppe": e.gruppe, "ort": e.ort, "start": e.start.isoformat(), "end": e.end.isoformat(), "uid": e.uid, } for e in events]