calendar-fetcher/calendar_fetcher.py

194 lines
8.6 KiB
Python
Raw Normal View History

2023-10-08 21:12:55 +00:00
#!/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
2023-10-13 17:08:38 +00:00
#excludedCalendars = ['todo', 'todogemeinsam', 'contact_birthdays', 'geburtstage', 'infos']
excludedCalendars = ['ToDo', 'ToDo Gemeinsam', 'Geburtstage von Kontakten', 'Geburtstage', 'Infos']
2023-10-08 21:12:55 +00:00
#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])
2023-10-13 17:08:38 +00:00
#print("Ignoring %s" % str(calendar))
2023-10-08 21:12:55 +00:00
continue
else:
#print("Adding %s" % str(calendar).split("/")[-2])
2023-10-13 17:08:38 +00:00
#print("Adding %s" % str(calendar))
2023-10-08 21:12:55 +00:00
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)
2023-10-08 21:12:55 +00:00
# print(str(eventraw))
2023-10-08 21:12:55 +00:00
#event = icalendar.Calendar.from_ical(eventraw._data)
url = "https://" + cfg.userN + ":" + cfg.passW + "@" + str(eventraw).replace("Event: https://", "")
# print(url)
2023-10-08 21:12:55 +00:00
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'))
2023-10-08 21:12:55 +00:00
eventsList[day][eventUuid]["summary"] = str(component.get('summary'))
# print(" description: %s" % component.get('description'))
2023-10-08 21:12:55 +00:00
#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))
2023-10-08 21:12:55 +00:00
if startDate.dt.strftime('%m/%d/%Y') == day.strftime('%m/%d/%Y'): # event starts today
# print(" single day event")
2023-10-08 21:12:55 +00:00
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")
2023-10-08 21:12:55 +00:00
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')
2023-10-08 21:12:55 +00:00
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')
2023-10-08 21:12:55 +00:00
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())