#!/usr/bin/env python3 import time import datetime import json from pytz import UTC # timezone import caldav import caldav.elements.ical # for calendarColor import icalendar import requests import config as cfg from collections import OrderedDict from operator import getitem import pprint as pp def fetch_calendar(): numberOfDaysToShow = 5 #excludedCalendars = ['todo', 'todogemeinsam', 'contact_birthdays', 'geburtstage', 'infos'] excludedCalendars = ['ToDo', 'ToDo Gemeinsam', 'Geburtstage von Kontakten', 'Geburtstage', 'Infos'] #excludedCalendars = ['todo', 'todogemeinsam', 'contact_birthdays', 'geburtstage', 'gemeinsam', 'evtermine_shared_by_anita', 'jael'] #excludedCalendars = ['gemeinsam', 'contact_birthdays', 'geburtstage'] client = caldav.DAVClient(url=cfg.url, username=cfg.userN, password=cfg.passW) principal = client.principal() calendars = principal.calendars() firstDay = datetime.date.today() lastDay = firstDay + datetime.timedelta(days=numberOfDaysToShow) #print("Showing all events between %s and %s" % (firstDay, lastDay)) days = [] day = firstDay while day < lastDay: days.append(day) day += datetime.timedelta(days=1) #print("Days to list: %s" % days) # Get offset from local time to UTC, see also https://stackoverflow.com/questions/3168096/getting-computers-utc-offset-in-python # TODO: This should be done for every event individually! ts = time.time() utcOffset = (datetime.datetime.fromtimestamp(ts) - datetime.datetime.utcfromtimestamp(ts)).total_seconds() utcOffset = int(utcOffset / 3600) # in hours #print("UTC offset:", utcOffset) #print("All calendars:") #for calendar in calendars: #print(" %s" % str(calendar).split("/")[-2]) calendarsFiltered = [] # exclude some calendars: for calendar in calendars: #print(excludedCalendars) #print(calendar) #if str(calendar).split("/")[-2] in excludedCalendars: if str(calendar) in excludedCalendars: #print("Ignoring %s" % str(calendar).split("/")[-2]) #print("Ignoring %s" % str(calendar)) continue else: #print("Adding %s" % str(calendar).split("/")[-2]) #print("Adding %s" % str(calendar)) calendarsFiltered.append(calendar) #print("Filtered calendars:") #for calendar in calendarsFiltered: #print(" %s" % str(calendar).split("/")[-2]) eventsList = {} metaData = {} for day in days: eventsList[day] = {} #print("processing calendars") for calendar in calendarsFiltered: #calendarName = str(calendar).split("/")[-2] calendarName = str(calendar) metaData[calendarName] = {} metaData[calendarName]['displayname'] = list(calendar.get_properties([caldav.dav.DisplayName()]).values())[0] metaData[calendarName]['color'] = list(calendar.get_properties([caldav.elements.ical.CalendarColor()]).values())[0] #print(metaData) for day in days: #print(" Looking for events between %s and %s" % (day, day + datetime.timedelta(days=1))) results = calendar.date_search(day, day + datetime.timedelta(days=1)) # get event for selected day (24h) for eventraw in results: eventUuid = str(eventraw).split("/")[-1].split(".")[0] #print(" - %s" % eventUuid) # print(str(eventraw)) #event = icalendar.Calendar.from_ical(eventraw._data) url = "https://" + cfg.userN + ":" + cfg.passW + "@" + str(eventraw).replace("Event: https://", "") # print(url) data = requests.get(url).text event = icalendar.Calendar.from_ical(data) for component in event.walk(): if component.name == "VEVENT": eventsList[day][eventUuid] = {} ##eventsList[day][eventUuid]["calendar"] = str(calendar).split("/")[-2] eventsList[day][eventUuid]["calendar"] = str(calendar) # print(" summary: %s" % component.get('summary')) eventsList[day][eventUuid]["summary"] = str(component.get('summary')) # print(" description: %s" % component.get('description')) #eventsList[day][eventUuid]["description"] = str(component.get('description')) startDate = component.get('dtstart') # print(" dtstart: %s == %s?" % (startDate.dt.strftime('%m/%d/%Y %H:%M'), day)) if startDate.dt.strftime('%m/%d/%Y') == day.strftime('%m/%d/%Y'): # event starts today # print(" single day event") eventsList[day][eventUuid]["startDate"] = startDate.dt.strftime('%m/%d/%Y') eventsList[day][eventUuid]["startTime"] = (startDate.dt + datetime.timedelta(hours=utcOffset)).strftime('%H:%M') else: # event started before today, set startdate to day and start time to midnight (for multi day events with start/end time) # print(" multi day event") eventsList[day][eventUuid]["startDate"] = day.strftime('%m/%d/%Y') # eventsList[day][eventUuid]["startTime"] = "00:00" # No longer working! eventsList[day][eventUuid]["startTime"] = (startDate.dt + datetime.timedelta(hours=utcOffset)).strftime('%H:%M') try: endDate = component.get('dtend') #print(" dtend: %s" % endDate.dt.strftime('%m/%d/%Y %H:%M')) if endDate.dt.strftime('%m/%d/%Y') == day.strftime('%m/%d/%Y'): # event ends today eventsList[day][eventUuid]["endDate"] = endDate.dt.strftime('%m/%d/%Y') eventsList[day][eventUuid]["endTime"] = (endDate.dt + datetime.timedelta(hours=utcOffset)).strftime('%H:%M') else: # event ends after today, set enddate to day and end time to midnight (for multi day events with start/end time) eventsList[day][eventUuid]["endDate"] = day.strftime('%m/%d/%Y') # eventsList[day][eventUuid]["endTime"] = "24:00" # No longer working! eventsList[day][eventUuid]["endDate"] = endDate.dt.strftime('%m/%d/%Y') except AttributeError as ae: # event has no endtime, use duration #print(" %s" % ae) duration = component.get('duration') #print(" Event misses end time, using duration") #print(" duration: %s" % duration.dt) if (startDate.dt + duration.dt).strftime('%m/%d/%Y') == day.strftime('%m/%d/%Y'): # event ends today eventsList[day][eventUuid]["endDate"] = (startDate.dt + duration.dt).strftime('%m/%d/%Y') eventsList[day][eventUuid]["endTime"] = (startDate.dt + duration.dt + datetime.timedelta(hours=utcOffset)).strftime('%H:%M') else: # event ends after today, set enddate to day and end time to midnight (for multi day events with start/end time) eventsList[day][eventUuid]["endDate"] = day.strftime('%m/%d/%Y') eventsList[day][eventUuid]["endTime"] = "24:00" #print('') #break # order by time per day, see https://www.geeksforgeeks.org/python-sort-nested-dictionary-by-key/ for day in days: sortedbyTimePerDay = OrderedDict(sorted(eventsList[day].items(), key = lambda x: getitem(x[1], 'startTime'))) eventsList[day] = sortedbyTimePerDay # Sort days eventsList = OrderedDict(sorted(eventsList.items())) # reformat day key (datetime => string) for day in days: eventsList[day.strftime('%m/%d/%Y')] = eventsList.pop(day) #pp.pprint(eventsList) data = {} data['metadata'] = metaData data['events'] = OrderedDict(eventsList) #pp.pprint(data) # Converting to JSON dataJson = json.dumps(data, indent=4) return dataJson if __name__ == '__main__': print(fetch_calendar())