Compare commits

..

15 Commits

Author SHA1 Message Date
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
8d546e37a8 adds regular processing script whichs writes to database in server 2020-04-24 18:23:47 +00:00
aaf66a1f10 changes process the colors are remapped into a more capsuled function 2020-04-15 17:43:52 +00:00
8 changed files with 205 additions and 261 deletions

1
.gitignore vendored
View File

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

201
__init__.py Normal file
View File

@ -0,0 +1,201 @@
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
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, toggle='False', color="#000000"):
self.name = name
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,
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
pyCalendars.append(Calendar(name, calendarId, 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
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
def toNaturalColor(colormap, orgColor):
for remap in colormap['colors']:
if remap['api'] == orgColor:
return remap['natural']
print(f"failed with {orgColor}")
return orgColor
# use the event color id to convert it to a hex color
def colorizeEvents(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
def fromColorIdGetColor(color, colormap, colors):
return toNaturalColor(colormap, colors['calendar'][color]['background'])
# generates the natural color for a event color id
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).isoformat()
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().isoformat()
return today, twodaysfromnow
def generateJsonFromCalendarEntries(events, colors):
# fix all colors in events that have a different event color
colorizeEvents(events, colors)
# if not events:
# print('No upcoming events found.')
return toJson(events)
if __name__ == '__main__':
main()

View File

@ -1 +0,0 @@
{"kind": "calendar#events", "events": [{"name": "Sleep", "isAllDay": false, "color": "#616161", "startDateTime": {"date": {"year": 2020, "month": 3, "day": 28}, "time": {"hour": 0, "minute": 0, "second": 0}}, "stopDateTime": {"date": {"year": 2020, "month": 3, "day": 28}, "time": {"hour": 8, "minute": 0, "second": 0}}}, {"name": "Sleep", "isAllDay": false, "color": "#616161", "startDateTime": {"date": {"year": 2020, "month": 3, "day": 29}, "time": {"hour": 0, "minute": 0, "second": 0}}, "stopDateTime": {"date": {"year": 2020, "month": 3, "day": 29}, "time": {"hour": 8, "minute": 0, "second": 0}}}]}

View File

@ -1 +0,0 @@
{"calendars": [{"name": "Todoist"}, {"name": "https://intranet.fhwn.ac.at/ics/Default.aspx?FHTimetable.ics&id=YUWSjNJUx5I%3d&datediffmonths=1&replaceSpecialChars=1"}, {"name": "Hightower"}, {"name": "Home \ud83c\udfe0"}, {"name": "D-ARIA"}, {"name": "Office"}, {"name": "http://followshows.com/ical/AS3Bbq0q"}, {"name": "Grey"}, {"name": "Privat"}, {"name": "Work"}, {"name": "Social"}, {"name": "Life"}, {"name": "https://poledancevienna.at/mypdv/ical/XJSY-56GQ-G2VC"}, {"name": "Contacts"}, {"name": "Holidays in Austria"}, {"name": "Week Numbers"}]}

View File

@ -1,255 +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, 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 allEvents:
if event.calendarColorId != 0:
orgColor = colors['calendar'][event.calendarColorId]['background']
else:
calColorId = toCalendarColorId(colormap, event.eventColorId)
orgColor = colors['calendar'][calColorId]['background']
event.colorHex = 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)
for calendar in calendars:
calendar.color = toNaturalColor(colormap, calendar.color)
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)
colors = getCalendarColors(service)
colorizeEvents(allEvents, colors)
# if not events:
# print('No upcoming events found.')
return toJson(allEvents)
if __name__ == '__main__':
main()

View File

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

View File

@ -1 +0,0 @@
{"installed":{"client_id":"590778864034-e3gjrjk8csdu2mee29dpe3mkql419no9.apps.googleusercontent.com","project_id":"quickstart-1583661954000","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"kj-suDOOuYQ3M4RpYG2exHyK","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}}

Binary file not shown.