Skip to content
Snippets Groups Projects
Commit 716996da authored by David Huss's avatar David Huss :speech_balloon:
Browse files

Integrate config, split get/post

parent e4697c8e
No related branches found
No related tags found
No related merge requests found
#! /bin/bash
## Run dev-server
To make a development server run on localhost:
```python3
export FLASK_APP=stechuhr_server/server.py
$HOME/.poetry/bin/poetry run flask run
export FLASK_ENV=development
poetry run flask run
```
......@@ -14,17 +14,35 @@ from .config import initialize_config
APPLICATION_NAME = "stechuhr-server"
DEFAULT_CONFIG = """
[server]
address = "127.0.0.1"
port = 80
timeout = 5
[application]
ignore_get_requests = false
[database]
# sqlite db path, use ":memory:" for RAM db
path = "visitors.db"
# minimum and maximum lengths
min_id_length = 6
max_id_length = 24
min_entrance_length = 1
max_entrance_length = 128
min_place_length = 1
max_place_length = 128
[log]
level = "INFO"
format = "[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
"""
# Initialize the configuration (create a default one if needed)
config = initialize_config(APPLICATION_NAME, DEFAULT_CONFIG)
dictConfig({
'version': 1,
'formatters': {'default': {
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
'format': config["log"]["format"],
}},
'handlers': {'wsgi': {
'class': 'logging.StreamHandler',
......@@ -32,50 +50,85 @@ dictConfig({
'formatter': 'default'
}},
'root': {
'level': 'INFO',
'level': config["log"]["level"],
'handlers': ['wsgi']
}
})
app = Flask(APPLICATION_NAME)
conn = sqlite3.connect('test.db')
conn = sqlite3.connect(config["database"]["path"])
cursor = conn.cursor()
sqlite_create_table_query = '''CREATE TABLE visitors (
time timestamp PRIMARY KEY,
movement_id TEXT PRIMARY KEY,
time timestamp NOT NULL,
place TEXT NOT NULL,
entrance TEXT NOT NULL,
direction TEXT NOT NULL,
id TEXT NOT NULL);'''
# Try to construct the new table. If it exists already, do nothing
try:
app.logger.info('Constructing table for visitors')
cursor.execute(sqlite_create_table_query)
except sqlite3.OperationalError:
app.logger.info('Constructed table for visitors')
except sqlite3.OperationalError as e:
app.logger.warning(e)
pass
conn.close()
def register_movement(data: dict, conn, cursor):
"""
Construct and store a new movment in the database
"""
data["time"] = dt.datetime.now()
data = construct_movement_id(data)
sqlite_insert_with_param = """INSERT INTO 'visitors'
('id', 'place', 'direction', 'time')
VALUES (?, ?, ?, ?);"""
data_tuple = (data["id"], data["place"], data["direction"], data["time"])
('movement_id', 'id', 'place', 'entrance', 'direction', 'time')
VALUES (?, ?, ?, ?, ?, ?);"""
data_tuple = (data["movement_id"], data["id"], data["place"], data["entrance"], data["direction"], data["time"])
cursor.execute(sqlite_insert_with_param, data_tuple)
conn.commit()
def construct_movement_id(data: dict) -> dict:
"""
Generate and set the "movement_id" for a given data dict
"""
if not "time" in data.keys():
raise ValueError("No \"time\" key set yet. run construct_movement_id only after setting time")
time = data["time"].isoformat()
movement_id = "{}--{}-{}/{}/{}".format(time, data["id"], data["place"], data["entrance"], data["direction"])
data["movement_id"] = movement_id
return data
def get_records(conn, cursor):
sqlite_select_query = """SELECT place, direction, time, id from visitors where time = ?"""
"""
Get all existing movement records
"""
sqlite_select_query = """SELECT place, entrance, direction, time, id from visitors where movement_id = ?"""
try:
cursor.execute(sqlite_select_query, (1,))
records = cursor.fetchall()
visitors = [{"place":r[0], "direction":r[1], "time":r[2], "id":r[3]} for r in records]
visitors = [{"place":r[0], "entrance":r[1], "direction":r[2], "time":r[3], "id":r[4]} for r in records]
except sqlite3.OperationalError as e:
# If there is no table or no records, just return an empty list
app.logger.info('Couldn\'t retrieve visitor records: {}'.format(e))
visitors = []
return visitors
def is_valid_data(data: dict) -> bool:
"""
Check if the body data of the request is valid
"""
expected_keys = ["place", "direction", "id"]
expected_keys = ["place", "entrance", "direction", "id"]
expected_directions = ["in", "out"]
max_entrance_length = 128
max_place_length = 128
max_id_length = 128
has_all_keys = all([k in data.keys() for k in expected_keys ])
......@@ -92,7 +145,12 @@ def is_valid_data(data: dict) -> bool:
# Basic length check for place
if len(data["place"]) >= max_place_length:
app.logger.info('400, JSON "place"-key was too long: was {} should be <= {}'.format(len(data["place"]), max_place_length))
app.logger.info('400, JSON "place"-key was too long: was {} should be <= {}'.format(len(data["place"]), max_entrance_length))
return False
# Basic length check for entrance
if len(data["entrance"]) >= max_entrance_length:
app.logger.info('400, JSON "entrance"-key was too long: was {} should be <= {}'.format(len(data["entrance"]), max_entrance_length))
return False
# Basic length check for ID
......@@ -104,11 +162,9 @@ def is_valid_data(data: dict) -> bool:
return True
@app.route('/', methods = ['GET', 'POST'])
def root():
conn = sqlite3.connect('test.db')
cursor = conn.cursor()
if request.method == 'POST':
# This gets run for each request
@app.route('/', methods = ['POST'])
def post():
if not request.data:
# Missing data body, reject
app.logger.info('400, Missing data body')
......@@ -120,7 +176,9 @@ def root():
# Get JSON
data = request.json
if is_valid_data(data):
# Everything went ok
# Valid data, open a db connection
conn = sqlite3.connect(config["database"]["path"])
cursor = conn.cursor()
register_movement(data, conn, cursor)
conn.close()
app.logger.info('200, OK')
......@@ -129,24 +187,19 @@ def root():
# Malformed data, reject
app.logger.info('400, Malformed JSON-Body')
return "", 400
# This gets run for each request
@app.route('/', methods = ['GET'])
def get():
if config["application"]["ignore_get_requests"]:
app.logger.info('501, Get Request Ignored')
return "", 501
else:
# Get Request, not implemented
conn = sqlite3.connect(config["database"]["path"])
cursor = conn.cursor()
visitors = get_records(conn, cursor)
visitors = "\n".join([str(v) for v in visitors])
conn.close()
app.logger.info('200, Get visitors: {}'.format(visitors))
# app.logger.info('501, Get Request not implemented')
return visitors, 200
\ No newline at end of file
def main():
# Initialize the configuration (create a default one if needed)
config = initialize_config(APPLICATION_NAME, DEFAULT_CONFIG)
if (__name__ == '__main__'):
main()
\ No newline at end of file
#! /bin/bash
curl --header "Content-Type: application/json" --request POST --data '{"place":"Haupteingang", "direction":"in","id":"543GFDHGfddf455"}' http://localhost:5000/
curl --header "Content-Type: application/json" --request POST --data '{"place":"lerchenfeld", "entrance":"Haupteingang", "direction":"in", "id":"543GFDHGfddf455"}' http://localhost:5000/
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment