From baca41619d5051b056e4a58d22310f6cad58e993 Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Tue, 19 Apr 2016 06:47:10 +0200 Subject: [PATCH 01/10] first iteration of vehicles --- app/main.py | 52 ++++++++++++++++++++++++++++--- app/templates/newPitStopForm.html | 3 +- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/app/main.py b/app/main.py index 4a75aa5..5fc40ac 100644 --- a/app/main.py +++ b/app/main.py @@ -7,11 +7,12 @@ from flask import url_for from flask_sqlalchemy import SQLAlchemy from flask.ext.security import Security, SQLAlchemyUserDatastore, \ UserMixin, RoleMixin, login_required, utils +from flask.ext.security.core import current_user import uuid import hashlib from functools import wraps from flask_wtf import Form -from wtforms import DateField, IntegerField, DecimalField +from wtforms import DateField, IntegerField, DecimalField, SelectField from wtforms.validators import DataRequired, ValidationError import os @@ -27,8 +28,8 @@ app.config.from_object(__name__) 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'))) + 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): @@ -49,6 +50,9 @@ class User(db.Model, UserMixin): password = db.Column(db.String(255)) active = db.Column(db.Boolean()) confirmed_at = db.Column(db.DateTime()) + vehicles = db.relationship( + 'Vehicle' + ) roles = db.relationship( 'Role', secondary=roles_users, @@ -56,7 +60,22 @@ class User(db.Model, UserMixin): ) def __repr__(self): - return '' % self.username + return '' % self.email + + +class Vehicle(db.Model): + id = db.Column(db.Integer, primary_key=True) + owner_id = db.Column(db.Integer, db.ForeignKey('user.id')) + name = db.Column(db.String(255), unique=True) + pitstops = db.relationship( + 'Pitstop' + ) + + def __init__(self, name): + self.name = name + + def __repr__(self): + return '' % self.name class Pitstop(db.Model): @@ -64,6 +83,7 @@ class Pitstop(db.Model): date = db.Column(db.Date) odometer = db.Column(db.Integer) litres = db.Column(db.Numeric(5,2)) + vehicle_id = db.Column(db.Integer, db.ForeignKey('vehicle.id')) def __init__(self, odometer, litres, date): self.odometer = odometer @@ -99,6 +119,23 @@ def before_first_request(): user_datastore.add_role_to_user('admin@example.com', 'admin') db.session.commit() + user = User.query.filter(User.email=='someone@example.com').first() + if len(user.vehicles) == 0: + v = Vehicle('someone') + db.session.add(v) + user.vehicles.append(v) + db.session.commit() + + user = User.query.filter(User.email=='admin@example.com').first() + if len(user.vehicles) == 0: + v = Vehicle('admin_1') + db.session.add(v) + user.vehicles.append(v) + v = Vehicle('admin_2') + db.session.add(v) + user.vehicles.append(v) + db.session.commit() + @app.before_request def before_request(): @@ -127,6 +164,7 @@ def litres_check(form, field): class CreatePitstopForm(Form): + vehicle = SelectField('Vehicle', coerce=int) date = DateField('Date of Pitstop', validators=[date_check]) odometer = IntegerField('Odometer (km)', validators=[odometer_check]) litres = DecimalField('Litres (l)', places=1, validators=[litres_check]) @@ -145,9 +183,15 @@ def create_pit_stop_form(): form = CreatePitstopForm() form.set_pitstop(last_pitstop) + print(current_user.vehicles) + + form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles] if form.validate_on_submit(): + print(form.vehicle.data) + v = Vehicle.query.filter(Vehicle.id==form.vehicle.data).first() new_stop = Pitstop(form.odometer.data, form.litres.data, form.date.data) db.session.add(new_stop) + v.pitstops.append(new_stop) db.session.commit() return redirect(url_for('get_pit_stops')) diff --git a/app/templates/newPitStopForm.html b/app/templates/newPitStopForm.html index c661f10..66e1aca 100644 --- a/app/templates/newPitStopForm.html +++ b/app/templates/newPitStopForm.html @@ -3,7 +3,8 @@ {% block body %}
{{ form.hidden_tag() }} - {{ render_field_with_errors(form.date) }} + {{ render_field_with_errors(form.vehicle) }} + {{ render_field_with_errors(form.date) }} {{ render_field_with_errors(form.odometer) }} {{ render_field_with_errors(form.litres) }} From d19b0a5858f0d5c81eb53c2134150a361191eb86 Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Sat, 23 Apr 2016 11:20:39 +0200 Subject: [PATCH 02/10] adds edit vehicle name --- app/main.py | 115 +++++++++++++++-------------- app/templates/account.html | 9 +++ app/templates/editVehicleForm.html | 11 +++ 3 files changed, 80 insertions(+), 55 deletions(-) create mode 100644 app/templates/editVehicleForm.html diff --git a/app/main.py b/app/main.py index b7de3dd..3db885a 100644 --- a/app/main.py +++ b/app/main.py @@ -14,8 +14,8 @@ 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, SelectField -from wtforms.validators import DataRequired, ValidationError +from wtforms import DateField, IntegerField, StringField, DecimalField, SelectField +from wtforms.validators import DataRequired, ValidationError, Length import os app = Flask(__name__) @@ -99,55 +99,6 @@ class Pitstop(db.Model): return '' % (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() - - user = User.query.filter(User.email=='someone@example.com').first() - if len(user.vehicles) == 0: - v = Vehicle('someone') - db.session.add(v) - user.vehicles.append(v) - db.session.commit() - - user = User.query.filter(User.email=='admin@example.com').first() - if len(user.vehicles) == 0: - v = Vehicle('admin_1') - db.session.add(v) - user.vehicles.append(v) - v = Vehicle('admin_2') - db.session.add(v) - user.vehicles.append(v) - 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) @@ -174,6 +125,62 @@ class CreatePitstopForm(Form): self.pitstop = pitstop +class EditVehicleForm(Form): + name = StringField('Name', validators=[Length(1, 255)]) + + +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) + v = Vehicle('default vehicle') + db.session.add(v) + user.vehicles.append(v) + db.session.commit() + + +@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('/account/edit_vehicle/', methods=['GET', 'POST']) +@login_required +def edit_vehicle(vid): + vehicle = Vehicle.query.filter(Vehicle.id == vid).first() + form = EditVehicleForm() + + if form.validate_on_submit(): + vehicle.name = form.name.data; + db.session.commit() + return redirect(url_for('get_account_page')) + + form.name.default = vehicle.name + form.process() + return render_template('editVehicleForm.html', form=form) + + @app.route('/pitstops/createForm', methods=['GET', 'POST']) @login_required def create_pit_stop_form(): @@ -183,12 +190,9 @@ def create_pit_stop_form(): form = CreatePitstopForm() form.set_pitstop(last_pitstop) - print(current_user.vehicles) - form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles] if form.validate_on_submit(): - print(form.vehicle.data) - v = Vehicle.query.filter(Vehicle.id==form.vehicle.data).first() + v = Vehicle.query.filter(Vehicle.id == form.vehicle.data).first() new_stop = Pitstop(form.odometer.data, form.litres.data, form.date.data) db.session.add(new_stop) v.pitstops.append(new_stop) @@ -196,6 +200,7 @@ def create_pit_stop_form(): return redirect(url_for('get_pit_stops')) # dynamically set values + form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles] form.odometer.default = last_pitstop.odometer form.litres.default = last_pitstop.litres form.date.default = date.today() diff --git a/app/templates/account.html b/app/templates/account.html index de46b10..d111351 100644 --- a/app/templates/account.html +++ b/app/templates/account.html @@ -4,4 +4,13 @@

Account management for {{current_user.email}}

Change password + + + {% for vehicle in current_user.vehicles %} + + + + + {% endfor %} +
{{vehicle.name}}edit
{% endblock %} diff --git a/app/templates/editVehicleForm.html b/app/templates/editVehicleForm.html new file mode 100644 index 0000000..09360ac --- /dev/null +++ b/app/templates/editVehicleForm.html @@ -0,0 +1,11 @@ +{% extends "layout.html" %} + +{% block body %} + + {{ form.hidden_tag() }} + {{ render_field_with_errors(form.name) }} + + + + +{% endblock %} From 267c59cbb35542371ba833642e0c969744a615b3 Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Sat, 23 Apr 2016 17:11:43 +0200 Subject: [PATCH 03/10] adds create and delete vehicle --- app/main.py | 36 ++++++++++++++++++++++++++-- app/templates/account.html | 2 ++ app/templates/createVehicleForm.html | 12 ++++++++++ app/templates/deleteVehicleForm.html | 11 +++++++++ app/templates/editVehicleForm.html | 1 + 5 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 app/templates/createVehicleForm.html create mode 100644 app/templates/deleteVehicleForm.html diff --git a/app/main.py b/app/main.py index 3db885a..8038a9a 100644 --- a/app/main.py +++ b/app/main.py @@ -129,6 +129,10 @@ class EditVehicleForm(Form): name = StringField('Name', validators=[Length(1, 255)]) +class DeleteVehicleForm(Form): + pass + + user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(app, user_datastore) @@ -181,6 +185,35 @@ def edit_vehicle(vid): return render_template('editVehicleForm.html', form=form) +@app.route('/account/delete_vehicle/', methods=['GET', 'POST']) +@login_required +def delete_vehicle(vid): + vehicle = Vehicle.query.filter(Vehicle.id == vid).first() + form = DeleteVehicleForm() + + if form.validate_on_submit(): + db.session.delete(vehicle) + db.session.commit() + return redirect(url_for('get_account_page')) + + return render_template('deleteVehicleForm.html', form=form, vehicle=vehicle) + + +@app.route('/account/create_vehicle', methods=['GET', 'POST']) +@login_required +def create_vehicle(): + form = EditVehicleForm() + + if form.validate_on_submit(): + new_vehicle = Vehicle(form.name.data) + db.session.add(new_vehicle) + current_user.vehicles.append(new_vehicle) + db.session.commit() + return redirect(url_for('get_account_page')) + + return render_template('createVehicleForm.html', form=form) + + @app.route('/pitstops/createForm', methods=['GET', 'POST']) @login_required def create_pit_stop_form(): @@ -190,6 +223,7 @@ def create_pit_stop_form(): form = CreatePitstopForm() form.set_pitstop(last_pitstop) + form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles] if form.validate_on_submit(): v = Vehicle.query.filter(Vehicle.id == form.vehicle.data).first() @@ -199,8 +233,6 @@ def create_pit_stop_form(): db.session.commit() return redirect(url_for('get_pit_stops')) - # dynamically set values - form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles] form.odometer.default = last_pitstop.odometer form.litres.default = last_pitstop.litres form.date.default = date.today() diff --git a/app/templates/account.html b/app/templates/account.html index d111351..0feafd5 100644 --- a/app/templates/account.html +++ b/app/templates/account.html @@ -5,11 +5,13 @@ Change password + create {% for vehicle in current_user.vehicles %} + {% endfor %}
{{vehicle.name}} editdelete
diff --git a/app/templates/createVehicleForm.html b/app/templates/createVehicleForm.html new file mode 100644 index 0000000..97702be --- /dev/null +++ b/app/templates/createVehicleForm.html @@ -0,0 +1,12 @@ +{% extends "layout.html" %} + +{% block body %} +

Create vehicle

+
+ {{ form.hidden_tag() }} + {{ render_field_with_errors(form.name) }} + +
+ + +{% endblock %} diff --git a/app/templates/deleteVehicleForm.html b/app/templates/deleteVehicleForm.html new file mode 100644 index 0000000..c39cd4d --- /dev/null +++ b/app/templates/deleteVehicleForm.html @@ -0,0 +1,11 @@ +{% extends "layout.html" %} + +{% block body %} +

Delete vehicle "{{vehicle.name}}"

+
+ {{ form.hidden_tag() }} + +
+ + +{% endblock %} diff --git a/app/templates/editVehicleForm.html b/app/templates/editVehicleForm.html index 09360ac..fe6b055 100644 --- a/app/templates/editVehicleForm.html +++ b/app/templates/editVehicleForm.html @@ -1,6 +1,7 @@ {% extends "layout.html" %} {% block body %} +

Edit vehicle

{{ form.hidden_tag() }} {{ render_field_with_errors(form.name) }} From 0d8ae0cd18cac228597ba16c04bf6880d64d6a41 Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Sat, 23 Apr 2016 23:08:39 +0200 Subject: [PATCH 04/10] introduces modules --- .gitignore | 1 + app/main.py | 233 +----------------- app/rollerverbrauch/__init__.py | 166 +++++++++++++ .../config/config.py.example | 0 app/rollerverbrauch/entities.py | 50 ++++ app/rollerverbrauch/forms.py | 30 +++ .../static/android-icon-192x192.png | Bin .../static/apple-touch-icon-114.png | Bin .../static/apple-touch-icon-120.png | Bin .../static/apple-touch-icon-144.png | Bin .../static/apple-touch-icon-152.png | Bin .../static/apple-touch-icon-180.png | Bin .../static/apple-touch-icon-57.png | Bin .../static/apple-touch-icon-60.png | Bin .../static/apple-touch-icon-72.png | Bin .../static/apple-touch-icon-76.png | Bin .../static/favicon-16.png | Bin .../static/favicon-32.png | Bin .../static/favicon-96.png | Bin .../static/jquery-1.11.2.min.js | 0 app/{ => rollerverbrauch}/static/main.css | 0 app/{ => rollerverbrauch}/static/main.js | 0 .../modernizr-2.8.3-respond-1.4.2.min.js | 0 .../static/normalize.min.css | 0 .../templates/account.html | 0 .../templates/admin.html | 0 .../templates/layout.html | 0 .../templates/manual.html | 0 .../templates/newPitStopForm.html | 0 .../templates/pitstops.html | 0 .../templates/security/change_password.html | 0 .../templates/security/forgot_password.html | 0 .../templates/security/login_user.html | 0 .../templates/security/register_user.html | 0 .../templates/security/reset_password.html | 0 .../templates/statistics.html | 0 app/schema.sql | 16 -- create_icons.sh | 6 +- 38 files changed, 251 insertions(+), 251 deletions(-) create mode 100644 app/rollerverbrauch/__init__.py rename app/{ => rollerverbrauch}/config/config.py.example (100%) create mode 100644 app/rollerverbrauch/entities.py create mode 100644 app/rollerverbrauch/forms.py rename app/{ => rollerverbrauch}/static/android-icon-192x192.png (100%) rename app/{ => rollerverbrauch}/static/apple-touch-icon-114.png (100%) rename app/{ => rollerverbrauch}/static/apple-touch-icon-120.png (100%) rename app/{ => rollerverbrauch}/static/apple-touch-icon-144.png (100%) rename app/{ => rollerverbrauch}/static/apple-touch-icon-152.png (100%) rename app/{ => rollerverbrauch}/static/apple-touch-icon-180.png (100%) rename app/{ => rollerverbrauch}/static/apple-touch-icon-57.png (100%) rename app/{ => rollerverbrauch}/static/apple-touch-icon-60.png (100%) rename app/{ => rollerverbrauch}/static/apple-touch-icon-72.png (100%) rename app/{ => rollerverbrauch}/static/apple-touch-icon-76.png (100%) rename app/{ => rollerverbrauch}/static/favicon-16.png (100%) rename app/{ => rollerverbrauch}/static/favicon-32.png (100%) rename app/{ => rollerverbrauch}/static/favicon-96.png (100%) rename app/{ => rollerverbrauch}/static/jquery-1.11.2.min.js (100%) rename app/{ => rollerverbrauch}/static/main.css (100%) rename app/{ => rollerverbrauch}/static/main.js (100%) rename app/{ => rollerverbrauch}/static/modernizr-2.8.3-respond-1.4.2.min.js (100%) rename app/{ => rollerverbrauch}/static/normalize.min.css (100%) rename app/{ => rollerverbrauch}/templates/account.html (100%) rename app/{ => rollerverbrauch}/templates/admin.html (100%) rename app/{ => rollerverbrauch}/templates/layout.html (100%) rename app/{ => rollerverbrauch}/templates/manual.html (100%) rename app/{ => rollerverbrauch}/templates/newPitStopForm.html (100%) rename app/{ => rollerverbrauch}/templates/pitstops.html (100%) rename app/{ => rollerverbrauch}/templates/security/change_password.html (100%) rename app/{ => rollerverbrauch}/templates/security/forgot_password.html (100%) rename app/{ => rollerverbrauch}/templates/security/login_user.html (100%) rename app/{ => rollerverbrauch}/templates/security/register_user.html (100%) rename app/{ => rollerverbrauch}/templates/security/reset_password.html (100%) rename app/{ => rollerverbrauch}/templates/statistics.html (100%) delete mode 100644 app/schema.sql diff --git a/.gitignore b/.gitignore index 28ebf13..45c35c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/ **.DS_Store **.swp +**.pyc diff --git a/app/main.py b/app/main.py index 147e7ab..77519c8 100644 --- a/app/main.py +++ b/app/main.py @@ -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 '' % 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 '' % (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' diff --git a/app/rollerverbrauch/__init__.py b/app/rollerverbrauch/__init__.py new file mode 100644 index 0000000..544fbe0 --- /dev/null +++ b/app/rollerverbrauch/__init__.py @@ -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 + diff --git a/app/config/config.py.example b/app/rollerverbrauch/config/config.py.example similarity index 100% rename from app/config/config.py.example rename to app/rollerverbrauch/config/config.py.example diff --git a/app/rollerverbrauch/entities.py b/app/rollerverbrauch/entities.py new file mode 100644 index 0000000..f1dba99 --- /dev/null +++ b/app/rollerverbrauch/entities.py @@ -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 '' % 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 '' % (self.odometer, self.litres) + diff --git a/app/rollerverbrauch/forms.py b/app/rollerverbrauch/forms.py new file mode 100644 index 0000000..1a317be --- /dev/null +++ b/app/rollerverbrauch/forms.py @@ -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 + + diff --git a/app/static/android-icon-192x192.png b/app/rollerverbrauch/static/android-icon-192x192.png similarity index 100% rename from app/static/android-icon-192x192.png rename to app/rollerverbrauch/static/android-icon-192x192.png diff --git a/app/static/apple-touch-icon-114.png b/app/rollerverbrauch/static/apple-touch-icon-114.png similarity index 100% rename from app/static/apple-touch-icon-114.png rename to app/rollerverbrauch/static/apple-touch-icon-114.png diff --git a/app/static/apple-touch-icon-120.png b/app/rollerverbrauch/static/apple-touch-icon-120.png similarity index 100% rename from app/static/apple-touch-icon-120.png rename to app/rollerverbrauch/static/apple-touch-icon-120.png diff --git a/app/static/apple-touch-icon-144.png b/app/rollerverbrauch/static/apple-touch-icon-144.png similarity index 100% rename from app/static/apple-touch-icon-144.png rename to app/rollerverbrauch/static/apple-touch-icon-144.png diff --git a/app/static/apple-touch-icon-152.png b/app/rollerverbrauch/static/apple-touch-icon-152.png similarity index 100% rename from app/static/apple-touch-icon-152.png rename to app/rollerverbrauch/static/apple-touch-icon-152.png diff --git a/app/static/apple-touch-icon-180.png b/app/rollerverbrauch/static/apple-touch-icon-180.png similarity index 100% rename from app/static/apple-touch-icon-180.png rename to app/rollerverbrauch/static/apple-touch-icon-180.png diff --git a/app/static/apple-touch-icon-57.png b/app/rollerverbrauch/static/apple-touch-icon-57.png similarity index 100% rename from app/static/apple-touch-icon-57.png rename to app/rollerverbrauch/static/apple-touch-icon-57.png diff --git a/app/static/apple-touch-icon-60.png b/app/rollerverbrauch/static/apple-touch-icon-60.png similarity index 100% rename from app/static/apple-touch-icon-60.png rename to app/rollerverbrauch/static/apple-touch-icon-60.png diff --git a/app/static/apple-touch-icon-72.png b/app/rollerverbrauch/static/apple-touch-icon-72.png similarity index 100% rename from app/static/apple-touch-icon-72.png rename to app/rollerverbrauch/static/apple-touch-icon-72.png diff --git a/app/static/apple-touch-icon-76.png b/app/rollerverbrauch/static/apple-touch-icon-76.png similarity index 100% rename from app/static/apple-touch-icon-76.png rename to app/rollerverbrauch/static/apple-touch-icon-76.png diff --git a/app/static/favicon-16.png b/app/rollerverbrauch/static/favicon-16.png similarity index 100% rename from app/static/favicon-16.png rename to app/rollerverbrauch/static/favicon-16.png diff --git a/app/static/favicon-32.png b/app/rollerverbrauch/static/favicon-32.png similarity index 100% rename from app/static/favicon-32.png rename to app/rollerverbrauch/static/favicon-32.png diff --git a/app/static/favicon-96.png b/app/rollerverbrauch/static/favicon-96.png similarity index 100% rename from app/static/favicon-96.png rename to app/rollerverbrauch/static/favicon-96.png diff --git a/app/static/jquery-1.11.2.min.js b/app/rollerverbrauch/static/jquery-1.11.2.min.js similarity index 100% rename from app/static/jquery-1.11.2.min.js rename to app/rollerverbrauch/static/jquery-1.11.2.min.js diff --git a/app/static/main.css b/app/rollerverbrauch/static/main.css similarity index 100% rename from app/static/main.css rename to app/rollerverbrauch/static/main.css diff --git a/app/static/main.js b/app/rollerverbrauch/static/main.js similarity index 100% rename from app/static/main.js rename to app/rollerverbrauch/static/main.js diff --git a/app/static/modernizr-2.8.3-respond-1.4.2.min.js b/app/rollerverbrauch/static/modernizr-2.8.3-respond-1.4.2.min.js similarity index 100% rename from app/static/modernizr-2.8.3-respond-1.4.2.min.js rename to app/rollerverbrauch/static/modernizr-2.8.3-respond-1.4.2.min.js diff --git a/app/static/normalize.min.css b/app/rollerverbrauch/static/normalize.min.css similarity index 100% rename from app/static/normalize.min.css rename to app/rollerverbrauch/static/normalize.min.css diff --git a/app/templates/account.html b/app/rollerverbrauch/templates/account.html similarity index 100% rename from app/templates/account.html rename to app/rollerverbrauch/templates/account.html diff --git a/app/templates/admin.html b/app/rollerverbrauch/templates/admin.html similarity index 100% rename from app/templates/admin.html rename to app/rollerverbrauch/templates/admin.html diff --git a/app/templates/layout.html b/app/rollerverbrauch/templates/layout.html similarity index 100% rename from app/templates/layout.html rename to app/rollerverbrauch/templates/layout.html diff --git a/app/templates/manual.html b/app/rollerverbrauch/templates/manual.html similarity index 100% rename from app/templates/manual.html rename to app/rollerverbrauch/templates/manual.html diff --git a/app/templates/newPitStopForm.html b/app/rollerverbrauch/templates/newPitStopForm.html similarity index 100% rename from app/templates/newPitStopForm.html rename to app/rollerverbrauch/templates/newPitStopForm.html diff --git a/app/templates/pitstops.html b/app/rollerverbrauch/templates/pitstops.html similarity index 100% rename from app/templates/pitstops.html rename to app/rollerverbrauch/templates/pitstops.html diff --git a/app/templates/security/change_password.html b/app/rollerverbrauch/templates/security/change_password.html similarity index 100% rename from app/templates/security/change_password.html rename to app/rollerverbrauch/templates/security/change_password.html diff --git a/app/templates/security/forgot_password.html b/app/rollerverbrauch/templates/security/forgot_password.html similarity index 100% rename from app/templates/security/forgot_password.html rename to app/rollerverbrauch/templates/security/forgot_password.html diff --git a/app/templates/security/login_user.html b/app/rollerverbrauch/templates/security/login_user.html similarity index 100% rename from app/templates/security/login_user.html rename to app/rollerverbrauch/templates/security/login_user.html diff --git a/app/templates/security/register_user.html b/app/rollerverbrauch/templates/security/register_user.html similarity index 100% rename from app/templates/security/register_user.html rename to app/rollerverbrauch/templates/security/register_user.html diff --git a/app/templates/security/reset_password.html b/app/rollerverbrauch/templates/security/reset_password.html similarity index 100% rename from app/templates/security/reset_password.html rename to app/rollerverbrauch/templates/security/reset_password.html diff --git a/app/templates/statistics.html b/app/rollerverbrauch/templates/statistics.html similarity index 100% rename from app/templates/statistics.html rename to app/rollerverbrauch/templates/statistics.html diff --git a/app/schema.sql b/app/schema.sql deleted file mode 100644 index c280175..0000000 --- a/app/schema.sql +++ /dev/null @@ -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'); diff --git a/create_icons.sh b/create_icons.sh index ddabb26..af41ca8 100755 --- a/create_icons.sh +++ b/create_icons.sh @@ -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 From c941e1bccf2fef22e4f105e5adecd6fef2883b40 Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Sat, 23 Apr 2016 23:24:25 +0200 Subject: [PATCH 05/10] finished modules merge --- app/rollerverbrauch/__init__.py | 65 ++++++++++++++++++++++++++++++--- app/rollerverbrauch/entities.py | 28 +++++++++++--- app/rollerverbrauch/forms.py | 15 +++++++- 3 files changed, 95 insertions(+), 13 deletions(-) diff --git a/app/rollerverbrauch/__init__.py b/app/rollerverbrauch/__init__.py index 544fbe0..6811f1c 100644 --- a/app/rollerverbrauch/__init__.py +++ b/app/rollerverbrauch/__init__.py @@ -25,12 +25,15 @@ mail = Mail(app) from rollerverbrauch.forms import \ - CreatePitstopForm + CreatePitstopForm, \ + EditVehicleForm, \ + DeleteVehicleForm from rollerverbrauch.entities import \ User, \ Role, \ - Pitstop + Pitstop, \ + Vehicle user_datastore = SQLAlchemyUserDatastore(db, User, Role) @@ -44,6 +47,10 @@ def user_registered_sighandler(app, user, confirm_token): """ role = user_datastore.find_role('user') user_datastore.add_role_to_user(user, role) + v = Vehicle('default vehicle') + db.session.add(v) + user.vehicles.append(v) + db.session.commit() @app.before_first_request @@ -65,6 +72,51 @@ def index(): return redirect(url_for('get_pit_stops')) +@app.route('/account/edit_vehicle/', methods=['GET', 'POST']) +@login_required +def edit_vehicle(vid): + vehicle = Vehicle.query.filter(Vehicle.id == vid).first() + form = EditVehicleForm() + + if form.validate_on_submit(): + vehicle.name = form.name.data; + db.session.commit() + return redirect(url_for('get_account_page')) + + form.name.default = vehicle.name + form.process() + return render_template('editVehicleForm.html', form=form) + + +@app.route('/account/delete_vehicle/', methods=['GET', 'POST']) +@login_required +def delete_vehicle(vid): + vehicle = Vehicle.query.filter(Vehicle.id == vid).first() + form = DeleteVehicleForm() + + if form.validate_on_submit(): + db.session.delete(vehicle) + db.session.commit() + return redirect(url_for('get_account_page')) + + return render_template('deleteVehicleForm.html', form=form, vehicle=vehicle) + + +@app.route('/account/create_vehicle', methods=['GET', 'POST']) +@login_required +def create_vehicle(): + form = EditVehicleForm() + + if form.validate_on_submit(): + new_vehicle = Vehicle(form.name.data) + db.session.add(new_vehicle) + current_user.vehicles.append(new_vehicle) + db.session.commit() + return redirect(url_for('get_account_page')) + + return render_template('createVehicleForm.html', form=form) + + @app.route('/pitstops/createForm', methods=['GET', 'POST']) @login_required def create_pit_stop_form(): @@ -74,13 +126,16 @@ def create_pit_stop_form(): form = CreatePitstopForm() form.set_pitstop(last_pitstop) + form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles] + if form.validate_on_submit(): + v = Vehicle.query.filter(Vehicle.id == form.vehicle.data).first() new_stop = Pitstop(form.odometer.data, form.litres.data, form.date.data) db.session.add(new_stop) + v.pitstops.append(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() @@ -144,7 +199,6 @@ def get_statistics(): 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)): @@ -162,5 +216,4 @@ def prepare_pit_stops(pss): curr_date = curr['date'] curr['days'] = (curr_date - last_date).days pitstops.reverse() - return pitstops - + return pitstops \ No newline at end of file diff --git a/app/rollerverbrauch/entities.py b/app/rollerverbrauch/entities.py index f1dba99..8c40b1c 100644 --- a/app/rollerverbrauch/entities.py +++ b/app/rollerverbrauch/entities.py @@ -2,8 +2,8 @@ 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'))) + 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): @@ -24,6 +24,9 @@ class User(db.Model, UserMixin): password = db.Column(db.String(255)) active = db.Column(db.Boolean()) confirmed_at = db.Column(db.DateTime()) + vehicles = db.relationship( + 'Vehicle' + ) roles = db.relationship( 'Role', secondary=roles_users, @@ -34,11 +37,27 @@ class User(db.Model, UserMixin): return '' % self.email +class Vehicle(db.Model): + id = db.Column(db.Integer, primary_key=True) + owner_id = db.Column(db.Integer, db.ForeignKey('user.id')) + name = db.Column(db.String(255), unique=True) + pitstops = db.relationship( + 'Pitstop' + ) + + def __init__(self, name): + self.name = name + + def __repr__(self): + return '' % self.name + + 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)) + litres = db.Column(db.Numeric(5, 2)) + vehicle_id = db.Column(db.Integer, db.ForeignKey('vehicle.id')) def __init__(self, odometer, litres, date): self.odometer = odometer @@ -46,5 +65,4 @@ class Pitstop(db.Model): self.date = date def __repr__(self): - return '' % (self.odometer, self.litres) - + return '' % (self.odometer, self.litres) \ No newline at end of file diff --git a/app/rollerverbrauch/forms.py b/app/rollerverbrauch/forms.py index 1a317be..a5402b2 100644 --- a/app/rollerverbrauch/forms.py +++ b/app/rollerverbrauch/forms.py @@ -1,6 +1,6 @@ from flask_wtf import Form -from wtforms import DateField, IntegerField, DecimalField -from wtforms.validators import ValidationError +from wtforms import DateField, IntegerField, DecimalField, StringField, SelectField +from wtforms.validators import ValidationError, Length def date_check(form, field): @@ -19,6 +19,7 @@ def litres_check(form, field): class CreatePitstopForm(Form): + vehicle = SelectField('Vehicle', coerce=int) date = DateField('Date of Pitstop', validators=[date_check]) odometer = IntegerField('Odometer (km)', validators=[odometer_check]) litres = DecimalField('Litres (l)', places=1, validators=[litres_check]) @@ -28,3 +29,13 @@ class CreatePitstopForm(Form): self.pitstop = pitstop +class EditVehicleForm(Form): + name = StringField('Name', validators=[Length(1, 255)]) + + +class DeleteVehicleForm(Form): + pass + + + + From 3709eddabbd19c03dbfb81a686d1b5b89b0b85f2 Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Sun, 24 Apr 2016 12:42:07 +0200 Subject: [PATCH 06/10] prevents the deletion of vehicles of other users --- app/rollerverbrauch/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/rollerverbrauch/__init__.py b/app/rollerverbrauch/__init__.py index 6811f1c..e2ddb9f 100644 --- a/app/rollerverbrauch/__init__.py +++ b/app/rollerverbrauch/__init__.py @@ -92,6 +92,11 @@ def edit_vehicle(vid): @login_required def delete_vehicle(vid): vehicle = Vehicle.query.filter(Vehicle.id == vid).first() + + # prevent deletion of foreign vehicles + if not vehicle in current_user.vehicles: + return redirect(url_for('get_account_page')) + form = DeleteVehicleForm() if form.validate_on_submit(): @@ -216,4 +221,4 @@ def prepare_pit_stops(pss): curr_date = curr['date'] curr['days'] = (curr_date - last_date).days pitstops.reverse() - return pitstops \ No newline at end of file + return pitstops From 038e255b565ac195edf34ad87c28f0673151027d Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Sun, 24 Apr 2016 14:09:41 +0200 Subject: [PATCH 07/10] can add pitstops to vehicles nows --- app/rollerverbrauch/__init__.py | 38 +++++++++--- app/rollerverbrauch/forms.py | 9 ++- app/rollerverbrauch/templates/account.html | 62 +++++++++++++++---- app/rollerverbrauch/templates/admin.html | 2 +- .../templates/createVehicleForm.html | 2 +- .../templates/deleteVehicleForm.html | 2 +- .../templates/editVehicleForm.html | 2 +- app/rollerverbrauch/templates/layout.html | 2 +- .../templates/newPitStopForm.html | 2 +- app/rollerverbrauch/templates/pitstops.html | 4 +- .../templates/selectVehice.html | 10 +++ app/rollerverbrauch/templates/statistics.html | 2 +- 12 files changed, 102 insertions(+), 35 deletions(-) create mode 100644 app/rollerverbrauch/templates/selectVehice.html diff --git a/app/rollerverbrauch/__init__.py b/app/rollerverbrauch/__init__.py index e2ddb9f..cc496a8 100644 --- a/app/rollerverbrauch/__init__.py +++ b/app/rollerverbrauch/__init__.py @@ -27,7 +27,8 @@ mail = Mail(app) from rollerverbrauch.forms import \ CreatePitstopForm, \ EditVehicleForm, \ - DeleteVehicleForm + DeleteVehicleForm, \ + SelectVehicleForm from rollerverbrauch.entities import \ User, \ @@ -122,22 +123,41 @@ def create_vehicle(): return render_template('createVehicleForm.html', form=form) -@app.route('/pitstops/createForm', methods=['GET', 'POST']) +@app.route('/pitstops/select_vehicle', 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: +def select_vehicle_for_new_pitstop(): + form = SelectVehicleForm() + form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles] + + if form.validate_on_submit(): + vehicle = Vehicle.query.filter(Vehicle.id == form.vehicle.data).first() + if not vehicle in current_user.vehicles: + return render_template('selectVehice.html', form=form) + + return redirect(url_for('create_pit_stop_form', vid=form.vehicle.data)) + + return render_template('selectVehice.html', form=form) + + +@app.route('/pitstops/create/', methods=['GET', 'POST']) +@login_required +def create_pit_stop_form(vid): + vehicle = Vehicle.query.filter(Vehicle.id == vid).first() + if not vehicle in current_user.vehicles: + return redirect(url_for('select_vehicle_for_new_pitstop')) + + if len(vehicle.pitstops) > 0: + last_pitstop = vehicle.pitstops[-1] + else: last_pitstop = Pitstop(0, 0, date.today()) form = CreatePitstopForm() form.set_pitstop(last_pitstop) - form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles] if form.validate_on_submit(): - v = Vehicle.query.filter(Vehicle.id == form.vehicle.data).first() new_stop = Pitstop(form.odometer.data, form.litres.data, form.date.data) db.session.add(new_stop) - v.pitstops.append(new_stop) + vehicle.pitstops.append(new_stop) db.session.commit() return redirect(url_for('get_pit_stops')) @@ -145,7 +165,7 @@ def create_pit_stop_form(): form.litres.default = last_pitstop.litres form.date.default = date.today() form.process() - return render_template('newPitStopForm.html', form=form) + return render_template('newPitStopForm.html', form=form, vehicle = vehicle) @app.route('/pitstops', methods=['GET']) diff --git a/app/rollerverbrauch/forms.py b/app/rollerverbrauch/forms.py index a5402b2..d8a708f 100644 --- a/app/rollerverbrauch/forms.py +++ b/app/rollerverbrauch/forms.py @@ -18,8 +18,11 @@ def litres_check(form, field): raise ValidationError('You must fuel at least 0.1 l') -class CreatePitstopForm(Form): +class SelectVehicleForm(Form): vehicle = SelectField('Vehicle', coerce=int) + + +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]) @@ -35,7 +38,3 @@ class EditVehicleForm(Form): class DeleteVehicleForm(Form): pass - - - - diff --git a/app/rollerverbrauch/templates/account.html b/app/rollerverbrauch/templates/account.html index 0feafd5..8cec4de 100644 --- a/app/rollerverbrauch/templates/account.html +++ b/app/rollerverbrauch/templates/account.html @@ -1,18 +1,56 @@ {% extends "layout.html" %} {% block body %} -

Account management for {{current_user.email}}

- - Change password - - create - - {% for vehicle in current_user.vehicles %} +

Account management for {{current_user.email}}

+
+
Password
+ +
+
+
Vehicles
+ +
- - - + + + - {% endfor %} -
{{vehicle.name}}editdelete + Vehicle + + Info + + Actions +
+ {% for vehicle in current_user.vehicles %} + + + {{ vehicle.name }} + + + {{ vehicle.pitstops | length }} pitstops + + + + edit + + {% if current_user.vehicles | length > 1 %} + + delete + + {% else %} +   + {% endif %} + + + {% endfor %} + + {% endblock %} diff --git a/app/rollerverbrauch/templates/admin.html b/app/rollerverbrauch/templates/admin.html index 97c2db5..d57f4da 100644 --- a/app/rollerverbrauch/templates/admin.html +++ b/app/rollerverbrauch/templates/admin.html @@ -1,7 +1,7 @@ {% extends "layout.html" %} {% block body %} -

Admin

+

Admin

We have {{ data.users|length }} users so far:
    {% for user in data.users %} diff --git a/app/rollerverbrauch/templates/createVehicleForm.html b/app/rollerverbrauch/templates/createVehicleForm.html index 97702be..d09daa8 100644 --- a/app/rollerverbrauch/templates/createVehicleForm.html +++ b/app/rollerverbrauch/templates/createVehicleForm.html @@ -1,7 +1,7 @@ {% extends "layout.html" %} {% block body %} -

    Create vehicle

    +

    Create vehicle

    {{ form.hidden_tag() }} {{ render_field_with_errors(form.name) }} diff --git a/app/rollerverbrauch/templates/deleteVehicleForm.html b/app/rollerverbrauch/templates/deleteVehicleForm.html index c39cd4d..73678cf 100644 --- a/app/rollerverbrauch/templates/deleteVehicleForm.html +++ b/app/rollerverbrauch/templates/deleteVehicleForm.html @@ -1,7 +1,7 @@ {% extends "layout.html" %} {% block body %} -

    Delete vehicle "{{vehicle.name}}"

    +

    Delete vehicle '{{vehicle.name}}'

    {{ form.hidden_tag() }} diff --git a/app/rollerverbrauch/templates/editVehicleForm.html b/app/rollerverbrauch/templates/editVehicleForm.html index fe6b055..3d7958a 100644 --- a/app/rollerverbrauch/templates/editVehicleForm.html +++ b/app/rollerverbrauch/templates/editVehicleForm.html @@ -1,7 +1,7 @@ {% extends "layout.html" %} {% block body %} -

    Edit vehicle

    +

    Edit vehicle

    {{ form.hidden_tag() }} {{ render_field_with_errors(form.name) }} diff --git a/app/rollerverbrauch/templates/layout.html b/app/rollerverbrauch/templates/layout.html index 29fb991..4f6a810 100644 --- a/app/rollerverbrauch/templates/layout.html +++ b/app/rollerverbrauch/templates/layout.html @@ -1,6 +1,6 @@ {% macro navigation() -%} {% if current_user.email %} -
  • Create Pitstop
  • +
  • Create Pitstop
  • Statistics
  • Account
  • {% if current_user.has_role('admin') %} diff --git a/app/rollerverbrauch/templates/newPitStopForm.html b/app/rollerverbrauch/templates/newPitStopForm.html index 66e1aca..e82133e 100644 --- a/app/rollerverbrauch/templates/newPitStopForm.html +++ b/app/rollerverbrauch/templates/newPitStopForm.html @@ -1,9 +1,9 @@ {% extends "layout.html" %} {% block body %} +

    New Pitstop for '{{ vehicle.name }}'

    {{ form.hidden_tag() }} - {{ render_field_with_errors(form.vehicle) }} {{ render_field_with_errors(form.date) }} {{ render_field_with_errors(form.odometer) }} {{ render_field_with_errors(form.litres) }} diff --git a/app/rollerverbrauch/templates/pitstops.html b/app/rollerverbrauch/templates/pitstops.html index 093b226..a8e43c8 100644 --- a/app/rollerverbrauch/templates/pitstops.html +++ b/app/rollerverbrauch/templates/pitstops.html @@ -17,7 +17,7 @@ Litres
    Average - + {% for pitstop in data['pitstops'] %} @@ -36,4 +36,4 @@ {% endfor %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/app/rollerverbrauch/templates/selectVehice.html b/app/rollerverbrauch/templates/selectVehice.html new file mode 100644 index 0000000..f3677bf --- /dev/null +++ b/app/rollerverbrauch/templates/selectVehice.html @@ -0,0 +1,10 @@ +{% extends "layout.html" %} + +{% block body %} + + {{ form.hidden_tag() }} + {{ render_field_with_errors(form.vehicle) }} + + + +{% endblock %} diff --git a/app/rollerverbrauch/templates/statistics.html b/app/rollerverbrauch/templates/statistics.html index dd75d98..e9be16b 100644 --- a/app/rollerverbrauch/templates/statistics.html +++ b/app/rollerverbrauch/templates/statistics.html @@ -30,4 +30,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} From 34905921d903678362fb99a14d7ba877509f2fe2 Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Mon, 25 Apr 2016 07:23:01 +0200 Subject: [PATCH 08/10] fixes display of last pitstops the display of the last pitstops now is separated by vehicle. --- app/rollerverbrauch/__init__.py | 30 +---- app/rollerverbrauch/templates/pitstops.html | 117 ++++++++++++++------ 2 files changed, 87 insertions(+), 60 deletions(-) diff --git a/app/rollerverbrauch/__init__.py b/app/rollerverbrauch/__init__.py index cc496a8..47ba0e1 100644 --- a/app/rollerverbrauch/__init__.py +++ b/app/rollerverbrauch/__init__.py @@ -95,7 +95,7 @@ def delete_vehicle(vid): vehicle = Vehicle.query.filter(Vehicle.id == vid).first() # prevent deletion of foreign vehicles - if not vehicle in current_user.vehicles: + if vehicle not in current_user.vehicles: return redirect(url_for('get_account_page')) form = DeleteVehicleForm() @@ -131,7 +131,7 @@ def select_vehicle_for_new_pitstop(): if form.validate_on_submit(): vehicle = Vehicle.query.filter(Vehicle.id == form.vehicle.data).first() - if not vehicle in current_user.vehicles: + if vehicle not in current_user.vehicles: return render_template('selectVehice.html', form=form) return redirect(url_for('create_pit_stop_form', vid=form.vehicle.data)) @@ -143,7 +143,7 @@ def select_vehicle_for_new_pitstop(): @login_required def create_pit_stop_form(vid): vehicle = Vehicle.query.filter(Vehicle.id == vid).first() - if not vehicle in current_user.vehicles: + if vehicle not in current_user.vehicles: return redirect(url_for('select_vehicle_for_new_pitstop')) if len(vehicle.pitstops) > 0: @@ -159,7 +159,7 @@ def create_pit_stop_form(vid): db.session.add(new_stop) vehicle.pitstops.append(new_stop) db.session.commit() - return redirect(url_for('get_pit_stops')) + return redirect(url_for('get_pit_stops', _anchor= 'v' + str(vehicle.id))) form.odometer.default = last_pitstop.odometer form.litres.default = last_pitstop.litres @@ -171,9 +171,7 @@ def create_pit_stop_form(vid): @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) + return render_template('pitstops.html', user=current_user) @app.route('/manual', methods=['GET']) @@ -224,21 +222,3 @@ def get_statistics(): 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 diff --git a/app/rollerverbrauch/templates/pitstops.html b/app/rollerverbrauch/templates/pitstops.html index a8e43c8..0088269 100644 --- a/app/rollerverbrauch/templates/pitstops.html +++ b/app/rollerverbrauch/templates/pitstops.html @@ -1,39 +1,86 @@ {% extends "layout.html" %} - {% block body %} +
    + +
    + {% for vehicle in current_user.vehicles %} +
    +

    {{vehicle.name}}

    +
    + + + + + + + {% for pitstop in vehicle.pitstops|reverse %} + {% if not loop.last %} + {% set days = (pitstop.date - vehicle.pitstops[vehicle.pitstops|length - loop.index - 1].date).days %} + {% set distance = pitstop.odometer - vehicle.pitstops[vehicle.pitstops|length - loop.index - 1].odometer %} + {% set average = (pitstop.litres / distance) * 100 %} + + + + + + {% else %} + + + + + + {% endif %} + {% endfor %} +
    + Date
    + Days +
    + Odometer
    + Distance +
    + Litres
    + Average +
    + {{pitstop.date}}
    + {{ days }} days +
    + {{pitstop.odometer}} km
    + {{distance}} km +
    + {{pitstop.litres}} l
    + {{average | round(2)}} l/100km +
    + {{pitstop.date}}
    + -- days +
    + {{pitstop.odometer}} km
    + -- km +
    + {{pitstop.litres}} l
    + -- l/100km +
    +
    +
    + {% endfor %} +
    +
    -
    - - - - - - - {% for pitstop in data['pitstops'] %} - - - - - - {% endfor %} -
    - Date
    - Days -
    - Odometer
    - Distance -
    - Litres
    - Average -
    - {{pitstop.date}}
    - {% if pitstop.days %}{{pitstop.days}}{% else %} --{% endif %} days -
    - {{pitstop.odometer}} km
    - {% if pitstop.distance %}{{pitstop.distance}}{% else %} --{% endif %} km -
    - {{pitstop.litres}} l
    - {% if pitstop.average %}{{pitstop.average | round(2)}}{% else %} --{% endif %} l/100km -
    -
    + + {% endblock %} From ca7166f7310e61459f420dae7bb88935523e6204 Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Mon, 25 Apr 2016 22:28:35 +0200 Subject: [PATCH 09/10] fixed stats --- app/rollerverbrauch/__init__.py | 30 ++----- app/rollerverbrauch/templates/statistics.html | 85 +++++++++++++------ app/rollerverbrauch/tools.py | 18 ++++ 3 files changed, 81 insertions(+), 52 deletions(-) create mode 100644 app/rollerverbrauch/tools.py diff --git a/app/rollerverbrauch/__init__.py b/app/rollerverbrauch/__init__.py index 47ba0e1..8a959cd 100644 --- a/app/rollerverbrauch/__init__.py +++ b/app/rollerverbrauch/__init__.py @@ -23,6 +23,8 @@ app.config.from_object(__name__) db = SQLAlchemy(app) mail = Mail(app) +from rollerverbrauch.tools import \ + VehicleStats from rollerverbrauch.forms import \ CreatePitstopForm, \ @@ -197,28 +199,8 @@ def get_account_page(): @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) + stats = [] + for vehicle in current_user.vehicles: + stats.append(VehicleStats(vehicle)) + return render_template('statistics.html', data=stats) diff --git a/app/rollerverbrauch/templates/statistics.html b/app/rollerverbrauch/templates/statistics.html index e9be16b..9fd7b0f 100644 --- a/app/rollerverbrauch/templates/statistics.html +++ b/app/rollerverbrauch/templates/statistics.html @@ -1,33 +1,62 @@ {% extends "layout.html" %} {% block body %} +
    + +
    + {% for vehicle in data %} +
    +

    {{vehicle.name}}

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Number of Pitstops:{{ vehicle.pitstop_count }}
    Logged Distance:{{ vehicle.overall_distance | round(2) }} km
    Average Distance:{{ vehicle.average_distance | round(2) }} km
    Litres fuelled:{{ vehicle.overall_litres | round(2) }} l
    Average Litres fuelled:{{ vehicle.average_litres_fuelled | round(2) }} l
    Average Litres used:{{ vehicle.average_litres_used | round(2) }} l/100km
    +
    +
    + {% endfor %} +
    +
    + + + -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Number of Pitstops:{{ data.count }}
    Logged Distance:{{ data.distance | round(2) }} km
    Average Distance:{{ data.averageDistance | round(2) }} km
    Litres fuelled:{{ data.litres | round(2) }} l
    Average Litres fuelled:{{ data.averageListresFuelled | round(2) }} l
    Average Litres used:{{ data.averageListresUsed | round(2) }} l/100km
    -
    {% endblock %} diff --git a/app/rollerverbrauch/tools.py b/app/rollerverbrauch/tools.py new file mode 100644 index 0000000..a6281dc --- /dev/null +++ b/app/rollerverbrauch/tools.py @@ -0,0 +1,18 @@ +class VehicleStats: + def __init__(self, vehicle): + self.name = vehicle.name + self.id = vehicle.id + self.pitstop_count = len(vehicle.pitstops) + self.overall_distance = 0 + self.average_distance = 0 + self.overall_litres = 0 + self.average_litres_fuelled = 0 + self.average_litres_used = 0 + if self.pitstop_count > 0: + for pitstop in vehicle.pitstops: + self.overall_litres += pitstop.litres + self.average_litres_fuelled = self.overall_litres / self.pitstop_count + if self.pitstop_count > 1: + self.overall_distance = vehicle.pitstops[-1].odometer - vehicle.pitstops[0].odometer + self.average_distance = self.overall_distance / (self.pitstop_count - 1) + self.average_litres_used = 100 * (self.overall_litres - vehicle.pitstops[0].litres) / self.overall_distance From 3e2d62b28fe386a98501058cd0d3bf1ab97c5d4e Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Wed, 27 Apr 2016 07:23:28 +0200 Subject: [PATCH 10/10] improved design of the form elements --- app/rollerverbrauch/__init__.py | 5 +- app/rollerverbrauch/filters.py | 9 ++++ app/rollerverbrauch/forms.py | 9 ++-- .../templates/createVehicleForm.html | 2 +- .../templates/deleteVehicleForm.html | 2 +- .../templates/editVehicleForm.html | 6 +-- app/rollerverbrauch/templates/layout.html | 53 ++++++++++++++----- .../templates/newPitStopForm.html | 6 +-- .../templates/security/change_password.html | 2 +- .../templates/security/forgot_password.html | 2 +- .../templates/security/login_user.html | 2 +- .../templates/security/register_user.html | 2 +- .../templates/selectVehice.html | 3 +- 13 files changed, 73 insertions(+), 30 deletions(-) create mode 100644 app/rollerverbrauch/filters.py diff --git a/app/rollerverbrauch/__init__.py b/app/rollerverbrauch/__init__.py index 8a959cd..e2eea31 100644 --- a/app/rollerverbrauch/__init__.py +++ b/app/rollerverbrauch/__init__.py @@ -38,6 +38,9 @@ from rollerverbrauch.entities import \ Pitstop, \ Vehicle +# required to activate the filters +import rollerverbrauch.filters + user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(app, user_datastore) @@ -82,7 +85,7 @@ def edit_vehicle(vid): form = EditVehicleForm() if form.validate_on_submit(): - vehicle.name = form.name.data; + vehicle.name = form.name.data db.session.commit() return redirect(url_for('get_account_page')) diff --git a/app/rollerverbrauch/filters.py b/app/rollerverbrauch/filters.py new file mode 100644 index 0000000..00a0e59 --- /dev/null +++ b/app/rollerverbrauch/filters.py @@ -0,0 +1,9 @@ +from rollerverbrauch import app + + +@app.template_filter('none_filter') +def none_filter(value): + if value is None: + return '' + else: + return value diff --git a/app/rollerverbrauch/forms.py b/app/rollerverbrauch/forms.py index d8a708f..2f2b87f 100644 --- a/app/rollerverbrauch/forms.py +++ b/app/rollerverbrauch/forms.py @@ -1,5 +1,5 @@ from flask_wtf import Form -from wtforms import DateField, IntegerField, DecimalField, StringField, SelectField +from wtforms import DateField, IntegerField, DecimalField, StringField, SelectField, SubmitField from wtforms.validators import ValidationError, Length @@ -20,12 +20,14 @@ def litres_check(form, field): class SelectVehicleForm(Form): vehicle = SelectField('Vehicle', coerce=int) + submit = SubmitField(label='Do it!') 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]) + litres = DecimalField('Litres (l)', places=2, validators=[litres_check]) + submit = SubmitField(label='Do it!') pitstop = None def set_pitstop(self, pitstop): @@ -34,7 +36,8 @@ class CreatePitstopForm(Form): class EditVehicleForm(Form): name = StringField('Name', validators=[Length(1, 255)]) + submit = SubmitField(label='Do it!') class DeleteVehicleForm(Form): - pass + submit = SubmitField(label='Do it!') diff --git a/app/rollerverbrauch/templates/createVehicleForm.html b/app/rollerverbrauch/templates/createVehicleForm.html index d09daa8..4ea3b84 100644 --- a/app/rollerverbrauch/templates/createVehicleForm.html +++ b/app/rollerverbrauch/templates/createVehicleForm.html @@ -5,7 +5,7 @@
    {{ form.hidden_tag() }} {{ render_field_with_errors(form.name) }} - + {{ render_field_with_errors(form.submit) }}
    diff --git a/app/rollerverbrauch/templates/deleteVehicleForm.html b/app/rollerverbrauch/templates/deleteVehicleForm.html index 73678cf..5e957c3 100644 --- a/app/rollerverbrauch/templates/deleteVehicleForm.html +++ b/app/rollerverbrauch/templates/deleteVehicleForm.html @@ -4,7 +4,7 @@

    Delete vehicle '{{vehicle.name}}'

    {{ form.hidden_tag() }} - + {{ render_field_with_errors(form.submit) }}
    diff --git a/app/rollerverbrauch/templates/editVehicleForm.html b/app/rollerverbrauch/templates/editVehicleForm.html index 3d7958a..627a24f 100644 --- a/app/rollerverbrauch/templates/editVehicleForm.html +++ b/app/rollerverbrauch/templates/editVehicleForm.html @@ -3,9 +3,9 @@ {% block body %}

    Edit vehicle

    - {{ form.hidden_tag() }} - {{ render_field_with_errors(form.name) }} - + {{ form.hidden_tag() }} + {{ render_field_with_errors(form.name) }} + {{ render_field_with_errors(form.submit) }}
    diff --git a/app/rollerverbrauch/templates/layout.html b/app/rollerverbrauch/templates/layout.html index 4f6a810..e44af45 100644 --- a/app/rollerverbrauch/templates/layout.html +++ b/app/rollerverbrauch/templates/layout.html @@ -15,19 +15,46 @@ {% macro render_field_with_errors(field) %}
    - -
    - {{ field(**kwargs)|safe }} - {% if field.errors %} -

    - {% for error in field.errors %} - {{ error }} - {% endfor %} -

    - {% endif %} -
    + {% if field.type == 'SubmitField' %} +
    + +
    + {% else %} + +
    + {% if field.type == 'SelectField' %} + + {% elif field.type == 'BooleanField' %} + + {% elif field.type == 'StringField' %} + + {% elif field.type == 'PasswordField' %} + + {% elif field.type == 'DateField' %} + + {% elif field.type == 'IntegerField' %} + + {% elif field.type == 'DecimalField' %} + + {% else %} + {{ field(**kwargs)|safe }} + {% endif %} + {% if field.errors %} +

    + {% for error in field.errors %} + {{ error }} + {% endfor %} +

    + {% endif %} +
    + {% endif %} +
    {% endmacro %} diff --git a/app/rollerverbrauch/templates/newPitStopForm.html b/app/rollerverbrauch/templates/newPitStopForm.html index e82133e..849432e 100644 --- a/app/rollerverbrauch/templates/newPitStopForm.html +++ b/app/rollerverbrauch/templates/newPitStopForm.html @@ -3,11 +3,11 @@ {% block body %}

    New Pitstop for '{{ vehicle.name }}'

    - {{ form.hidden_tag() }} - {{ render_field_with_errors(form.date) }} + {{ form.hidden_tag() }} + {{ render_field_with_errors(form.date) }} {{ render_field_with_errors(form.odometer) }} {{ render_field_with_errors(form.litres) }} - + {{ render_field_with_errors(form.submit) }}
    diff --git a/app/rollerverbrauch/templates/security/change_password.html b/app/rollerverbrauch/templates/security/change_password.html index 27f5d62..4cc83e1 100644 --- a/app/rollerverbrauch/templates/security/change_password.html +++ b/app/rollerverbrauch/templates/security/change_password.html @@ -8,6 +8,6 @@ {{ render_field_with_errors(change_password_form.password) }} {{ render_field_with_errors(change_password_form.new_password) }} {{ render_field_with_errors(change_password_form.new_password_confirm) }} - {{ render_field(change_password_form.submit) }} + {{ render_field_with_errors(change_password_form.submit) }} {% endblock %} diff --git a/app/rollerverbrauch/templates/security/forgot_password.html b/app/rollerverbrauch/templates/security/forgot_password.html index 556f254..729b28f 100644 --- a/app/rollerverbrauch/templates/security/forgot_password.html +++ b/app/rollerverbrauch/templates/security/forgot_password.html @@ -6,6 +6,6 @@
    {{ forgot_password_form.hidden_tag() }} {{ render_field_with_errors(forgot_password_form.email) }} - {{ render_field(forgot_password_form.submit) }} + {{ render_field_with_errors(forgot_password_form.submit) }}
    {% endblock %} diff --git a/app/rollerverbrauch/templates/security/login_user.html b/app/rollerverbrauch/templates/security/login_user.html index 9bc86c2..081f13f 100644 --- a/app/rollerverbrauch/templates/security/login_user.html +++ b/app/rollerverbrauch/templates/security/login_user.html @@ -9,7 +9,7 @@ {{ render_field_with_errors(login_user_form.password) }} {{ render_field_with_errors(login_user_form.remember) }} {{ render_field(login_user_form.next) }} - {{ render_field(login_user_form.submit) }} + {{ render_field_with_errors(login_user_form.submit) }} {% if security.recoverable %} Forgot password {% endif %} diff --git a/app/rollerverbrauch/templates/security/register_user.html b/app/rollerverbrauch/templates/security/register_user.html index 9cad77d..b269de4 100644 --- a/app/rollerverbrauch/templates/security/register_user.html +++ b/app/rollerverbrauch/templates/security/register_user.html @@ -10,6 +10,6 @@ {% if register_user_form.password_confirm %} {{ render_field_with_errors(register_user_form.password_confirm) }} {% endif %} - {{ render_field(register_user_form.submit) }} + {{ render_field_with_errors(register_user_form.submit) }} {% endblock %} diff --git a/app/rollerverbrauch/templates/selectVehice.html b/app/rollerverbrauch/templates/selectVehice.html index f3677bf..f46075d 100644 --- a/app/rollerverbrauch/templates/selectVehice.html +++ b/app/rollerverbrauch/templates/selectVehice.html @@ -1,10 +1,11 @@ {% extends "layout.html" %} {% block body %} +

    Select Vehicle

    {{ form.hidden_tag() }} {{ render_field_with_errors(form.vehicle) }} - + {{ render_field_with_errors(form.submit) }}
    {% endblock %}