dockerized it
This commit is contained in:
parent
a8b6789295
commit
e64f94cfd9
6
Dockerfile
Normal file
6
Dockerfile
Normal file
@ -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
|
3
app/init.py
Normal file
3
app/init.py
Normal file
@ -0,0 +1,3 @@
|
||||
from main import init_db
|
||||
|
||||
init_db()
|
95
app/main.py
Normal file
95
app/main.py
Normal file
@ -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')
|
2
app/requirements.txt
Normal file
2
app/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
#db-sqlite3
|
||||
Flask
|
7
app/schema.sql
Normal file
7
app/schema.sql
Normal file
@ -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
|
||||
);
|
BIN
app/static/apple-touch-icon.png
Normal file
BIN
app/static/apple-touch-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
4
app/static/jquery-1.11.2.min.js
vendored
Normal file
4
app/static/jquery-1.11.2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
19
app/static/main.css
Normal file
19
app/static/main.css
Normal file
@ -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;
|
||||
}
|
1
app/static/main.js
Normal file
1
app/static/main.js
Normal file
@ -0,0 +1 @@
|
||||
|
11
app/static/modernizr-2.8.3-respond-1.4.2.min.js
vendored
Normal file
11
app/static/modernizr-2.8.3-respond-1.4.2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
app/static/normalize.min.css
vendored
Normal file
1
app/static/normalize.min.css
vendored
Normal file
@ -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}
|
5
app/templates/index.html
Normal file
5
app/templates/index.html
Normal file
@ -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 %}
|
61
app/templates/layout.html
Normal file
61
app/templates/layout.html
Normal file
@ -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>
|
||||
|
56
app/templates/newPitStopForm.html
Normal file
56
app/templates/newPitStopForm.html
Normal file
@ -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 %}
|
53
app/templates/pitstops.html
Normal file
53
app/templates/pitstops.html
Normal file
@ -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
Block a user