Merge branch 'master' into user_handling
Conflicts: app/main.py app/requirements.txt
This commit is contained in:
commit
2e0fc3b772
|
@ -4,7 +4,13 @@
|
||||||
`docker build --tag=$(basename $PWD) .`
|
`docker build --tag=$(basename $PWD) .`
|
||||||
|
|
||||||
## run in development
|
## run in development
|
||||||
`docker run --name rollerverbrauch -ti -v `pwd`/app:/app -p 5000:5000 rollerverbrauch`
|
|
||||||
|
Include the development version of the code as volume, so the app gets
|
||||||
|
reloaded automatically. The sqlite file will be stored in *tmp* so it
|
||||||
|
can be inspected with tools like *sqlite3*. The switch *DEBUG* enables
|
||||||
|
debugging during development.
|
||||||
|
|
||||||
|
`docker run --name rollerverbrauch -ti -v `pwd`/app:/app -v /tmp/pitstops/:/data -e DEBUG=True -p 5000:5000 rollerverbrauch`
|
||||||
|
|
||||||
## run in production
|
## run in production
|
||||||
`docker run --name pitstops -d -v /data/pitstops/:/data -p 80:5000 rollerverbrauch`
|
`docker run --name pitstops -d -v /data/pitstops/:/data -p 80:5000 rollerverbrauch`
|
111
app/main.py
111
app/main.py
|
@ -9,15 +9,17 @@ from flask.ext.security import Security, SQLAlchemyUserDatastore, \
|
||||||
UserMixin, RoleMixin, login_required, utils
|
UserMixin, RoleMixin, login_required, utils
|
||||||
import uuid
|
import uuid
|
||||||
import hashlib
|
import hashlib
|
||||||
import time
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from flask_wtf import Form
|
||||||
|
from wtforms import DateField, IntegerField, DecimalField
|
||||||
|
from wtforms.validators import DataRequired, ValidationError
|
||||||
|
import os
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
DATABASE = '/data/rollerverbrauch.db'
|
DATABASE = '/data/rollerverbrauch.db'
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///'+DATABASE
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///'+DATABASE
|
||||||
db = SQLAlchemy(app)
|
db = SQLAlchemy(app)
|
||||||
|
|
||||||
app.config['DEBUG'] = True
|
|
||||||
app.config['SECRET_KEY'] = 'development key'
|
app.config['SECRET_KEY'] = 'development key'
|
||||||
app.config['SECURITY_PASSWORD_HASH'] = 'pbkdf2_sha512'
|
app.config['SECURITY_PASSWORD_HASH'] = 'pbkdf2_sha512'
|
||||||
app.config['SECURITY_PASSWORD_SALT'] = 'xxxxxxxxxxxxxxxxxxxxxx'
|
app.config['SECURITY_PASSWORD_SALT'] = 'xxxxxxxxxxxxxxxxxxxxxx'
|
||||||
|
@ -109,70 +111,52 @@ def index():
|
||||||
return redirect(url_for('get_pit_stops'))
|
return redirect(url_for('get_pit_stops'))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/pitstops', methods=['POST'])
|
def date_check(form, field):
|
||||||
@login_required
|
if field.data < form.pitstop.date:
|
||||||
def create_pit_stop():
|
raise ValidationError('The new date must after %s' % form.pitstop.date)
|
||||||
last_pitstop = Pitstop.query.order_by(Pitstop.date.desc()).first()
|
|
||||||
if last_pitstop is None:
|
|
||||||
last_pitstop = Pitstop(0, 0, None)
|
|
||||||
error_msg = {}
|
|
||||||
|
|
||||||
date_of_pitstop = request.form['date']
|
|
||||||
try:
|
|
||||||
date_of_pitstop = datetime.strptime(date_of_pitstop, '%Y-%m-%d').strftime('%Y-%m-%d')
|
|
||||||
except ValueError:
|
|
||||||
error_msg['date'] = 'invalid date, only YYYY-MM-DD is allowed'
|
|
||||||
date_of_pitstop = request.form['date']
|
|
||||||
|
|
||||||
odometer = request.form['odometer']
|
|
||||||
try:
|
|
||||||
odometer = int(odometer)
|
|
||||||
except ValueError:
|
|
||||||
error_msg['odometer'] = 'Illegal Value, only Integers allowed'
|
|
||||||
odometer = None
|
|
||||||
if odometer is not None and odometer <= last_pitstop.odometer:
|
|
||||||
error_msg['odometer'] = 'Illegal Value, new Value must be bigger as given value'
|
|
||||||
odometer = request.form['odometer']
|
|
||||||
if odometer is None:
|
|
||||||
odometer = request.form['odometer']
|
|
||||||
|
|
||||||
litres = request.form['litres']
|
|
||||||
try:
|
|
||||||
litres = float(litres)
|
|
||||||
except ValueError:
|
|
||||||
error_msg['litres'] = 'Illegal Value, only floating point allowed'
|
|
||||||
litres = None
|
|
||||||
if litres is not None and litres <= 0:
|
|
||||||
error_msg['litres'] = 'Litres must not be 0'
|
|
||||||
litres = request.form['litres']
|
|
||||||
if litres is None:
|
|
||||||
litres = request.form['litres']
|
|
||||||
|
|
||||||
# error checking here
|
|
||||||
if len(error_msg) > 0:
|
|
||||||
data = {'last': {'date': date_of_pitstop, 'odometer': odometer, 'litres': litres}, 'error': error_msg}
|
|
||||||
return render_template('newPitStopForm.html', data=data)
|
|
||||||
|
|
||||||
new_stop = Pitstop(odometer, litres, datetime.strptime(date_of_pitstop, '%Y-%m-%d'))
|
|
||||||
db.session.add(new_stop)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return redirect(url_for('get_pit_stops'))
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/pitstops/createForm', methods=['GET'])
|
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
|
@login_required
|
||||||
def create_pit_stop_form():
|
def create_pit_stop_form():
|
||||||
last_stop = Pitstop.query.order_by(Pitstop.date.desc()).first()
|
last_pitstop = Pitstop.query.order_by(Pitstop.id.desc()).first()
|
||||||
if last_stop is None:
|
if last_pitstop is None:
|
||||||
last_stop = Pitstop(0, 0, date(1970, 1, 1))
|
last_pitstop = Pitstop(0, 0, date.today())
|
||||||
values = dict()
|
|
||||||
values['odometer'] = last_stop.odometer
|
form = CreatePitstopForm()
|
||||||
values['litres'] = last_stop.litres
|
form.set_pitstop(last_pitstop)
|
||||||
values['date'] = time.strftime("%Y-%m-%d")
|
if form.validate_on_submit():
|
||||||
g.data['last'] = values
|
new_stop = Pitstop(form.odometer.data, form.litres.data, form.date.data)
|
||||||
g.data['error'] = None
|
db.session.add(new_stop)
|
||||||
return render_template('newPitStopForm.html', data=g.data)
|
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'])
|
@app.route('/pitstops', methods=['GET'])
|
||||||
|
@ -238,4 +222,5 @@ def prepare_pit_stops(pss):
|
||||||
return pitstops
|
return pitstops
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(debug=True, host='0.0.0.0')
|
DEBUG = 'DEBUG' in os.environ and os.environ['DEBUG'] != 'False'
|
||||||
|
app.run(debug=DEBUG, host='0.0.0.0')
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
Flask
|
Flask
|
||||||
Flask-SQLAlchemy
|
Flask-SQLAlchemy
|
||||||
Flask-Security
|
Flask-Security
|
||||||
|
Flask-WTF
|
||||||
|
|
|
@ -7,6 +7,25 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro render_field_with_errors(field) %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-6 control-label">
|
||||||
|
{{ field.label }}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-2">
|
||||||
|
{{ field(**kwargs)|safe }}
|
||||||
|
{% if field.errors %}
|
||||||
|
<p class='error'>
|
||||||
|
{% for error in field.errors %}
|
||||||
|
{{ error }}
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang=""> <![endif]-->
|
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang=""> <![endif]-->
|
||||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang=""> <![endif]-->
|
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang=""> <![endif]-->
|
||||||
|
@ -69,7 +88,6 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ current_user.email }}
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
{% extends "layout.html" %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
Please authorize yourself!
|
|
||||||
{% endblock %}
|
|
|
@ -1,43 +1,13 @@
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
<form class='form-horizontal' method="POST">
|
||||||
<form class='form-horizontal' id='createPitStop' action="{{ url_for('create_pit_stop') }}" method='post'>
|
{{ form.hidden_tag() }}
|
||||||
|
{{ render_field_with_errors(form.date) }}
|
||||||
<!-- Text input-->
|
{{ render_field_with_errors(form.odometer) }}
|
||||||
<div class="form-group {% if data.error['date'] %}has-error{% endif %}">
|
{{ render_field_with_errors(form.litres) }}
|
||||||
<label class="col-sm-2 control-label" for="date">Date of Pitstop</label>
|
<input type="submit" value="Go">
|
||||||
<div class="col-sm-10">
|
|
||||||
<input class="form-control" id="date" name="date" placeholder="" required="" type="date" value='{{ data.last.date }}' />
|
|
||||||
<p class='error'>{{ data.error['date'] }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Text input-->
|
|
||||||
<div class="form-group {% if data.error['odometer'] %}has-error{% endif %}">
|
|
||||||
<label class="col-sm-2 control-label" for="odometer">Odometer (km)</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input class="form-control" id="odometer" name="odometer" placeholder="" type="number" value='{{ data.last.odometer }}' />
|
|
||||||
<p class='error'>{{ data.error['odometer'] }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Text input-->
|
|
||||||
<div class="form-group {% if data.error['litres'] %}has-error{% endif %}">
|
|
||||||
<label class="col-sm-2 control-label" for="litres">Litres (l)</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input class="form-control" id="litres" name="litres" placeholder="" type="number" step='0.1' value='{{ data.last.litres }}' />
|
|
||||||
<p class='error'>{{ data.error['litres'] }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Button (Double) -->
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="controls">
|
|
||||||
<button id="buttonLogId" name="buttonLogId" class="btn btn-success" onclick="document.getElementById('create_pit_stop').submit();">Log Pitstop</button>
|
|
||||||
<button id="buttonAbortId" name="buttonAbortId" class="btn btn-warning" onclick="window.location.href='{{ url_for('get_pit_stops') }}'" type="button" >Abort</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
{% from "security/_macros.html" import render_field_with_errors, render_field %}
|
{% from "security/_macros.html" import render_field_with_errors, render_field %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% include "security/_messages.html" %}
|
|
||||||
<form class='form-horizontal' action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
|
<form class='form-horizontal' action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
|
||||||
{{ login_user_form.hidden_tag() }}
|
{{ login_user_form.hidden_tag() }}
|
||||||
{{ render_field_with_errors(login_user_form.email) }}
|
{{ render_field_with_errors(login_user_form.email) }}
|
||||||
|
|
Loading…
Reference in New Issue