www/tools/gen_upcoming.py
2026-08-28 15:39:56 +02:00

108 lines
3.1 KiB
Python
Executable file

#!/usr/bin/env python3
import sys
import html
import logging
import locale
from datetime import datetime, timedelta, timezone, date
from dateutil.rrule import rruleset, rrulestr
import icalendar
def _to_aware(dt):
"""Normalize a date or datetime to a timezone-aware UTC datetime."""
if isinstance(dt, date) and not isinstance(dt, datetime):
return datetime(dt.year, dt.month, dt.day, tzinfo=timezone.utc)
if dt.tzinfo is None:
return dt.replace(tzinfo=timezone.utc)
return dt.astimezone(timezone.utc)
def vevent_to_event(event, rrstart=None):
if rrstart is None:
begin = _to_aware(event["DTSTART"].dt)
else:
begin = _to_aware(rrstart)
return {
"name": str(event.get("SUMMARY", "")),
"url": str(event.get("URL", "")),
"begin": begin
}
def parse_single_event(event, start, end):
logging.info(f"Processing single event {event.get('SUMMARY', '')}")
dtstart = _to_aware(event["DTSTART"].dt)
if dtstart >= start and dtstart < end:
return vevent_to_event(event)
def parse_recurring_event(event, start, end):
logging.info(f"Processing recurring event {event.get('SUMMARY', '')}")
dtstart = _to_aware(event["DTSTART"].dt)
rs = rruleset()
rs.rrule(rrulestr(event["RRULE"].to_ical().decode("utf-8"), dtstart=dtstart))
if "EXDATE" in event.keys():
exdates = event["EXDATE"]
if not isinstance(exdates, list):
exdates = [exdates]
for exdate_list in exdates:
for dt in exdate_list.dts:
rs.exdate(_to_aware(dt.dt))
events = []
for dt in rs.between(start, end, inc=True):
events.append(vevent_to_event(event, dt))
return events
def find_events(icsfilestr, start, end, num):
with open(icsfilestr, "r") as icsfile:
cal = icalendar.Calendar.from_ical(icsfile.read())
events = []
for event in cal.subcomponents:
if event.name == "VEVENT":
if "RRULE" in event.keys():
events.extend(parse_recurring_event(event, start, end))
else:
ev = parse_single_event(event, start, end)
if ev is not None:
events.append(ev)
events = sorted(events, key=lambda k: k["begin"])
events = events[0:num]
return events
def format_events(events):
print("<table class=\"table table-condensed\">")
for event in events:
name = html.escape(event['name'])
url = html.escape(event['url'])
print(
"<tr>"
f"<td>{event['begin'].strftime('%A, %d.%m um %H:%M Uhr')}</td>"
f"<td><a href=\"{url}\">{name}</a></td>"
"</tr>"
)
print("</table><!--/.table .table-condensed-->")
if __name__ == "__main__":
if len(sys.argv) < 4:
print(f"Usage: {sys.argv[0]} calendar max_days max_items")
sys.exit(1)
locale.setlocale(locale.LC_TIME, "de_DE.UTF-8")
calendar = sys.argv[1]
max_days = int(sys.argv[2])
max_items = int(sys.argv[3])
now = datetime.now(timezone.utc)
events = find_events(calendar, now, now + timedelta(days=max_days), max_items)
format_events(events)