diff --git a/README.md b/README.md index c9b834b..bdd6bb8 100644 --- a/README.md +++ b/README.md @@ -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` + diff --git a/app/rollerverbrauch/__init__.py b/app/rollerverbrauch/__init__.py index 0465b5d..7da899b 100644 --- a/app/rollerverbrauch/__init__.py +++ b/app/rollerverbrauch/__init__.py @@ -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(): - return redirect(url_for('get_pit_stops')) + 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/', 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) diff --git a/app/rollerverbrauch/entities.py b/app/rollerverbrauch/entities.py index f89069b..64d456b 100644 --- a/app/rollerverbrauch/entities.py +++ b/app/rollerverbrauch/entities.py @@ -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 diff --git a/app/rollerverbrauch/forms.py b/app/rollerverbrauch/forms.py index 7e5bd3c..1e20c47 100644 --- a/app/rollerverbrauch/forms.py +++ b/app/rollerverbrauch/forms.py @@ -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!') diff --git a/app/rollerverbrauch/static/main.css b/app/rollerverbrauch/static/main.css index 7626b33..6b9476b 100644 --- a/app/rollerverbrauch/static/main.css +++ b/app/rollerverbrauch/static/main.css @@ -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; + } } \ No newline at end of file diff --git a/app/rollerverbrauch/templates/account.html b/app/rollerverbrauch/templates/account.html index 8cec4de..cc1ed3a 100644 --- a/app/rollerverbrauch/templates/account.html +++ b/app/rollerverbrauch/templates/account.html @@ -1,11 +1,11 @@ {% extends "layout.html" %} {% block body %} -

Account management for {{current_user.email}}

+

Account management for {{current_user.email}}

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

Admin

- We have {{ data.users|length }} users so far: - - Login +
+
+
+
+

Admin

+ We have {{ data.users|length }} users so far: +
    + {% for user in data.users %} +
  • {{user.email}}
  • + {% endfor %} +
+
+
+
{% endblock %} diff --git a/app/rollerverbrauch/templates/createVehicleForm.html b/app/rollerverbrauch/templates/createVehicleForm.html index 4ea3b84..2dce35b 100644 --- a/app/rollerverbrauch/templates/createVehicleForm.html +++ b/app/rollerverbrauch/templates/createVehicleForm.html @@ -1,12 +1,18 @@ {% extends "layout.html" %} {% block body %} -

Create vehicle

-
- {{ form.hidden_tag() }} - {{ render_field_with_errors(form.name) }} - {{ render_field_with_errors(form.submit) }} -
- - +
+
+
+
+

Create vehicle

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

Delete account for '{{current_user.email}}'?

+ This cannot be undone! +
+ {{ form.hidden_tag() }} + {{ render_field_with_errors(form.submit) }} +
+
+
+
+
+{% endblock %} diff --git a/app/rollerverbrauch/templates/deleteVehicleForm.html b/app/rollerverbrauch/templates/deleteVehicleForm.html index 5e957c3..c058771 100644 --- a/app/rollerverbrauch/templates/deleteVehicleForm.html +++ b/app/rollerverbrauch/templates/deleteVehicleForm.html @@ -1,11 +1,18 @@ {% extends "layout.html" %} {% block body %} -

Delete vehicle '{{vehicle.name}}'

-
- {{ form.hidden_tag() }} - {{ render_field_with_errors(form.submit) }} -
- +
+
+
+
+

Delete vehicle '{{vehicle.name}}'?

+
+ {{ form.hidden_tag() }} + {{ render_field_with_errors(form.submit) }} +
+
+
+
+
{% endblock %} diff --git a/app/rollerverbrauch/templates/editVehicleForm.html b/app/rollerverbrauch/templates/editVehicleForm.html index 627a24f..8d2827f 100644 --- a/app/rollerverbrauch/templates/editVehicleForm.html +++ b/app/rollerverbrauch/templates/editVehicleForm.html @@ -1,12 +1,18 @@ {% extends "layout.html" %} {% block body %} -

Edit vehicle

-
- {{ form.hidden_tag() }} - {{ render_field_with_errors(form.name) }} - {{ render_field_with_errors(form.submit) }} -
- +
+
+
+
+

Edit vehicle

+
+ {{ form.hidden_tag() }} + {{ render_field_with_errors(form.name) }} + {{ render_field_with_errors(form.submit) }} +
+
+
+
{% endblock %} diff --git a/app/rollerverbrauch/templates/index.html b/app/rollerverbrauch/templates/index.html new file mode 100644 index 0000000..162c066 --- /dev/null +++ b/app/rollerverbrauch/templates/index.html @@ -0,0 +1,28 @@ +{% extends "layout.html" %} +{% from "security/_macros.html" import render_field_with_errors, render_field %} + +{% block body %} +
+
+ {{ render_login_form() }} +
+
+
+
+

Join the pitstop community!

+ +

There are already {{ data.users}} members with {{ data.vehicles }} vehicles who have logged {{ data.pitstops }} pitstops fuelling {{ data.litres }}l for {{ data.kilometers }}km.

+ +

With pitstop community you can:

+
    +
  • manage multiple vehicles
  • +
  • track each pitstop
  • +
  • get statistics about the fuel consumption
  • +
+ +

Register your account now or log into your account.

+
+
+
+
+{% endblock %} diff --git a/app/rollerverbrauch/templates/layout.html b/app/rollerverbrauch/templates/layout.html index 2df1885..60e5ebe 100644 --- a/app/rollerverbrauch/templates/layout.html +++ b/app/rollerverbrauch/templates/layout.html @@ -16,14 +16,22 @@ {% macro render_field_with_errors(field) %}
{% if field.type == 'SubmitField' %} -
+
+ +
+ +
{% else %} -
+
{% if field.type == 'SelectField' %}