added rest-API, sqlalchemy and nice structure

This commit is contained in:
2019-03-06 16:36:57 +01:00
commit 38ab1578b0
9 changed files with 422 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
__pycache__/

15
Pipfile Normal file
View File

@@ -0,0 +1,15 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
flask = "*"
flask-httpauth = "*"
flask-restplus-patched = "*"
flask-sqlalchemy = "*"
[dev-packages]
[requires]
python_version = "3.7"

203
Pipfile.lock generated Normal file
View File

@@ -0,0 +1,203 @@
{
"_meta": {
"hash": {
"sha256": "b3e3f1a3dbe6801a4a969a2b5c9b73167ea078c1916db723fdc282bc9af814a7"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"aniso8601": {
"hashes": [
"sha256:29ad6be3828ab6ac2a31fd2876fd84477cde11890ffca7e8a9434aad5d4acec8",
"sha256:a5c7595bb65d3919a9944a759d907b57c4d050abaa0e5cf845e84c26cdfd1218"
],
"version": "==5.1.0"
},
"apispec": {
"hashes": [
"sha256:57a7b81fd19fff0663a7e5ffd196eaea79b5364151ed2b65533be36d55e0229c",
"sha256:b45def53903516e67e8584ee41f34bc60c3e4acace6892b69340293ea20f3caa"
],
"version": "==1.0.0"
},
"attrs": {
"hashes": [
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
],
"version": "==19.1.0"
},
"click": {
"hashes": [
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
],
"version": "==7.0"
},
"flask": {
"hashes": [
"sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
"sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
],
"index": "pypi",
"version": "==1.0.2"
},
"flask-httpauth": {
"hashes": [
"sha256:c08b69b302f1aa7ecd0db327809132ef6ca9486a36a9174776da146d1a4adc18",
"sha256:f71b7611f385fbdf350e8c430eed17b41c3b2200dc35eae19c1734264b68e31d"
],
"index": "pypi",
"version": "==3.2.4"
},
"flask-marshmallow": {
"hashes": [
"sha256:75c9d80f22af982b1e8ccec109d3b75c14bb5570602ae3705a4ff775badd2816",
"sha256:db7aff4130eb99fd05ab78fd2e2c58843ba0f208899aeb1c14aff9cd98ae8c80"
],
"version": "==0.9.0"
},
"flask-restplus": {
"hashes": [
"sha256:3fad697e1d91dfc13c078abcb86003f438a751c5a4ff41b84c9050199d2eab62",
"sha256:cdc27b5be63f12968a7f762eaa355e68228b0c904b4c96040a314ba7dc6d0e69"
],
"version": "==0.12.1"
},
"flask-restplus-patched": {
"hashes": [
"sha256:36342775f9e0990dfc000dbe61133dfe56f9ef32c9b4c6293ba7f2c128d16efc"
],
"index": "pypi",
"version": "==0.1.10"
},
"flask-sqlalchemy": {
"hashes": [
"sha256:3bc0fac969dd8c0ace01b32060f0c729565293302f0c4269beed154b46bec50b",
"sha256:5971b9852b5888655f11db634e87725a9031e170f37c0ce7851cf83497f56e53"
],
"index": "pypi",
"version": "==2.3.2"
},
"itsdangerous": {
"hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
],
"version": "==1.1.0"
},
"jinja2": {
"hashes": [
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
],
"version": "==2.10"
},
"jsonschema": {
"hashes": [
"sha256:0c0a81564f181de3212efa2d17de1910f8732fa1b71c42266d983cd74304e20d",
"sha256:a5f6559964a3851f59040d3b961de5e68e70971afb88ba519d27e6a039efff1a"
],
"version": "==3.0.1"
},
"markupsafe": {
"hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
],
"version": "==1.1.1"
},
"marshmallow": {
"hashes": [
"sha256:6eeaf1301a5f5942bfe8ab2c2eaf03feb793072b56d5fae563638bddd7bb62e6",
"sha256:f72a206432a3369dd72824564d18d915761e07805c05f00d0dcc7885fac1e385"
],
"version": "==2.18.1"
},
"pyrsistent": {
"hashes": [
"sha256:3ca82748918eb65e2d89f222b702277099aca77e34843c5eb9d52451173970e2"
],
"version": "==0.14.11"
},
"pytz": {
"hashes": [
"sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9",
"sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c"
],
"version": "==2018.9"
},
"relativetimebuilder": {
"hashes": [
"sha256:5cc415b539d18a20e09a600cf7ba7199eda7b365d13aaaf9ffbbaa26cfb8062a",
"sha256:8b11e6fa6d6d4a09c61cfa9dadae4ea640bf10818e0991874d33452c0aeff2d7"
],
"version": "==0.2.0"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
},
"sqlalchemy": {
"hashes": [
"sha256:11ead7047ff3f394ed0d4b62aded6c5d970a9b718e1dc6add9f5e79442cc5b14"
],
"version": "==1.3.0"
},
"webargs": {
"hashes": [
"sha256:10438164b41b81abe45b299eb182580f7bc6bcdbc864b0cbd62845bb6bab424d",
"sha256:3bed01136ea4a7d1468a54f6c3925d133872a83a2144e83a94f484731576bc58",
"sha256:494044344b5673e3624621d0e9d14d5dc01dd05c0b5b8952febc80a4f80181f6"
],
"version": "==5.1.2"
},
"werkzeug": {
"hashes": [
"sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
"sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
],
"version": "==0.14.1"
}
},
"develop": {}
}

