Merge branch 'development' into 'master'

Merge preparation for Cathrine



See merge request !13
This commit is contained in:
Joachim Lusiardi 2016-05-28 20:47:26 +02:00
commit f82cf425fd
24 changed files with 527 additions and 222 deletions

View File

@ -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
operation.
## start database
`docker run --name pitstops_db -e MYSQL_ROOT_PASSWORD=$SOMESECUREPASSWORD$ -e MYSQL_DATABASE=pitstops -d mysql:latest`
## run in development
Include the development version of the code as volume, so the app gets
@ -22,3 +25,4 @@ debugging during development.
## run in production
`docker run --name pitstops -d -v /data/pitstops/:/data -v /configs/pitstops/:/app/config -p 80:5000 rollerverbrauch`

View File

@ -9,6 +9,7 @@ from flask.ext.security import Security, SQLAlchemyUserDatastore, \
from flask.ext.security import user_registered
from flask_security.core import current_user
from flask_sqlalchemy import SQLAlchemy
from flask.ext.security.forms import LoginForm
app = Flask(__name__)
app.config['SECURITY_PASSWORD_HASH'] = 'pbkdf2_sha512'
@ -21,17 +22,14 @@ app.config.from_object(__name__)
db = SQLAlchemy(app)
mail = Mail(app)
from rollerverbrauch.tools import \
VehicleStats, \
db_log_add, \
db_log_delete, \
db_log_update
import rollerverbrauch.tools as tools
from rollerverbrauch.forms import \
CreatePitstopForm, \
EditVehicleForm, \
DeleteVehicleForm, \
SelectVehicleForm
SelectVehicleForm, \
DeleteAccountForm
from rollerverbrauch.entities import \
User, \
@ -58,8 +56,8 @@ def user_registered_sighandler(app, user, confirm_token):
db.session.add(new_vehicle)
user.vehicles.append(new_vehicle)
db.session.commit()
db_log_add(user)
db_log_add(new_vehicle)
tools.db_log_add(user)
tools.db_log_add(new_vehicle)
@app.before_first_request
@ -76,9 +74,28 @@ def before_request():
@app.route('/')
@login_required
def index():
if current_user.is_authenticated:
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'])
@ -88,9 +105,11 @@ def edit_vehicle(vid):
form = EditVehicleForm()
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
db.session.commit()
db_log_update(vehicle)
tools.db_log_update(vehicle)
return redirect(url_for('get_account_page'))
form.name.default = vehicle.name
@ -112,7 +131,7 @@ def delete_vehicle(vid):
if form.validate_on_submit():
db.session.delete(vehicle)
db.session.commit()
db_log_delete(vehicle)
tools.db_log_delete(vehicle)
return redirect(url_for('get_account_page'))
return render_template('deleteVehicleForm.html', form=form, vehicle=vehicle)
@ -124,11 +143,14 @@ def create_vehicle():
form = EditVehicleForm()
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)
current_user.vehicles.append(new_vehicle)
db.session.commit()
db_log_add(new_vehicle)
tools.db_log_add(new_vehicle)
return redirect(url_for('get_account_page'))
return render_template('createVehicleForm.html', form=form)
@ -170,7 +192,7 @@ def create_pit_stop_form(vid):
db.session.add(new_stop)
vehicle.pitstops.append(new_stop)
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)))
form.odometer.default = last_pitstop.odometer
@ -214,5 +236,18 @@ def get_account_page():
def get_statistics():
stats = []
for vehicle in current_user.vehicles:
stats.append(VehicleStats(vehicle))
stats.append(tools.VehicleStats(vehicle))
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)

View File

@ -40,10 +40,11 @@ class User(db.Model, UserMixin):
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)
name = db.Column(db.String(255))
pitstops = db.relationship(
'Pitstop'
)
__table_args__ = (db.UniqueConstraint('owner_id', 'name', name='_owner_name_uniq'),)
def __init__(self, name):
self.name = name

View File

@ -44,3 +44,7 @@ class EditVehicleForm(Form):
class DeleteVehicleForm(Form):
submit = SubmitField(label='Do it!')
class DeleteAccountForm(Form):
submit = SubmitField(label='Really delete my account!')

View File

@ -1,8 +1,10 @@
body {
padding-top: 50px;
}
.starter-template {
padding: 40px 15px;
padding-top: 30px;
padding-bottom: 60px;
text-align: center;
}
@ -22,9 +24,55 @@ td {
color: #a94442;
}
.pitstop {
}
.nav-pills > li > a {
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;
}
}

View File

