dockerized it
This commit is contained in:
parent
a8b6789295
commit
e64f94cfd9
|
@ -0,0 +1,6 @@
|
||||||
|
FROM debian8_python3
|
||||||
|
|
||||||
|
ADD app /app
|
||||||
|
RUN pip3 install -r /app/requirements.txt
|
||||||
|
RUN (cd /app; python3 init.py)
|
||||||
|
ENTRYPOINT python3 /app/main.py
|
|
@ -0,0 +1,3 @@
|
||||||
|
from main import init_db
|
||||||
|
|
||||||
|
init_db()
|
|
@ -0,0 +1,95 @@
|
||||||
|
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
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def init_db():
|
||||||
|
with closing(connect_db()) as db:
|
||||||
|
with app.open_resource('schema.sql', mode='r') as f:
|
||||||
|
db.cursor().executescript(f.read())
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
|
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.strftime(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, host='0.0.0.0')
|
|
@ -0,0 +1,2 @@
|
||||||
|
#db-sqlite3
|
||||||
|
Flask
|
|
@ -0,0 +1,7 @@
|
||||||
|
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.
After Width: | Height: | Size: 3.9 KiB |
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,19 @@
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
/*! 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}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{% extends "layout.html" %}
|
||||||
|
{% block body %}
|
||||||
|
<a href='{{ url_for('getPitStops') }}'>Pitstop List</a>
|
||||||
|
<a href='{{ url_for('createPitStopForm') }}'>Create Pitstop</a>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,61 @@
|
||||||
|
<!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">
|
||||||
|
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
|
||||||
|
<!-- 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>
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
{% 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>
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,53 @@
|
||||||
|
{% 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>
|
||||||
|
{% 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