From 43fa41aace8861536dbf9e3ab110eb61060c8e9d Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Tue, 1 Nov 2016 11:16:13 +0100 Subject: [PATCH 01/13] Add input type TextAreaField adds a possibility to add longer texts in the style of rollerverbrauch --- app/templates/layout.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/templates/layout.html b/app/templates/layout.html index 91314bf..c511fd7 100644 --- a/app/templates/layout.html +++ b/app/templates/layout.html @@ -56,6 +56,8 @@ {% elif field.type == 'DecimalField' %} + {% elif field.type == 'TextAreaField' %} + {% else %} {{ field(**kwargs)|safe }} {% endif %} From d132dc72de6df4de868bfdaba701ff81ae966b8a Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Tue, 1 Nov 2016 11:17:54 +0100 Subject: [PATCH 02/13] Add Entitiy for the Service Object --- app/entities.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/app/entities.py b/app/entities.py index 5e06001..d138bbf 100644 --- a/app/entities.py +++ b/app/entities.py @@ -63,6 +63,9 @@ class Vehicle(db.Model): pitstops = db.relationship( 'Pitstop' ) + services = db.relationship( + 'Service' + ) consumables = db.relationship( 'Consumable', secondary=vehicles_consumables @@ -140,3 +143,23 @@ class Consumable(db.Model): def __repr__(self): return '' % (self.name, self.unit) + + +class Service(db.Model): + id = db.Column(db.Integer, primary_key=True) + date = db.Column(db.Date) + odometer = db.Column(db.Integer) + vehicle_id = db.Column(db.Integer, db.ForeignKey('vehicle.id')) + costs = db.Column(db.Numeric(10, 2), default=0) + description = db.Column(db.String(4096)) + + def __init__(self, date, odometer, vehicle_id, costs, description): + self.description = description + self.costs = costs + self.date = date + self.odometer = odometer + self.vehicle_id = vehicle_id + + def __repr__(self): + return '' % \ + (self.odometer, self.date, self.vehicle_id, self.costs, self.description) From dd11419305f99d0de662adea620cc5d4b9edad85 Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Tue, 1 Nov 2016 11:18:40 +0100 Subject: [PATCH 03/13] add the crud forms for services --- app/forms.py | 66 +++++++++++++++++++++++++++- app/templates/createServiceForm.html | 30 +++++++++++++ app/templates/deleteServiceForm.html | 42 ++++++++++++++++++ app/templates/editServiceForm.html | 29 ++++++++++++ 4 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 app/templates/createServiceForm.html create mode 100644 app/templates/deleteServiceForm.html create mode 100644 app/templates/editServiceForm.html diff --git a/app/forms.py b/app/forms.py index 5a51f8c..96af2f7 100644 --- a/app/forms.py +++ b/app/forms.py @@ -1,5 +1,6 @@ from flask_wtf import Form -from wtforms import DateField, IntegerField, DecimalField, StringField, SelectField, SubmitField, SelectMultipleField, BooleanField +from wtforms import DateField, IntegerField, DecimalField, StringField, SelectField, SubmitField, SelectMultipleField, \ + BooleanField, TextAreaField from wtforms.validators import ValidationError, Length from datetime import date @@ -167,3 +168,66 @@ class EditConsumableForm(Form): class DeletConsumableForm(Form): submit = SubmitField(label='Do it!') + + +class CreateServiceForm(Form): + date = DateField('Date of Service', validators=[date_check]) + odometer = IntegerField('Odometer (km)', validators=[odometer_check]) + costs = DecimalField('Costs (€, overall)', places=2, validators=[costs_check]) + description = TextAreaField('Description', validators=[Length(1, 4096)]) + submit = SubmitField(label='Do it!') + last_pitstop = None + + def set_pitstop(self, last_pitstop): + self.last_pitstop = last_pitstop + + def preinit_with_data(self): + if self.date.data: + self.date.default = self.date.data + else: + self.date.default = date.today() + + if self.odometer.data: + self.odometer.default = self.odometer.data + else: + self.odometer.default = self.last_pitstop.odometer + + if self.costs.data: + self.costs.default = self.costs.data + else: + self.costs.default = 0 + + +class DeleteServiceForm(Form): + submit = SubmitField(label='Really delete this service!') + + +class EditServiceForm(Form): + date = DateField('Date of Service', validators=[date_check]) + odometer = IntegerField('Odometer (km)', validators=[odometer_check]) + costs = DecimalField('Costs (€, overall)', places=2, validators=[costs_check]) + description = TextAreaField('Description', validators=[Length(1, 4096)]) + submit = SubmitField(label='Do it!') + last_pitstop = None + same_odometer_allowed = True + + def set_pitstop(self, last_pitstop): + self.last_pitstop = last_pitstop + + def preinit_with_data(self): + if self.date.data: + self.date.default = self.date.data + else: + self.date.default = date.today() + + if self.odometer.data: + self.odometer.default = self.odometer.data + else: + self.odometer.default = self.last_pitstop.odometer + + if self.costs.data: + self.costs.default = self.costs.data + else: + self.costs.default = 0 + + diff --git a/app/templates/createServiceForm.html b/app/templates/createServiceForm.html new file mode 100644 index 0000000..68758f4 --- /dev/null +++ b/app/templates/createServiceForm.html @@ -0,0 +1,30 @@ +{% extends "layout.html" %} + +{% block body %} +
+
+
+
+

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

+
+ {{ form.hidden_tag() }} + {{ render_field_with_errors(form.date) }} + + {{messages['date']}} + + {{ render_field_with_errors(form.odometer) }} + + {{messages['odometer']}} + + {{ render_field_with_errors(form.costs) }} + + {{messages['costs']}} + + {{ render_field_with_errors(form.description) }} + {{ render_field_with_errors(form.submit) }} +
+
+
+
+
+{% endblock %} diff --git a/app/templates/deleteServiceForm.html b/app/templates/deleteServiceForm.html new file mode 100644 index 0000000..06f2bec --- /dev/null +++ b/app/templates/deleteServiceForm.html @@ -0,0 +1,42 @@ +{% extends 'layout.html' %} + +{% block body %} +
+
+
+
+

Delete service?

+ + + + + + + + + + + + + + + + + +
Date of Pitstop{{ service.date }}
Odometer{{ service.odometer }} km
Description{{ service.description }}
Costs (overall) + {% if service.costs %} + {{service.costs}} + {% else %} + -- + {% endif %} + € +
+
+ {{ form.hidden_tag() }} + {{ render_field_with_errors(form.submit) }} +
+
+
+
+
+{% endblock %} diff --git a/app/templates/editServiceForm.html b/app/templates/editServiceForm.html new file mode 100644 index 0000000..c1bfe62 --- /dev/null +++ b/app/templates/editServiceForm.html @@ -0,0 +1,29 @@ +{% extends "layout.html" %} + +{% block body %} +
+
+
+
+

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

+
+ {{ form.hidden_tag() }} + {{ render_field_with_errors(form.date) }} + + {{messages['date']}} + + {{ render_field_with_errors(form.odometer) }} + + {{messages['odometer']}} + + {{ render_field_with_errors(form.description) }} + {{ render_field_with_errors(form.costs) }} + + {{messages['costs']}} + + {{ render_field_with_errors(form.submit) }} +
+
+
+
+{% endblock %} From 71412bf4878a4bbcea9268f20d0d0f52f630b88e Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Tue, 1 Nov 2016 11:19:26 +0100 Subject: [PATCH 04/13] integrate services into rollerverbrauch --- app/__init__.py | 120 ++++++++++++++++++++++++++++++- app/templates/account.html | 6 ++ app/templates/pitstops.html | 138 +++++++++++++++++++++++++----------- 3 files changed, 220 insertions(+), 44 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index c2d0e09..bf23383 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -24,7 +24,10 @@ from .forms import \ CreateConsumableForm, \ EditConsumableForm, \ DeletConsumableForm, \ - SelectConsumableForm + SelectConsumableForm, \ + CreateServiceForm, \ + DeleteServiceForm, \ + EditServiceForm app = Flask(__name__) @@ -38,7 +41,8 @@ from .entities import \ Role, \ Pitstop, \ Vehicle, \ - Consumable + Consumable, \ + Service # required to activate the filters from .filters import * @@ -367,10 +371,120 @@ def edit_pit_stop_form(pid): return render_template('editPitStopForm.html', form=form, vehicle=vehicle, messages=messages) +def pitstop_service_key(x): + return x.odometer + + @app.route('/pitstops', methods=['GET']) @login_required def get_pit_stops(): - return render_template('pitstops.html', user=current_user) + user = { + 'vehicles': [] + } + for vehicle in current_user.vehicles: + data = [] + for pitstop in vehicle.pitstops: + data.append(pitstop) + for service in vehicle.services: + data.append(service) + v = { + 'id': vehicle.id, + 'name': vehicle.name, + 'data': sorted(data, key=pitstop_service_key) + } + user['vehicles'].append(v) + + return render_template('pitstops.html', user=user) + + +@app.route('/service/vehicle//create', methods=['GET', 'POST']) +@login_required +def create_service_for_vehicle(vid): + vehicle = Vehicle.query.get(vid) + if vehicle is None or vehicle not in current_user.vehicles: + return redirect(url_for('get_account_page')) + + form = CreateServiceForm() + last_pitstop = tools.get_latest_pitstop_for_vehicle(vid) + form.set_pitstop(last_pitstop) + form.same_odometer_allowed = True + + form.preinit_with_data() + + if form.validate_on_submit(): + new_service = Service(form.date.data, form.odometer.data, vid, form.costs.data, form.description.data) + db.session.add(new_service) + vehicle.services.append(new_service) + db.session.commit() + print(new_service) + return redirect(url_for('get_account_page')) + + form.process() + return render_template('createServiceForm.html', form=form, vehicle=vehicle, messages=[]) + + +@app.route('/service/delete/', methods=['GET', 'POST']) +@login_required +def delete_service_form(sid): + service = Service.query.filter(Service.id == sid).first() + if service is None: + return redirect(url_for('get_pit_stops')) + vehicle = Vehicle.query.filter(Vehicle.id == service.vehicle_id).first() + if vehicle not in current_user.vehicles: + return redirect(url_for('get_pit_stops')) + + form = DeleteServiceForm() + if form.validate_on_submit(): + db.session.delete(service) + db.session.commit() + tools.db_log_delete(service) + return redirect(url_for('get_pit_stops', _anchor='v' + str(vehicle.id))) + + return render_template('deleteServiceForm.html', form=form, service=service ) + + +@app.route('/service/edit/', methods=['GET', 'POST']) +@login_required +def edit_service_form(sid): + edit_service = Service.query.get(sid) + if edit_service is None: + return redirect(url_for('get_pit_stops')) + + vehicle = Vehicle.query.filter(Vehicle.id == edit_service.vehicle_id).first() + if vehicle not in current_user.vehicles: + return redirect(url_for('get_pit_stops')) + + # last_pitstop_pos = vehicle.pitstops.index(edit_service) - 1 + # if last_pitstop_pos > 0: + # last_pitstop = vehicle.pitstops[last_pitstop_pos] + # else: + # last_pitstop = Pitstop(0, 0, date(1970, 1, 1), 0, 0) + last_pitstop = Pitstop(0, 0, date(1970, 1, 1), 0, 0) + + form = EditServiceForm() + form.set_pitstop(last_pitstop) + + if form.validate_on_submit(): + edit_service.costs = form.costs.data + edit_service.date = form.date.data + edit_service.description = form.description.data + edit_service.odometer = form.odometer.data + db.session.commit() + tools.db_log_update(edit_service) + return redirect(url_for('get_pit_stops', _anchor='v' + str(vehicle.id))) + + form.odometer.default = edit_service.odometer + form.description.default = edit_service.description + form.date.default = edit_service.date + form.costs.default = edit_service.costs + form.process() + messages = { + # 'date': 'Date must be between %s and %s (including).' % (str(last_pitstop.date), str(date.today())), + # 'odometer': 'Odometer must be greater than %s km.' % (str(last_pitstop.odometer)) + } + if edit_service.costs is not None and edit_service.costs > 0: + messages['costs'] = 'Costs must be higher than 0.01 €.' + return render_template('editServiceForm.html', form=form, vehicle=vehicle, messages=messages) @app.route('/manual', methods=['GET']) diff --git a/app/templates/account.html b/app/templates/account.html index e408636..01bf40a 100644 --- a/app/templates/account.html +++ b/app/templates/account.html @@ -40,6 +40,12 @@ {{ vehicle.consumables | length }} consumables + + add service + + + add pitstop + edit diff --git a/app/templates/pitstops.html b/app/templates/pitstops.html index 9a4cec5..4a5ba12 100644 --- a/app/templates/pitstops.html +++ b/app/templates/pitstops.html @@ -1,9 +1,95 @@ - {% extends "layout.html" %} +{% extends "layout.html" %} + +{% macro pitstop(field, vindex, loop) -%} +
+
+
+ +
+ + + + + + + + + + + + + + + + + +
Date{{field.date}}
Odometer{{field.odometer}} km
{{ field.consumable.name }}{{field.amount}} {{ field.consumable.unit }}
Costs + {% if field.costs %} + {{field.costs}} € + {% else %} + -- € + {% endif %} +
+ {% if loop.first %} + + edit + + + delete + + {% endif %} +
+
+{%- endmacro %} + +{% macro service(field, vindex, loop) -%} +
+
+
+ +
+ + + + + + + + + + + + + + + + + +
Date{{field.date}}
Odometer{{field.odometer}} km
Description{{field.description}}
Costs + {% if field.costs %} + {{field.costs}} € + {% else %} + -- € + {% endif %} +
+ {% if loop.first %} + + edit + + + delete + + {% endif %} +
+
+{%- endmacro %} + + {% block body %}
- {% for vehicle in current_user.vehicles %} + {% for vehicle in user.vehicles %} {% set vehicleloop = loop %}

{{vehicle.name}}

- {% if vehicle.pitstops %} - {% for pitstop in vehicle.pitstops|reverse %} -
+ {% if vehicle.data %} + {% for data in vehicle.data|reverse %} + {% if 'Pitstop' in data.__class__.__name__ %} + {{ pitstop(data, vehicleloop.index, loop) }} + {% endif %} + {% if 'Service' in data.__class__.__name__ %} + {{ service(data, vehicleloop.index, loop) }} + {% endif %} {% endfor %} {% else %}