diff --git a/app/__init__.py b/app/__init__.py deleted file mode 100644 index a811a0d..0000000 --- a/app/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Bind app.db to db -from app.app import db as db diff --git a/app/admin.py b/app/admin.py index a18e048..c2ddb34 100644 --- a/app/admin.py +++ b/app/admin.py @@ -1,14 +1,11 @@ +import flask_login as login from flask_admin import Admin from flask_admin.contrib.sqla import ModelView -import flask_login as login - -from app import app, db -from models import User, Location, Product, Order, OrderItem +from models import Location, Order, OrderItem, Product, User class ModelBaseView(ModelView): - def is_accessible(self): if login.current_user.is_anonymous(): return False @@ -17,12 +14,12 @@ class ModelBaseView(ModelView): class UserAdminModel(ModelBaseView): - column_searchable_list = ('username',) + column_searchable_list = ('username', ) inline_models = None class ProductAdminModel(ModelBaseView): - column_searchable_list = ('name',) + column_searchable_list = ('name', ) inline_models = None @@ -32,11 +29,11 @@ class LocationAdminModel(ModelBaseView): form_columns = ('name', 'address', 'website', 'telephone') -admin = Admin(app, name='Haldis', url='/admin', template_mode='bootstrap3') +def init_admin(app, db): + admin = Admin(app, name='Haldis', url='/admin', template_mode='bootstrap3') - -admin.add_view(UserAdminModel(User, db.session)) -admin.add_view(LocationAdminModel(Location, db.session)) -admin.add_view(ProductAdminModel(Product, db.session)) -admin.add_view(ModelBaseView(Order, db.session)) -admin.add_view(ModelBaseView(OrderItem, db.session)) + admin.add_view(UserAdminModel(User, db.session)) + admin.add_view(LocationAdminModel(Location, db.session)) + admin.add_view(ProductAdminModel(Product, db.session)) + admin.add_view(ModelBaseView(Order, db.session)) + admin.add_view(ModelBaseView(OrderItem, db.session)) diff --git a/app/app.py b/app/app.py index bbeb0af..8844f8e 100644 --- a/app/app.py +++ b/app/app.py @@ -1,48 +1,148 @@ import logging +from datetime import datetime from logging.handlers import TimedRotatingFileHandler -from flask import Flask -from flask_bootstrap import Bootstrap, StaticCDN -from flask_sqlalchemy import SQLAlchemy -from flask_debugtoolbar import DebugToolbarExtension from airbrake import Airbrake, AirbrakeHandler +from flask import Flask, render_template +from flask_bootstrap import Bootstrap, StaticCDN +from flask_debugtoolbar import DebugToolbarExtension +from flask_login import LoginManager +from flask_migrate import Migrate, MigrateCommand +from flask_oauthlib.client import OAuth, OAuthException +from flask_script import Manager + +from admin import init_admin +from login import init_login +from models import db +from models.anonymous_user import AnonymouseUser +from utils import euro_string +from zeus import init_oauth -app = Flask(__name__) -app.config.from_object('config.Configuration') -Bootstrap(app) -app.config['BOOTSTRAP_SERVE_LOCAL'] = True +def create_app(): + app = Flask(__name__) -# use our own bootstrap theme -app.extensions['bootstrap']['cdns']['bootstrap'] = StaticCDN() + # Load the config file + app.config.from_object('config.Configuration') -db = SQLAlchemy(app) + manager = register_plugins(app, debug=app.debug) + add_handlers(app) + add_routes(app) + add_template_filters(app) -toolbar = DebugToolbarExtension(app) + # TODO do we need to return and then run the manager? + return app -if not app.debug: - timedFileHandler = TimedRotatingFileHandler(app.config['LOGFILE'], when='midnight', backupCount=100) - timedFileHandler.setLevel(logging.DEBUG) +def register_plugins(app, debug: bool): + # Register Airbrake and enable the logrotation + if not app.debug: + timedFileHandler = TimedRotatingFileHandler(app.config['LOGFILE'], + when='midnight', + backupCount=100) + timedFileHandler.setLevel(logging.DEBUG) - loglogger = logging.getLogger('werkzeug') - loglogger.setLevel(logging.DEBUG) - loglogger.addHandler(timedFileHandler) - app.logger.addHandler(timedFileHandler) + loglogger = logging.getLogger('werkzeug') + loglogger.setLevel(logging.DEBUG) + loglogger.addHandler(timedFileHandler) + app.logger.addHandler(timedFileHandler) - airbrakelogger = logging.getLogger('airbrake') + airbrakelogger = logging.getLogger('airbrake') - # Airbrake - airbrake = Airbrake( - project_id=app.config['AIRBRAKE_ID'], - api_key=app.config['AIRBRAKE_KEY'] - ) - # ugly hack to make this work for out errbit - airbrake._api_url = "http://errbit.awesomepeople.tv/api/v3/projects/{}/notices".format(airbrake.project_id) + # Airbrake + airbrake = Airbrake(project_id=app.config['AIRBRAKE_ID'], + api_key=app.config['AIRBRAKE_KEY']) + # ugly hack to make this work for out errbit + airbrake._api_url = "http://errbit.awesomepeople.tv/api/v3/projects/{}/notices".format( + airbrake.project_id) - airbrakelogger.addHandler( - AirbrakeHandler(airbrake=airbrake) - ) - app.logger.addHandler( - AirbrakeHandler(airbrake=airbrake) - ) + airbrakelogger.addHandler(AirbrakeHandler(airbrake=airbrake)) + app.logger.addHandler(AirbrakeHandler(airbrake=airbrake)) + + # Initialize SQLAlchemy + db.init_app(app) + + # Initialize Flask-Migrate + migrate = Migrate(app, db) + manager = Manager(app) + manager.add_command('db', MigrateCommand) + + # Add admin interface + init_admin(app, db) + + # Init login manager + login_manager = LoginManager() + login_manager.init_app(app) + login_manager.anonymous_user = AnonymouseUser + init_login(app) + + # Add oauth + zeus = init_oauth(app) + app.zeus = zeus + + # Load the bootstrap local cdn + Bootstrap(app) + app.config['BOOTSTRAP_SERVE_LOCAL'] = True + + # use our own bootstrap theme + app.extensions['bootstrap']['cdns']['bootstrap'] = StaticCDN() + + # Load the flask debug toolbar + toolbar = DebugToolbarExtension(app) + + return manager + + +def add_handlers(app): + @app.errorhandler(404) + def handle404(e): + return render_template('errors/404.html'), 404 + + @app.errorhandler(401) + def handle401(e): + return render_template('errors/401.html'), 401 + + +def add_routes(application): + # import views # TODO convert to blueprint + # import views.stats # TODO convert to blueprint + + from views.order import order_bp + from views.general import general_bp + from views.stats import stats_blueprint + from login import auth_bp + from zeus import oauth_bp + + application.register_blueprint(general_bp, url_prefix='/') + application.register_blueprint(order_bp, url_prefix='/order') + application.register_blueprint(stats_blueprint, url_prefix='/stats') + application.register_blueprint(auth_bp, url_prefix='/') + application.register_blueprint(oauth_bp, url_prefix='/') + + +def add_template_filters(app): + @app.template_filter('countdown') + def countdown(value, only_positive=True, show_text=True): + delta = value - datetime.now() + if delta.total_seconds() < 0 and only_positive: + return "closed" + hours, remainder = divmod(delta.seconds, 3600) + minutes, seconds = divmod(remainder, 60) + time = '%02d:%02d:%02d' % (hours, minutes, seconds) + if show_text: + return 'closes in ' + time + return time + + @app.template_filter('year') + def current_year(value): + return str(datetime.now().year) + + @app.template_filter('euro') + def euro(value): + euro_string(value) + + +# For usage when you directly call the script with python +if __name__ == '__main__': + app = create_app() + app.run(threaded=True, host='0.0.0.0') diff --git a/app/forms.py b/app/forms.py index a10ba66..d326ea0 100644 --- a/app/forms.py +++ b/app/forms.py @@ -1,20 +1,25 @@ from datetime import datetime, timedelta + from flask import session from flask_login import current_user from flask_wtf import FlaskForm as Form -from wtforms import SelectField, DateTimeField, validators, SubmitField, StringField +from wtforms import (DateTimeField, SelectField, StringField, SubmitField, + validators) - -from models import User, Location -from utils import euro +from models import Location, User +from utils import euro_string __author__ = 'feliciaan' class OrderForm(Form): courrier_id = SelectField('Courrier', coerce=int) - location_id = SelectField('Location', coerce=int, validators=[validators.required()]) - starttime = DateTimeField('Starttime', default=datetime.now, format='%d-%m-%Y %H:%M') + location_id = SelectField('Location', + coerce=int, + validators=[validators.required()]) + starttime = DateTimeField('Starttime', + default=datetime.now, + format='%d-%m-%Y %H:%M') stoptime = DateTimeField('Stoptime', format='%d-%m-%Y %H:%M') submit_button = SubmitField('Submit') @@ -23,7 +28,9 @@ class OrderForm(Form): self.courrier_id.choices = [(0, None)] + \ [(u.id, u.username) for u in User.query.order_by('username')] else: - self.courrier_id.choices = [(0, None), (current_user.id, current_user.username)] + self.courrier_id.choices = [(0, None), + (current_user.id, + current_user.username)] self.location_id.choices = [(l.id, l.name) for l in Location.query.order_by('name')] if self.stoptime.data is None: @@ -36,7 +43,9 @@ class OrderItemForm(Form): submit_button = SubmitField('Submit') def populate(self, location): - self.product_id.choices = [(i.id, (i.name + ": " + euro(i.price))) for i in location.products] + self.product_id.choices = [(i.id, + (i.name + ": " + euro_string(i.price))) + for i in location.products] class AnonOrderItemForm(OrderItemForm): diff --git a/app/haldis.py b/app/haldis.py deleted file mode 100644 index 272d516..0000000 --- a/app/haldis.py +++ /dev/null @@ -1,19 +0,0 @@ -from views import * - -from app import app, db - -from admin import admin -from login import login_manager -from models import * -from forms import * -from utils import * -from views import * -from flask_migrate import Migrate, MigrateCommand -from flask_script import Manager - -if __name__ == '__main__': - # do it here, because make accessing db changes only possible when executing the program directly - migrate = Migrate(app, db) - manager = Manager(app) - manager.add_command('db', MigrateCommand) - manager.run() diff --git a/app/login.py b/app/login.py index 0c96825..a14cd2b 100644 --- a/app/login.py +++ b/app/login.py @@ -1,31 +1,30 @@ -from flask import redirect, abort, session, url_for -from flask_login import LoginManager, current_user, logout_user +from flask import abort, Blueprint +from flask import redirect, session, url_for +from flask_login import current_user, logout_user - -from app import app from models import User from zeus import zeus_login -login_manager = LoginManager() -login_manager.init_app(app) +auth_bp = Blueprint('auth_bp', __name__) -@login_manager.user_loader -def load_user(userid): - return User.query.filter_by(id=userid).first() +def init_login(app): + @app.login_manager.user_loader + def load_user(userid): + return User.query.filter_by(id=userid).first() -@app.route('/login') +@auth_bp.route('/login') def login(): return zeus_login() -@app.route('/logout') +@auth_bp.route('/logout') def logout(): if 'zeus_token' in session: session.pop('zeus_token', None) logout_user() - return redirect(url_for('home')) + return redirect(url_for('general_bp.home')) def before_request(): diff --git a/app/models.py b/app/models.py deleted file mode 100644 index 81388df..0000000 --- a/app/models.py +++ /dev/null @@ -1,173 +0,0 @@ -from datetime import datetime -from collections import defaultdict - -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, nullable=False) - admin = db.Column(db.Boolean) - bias = db.Column(db.Integer) - runs = db.relation('Order', backref='courrier', primaryjoin='Order.courrier_id==User.id', - foreign_keys='Order.courrier_id') - orderItems = db.relationship('OrderItem', backref='user', lazy='dynamic') - - def configure(self, username, admin, bias): - self.username = username - 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), nullable=False) - address = db.Column(db.String(254)) - website = db.Column(db.String(120)) - telephone = db.Column(db.String(20), nullable=True) - products = db.relationship('Product', backref='location', lazy='dynamic') - orders = db.relationship('Order', backref='location', lazy='dynamic') - - def configure(self, name, address, telephone, website): - self.name = name - self.address = address - self.website = website - self.telephone = telephone - - def __repr__(self): - return '%s' % (self.name) - - -class Product(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), nullable=False) - price = db.Column(db.Integer, nullable=False) - orderItems = db.relationship('OrderItem', backref='product', lazy='dynamic') - - def configure(self, location, name, price): - self.location = location - self.name = name - self.price = price - - def __repr__(self): - return '%s (€%d)from %s' % (self.name, self.price/100, self.location or 'None') - - -class Order(db.Model): - id = db.Column(db.Integer, primary_key=True) - courrier_id = db.Column(db.Integer, nullable=True) - location_id = db.Column(db.Integer, db.ForeignKey('location.id')) - starttime = db.Column(db.DateTime) - stoptime = db.Column(db.DateTime) - public = db.Column(db.Boolean, default=True) - items = db.relationship('OrderItem', 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): - if self.location: - return 'Order %d @ %s' % (self.id, self.location.name or 'None') - else: - return 'Order %d' % (self.id) - - def group_by_user(self): - group = dict() - for item in self.items: - user = group.get(item.get_name(), dict()) - user["total"] = user.get("total", 0) + item.product.price - user["to_pay"] = user.get("to_pay", 0) + item.product.price if not item.paid else 0 - user["paid"] = user.get("paid", True) and item.paid - user["products"] = user.get("products", []) + [item.product] - group[item.get_name()] = user - - return group - - def group_by_product(self): - group = dict() - for item in self.items: - product = group.get(item.product.name, dict()) - product['count'] = product.get("count", 0) + 1 - if item.extra: - product["extras"] = product.get("extras", []) + [item.extra] - group[item.product.name] = product - - return group - - def can_close(self, user_id): - if self.stoptime and self.stoptime < datetime.now(): - return False - user = None - if user_id: - user = User.query.filter_by(id=user_id).first() - print(user) - if self.courrier_id == user_id or (user and user.is_admin()): - return True - return False - - -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'), nullable=False) - product_id = db.Column(db.Integer, db.ForeignKey('product.id'), - nullable=True) # TODO make false after init migration - paid = db.Column(db.Boolean, default=False, nullable=True) # TODO make false after init migration - extra = db.Column(db.String(254), nullable=True) - name = db.Column(db.String(120)) - - def configure(self, user, order, product): - self.user = user - self.order = order - self.product = product - - def get_name(self): - if self.user_id is not None and self.user_id > 0: - return self.user.username - return self.name - - def __repr__(self): - product_name = None - if self.product: - product_name = self.product.name - return 'Order %d: %s wants %s' % (self.order_id or 0, self.get_name(), product_name or 'None') - - def can_delete(self, order_id, user_id, name): - if int(self.order_id) != int(order_id): - return False - if self.order.stoptime and self.order.stoptime < datetime.now(): - return False - if self.user is not None and self.user_id == user_id: - return True - if user_id is None: - return False - user = User.query.filter(User.id == user_id).first() - if user and user.is_admin(): - return True - return False diff --git a/app/models/__init__.py b/app/models/__init__.py new file mode 100644 index 0000000..60576f2 --- /dev/null +++ b/app/models/__init__.py @@ -0,0 +1,16 @@ +# This file will expose what we want from the models module +# This will probably be everything. But putting the imports here makes it possible to import all models in one line like this: +# +# from models import User, Item, ... +# +# Instead of this: +# from models.user import User +# from models.item import Item +# ... + +from .database import db +from .location import Location +from .order import Order +from .orderitem import OrderItem +from .product import Product +from .user import User diff --git a/app/models/anonymous_user.py b/app/models/anonymous_user.py new file mode 100644 index 0000000..d82abd9 --- /dev/null +++ b/app/models/anonymous_user.py @@ -0,0 +1,17 @@ +class AnonymouseUser: + id = None + + def is_active(self): + return False + + def is_authenticated(self): + return False + + def is_anonymous(self): + return True + + def is_admin(self): + return False + + def get_id(self): + return None diff --git a/app/models/database.py b/app/models/database.py new file mode 100644 index 0000000..f0b13d6 --- /dev/null +++ b/app/models/database.py @@ -0,0 +1,3 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() diff --git a/app/models/location.py b/app/models/location.py new file mode 100644 index 0000000..aedd96f --- /dev/null +++ b/app/models/location.py @@ -0,0 +1,20 @@ +from models import db + + +class Location(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(120), nullable=False) + address = db.Column(db.String(254)) + website = db.Column(db.String(120)) + telephone = db.Column(db.String(20), nullable=True) + products = db.relationship('Product', backref='location', lazy='dynamic') + orders = db.relationship('Order', backref='location', lazy='dynamic') + + def configure(self, name, address, telephone, website): + self.name = name + self.address = address + self.website = website + self.telephone = telephone + + def __repr__(self): + return '%s' % (self.name) diff --git a/app/models/order.py b/app/models/order.py new file mode 100644 index 0000000..d2c4560 --- /dev/null +++ b/app/models/order.py @@ -0,0 +1,61 @@ +from datetime import datetime + +from .database import db +from .user import User + + +class Order(db.Model): + id = db.Column(db.Integer, primary_key=True) + courrier_id = db.Column(db.Integer, nullable=True) + location_id = db.Column(db.Integer, db.ForeignKey('location.id')) + starttime = db.Column(db.DateTime) + stoptime = db.Column(db.DateTime) + public = db.Column(db.Boolean, default=True) + items = db.relationship('OrderItem', 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): + if self.location: + return 'Order %d @ %s' % (self.id, self.location.name or 'None') + else: + return 'Order %d' % (self.id) + + def group_by_user(self): + group = dict() + for item in self.items: + user = group.get(item.get_name(), dict()) + user["total"] = user.get("total", 0) + item.product.price + user["to_pay"] = user.get( + "to_pay", 0) + item.product.price if not item.paid else 0 + user["paid"] = user.get("paid", True) and item.paid + user["products"] = user.get("products", []) + [item.product] + group[item.get_name()] = user + + return group + + def group_by_product(self): + group = dict() + for item in self.items: + product = group.get(item.product.name, dict()) + product['count'] = product.get("count", 0) + 1 + if item.extra: + product["extras"] = product.get("extras", []) + [item.extra] + group[item.product.name] = product + + return group + + def can_close(self, user_id): + if self.stoptime and self.stoptime < datetime.now(): + return False + user = None + if user_id: + user = User.query.filter_by(id=user_id).first() + print(user) + if self.courrier_id == user_id or (user and user.is_admin()): + return True + return False diff --git a/app/models/orderitem.py b/app/models/orderitem.py new file mode 100644 index 0000000..1631c2d --- /dev/null +++ b/app/models/orderitem.py @@ -0,0 +1,48 @@ +from datetime import datetime + +from .database import db +from .user import User + + +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'), nullable=False) + product_id = db.Column( + db.Integer, db.ForeignKey('product.id'), + nullable=True) # TODO make false after init migration + paid = db.Column(db.Boolean, default=False, + nullable=True) # TODO make false after init migration + extra = db.Column(db.String(254), nullable=True) + name = db.Column(db.String(120)) + + def configure(self, user, order, product): + self.user = user + self.order = order + self.product = product + + def get_name(self): + if self.user_id is not None and self.user_id > 0: + return self.user.username + return self.name + + def __repr__(self): + product_name = None + if self.product: + product_name = self.product.name + return 'Order %d: %s wants %s' % (self.order_id or 0, self.get_name(), + product_name or 'None') + + def can_delete(self, order_id, user_id, name): + if int(self.order_id) != int(order_id): + return False + if self.order.stoptime and self.order.stoptime < datetime.now(): + return False + if self.user is not None and self.user_id == user_id: + return True + if user_id is None: + return False + user = User.query.filter(User.id == user_id).first() + if user and user.is_admin(): + return True + return False diff --git a/app/models/product.py b/app/models/product.py new file mode 100644 index 0000000..1ffa122 --- /dev/null +++ b/app/models/product.py @@ -0,0 +1,20 @@ +from models import db + + +class Product(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), nullable=False) + price = db.Column(db.Integer, nullable=False) + orderItems = db.relationship('OrderItem', + backref='product', + lazy='dynamic') + + def configure(self, location, name, price): + self.location = location + self.name = name + self.price = price + + def __repr__(self): + return '%s (€%d)from %s' % (self.name, self.price / 100, self.location + or 'None') diff --git a/app/models/user.py b/app/models/user.py new file mode 100644 index 0000000..fa2c02d --- /dev/null +++ b/app/models/user.py @@ -0,0 +1,39 @@ +from models import db + + +class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True, nullable=False) + admin = db.Column(db.Boolean) + bias = db.Column(db.Integer) + runs = db.relation('Order', + backref='courrier', + primaryjoin='Order.courrier_id==User.id', + foreign_keys='Order.courrier_id') + orderItems = db.relationship('OrderItem', backref='user', lazy='dynamic') + + def configure(self, username, admin, bias): + self.username = username + 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 diff --git a/app/notification.py b/app/notification.py new file mode 100644 index 0000000..3d2b821 --- /dev/null +++ b/app/notification.py @@ -0,0 +1,47 @@ +import json +from datetime import datetime +from threading import Thread + +import requests +from flask import current_app as app +from flask import url_for + + +def post_order_to_webhook(order_item): + message = '' + if order_item.courrier is not None: + message = ' {3} is going to {1}, order <{0}|here>! Deadline in {2} minutes!'.format( + url_for('order_bp.order', id=order_item.id, _external=True), + order_item.location.name, remaining_minutes(order_item.stoptime), + order_item.courrier.username.title()) + else: + message = ' New order for {}. Deadline in {} minutes. <{}|Open here.>'.format( + order_item.location.name, remaining_minutes(order_item.stoptime), + url_for('order_bp.order', id=order_item.id, _external=True)) + webhookthread = WebhookSenderThread(message) + webhookthread.start() + + +class WebhookSenderThread(Thread): + def __init__(self, message): + super(WebhookSenderThread, self).__init__() + self.message = message + + def run(self): + self.slack_webhook() + + def slack_webhook(self): + js = json.dumps({'text': self.message}) + url = app.config['SLACK_WEBHOOK'] + if len(url) > 0: + requests.post(url, data=js) + else: + app.logger.info(str(js)) + + +def remaining_minutes(value): + delta = value - datetime.now() + if delta.total_seconds() < 0: + return "0" + minutes, _ = divmod(delta.total_seconds(), 60) + return "%02d" % minutes diff --git a/app/passenger_wsgi.py b/app/passenger_wsgi.py index e8520f7..611b2e4 100644 --- a/app/passenger_wsgi.py +++ b/app/passenger_wsgi.py @@ -1,7 +1,9 @@ #!/usr/bin/env python -import sys import os +import sys + +from app import create_app INTERP = os.path.expanduser("~/env/bin/python") if sys.executable != INTERP: @@ -9,4 +11,8 @@ if sys.executable != INTERP: sys.path.append(os.getcwd()) -from haldis import app as application +application = create_app() + +# For running on the server with passenger etc +if __name__ == "__main__": + application.run(port=8000) diff --git a/app/templates/errors/401.html b/app/templates/errors/401.html index 59d7815..ba80436 100644 --- a/app/templates/errors/401.html +++ b/app/templates/errors/401.html @@ -4,6 +4,6 @@
{% endblock %} \ No newline at end of file diff --git a/app/templates/errors/404.html b/app/templates/errors/404.html index 534a1e1..76437e2 100644 --- a/app/templates/errors/404.html +++ b/app/templates/errors/404.html @@ -4,6 +4,6 @@ {% endblock %} \ No newline at end of file diff --git a/app/templates/layout.html b/app/templates/layout.html index 15d5212..e2af745 100644 --- a/app/templates/layout.html +++ b/app/templates/layout.html @@ -2,12 +2,12 @@ {% import "bootstrap/utils.html" as utils %} {% set navbar = [ - ('home', 'Home'), + ('general_bp.home', 'Home'), ('order_bp.orders', 'Orders'), - ('locations', 'Locations'), - ('map', 'Map'), - ('about', 'About'), - ('stats', 'Stats'), + ('general_bp.locations', 'Locations'), + ('general_bp.map', 'Map'), + ('general_bp.about', 'About'), + ('stats_blueprint.stats', 'Stats'), ] -%} {% if not current_user.is_anonymous() and current_user.is_admin() -%} {% set navbar = navbar + [('admin.index', 'Admin')] -%} @@ -43,7 +43,7 @@ Haldis - {{ active_page|capitalize }} - HALDIS + HALDIS diff --git a/app/templates/locations.html b/app/templates/locations.html index 7dbc072..b174404 100644 --- a/app/templates/locations.html +++ b/app/templates/locations.html @@ -14,7 +14,7 @@ {% for loc in locations -%}