diff --git a/.gitignore b/.gitignore index 240e4a5..d0e081e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ output/ __pycache__/ *.code-workspace utils/.caldav_pass +content/pages/termine.md diff --git a/content/pages/termine.md b/content/pages/termine.md deleted file mode 100644 index e69de29..0000000 diff --git a/utils/refresh-events.py b/utils/refresh-events.py index 9bf6912..0a0feca 100755 --- a/utils/refresh-events.py +++ b/utils/refresh-events.py @@ -1,16 +1,59 @@ import caldav -from datetime import datetime import vobject +from datetime import datetime, date, time import yaml server_url = "https://***REMOVED*** cal_user = "tobias" cal_url = "https://***REMOVED*** -cal_category = "Lesung/Veranstaltung" -metadata_keys = ["summary", "dtstart", "dtend", "description", "location"] +cal_category = "Veranstaltung" result_file = "../content/pages/termine.md" +now = datetime.now().astimezone() -# get passwort +cal_properties = [ # taken from ical specification + # descriptive + "attach", "categories", "class", "comment", "description", "geo", "location", "percent-complete", "priority", "resources", "status", "summary", + # date and time + "completed", "dtend", "due", "dtstart", "duration", "freebusy", "transp", + # timezone components + "tzid", "tzname", "tzoffsetfrom", "tzoffsetto", "tzurl", + # relationship + "attendee", "contact", "organizer", "recurrence-id", "related-to", "url", "uid", + # recurrence + "exdate", "exrule", "rdate", "rrule", + # alarm +# "action", "repeat", "trigger", + # change management + "created", "dtstamp", "last-modified", "sequence" +] +cal_prop_datetimes = ["dtend", "due", "dtstart", "duration", "dtstamp", "last-modified"] + + +"""Converts a datetime.datetime or datetime.date object into an aware datetime object.""" +def fixDatetime(d, t=None, tz=None): + if not tz: + tz = now.tzinfo + + # datetime object is made aware if necessary + if type(d) == datetime: + if d.tzinfo: + return d + else: + return datetime(d.date(), d.time(), tz) + + # raise error if d is neither datetime nor date + elif type(d) != date: + raise TypeError("parameter must be a datetime.date or datetime.datetime object") + + # d is a date object + if not t: + t = time(23, 59, 59, tzinfo=tz) # if no time parameter was passed, use 23:59:59 + if not t.tzinfo: + t.replace(tzinfo=tz) + return datetime.combine(d, t) + + +# get password with open(".caldav_pass", "r") as f: cal_pass = f.read().strip() @@ -30,16 +73,41 @@ with caldav.DAVClient(url=server_url, username=cal_user, password=cal_pass) as d # we only want events belonging to a specific category events = [e for e in vcal.getChildren() if "categories" in e.contents.keys() and cal_category in map(lambda x: x.value[0], e.contents["categories"])] -# convert events into a structure that Pelican can use -events = [{k: v[0].value for k, v in e.contents.items() if k in metadata_keys} for e in events] +# convert events into a structure suitable for Pelican +e_tmp = [] +for e in events: + d = {} + for k, v in e.contents.items(): + # only keep specific calendar properties + if not k in cal_properties: + continue + + # v is usually a list with a single item + if len(v) == 1: + d[k] = v[0].value + # but sometimes there's more than one item (this only ever happens when k is "categories") + # and in this case, each list item's value is itself a one item list (which is stupid but that's how vobject handles categories) + else: + d[k] = [v[i].value[0] for i in range(len(v))] + e_tmp.append(d) +events = e_tmp + +# fix dates and datetimes +for e in events: + for k in e.keys(): + if k in cal_prop_datetimes: + e[k] = fixDatetime(e[k]) # keep only future events -today = datetime.today().date() -events = [e for e in events if e["dtstart"].date() >= today] +events = [e for e in events if e["dtstart"] >= now] + +# sort by start date +events.sort(key=lambda e: e["dtstart"]) # write data as YAML with open(result_file, 'w') as f: f.write("---\n") yaml.dump({"termine": events}, f) f.write("---\n") + f.write("written at " + datetime.today().isoformat(" "))