calendarwatch_frontend/app.py
raphael 596f690cce adds sending toggle (true, false) information; adds color picker and sending that information back to server
- the color picker is from 'colorPick' which makes implementation easy. It's only a .js and a .css file
  which need to be included and the color-picker is simple enough to handle on the client side.
  The only main problem with it, is that accessing the 'id' information of the css it was called from is not straightforward

- the toggle switch which is just implemented in .css currently has the checklist inverted, works but ugly

- The Client sends this information back to the server via a json file, which defines the calendar id and either
  color or the toggle information. The server currently just prints this information.great stuff.
2020-04-14 20:11:34 +00:00

253 lines
7.9 KiB
Python

# Python standard libraries
import json
import os
import sqlite3
# Third-party libraries
import flask
from flask import Flask, redirect, request, url_for
from flask_login import (
LoginManager,
current_user,
login_required,
login_user,
logout_user,
)
from oauthlib.oauth2 import WebApplicationClient
import requests
# Internal imports
from database.db import init_db_command
from database.user import User
import caltojson
import google.oauth2.credentials
import google_auth_oauthlib.flow
import googleapiclient.discovery
# Configuration
CLIENT_SECRETS_FILE = "certificate/client_secret.json"
# This OAuth 2.0 access scope allows for full read/write access to the
# authenticated user's account and requires requests to use an SSL connection.
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'
GOOGLE_CLIENT_ID ="377787187748-shuvi4iq5bi4gdet6q3ioataimobs4lh.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET = "Hu_YWmKsVKUcLwyeINYzdKfZ"
GOOGLE_DISCOVERY_URL = (
"https://accounts.google.com/.well-known/openid-configuration"
)
# Flask app setup
app = Flask(__name__,
static_folder='static',
template_folder='template')
app.secret_key = os.environ.get("SECRET_KEY") or os.urandom(24)
# User session management setup
# https://flask-login.readthedocs.io/en/latest
login_manager = LoginManager()
login_manager.init_app(app)
# Naive database setup
try:
init_db_command()
except sqlite3.OperationalError:
# Assume it's already been created
pass
# OAuth 2 client setup
client = WebApplicationClient(GOOGLE_CLIENT_ID)
# Flask-Login helper to retrieve a user from our db
@login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
@app.route("/")
def account():
return flask.redirect('account')
@app.route("/account")
def index():
if current_user.is_authenticated:
return (flask.render_template('account.html',
username = current_user.name, email = current_user.email, profile_img=current_user.profile_pic
)
)
else:
return flask.render_template('login.html')
def get_google_provider_cfg():
return requests.get(GOOGLE_DISCOVERY_URL).json()
class Calendar:
def __init__(self, name, color):
self.name = name
self.color = color
@app.route("/calendar")
@login_required
def calendar():
ca1 = Calendar("Hightower", "#30ff30")
ca2 = Calendar("Toast", "#66e230")
calendars = [ca1, ca2]
return flask.render_template('calendar.html', calendars=calendars)
@app.route('/test')
def test_api_request():
if 'credentials' not in flask.session:
return flask.redirect('login/google')
# Load credentials from the session.
credentials = google.oauth2.credentials.Credentials(
**flask.session['credentials'])
todaysCal = caltojson.generateJsonFromCalendarEntries(credentials)
# Save credentials back to session in case access token was refreshed.
# ACTION ITEM: In a production app, you likely want to save these
# credentials in a persistent database instead.
flask.session['credentials'] = credentials_to_dict(credentials)
with open('./userinfo/' + current_user.id + '/calendarevents.json', 'w') as outfile:
json.dump(todaysCal, outfile)
return flask.jsonify(todaysCal)
@app.route("/login/google")
def login():
'''
# Find out what URL to hit for Google login
google_provider_cfg = get_google_provider_cfg()
authorization_endpoint = google_provider_cfg["authorization_endpoint"]
# Use library to construct the request for Google login and provide
# scopes that let you retrieve user's profile from Google
request_uri = client.prepare_request_uri(
authorization_endpoint,
redirect_uri=request.base_url + "/callback",
scope=["openid", "email", "profile", "https://www.googleapis.com/auth/calendar.readonly"],
)
return redirect(request_uri)
'''
# Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps.
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES)
# The URI created here must exactly match one of the authorized redirect URIs
# for the OAuth 2.0 client, which you configured in the API Console. If this
# value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch'
# error.
flow.redirect_uri = request.base_url + "/callback"
authorization_url, state = flow.authorization_url(
# Enable offline access so that you can refresh an access token without
# re-prompting the user for permission. Recommended for web server apps.
access_type='offline',
# Enable incremental authorization. Recommended as a best practice.
include_granted_scopes='true')
# Store the state so the callback can verify the auth server response.
flask.session['state'] = state
return flask.redirect(authorization_url)
@app.route("/login/google/callback")
def callback():
# Specify the state when creating the flow in the callback so that it can
# verified in the authorization server response.
state = flask.session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
flow.redirect_uri = request.base_url
# Use the authorization server's response to fetch the OAuth 2.0 tokens.
authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)
# Store credentials in the session.
# ACTION ITEM: In a production app, you likely want to save these
# credentials in a persistent database instead.
credentials = flow.credentials
flask.session['credentials'] = credentials_to_dict(credentials)
session = flow.authorized_session()
userinfo = session.get('https://www.googleapis.com/userinfo/v2/me').json()
# Create a user in your db with the information provided
# by Google
user = User(
id_=userinfo['id'],
name=userinfo['name'],
email=userinfo['email'],
profile_pic=userinfo['picture']
)
# Doesn't exist? Add it to the database.
if not User.get(user.id):
User.create(user.id, user.name, user.email, user.profile_pic)
# Begin user session by logging the user in
login_user(user)
return flask.redirect(flask.url_for('index'))
@app.route("/logout")
@login_required
def logout():
logout_user()
return redirect(url_for("index"))
def credentials_to_dict(credentials):
return {'token': credentials.token,
'refresh_token': credentials.refresh_token,
'token_uri': credentials.token_uri,
'client_id': credentials.client_id,
'client_secret': credentials.client_secret,
'scopes': credentials.scopes}
@app.route("/userinfo/<path:user>/calendarevents.json")
def downloader(user):
print(user)
path = "/home/raphael/dev/website_ws/website/userinfo/" + user
return flask.send_from_directory(path, "calendarevents.json")
# POST
@app.route('/calendar', methods = ['POST', 'DELETE'])
@login_required
def user():
if request.method == 'POST':
calId = request.json.get('calendar_id')
color = request.json.get('color')
toggle = request.json.get('toggle')
print(calId)
if color != None:
print(color)
if toggle != None:
print(toggle)
# toggle specific calendar of user
elif request.method == 'DELETE':
# do nothing
return 'NONE'
else:
# POST Error 405
print("405")
return 'OK'
if __name__ == "__main__":
context = ('certificate/xip.io.crt', 'certificate/xip.io.key')#certificate and key files
app.run('0.0.0.0', 1234, ssl_context=context, debug=True)