Files
cis-enhanced/app/util.py
T
Nicolas Gruber 32b4dc4823 CIS Enhanced: FastAPI-App für LV-Plan, Studiengangs-Pläne und Namenslisten des FH-Burgenland-CIS
- 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
2026-10-15 16:27:52 +02:00

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]