From 5b69a82e05253d2ae5a448a4ae9e89c96efa74a6 Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Tue, 21 Jun 2016 21:06:41 +0200 Subject: [PATCH] adds tracking of costs This commit adds a new field to each pitstop for tracking of costs. This includes: * a new DB column * a new field in the create pitstop form * a new column in the pitstop overview * new stats values related to costs * 2 new graphs --- README.md | 7 +++- app/rollerverbrauch/__init__.py | 6 ++- app/rollerverbrauch/entities.py | 4 +- app/rollerverbrauch/forms.py | 6 +++ .../templates/newPitStopForm.html | 4 ++ app/rollerverbrauch/templates/pitstops.html | 12 ++++++ app/rollerverbrauch/templates/statistics.html | 42 +++++++++++++++++++ app/rollerverbrauch/tools.py | 20 ++++++++- 8 files changed, 96 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bdd6bb8..442cb08 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,11 @@ operation. ## start database `docker run --name pitstops_db -e MYSQL_ROOT_PASSWORD=$SOMESECUREPASSWORD$ -e MYSQL_DATABASE=pitstops -d mysql:latest` +## Database migrations +### From *Cathrine* to *Master*: + +`ALTER TABLE pitstop ADD COLUMN costs DECIMAL(5,2) NOT NULL DEFAULT 0.0 AFTER vehicle_id;` + ## run in development Include the development version of the code as volume, so the app gets @@ -21,7 +26,7 @@ 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 --rm --name rollerverbrauch -ti -v $PWD/app:/app -v $PWD/../rollerverbrauch_config:/app/config -v /tmp/pitstops/:/data -e DEBUG=True -p 5000:5000 rollerverbrauch` +`docker run --rm --name rollerverbrauch -ti -v $PWD/app:/app -v $PWD/../rollerverbrauch_config:/app/config -v /tmp/pitstops/:/data -e DEBUG=True -e config=../config/config.py --link pitstops_db:database -p 5000:5000 rollerverbrauch` ## 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 7da899b..b37ac1c 100644 --- a/app/rollerverbrauch/__init__.py +++ b/app/rollerverbrauch/__init__.py @@ -188,7 +188,7 @@ def create_pit_stop_form(vid): form.set_pitstop(last_pitstop) if form.validate_on_submit(): - new_stop = Pitstop(form.odometer.data, form.litres.data, form.date.data) + new_stop = Pitstop(form.odometer.data, form.litres.data, form.date.data, form.costs.data) db.session.add(new_stop) vehicle.pitstops.append(new_stop) db.session.commit() @@ -198,10 +198,12 @@ def create_pit_stop_form(vid): form.odometer.default = last_pitstop.odometer form.litres.default = last_pitstop.litres form.date.default = date.today() + form.costs.default = last_pitstop.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)) + 'odometer': 'Odometer must be greater than %s km.' % (str(last_pitstop.odometer)), + 'costs': 'Costs must be higher than 0.01 €.' } return render_template('newPitStopForm.html', form=form, vehicle=vehicle, messages = messages) diff --git a/app/rollerverbrauch/entities.py b/app/rollerverbrauch/entities.py index 64d456b..0a72a29 100644 --- a/app/rollerverbrauch/entities.py +++ b/app/rollerverbrauch/entities.py @@ -58,12 +58,14 @@ class Pitstop(db.Model): date = db.Column(db.Date) odometer = db.Column(db.Integer) litres = db.Column(db.Numeric(5, 2)) + costs = db.Column(db.Numeric(5, 2), default=0) vehicle_id = db.Column(db.Integer, db.ForeignKey('vehicle.id')) - def __init__(self, odometer, litres, date): + def __init__(self, odometer, litres, date, costs): self.odometer = odometer self.litres = litres self.date = date + self.costs = costs def __repr__(self): return '' % (self.odometer, self.litres, self.date, self.vehicle_id) \ No newline at end of file diff --git a/app/rollerverbrauch/forms.py b/app/rollerverbrauch/forms.py index 1e20c47..560de7b 100644 --- a/app/rollerverbrauch/forms.py +++ b/app/rollerverbrauch/forms.py @@ -21,6 +21,11 @@ def litres_check(form, field): raise ValidationError('You must fuel at least 0.1 l') +def costs_check(form, field): + if field.data is not None and field.data <= 0: + raise ValidationError('Costs must be above 0.01 €.') + + class SelectVehicleForm(Form): vehicle = SelectField('Vehicle', coerce=int) submit = SubmitField(label='Do it!') @@ -30,6 +35,7 @@ class CreatePitstopForm(Form): date = DateField('Date of Pitstop', validators=[date_check]) odometer = IntegerField('Odometer (km)', validators=[odometer_check]) litres = DecimalField('Litres (l)', places=2, validators=[litres_check]) + costs = DecimalField('Costs (€, overall)', places=2, validators=[costs_check]) submit = SubmitField(label='Do it!') last_pitstop = None diff --git a/app/rollerverbrauch/templates/newPitStopForm.html b/app/rollerverbrauch/templates/newPitStopForm.html index 37f2f71..feebbec 100644 --- a/app/rollerverbrauch/templates/newPitStopForm.html +++ b/app/rollerverbrauch/templates/newPitStopForm.html @@ -17,6 +17,10 @@ {{messages['odometer']}} {{ render_field_with_errors(form.litres) }} + {{ render_field_with_errors(form.costs) }} + + {{messages['costs']}} + {{ render_field_with_errors(form.submit) }} diff --git a/app/rollerverbrauch/templates/pitstops.html b/app/rollerverbrauch/templates/pitstops.html index 043485e..157df6e 100644 --- a/app/rollerverbrauch/templates/pitstops.html +++ b/app/rollerverbrauch/templates/pitstops.html @@ -30,6 +30,10 @@ Litres
Average + + Costs
+ Costs per Litre + {% for pitstop in vehicle.pitstops|reverse %} {% if not loop.last %} @@ -49,6 +53,10 @@ {{pitstop.litres}} l
{{average | round(2)}} l/100km + + {{pitstop.costs}} €
+ {{ (pitstop.costs / pitstop.litres) | round(2) }} €/l + {% else %} @@ -64,6 +72,10 @@ {{pitstop.litres}} l
-- l/100km + + {{pitstop.costs}} €
+ {{ (pitstop.costs / pitstop.litres) | round(2) }} €/l + {% endif %} {% endfor %} diff --git a/app/rollerverbrauch/templates/statistics.html b/app/rollerverbrauch/templates/statistics.html index c1d5aeb..c66622b 100644 --- a/app/rollerverbrauch/templates/statistics.html +++ b/app/rollerverbrauch/templates/statistics.html @@ -41,6 +41,14 @@ Average Litres used: {{ vehicle.average_litres_used | round(2) }} l/100km + + Logged Costs: + {{ vehicle.overall_costs | round(2) }} € + + + Average Costs per Litre: + {{ vehicle.average_costs_per_litre | round(2) }} €/l +
@@ -97,6 +115,30 @@
{% endif %}
+
+ {% if vehicle.pitstop_count > 0 %} +
+ + {% else %} + + {% endif %} +
+
+ {% if vehicle.pitstop_count > 0 %} +
+ + {% else %} + + {% endif %} +
{% endfor %} diff --git a/app/rollerverbrauch/tools.py b/app/rollerverbrauch/tools.py index 3099d76..eabea14 100644 --- a/app/rollerverbrauch/tools.py +++ b/app/rollerverbrauch/tools.py @@ -14,13 +14,29 @@ class VehicleStats: self.litres = [] self.average_litres = [] self.odometers = [] + self.costsPerLitre = [] + self.costs = [] + self.overall_costs = 0 + self.average_costs_per_litre = 0 + cost_count = 0; if self.pitstop_count > 0: for pitstop in vehicle.pitstops: self.overall_litres += pitstop.litres self.litres.append(StatsEvent(pitstop.date, pitstop.litres)) self.odometers.append(StatsEvent(pitstop.date, pitstop.odometer)) + self.costsPerLitre.append(StatsEvent(pitstop.date, pitstop.costs / pitstop.litres)) + self.costs.append(StatsEvent(pitstop.date, pitstop.costs)) + self.overall_costs += pitstop.costs + self.average_costs_per_litre += (pitstop.costs / pitstop.litres) + if pitstop.costs > 0: + cost_count += 1 self.average_litres_fuelled = self.overall_litres / self.pitstop_count + if cost_count > 0: + self.average_costs_per_litre = self.average_costs_per_litre / cost_count + else: + self.average_costs_per_litre = 0 + if self.pitstop_count > 1: self.overall_distance = vehicle.pitstops[-1].odometer - vehicle.pitstops[0].odometer self.average_distance = self.overall_distance / (self.pitstop_count - 1) @@ -28,7 +44,9 @@ class VehicleStats: for index in range(1, self.pitstop_count): last_ps = vehicle.pitstops[index - 1] current_ps = vehicle.pitstops[index] - self.average_litres.append(StatsEvent(current_ps.date, round(100 * current_ps.litres/(current_ps.odometer - last_ps.odometer),2))) + self.average_litres.append(StatsEvent( + current_ps.date, + round(100 * current_ps.litres/(current_ps.odometer - last_ps.odometer), 2))) class StatsEvent: