Merge branch 'development' into 'master'
Merge preparation for Cathrine See merge request !13
This commit is contained in:
commit
f82cf425fd
|
@ -11,6 +11,9 @@ a folder that will serve as configuration directory and fill in the
|
||||||
information. The directory will be used as volume during container
|
information. The directory will be used as volume during container
|
||||||
operation.
|
operation.
|
||||||
|
|
||||||
|
## start database
|
||||||
|
`docker run --name pitstops_db -e MYSQL_ROOT_PASSWORD=$SOMESECUREPASSWORD$ -e MYSQL_DATABASE=pitstops -d mysql:latest`
|
||||||
|
|
||||||
## run in development
|
## run in development
|
||||||
|
|
||||||
Include the development version of the code as volume, so the app gets
|
Include the development version of the code as volume, so the app gets
|
||||||
|
@ -22,3 +25,4 @@ debugging during development.
|
||||||
|
|
||||||
## run in production
|
## run in production
|
||||||
`docker run --name pitstops -d -v /data/pitstops/:/data -v /configs/pitstops/:/app/config -p 80:5000 rollerverbrauch`
|
`docker run --name pitstops -d -v /data/pitstops/:/data -v /configs/pitstops/:/app/config -p 80:5000 rollerverbrauch`
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ from flask.ext.security import Security, SQLAlchemyUserDatastore, \
|
||||||
from flask.ext.security import user_registered
|
from flask.ext.security import user_registered
|
||||||
from flask_security.core import current_user
|
from flask_security.core import current_user
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
from flask.ext.security.forms import LoginForm
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config['SECURITY_PASSWORD_HASH'] = 'pbkdf2_sha512'
|
app.config['SECURITY_PASSWORD_HASH'] = 'pbkdf2_sha512'
|
||||||
|
@ -21,17 +22,14 @@ app.config.from_object(__name__)
|
||||||
db = SQLAlchemy(app)
|
db = SQLAlchemy(app)
|
||||||
mail = Mail(app)
|
mail = Mail(app)
|
||||||
|
|
||||||
from rollerverbrauch.tools import \
|
import rollerverbrauch.tools as tools
|
||||||
VehicleStats, \
|
|
||||||
db_log_add, \
|
|
||||||
db_log_delete, \
|
|
||||||
db_log_update
|
|
||||||
|
|
||||||
from rollerverbrauch.forms import \
|
from rollerverbrauch.forms import \
|
||||||
CreatePitstopForm, \
|
CreatePitstopForm, \
|
||||||
EditVehicleForm, \
|
EditVehicleForm, \
|
||||||
DeleteVehicleForm, \
|
DeleteVehicleForm, \
|
||||||
SelectVehicleForm
|
SelectVehicleForm, \
|
||||||
|
DeleteAccountForm
|
||||||
|
|
||||||
from rollerverbrauch.entities import \
|
from rollerverbrauch.entities import \
|
||||||
User, \
|
User, \
|
||||||
|
@ -58,8 +56,8 @@ def user_registered_sighandler(app, user, confirm_token):
|
||||||
db.session.add(new_vehicle)
|
db.session.add(new_vehicle)
|
||||||
user.vehicles.append(new_vehicle)
|
user.vehicles.append(new_vehicle)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
db_log_add(user)
|
tools.db_log_add(user)
|
||||||
db_log_add(new_vehicle)
|
tools.db_log_add(new_vehicle)
|
||||||
|
|
||||||
|
|
||||||
@app.before_first_request
|
@app.before_first_request
|
||||||
|
@ -76,9 +74,28 @@ def before_request():
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
@login_required
|
|
||||||
def index():
|
def index():
|
||||||
|
if current_user.is_authenticated:
|
||||||
return redirect(url_for('get_pit_stops'))
|
return redirect(url_for('get_pit_stops'))
|
||||||
|
else:
|
||||||
|
user_count = len(User.query.all())
|
||||||
|
vehicles = Vehicle.query.all()
|
||||||
|
litres = 0
|
||||||
|
kilometers = 0
|
||||||
|
for vehicle in vehicles:
|
||||||
|
stats = tools.VehicleStats(vehicle)
|
||||||
|
litres += stats.overall_litres
|
||||||
|
kilometers += stats.overall_distance
|
||||||
|
vehicle_count = len(vehicles)
|
||||||
|
pitstop_count = len(Pitstop.query.all())
|
||||||
|
data = {
|
||||||
|
'users':user_count,
|
||||||
|
'vehicles': vehicle_count,
|
||||||
|
'pitstops': pitstop_count,
|
||||||
|
'litres': litres,
|
||||||
|
'kilometers': kilometers
|
||||||
|
}
|
||||||
|
return render_template('index.html', login_user_form=LoginForm(), data=data)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/account/edit_vehicle/<int:vid>', methods=['GET', 'POST'])
|
@app.route('/account/edit_vehicle/<int:vid>', methods=['GET', 'POST'])
|
||||||
|
@ -88,9 +105,11 @@ def edit_vehicle(vid):
|
||||||
form = EditVehicleForm()
|
form = EditVehicleForm()
|
||||||
|
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
|
if not tools.check_vehicle_name_is_unique(current_user, form.name):
|
||||||
|
return render_template('editVehicleForm.html', form=form)
|
||||||
vehicle.name = form.name.data
|
vehicle.name = form.name.data
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
db_log_update(vehicle)
|
tools.db_log_update(vehicle)
|
||||||
return redirect(url_for('get_account_page'))
|
return redirect(url_for('get_account_page'))
|
||||||
|
|
||||||
form.name.default = vehicle.name
|
form.name.default = vehicle.name
|
||||||
|
@ -112,7 +131,7 @@ def delete_vehicle(vid):
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
db.session.delete(vehicle)
|
db.session.delete(vehicle)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
db_log_delete(vehicle)
|
tools.db_log_delete(vehicle)
|
||||||
return redirect(url_for('get_account_page'))
|
return redirect(url_for('get_account_page'))
|
||||||
|
|
||||||
return render_template('deleteVehicleForm.html', form=form, vehicle=vehicle)
|
return render_template('deleteVehicleForm.html', form=form, vehicle=vehicle)
|
||||||
|
@ -124,11 +143,14 @@ def create_vehicle():
|
||||||
form = EditVehicleForm()
|
form = EditVehicleForm()
|
||||||
|
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
new_vehicle = Vehicle(form.name.data)
|
vehicle_name = form.name.data
|
||||||
|
if not tools.check_vehicle_name_is_unique(current_user, form.name):
|
||||||
|
return render_template('createVehicleForm.html', form=form)
|
||||||
|
new_vehicle = Vehicle(vehicle_name)
|
||||||
db.session.add(new_vehicle)
|
db.session.add(new_vehicle)
|
||||||
current_user.vehicles.append(new_vehicle)
|
current_user.vehicles.append(new_vehicle)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
db_log_add(new_vehicle)
|
tools.db_log_add(new_vehicle)
|
||||||
return redirect(url_for('get_account_page'))
|
return redirect(url_for('get_account_page'))
|
||||||
|
|
||||||
return render_template('createVehicleForm.html', form=form)
|
return render_template('createVehicleForm.html', form=form)
|
||||||
|
@ -170,7 +192,7 @@ def create_pit_stop_form(vid):
|
||||||
db.session.add(new_stop)
|
db.session.add(new_stop)
|
||||||
vehicle.pitstops.append(new_stop)
|
vehicle.pitstops.append(new_stop)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
db_log_add(new_stop)
|
tools.db_log_add(new_stop)
|
||||||
return redirect(url_for('get_pit_stops', _anchor= 'v' + str(vehicle.id)))
|
return redirect(url_for('get_pit_stops', _anchor= 'v' + str(vehicle.id)))
|
||||||
|
|
||||||
form.odometer.default = last_pitstop.odometer
|
form.odometer.default = last_pitstop.odometer
|
||||||
|
@ -214,5 +236,18 @@ def get_account_page():
|
||||||
def get_statistics():
|
def get_statistics():
|
||||||
stats = []
|
stats = []
|
||||||
for vehicle in current_user.vehicles:
|
for vehicle in current_user.vehicles:
|
||||||
stats.append(VehicleStats(vehicle))
|
stats.append(tools.VehicleStats(vehicle))
|
||||||
return render_template('statistics.html', data=stats)
|
return render_template('statistics.html', data=stats)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/account/delete', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_account():
|
||||||
|
form = DeleteAccountForm()
|
||||||
|
|
||||||
|
if form.validate_on_submit():
|
||||||
|
user_datastore.delete_user(current_user)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
return render_template('deleteAccountForm.html', form=form)
|
||||||
|
|
|
@ -40,10 +40,11 @@ class User(db.Model, UserMixin):
|
||||||
class Vehicle(db.Model):
|
class Vehicle(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||||||
name = db.Column(db.String(255), unique=True)
|
name = db.Column(db.String(255))
|
||||||
pitstops = db.relationship(
|
pitstops = db.relationship(
|
||||||
'Pitstop'
|
'Pitstop'
|
||||||
)
|
)
|
||||||
|
__table_args__ = (db.UniqueConstraint('owner_id', 'name', name='_owner_name_uniq'),)
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
|
@ -44,3 +44,7 @@ class EditVehicleForm(Form):
|
||||||
|
|
||||||
class DeleteVehicleForm(Form):
|
class DeleteVehicleForm(Form):
|
||||||
submit = SubmitField(label='Do it!')
|
submit = SubmitField(label='Do it!')
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteAccountForm(Form):
|
||||||
|
submit = SubmitField(label='Really delete my account!')
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
body {
|
body {
|
||||||
padding-top: 50px;
|
padding-top: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.starter-template {
|
.starter-template {
|
||||||
padding: 40px 15px;
|
padding-top: 30px;
|
||||||
|
padding-bottom: 60px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +24,55 @@ td {
|
||||||
color: #a94442;
|
color: #a94442;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pitstop {
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-pills > li > a {
|
.nav-pills > li > a {
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-top: 0px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
h3:before{
|
||||||
|
content:"― ";
|
||||||
|
}
|
||||||
|
h3:after{
|
||||||
|
content:" ―";
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content > .active {
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
border-right: 1px solid #ddd;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
padding: 1px;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-body > p, .panel-body > ul {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for small devices
|
||||||
|
@media only screen
|
||||||
|
and (min-device-width : 320px)
|
||||||
|
and (max-device-width : 568px) {
|
||||||
|
h3:before{
|
||||||
|
content:"";
|
||||||
|
}
|
||||||
|
h3:after{
|
||||||
|
content:"";
|
||||||
|
}
|
||||||
|
#charts_tabs {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
#charts_tabs-content {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Password</div>
|
<div class="panel-heading">Password</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<a href='{{ url_for('security.change_password') }}'>
|
<a href='{{ url_for('security.change_password') }}' class="btn btn-primary " role="button">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Change
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Change
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,11 +13,12 @@
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Vehicles</div>
|
<div class="panel-heading">Vehicles</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<a href="{{ url_for('create_vehicle') }}">
|
<a href="{{ url_for('create_vehicle') }}" class="btn btn-primary " role="button">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> create
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> create
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-striped table-bordered">
|
<table class="table table-striped table-bordered">
|
||||||
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
Vehicle
|
Vehicle
|
||||||
|
@ -31,18 +32,18 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% for vehicle in current_user.vehicles %}
|
{% for vehicle in current_user.vehicles %}
|
||||||
<tr>
|
<tr>
|
||||||
<td style="text-align:center">
|
<td>
|
||||||
{{ vehicle.name }}
|
{{ vehicle.name }}
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align:center">
|
<td>
|
||||||
{{ vehicle.pitstops | length }} pitstops
|
{{ vehicle.pitstops | length }} pitstops
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align:center">
|
<td>
|
||||||
<a href="{{ url_for('edit_vehicle', vid=vehicle.id) }}">
|
<a href="{{ url_for('edit_vehicle', vid=vehicle.id) }}" class="btn btn-primary " role="button">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> edit
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> edit
|
||||||
</a>
|
</a>
|
||||||
{% if current_user.vehicles | length > 1 %}
|
{% if current_user.vehicles | length > 1 %}
|
||||||
<a href="{{ url_for('delete_vehicle', vid=vehicle.id) }}">
|
<a href="{{ url_for('delete_vehicle', vid=vehicle.id) }}" class="btn btn-primary btn-warning " role="button">
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> delete
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> delete
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -51,6 +52,15 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">Account</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<a href='{{ url_for('delete_account') }}' class="btn btn-primary " role="button">
|
||||||
|
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Delete
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
<div class="col-md-2" ></div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
<h3>Admin</h3>
|
<h3>Admin</h3>
|
||||||
We have {{ data.users|length }} users so far:
|
We have {{ data.users|length }} users so far:
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -8,5 +12,7 @@
|
||||||
<li>{{user.email}}</li>
|
<li>{{user.email}}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<a href='{{ url_for('security.login', _external=True) }}'>Login</a>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
<div class="col-md-2" ></div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
<h3>Create vehicle</h3>
|
<h3>Create vehicle</h3>
|
||||||
<form class='form-horizontal' method="POST">
|
<form class='form-horizontal' method="POST">
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
{{ render_field_with_errors(form.name) }}
|
{{ render_field_with_errors(form.name) }}
|
||||||
{{ render_field_with_errors(form.submit) }}
|
{{ render_field_with_errors(form.submit) }}
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2" ></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div class="col-md-2" ></div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<h3>Delete account for '{{current_user.email}}'?</h3>
|
||||||
|
This cannot be undone!
|
||||||
|
<form class='form-horizontal' method="POST">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
{{ render_field_with_errors(form.submit) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2" ></div>
|
||||||
|
{% endblock %}
|
|
@ -1,11 +1,18 @@
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<h3>Delete vehicle '{{vehicle.name}}'</h3>
|
<div class="col-md-2" ></div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<h3>Delete vehicle '{{vehicle.name}}'?</h3>
|
||||||
<form class='form-horizontal' method="POST">
|
<form class='form-horizontal' method="POST">
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
{{ render_field_with_errors(form.submit) }}
|
{{ render_field_with_errors(form.submit) }}
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2" ></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
<div class="col-md-2" ></div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
<h3>Edit vehicle</h3>
|
<h3>Edit vehicle</h3>
|
||||||
<form class='form-horizontal' method="POST">
|
<form class='form-horizontal' method="POST">
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
{{ render_field_with_errors(form.name) }}
|
{{ render_field_with_errors(form.name) }}
|
||||||
{{ render_field_with_errors(form.submit) }}
|
{{ render_field_with_errors(form.submit) }}
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "layout.html" %}
|
||||||
|
{% from "security/_macros.html" import render_field_with_errors, render_field %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
{{ render_login_form() }}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body" >
|
||||||
|
<h1>Join the pitstop community!</h1>
|
||||||
|
|
||||||
|
<p>There are already {{ data.users}} members with {{ data.vehicles }} vehicles who have logged {{ data.pitstops }} pitstops fuelling {{ data.litres }}l for {{ data.kilometers }}km.</p>
|
||||||
|
|
||||||
|
<p>With pitstop community you can:</p>
|
||||||
|
<ul>
|
||||||
|
<li>manage multiple vehicles</li>
|
||||||
|
<li>track each pitstop</li>
|
||||||
|
<li>get statistics about the fuel consumption</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p><a href='{{ url_for('security.register') }}'>Register your account now</a> or <a href='{{ url_for('security.login') }}'>log into your account</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -16,14 +16,22 @@
|
||||||
{% macro render_field_with_errors(field) %}
|
{% macro render_field_with_errors(field) %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
{% if field.type == 'SubmitField' %}
|
{% if field.type == 'SubmitField' %}
|
||||||
<div class="col-sm-12" style="align:center">
|
<div class="col-md-4" ></div>
|
||||||
|
|
||||||
|
<div class="col-sm-4" style="align:center">
|
||||||
<input id="{{ field.id }}" name="{{ field.id }}" class="btn btn-default" type="submit" value="{{ field.label.text }}">
|
<input id="{{ field.id }}" name="{{ field.id }}" class="btn btn-default" type="submit" value="{{ field.label.text }}">
|
||||||
</div>
|
</div>
|
||||||
|
<!--
|
||||||
|
<div class="col-sm-3" style="align:center">
|
||||||
|
<a class="btn btn-default" href="{{ g.data['back'] }}" role="button">Cancel</a>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
<div class="col-md-4" ></div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<label class="col-sm-6 control-label">
|
<label class="col-sm-6 control-label">
|
||||||
{{ field.label }}
|
{{ field.label }}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-6">
|
||||||
{% if field.type == 'SelectField' %}
|
{% if field.type == 'SelectField' %}
|
||||||
<select id="{{ field.id }}" name="{{ field.id }}" class="form-control">
|
<select id="{{ field.id }}" name="{{ field.id }}" class="form-control">
|
||||||
{% for choice in field.choices %}
|
{% for choice in field.choices %}
|
||||||
|
@ -54,7 +62,25 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro render_login_form() %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<h3>Login</h3>
|
||||||
|
<form class='form-horizontal' action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
|
||||||
|
{{ login_user_form.hidden_tag() }}
|
||||||
|
{{ render_field_with_errors(login_user_form.email) }}
|
||||||
|
{{ render_field_with_errors(login_user_form.password) }}
|
||||||
|
{{ render_field_with_errors(login_user_form.remember) }}
|
||||||
|
{{ render_field(login_user_form.next) }}
|
||||||
|
{{ render_field_with_errors(login_user_form.submit) }}
|
||||||
|
{% if security.recoverable %}
|
||||||
|
<a href="{{ url_for_security('forgot_password') }}">Forgot password</a>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
@ -134,7 +160,7 @@
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</button>
|
</button>
|
||||||
<a class="navbar-brand" href="{{ url_for('get_pit_stops') }}">refuel journal</a>
|
<a class="navbar-brand" href="{{ url_for('index') }}">refuel journal</a>
|
||||||
</div>
|
</div>
|
||||||
<div id="navbar" class="collapse navbar-collapse">
|
<div id="navbar" class="collapse navbar-collapse">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
|
@ -150,6 +176,14 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{#
|
||||||
|
<nav class="navbar navbar-inverse navbar-fixed-bottom">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-footer">
|
||||||
|
<a class="navbar-brand" href="">Imprint</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
#}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
<div class="col-md-2" ></div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
<h3>New Pitstop for '{{ vehicle.name }}'</h3>
|
<h3>New Pitstop for '{{ vehicle.name }}'</h3>
|
||||||
<form class='form-horizontal' method="POST">
|
<form class='form-horizontal' method="POST">
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
|
@ -15,6 +19,7 @@
|
||||||
{{ render_field_with_errors(form.litres) }}
|
{{ render_field_with_errors(form.litres) }}
|
||||||
{{ render_field_with_errors(form.submit) }}
|
{{ render_field_with_errors(form.submit) }}
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
{% for vehicle in current_user.vehicles %}
|
{% for vehicle in current_user.vehicles %}
|
||||||
<div class="tab-pane {% if loop.first %}active{% endif %}" id="v{{vehicle.id}}">
|
<div class="tab-pane {% if loop.first %}active{% endif %}" id="v{{vehicle.id}}">
|
||||||
<h3>{{vehicle.name}}</h3>
|
<h3>{{vehicle.name}}</h3>
|
||||||
|
{% if vehicle.pitstops %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped table-bordered table-condensed">
|
<table class="table table-striped table-bordered table-condensed">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -68,6 +69,11 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
not enough data: <a href="{{ url_for('create_pit_stop_form', vid=vehicle.id) }}">log a pitstop</a>?
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
{% 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 %}
|
||||||
<h1>Change password</h1>
|
<div class="col-md-2" ></div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<h3>Change password</h3>
|
||||||
<form class='form-horizontal' action="{{ url_for_security('change_password') }}" method="POST" name="change_password_form">
|
<form class='form-horizontal' action="{{ url_for_security('change_password') }}" method="POST" name="change_password_form">
|
||||||
{{ change_password_form.hidden_tag() }}
|
{{ change_password_form.hidden_tag() }}
|
||||||
{{ render_field_with_errors(change_password_form.password) }}
|
{{ render_field_with_errors(change_password_form.password) }}
|
||||||
|
@ -10,4 +14,7 @@
|
||||||
{{ render_field_with_errors(change_password_form.new_password_confirm) }}
|
{{ render_field_with_errors(change_password_form.new_password_confirm) }}
|
||||||
{{ render_field_with_errors(change_password_form.submit) }}
|
{{ render_field_with_errors(change_password_form.submit) }}
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -2,10 +2,18 @@
|
||||||
{% 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 %}
|
||||||
<h1>Send password reset instructions</h1>
|
<div class="col-md-2" ></div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<h3>Reset password</h3>
|
||||||
<form class='form-horizontal' action="{{ url_for_security('forgot_password') }}" method="POST" name="forgot_password_form">
|
<form class='form-horizontal' action="{{ url_for_security('forgot_password') }}" method="POST" name="forgot_password_form">
|
||||||
{{ forgot_password_form.hidden_tag() }}
|
{{ forgot_password_form.hidden_tag() }}
|
||||||
{{ render_field_with_errors(forgot_password_form.email) }}
|
{{ render_field_with_errors(forgot_password_form.email) }}
|
||||||
{{ render_field_with_errors(forgot_password_form.submit) }}
|
{{ render_field_with_errors(forgot_password_form.submit) }}
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2" ></div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -2,16 +2,12 @@
|
||||||
{% 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 %}
|
||||||
<h1>Login</h1>
|
<div class="row">
|
||||||
<form class='form-horizontal' action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
|
<div class="col-md-2" ></div>
|
||||||
{{ login_user_form.hidden_tag() }}
|
<div class="col-md-8">
|
||||||
{{ render_field_with_errors(login_user_form.email) }}
|
{{ render_login_form() }}
|
||||||
{{ render_field_with_errors(login_user_form.password) }}
|
</div>
|
||||||
{{ render_field_with_errors(login_user_form.remember) }}
|
<div class="col-md-2" ></div>
|
||||||
{{ render_field(login_user_form.next) }}
|
</div>
|
||||||
{{ render_field_with_errors(login_user_form.submit) }}
|
|
||||||
{% if security.recoverable %}
|
|
||||||
<a href="{{ url_for_security('forgot_password') }}">Forgot password</a>
|
|
||||||
{% endif %}
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
{% 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 %}
|
||||||
<h1>Register User</h1>
|
<div class="col-md-2" ></div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<h3>Register</h3>
|
||||||
<form class='form-horizontal' action="{{ url_for_security('register') }}" method="POST" name="register_user_form">
|
<form class='form-horizontal' action="{{ url_for_security('register') }}" method="POST" name="register_user_form">
|
||||||
{{ register_user_form.hidden_tag() }}
|
{{ register_user_form.hidden_tag() }}
|
||||||
{{ render_field_with_errors(register_user_form.email) }}
|
{{ render_field_with_errors(register_user_form.email) }}
|
||||||
|
@ -12,4 +16,8 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ render_field_with_errors(register_user_form.submit) }}
|
{{ render_field_with_errors(register_user_form.submit) }}
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2" ></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
<div class="col-md-2" ></div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
<h3>Select Vehicle</h3>
|
<h3>Select Vehicle</h3>
|
||||||
<form class='form-horizontal' method="POST">
|
<form class='form-horizontal' method="POST">
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
{{ render_field_with_errors(form.vehicle) }}
|
{{ render_field_with_errors(form.vehicle) }}
|
||||||
{{ render_field_with_errors(form.submit) }}
|
{{ render_field_with_errors(form.submit) }}
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2" ></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<div id="vehicle_contetn" class="tab-content">
|
<div id="vehicle_content" class="tab-content ">
|
||||||
{% for vehicle in data %}
|
{% for vehicle in data %}
|
||||||
<div class="tab-pane {% if loop.first %}active{% endif %}" id="v{{vehicle.id}}">
|
<div class="tab-pane {% if loop.first %}active{% endif %}" id="v{{vehicle.id}}">
|
||||||
<h3>{{vehicle.name}}</h3>
|
<h3>{{vehicle.name}}</h3>
|
||||||
|
@ -45,6 +45,11 @@
|
||||||
</div>
|
</div>
|
||||||
<ul id="charts_tabs" class="nav nav-tabs" data-tabs="tabs">
|
<ul id="charts_tabs" class="nav nav-tabs" data-tabs="tabs">
|
||||||
<li class="active">
|
<li class="active">
|
||||||
|
<a href="#v{{vehicle.id}}_c3" id="i{{vehicle.id}}_c3" data-toggle="tab">
|
||||||
|
Consumption
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
<a href="#v{{vehicle.id}}_c1" id="i{{vehicle.id}}_c1" data-toggle="tab">
|
<a href="#v{{vehicle.id}}_c1" id="i{{vehicle.id}}_c1" data-toggle="tab">
|
||||||
Fuelled litres
|
Fuelled litres
|
||||||
</a>
|
</a>
|
||||||
|
@ -54,21 +59,30 @@
|
||||||
Odometer
|
Odometer
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<a href="#v{{vehicle.id}}_c3" id="i{{vehicle.id}}_c3" data-toggle="tab">
|
|
||||||
Consumption
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<div id="my-tab-content" class="tab-content">
|
<div id="charts_tabs-content" class="tab-content">
|
||||||
<div class="tab-pane active" id="v{{vehicle.id}}_c1">
|
<div class="tab-pane active" id="v{{vehicle.id}}_c3">
|
||||||
|
{% if vehicle.pitstop_count > 1 %}
|
||||||
|
<div id="averageUsageDiv{{vehicle.id}}" style="width:100%; height:500px;"></div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
{{ chartScript('averageUsageDiv'+vehicle.id|str, vehicle.average_litres, 'l/100 km') }}
|
||||||
|
</script>
|
||||||
|
{% else %}
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
not enough data: <a href="{{ url_for('create_pit_stop_form', vid=vehicle.id) }}">log a pitstop</a>?
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane " id="v{{vehicle.id}}_c1">
|
||||||
{% if vehicle.pitstop_count > 0 %}
|
{% if vehicle.pitstop_count > 0 %}
|
||||||
<div id="fuelledChartDiv{{vehicle.id}}" style="width:100%; height:500px;"></div>
|
<div id="fuelledChartDiv{{vehicle.id}}" style="width:100%; height:500px;"></div>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
{{ chartScript('fuelledChartDiv'+vehicle.id|str, vehicle.litres, 'l') }}
|
{{ chartScript('fuelledChartDiv'+vehicle.id|str, vehicle.litres, 'l') }}
|
||||||
</script>
|
</script>
|
||||||
{% else %}
|
{% else %}
|
||||||
not enough data.
|
<div class="alert alert-warning" role="alert">
|
||||||
|
not enough data: <a href="{{ url_for('create_pit_stop_form', vid=vehicle.id) }}">log a pitstop</a>?
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane " id="v{{vehicle.id}}_c2">
|
<div class="tab-pane " id="v{{vehicle.id}}_c2">
|
||||||
|
@ -78,17 +92,9 @@
|
||||||
{{ chartScript('odometerChartDiv'+vehicle.id|str, vehicle.odometers, 'km') }}
|
{{ chartScript('odometerChartDiv'+vehicle.id|str, vehicle.odometers, 'km') }}
|
||||||
</script>
|
</script>
|
||||||
{% else %}
|
{% else %}
|
||||||
not enough data.
|
<div class="alert alert-warning" role="alert">
|
||||||
{% endif %}
|
not enough data: <a href="{{ url_for('create_pit_stop_form', vid=vehicle.id) }}">log a pitstop</a>?
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane " id="v{{vehicle.id}}_c3">
|
|
||||||
{% if vehicle.pitstop_count > 1 %}
|
|
||||||
<div id="averageUsageDiv{{vehicle.id}}" style="width:100%; height:500px;"></div>
|
|
||||||
<script type="text/javascript">
|
|
||||||
{{ chartScript('averageUsageDiv'+vehicle.id|str, vehicle.average_litres, 'l/100 km') }}
|
|
||||||
</script>
|
|
||||||
{% else %}
|
|
||||||
not enough data.
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -47,3 +47,21 @@ def db_log_delete(entity):
|
||||||
|
|
||||||
def db_log_update(entity):
|
def db_log_update(entity):
|
||||||
logging.info('db_update: %s' % str(entity))
|
logging.info('db_update: %s' % str(entity))
|
||||||
|
|
||||||
|
|
||||||
|
def check_vehicle_name_is_unique(current_user, name_field):
|
||||||
|
"""
|
||||||
|
Checks if the vehicle name given in the name_field is unique for the vehicles of the current user. An error is added
|
||||||
|
to the field it the name is not unique.
|
||||||
|
|
||||||
|
:param current_user: the user currently logged in
|
||||||
|
:param name_field: the form field to enter the name to
|
||||||
|
:return: True if the name is unique, False otherwise.
|
||||||
|
"""
|
||||||
|
vehicle_name = name_field.data
|
||||||
|
for vehicle in current_user.vehicles:
|
||||||
|
if vehicle.name == vehicle_name:
|
||||||
|
name_field.default = vehicle_name
|
||||||
|
name_field.errors.append('Vehicle "%s" already exists.' % vehicle_name)
|
||||||
|
return False
|
||||||
|
return True
|
|
@ -0,0 +1,16 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:%s@database/pitstops' % (os.environ['DATABASE_ENV_MYSQL_ROOT_PASSWORD'])
|
||||||
|
#SQLALCHEMY_DATABASE_URI = 'sqlite:////data/rollerverbrauch.db'
|
||||||
|
|
||||||
|
MAIL_SERVER = ''
|
||||||
|
MAIL_PORT= 25
|
||||||
|
MAIL_USE_TLS = True
|
||||||
|
MAIL_USE_SSL = False
|
||||||
|
MAIL_USERNAME = ''
|
||||||
|
MAIL_PASSWORD = ''
|
||||||
|
SECURITY_EMAIL_SENDER = ''
|
||||||
|
SECURITY_PASSWORD_SALT = 'SecretSalt'
|
||||||
|
SECRET_KEY = 'SecretKey'
|
||||||
|
|
||||||
|
SECURITY_SEND_REGISTER_EMAIL = False
|
|
@ -0,0 +1,20 @@
|
||||||
|
version: '2'
|
||||||
|
services:
|
||||||
|
rollerverbrauch:
|
||||||
|
build: .
|
||||||
|
depends_on:
|
||||||
|
- database
|
||||||
|
volumes:
|
||||||
|
- ./compose_config/:/config
|
||||||
|
environment:
|
||||||
|
- config=/config/config.py
|
||||||
|
- DATABASE_ENV_MYSQL_ROOT_PASSWORD=foobar123
|
||||||
|
ports:
|
||||||
|
- 5000
|
||||||
|
database:
|
||||||
|
image: mysql
|
||||||
|
environment:
|
||||||
|
- MYSQL_ROOT_PASSWORD=foobar123
|
||||||
|
- MYSQL_DATABASE=pitstops
|
||||||
|
ports:
|
||||||
|
- 3306
|
Loading…
Reference in New Issue