@ -5,7 +5,7 @@
<div class="panel panel-default">
<div class="panel-heading">Password</div>
<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
</a>
</div>
@ -13,11 +13,12 @@
<div class="panel panel-default">
<div class="panel-heading">Vehicles</div>
<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
</a>
</div>
<table class="table table-striped table-bordered">
<tbody>
<tr>
<th>
Vehicle
@ -31,18 +32,18 @@
</tr>
{% for vehicle in current_user.vehicles %}
<tr>
<td style="text-align:center">
<td>
{{ vehicle.name }}
</td>
<td style="text-align:center">
<td>
{{ vehicle.pitstops | length }} pitstops
</td>
<td style="text-align:center">
<a href="{{ url_for('edit_vehicle', vid=vehicle.id) }}">
<td>
<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
</a>
{% 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
</a>
{% else %}
@ -51,6 +52,15 @@
</td>
</tr>
{% endfor %}
</tbody>
</table>
</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 %}

View File

@ -1,6 +1,10 @@
{% 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>Admin</h3>
We have {{ data.users|length }} users so far:
<ul>
@ -8,5 +12,7 @@
<li>{{user.email}}</li>
{% endfor %}
</ul>
<a href='{{ url_for('security.login', _external=True) }}'>Login</a>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,12 +1,18 @@
{% extends "layout.html" %}
{% block body %}
<h3>Create vehicle</h3>
<form class='form-horizontal' method="POST">
<div class="col-md-2" ></div>
<div class="col-md-8">
<div class="panel panel-default">
<div class="panel-body">
<h3>Create vehicle</h3>
<form class='form-horizontal' method="POST">
{{ form.hidden_tag() }}
{{ render_field_with_errors(form.name) }}
{{ render_field_with_errors(form.submit) }}
</form>
</form>
</div>
</div>
</div>
<div class="col-md-2" ></div>
{% endblock %}

View File

@ -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 %}

View File

@ -1,11 +1,18 @@
{% extends "layout.html" %}
{% block body %}
<h3>Delete vehicle '{{vehicle.name}}'</h3>
<form class='form-horizontal' method="POST">
<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.hidden_tag() }}
{{ render_field_with_errors(form.submit) }}
</form>
</form>
</div>
</div>
</div>
<div class="col-md-2" ></div>
{% endblock %}

View File

@ -1,12 +1,18 @@
{% extends "layout.html" %}
{% block body %}
<h3>Edit vehicle</h3>
<form class='form-horizontal' method="POST">
<div class="col-md-2" ></div>
<div class="col-md-8">
<div class="panel panel-default">
<div class="panel-body">
<h3>Edit vehicle</h3>
<form class='form-horizontal' method="POST">
{{ form.hidden_tag() }}
{{ render_field_with_errors(form.name) }}
{{ render_field_with_errors(form.submit) }}
</form>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@ -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 %}

View File

@ -16,14 +16,22 @@
{% macro render_field_with_errors(field) %}
<div class="form-group">
{% 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 }}">
</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 %}
<label class="col-sm-6 control-label">
{{ field.label }}
</label>
<div class="col-sm-2">
<div class="col-sm-6">
{% if field.type == 'SelectField' %}
<select id="{{ field.id }}" name="{{ field.id }}" class="form-control">
{% for choice in field.choices %}
@ -54,7 +62,25 @@
{% endif %}
</div>
{% 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>
{% endmacro %}
@ -134,7 +160,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</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 id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
@ -150,6 +176,14 @@
{% endblock %}
</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>
</html>

View File

@ -1,8 +1,12 @@
{% extends "layout.html" %}
{% block body %}
<h3>New Pitstop for '{{ vehicle.name }}'</h3>
<form class='form-horizontal' method="POST">
<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>
<form class='form-horizontal' method="POST">
{{ form.hidden_tag() }}
{{ render_field_with_errors(form.date) }}
<span id="{{form.date.id}}_help" class="help-block">
@ -14,7 +18,8 @@
</span>
{{ render_field_with_errors(form.litres) }}
{{ render_field_with_errors(form.submit) }}
</form>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends "layout.html" %}
{% extends "layout.html" %}
{% block body %}
<div id="content">
<ul id="tabs" class="nav nav-tabs" data-tabs="tabs">
@ -14,6 +14,7 @@
{% for vehicle in current_user.vehicles %}
<div class="tab-pane {% if loop.first %}active{% endif %}" id="v{{vehicle.id}}">
<h3>{{vehicle.name}}</h3>
{% if vehicle.pitstops %}
<div class="table-responsive">
<table class="table table-striped table-bordered table-condensed">
<tr>
@ -68,6 +69,11 @@
{% endfor %}
</table>
</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>
{% endfor %}
</div>

