calendar-fetcher/calendar_fetcher.py

326 lines
12 KiB
Python
Executable File

#!/usr/bin/env python3
import icalendar
import recurring_ical_events
import requests
import datetime
import pickle
import pprint
import json
import config as cfg
numberOfDaysToShow = 5
#numberOfDaysToShow = 1
useCachedData = False
storeDataInCache = False
includedCalendarIDs = []
for m in cfg.calendarsMetadata:
excluded = False
for excludedCalendar in cfg.excludedCalendarIDs:
#if excludedCalendar == m["calendarName"]:
if excludedCalendar == m["displayName"]:
excluded = True
if excluded:
continue
includedCalendarIDs.append(m["calendarName"])
def fetchCalendar(calendarName):
url = cfg.urlBase + "/" + calendarName + cfg.urlSuffix
#print(f"Fetching calendar \"{calendarName}\" from {url}...")
response = requests.get(url, auth=(cfg.userN, cfg.passW))
return response.text
def fetchCalendarsMetadata():
size = 0
icsData = {}
for id in includedCalendarIDs:
icsData[id] = fetchCalendar(id)
#print(id, icsData[id])
size += len(icsData[id])
#print(f"Fetched {(float(size) / 1024 / 1024):.4} MB")
return icsData
def get_calendar_data():
# Fetch calendars
if useCachedData:
#print("Using cached data")
with open('cacheData.pkl', 'rb') as fp:
icsData = pickle.load(fp)
else:
icsData = fetchCalendarsMetadata()
if storeDataInCache:
# save dictionary to .pkl file (for debugging only)
with open('cacheData.pkl', 'wb') as fp:
pickle.dump(icsData, fp)
#print('ICS data saved successfully to file')
# Prepare list of days
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)
# extracting events
events = {}
for day in days:
dayId = day.strftime("%m/%d/%Y")
events[dayId] = {}
#print(f"Extracting events for {day}...")
for id in includedCalendarIDs:
calendarData = icalendar.Calendar.from_ical(icsData[id])
es = recurring_ical_events.of(calendarData).at(day)
for e in es:
#print(e)
start = e["DTSTART"].dt
startDate = start.strftime("%m/%d/%Y")
startTime = start.strftime("%H:%M")
end = e["DTEND"].dt
endDate = end.strftime("%m/%d/%Y")
endTime = end.strftime("%H:%M")
summary = str(e["SUMMARY"])
for m in cfg.calendarsMetadata:
if id == m["calendarName"]:
name = m["displayName"]
break
d = {"calendar": name, "summary": summary,
"startDate": startDate, "startTime": startTime,
"endDate": endDate, "endTime": endTime
}
uid = start.strftime("%H-%M") + "_" + str(e["UID"])
events[dayId][uid] = d
#print(d)
# Sort by time
myKeys = list(events[dayId].keys())
myKeys.sort()
events[dayId] = {i: events[dayId][i] for i in myKeys}
#pprint.pprint(events)
metaData = {}
for m in cfg.calendarsMetadata:
#print(m)
if m["calendarName"] in includedCalendarIDs:
metaData[m["displayName"]] = {
"displayname": m["displayName"],
"color": m["color"]
}
# Converting to JSON
return json.dumps({"metadata": metaData, "events": events}, indent=4)
#
#
#
# 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(get_calendar_data())