Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
S
stechuhr-server
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
pandemic_Response
stechuhr-server
Commits
0a2f0151
Commit
0a2f0151
authored
4 years ago
by
David Huss
Browse files
Options
Downloads
Patches
Plain Diff
Refactor, comments, pattern check
parent
716996da
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
stechuhr_server/server.py
+66
-18
66 additions, 18 deletions
stechuhr_server/server.py
test.sh
+1
-1
1 addition, 1 deletion
test.sh
with
67 additions
and
19 deletions
stechuhr_server/server.py
+
66
−
18
View file @
0a2f0151
#!/usr/bin/env python
#!/usr/bin/env python
#-*- coding: utf-8 -*-
#-*- coding: utf-8 -*-
import
re
import
datetime
as
dt
import
datetime
as
dt
import
sqlite3
import
sqlite3
from
flask
import
Flask
,
request
from
flask
import
Flask
,
request
from
logging.config
import
dictConfig
from
logging.config
import
dictConfig
from
.config
import
initialize_config
from
.config
import
initialize_config
...
@@ -21,9 +21,13 @@ ignore_get_requests = false
...
@@ -21,9 +21,13 @@ ignore_get_requests = false
# sqlite db path, use
"
:memory:
"
for RAM db
# sqlite db path, use
"
:memory:
"
for RAM db
path =
"
visitors.db
"
path =
"
visitors.db
"
# minimum and maximum lengths
# A list of possible regex patterns for the id (logical OR!)
min_id_length = 6
id_patterns = [
max_id_length = 24
"
[A-z0-9]{24}
"
,
"
[A-Z0-9]{6,8}
"
,
]
# minimum and maximum lengths for the received strings
min_entrance_length = 1
min_entrance_length = 1
max_entrance_length = 128
max_entrance_length = 128
min_place_length = 1
min_place_length = 1
...
@@ -38,7 +42,11 @@ format = "[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
...
@@ -38,7 +42,11 @@ format = "[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
# Initialize the configuration (create a default one if needed)
# Initialize the configuration (create a default one if needed)
config
=
initialize_config
(
APPLICATION_NAME
,
DEFAULT_CONFIG
)
config
=
initialize_config
(
APPLICATION_NAME
,
DEFAULT_CONFIG
)
# Compile the patterns for the ids once at startup
config
[
"
database
"
][
"
id_patterns
"
]
=
[
re
.
compile
(
p
)
for
p
in
config
[
"
database
"
][
"
id_patterns
"
]]
# Config for the logger, there should be no need to make
# manual changes here
dictConfig
({
dictConfig
({
'
version
'
:
1
,
'
version
'
:
1
,
'
formatters
'
:
{
'
default
'
:
{
'
formatters
'
:
{
'
default
'
:
{
...
@@ -56,7 +64,11 @@ dictConfig({
...
@@ -56,7 +64,11 @@ dictConfig({
})
})
# Initialization
app
=
Flask
(
APPLICATION_NAME
)
app
=
Flask
(
APPLICATION_NAME
)
# Create an initial connection and create the needed tables if they
# don't exist yet.
conn
=
sqlite3
.
connect
(
config
[
"
database
"
][
"
path
"
])
conn
=
sqlite3
.
connect
(
config
[
"
database
"
][
"
path
"
])
cursor
=
conn
.
cursor
()
cursor
=
conn
.
cursor
()
sqlite_create_table_query
=
'''
CREATE TABLE visitors (
sqlite_create_table_query
=
'''
CREATE TABLE visitors (
...
@@ -71,7 +83,6 @@ sqlite_create_table_query = '''CREATE TABLE visitors (
...
@@ -71,7 +83,6 @@ sqlite_create_table_query = '''CREATE TABLE visitors (
try
:
try
:
app
.
logger
.
info
(
'
Constructing table for visitors
'
)
app
.
logger
.
info
(
'
Constructing table for visitors
'
)
cursor
.
execute
(
sqlite_create_table_query
)
cursor
.
execute
(
sqlite_create_table_query
)
app
.
logger
.
info
(
'
Constructed table for visitors
'
)
except
sqlite3
.
OperationalError
as
e
:
except
sqlite3
.
OperationalError
as
e
:
app
.
logger
.
warning
(
e
)
app
.
logger
.
warning
(
e
)
pass
pass
...
@@ -82,12 +93,21 @@ def register_movement(data: dict, conn, cursor):
...
@@ -82,12 +93,21 @@ def register_movement(data: dict, conn, cursor):
"""
"""
Construct and store a new movment in the database
Construct and store a new movment in the database
"""
"""
# Create a timestamp
data
[
"
time
"
]
=
dt
.
datetime
.
now
()
data
[
"
time
"
]
=
dt
.
datetime
.
now
()
# Construct an ID for the current movement
data
=
construct_movement_id
(
data
)
data
=
construct_movement_id
(
data
)
# 1. Draft SQL-Statement
sqlite_insert_with_param
=
"""
INSERT INTO
'
visitors
'
sqlite_insert_with_param
=
"""
INSERT INTO
'
visitors
'
(
'
movement_id
'
,
'
id
'
,
'
place
'
,
'
entrance
'
,
'
direction
'
,
'
time
'
)
(
'
movement_id
'
,
'
id
'
,
'
place
'
,
'
entrance
'
,
'
direction
'
,
'
time
'
)
VALUES (?, ?, ?, ?, ?, ?);
"""
VALUES (?, ?, ?, ?, ?, ?);
"""
# 2. Draft parameters
data_tuple
=
(
data
[
"
movement_id
"
],
data
[
"
id
"
],
data
[
"
place
"
],
data
[
"
entrance
"
],
data
[
"
direction
"
],
data
[
"
time
"
])
data_tuple
=
(
data
[
"
movement_id
"
],
data
[
"
id
"
],
data
[
"
place
"
],
data
[
"
entrance
"
],
data
[
"
direction
"
],
data
[
"
time
"
])
# Execute both and commit to db
cursor
.
execute
(
sqlite_insert_with_param
,
data_tuple
)
cursor
.
execute
(
sqlite_insert_with_param
,
data_tuple
)
conn
.
commit
()
conn
.
commit
()
...
@@ -95,11 +115,19 @@ def register_movement(data: dict, conn, cursor):
...
@@ -95,11 +115,19 @@ def register_movement(data: dict, conn, cursor):
def
construct_movement_id
(
data
:
dict
)
->
dict
:
def
construct_movement_id
(
data
:
dict
)
->
dict
:
"""
"""
Generate and set the
"
movement_id
"
for a given data dict
Generate and set the
"
movement_id
"
for a given data dict
Needs to be collision proof for the db.
Looks like: 2020-11-12T07:10:49.81--543GFDHGfddf455-lerchenfeld_Haupteingang/in
"""
"""
# If data["time"] is not set, raise Error
if
not
"
time
"
in
data
.
keys
():
if
not
"
time
"
in
data
.
keys
():
raise
ValueError
(
"
No
\"
time
\"
key set yet. run construct_movement_id only after setting time
"
)
raise
ValueError
(
"
No
\"
time
\"
key set yet. run construct_movement_id only after setting time
"
)
# Use isoformat time like 2020-11-12T07:06:08.595770 ...
time
=
data
[
"
time
"
].
isoformat
()
time
=
data
[
"
time
"
].
isoformat
()
movement_id
=
"
{}--{}-{}/{}/{}
"
.
format
(
time
,
data
[
"
id
"
],
data
[
"
place
"
],
data
[
"
entrance
"
],
data
[
"
direction
"
])
# ... but limit the precision of sub seconds to two places
time
=
"
{}.{}
"
.
format
(
time
.
rsplit
(
"
.
"
)[
0
],
time
.
rsplit
(
"
.
"
)[
1
][:
2
])
# Construct final movement ID.
movement_id
=
"
{}--{}-{}_{}/{}
"
.
format
(
time
,
data
[
"
id
"
],
data
[
"
place
"
],
data
[
"
entrance
"
],
data
[
"
direction
"
])
data
[
"
movement_id
"
]
=
movement_id
data
[
"
movement_id
"
]
=
movement_id
return
data
return
data
...
@@ -108,9 +136,11 @@ def get_records(conn, cursor):
...
@@ -108,9 +136,11 @@ def get_records(conn, cursor):
"""
"""
Get all existing movement records
Get all existing movement records
"""
"""
sqlite_select_query
=
"""
SELECT place, entrance, direction, time, id from visitors where movement_id = ?
"""
# Draft select query
sqlite_select_query
=
"""
SELECT place, entrance, direction, time, id from visitors
"""
try
:
try
:
cursor
.
execute
(
sqlite_select_query
,
(
1
,))
# Execute query and fill visitor with results
cursor
.
execute
(
sqlite_select_query
)
records
=
cursor
.
fetchall
()
records
=
cursor
.
fetchall
()
visitors
=
[{
"
place
"
:
r
[
0
],
"
entrance
"
:
r
[
1
],
"
direction
"
:
r
[
2
],
"
time
"
:
r
[
3
],
"
id
"
:
r
[
4
]}
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
:
except
sqlite3
.
OperationalError
as
e
:
...
@@ -120,8 +150,6 @@ def get_records(conn, cursor):
...
@@ -120,8 +150,6 @@ def get_records(conn, cursor):
return
visitors
return
visitors
def
is_valid_data
(
data
:
dict
)
->
bool
:
def
is_valid_data
(
data
:
dict
)
->
bool
:
"""
"""
Check if the body data of the request is valid
Check if the body data of the request is valid
...
@@ -144,21 +172,41 @@ def is_valid_data(data: dict) -> bool:
...
@@ -144,21 +172,41 @@ def is_valid_data(data: dict) -> bool:
return
False
return
False
# Basic length check for place
# Basic length check for place
if
len
(
data
[
"
place
"
])
>=
max_place_length
:
if
not
length_check
(
data
,
"
place
"
,
config
[
"
database
"
][
"
min_place_length
"
],
config
[
"
database
"
][
"
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
return
False
# Basic length check for entrance
# Basic length check for entrance
if
len
(
data
[
"
entrance
"
])
>=
max_entrance_length
:
if
not
length_check
(
data
,
"
entrance
"
,
config
[
"
database
"
][
"
min_entrance_length
"
],
config
[
"
database
"
][
"
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
return
False
#
Basic length check f
or ID
#
Match regex patterns with visit
or ID
if
len
(
data
[
"
id
"
])
>=
max_id_length
:
if
not
id_pattern_check
(
data
[
"
id
"
])
:
app
.
logger
.
info
(
'
400,
JSON
"
id
"
-
key was too long: was {} should be <=
{}
'
.
format
(
len
(
data
[
"
id
"
])
,
max_id_length
)
)
app
.
logger
.
info
(
'
JSON
"
id
"
-
value didn
\'
t match any pattern:
{}
'
.
format
(
data
[
"
id
"
]))
return
False
return
False
# todo: Add more concise checks for the ID
return
True
def
id_pattern_check
(
visitor_id
:
str
)
->
bool
:
"""
Returns True if any of the patterns from the config matches.
Returns False if none of the patterns matches.
"""
return
any
([
re
.
match
(
p
,
visitor_id
)
for
p
in
config
[
"
database
"
][
"
id_patterns
"
]])
def
length_check
(
data
:
dict
,
key
:
str
,
minimum
:
int
,
maximum
:
int
)
->
bool
:
"""
Returns True if the given data[key] value length is within
minimum and maximum. False and a log message otherwise.
"""
# Basic length check for ID (max)
if
len
(
data
[
key
])
>=
maximum
:
app
.
logger
.
info
(
'
JSON
"
{}
"
-value was too long: was {} should be <= {}
'
.
format
(
key
,
len
(
data
[
key
]),
maximum
))
return
False
if
len
(
data
[
key
])
<=
minimum
:
app
.
logger
.
info
(
'
JSON
"
{}
"
-value was too short: was {} should be >= {}
'
.
format
(
key
,
len
(
data
[
key
]),
minimum
))
return
False
return
True
return
True
...
...
This diff is collapsed.
Click to expand it.
test.sh
+
1
−
1
View file @
0a2f0151
#! /bin/bash
#! /bin/bash
curl
--header
"Content-Type: application/json"
--request
POST
--data
'{"place":"lerchenfeld", "entrance":"
H
aupteingang", "direction":"in", "id":"543GFDHGfddf455"}'
http://localhost:5000/
curl
--header
"Content-Type: application/json"
--request
POST
--data
'{"place":"lerchenfeld", "entrance":"
h
aupteingang", "direction":"in", "id":"543GFDHGfddf455"}'
http://localhost:5000/
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment