introduces modules
This commit is contained in:
1 parent
3ec2e4648d
commit
0d8ae0cd18
38 files changed
+251
-251
No files matched your search
@@ -1,3 +1,4 @@
|
||||
.idea/
|
||||
**.DS_Store
|
||||
**.swp
|
||||
**.pyc
|
||||
+1
-232
@@ -1,236 +1,5 @@
|
||||
from datetime import date
|
||||
from flask import Flask
|
||||
from flask import render_template, make_response
|
||||
from flask import request, redirect, g
|
||||
from flask import url_for
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask.ext.security import Security, SQLAlchemyUserDatastore, \
|
||||
UserMixin, RoleMixin, login_required, roles_required, utils
|
||||
from flask.ext.security import user_registered
|
||||
from flask.ext.mail import Mail, Message
|
||||
from flask_security.core import current_user
|
||||
from flask_wtf import Form
|
||||
from wtforms import DateField, IntegerField, DecimalField
|
||||
from wtforms.validators import DataRequired, ValidationError
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
DATABASE = '/data/rollerverbrauch.db'
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///'+DATABASE
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
app.config['SECURITY_PASSWORD_HASH'] = 'pbkdf2_sha512'
|
||||
app.config['SECURITY_REGISTERABLE'] = True
|
||||
app.config['SECURITY_CHANGEABLE'] = True
|
||||
app.config['SECURITY_RECOVERABLE'] = True
|
||||
app.config.from_envvar('config')
|
||||
app.config.from_object(__name__)
|
||||
|
||||
mail = Mail(app)
|
||||
|
||||
roles_users = db.Table('roles_users',
|
||||
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
|
||||
db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))
|
||||
|
||||
|
||||
class Role(db.Model, RoleMixin):
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
name = db.Column(db.String(80), unique=True)
|
||||
description = db.Column(db.String(255))
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
email = db.Column(db.String(255), unique=True)
|
||||
password = db.Column(db.String(255))
|
||||
active = db.Column(db.Boolean())
|
||||
confirmed_at = db.Column(db.DateTime())
|
||||
roles = db.relationship(
|
||||
'Role',
|
||||
secondary=roles_users,
|
||||
backref=db.backref('users', lazy='dynamic')
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return '<User %r>' % self.email
|
||||
|
||||
|
||||
class Pitstop(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
date = db.Column(db.Date)
|
||||
odometer = db.Column(db.Integer)
|
||||
litres = db.Column(db.Numeric(5,2))
|
||||
|
||||
def __init__(self, odometer, litres, date):
|
||||
self.odometer = odometer
|
||||
self.litres = litres
|
||||
self.date = date
|
||||
|
||||
def __repr__(self):
|
||||
return '<Pitstop %r km, %r l>' % (self.odometer, self.litres)
|
||||
|
||||
|
||||
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
|
||||
security = Security(app, user_datastore)
|
||||
|
||||
|
||||
@user_registered.connect_via(app)
|
||||
def user_registered_sighandler(app, user, confirm_token):
|
||||
"""
|
||||
Called after a user was created
|
||||
"""
|
||||
role = user_datastore.find_role('user')
|
||||
user_datastore.add_role_to_user(user, role)
|
||||
|
||||
|
||||
@app.before_first_request
|
||||
def before_first_request():
|
||||
db.create_all()
|
||||
user_datastore.find_or_create_role(name='admin', description='Role for administrators')
|
||||
user_datastore.find_or_create_role(name='user', description='Role for all users.')
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@app.before_request
|
||||
def before_request():
|
||||
g.data = {}
|
||||
|
||||
|
||||
@app.route('/')
|
||||
@login_required
|
||||
def index():
|
||||
return redirect(url_for('get_pit_stops'))
|
||||
|
||||
|
||||
def date_check(form, field):
|
||||
if field.data < form.pitstop.date:
|
||||
raise ValidationError('The new date must after %s' % form.pitstop.date)
|
||||
|
||||
|
||||
def odometer_check(form, field):
|
||||
if field.data <= form.pitstop.odometer:
|
||||
raise ValidationError('The new odometer value must be higher than %i km' % form.pitstop.odometer)
|
||||
|
||||
|
||||
def litres_check(form, field):
|
||||
if field.data is not None and field.data <= 0:
|
||||
raise ValidationError('You must fuel at least 0.1 l')
|
||||
|
||||
|
||||
class CreatePitstopForm(Form):
|
||||
date = DateField('Date of Pitstop', validators=[date_check])
|
||||
odometer = IntegerField('Odometer (km)', validators=[odometer_check])
|
||||
litres = DecimalField('Litres (l)', places=1, validators=[litres_check])
|
||||
pitstop = None
|
||||
|
||||
def set_pitstop(self, pitstop):
|
||||
self.pitstop = pitstop
|
||||
|
||||
|
||||
@app.route('/pitstops/createForm', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def create_pit_stop_form():
|
||||
last_pitstop = Pitstop.query.order_by(Pitstop.id.desc()).first()
|
||||
if last_pitstop is None:
|
||||
last_pitstop = Pitstop(0, 0, date.today())
|
||||
|
||||
form = CreatePitstopForm()
|
||||
form.set_pitstop(last_pitstop)
|
||||
if form.validate_on_submit():
|
||||
new_stop = Pitstop(form.odometer.data, form.litres.data, form.date.data)
|
||||
db.session.add(new_stop)
|
||||
db.session.commit()
|
||||
return redirect(url_for('get_pit_stops'))
|
||||
|
||||
# dynamically set values
|
||||
form.odometer.default = last_pitstop.odometer
|
||||
form.litres.default = last_pitstop.litres
|
||||
form.date.default = date.today()
|
||||
form.process()
|
||||
return render_template('newPitStopForm.html', form=form)
|
||||
|
||||
|
||||
@app.route('/pitstops', methods=['GET'])
|
||||
@login_required
|
||||
def get_pit_stops():
|
||||
data = prepare_pit_stops(Pitstop.query.all())
|
||||
g.data['pitstops'] = data
|
||||
return render_template('pitstops.html', data=g.data)
|
||||
|
||||
|
||||
@app.route('/manual', methods=['GET'])
|
||||
@login_required
|
||||
def get_manual():
|
||||
return render_template('manual.html', data=g.data)
|
||||
|
||||
|
||||
@app.route('/admin', methods=['GET'])
|
||||
@roles_required('admin')
|
||||
def get_admin_page():
|
||||
g.data['users'] = User.query.all()
|
||||
return render_template('admin.html', data=g.data)
|
||||
|
||||
|
||||
@app.route('/account', methods=['GET'])
|
||||
@login_required
|
||||
def get_account_page():
|
||||
print(current_user)
|
||||
return render_template('account.html', data=g.data)
|
||||
|
||||
|
||||
@app.route('/statistics', methods=['GET'])
|
||||
@login_required
|
||||
def get_statistics():
|
||||
pitstops = Pitstop.query.all()
|
||||
count = len(pitstops)
|
||||
distance = 0
|
||||
sum_litres = 0
|
||||
average_distance = 0
|
||||
average_litres_fuelled = 0
|
||||
average_litres_used = 0
|
||||
|
||||
if count > 0:
|
||||
sum_litres = 0
|
||||
for pitstop in pitstops:
|
||||
sum_litres += pitstop.litres
|
||||
average_litres_fuelled = sum_litres/count
|
||||
if count > 1:
|
||||
distance = pitstops[-1].odometer - pitstops[0].odometer
|
||||
average_distance = distance/(count - 1)
|
||||
average_litres_used = 100 * (sum_litres-pitstops[0].litres)/distance
|
||||
g.data['distance'] = distance
|
||||
g.data['count'] = count
|
||||
g.data['litres'] = sum_litres
|
||||
g.data['averageDistance'] = average_distance
|
||||
g.data['averageListresFuelled'] = average_litres_fuelled
|
||||
g.data['averageListresUsed'] = average_litres_used
|
||||
return render_template('statistics.html', data=g.data)
|
||||
|
||||
|
||||
def prepare_pit_stops(pss):
|
||||
pitstops = []
|
||||
for pitstop_index in range(0, len(pss)):
|
||||
p = dict()
|
||||
p['odometer'] = pss[pitstop_index].odometer
|
||||
p['litres'] = pss[pitstop_index].litres
|
||||
p['date'] = pss[pitstop_index].date
|
||||
pitstops.append(p)
|
||||
for pitstop_index in range(1, len(pitstops)):
|
||||
last = pitstops[pitstop_index - 1]
|
||||
curr = pitstops[pitstop_index]
|
||||
curr['distance'] = curr['odometer'] - last['odometer']
|
||||
curr['average'] = 100 * curr['litres']/curr['distance']
|
||||
last_date = last['date']
|
||||
curr_date = curr['date']
|
||||
curr['days'] = (curr_date - last_date).days
|
||||
pitstops.reverse()
|
||||
return pitstops
|
||||
from rollerverbrauch import app
|
||||
|
||||
if __name__ == '__main__':
|
||||
DEBUG = 'DEBUG' in os.environ and os.environ['DEBUG'] != 'False'
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
from datetime import date
|
||||
from flask import Flask
|
||||
from flask import redirect, g
|
||||
from flask import render_template
|
||||
from flask import url_for
|
||||
from flask.ext.mail import Mail
|
||||
from flask.ext.security import Security, SQLAlchemyUserDatastore, \
|
||||
UserMixin, RoleMixin, login_required, roles_required
|
||||
from flask.ext.security import user_registered
|
||||
from flask_security.core import current_user
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////data/rollerverbrauch.db'
|
||||
app.config['SECURITY_PASSWORD_HASH'] = 'pbkdf2_sha512'
|
||||
app.config['SECURITY_REGISTERABLE'] = True
|
||||
app.config['SECURITY_CHANGEABLE'] = True
|
||||
app.config['SECURITY_RECOVERABLE'] = True
|
||||
app.config.from_envvar('config')
|
||||
app.config.from_object(__name__)
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
mail = Mail(app)
|
||||
|
||||
|
||||
from rollerverbrauch.forms import \
|
||||
CreatePitstopForm
|
||||
|
||||
from rollerverbrauch.entities import \
|
||||
User, \
|
||||
Role, \
|
||||
Pitstop
|
||||
|
||||
|
||||
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
|
||||
security = Security(app, user_datastore)
|
||||
|
||||
|
||||
@user_registered.connect_via(app)
|
||||
def user_registered_sighandler(app, user, confirm_token):
|
||||
"""
|
||||
Called after a user was created
|
||||
"""
|
||||
role = user_datastore.find_role('user')
|
||||
user_datastore.add_role_to_user(user, role)
|
||||
|
||||
|
||||
@app.before_first_request
|
||||
def before_first_request():
|
||||
db.create_all()
|
||||
user_datastore.find_or_create_role(name='admin', description='Role for administrators')
|
||||
user_datastore.find_or_create_role(name='user', description='Role for all users.')
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@app.before_request
|
||||
def before_request():
|
||||
g.data = {}
|
||||
|
||||
|
||||
@app.route('/')
|
||||
@login_required
|
||||
def index():
|
||||
return redirect(url_for('get_pit_stops'))
|
||||
|
||||
|
||||
@app.route('/pitstops/createForm', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def create_pit_stop_form():
|
||||
last_pitstop = Pitstop.query.order_by(Pitstop.id.desc()).first()
|
||||
if last_pitstop is None:
|
||||
last_pitstop = Pitstop(0, 0, date.today())
|
||||
|
||||
form = CreatePitstopForm()
|
||||
form.set_pitstop(last_pitstop)
|
||||
if form.validate_on_submit():
|
||||
new_stop = Pitstop(form.odometer.data, form.litres.data, form.date.data)
|
||||
db.session.add(new_stop)
|
||||
db.session.commit()
|
||||
return redirect(url_for('get_pit_stops'))
|
||||
|
||||
# dynamically set values
|
||||
form.odometer.default = last_pitstop.odometer
|
||||
form.litres.default = last_pitstop.litres
|
||||
form.date.default = date.today()
|
||||
form.process()
|
||||
return render_template('newPitStopForm.html', form=form)
|
||||
|
||||
|
||||
@app.route('/pitstops', methods=['GET'])
|
||||
@login_required
|
||||
def get_pit_stops():
|
||||
data = prepare_pit_stops(Pitstop.query.all())
|
||||
g.data['pitstops'] = data
|
||||
return render_template('pitstops.html', data=g.data)
|
||||
|
||||
|
||||
@app.route('/manual', methods=['GET'])
|
||||
@login_required
|
||||
def get_manual():
|
||||
return render_template('manual.html', data=g.data)
|
||||
|
||||
|
||||
@app.route('/admin', methods=['GET'])
|
||||
@roles_required('admin')
|
||||
def get_admin_page():
|
||||
g.data['users'] = User.query.all()
|
||||
return render_template('admin.html', data=g.data)
|
||||
|
||||
|
||||
@app.route('/account', methods=['GET'])
|
||||
@login_required
|
||||
def get_account_page():
|
||||
print(current_user)
|
||||
return render_template('account.html', data=g.data)
|
||||
|
||||
|
||||
@app.route('/statistics', methods=['GET'])
|
||||
@login_required
|
||||
def get_statistics():
|
||||
pitstops = Pitstop.query.all()
|
||||
count = len(pitstops)
|
||||
distance = 0
|
||||
sum_litres = 0
|
||||
average_distance = 0
|
||||
average_litres_fuelled = 0
|
||||
average_litres_used = 0
|
||||
|
||||
if count > 0:
|
||||
sum_litres = 0
|
||||
for pitstop in pitstops:
|
||||
sum_litres += pitstop.litres
|
||||
average_litres_fuelled = sum_litres/count
|
||||
if count > 1:
|
||||
distance = pitstops[-1].odometer - pitstops[0].odometer
|
||||
average_distance = distance/(count - 1)
|
||||
average_litres_used = 100 * (sum_litres-pitstops[0].litres)/distance
|
||||
g.data['distance'] = distance
|
||||
g.data['count'] = count
|
||||
g.data['litres'] = sum_litres
|
||||
g.data['averageDistance'] = average_distance
|
||||
g.data['averageListresFuelled'] = average_litres_fuelled
|
||||
g.data['averageListresUsed'] = average_litres_used
|
||||
return render_template('statistics.html', data=g.data)
|
||||
|
||||
|
||||
def prepare_pit_stops(pss):
|
||||
pitstops = []
|
||||
for pitstop_index in range(0, len(pss)):
|
||||
p = dict()
|
||||
p['odometer'] = pss[pitstop_index].odometer
|
||||
p['litres'] = pss[pitstop_index].litres
|
||||
p['date'] = pss[pitstop_index].date
|
||||
pitstops.append(p)
|
||||
for pitstop_index in range(1, len(pitstops)):
|
||||
last = pitstops[pitstop_index - 1]
|
||||
curr = pitstops[pitstop_index]
|
||||
curr['distance'] = curr['odometer'] - last['odometer']
|
||||
curr['average'] = 100 * curr['litres']/curr['distance']
|
||||
last_date = last['date']
|
||||
curr_date = curr['date']
|
||||
curr['days'] = (curr_date - last_date).days
|
||||
pitstops.reverse()
|
||||
return pitstops
|
||||
|
||||
File renamed without changes.
@@ -0,0 +1,50 @@
|
||||
from rollerverbrauch import db
|
||||
from flask.ext.security import UserMixin, RoleMixin
|
||||
|
||||
roles_users = db.Table('roles_users',
|
||||
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
|
||||
db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))
|
||||
|
||||
|
||||
class Role(db.Model, RoleMixin):
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
name = db.Column(db.String(80), unique=True)
|
||||
description = db.Column(db.String(255))
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
email = db.Column(db.String(255), unique=True)
|
||||
password = db.Column(db.String(255))
|
||||
active = db.Column(db.Boolean())
|
||||
confirmed_at = db.Column(db.DateTime())
|
||||
roles = db.relationship(
|
||||
'Role',
|
||||
secondary=roles_users,
|
||||
backref=db.backref('users', lazy='dynamic')
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return '<User %r>' % self.email
|
||||
|
||||
|
||||
class Pitstop(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
date = db.Column(db.Date)
|
||||
odometer = db.Column(db.Integer)
|
||||
litres = db.Column(db.Numeric(5,2))
|
||||
|
||||
def __init__(self, odometer, litres, date):
|
||||
self.odometer = odometer
|
||||
self.litres = litres
|
||||
self.date = date
|
||||
|
||||
def __repr__(self):
|
||||
return '<Pitstop %r km, %r l>' % (self.odometer, self.litres)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
from flask_wtf import Form
|
||||
from wtforms import DateField, IntegerField, DecimalField
|
||||
from wtforms.validators import ValidationError
|
||||
|
||||
|
||||
def date_check(form, field):
|
||||
if field.data < form.pitstop.date:
|
||||
raise ValidationError('The new date must after %s' % form.pitstop.date)
|
||||
|
||||
|
||||
def odometer_check(form, field):
|
||||
if field.data <= form.pitstop.odometer:
|
||||
raise ValidationError('The new odometer value must be higher than %i km' % form.pitstop.odometer)
|
||||
|
||||
|
||||
def litres_check(form, field):
|
||||
if field.data is not None and field.data <= 0:
|
||||
raise ValidationError('You must fuel at least 0.1 l')
|
||||
|
||||
|
||||
class CreatePitstopForm(Form):
|
||||
date = DateField('Date of Pitstop', validators=[date_check])
|
||||
odometer = IntegerField('Odometer (km)', validators=[odometer_check])
|
||||
litres = DecimalField('Litres (l)', places=1, validators=[litres_check])
|
||||
pitstop = None
|
||||
|
||||
def set_pitstop(self, pitstop):
|
||||
self.pitstop = pitstop
|
||||
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,16 +0,0 @@
|
||||
drop table if exists pitstops;
|
||||
create table pitstops (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`date` TEXT NOT NULL,
|
||||
`odometer` INTEGER NOT NULL,
|
||||
`litres` REAL NOT NULL
|
||||
);
|
||||
|
||||
drop table if exists users;
|
||||
create table `users` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT NOT NULL,
|
||||
`salt` TEXT NOT NULL,
|
||||
`password` TEXT NOT NULL
|
||||
);
|
||||
insert into users (name, salt, password) values ('shing19m', 'pL85Kl2U', '207357fdbf6f379c53bb5ab7fa0bc8c0072ae743973a510f551db7b5c90049b7');
|
||||
+3
-3
@@ -4,14 +4,14 @@ SIZES="57 60 72 76 114 120 144 152 180"
|
||||
|
||||
for SIZE in $SIZES
|
||||
do
|
||||
convert ./icon_orig.png -resize ${SIZE}x${SIZE} app/static/apple-touch-icon-${SIZE}.png
|
||||
convert ./icon_orig.png -resize ${SIZE}x${SIZE} app/rollerverbrauch/static/apple-touch-icon-${SIZE}.png
|
||||
done
|
||||
|
||||
convert ./icon_orig.png -resize 192x192 app/static/android-icon-192x192.png
|
||||
convert ./icon_orig.png -resize 192x192 app/rollerverbrauch/static/android-icon-192x192.png
|
||||
|
||||
SIZES="16 32 96"
|
||||
for SIZE in $SIZES
|
||||
do
|
||||
convert ./icon_orig.png -resize ${SIZE}x${SIZE} app/static/favicon-${SIZE}.png
|
||||
convert ./icon_orig.png -resize ${SIZE}x${SIZE} app/rollerverbrauch/static/favicon-${SIZE}.png
|
||||
done
|
||||
|
||||
Reference in new issue
Block a user