13 Commits

9 changed files with 38 additions and 24 deletions

View File

@@ -6,7 +6,6 @@ from flask_sqlalchemy import SQLAlchemy
import os import os
from config import config from config import config
from flask_security.forms import LoginForm from flask_security.forms import LoginForm
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address from flask_limiter.util import get_remote_address
from .forms import * from .forms import *
@@ -15,13 +14,6 @@ from .forms import *
app = Flask(__name__) app = Flask(__name__)
app.config.from_object(config[os.getenv('FLASK_CONFIG') or 'default']) app.config.from_object(config[os.getenv('FLASK_CONFIG') or 'default'])
# applies to all routes, so choose limits wisely!
limiter = Limiter(
app,
key_func=get_remote_address,
# default_limits=["500 per second"]
)
@app.errorhandler(429) @app.errorhandler(429)
def ratelimit_handler(e): def ratelimit_handler(e):

View File

@@ -6,7 +6,7 @@ from .checks import *
class CreateServiceForm(FlaskForm): class CreateServiceForm(FlaskForm):
date = DateField('Date of Pitstop') date = DateField('Date of Service')
odometer = IntegerField('Odometer (km)', validators=[odometer_date_check]) odometer = IntegerField('Odometer (km)', validators=[odometer_date_check])
costs = DecimalField('Costs (€, overall)', places=2, validators=[costs_check]) costs = DecimalField('Costs (€, overall)', places=2, validators=[costs_check])
description = TextAreaField('Description', validators=[Length(1, 4096)]) description = TextAreaField('Description', validators=[Length(1, 4096)])
@@ -72,4 +72,4 @@ class EditServiceForm(FlaskForm):
'litres': 'Litres must be higher than 0.01 L.', 'litres': 'Litres must be higher than 0.01 L.',
'costs': 'Costs must be higher than 0.01 €.' 'costs': 'Costs must be higher than 0.01 €.'
} }
return messages return messages

View File

@@ -4,7 +4,7 @@ from flask_security.core import current_user
import requests import requests
from ..entities import FillingStation from ..entities import FillingStation
from .. import app, db, limiter from .. import app, db
@app.route('/filling_stations/favourites/toggle/<fsid>') @app.route('/filling_stations/favourites/toggle/<fsid>')
@@ -24,7 +24,6 @@ def add_favourite_filling_stations(fsid):
@app.route('/filling_stations', methods=['GET']) @app.route('/filling_stations', methods=['GET'])
@login_required @login_required
@limiter.limit('1 per second')
def query_filling_stations(): def query_filling_stations():
api_key = app.config['TANKERKOENIG_API_KEY'] api_key = app.config['TANKERKOENIG_API_KEY']

View File

@@ -14,6 +14,7 @@ from ..tools import (
db_log_delete, db_log_delete,
get_event_line_for_vehicle, get_event_line_for_vehicle,
get_latest_pitstop_for_vehicle, get_latest_pitstop_for_vehicle,
get_users_active_vehicle,
) )
from .. import app, db from .. import app, db

View File

@@ -185,7 +185,7 @@
<div class="col-md-12"> <div class="col-md-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-body"> <div class="panel-body">
<a href="https://www.lusiardi.de/impressum/" target="_new">Impressum</a> - <a href="https://www.lusiardi.de/datenschutzerklaerung/" target="_new">Datenschutzerklärung</a> <a href="https://lusiardi.de/pages/impressum.html" target="_new">Impressum</a> - <a href="https://lusiardi.de/pages/datenschutzerklaerung.html" target="_new">Datenschutzerklärung</a>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -164,6 +164,7 @@
<ul id="consumable_{{vehicle.id}}_{{consumable.id}}_tabs" class="nav nav-tabs" data-tabs="tabs"> <ul id="consumable_{{vehicle.id}}_{{consumable.id}}_tabs" class="nav nav-tabs" data-tabs="tabs">
{{ nav_tab(vehicle.id|string + '_' + consumable.id|string + '_consumption', 'Consumption', true) }} {{ nav_tab(vehicle.id|string + '_' + consumable.id|string + '_consumption', 'Consumption', true) }}
{{ nav_tab(vehicle.id|string + '_' + consumable.id|string + '_amount', 'Amount', false) }} {{ nav_tab(vehicle.id|string + '_' + consumable.id|string + '_amount', 'Amount', false) }}
{{ nav_tab(vehicle.id|string + '_' + consumable.id|string + '_price', 'Price', false) }}
</ul> </ul>
<div id="consumable_{{vehicle.id}}_{{consumable.id}}_content" class="tab-content "> <div id="consumable_{{vehicle.id}}_{{consumable.id}}_content" class="tab-content ">
{{ tab_pane( {{ tab_pane(
@@ -188,6 +189,17 @@
false false
) )
}} }}
{{ tab_pane(
vehicle.id|string + '_' + consumable.id|string + '_price',
chart(
consumable.price,
'ref_' + vehicle.id|string + '_' + consumable.id|string + '_price',
'€ / '+consumable.unit,
url_for('create_pit_stop_form', vid=vehicle.id, cid=consumable.id)
),
false
)
}}
</div> </div>
{{ tab_script('vehicle_' + vehicle.id|string + '_' + consumable.id|string + '_tabs') }} {{ tab_script('vehicle_' + vehicle.id|string + '_' + consumable.id|string + '_tabs') }}
</div> </div>

View File

@@ -27,6 +27,7 @@ class ConsumableStats:
self.average_amount_used = 0 self.average_amount_used = 0
self.average_amount = [] self.average_amount = []
self.amounts = [] self.amounts = []
self.price = []
pitstops = [ pitstops = [
stop for stop in vehicle.pitstops if stop.consumable_id == consumable.id stop for stop in vehicle.pitstops if stop.consumable_id == consumable.id
@@ -37,6 +38,9 @@ class ConsumableStats:
for pitstop in pitstops: for pitstop in pitstops:
self.overall_amount += pitstop.amount self.overall_amount += pitstop.amount
self.amounts.append(StatsEvent(pitstop.date, pitstop.amount)) self.amounts.append(StatsEvent(pitstop.date, pitstop.amount))
# some pitstops seem to have lost their costs...
if pitstop.costs:
self.price.append(StatsEvent(pitstop.date, pitstop.costs/pitstop.amount))
self.average_amount_fuelled = self.overall_amount / pitstop_count self.average_amount_fuelled = self.overall_amount / pitstop_count
if pitstop_count > 1: if pitstop_count > 1:
overall_distance = ( overall_distance = (
@@ -204,7 +208,9 @@ def compute_lower_limits_for_new_pitstop(
def pitstop_service_key(x): def pitstop_service_key(x):
return x.date, x.odometer # if the entry got no odometer (regular costs!) then we assume it's okay
# to have regular cost at a virtual odometer of 0 on that day.
return x.date, x.odometer or 0
def get_event_line_for_vehicle(vehicle): def get_event_line_for_vehicle(vehicle):

View File

@@ -41,8 +41,8 @@ class TestingConfig(Config):
class ProductionConfig(Config): class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:{h}@database/pitstops'.format( SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://pitstops:{h}@localhost/pitstops'.format(
h=os.environ.get('DATABASE_ENV_MYSQL_ROOT_PASSWORD')) h=os.environ.get('MYSQL_PASSWORD'))
config = { config = {

View File

@@ -1,9 +1,13 @@
Flask Flask==2.1.2
Flask-SQLAlchemy Flask-SQLAlchemy==2.5.1
Flask-Security Flask-Security==3.0.0
Flask-WTF Flask-WTF==1.0.1
PyMySQL PyMySQL==1.0.2
markdown markdown
Flask-Limiter Flask-Limiter==2.4.5.1
requests requests==2.27.1
email_validator email-validator==1.2.1
gunicorn==20.1.0
pytz==2022.1
SQLAlchemy==1.4.36
Werkzeug==2.2.2