From 8fe0bbcd381cb883f9c885dff889481b10ab1e73 Mon Sep 17 00:00:00 2001 From: Anon Ray Date: Sat, 10 May 2014 21:28:02 +0530 Subject: [PATCH] Lots of refactor, editing sweet and few more functionality - Refactor views to abstract a lot of logic to models - API for editing sweet - Permalink(non-API) to see a specific sweet - Frontend async code to edit sweets. (It would be better, IMO, if we can make the frontend of swtstore a separate app). - Change few APIs to be better. (Like previously to list a user's apps or contexts; one would go to /apps or /contexts; now it is /users/me/apps and /users/me/contexts, also added /users/me/sweets). - Move a lot of debug print statements to a logger component. --- runserver.py | 2 +- swtstore/application.py | 21 ++++- swtstore/classes/exceptions.py | 2 + swtstore/classes/models/client.py | 11 +-- swtstore/classes/models/context.py | 2 +- swtstore/classes/models/sweet.py | 75 +++++++++++++++- swtstore/classes/models/um | 2 +- swtstore/classes/utils/__init__.py | 1 + swtstore/classes/utils/httputils.py | 36 ++++++-- swtstore/classes/views/__init__.py | 1 + swtstore/classes/views/api.py | 154 ++++++++++++++------------------ swtstore/classes/views/app.py | 21 +---- swtstore/classes/views/context.py | 36 ++------ swtstore/classes/views/frontend.py | 12 +-- swtstore/classes/views/oauth.py | 14 +-- swtstore/classes/views/sweet.py | 30 +++++++ swtstore/classes/views/user.py | 72 ++++++++++++--- swtstore/static/js/main.js | 64 +++++++++++++ swtstore/templates/index.html | 2 +- swtstore/templates/layout.html | 26 ++---- swtstore/templates/my_apps.html | 25 ++++++ swtstore/templates/my_contexts.html | 20 +++++ swtstore/templates/my_sweets.html | 68 ++++++++++++++ swtstore/templates/specific_sweet.html | 28 ++++++ 24 files changed, 532 insertions(+), 193 deletions(-) create mode 100644 swtstore/classes/views/sweet.py create mode 100644 swtstore/templates/my_apps.html create mode 100644 swtstore/templates/my_contexts.html create mode 100644 swtstore/templates/my_sweets.html create mode 100644 swtstore/templates/specific_sweet.html diff --git a/runserver.py b/runserver.py index c017938..a2a74b1 100644 --- a/runserver.py +++ b/runserver.py @@ -19,4 +19,4 @@ app = create_app() # Run the server if this script is directly executed # Presumably, this is development mode if __name__ == '__main__': - app.run(debug=True, host='0.0.0.0', port=5001) + app.run(host='0.0.0.0', port=5001) diff --git a/swtstore/application.py b/swtstore/application.py index 96f7ada..89fdc97 100644 --- a/swtstore/application.py +++ b/swtstore/application.py @@ -5,6 +5,7 @@ from flask import Flask, request, jsonify, render_template, make_response, g import os +import logging from classes.database import db from config import DefaultConfig @@ -21,6 +22,7 @@ DEFAULT_MODULES = ( (views.api, '/api'), (views.user, '/users'), (views.context, '/contexts'), + (views.sweet, '/sweets'), (views.app, '/apps'), (views.Oauth, '/oauth') ) @@ -148,6 +150,21 @@ def configure_errorhandlers(app): def configure_logging(app): - #TODO: implement - pass + + formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s ' + '[in %(pathname)s:%(lineno)d]') + + # TODO: maybe we can use a RotatingFileHandler? + # Also error can be sent out via email. So we can also have a SMTPHandler? + log_handler = logging.StreamHandler() + + if app.config.has_key('LOG_LEVEL'): + log_level = app.config['LOG_LEVEL'] or 'ERROR' + else: + log_level = 'ERROR' + + log_handler.setLevel(log_level) + log_handler.setFormatter(formatter) + + app.logger.addHandler(log_handler) diff --git a/swtstore/classes/exceptions.py b/swtstore/classes/exceptions.py index 067fce8..72dde12 100644 --- a/swtstore/classes/exceptions.py +++ b/swtstore/classes/exceptions.py @@ -6,3 +6,5 @@ from sqlalchemy.exc import DontWrapMixin class AlreadyExistsError(Exception, DontWrapMixin): pass +class InvalidPayload(Exception, DontWrapMixin): + pass diff --git a/swtstore/classes/models/client.py b/swtstore/classes/models/client.py index 06e384b..82199b9 100644 --- a/swtstore/classes/models/client.py +++ b/swtstore/classes/models/client.py @@ -3,6 +3,7 @@ # class:: Client from datetime import datetime, timedelta +from flask import current_app from swtstore.classes.database import db from swtstore.classes.models.um import User @@ -156,18 +157,18 @@ class Token(db.Model): @oauth.clientgetter def loadClient(client_id): - print '@oauth.clientgetter' + current_app.logger.debug('@oauth.clientgetter') #return Client.query.filter_by(id=client_id).first() return Client.query.get(client_id) @oauth.grantgetter def loadGrant(client_id, code): - print '@oauth.grantgetter' + current_app.logger.debug('@oauth.grantgetter') return Grant.query.filter_by(client_id=client_id, code=code).first() @oauth.grantsetter def saveGrant(client_id, code, request, *args, **kwargs): - print '@oauth.grantsetter' + current_app.logger.debug('@oauth.grantsetter') expires = datetime.utcnow() + timedelta(seconds=100) grant = Grant( client_id = client_id, @@ -183,7 +184,7 @@ def saveGrant(client_id, code, request, *args, **kwargs): @oauth.tokengetter def loadToken(access_token=None, refresh_token=None): - print '@oauth.tokengetter' + current_app.logger.debug('@oauth.tokengetter') if access_token: return Token.query.filter_by(access_token=access_token).first() elif refresh_token: @@ -191,7 +192,7 @@ def loadToken(access_token=None, refresh_token=None): @oauth.tokensetter def saveToken(token, request, *args, **kwargs): - print '@oauth.tokensetter' + current_app.logger.debug('@oauth.tokensetter') toks = Token.query.filter_by(client_id=request.client.id, user_id=request.user.id) diff --git a/swtstore/classes/models/context.py b/swtstore/classes/models/context.py index f81a9c7..bbab4a1 100644 --- a/swtstore/classes/models/context.py +++ b/swtstore/classes/models/context.py @@ -58,7 +58,7 @@ class Context(db.Model): # return a context instance given a name @staticmethod - def getContextByName(name): + def getByName(name): return Context.query.filter_by(name=name).first() @staticmethod diff --git a/swtstore/classes/models/sweet.py b/swtstore/classes/models/sweet.py index 4af55b8..29677e1 100644 --- a/swtstore/classes/models/sweet.py +++ b/swtstore/classes/models/sweet.py @@ -2,12 +2,15 @@ # classes/sweet.py # class:: Sweet +from flask import current_app from datetime import datetime from swtstore.classes.database import db # custom SQLAlchemy type JSONType from swtstore.classes.models.types import JSONType from swtstore.classes.utils import urlnorm # normalize URLs +from swtstore.classes.models import Context +from swtstore.classes.models.um import User class Sweet(db.Model): """ customary docstring """ @@ -30,7 +33,7 @@ class Sweet(db.Model): def __init__(self, who, what, where, how): - print 'initing sweet..' + current_app.logger.info('initing sweet..') self.who = who self.what = what self.where = urlnorm(where) @@ -45,9 +48,76 @@ class Sweet(db.Model): return '[Sweet Object: <%s : @%s: #%s : %s>]' % (self.id, self.who, self.what, self.where) + # Update the sweet - only 'how' and 'where' fields can be updated + def update(self, **kwargs): + if kwargs.get('how'): + self.how = kwargs.get('how') + self.persist() + if kwargs.get('where'): + self.where = kwargs.get('where') + self.persist() + + return None + + + # create multiple sweets from a list of JSON + @staticmethod + def createSweets(who, payload): + # the payload has to be a list; a list of swts + for each in payload: + if 'what' not in each and 'where' not in\ + each and 'how' not in each: + + raise InvalidPayload('Invalid payload %s \n for creating\ + mutiple sweets' % (each)) + return None + + # all ok. create swts from the list now + swts = [] + for each in payload: + + what = Context.getByName(each['what']) + + if what is None: + current_app.logger.info('Context "%s" do not exist. Aborting', + what) + g.error = 'Context do not exist' + abort(400) # this context doesn't exist! + + current_app.logger.debug('SWEET PAYLOAD\n---\n%s\n%s\n%s\n%s\n----', + who, what, each['where'], each['how']) + + new_sweet = Sweet(who, what, each['where'], each['how']) + + new_sweet.persist() + current_app.logger.debug('New Sweet %s', new_sweet) + swts.append(new_sweet) + + return swts + + # get Sweets for frontend + @staticmethod + def getFrontendSwts(): + return Sweet.query.order_by(Sweet.created.desc()).all() + + # get sweets all sweets authored by a particular user + @staticmethod + def getByCreator(user): + return Sweet.query.filter_by(who=user).\ + order_by(Sweet.created.desc()).all() + + # allow to query all sweets based on "who", "what" and "where" params + @staticmethod + def queryByAll(params): + if params.get('who'): + params['who'] = User.getByName(params['who']) + if params.get('what'): + params['what'] = Context.getByName(params['what']) + + return Sweet.query.filter_by(**params).all() + # return a dictionary of data members def to_dict(self): - print self.created return { 'id': self.id, 'who': self.who.username, @@ -63,6 +133,7 @@ class Sweet(db.Model): # create and persist the sweet to the database def persist(self): + current_app.logger.debug('Commiting sweet %s to db', self) db.session.add(self) db.session.commit() diff --git a/swtstore/classes/models/um b/swtstore/classes/models/um index e432b39..688a5be 160000 --- a/swtstore/classes/models/um +++ b/swtstore/classes/models/um @@ -1 +1 @@ -Subproject commit e432b39ffc7d1d465d443db00be12fe16a34f073 +Subproject commit 688a5beac835a26efe7c06efe6b9127f83adeb03 diff --git a/swtstore/classes/utils/__init__.py b/swtstore/classes/utils/__init__.py index 43cee70..28f6d95 100644 --- a/swtstore/classes/utils/__init__.py +++ b/swtstore/classes/utils/__init__.py @@ -3,3 +3,4 @@ __init__.py """ from urlnorm import urlnorm +from httputils import makeCORSHeaders diff --git a/swtstore/classes/utils/httputils.py b/swtstore/classes/utils/httputils.py index d7db776..8ad3f92 100644 --- a/swtstore/classes/utils/httputils.py +++ b/swtstore/classes/utils/httputils.py @@ -1,13 +1,39 @@ +# HTTP utilities +from flask import current_app - -def make_cross_origin_headers(response, host_url): - print 'client\'s host_url: %s' % host_url +def makeCORSHeaders(response, host_url): + current_app.logger.debug('makeCORSHeaders(): client\'s host_url: %s', + host_url) response.headers['Access-Control-Allow-Origin'] = host_url response.headers['Access-Control-Max-Age'] = '3600' response.headers['Access-Control-Allow-Credentials'] = 'true' response.headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept' - print 'Updated headers' - print response.headers + current_app.logger.debug('Updated headers %s', response.headers) return response + +from datetime import timedelta +from flask import make_response, request, current_app +from functools import update_wrapper + +""" +def crossdomain(origin=None, methods=None, headers=None, max_age=3600, + attach_to_all=True, automatic_options=True): + + if methods is not None: + methods = ', '.join(sorted(i.upper() for i in methods)) + if headers in not None and not isinstance(headers, basestring): + headers = ', '.join(i.upper() for i in headers) + if not isinstance(origin, basestring): + origin = ', '.join(origin) + if isinstance(max_age, timedelta): + max_age = max_age.total_seconds() + + def get_methods(): + if methods is not None: + return methods + + options_resp = current + +""" diff --git a/swtstore/classes/views/__init__.py b/swtstore/classes/views/__init__.py index 1f6d0ed..20db930 100644 --- a/swtstore/classes/views/__init__.py +++ b/swtstore/classes/views/__init__.py @@ -2,5 +2,6 @@ from .frontend import frontend from .api import api from .user import user from .context import context +from .sweet import sweet from .app import app from .oauth import Oauth diff --git a/swtstore/classes/views/api.py b/swtstore/classes/views/api.py index 62b8487..ef42a6f 100644 --- a/swtstore/classes/views/api.py +++ b/swtstore/classes/views/api.py @@ -1,12 +1,11 @@ -from flask import Module, jsonify, request, make_response, abort, g, json -#import json -from sqlalchemy.exc import IntegrityError +from flask import Module, jsonify, request, make_response, abort, g, json,\ + current_app from swtstore.classes.models import Context from swtstore.classes.models import Sweet -from swtstore.classes.exceptions import AlreadyExistsError +from swtstore.classes.exceptions import AlreadyExistsError, InvalidPayload from swtstore.classes.utils import urlnorm # normalize URLs -from swtstore.classes.utils.httputils import make_cross_origin_headers +from swtstore.classes.utils.httputils import makeCORSHeaders from swtstore.classes import oauth @@ -14,18 +13,39 @@ api = Module(__name__) # Get a specific sweet -@api.route('/sweets/', methods=['GET']) +# Update a specific sweet +@api.route('/sweets/', methods=['GET', 'PUT']) def getSweetById(id): - try: + + # Get a specific sweet + if request.method == 'GET': sweet = Sweet.query.get(id) - except: - abort(404) - if sweet is None: - abort(404) + if sweet is None: + abort(404) + + current_app.logger.debug('getSweetById: %s', sweet) + return jsonify(sweet.to_dict()) + + # Update a specific sweet + elif request.method == 'PUT': + payload = request.json + if payload is None: + abort(400) + + current_app.logger.debug('Update Sweet: recvd payload: %s', payload) - print sweet - return jsonify(sweet.to_dict()) + sweet = Sweet.query.get(id) + + if sweet is None: + abort(404) + + current_app.logger.debug('Updating sweet %s with new how: %s ', + sweet.id, payload) + + sweet.update(how=payload) + + return jsonify(sweet.to_dict()) # Post a sweet to the sweet store @@ -37,77 +57,46 @@ def createSweet(oauth_request): client = oauth_request.client - #TODO: check if response is coming from a registered client - response = make_cross_origin_headers(response, client.host_url) + #TODO: make a decorator of CORS request + response = makeCORSHeaders(response, client.host_url) if request.method == 'OPTIONS': response.status_code = 200 return response - if request.json: - payload = request.json - if request.data: - payload = json.loads(request.data) - else: - print 'data not found in payload!' + payload = request.json or request.data + if not payload: + current_app.logger.error('data not found in payload!') g.error= 'data not found in payload!' abort(400) - print 'new sweet payload recvd..' - print payload + current_app.logger.debug('new sweet payload recvd.. %s', payload) - # the payload has to be a list; a list of swts - for each in payload: - if 'what' not in each and 'where' not in\ - each and 'how' not in each: + # Get the authenticated user from the oauth request object. + # Older swtr clients sending `who` in string will be ignored. + who = oauth_request.user - print 'Invalid Request..' - abort(400) - - # all ok. create swts from the list now - - swts = [] - for each in payload: - - what = Context.query.filter_by(name=each['what']).first() - - if what is None: - print 'Context doesn\'t exist' - g.error = 'Context doesn\'t exist' - abort(400) # this context doesn't exist! - - # Get the authenticated user from the oauth request object. - # Older swtr clients sending `who` in string will be ignored. - who = oauth_request.user - - print 'SWEET DATA' - print '------------' - print who - print what - print each['where'] - print each['how'] - print '-------------' - - new_sweet = Sweet(who, what, each['where'], each['how']) - - new_sweet.persist() - print new_sweet - swts.append(new_sweet.id) + try: + swts = Sweet.createSweets(who, payload) + except InvalidPayload(msg): + current_app.logger.error('Invalid Payload in request') + abort(400) response.status_code = 200 - response.data = json.dumps(swts) + response.headers['Content-type'] = 'application/json' + response.data = json.dumps([i.to_dict() for i in swts]) return response # The Sweet query API: /sweets/q?who=<>&what=<>&where=<> # args: who, what, where @api.route('/sweets/q', methods=['GET', 'OPTIONS']) -@oauth.require_oauth('sweet') -def querySweets(oauth_request): +#@oauth.require_oauth('sweet') +def querySweets(): response = make_response() - response = make_cross_origin_headers(response, - oauth_request.client.host_url) + origin = request.headers.get('Origin', '*') + response = makeCORSHeaders(response, origin) if request.method == 'OPTIONS': reponse.status_code = 200 @@ -123,8 +112,7 @@ def querySweets(oauth_request): if args.get('who'): params['who'] = args.get('who') if args.get('what'): - what = Context.query.filter_by(name=args.get('what')).first() - params['what'] = what + params['what'] = args.get('what') if args.get('where'): params['where'] = urlnorm(args.get('where')) @@ -132,14 +120,12 @@ def querySweets(oauth_request): if len(params) == 0: abort(400) - print 'recvd params' - print params + current_app.logger.debug('recvd params: %s', params) - - sweets = Sweet.query.filter_by(**params).all() + sweets = Sweet.queryByAll(params) if len(sweets) == 0: - print 'No sweets found to satisfy query..' + current_app.logger.info('No sweets found to satisfy query..') abort(404) swts = [i.to_dict() for i in sweets] @@ -153,11 +139,11 @@ def querySweets(oauth_request): @api.route('/contexts/', methods=['GET']) def getContextByName(name): - context = Context.query.filter_by(name=name).first() + context = Context.getByName(name) if context is None: abort(404) - print context + current_app.logger.debug('getContextByName : %s', context) return jsonify(context.to_dict()) # Get a specific context with its definition; based on id @@ -168,10 +154,9 @@ def getContextById(id): if context is None: abort(404) - print context - print context.created - print context.to_dict() - print jsonify(context.to_dict()).data + current_app.logger.debug('getContextById response: %s', + jsonify(context.to_dict()).data) + return jsonify(context.to_dict()) @@ -191,9 +176,7 @@ def createContext(): # if not found send back a 400 abort(400) - #TODO: change it to logger component - print 'new context payload recvd..' - print payload + current_app.logger.debug('new context payload recvd.. %s', payload) # if data is invalid send back 400 if 'name' not in payload and 'definition' not in payload: @@ -204,11 +187,11 @@ def createContext(): except AlreadyExistsError: # context with same name exists; send back 400? - print 'Already Exists Error' + current_app.logger.info('Context Already Exists Error') abort(400) - print 'new context' - print new_context + current_app.logger.debug('new context created: %s', new_context) + # all ok. save the new context res = new_context.persist() @@ -221,13 +204,14 @@ def createContext(): @oauth.require_oauth('email') def getCurrentUser(oauth_request): response = make_response() - response = make_cross_origin_headers(response, - oauth_request.client.host_url) + response = makeCORSHeaders(response, oauth_request.client.host_url) response.status_code = 200 if request.method == 'OPTIONS': return response + response.headers['Content-type'] = 'application/json' + # We have the user object along with the oauth request. Just return it back response.data = json.dumps(oauth_request.user.to_dict()) return response diff --git a/swtstore/classes/views/app.py b/swtstore/classes/views/app.py index 9b91c07..75eceeb 100644 --- a/swtstore/classes/views/app.py +++ b/swtstore/classes/views/app.py @@ -1,8 +1,6 @@ - # -*- coding utf-8 -*- # classes/views/apps.py - from flask import Module, jsonify, request, render_template, redirect,\ url_for, flash, abort @@ -16,19 +14,6 @@ from swtstore.classes.utils import urlnorm app = Module(__name__) -@app.route('/', methods=['GET']) -# list apps owned by current user -def list(): - current_user = User.getCurrentUser() - if current_user is None: - return redirect(url_for('frontend.index')) - - her_apps = Client.getClientsByCreator(current_user.id) - print 'her_apps' - print her_apps - return render_template('list_apps.html', apps=her_apps, - user=current_user.to_dict()) - @app.route('/register', methods=['GET', 'POST']) def register(): @@ -36,10 +21,8 @@ def register(): if current_user is None: return redirect(url_for('frontend.index')) - user = current_user.to_dict() - if request.method == 'GET': - return render_template('register_app.html', user=user) + return render_template('register_app.html') elif request.method == 'POST': req_fields = ['name', 'host_url', 'redirect_uris', 'scopes'] @@ -60,5 +43,5 @@ def register(): ) new_app.persist() - return redirect(url_for('list')) + return redirect(url_for('user.myApps')) diff --git a/swtstore/classes/views/context.py b/swtstore/classes/views/context.py index 5b0891d..b80623c 100644 --- a/swtstore/classes/views/context.py +++ b/swtstore/classes/views/context.py @@ -1,10 +1,8 @@ - # -*- coding utf-8 -*- # classes/views/context.py - -from flask import Module, jsonify, request, render_template, redirect, url_for -import json +from flask import Module, jsonify, request, render_template, redirect,\ + url_for, json, current_app from swtstore.classes.models import Context from swtstore.classes.models.um import User @@ -12,47 +10,31 @@ from swtstore.classes.models.um import User context = Module(__name__) -@context.route('/', methods=['GET']) -# list apps owned by current user -def list(): - current_user = User.getCurrentUser() - if current_user is None: - return redirect(url_for('index')) - - her_contexts = Context.getByCreator(current_user.id) - print 'her_apps' - print her_contexts - - return render_template('list_contexts.html', contexts=her_contexts, - user=current_user.to_dict()) - - @context.route('/register', methods=['GET', 'POST']) def register(): current_user = User.getCurrentUser() if current_user is None: - return redirect(url_for('index')) - - user = current_user.to_dict() + return redirect(url_for('frontend.index')) if request.method == 'GET': - return render_template('register_context.html', user=user) + return render_template('register_context.html') if request.method == 'POST': if not request.form.get('name') or not request.form.get('defn'): abort(400) - print request.form.get('defn') + current_app.logger.debug('New Context: defn: %s ', + request.form.get('defn')) json_ld = json.loads(request.form.get('defn')) - print json_ld + current_app.logger.debug('Resulting json_ld %s', json_ld) new_context = Context( name = request.form.get('name'), definition = json_ld, user_id = current_user.id ) - print new_context + current_app.logger.debug('New Context created: %s', new_context) new_context.persist() - return redirect(url_for('list')) + return redirect(url_for('user.myContexts')) diff --git a/swtstore/classes/views/frontend.py b/swtstore/classes/views/frontend.py index 5053b46..5134b79 100644 --- a/swtstore/classes/views/frontend.py +++ b/swtstore/classes/views/frontend.py @@ -2,7 +2,10 @@ # classes/views/frontend.py -from flask import Module, jsonify, request, render_template, redirect, url_for +from flask import Module, jsonify, request, render_template, redirect,\ + url_for, g, current_app + +from sqlalchemy import desc from swtstore.classes.models import Sweet from swtstore.classes.models.um import User @@ -12,14 +15,11 @@ frontend = Module(__name__) @frontend.route('/', methods=['GET']) def index(): - # sweets = Sweet.query.somethingToGetRelevant() - sweets = Sweet.query.all() + sweets = Sweet.getFrontendSwts() user = User.getCurrentUser() - if user is not None: - user = user.to_dict() - return render_template('index.html', sweets=sweets, user=user) + return render_template('index.html', sweets=sweets) # Create a new sweet context diff --git a/swtstore/classes/views/oauth.py b/swtstore/classes/views/oauth.py index aaf70f2..442bd50 100644 --- a/swtstore/classes/views/oauth.py +++ b/swtstore/classes/views/oauth.py @@ -1,8 +1,8 @@ # -*- coding utf-8 -*- # classes/views/oauth.py -from flask import Module, jsonify, request, render_template, redirect, url_for -import json +from flask import Module, jsonify, request, render_template, redirect,\ + url_for, current_app from swtstore.classes import oauth from swtstore.classes.models.um import User @@ -21,24 +21,24 @@ def authorize(*args, **kwargs): if request.method == 'GET': client_id = kwargs.get('client_id') client = Client.query.get(client_id) - print '/authorize: ' - print client + current_app.logger.debug('In /authorize: client: %s', client) kwargs['client'] = client kwargs['user'] = current_user - print kwargs + current_app.logger.debug('kwargs %s', kwargs) return render_template('authorize.html', **kwargs) confirm = request.form.get('confirm', 'no') - print confirm + current_app.logger.debug('confirm authorize from user: %s', confirm) return confirm == 'yes' @Oauth.route('/token', methods=['GET', 'POST']) @oauth.token_handler def access_token(): #print request.form - print 'access token touched..' + current_app.logger.debug('access token touched..') return None @Oauth.route('/errors') def error(): return jsonify(error=request.args.get('error')) + diff --git a/swtstore/classes/views/sweet.py b/swtstore/classes/views/sweet.py new file mode 100644 index 0000000..0a2785c --- /dev/null +++ b/swtstore/classes/views/sweet.py @@ -0,0 +1,30 @@ +# -*- coding utf-8 -*- +# classes/views/sweet.py + + +from flask import Module, jsonify, request, render_template, redirect,\ + url_for, abort, json + +from swtstore.classes.models import Context +from swtstore.classes.models import Sweet +from swtstore.classes.models.um import User + + +sweet = Module(__name__) + +@sweet.route('/', methods=['GET']) +def showSweet(id): + #current_user = User.getCurrentUser() + #if current_user is None: + # return redirect(url_for('index')) + + #user = current_user.to_dict() + print "recvd sweet id: %s" % (id) + sweet = Sweet.query.get(id) + if sweet: + print "sweet found " + str(sweet) + return render_template('specific_sweet.html', sweet=sweet) + else: + abort(404) + + diff --git a/swtstore/classes/views/user.py b/swtstore/classes/views/user.py index 5cb10dc..48b1f46 100644 --- a/swtstore/classes/views/user.py +++ b/swtstore/classes/views/user.py @@ -3,13 +3,15 @@ import requests +# flask imports from flask import Module, jsonify, request, render_template, session,\ - make_response, url_for, redirect -import json + make_response, url_for, redirect, json, current_app +# swtstore imports from swtstore.classes.models.um import User +from swtstore.classes.models import Sweet, Context, Client -from swtstore.classes.utils.httputils import make_cross_origin_headers +from swtstore.classes.utils.httputils import makeCORSHeaders from swtstore.config import DefaultConfig @@ -21,19 +23,22 @@ user = Module(__name__) def login(): response = make_response() - #response = make_cross_origin_headers(response) + #response = makeCORSHeaders(response) if 'assertion' not in request.form: response.status_code = 400 return response - print request.remote_addr + current_app.logger.debug('remote address of request for user login %s', + request.remote_addr) + data = {'assertion': request.form['assertion'], 'audience': config.SWTSTORE_URL} resp = requests.post(config.MOZ_PERSONA_VERIFIER, data=data, verify=True) - print resp.status_code - print resp.json() + current_app.logger.debug('Response code from MOZ_PERSONA_VERIFIER %s', + resp.status_code) + current_app.logger.debug('Response body: %s', resp.json()) if resp.ok: verified_data = json.loads(resp.content) @@ -43,14 +48,17 @@ def login(): current_user = User.query.filter_by(email=user_email).first() # user doesn't exist; create her if current_user is None: - print 'user with email ' + user_email + ' doesn\'t exist;' - print 'creating new user:' + user_email + current_app.logger.info('user with email %s doesn\'t exist', + user_email) + current_app.logger.info('creating new user: %s', user_email) + new_user = User('', user_email) new_user.persist() current_user = new_user #session.update({'email': verified_data['email']}) - print 'logging in user with email' + user_email + current_app.logger.info('logging in user with email %s', + user_email) session['email'] = current_user.email response.status_code = 200 @@ -64,11 +72,10 @@ def login(): def logout(): response = make_response() - #response = make_cross_origin_headers(response) + #response = makeCORSHeaders(response) if 'email' in session: - print 'logging out ' - print session['email'] + current_app.logger.info('logging out user %s', session['email']) session.pop('email') response.status_code = 200 @@ -85,9 +92,46 @@ def profile(): return render_template('me.html', user=current_user) username = request.form.get('username') - print username + + current_app.logger.debug('Updating username of %s to %s', + current_user.username, username) + current_user.update(username=username) return redirect(url_for('profile')) +@user.route('/me/sweets', methods=['GET']) +def mySweets(): + + user = User.getCurrentUser() + if user is None: + return redirect(url_for('frontend.index')) + + swts = Sweet.getByCreator(user) + return render_template('my_sweets.html', sweets=swts) + + +@user.route('/me/contexts', methods=['GET']) +def myContexts(): + + user = User.getCurrentUser() + if user is None: + return redirect(url_for('frontend.index')) + + contexts = Context.getByCreator(user.id) + return render_template('my_contexts.html', contexts=contexts) + + +@user.route('/me/apps', methods=['GET']) +def myApps(): + + # make a decorator out of this repetative code + user = User.getCurrentUser() + if user is None: + return redirect(url_for('frontend.index')) + + apps = Client.getClientsByCreator(user.id) + return render_template('my_apps.html', apps=apps) + + diff --git a/swtstore/static/js/main.js b/swtstore/static/js/main.js index e42c5f2..2834b83 100644 --- a/swtstore/static/js/main.js +++ b/swtstore/static/js/main.js @@ -5,6 +5,7 @@ this.attachLogout(); this.initPersona(); this.activeNav(); + $('.edit-sweet').click(ss.editSweet); }; ss.activeNav = function() { @@ -70,4 +71,67 @@ }); }; + ss.editSweet = function(event) { + var target = $(event.currentTarget).attr('for'); + var how = JSON.parse($(event.currentTarget).siblings('.how').html()); + //console.log(how); + // update sweet function + function updateSweet(event) { + var changed = false; + for(var field in how) { + var data = $('#edit-sweet-modal .modal-body textarea[name="'+field+'"]').val(); + var item = (typeof how[field] === 'object') ? JSON.stringify(how[field]) : + how[field]; + if(data !== item) { + changed = true; + how[field] = data; + console.log('Updated '+ field + ' with data: ', data); + } + } + if(changed) { + $('#save-edited-sweet').text('Saving Changes'); + + $.ajax({ + type: 'PUT', + url: 'http://localhost:5001/api/sweets/'+target, + contentType: 'application/json', + data: JSON.stringify(how), + success: function(data) { + console.log('Updated swt from the server ', data); + $('#save-edited-sweet').text('Save Changes'); + $('#edit-sweet-modal').modal('hide'); + }, + error: function() { + $('#save-edited-sweet').text('Save Changes'); + } + }); + } + else { + return; + } + } + // prepare the edit view + $('#edit-sweet-modal .modal-body').html(''); + for(var field in how) { + var item = (typeof how[field] === 'object') ? JSON.stringify(how[field]) : + how[field]; + + $('#edit-sweet-modal .modal-body').append('
'+ + field+''); + /*$('', + {name: field, value: item, class: 'form-control', type: 'text'}). + appendTo('#edit-sweet-modal .modal-body');*/ + $('#edit-sweet-modal .modal-body').append(''); + + $('#edit-sweet-modal').append('
'); + } + // launch the modal + $('#edit-sweet-modal').modal(); + + // attach event handlers + $('#save-edited-sweet').off('click'); + $('#save-edited-sweet').on('click', updateSweet); + + }; + })(ss); diff --git a/swtstore/templates/index.html b/swtstore/templates/index.html index c7a42b5..04736a4 100644 --- a/swtstore/templates/index.html +++ b/swtstore/templates/index.html @@ -28,7 +28,7 @@ created: {{sweet.created }} - + diff --git a/swtstore/templates/layout.html b/swtstore/templates/layout.html index 26dc25e..8c420f5 100644 --- a/swtstore/templates/layout.html +++ b/swtstore/templates/layout.html @@ -17,11 +17,15 @@
  • Home
  • - {% if not user %} + {% if not session.email %}
  • Login
  • {% else %} + +
  • + My Sweets +
  • @@ -44,11 +48,7 @@