replaced implementation
This commit is contained in:
parent
7385ada9b5
commit
00446bc0b6
@ -1,193 +1,325 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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 icalendar
|
||||||
|
import recurring_ical_events
|
||||||
import requests
|
import requests
|
||||||
|
import datetime
|
||||||
|
import pickle
|
||||||
|
import pprint
|
||||||
|
import json
|
||||||
|
|
||||||
import config as cfg
|
import config as cfg
|
||||||
|
|
||||||
|
|
||||||
from collections import OrderedDict
|
numberOfDaysToShow = 5
|
||||||
from operator import getitem
|
#numberOfDaysToShow = 1
|
||||||
|
|
||||||
import pprint as pp
|
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
|
||||||
|
|
||||||
def fetch_calendar():
|
if excluded:
|
||||||
numberOfDaysToShow = 5
|
continue
|
||||||
|
includedCalendarIDs.append(m["calendarName"])
|
||||||
|
|
||||||
#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)
|
def fetchCalendar(calendarName):
|
||||||
principal = client.principal()
|
url = cfg.urlBase + "/" + calendarName + cfg.urlSuffix
|
||||||
calendars = principal.calendars()
|
#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()
|
firstDay = datetime.date.today()
|
||||||
lastDay = firstDay + datetime.timedelta(days=numberOfDaysToShow)
|
lastDay = firstDay + datetime.timedelta(days=numberOfDaysToShow)
|
||||||
#print("Showing all events between %s and %s" % (firstDay, lastDay))
|
#print("Showing all events between %s and %s" % (firstDay, lastDay))
|
||||||
|
|
||||||
days = []
|
days = []
|
||||||
|
|
||||||
day = firstDay
|
day = firstDay
|
||||||
while day < lastDay:
|
while day < lastDay:
|
||||||
days.append(day)
|
days.append(day)
|
||||||
day += datetime.timedelta(days=1)
|
day += datetime.timedelta(days=1)
|
||||||
|
|
||||||
#print("Days to list: %s" % days)
|
#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:")
|
# extracting events
|
||||||
#for calendar in calendars:
|
events = {}
|
||||||
#print(" %s" % str(calendar).split("/")[-2])
|
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"])
|
||||||
|
|
||||||
calendarsFiltered = []
|
for m in cfg.calendarsMetadata:
|
||||||
|
if id == m["calendarName"]:
|
||||||
|
name = m["displayName"]
|
||||||
|
break
|
||||||
|
|
||||||
# exclude some calendars:
|
d = {"calendar": name, "summary": summary,
|
||||||
for calendar in calendars:
|
"startDate": startDate, "startTime": startTime,
|
||||||
#print(excludedCalendars)
|
"endDate": endDate, "endTime": endTime
|
||||||
#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)
|
|
||||||
|
|
||||||
|
uid = start.strftime("%H-%M") + "_" + str(e["UID"])
|
||||||
|
events[dayId][uid] = d
|
||||||
|
#print(d)
|
||||||
|
|
||||||
#print("Filtered calendars:")
|
# Sort by time
|
||||||
#for calendar in calendarsFiltered:
|
myKeys = list(events[dayId].keys())
|
||||||
#print(" %s" % str(calendar).split("/")[-2])
|
myKeys.sort()
|
||||||
|
events[dayId] = {i: events[dayId][i] for i in myKeys}
|
||||||
|
|
||||||
|
#pprint.pprint(events)
|
||||||
|
|
||||||
eventsList = {}
|
|
||||||
metaData = {}
|
metaData = {}
|
||||||
|
for m in cfg.calendarsMetadata:
|
||||||
for day in days:
|
#print(m)
|
||||||
eventsList[day] = {}
|
if m["calendarName"] in includedCalendarIDs:
|
||||||
|
metaData[m["displayName"]] = {
|
||||||
#print("processing calendars")
|
"displayname": m["displayName"],
|
||||||
for calendar in calendarsFiltered:
|
"color": m["color"]
|
||||||
#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
|
# Converting to JSON
|
||||||
dataJson = json.dumps(data, indent=4)
|
return json.dumps({"metadata": metaData, "events": events}, indent=4)
|
||||||
return dataJson
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# 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__':
|
if __name__ == '__main__':
|
||||||
print(fetch_calendar())
|
print(get_calendar_data())
|
||||||
|
22
config.py
22
config.py
@ -1,6 +1,26 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
# Configuration for the CalDAV server
|
# Configuration for the CalDAV server
|
||||||
url = "https://cloud.ruinelli.ch/remote.php/dav/principals/users/"
|
urlBase = 'https://cloud.ruinelli.ch/remote.php/dav/calendars/gruinelli'
|
||||||
|
urlSuffix = "?export"
|
||||||
userN = "gruinelli"
|
userN = "gruinelli"
|
||||||
passW = "attgigoc1"
|
passW = "attgigoc1"
|
||||||
|
|
||||||
|
|
||||||
|
"""calendar metaData
|
||||||
|
Fetched with the helper.py script"""
|
||||||
|
calendarsMetadata = [
|
||||||
|
{'calendarName': 'george', 'color': '#55aaff', 'displayName': 'George'},
|
||||||
|
{'calendarName': 'gemeinsam', 'color': '#ffff00', 'displayName': 'Gemeinsam'},
|
||||||
|
{'calendarName': 'todo', 'color': '#ffaaff', 'displayName': 'ToDo'},
|
||||||
|
{'calendarName': 'todogemeinsam', 'color': '#cc66cc', 'displayName': 'ToDo Gemeinsam'},
|
||||||
|
{'calendarName': 'contact_birthdays', 'color': '#FFFFCA', 'displayName': 'Geburtstage von Kontakten'},
|
||||||
|
{'calendarName': 'geburtstage', 'color': '#74e7d2', 'displayName': 'Geburtstage'},
|
||||||
|
{'calendarName': 'jael', 'color': '#16fb04', 'displayName': 'Kinder'},
|
||||||
|
{'calendarName': 'infos', 'color': '#0090a1', 'displayName': 'Infos'},
|
||||||
|
{'calendarName': 'anita_shared_by_anita', 'color': '#FF8000', 'displayName': 'Anita'},
|
||||||
|
{'calendarName': 'anitagesch%C3%A4ft%28bga%2Cpa%29_shared_by_anita', 'color': '#007B00', 'displayName': 'Anita Arbeit (Alixon, PAI)'},
|
||||||
|
{'calendarName': 'evtermine_shared_by_anita', 'color': '#FFE5CC', 'displayName': 'ev Termine'}
|
||||||
|
]
|
||||||
|
|
||||||
|
excludedCalendarIDs = ['ToDo', 'ToDo Gemeinsam', 'Geburtstage von Kontakten', 'Geburtstage', 'Infos']
|
||||||
|
51
helper.py
Normal file
51
helper.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
"""Script to fetch the calendar names and colors"""
|
||||||
|
|
||||||
|
import caldav
|
||||||
|
import caldav.elements.ical # for calendarColor
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
# Configuration for the CalDAV server
|
||||||
|
url = "https://cloud.ruinelli.ch/remote.php/dav/principals/users/"
|
||||||
|
userN = "gruinelli"
|
||||||
|
passW = "attgigoc1"
|
||||||
|
|
||||||
|
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=url, username=userN, password=passW)
|
||||||
|
principal = client.principal()
|
||||||
|
calendars = principal.calendars()
|
||||||
|
|
||||||
|
#print(calendars)
|
||||||
|
#print("")
|
||||||
|
|
||||||
|
calendarNames = (str(calendars).replace("Calendar(https://cloud.ruinelli.ch/remote.php/dav/calendars/gruinelli/", "").
|
||||||
|
replace(")", "").replace("/", "").replace("[", "").replace("]", "").
|
||||||
|
split(","))
|
||||||
|
#print(calendarNames)
|
||||||
|
#print("")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
metaData = []
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
for calendar in calendars:
|
||||||
|
#print(calendar)
|
||||||
|
data = {}
|
||||||
|
data["calendarName"] = calendarNames[i]
|
||||||
|
i += 1
|
||||||
|
data["displayname"] = list(calendar.get_properties([caldav.dav.DisplayName()]).values())[0]
|
||||||
|
data["color"] = list(calendar.get_properties([caldav.elements.ical.CalendarColor()]).values())[0]
|
||||||
|
metaData.append(data)
|
||||||
|
|
||||||
|
print("")
|
||||||
|
pprint.pprint(metaData)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
fetch_calendar()
|
@ -1,4 +1,6 @@
|
|||||||
# Docker
|
# Docker
|
||||||
|
## Local run for testing
|
||||||
|
`python webserver.py`
|
||||||
|
|
||||||
## Build it manually
|
## Build it manually
|
||||||
`docker build -t calendar-fetcher .`
|
`docker build -t calendar-fetcher .`
|
||||||
@ -16,4 +18,3 @@ docker stop calendar-fetcher
|
|||||||
docker rm calendar-fetcher
|
docker rm calendar-fetcher
|
||||||
docker run -d -P --name calendar-fetcher -p 8014:8014 -v /volume1/web/smartmirror/data:/data --label=com.centurylinklabs.watchtower.enable=false calendar-fetcher
|
docker run -d -P --name calendar-fetcher -p 8014:8014 -v /volume1/web/smartmirror/data:/data --label=com.centurylinklabs.watchtower.enable=false calendar-fetcher
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,2 +1,6 @@
|
|||||||
pytz
|
icalendar
|
||||||
caldav
|
recurring_ical_events
|
||||||
|
requests
|
||||||
|
datetime
|
||||||
|
#pickle
|
||||||
|
#pprint
|
||||||
|
22
webserver.py
22
webserver.py
@ -3,7 +3,7 @@
|
|||||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
from urllib.parse import urlparse, parse_qs
|
from urllib.parse import urlparse, parse_qs
|
||||||
|
|
||||||
from calendar_fetcher import fetch_calendar
|
from calendar_fetcher import *
|
||||||
|
|
||||||
|
|
||||||
internalPort = 8014
|
internalPort = 8014
|
||||||
@ -29,28 +29,26 @@ class myHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
self.wfile.write(b"<h1>Calendar Fetcher</h1>\n")
|
self.wfile.write(b"<h1>Calendar Fetcher</h1>\n")
|
||||||
self.wfile.write(b"fetching...<br>\n")
|
self.wfile.write(b"fetching...<br>\n")
|
||||||
print("fetching...")
|
print("fetching and processing...")
|
||||||
self.wfile.flush()
|
self.wfile.flush()
|
||||||
|
|
||||||
data = fetch_calendar()
|
data = get_calendar_data()
|
||||||
|
|
||||||
self.wfile.write(b"storing...<br>\n")
|
self.wfile.write(b"storing...<br>\n")
|
||||||
print("storing...")
|
|
||||||
self.wfile.flush()
|
self.wfile.flush()
|
||||||
|
print("storing...")
|
||||||
|
|
||||||
f = open(outfile, "w")
|
f = open(outfile, "w")
|
||||||
f.write(data)
|
f.write(data)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
try: # Might trow a "[Errno 32] Broken pipe" becasue the client closed the socket already (eg. due to a timeout)
|
||||||
self.wfile.write(b"Done.\nData got written to %s}<br>\n" % bytes(outfile, "utf8"))
|
self.wfile.write(b"Done.\nData got written to %s}<br>\n" % bytes(outfile, "utf8"))
|
||||||
|
self.wfile.flush()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
print("Done.\nData got written to %s}" % outfile)
|
print("Done.\nData got written to %s}" % outfile)
|
||||||
self.wfile.flush()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Run the service
|
# Run the service
|
||||||
|
Loading…
Reference in New Issue
Block a user