dockerized it
This commit is contained in:
parent
0a778295b6
commit
00c6f6d71d
|
@ -1,6 +1,9 @@
|
||||||
FROM debian8_python3
|
FROM debian8_python3
|
||||||
|
|
||||||
ADD app /app
|
ADD app /app
|
||||||
RUN pip3 install -r /app/requirements.txt
|
RUN pip3 install -r /app/requirements.txt; \
|
||||||
RUN (cd /app; python3 init.py)
|
mkdir /data
|
||||||
|
|
||||||
|
VOLUME ["/data"]
|
||||||
|
|
||||||
ENTRYPOINT python3 /app/main.py
|
ENTRYPOINT python3 /app/main.py
|
37
app/main.py
37
app/main.py
|
@ -5,9 +5,10 @@ from flask import render_template
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
from flask import request, redirect, g
|
from flask import request, redirect, g
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
|
import os, os.path
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
DATABASE = '/tmp/rollerverbrauch.db'
|
DATABASE = '/data/rollerverbrauch.db'
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
SECRET_KEY = 'development key'
|
SECRET_KEY = 'development key'
|
||||||
USERNAME = 'admin'
|
USERNAME = 'admin'
|
||||||
|
@ -21,7 +22,9 @@ def connect_db():
|
||||||
def init_db():
|
def init_db():
|
||||||
with closing(connect_db()) as db:
|
with closing(connect_db()) as db:
|
||||||
with app.open_resource('schema.sql', mode='r') as f:
|
with app.open_resource('schema.sql', mode='r') as f:
|
||||||
db.cursor().executescript(f.read())
|
sql_commands = f.read()
|
||||||
|
print(sql_commands)
|
||||||
|
db.cursor().executescript(sql_commands)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
|
@ -62,6 +65,34 @@ def getPitStops():
|
||||||
data = {'pitstops': preparePitStops(getAllPitStops())}
|
data = {'pitstops': preparePitStops(getAllPitStops())}
|
||||||
return render_template('pitstops.html', data=data)
|
return render_template('pitstops.html', data=data)
|
||||||
|
|
||||||
|
@app.route('/statistics', methods=['GET'])
|
||||||
|
def getStatistics():
|
||||||
|
pitstops = getAllPitStops()
|
||||||
|
count = len(pitstops)
|
||||||
|
distance = 0
|
||||||
|
sumLitres = 0
|
||||||
|
averageDistance = 0
|
||||||
|
averageLitresFuelled = 0
|
||||||
|
averageLitresUsed = 0
|
||||||
|
count = len(pitstops)
|
||||||
|
if len(pitstops) > 0:
|
||||||
|
sumLitres = 0
|
||||||
|
for pitstop in pitstops:
|
||||||
|
sumLitres += pitstop['litres']
|
||||||
|
averageLitresFuelled = round(sumLitres/count, 2)
|
||||||
|
if len(pitstops) > 1:
|
||||||
|
distance = pitstops[-1]['odometer'] - pitstops[0]['odometer']
|
||||||
|
averageDistance = round(distance/(count - 1), 2)
|
||||||
|
averageLitresUsed = round(100 * (sumLitres-pitstops[0]['litres'])/distance, 2)
|
||||||
|
data = {
|
||||||
|
'distance':distance,
|
||||||
|
'count': count,
|
||||||
|
'litres': round(sumLitres, 2),
|
||||||
|
'averageDistance': averageDistance,
|
||||||
|
'averageListresFuelled': averageLitresFuelled,
|
||||||
|
'averageListresUsed': averageLitresUsed}
|
||||||
|
return render_template('statistics.html', data=data)
|
||||||
|
|
||||||
def preparePitStops(pitstops):
|
def preparePitStops(pitstops):
|
||||||
for index in range(1, len(pitstops)):
|
for index in range(1, len(pitstops)):
|
||||||
last = pitstops[index - 1]
|
last = pitstops[index - 1]
|
||||||
|
@ -92,4 +123,6 @@ def addPitStop(date, odometer, litres):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
if not os.path.isfile(DATABASE) or os.stat(DATABASE).st_size == 0:
|
||||||
|
init_db()
|
||||||
app.run(debug=True, host='0.0.0.0')
|
app.run(debug=True, host='0.0.0.0')
|
|
@ -3,6 +3,7 @@
|
||||||
{% block navigation %}
|
{% block navigation %}
|
||||||
<li><a href='{{ url_for('getPitStops') }}'>Home</a></li>
|
<li><a href='{{ url_for('getPitStops') }}'>Home</a></li>
|
||||||
<li><a href='{{ url_for('createPitStopForm') }}' class="active">Create Pitstop</a></li>
|
<li><a href='{{ url_for('createPitStopForm') }}' class="active">Create Pitstop</a></li>
|
||||||
|
<li><a href='{{ url_for('getStatistics') }}'>Statistics</a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
{% block navigation %}
|
{% block navigation %}
|
||||||
<li><a href='{{ url_for('getPitStops') }}' class="active">Home</a></li>
|
<li><a href='{{ url_for('getPitStops') }}' class="active">Home</a></li>
|
||||||
<li><a href='{{ url_for('createPitStopForm') }}'>Create Pitstop</a></li>
|
<li><a href='{{ url_for('createPitStopForm') }}'>Create Pitstop</a></li>
|
||||||
|
<li><a href='{{ url_for('getStatistics') }}'>Statistics</a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
@ -49,5 +50,5 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table
|
</table>
|
||||||
{% endblock %}
|
{% endblock %}
|
108
main.py
108
main.py
|
@ -1,108 +0,0 @@
|
||||||
import sqlite3
|
|
||||||
from datetime import datetime
|
|
||||||
from flask import Flask
|
|
||||||
from flask import render_template
|
|
||||||
from flask import url_for
|
|
||||||
from flask import request, redirect, g
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
DATABASE = '/tmp/rollerverbrauch.db'
|
|
||||||
DEBUG = True
|
|
||||||
SECRET_KEY = 'development key'
|
|
||||||
USERNAME = 'admin'
|
|
||||||
PASSWORD = 'default'
|
|
||||||
app.config.from_object(__name__)
|
|
||||||
|
|
||||||
def connect_db():
|
|
||||||
result = sqlite3.connect(app.config['DATABASE'])
|
|
||||||
return result
|
|
||||||
|
|
||||||
@app.before_request
|
|
||||||
def before_request():
|
|
||||||
g.db = connect_db()
|
|
||||||
|
|
||||||
@app.teardown_request
|
|
||||||
def teardown_request(exception):
|
|
||||||
db = getattr(g, 'db', None)
|
|
||||||
if db is not None:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def index():
|
|
||||||
#data = {'pitstopsUrl': url_for('getPitStops')}
|
|
||||||
#return render_template('index.html', data=data)
|
|
||||||
return redirect(url_for('getPitStops'))
|
|
||||||
|
|
||||||
@app.route('/pitstops', methods=['POST'])
|
|
||||||
def createPitStop():
|
|
||||||
date = request.form['date']
|
|
||||||
odometer = request.form['odometer']
|
|
||||||
litres = request.form['litres']
|
|
||||||
|
|
||||||
# error checking here
|
|
||||||
|
|
||||||
addPitStop(date, odometer, litres)
|
|
||||||
|
|
||||||
return redirect(url_for('getPitStops'))
|
|
||||||
|
|
||||||
@app.route('/pitstops/createForm', methods=['GET'])
|
|
||||||
def createPitStopForm():
|
|
||||||
data = {'last':getLastPitStop(), 'error': None}
|
|
||||||
return render_template('newPitStopForm.html', data=data)
|
|
||||||
|
|
||||||
@app.route('/pitstops', methods=['GET'])
|
|
||||||
def getPitStops():
|
|
||||||
data = {'pitstops': preparePitStops(getAllPitStops())}
|
|
||||||
return render_template('pitstops.html', data=data)
|
|
||||||
|
|
||||||
@app.route('/statistics', methods=['GET'])
|
|
||||||
def getStatistics():
|
|
||||||
pitstops = getAllPitStops()
|
|
||||||
distance = pitstops[-1]['odometer'] - pitstops[0]['odometer']
|
|
||||||
count = len(pitstops)
|
|
||||||
averageDistance = round(distance/count, 2)
|
|
||||||
sumLitres = 0
|
|
||||||
for pitstop in pitstops:
|
|
||||||
sumLitres += pitstop['litres']
|
|
||||||
averageLitresFuelled = round(sumLitres/count, 2)
|
|
||||||
averageLitresUsed = round(100 * sumLitres/distance, 2)
|
|
||||||
data = {
|
|
||||||
'distance':distance,
|
|
||||||
'count': count,
|
|
||||||
'litres': round(sumLitres, 2),
|
|
||||||
'averageDistance': averageDistance,
|
|
||||||
'averageListresFuelled': averageLitresFuelled,
|
|
||||||
'averageListresUsed': averageLitresUsed}
|
|
||||||
return render_template('statistics.html', data=data)
|
|
||||||
|
|
||||||
def preparePitStops(pitstops):
|
|
||||||
for index in range(1, len(pitstops)):
|
|
||||||
last = pitstops[index - 1]
|
|
||||||
curr = pitstops[index]
|
|
||||||
curr['distance'] = curr['odometer'] - last['odometer']
|
|
||||||
curr['average'] = round(100 * curr['litres']/curr['distance'], 2)
|
|
||||||
last_date = datetime.strptime(last['date'], '%Y-%m-%d')
|
|
||||||
curr_date = datetime.strptime(curr['date'], '%Y-%m-%d')
|
|
||||||
curr['days'] = (curr_date - last_date).days
|
|
||||||
return pitstops
|
|
||||||
|
|
||||||
def getLastPitStop():
|
|
||||||
cur = g.db.execute('select date, odometer, litres from pitstops order by date desc limit 1')
|
|
||||||
pitstops = [dict(date=row[0], odometer=row[1], litres=row[2]) for row in cur.fetchall()]
|
|
||||||
if len(pitstops) == 0:
|
|
||||||
return {'date': datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d'), 'odometer': 0, 'litres': 0}
|
|
||||||
return pitstops[0]
|
|
||||||
|
|
||||||
def getAllPitStops():
|
|
||||||
cur = g.db.execute('select date, odometer, litres from pitstops order by id asc')
|
|
||||||
pitstops = [dict(date=row[0], odometer=row[1], litres=row[2]) for row in cur.fetchall()]
|
|
||||||
return pitstops
|
|
||||||
|
|
||||||
def addPitStop(date, odometer, litres):
|
|
||||||
g.db.execute('insert into pitstops (date, odometer, litres) values (?, ?, ?)', [date, odometer, litres])
|
|
||||||
g.db.commit()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
app.run(debug=True)
|
|
|
@ -1,7 +0,0 @@
|
||||||
drop table if exists pitstops;
|
|
||||||
create table pitstops (
|
|
||||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
`date` TEXT NOT NULL,
|
|
||||||
`odometer` INTEGER NOT NULL,
|
|
||||||
`litres` REAL NOT NULL
|
|
||||||
);
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.9 KiB |
File diff suppressed because one or more lines are too long
|
@ -1,19 +0,0 @@
|
||||||
body {
|
|
||||||
padding-top: 50px;
|
|
||||||
}
|
|
||||||
.starter-template {
|
|
||||||
padding: 40px 15px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
text-align: center;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
text-align: right;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
||||||
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}
|
|
|
@ -1,5 +0,0 @@
|
||||||
{% extends "layout.html" %}
|
|
||||||
{% block body %}
|
|
||||||
<a href='{{ url_for('getPitStops') }}'>Pitstop List</a>
|
|
||||||
<a href='{{ url_for('createPitStopForm') }}'>Create Pitstop</a>
|
|
||||||
{% endblock %}
|
|
|
@ -1,60 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang=""> <![endif]-->
|
|
||||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang=""> <![endif]-->
|
|
||||||
<!--[if IE 8]> <html class="no-js lt-ie9" lang=""> <![endif]-->
|
|
||||||
<!--[if gt IE 8]><!--> <html class="no-js" lang=""> <!--<![endif]-->
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|
||||||
<title>Rollerverbrauch</title>
|
|
||||||
<meta name="description" content="">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<!-- <link rel="apple-touch-icon" href="{{ url_for('static', filename='apple-touch-icon.png') }}">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='normalize.min.css') }}">
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='main.css') }}">
|
|
||||||
|
|
||||||
<script src="{{ url_for('static', filename='modernizr-2.8.3-respond-1.4.2.min.js') }}"></script>-->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Latest compiled and minified CSS -->
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
|
|
||||||
|
|
||||||
<!-- Optional theme -->
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">
|
|
||||||
|
|
||||||
<!-- Latest compiled and minified JavaScript -->
|
|
||||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='main.css') }}">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
|
||||||
<div class="container">
|
|
||||||
<div class="navbar-header">
|
|
||||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
|
||||||
<span class="sr-only">Toggle navigation</span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
</button>
|
|
||||||
<a class="navbar-brand" href="{{ url_for('getPitStops') }}">Rollerverbrauch</a>
|
|
||||||
</div>
|
|
||||||
<div id="navbar" class="collapse navbar-collapse">
|
|
||||||
<ul class="nav navbar-nav">
|
|
||||||
{% block navigation %}
|
|
||||||
{% endblock %}
|
|
||||||
</ul>
|
|
||||||
</div><!--/.nav-collapse -->
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="starter-template">
|
|
||||||
{% block body %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
{% extends "layout.html" %}
|
|
||||||
|
|
||||||
{% block navigation %}
|
|
||||||
<li><a href='{{ url_for('getPitStops') }}'>Home</a></li>
|
|
||||||
<li><a href='{{ url_for('createPitStopForm') }}' class="active">Create Pitstop</a></li>
|
|
||||||
<li><a href='{{ url_for('getStatistics') }}'>Statistics</a></li>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
{% if data.error %}
|
|
||||||
<div>
|
|
||||||
<p class='error'><strong>Error:</strong> {{ data.error }}</p>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<form class="form-horizontal" id='createPitStop' action="{{ url_for('createPitStop') }}" method='post'>
|
|
||||||
<fieldset>
|
|
||||||
|
|
||||||
<!-- Form Name -->
|
|
||||||
<legend>Create Pitstop</legend>
|
|
||||||
|
|
||||||
<!-- Text input-->
|
|
||||||
<div class="control-group">
|
|
||||||
<label class="control-label" for="date">Date of Pitstop</label>
|
|
||||||
<div class="controls">
|
|
||||||
<input id="date" name="date" placeholder="" class="input-large" required="" type="date" value='{{ data.last.date }}' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Text input-->
|
|
||||||
<div class="control-group">
|
|
||||||
<label class="control-label" for="odometer">Odometer</label>
|
|
||||||
<div class="controls">
|
|
||||||
<input id="odometer" name="odometer" placeholder="" class="input-large" required="" type="text" value='{{ data.last.odometer }}' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Text input-->
|
|
||||||
<div class="control-group">
|
|
||||||
<label class="control-label" for="litres">Litres</label>
|
|
||||||
<div class="controls">
|
|
||||||
<input id="litres" name="litres" placeholder="" class="input-large" required="" type="text" value='{{ data.last.litres }}' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Button (Double) -->
|
|
||||||
<div class="control-group">
|
|
||||||
<label class="control-label" for="buttonLogId"></label>
|
|
||||||
<div class="controls">
|
|
||||||
<button id="buttonLogId" name="buttonLogId" class="btn btn-success" onclick="document.getElementById('createPitStop').submit();">Log Pitstop</button>
|
|
||||||
<button id="buttonAbortId" type="button" name="buttonAbortId" class="btn btn-warning" onclick="window.location.href='{{ url_for('getPitStops') }}'">Abort</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,54 +0,0 @@
|
||||||
{% extends "layout.html" %}
|
|
||||||
|
|
||||||
{% block navigation %}
|
|
||||||
<li><a href='{{ url_for('getPitStops') }}' class="active">Home</a></li>
|
|
||||||
<li><a href='{{ url_for('createPitStopForm') }}'>Create Pitstop</a></li>
|
|
||||||
<li><a href='{{ url_for('getStatistics') }}'>Statistics</a></li>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
Date
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
Day
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
Odometer
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
Distance
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
Litres
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
Average
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
{% for pitstop in data['pitstops'] %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{pitstop.date}}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if pitstop.days %}{{pitstop.days}}{% else %} --{% endif %} days
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{pitstop.odometer}} km
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if pitstop.distance %}{{pitstop.distance}}{% else %} --{% endif %} km
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{pitstop.litres}} l
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if pitstop.average %}{{pitstop.average}}{% else %} --{% endif %} l/100km
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
{% endblock %}
|
|
Loading…
Reference in New Issue