Compare commits

..

16 Commits

Author SHA1 Message Date
f939127a0c updates ical output to convert event times to calendar timzone based time 2020-07-25 19:26:49 +02:00
056779f7d2 adds ical url handling
- icalToCalendarDB converts the information the user puts into the form
  on the calendar.html page and pulls the calendar from the url.
  It then passes it into the database.

- fetchCanedarEvents gets all events within a startDate and an endDate
  and removes any all day events. This is currently a bit costly, because
  it takes some time for the ics library to download the entire .ical
  file from the url. Then it goes through every entry ever added to the
  file and saves only the future events.
2020-07-25 18:03:02 +02:00
fec09edb88 adds calendar_type settings to calendar 2020-07-25 11:27:24 +02:00
45cd71cc4b code commenting and cleanup 2020-07-10 10:10:07 +02:00
1a5700e9a0 minor bugfix when None value passed to function 2020-06-06 20:01:38 +02:00
183bf60fc0 fixes bug which would not pass along color Hex value 2020-05-30 23:32:37 +02:00
87a229f791 Large restructure of google and backend separation
- googleHandler now takes care of every google communication and mostly nothing else
- only backend classes for Calendar and Events are used for communication
- backend now takes care of every other interaction

- functionality is still the same though
2020-05-30 23:03:27 +02:00
b5cb35256c fixes bugs in database calls 2020-05-27 20:35:46 +02:00
fe1c216c29 updates database design 2020-05-27 20:05:25 +02:00
26cc3425ee updated models position 2020-05-24 13:32:42 +02:00
f94c64e699 routine no longer creates file 2020-05-24 13:25:15 +02:00
060e506547 increased nr of days to send to watch json 2020-05-21 10:19:37 +02:00
ba912de7f3 updates routine to use calendar_id
- calendar name creates problems, when two
  calendars have the same name
- calendar id is unique and was added into the
  css of the frontend
y
2020-05-18 23:49:26 +02:00
804ab33a6d updates color generation
- hopefully fixes a small bug that fucks up colors
- fixes generation when saving calendar colors
2020-05-17 22:58:13 +02:00
baf214e3cf updates gitignore 2020-05-17 22:57:43 +02:00
b5dbfd87de updates routine script 2020-05-15 16:00:11 +02:00
6 changed files with 282 additions and 299 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*.swp
__pycache__/ __pycache__/

View File

@ -1,33 +0,0 @@
from server import db
from server.models import User
from backend import caltojson
import google.oauth2.credentials
import json
class Routine:
def start(self, time=10):
self.updateCalendars()
def updateCalendars(self):
users = User.query.all()
for user in users:
# check google:
credentials = user.getGoogleCredentials()
if credentials is None:
continue
credentials = google.oauth2.credentials.Credentials(**credentials)
calendarjson = caltojson.generateJsonFromCalendarEntries(credentials)
user.setJson(calendarjson)
with open('userinfo/'+ user.id +'/calendarevents.json', 'w', encoding='utf-8') as f:
json.dump(calendarjson, f, ensure_ascii=False, indent=4)
if __name__ == "__main__":
routine = Routine()
routine.start()

View File

@ -1,3 +1,211 @@
from backend.Routine import Routine from __future__ import print_function
import datetime
import dateutil.parser
import pickle
import json
import os.path
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
routine = Routine() from server import db
from database.models import Calendar as dbCalendar
# If modifying these scopes, delete the file token.pickle.
SCOPES = ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/calendar.readonly", "openid"]
API_SERVICE_NAME = 'calendar'
API_VERSION = 'v3'
class Calendar:
def __init__(self, name, calendarId, calType, toggle='False', color="#000000"):
self.name = name
self.calType = calType
self.color = color
self.toggle=toggle
self.calendarId = calendarId
class Event:
def __init__(self, name_, start_, end_):
self.name = name_
self.eventColorId = None
self.start = start_
self.end = end_
self.colorHex = '#adfff5'
if self.start == None or self.end == None :
self.allDay = True
else:
self.allDay = False
def startDateTime(self):
if self.allDay:
return None
return self.jsonFromDT(self.start)
def stopDateTime(self):
if self.allDay:
return None
return self.jsonFromDT(self.end)
def jsonFromDT(self, string):
sdt = dateutil.parser.parse(string)
data = {
'date': {
'year': sdt.year,
'month': sdt.month,
'day': sdt.day
},
'time': {
'hour': sdt.hour,
'minute': sdt.minute,
'second': sdt.second
}
}
return data
def print(self):
if self.allDay:
print(self.name + "All Day")
def updateCalendars(user, calendars, colors):
if user == None or calendars == None or colors == None:
return
COLORS_FILE = os.path.join(os.path.dirname(__file__), 'colors.json')
with open(COLORS_FILE) as f:
colormap = json.load(f)
# add all new calendars into the database
# TODO update existing Name and Calendar ( do this in User class at db? )
# Take care, that you don't overwrite the custom color selection of the user
for calendar in calendars:
if not user.hasCalendar(calendar.calendarId):
color = fromColorIdGetColor(calendar.color, colormap, colors)
c = dbCalendar(calendar_id = calendar.calendarId,
calendar_type = calendar.calType,
name = calendar.name,
toggle = "False",
color = color)
db.session.add(c)
user.calendars.append(c)
db.session.commit()
def calendarsFromDb(user):
pyCalendars = []
for calendar in user.calendars:
name = (calendar.name[:16] + '..') if len(calendar.name)> 18 else calendar.name
calendarId = calendar.calendar_id
toggle = calendar.toggle
color = calendar.color
calType = calendar.calendar_type
pyCalendars.append(Calendar(name, calendarId, calType, toggle, color))
return pyCalendars
# removes not visible calendars from the list of all calendars
def purgeCalendars(calendars, visibleCalendars):
purged = []
for calendar in calendars:
if calendar.calendarId in visibleCalendars:
purged.append(calendar)
return purged
# remaps a event color id to a calendar color id
# for google calendars
def toCalendarColorId(colormap, colorId):
for remap in colormap['eventRemap']:
if remap['from'] == colorId:
return remap['to']
print(f"failed with {colorId}")
return colorId
# remaps a calendar color ID to its natural color
# for google calendars
def toNaturalColor(colormap, orgColor):
for remap in colormap['colors']:
if remap['api'] == orgColor:
return remap['natural']
print(f"failed with {orgColor}")
return orgColor
# uses the event color id to convert it to a hex color
# does this for every event in events
# this is used for Google Calendars
def colorizeGoogleEvents(events, colors):
COLORS_FILE = os.path.join(os.path.dirname(__file__), 'colors.json')
with open(COLORS_FILE) as f:
colormap = json.load(f)
for event in events:
if event.eventColorId != None:
event.colorHex = forEventGetColor(event, colormap, colors)
# returns a color for a specific calendar color id
# for google calendars
def fromColorIdGetColor(color, colormap, colors):
return toNaturalColor(colormap, colors['calendar'][color]['background'])
# generates the natural color for a event color id
# for google calendars
def forEventGetColor(event, colormap, colors):
calColorId = toCalendarColorId(colormap, event.eventColorId)
bg = colors['calendar'][calColorId]['background']
return toNaturalColor(colormap, bg)
# exports all events into a jsonable format (excluding all day events)
def toJson(events):
data = {}
data['kind'] = 'calendar#events'
data['events'] = []
for event in events:
if event.allDay:
continue
data['events'].append({
'name': event.name,
'isAllDay': event.allDay,
'color': event.colorHex,
'startDateTime': event.startDateTime(),
'stopDateTime': event.stopDateTime()
})
return data
# debug functions which prints color names
def printColors(colors):
for i in range(1, 25):
col = colors['event'][str(i)]['background']
print(f"{i}: {col}")
def main():
return
def getTimeStamps():
# define today and tomorrow
now = datetime.datetime.now(datetime.timezone.utc).astimezone()
today = now.replace(hour=0, minute=0, second = 0)
tomorrow = now.replace(hour=23, minute=59, second=59).isoformat() # + '+01:00'
twodaysfromnow = datetime.datetime.today() + datetime.timedelta(days=2)
twodaysfromnow = twodaysfromnow.replace(hour=23, minute=59, second=59).astimezone()
return today, twodaysfromnow
def generateJsonFromCalendarEntries(events, colors):
# fix all colors in events that have a different event color
if colors != None:
colorizeGoogleEvents(events, colors)
# if not events:
# print('No upcoming events found.')
return toJson(events)
if __name__ == '__main__':
main()

View File

@ -1,261 +0,0 @@
from __future__ import print_function
import datetime
import dateutil.parser
import pickle
import json
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# If modifying these scopes, delete the file token.pickle.
SCOPES = ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/calendar.readonly", "openid"]
API_SERVICE_NAME = 'calendar'
API_VERSION = 'v3'
visibleList = ['Hightower', 'Home', 'Office', 'Life', 'Social', 'Grey']
class Event:
def __init__(self, name_, start_, end_):
self.name = name_
self.eventColorId = 0
self.calendarColorId = 0
self.naturalColorId = 0
self.start = start_
self.end = end_
self.colorHex = '#adfff5'
if self.start == None or self.end == None :
self.allDay = True
else:
self.allDay = False
def startDateTime(self):
if self.allDay:
return None
return self.jsonFromDT(self.start)
def stopDateTime(self):
if self.allDay:
return None
return self.jsonFromDT(self.end)
def jsonFromDT(self, string):
sdt = dateutil.parser.parse(string)
data = {
'date': {
'year': sdt.year,
'month': sdt.month,
'day': sdt.day
},
'time': {
'hour': sdt.hour,
'minute': sdt.minute,
'second': sdt.second
}
}
return data
def print(self):
if self.allDay:
print(self.name + "All Day")
else:
print(self.name + ": " + self.naturalColorId)
class Calendar:
def __init__(self, summary_, calendarId_, color_):
self.summary = summary_
self.calendarId = calendarId_
self.color = color_
def calendarCredentials(token = None):
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if token == None:
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
token.seek(0)
creds = pickle.load(token)
else:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=1234)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
return service
def getCalendarColors(service):
colors = service.colors().get().execute()
return colors
def getCalendars(service):
page_token = None
calendars = []
while True:
calendar_list = service.calendarList().list(pageToken=page_token).execute()
for calendar in calendar_list['items']:
calendars.append(Calendar(calendar['summary'], calendar['id'], calendar['colorId']))
page_token = calendar_list.get('nextPageToken')
if not page_token:
break
return calendars
def purgeCalendars(calendars):
purged = []
for calendar in calendars:
if calendar.summary in visibleList:
purged.append(calendar)
return purged
def getCalendarEvents(service, startDate, endDate):
calendars = getCalendars(service)
calendars = purgeCalendars(calendars)
all_events = []
for calendar in calendars:
events_result = service.events().list(calendarId=calendar.calendarId, timeMin=startDate,
timeMax=endDate,
maxResults=10, singleEvents=True,
orderBy='startTime').execute()
for event in events_result.get('items', []):
# create simple event
name = event['summary']
start = event['start'].get('dateTime')
end = event['end'].get('dateTime')
newEvent = Event(name, start, end)
# handle weird colors from google
color = event.get('colorId')
if color == None:
newEvent.calendarColorId = calendar.color
else:
newEvent.eventColorId = color
all_events.append(newEvent)
return all_events
def toCalendarColorId(colormap, colorId):
for remap in colormap['eventRemap']:
if remap['from'] == colorId:
return remap['to']
print(f"failed with {colorId}")
return colorId
def toNaturalColor(colormap, orgColor):
for remap in colormap['colors']:
if remap['api'] == orgColor:
return remap['natural']
print(f"failed with {orgColor}")
return orgColor
def colorizeEvents(allEvents, service):
COLORS_FILE = os.path.join(os.path.dirname(__file__), 'colors.json')
with open(COLORS_FILE) as f:
colormap = json.load(f)
colors = getCalendarColors(service)
for event in allEvents:
event.colorHex = fromColorIdGetColor(event.calendarColorId, colormap, colors)
def fromColorIdGetColor(colorId, colormap, colors):
if colorId != 0:
orgColor = colors['calendar'][colorId]['background']
else:
calColorId = toCalendarColorId(colormap, colorId)
orgColor = colors['calendar'][calColorId]['background']
return toNaturalColor(colormap, orgColor)
def toJson(events):
data = {}
data['kind'] = 'calendar#events'
data['events'] = []
for event in events:
if event.allDay:
continue
data['events'].append({
'name': event.name,
'isAllDay': event.allDay,
'color': event.colorHex,
'startDateTime': event.startDateTime(),
'stopDateTime': event.stopDateTime()
})
return data
def printColors(colors):
for i in range(1, 25):
col = colors['event'][str(i)]['background']
print(f"{i}: {col}")
def main():
generateJsonFromCalendarEntries()
def getCalendarList(credentials = None):
# create service
if credentials == None:
credentials = calendarCredentials()
service = build(API_SERVICE_NAME, API_VERSION, credentials=credentials)
calendars = getCalendars(service)
COLORS_FILE = os.path.join(os.path.dirname(__file__), 'colors.json')
with open(COLORS_FILE) as f:
colormap = json.load(f)
colors = getCalendarColors(service)
for calendar in calendars:
calendar.color = fromColorIdGetColor(calendar.color, colormap, colors)
return calendars
def generateJsonFromCalendarEntries(credentials = None):
# create service
if credentials == None:
credentials = calendarCredentials()
service = build(API_SERVICE_NAME, API_VERSION, credentials=credentials)
# define today and tomorrow
now = datetime.datetime.now(datetime.timezone.utc).astimezone()
today = now.replace(hour=0, minute=0, second = 0).isoformat()
tomorrow = now.replace(hour=23, minute=59, second=59).isoformat() # + '+01:00'
allEvents = getCalendarEvents(service, today, tomorrow)
colorizeEvents(allEvents, service)
# if not events:
# print('No upcoming events found.')
return toJson(allEvents)
if __name__ == '__main__':
main()

View File

@ -10,7 +10,7 @@
}, },
{ {
"api": "#f83a22", "api": "#f83a22",
"natural": "#8E24AA" "natural": "#d50000"
}, },
{ {
"api": "#fa573c", "api": "#fa573c",
@ -90,7 +90,7 @@
}, },
{ {
"api": "#cd74e6", "api": "#cd74e6",
"natural": "#8E24AA" "natural": "#ff00cc"
}, },
{ {
"api": "#a47ae2", "api": "#a47ae2",
@ -143,4 +143,4 @@
"to": "8" "to": "8"
} }
] ]
} }

68
icalHandler.py Normal file
View File

@ -0,0 +1,68 @@
from server import db
from database.models import Calendar as dbCalendar
from backend import Event as bEvent
from ics import Calendar as iCalendar
import requests
def icalToCalendarDb(url, name, user):
try:
c = iCalendar(requests.get(url).text)
except:
return False
c = dbCalendar(calendar_id = url,
calendar_type = 'ical',
name = name,
toggle = "False",
color = "#000000")
db.session.add(c)
user.calendars.append(c)
db.session.commit()
def fetchCalendarEvents(calendars, startDate, endDate):
all_events = []
for calendar in calendars:
if calendar.toggle == "True" and calendar.calendar_type == "ical":
ical = iCalendar(requests.get(calendar.calendar_id).text)
for event in ical.events:
event = fitEventToCalendarTimezone(event, ical)
name = event.name
start = event.begin.format()
end = event.end.format()
newEvent = bEvent(name, start, end)
if eventWithinStartEnd(startDate, endDate, event):
if not eventAllDay(event):
print(start, flush=True)
all_events.append(newEvent)
return all_events
def fitEventToCalendarTimezone(event, calendar):
event.begin = event.begin.to(next(iter(calendar._timezones)))
event.end = event.end.to(next(iter(calendar._timezones)))
return event
# converts everything to epoch seconds and utc
# and then checks if at either event start or event end
# is is within startDate and endDate
def eventWithinStartEnd(startDate, endDate, event):
if((startDate.timestamp() <= event.begin.timestamp and
event.begin.timestamp <= endDate.timestamp()) or
startDate.timestamp() <= event.end.timestamp and
event.end.timestamp <= endDate.timestamp()):
return True
return False
def eventAllDay(event):
beginDay = event.begin.replace(hour=0, minute=0, second=0)
endDay = event.end.replace(hour=0, minute=0, second=0)
delta = endDay - beginDay
if delta.days >= 2:
return True
return False