View File

@ -2,12 +2,19 @@
{% from "security/_macros.html" import render_field_with_errors, render_field %}
{% block body %}
<h1>Change password</h1>
<form class='form-horizontal' action="{{ url_for_security('change_password') }}" method="POST" name="change_password_form">
<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">
{{ change_password_form.hidden_tag() }}
{{ 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_with_errors(change_password_form.submit) }}
</form>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@ -2,10 +2,18 @@
{% from "security/_macros.html" import render_field_with_errors, render_field %}
{% block body %}
<h1>Send password reset instructions</h1>
<form class='form-horizontal' action="{{ url_for_security('forgot_password') }}" method="POST" name="forgot_password_form">
<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">
{{ forgot_password_form.hidden_tag() }}
{{ render_field_with_errors(forgot_password_form.email) }}
{{ render_field_with_errors(forgot_password_form.submit) }}
</form>
</form>
</div>
</div>
<div class="col-md-2" ></div>
</div>
{% endblock %}

View File

@ -2,16 +2,12 @@
{% from "security/_macros.html" import render_field_with_errors, render_field %}
{% block body %}
<h1>Login</h1>
<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 class="row">
<div class="col-md-2" ></div>
<div class="col-md-8">
{{ render_login_form() }}
</div>
<div class="col-md-2" ></div>
</div>
{% endblock %}

View File

@ -2,8 +2,12 @@
{% from "security/_macros.html" import render_field_with_errors, render_field %}
{% block body %}
<h1>Register User</h1>
<form class='form-horizontal' action="{{ url_for_security('register') }}" method="POST" name="register_user_form">
<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">
{{ register_user_form.hidden_tag() }}
{{ render_field_with_errors(register_user_form.email) }}
{{ render_field_with_errors(register_user_form.password) }}
@ -11,5 +15,9 @@
{{ render_field_with_errors(register_user_form.password_confirm) }}
{% endif %}
{{ render_field_with_errors(register_user_form.submit) }}
</form>
</form>
</div>
</div>
</div>
<div class="col-md-2" ></div>
{% endblock %}

View File

@ -1,11 +1,19 @@
{% extends "layout.html" %}
{% block body %}
<h3>Select Vehicle</h3>
<form class='form-horizontal' method="POST">
<div class="col-md-2" ></div>
<div class="col-md-8">
<div class="panel panel-default">
<div class="panel-body">
<h3>Select Vehicle</h3>
<form class='form-horizontal' method="POST">
{{ form.hidden_tag() }}
{{ render_field_with_errors(form.vehicle) }}
{{ render_field_with_errors(form.submit) }}
</form>
</form>
</div>
</div>
</div>
<div class="col-md-2" ></div>
{% endblock %}

View File

@ -11,7 +11,7 @@
</li>
{% endfor %}
</ul>
<div id="vehicle_contetn" class="tab-content">
<div id="vehicle_content" class="tab-content ">
{% for vehicle in data %}
<div class="tab-pane {% if loop.first %}active{% endif %}" id="v{{vehicle.id}}">
<h3>{{vehicle.name}}</h3>
@ -45,6 +45,11 @@
</div>
<ul id="charts_tabs" class="nav nav-tabs" data-tabs="tabs">
<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">
Fuelled litres
</a>
@ -54,21 +59,30 @@
Odometer
</a>
</li>
<li>
<a href="#v{{vehicle.id}}_c3" id="i{{vehicle.id}}_c3" data-toggle="tab">
Consumption
</a>
</li>
</ul>
<div id="my-tab-content" class="tab-content">
<div class="tab-pane active" id="v{{vehicle.id}}_c1">
<div id="charts_tabs-content" class="tab-content">
<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 %}
<div id="fuelledChartDiv{{vehicle.id}}" style="width:100%; height:500px;"></div>
<script type="text/javascript">
{{ chartScript('fuelledChartDiv'+vehicle.id|str, vehicle.litres, 'l') }}
</script>
{% 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 %}
</div>
<div class="tab-pane " id="v{{vehicle.id}}_c2">
@ -78,17 +92,9 @@
{{ chartScript('odometerChartDiv'+vehicle.id|str, vehicle.odometers, 'km') }}
</script>
{% else %}
not enough data.
{% endif %}
<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>
<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 %}
</div>
</div>

View File

@ -47,3 +47,21 @@ def db_log_delete(entity):
def db_log_update(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

16
compose_config/config.py Normal file
View File

@ -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

20
docker-compose.yml Normal file
View File

@ -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