From eba3fc5f07b59536c41ccdeee5dea735f2856128 Mon Sep 17 00:00:00 2001 From: Feliciaan De Palmenaer Date: Thu, 26 Mar 2015 21:49:14 +0100 Subject: [PATCH] forgot to add some files --- app/admin.py | 28 +++++++++ app/app.py | 13 +++++ app/config.example.py | 13 +++++ app/config.py | 13 +++++ app/create_database.py | 18 ++++++ app/{foodput.py => foodbot.py} | 0 app/login.py | 34 +++++++++++ app/models.py | 103 +++++++++++++++++++++++++++++++++ app/requirements.txt | 16 +++++ app/views.py | 43 ++++++++++++++ app/zeus.py | 93 +++++++++++++++++++++++++++++ 11 files changed, 374 insertions(+) create mode 100644 app/admin.py create mode 100644 app/app.py create mode 100644 app/config.example.py create mode 100644 app/config.py create mode 100644 app/create_database.py rename app/{foodput.py => foodbot.py} (100%) create mode 100644 app/login.py create mode 100644 app/models.py create mode 100644 app/requirements.txt create mode 100644 app/views.py create mode 100644 app/zeus.py diff --git a/app/admin.py b/app/admin.py new file mode 100644 index 0000000..a27c1ac --- /dev/null +++ b/app/admin.py @@ -0,0 +1,28 @@ +from flask import url_for, redirect +from flask.ext.admin import Admin, BaseView, expose +from flask.ext.admin.contrib.sqla import ModelView +from flask.ext import login + + +from app import app, db +from models import User +from utils import send_command + + +class ModelBaseView(ModelView): + + def is_accessible(self): + if login.current_user.is_anonymous(): + return False + + return login.current_user.is_admin() + + +class UserAdminModel(ModelBaseView): + column_searchable_list = ('username',) + inline_models = None + form_columns = ('username', 'admin') + +admin = Admin(app, name='FoodBot', url='/foodbot/admin', template_mode='bootstrap3') + +admin.add_view(UserAdminModel(User, db.session)) diff --git a/app/app.py b/app/app.py new file mode 100644 index 0000000..dba2aa0 --- /dev/null +++ b/app/app.py @@ -0,0 +1,13 @@ +from flask import Flask +from flask.ext.sqlalchemy import SQLAlchemy +from werkzeug.contrib.cache import SimpleCache + + +from logbook import Logger + +app = Flask(__name__) +app.config.from_object('config.Configuration') + +db = SQLAlchemy(app) + +logger = Logger('FoodBot-Web') diff --git a/app/config.example.py b/app/config.example.py new file mode 100644 index 0000000..714e80a --- /dev/null +++ b/app/config.example.py @@ -0,0 +1,13 @@ +# config + + +class Configuration(object): + SQLALCHEMY_DATABASE_URI = 'sqlite:///foodbot.db' + DEBUG = True + SECRET_KEY = '' + SLACK_WEBHOOK = '' + PROCESS = 'python test.py' + LOGFILE = 'slotmachien.log' + ZEUS_KEY = '' + ZEUS_SECRET = '' + SLACK_TOKEN = '' diff --git a/app/config.py b/app/config.py new file mode 100644 index 0000000..42a1713 --- /dev/null +++ b/app/config.py @@ -0,0 +1,13 @@ +# config + + +class Configuration(object): + SQLALCHEMY_DATABASE_URI = 'sqlite:///foodbot.db' + DEBUG = True + SECRET_KEY = '' + SLACK_WEBHOOK = '' + PROCESS = 'python test.py' + LOGFILE = 'slotmachien.log' + ZEUS_KEY = 'tomtest' + ZEUS_SECRET = 'blargh' + SLACK_TOKEN = 'xoxp-2484654576-2486580711-4114448516-f21087' diff --git a/app/create_database.py b/app/create_database.py new file mode 100644 index 0000000..c8b86bd --- /dev/null +++ b/app/create_database.py @@ -0,0 +1,18 @@ +from models import * +from app import db + +db.drop_all() +db.create_all() + +feli = User() +feli.configure("feliciaan", True, True) +db.session.add(feli) + +don = User() +don.configure("don", True, True) +db.session.add(don) + +# To future developers, add yourself here + +# commit all the things +db.session.commit() diff --git a/app/foodput.py b/app/foodbot.py similarity index 100% rename from app/foodput.py rename to app/foodbot.py diff --git a/app/login.py b/app/login.py new file mode 100644 index 0000000..419a942 --- /dev/null +++ b/app/login.py @@ -0,0 +1,34 @@ +from flask import redirect, request, url_for, abort, session +from flask.ext.login import LoginManager, current_user, logout_user +from flask_oauthlib.client import OAuth + +import requests + +from app import app, db, logger, cache +from models import User +from zeus import oauth, zeus_login + +login_manager = LoginManager() +login_manager.init_app(app) + + +@login_manager.user_loader +def load_user(userid): + return User.query.filter_by(id=userid).first() + +@app.route('/foodbot/login') +def login(): + return zeus_login() + + +@app.route('/foodbot/logout') +def logout(): + if 'zeus_token' in session: + session.pop('zeus_token', None) + logout_user() + return redirect(url_for('admin.index')) + + +def before_request(): + if current_user.is_anonymous() or not current_user.is_allowed(): + abort(401) diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000..01cfc41 --- /dev/null +++ b/app/models.py @@ -0,0 +1,103 @@ +import uuid +from datetime import date, timedelta + + +from app import db + +# Create database models +class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True) + admin = db.Column(db.Boolean) + bias = db.Column(db.Integer) + courrier = db.relationship('Courrier', backref='courrier', lazy='dynamic') + logactions = db.relationship('LogAction', backref='user', lazy='dynamic') + + def configure(self, username, allowed, admin, bias): + self.username = username + self.allowed = allowed + self.admin = admin + self.bias = bias + + def is_authenticated(self): + return True + + def is_active(self): + return True + + def is_admin(self): + return self.admin + + def is_anonymous(self): + return False + + def get_id(self): + try: + return unicode(self.id) # python 2 + except NameError: + return str(self.id) # python 3 + + def __repr__(self): + return '%s' % self.username + + +class Location(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(120)) + address = db.Column(db.String(254)) + website = db.Column(db.String(120)) + food = db.relationship('Food', backref='location', lazy='dynamic') + + def configure(self, name, address, website): + self.name = name + self.address = address + self.website = website + + def __repr__(self): + return '%s: %s' % (self.name, self.address) + +class Food(db.Model): + id = db.Column(db.Integer, primary_key=True) + location_id = db.Column(db.Integer, db.ForeignKey('location.id')) + name = db.Column(db.String(120)) + price = db.Column(db.Integer) + + def configure(self, location, name, price): + self.location = location + self.name = name + self.price = price + + def __repr__(self): + return '%s' % self.name + +class Order(db.Model): + id = db.Column(db.Integer, primary_key=True) + courrier_id = db.Column(db.Integer, db.ForeignKey('user.id')) + location_id = db.Column(db.Integer, db.ForeignKey('location.id')) + starttime = db.Column(db.DateTime) + stoptime = db.Column(db.DateTime) + orders = db.relationship('OrdreItem', backref='order', lazy='dynamic') + + + def configure(self, courrier, location, starttime, stoptime): + self.courrier = courrier + self.location = location + self.starttime = starttime + self.stoptime = stoptime + + def __repr__(self): + return 'Order' + +class OrderItem(db.Model): + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) + order_id = db.Column(db.Integer, db.ForeignKey('order.id')) + food_id = db.Column(db.Integer, db.ForeignKey('food.id')) + + def configure(self, user, order, food): + self.user = user + self.order = order + self.food = food + + def __repr__(self): + return 'OrderItem' diff --git a/app/requirements.txt b/app/requirements.txt new file mode 100644 index 0000000..45bc6f9 --- /dev/null +++ b/app/requirements.txt @@ -0,0 +1,16 @@ +Flask==0.10.1 +Flask-Admin==1.0.9 +Flask-Login==0.2.11 +Flask-SQLAlchemy==2.0 +Flask-WTF==0.10.3 +Jinja2==2.7.2 +Logbook==0.8.1 +MarkupSafe==0.23 +SQLAlchemy==0.9.8 +WTForms==2.0 +Werkzeug==0.9.6 +flup==1.0.2 +itsdangerous==0.24 +requests==2.4.0 +Flask-OAuthlib==0.8.0 +oauthlib==0.7.2 diff --git a/app/views.py b/app/views.py new file mode 100644 index 0000000..03a7692 --- /dev/null +++ b/app/views.py @@ -0,0 +1,43 @@ +from flask import Blueprint, request, jsonify, redirect, url_for + + +from app import app +from login import before_request + + +@app.route('/') +def home(): + return render_template('home.html') + + +@app.route('/about/') +def about(): + return render_template('about.html') + + +@app.route('/stats/') +def stats(): + return render_template('stats.html') + + +if app.debug: # add route information + @app.route('/routes') + def list_routes(self): + import urllib + output = [] + for rule in app.url_map.iter_rules(): + options = {} + for arg in rule.arguments: + options[arg] = "[{0}]".format(arg) + + methods = ','.join(rule.methods) + url = url_for(rule.endpoint, **options) + line = urllib.unquote( + "{:50s} {:20s} {}".format(rule.endpoint, methods, url)) + output.append(line) + + string = '' + for line in sorted(output): + string += line + "
" + + return string diff --git a/app/zeus.py b/app/zeus.py new file mode 100644 index 0000000..aed492d --- /dev/null +++ b/app/zeus.py @@ -0,0 +1,93 @@ +from flask import Flask, redirect, url_for, session, request, jsonify, flash, request +from flask.ext.login import LoginManager, login_user, current_user, logout_user +from flask.ext.admin import helpers +from flask_oauthlib.client import OAuth, OAuthException +import json +import requests + + +from app import app, db +from models import User, Token + +oauth = OAuth(app) + +zeus = oauth.remote_app( + 'zeus', + consumer_key=app.config['ZEUS_KEY'], + consumer_secret=app.config['ZEUS_SECRET'], + request_token_params={}, + base_url='http://kelder.zeus.ugent.be/oauth/api/', + access_token_method='POST', + access_token_url='https://kelder.zeus.ugent.be/oauth/oauth2/token/', + authorize_url='https://kelder.zeus.ugent.be/oauth/oauth2/authorize/' +) + + +def zeus_login(): + if app.debug: + return zeus.authorize(callback=url_for('authorized', _external=True)) + else: # temporary solution because it otherwise gives trouble on the pi because of proxies and such + return zeus.authorize(callback='http://zeus.ugent.be/foodbot/login/zeus/authorized') + + +@app.route('/slotmachien/login/zeus/authorized') +def authorized(): + resp = zeus.authorized_response() + if resp is None: + return 'Access denied: reason=%s error=%s' % ( + request.args['error'], + request.args['error_description'] + ) + if isinstance(resp, OAuthException): + return 'Access denied: %s' % resp.message + '
' + str(resp.data) + + session['zeus_token'] = (resp['access_token'], '') + me = zeus.get('current_user/') + username = me.data.get('username', '').lower() + + user = User.query.filter_by(username=username).first() + if len(username) > 0 and user: + return login_and_redirect_user(user) + elif len(username) > 0: + user = create_user(username) + return login_and_redirect_user(user) + + flash("You're not allowed to enter, please contact a system administrator") + return redirect(url_for("admin.index")) + + +@zeus.tokengetter +def get_zeus_oauth_token(): + return session.get('zeus_token') + + +def login_and_redirect_user(user): + login_user(user) + # add_token(resp['access_token'], user) + content_type = request.headers.get('Content-Type', None) + if content_type and content_type in 'application/json': + token = add_token(user) + return jsonify({'token': token.token}) + return redirect(url_for("admin.index")) + + +def add_token(user): + token = Token() + token.configure(user) + db.session.add(token) + db.session.commit() + return token + + +def create_user(username): + user = User() + user.configure(username, False) + db.session.add(user) + db.session.commit() + # EASTER EGG + text = 'Welcome ' + username + '!' + js = json.dumps({'text': text}) + url = app.config['SLACK_WEBHOOK'] + if len(url) > 0: + requests.post(url, data=js) + return user