- Studentenplan (eigene/fremde Matrikelnummer) mit Wochenansicht, Semesterübersicht, iCal/CSV-Export - Studiengangs-Pläne nach StG/Semester/Verband mit Gruppen-Filter - Namenslisten mit Suche, Matrikel-Decode und CSV-Export - Studiennummer-Decoder auf der Startseite - Docker (Dockerfile, Compose) und Caddy-Beispiel für Deploy
77 lines
2.2 KiB
Python
77 lines
2.2 KiB
Python
"""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]
|