diff --git a/.gitignore b/.gitignore index 2a71b46fba5b04b5b228398405dc11074fb2221f..6e87e73443ebee35e218f004d38d002244d4ebbe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /Pipfile.lock /settings.json +/src/storageLeaf.db diff --git a/settings-example.json b/settings-example.json index 53242406cc0f101246ca00badfe5bcb1aeeb5b1c..d8ae21d57fc54bcd92bbc1b9ffda196f76b137af 100644 --- a/settings-example.json +++ b/settings-example.json @@ -6,5 +6,8 @@ "useSSL": false, "keyfile": "", "certfile": "" + }, + "database": { + "databasePath": "storageLeaf.db" } } \ No newline at end of file diff --git a/src/blueprints/Routes.py b/src/blueprints/Routes.py index da3d6d8f6d103376f7039cfd31671f45434c7c09..81dc5d8fffaaf6c85aeed0815035845a93ed341c 100644 --- a/src/blueprints/Routes.py +++ b/src/blueprints/Routes.py @@ -1,4 +1,27 @@ -from flask import Blueprint, render_template +from enum import Enum + +from flask import Blueprint, request, jsonify + +from logic.Database import Database +from logic.RequestValidator import ValidationError, RequestValidator + + +class DeviceParameters(Enum): + SENSORS = 'sensors' + + @staticmethod + def get_values(): + return [m.value for m in DeviceParameters] + + +class SensorParameters(Enum): + NAME = 'name' + TYPE = 'type' + VALUE = 'value' + + @staticmethod + def get_values(): + return [m.value for m in SensorParameters] def construct_blueprint(settings): @@ -8,4 +31,27 @@ def construct_blueprint(settings): def index(): return "Hello World!" + @routes.route('/device/<deviceName>', methods=['POST']) + def postSensorData(deviceName): + try: + parameters = RequestValidator.validate(request, DeviceParameters.get_values()) + sensors = parameters[DeviceParameters.SENSORS.value] + for sensor in sensors: + sensorParams = RequestValidator.validate_parameters(sensor, + SensorParameters.get_values(), + f'sensor "{sensor}"') + database = Database(settings['database']['databasePath']) + database.add_device_if_not_exists(deviceName) + device = database.get_device(deviceName) + database.add_or_update_sensor(device, + sensorParams['name'], + sensorParams['type'], + sensorParams['value']) + return jsonify(database.get_device("0182")) + + except ValidationError as e: + return e.response, 400 + + return "" + return routes diff --git a/src/logic/Database.py b/src/logic/Database.py new file mode 100644 index 0000000000000000000000000000000000000000..6774059723cb2646983bd352c6efa82cc33c89d0 --- /dev/null +++ b/src/logic/Database.py @@ -0,0 +1,79 @@ +import sqlite3 +from typing import Tuple + +from TheCodeLabs_BaseUtils import DefaultLogger + +from logic import Constants + +LOGGER = DefaultLogger().create_logger_if_not_exists(Constants.APP_NAME) + + +class Database: + TABLE_DEVICE = 'device' + TABLE_SENSOR = 'sensor' + + def __init__(self, databasePath): + self._databasePath = databasePath + self.__create_database() + + def __create_database(self): + LOGGER.debug('Creating database tables...') + with self.__get_connection() as connection: + connection.execute(f'''CREATE TABLE IF NOT EXISTS {self.TABLE_DEVICE} ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL)''') + connection.execute(f'''CREATE TABLE IF NOT EXISTS sensor ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + device_id INTEGER, + name TEXT NOT NULL, + type TEXT NOT NULL, + value TEXT NOT NULL)''') + + def __get_connection(self): + return sqlite3.connect(self._databasePath) + + def get_all_devices(self): + with self.__get_connection() as connection: + cursor = connection.execute(f'SELECT * FROM {self.TABLE_DEVICE} ORDER BY name') + return cursor.fetchall() + + def get_device(self, deviceName: str): + with self.__get_connection() as connection: + cursor = connection.execute(f'SELECT * FROM {self.TABLE_DEVICE} WHERE name = "{deviceName}"') + return cursor.fetchone() + + def add_device_if_not_exists(self, deviceName: str): + if self.get_device(deviceName): + LOGGER.debug(f'Device "{deviceName}" already exists') + return + + with self.__get_connection() as connection: + LOGGER.debug(f'Inserting new device "{deviceName}"') + connection.execute(f'INSERT INTO {self.TABLE_DEVICE}(name) VALUES(?)', (deviceName,)) + + def get_all_sensors(self): + with self.__get_connection() as connection: + cursor = connection.execute(f'SELECT * FROM {self.TABLE_SENSOR} ORDER BY device_id, name') + return cursor.fetchall() + + def get_sensor(self, deviceName: str, name: str): + device = self.get_device(deviceName) + if not device: + return None + + with self.__get_connection() as connection: + cursor = connection.execute(f'SELECT * FROM {self.TABLE_SENSOR} WHERE device_id = ? AND name = ?', + (device[0], name)) + return cursor.fetchone() + + def add_or_update_sensor(self, device: Tuple[int, str], name: str, sensorType: str, value: str): + sensor = self.get_sensor(device[1], name) + with self.__get_connection() as connection: + if sensor: + LOGGER.debug(f'Updating sensor "{name}" for device "{device[1]}" (type: "{sensorType}", value: "{value}")') + connection.execute(f'UPDATE {self.TABLE_SENSOR} SET value = ? WHERE device_id = ? AND name = ?', + (value, device[0], name)) + else: + LOGGER.debug(f'Inserting new sensor "{name}" for device "{device[1]}" (type: "{sensorType}", value: "{value}")') + connection.execute(f'INSERT INTO {self.TABLE_SENSOR}(name, device_id, type, value) VALUES(?, ?, ?, ?)', + (name, device[0], sensorType, value)) diff --git a/src/logic/RequestValidator.py b/src/logic/RequestValidator.py new file mode 100644 index 0000000000000000000000000000000000000000..5303324d6343de88d613d5ae0a27110dd90c86e9 --- /dev/null +++ b/src/logic/RequestValidator.py @@ -0,0 +1,29 @@ +from typing import Dict, List + +from flask import jsonify + + +class ValidationError(Exception): + def __init__(self, message): + super().__init__(message) + self.response = jsonify({'success': False, + 'msg': message}) + + +class RequestValidator: + @staticmethod + def validate(request, parameters: List[str]) -> Dict: + if not request.is_json: + raise ValidationError('Missing JSON in request') + + return RequestValidator.validate_parameters(request.json, parameters, 'request') + + @staticmethod + def validate_parameters(data: Dict, parameters: List[str], itemName: str) -> Dict: + result = {} + for param in parameters: + value = data.get(param, None) + if value is None: + raise ValidationError(f'Missing parameter "{param}" for {itemName}') + result[param] = value + return result