17
__init__.py Normal file
View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
import os
from flask import Flask
from flask_httpauth import HTTPTokenAuth, HTTPBasicAuth
from flask_sqlalchemy import SQLAlchemy
from .serve_frontend import fe_bp
from .api import api_bp
jwt_auth = HTTPTokenAuth('Bearer')
basic_auth = HTTPBasicAuth()
app = Flask(__name__)
app.register_blueprint(api_bp)
app.register_blueprint(fe_bp)
db = SQLAlchemy(app)

28
api/__init__.py Normal file
View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from flask import Blueprint
from flask_restplus import Api
api_authorizations = {
'apikey': {
'type': 'apiKey',
'in': 'header',
'name': 'X-API-KEY'
},
'basicAuth': {
'type': 'basic',
'scheme': 'basic'
},
'bearerAuth': {
'type': 'apiKey',
'scheme': 'bearer',
'name': 'Authorization',
'in': 'header'
}
}
api_bp = Blueprint('api', __name__, url_prefix='/api')
api_v1 = Api(api_bp, prefix="/v1", version='0.1', title='Vue Test API',
description='The Vue Test API', doc='/v1/doc/', authorizations=api_authorizations, security='bearerAuth')
from .example_api import *

118
api/example_api.py Normal file
View File

@@ -0,0 +1,118 @@
import logging
from random import *
from flask import jsonify, Blueprint
from flask_restplus import Resource, reqparse
from backend import basic_auth
from backend.api import api_v1, api_bp
@api_bp.route('/random')
def random_number():
"""
:return: a random number
"""
response = {
'randomNumber': randint(1, 100)
}
return jsonify(response)
class HelloWorld(Resource):
"""
This is a test class.
"""
def get(self):
"""
just a test!
:return: Hello: World
"""
print(str(self))
return {'hello': 'world'}
api_v1.add_resource(HelloWorld, '/')
class SensorData_Handler(Resource):
parser = reqparse.RequestParser()
parser.add_argument('values', type=str, required=True, help="sensor data values are required (as JSON list)")
@basic_auth.login_required
def get(self, mac):
sensor = Sensor.get_by_mac_user(mac, g.user)
if not sensor:
return "sensor not found", 404
return jsonify(sensor.get_sensor_data())
# return {'task': 'id: ' + mac}
@multi_auth.login_required
def post(self, mac):
sensor = Sensor.get_by_mac(mac)
if not sensor:
return "sensor not found", 404
print("JSON")
print(request.json)
print(request.json['values'])
args = SensorData_Handler.parser.parse_args()
print("values...")
print(args['values'])
values = json.loads(args['values'])
wasss_app.logger.info("vals: " + str(values) + " (len: " + str(len(values)) + ")")
rough_geo_location = None
try:
rough_geo_location_info = None
ip = ipaddress.ip_address(request.remote_addr)
if request.remote_addr == "127.0.0.1":
ip = ipaddress.ip_address("89.245.37.108")
if isinstance(ip, ipaddress.IPv4Address):
rough_geo_location_info = geoip4.record_by_addr(str(ip))
elif isinstance(ip, ipaddress.IPv6Address):
rough_geo_location_info = geoip6.record_by_addr(str(ip))
if rough_geo_location_info is not None:
rough_geo_location = json.dumps({key: rough_geo_location_info[key] for key in ['country_code',
'country_name',
'postal_code',
'city',
'time_zone',
'latitude',
'longitude']
})
except ValueError:
pass
for values_at_time in values:
wasss_app.logger.info("values_at_time: " + str(values_at_time) + " (len: " + str(len(values_at_time)) + ")")
sensor_data = SensorData(sensor=sensor, rough_geo_location=rough_geo_location)
val_cycle = cycle(values_at_time)
for sensor_data_header in sensor.get_sensor_data_headers():
value = next(val_cycle)
if sensor_data_header == SensorData.HEADER_TIMESTAMP:
sensor_data.timestamp = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S%z")
wasss_app.logger.info(sensor_data.timestamp)
elif sensor_data_header == SensorData.HEADER_VOLTAGE:
sensor_data.voltage = value
elif sensor_data_header == SensorData.HEADER_TEMPERATURE:
sensor_data.temp = value
elif sensor_data_header == SensorData.HEADER_HUMIDITY:
sensor_data.humidity = value
elif sensor_data_header == SensorData.HEADER_PRESSURE:
sensor_data.pressure = value
elif sensor_data_header == SensorData.HEADER_BRIGHTNESS:
sensor_data.brightness = value
elif sensor_data_header == SensorData.HEADER_SPEED:
sensor_data.speed = value
elif sensor_data_header == SensorData.HEADER_MOTION:
sensor_data.motion = value
else:
gen_sen_data = GenericSensorData(sensor=sensor, name=sensor_data_header, value=value)
db.session.add(sensor_data)
db.session.commit()
return jsonify("ok")
api_v1.add_resource(SensorData_Handler, '/sensor/<string:mac>/data')

0
models/__init__.py Normal file
View File

10
models/example_model.py Normal file
View File

@@ -0,0 +1,10 @@
class Sensor(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
mac = db.Column(db.String(32), nullable=False, unique=True, index=True)
uuid = db.Column(db.String(36), nullable=False, unique=True, index=True, default=str(uuid.uuid4()))
tags = db.relationship("SensorTag", secondary=sensor_tag_relations, back_populates="sensors")
rough_geo_location = db.Column(db.String, nullable=True, index=True)
name = db.Column(db.String(128), default="<not set>", nullable=False, index=True, unique=False)
description = db.Column(db.String(4096), nullable=True, unique=False)

30
serve_frontend.py Normal file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
from flask import render_template, send_from_directory, Blueprint
fe_path = os.path.join(os.getcwd(), "frontend", "dist")
fe_bp = Blueprint('frontend', __name__, url_prefix='/', template_folder=os.path.join(fe_path, ""))
@fe_bp.route('/js/<path:path>')
def send_js(path):
return send_from_directory(os.path.join(fe_path, "js"), path)
@fe_bp.route('/css/<path:path>')
def send_css(path):
return send_from_directory(os.path.join(fe_path, "css"), path)
@fe_bp.route('/img/<path:path>')
def send_img(path):
return send_from_directory(os.path.join(fe_path, "img"), path)
@fe_bp.route('/', defaults={'path': ''})
@fe_bp.route('/<path:path>')
def catch_all(path):
return render_template("index.html")