Commit 94820fc4 authored by Daniel Niecke's avatar Daniel Niecke

closing #11

some additional bug fixes
parent 8ee7c0dd
......@@ -6,13 +6,15 @@ RUN apt-get update
RUN apt-get install -y python3 python3-pip python3-virtualenv
# Setup flask application
RUN mkdir -p /deploy/app
RUN mkdir -p /deploy/server
COPY server/requirements.txt /
RUN pip3 install -r /requirements.txt
COPY gunicorn_config.py /deploy/gunicorn_config.py
COPY server /deploy/app
RUN pip3 install -r /deploy/app/requirements.txt
WORKDIR /deploy/app
COPY server /deploy/server
WORKDIR /deploy/server
EXPOSE 443
# Start gunicorn
CMD ["gunicorn", "--config", "/deploy/gunicorn_config.py", "test_server:app"]
CMD ["gunicorn", "--config", "/deploy/gunicorn_config.py", "run:app"]
......@@ -3,7 +3,7 @@ docker run --detach \
--hostname gps.niecke-it.de \
--name gps_service_test \
--restart always \
-v ~/git/gps_tracker/server/test.db:/deploy/app/test.db \
-v ~/git/gps_tracker/server/test.db:/deploy/server/test.db \
-e "LETSENCRYPT_HOST=gps.niecke-it.de" \
-e "LETSENCRYPT_EMAIL=niecke@bwl.uni-kiel.de" \
--env "VIRTUAL_HOST=gps.niecke-it.de" \
......
......@@ -2,7 +2,7 @@ from flask_sqlalchemy import SQLAlchemy
from flask_login import AnonymousUserMixin, UserMixin
from flask import redirect, url_for, request, flash
from server.extensions import bcrypt
from sqlalchemy import DateTime
db = SQLAlchemy()
roles = db.Table(
......@@ -15,8 +15,8 @@ roles = db.Table(
class GPSWork(db.Model):
__tablename__ = "gps_work"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.String(80), nullable=False)
datetime = db.Column(db.DateTime, unique=True, nullable=False)
datetime = db.Column(db.DateTime)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '<GPSWork %r>' % self.id
......@@ -31,6 +31,7 @@ class User(db.Model, UserMixin):
password = db.Column(db.String(256), nullable=True)
removed = db.Column(db.Boolean, default=False)
key = db.Column(db.String(512))
gps_works = db.relationship("GPSWork", backref="gps_work", lazy='dynamic')
roles = db.relationship(
'Role',
......
Flask
Flask-SQLAlchemy
gunicorn
\ No newline at end of file
gunicorn
Flask_Login
flask_principal
flask_bcrypt
flask_debugtoolbar
flask_wtf
wtforms_alchemy
......@@ -6,10 +6,14 @@ from server.models import db, Role
from server.extensions import bcrypt, login_manager, principals
from flask_login import current_user
from flask_principal import identity_loaded, UserNeed, RoleNeed
import logging
print(os.getcwd())
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SECRET_KEY'] = os.urandom(16)
app.config['SECRET_KEY'] = "vuzqegrbuoevbgreigu"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['DEBUG'] = True
db.init_app(app)
with app.app_context():
......@@ -39,6 +43,10 @@ with app.app_context():
for role in current_user.roles:
identity.provides.add(RoleNeed(role.name))
gunicorn_logger = logging.getLogger('gunicorn.error')
app.logger.handlers = gunicorn_logger.handlers
app.logger.setLevel(logging.DEBUG)
app.run(host='0.0.0.0')
if __name__ == "__main__":
app.run(host='0.0.0.0')
......@@ -2,10 +2,10 @@
{% block title %}Startseite{% endblock %}
{% block body %}
<h1>Arbeitszeiten</h1>
<form action="{{ url_for('home') }}" method="POST">
{{ form.hidden_tag() }}
<div class="row">
<div class="col">
<form action="{{ url_for('home') }}" method="POST">
{{ form.hidden_tag() }}
<div class="col">
<div class="form-group">
{{ form.begin.label }}
{% if form.begin.errors %}
......@@ -22,8 +22,8 @@
</div>
</div>
</div>
</div>
<div class="col">
</div>
<div class="col">
<div class="form-group">
{{ form.end.label }}
{% if form.end.errors %}
......@@ -34,17 +34,18 @@
{% endfor %}
{% endif %}
<div class="input-group date" id="endDate" data-target-input="nearest">
<input id="end" name="end" type="text" class="form-control datetimepicker-input" data-target="#endDate"/>
<input id="end" name="end" type="text" class="form-control datetimepicker-input" data-target="#endDate"/>
<div class="input-group-append" data-target="#endDate" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
</div>
</div>
<div class="col">
<input class="btn btn-primary" id="submit" name="submit" type="submit" value="Daten abfragen">
</div>
</div>
<div class="col">
<input class="btn btn-primary" id="submit" name="submit" type="submit" value="Daten abfragen">
</div>
</div>
</form>
{% if content %}
<div class="row">
<div class="col">
......@@ -99,12 +100,14 @@ $.extend($.fn.datetimepicker.Constructor.Default, {
$(function () {
$('#beginDate').datetimepicker({
format: 'L',
date: moment().startOf('month'),
});
});
$(function () {
$('#endDate').datetimepicker({
format: 'L',
date: moment().endOf('month'),
});
});
......
......@@ -8,11 +8,23 @@ from flask_principal import Identity, AnonymousIdentity, identity_changed
from server.models import User, db
from server.forms import LoginForm, PWForm, RegisterForm, DateForm
from datetime import date
from datetime import datetime
import datetime
import hashlib
import calendar
app = Flask(__name__)
@app.before_request
def before_request():
app.logger.info(f"[BEFORE] {request.base_url}")
"""
@app.after_request
def after_request(response):
app.logger.info(f"[AFTER] {request.base_url} -> {response.status}")
"""
def is_safe_url(target):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
......@@ -39,7 +51,7 @@ def register():
db.session.commit()
flash("User wurde erfolgreich angelegt.", category="success")
app.logger.info(f"User {user.username} successfully registered.")
return redirect(url_for('login'))
return render_template('register.html', form=form)
......@@ -73,7 +85,7 @@ def activate():
m = hashlib.sha3_512()
key_raw = f'{user.username}|{user.password}'
m.update(key_raw.encode('utf-8'))
server_key = m.digest()
server_key = m.hexdigest()
user.key = server_key
db.session.commit()
......@@ -87,14 +99,16 @@ def get_data(begin, end, format_type='list'):
if format_type not in ['list', 'plain', 'csv']:
return Response("The requested type is not supported.", 400)
user_id = current_user.id
begin = datetime.combine(datetime.strptime(begin, "%d.%m.%Y"), datetime.min.time())
end = datetime.combine(datetime.strptime(end, "%d.%m.%Y"), datetime.max.time())
begin = datetime.datetime.combine(datetime.datetime.strptime(begin, "%d.%m.%Y"), datetime.datetime.min.time())
end = datetime.datetime.combine(datetime.datetime.strptime(end, "%d.%m.%Y"), datetime.datetime.max.time())
app.logger.debug(begin)
app.logger.debug(end)
gps_works = GPSWork.query\
.filter_by(user_id=user_id)\
.filter(GPSWork.datetime >= begin)\
.filter(GPSWork.datetime <= end)\
.order_by(GPSWork.datetime).all()
app.logger.debug(gps_works)
aggregated_data = aggregat_data(gps_works)
# return plain
if format_type == 'list':
......@@ -159,24 +173,31 @@ def login():
return render_template('login.html', form=form)
@app.route("/<string:username>/<string:date>", methods=['POST'])
def index(username, date):
key = request.form['key', None]
if not key:
return Response('Missing key.', 400)
@app.route("/data", methods=['POST'])
def data():
key = request.form.get('key', None)
username = request.form.get('username', None)
date = request.form.get('date', None)
if not key or not username or not date:
app.logger.error(f"[REQUEST] {request.base_url} -> ERROR 400 - Data missing")
return Response('Missing data.', 400)
# check that key is correct
user = User.query.filter_by(username=username).one()
if key != user.key:
app.logger.info(f'[REQUEST] {request.base_url} -> ERROR 403')
return Response('Wrong credentials.', 403)
else:
gps_work = GPSWork()
gps_work.user_id = user
gps_work.datetime = datetime.utcfromtimestamp(int(date))
gps_work.user_id = user.id
dt = datetime.datetime.fromtimestamp(int(date))
app.logger.debug(dt)
gps_work.datetime = dt
app.logger.debug(f'Created GPS-Work {gps_work}')
db.session.add(gps_work)
db.session.commit()
print("New data added.")
app.logger.debug("New data added.")
return Response("OK", 200)
@app.route("/show_user_data")
......@@ -206,31 +227,6 @@ def show_user_data():
output.headers["Content-type"] = "text/csv"
return output
@app.route("/show_data/<int:user_id>")
def show_data(user_id):
format_type = request.args.get('format_type', 'plain')
if format_type not in ['plain', 'csv']:
return Response("The requested type is not supported.", 400)
gps_works = GPSWork.query.filter_by(user_id=user_id).order_by(GPSWork.timestamp).all()
aggregated_data = aggregat_data(gps_works)
# return plain
if format_type == 'plain':
content = f'User_ID | Start | End \n'
for data in aggregated_data:
print_string = f'{data.get("user_id", "df"): <8}|{data.get("start", "df").strftime("%Y-%m-%d %H:%M:%S")}|{data.get("end", "df").strftime("%Y-%m-%d %H:%M:%S")}\n'
content += print_string
return Response(content, mimetype='text/plain')
# return csv
if format_type == 'csv':
content = f'User_ID;Start;End;\n'
for data in aggregated_data:
print_string = f'{data.get("user_id", "df")};{data.get("start", "df").strftime("%Y-%m-%d %H:%M:%S")};{data.get("end", "df").strftime("%Y-%m-%d %H:%M:%S")}\n'
content += print_string
output = make_response(content)
output.headers["Content-Disposition"] = "attachment; filename=export.csv"
output.headers["Content-type"] = "text/csv"
return output
def aggregat_data(data_list):
aggregated_data = []
......@@ -249,12 +245,3 @@ def aggregat_data(data_list):
aggregated_data.append(aggregated_obj)
return aggregated_data
@app.route("/show_data_raw/<int:user_id>")
def show_raw(user_id):
gps_works = GPSWork.query.filter_by(user_id=user_id).order_by(GPSWork.timestamp).all()
content = ''
for gps_work in gps_works:
d = datetime.fromtimestamp(gps_work.timestamp/1000)
content += f'{d}\n'
return Response(content, mimetype='text/plain')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment