Merged in improvement/packages-setup (pull request #1)
Improvement/packages setup Approved-by: Velid Aljic <velid.aljic@gmail.com>master
commit
27fbe9d886
|
@ -1,117 +0,0 @@
|
|||
from app.core import bcrypt
|
||||
from flask import Blueprint
|
||||
from .models import Account, Role
|
||||
|
||||
accounts_bp = Blueprint('accounts', __name__)
|
||||
|
||||
|
||||
def create_account(username, email, password):
|
||||
"""
|
||||
Tries to create account with given parameters. Raises error on failure
|
||||
|
||||
:param username: Desired username for Account
|
||||
:param email: Desired email for Account
|
||||
:param password: Desired password for Account
|
||||
:type username: string
|
||||
:type email: string
|
||||
:type password: string
|
||||
:returns: True if account is successfully created
|
||||
:rtype: Boolean
|
||||
:raises: ValueError if account already exists
|
||||
"""
|
||||
if not Account.exists_with_any_of(username=username, email=email):
|
||||
pw_hash = bcrypt.generate_password_hash(password).decode('utf-8')
|
||||
account = Account(username, pw_hash, email)
|
||||
account.save()
|
||||
return True
|
||||
|
||||
raise ValueError("Account with given parameters already exists")
|
||||
|
||||
|
||||
def update_account_role(account_id, role_id):
|
||||
"""
|
||||
Tries to update account role
|
||||
|
||||
:param account_id: Target account id
|
||||
:param role_id: New role role_id
|
||||
:type account_id: int
|
||||
:type role_id: int
|
||||
:returns: True if role is updated successfully
|
||||
:rtype: Boolean
|
||||
"""
|
||||
acc = Account.get(id=account_id)
|
||||
acc.role_id = role_id
|
||||
acc.save()
|
||||
|
||||
|
||||
def create_role(display_name, permissions):
|
||||
"""
|
||||
Tries to create role
|
||||
|
||||
:param display_name: Name of role - display only
|
||||
:param permissions: List of strings - permissions that this role has
|
||||
:type display_name: String
|
||||
:type permissions: List of String
|
||||
:returns: True if role is successfully created
|
||||
:rtype: Boolean
|
||||
:raises: ValueError if role already exists
|
||||
"""
|
||||
role = Role(display_name, permissions)
|
||||
role.save()
|
||||
|
||||
|
||||
def get_role(role_id):
|
||||
"""
|
||||
Tries to get role
|
||||
|
||||
:param role_id: Id of role
|
||||
:type role_id: int
|
||||
:returns: Role if found
|
||||
:rtype: Role
|
||||
"""
|
||||
return Role.get(role_id)
|
||||
|
||||
|
||||
def get_all_roles():
|
||||
"""
|
||||
Gets all roles
|
||||
|
||||
:returns: Role list if found
|
||||
:rtype: List of Roles
|
||||
"""
|
||||
return Role.get_all()
|
||||
|
||||
|
||||
def create_token(username, password):
|
||||
"""
|
||||
Tries to create token for account with given parameters.
|
||||
Raises error on failure
|
||||
|
||||
:param username: username of Account
|
||||
:param password: password of Account
|
||||
:type username: string
|
||||
:type password: string
|
||||
:returns: created token
|
||||
:rtype: string
|
||||
:raises: ValueError if credentials are invalid or account does not exist
|
||||
"""
|
||||
if not Account.exists(username=username):
|
||||
raise ValueError("Invalid credentials")
|
||||
|
||||
account = Account.get(username=username)
|
||||
if not bcrypt.check_password_hash(account.password, password):
|
||||
raise ValueError("Invalid credentials")
|
||||
|
||||
return account.create_auth_token()
|
||||
|
||||
|
||||
def validate_token(token):
|
||||
"""
|
||||
Validates token and returns associated account
|
||||
|
||||
:param token: auth token to validate
|
||||
:type token: string
|
||||
:returns: created token
|
||||
:rtype: Account
|
||||
"""
|
||||
return Account.validate_token(token)
|
|
@ -0,0 +1,114 @@
|
|||
from app.core import bcrypt
|
||||
from .models import Account, Role
|
||||
|
||||
|
||||
def create_account(username, email, password):
|
||||
"""
|
||||
Tries to create account with given parameters. Raises error on failure
|
||||
|
||||
:param username: Desired username for Account
|
||||
:param email: Desired email for Account
|
||||
:param password: Desired password for Account
|
||||
:type username: string
|
||||
:type email: string
|
||||
:type password: string
|
||||
:returns: True if account is successfully created
|
||||
:rtype: Boolean
|
||||
:raises: ValueError if account already exists
|
||||
"""
|
||||
if not Account.exists_with_any_of(username=username, email=email):
|
||||
pw_hash = bcrypt.generate_password_hash(password).decode('utf-8')
|
||||
account = Account(username, pw_hash, email)
|
||||
account.save()
|
||||
return True
|
||||
|
||||
raise ValueError("Account with given parameters already exists")
|
||||
|
||||
|
||||
def update_account_role(account_id, role_id):
|
||||
"""
|
||||
Tries to update account role
|
||||
|
||||
:param account_id: Target account id
|
||||
:param role_id: New role role_id
|
||||
:type account_id: int
|
||||
:type role_id: int
|
||||
:returns: True if role is updated successfully
|
||||
:rtype: Boolean
|
||||
"""
|
||||
acc = Account.get(id=account_id)
|
||||
acc.role_id = role_id
|
||||
acc.save()
|
||||
|
||||
|
||||
def create_role(display_name, permissions):
|
||||
"""
|
||||
Tries to create role
|
||||
|
||||
:param display_name: Name of role - display only
|
||||
:param permissions: List of strings - permissions that this role has
|
||||
:type display_name: String
|
||||
:type permissions: List of String
|
||||
:returns: True if role is successfully created
|
||||
:rtype: Boolean
|
||||
:raises: ValueError if role already exists
|
||||
"""
|
||||
role = Role(display_name, permissions)
|
||||
role.save()
|
||||
|
||||
|
||||
def get_role(role_id):
|
||||
"""
|
||||
Tries to get role
|
||||
|
||||
:param role_id: Id of role
|
||||
:type role_id: int
|
||||
:returns: Role if found
|
||||
:rtype: Role
|
||||
"""
|
||||
return Role.get(role_id)
|
||||
|
||||
|
||||
def get_all_roles():
|
||||
"""
|
||||
Gets all roles
|
||||
|
||||
:returns: Role list if found
|
||||
:rtype: List of Roles
|
||||
"""
|
||||
return Role.get_all()
|
||||
|
||||
|
||||
def create_token(username, password):
|
||||
"""
|
||||
Tries to create token for account with given parameters.
|
||||
Raises error on failure
|
||||
|
||||
:param username: username of Account
|
||||
:param password: password of Account
|
||||
:type username: string
|
||||
:type password: string
|
||||
:returns: created token
|
||||
:rtype: string
|
||||
:raises: ValueError if credentials are invalid or account does not exist
|
||||
"""
|
||||
if not Account.exists(username=username):
|
||||
raise ValueError("Invalid credentials")
|
||||
|
||||
account = Account.get(username=username)
|
||||
if not bcrypt.check_password_hash(account.password, password):
|
||||
raise ValueError("Invalid credentials")
|
||||
|
||||
return account.create_auth_token()
|
||||
|
||||
|
||||
def validate_token(token):
|
||||
"""
|
||||
Validates token and returns associated account
|
||||
|
||||
:param token: auth token to validate
|
||||
:type token: string
|
||||
:returns: created token
|
||||
:rtype: Account
|
||||
"""
|
||||
return Account.validate_token(token)
|
|
@ -0,0 +1,3 @@
|
|||
from flask import Blueprint
|
||||
|
||||
accounts_bp = Blueprint('accounts', __name__)
|
|
@ -1,106 +0,0 @@
|
|||
import sys
|
||||
from flask import Blueprint, request, g
|
||||
from flask_restful import Api, Resource, abort
|
||||
from functools import wraps
|
||||
from marshmallow import ValidationError
|
||||
from app.accounts import validate_token
|
||||
|
||||
|
||||
api_bp = Blueprint('api', __name__)
|
||||
api = Api(api_bp)
|
||||
|
||||
|
||||
def protected(func):
|
||||
@wraps(func)
|
||||
def protected_function(*args, **kwargs):
|
||||
try:
|
||||
token = request.headers['Authorization'] or None
|
||||
|
||||
if not token:
|
||||
abort(401, message='Unauthorized', status='error')
|
||||
|
||||
g.current_account = validate_token(token.replace("Bearer ", ""))
|
||||
if not g.current_account:
|
||||
abort(401, message='Unauthorized', status='error')
|
||||
except Exception:
|
||||
error_type, error_instance, traceback = sys.exc_info()
|
||||
print(str(error_type))
|
||||
print(str(error_instance))
|
||||
abort(401, message='Unauthorized', status='error')
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return protected_function
|
||||
|
||||
|
||||
def requires_permission(permission, action_name='Action'):
|
||||
def requires_permission_decorator(func):
|
||||
@wraps(func)
|
||||
def permission_protected_function(*args, **kwargs):
|
||||
if permission not in g.current_account.role.permissions:
|
||||
abort(403,
|
||||
message=(action_name+' is not allowed'),
|
||||
status='error')
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return permission_protected_function
|
||||
|
||||
return requires_permission_decorator
|
||||
|
||||
|
||||
class ProtectedResource(Resource):
|
||||
method_decorators = [protected]
|
||||
|
||||
|
||||
def add_resources():
|
||||
from .resources.account import (AccountResource,
|
||||
AccountListResource,
|
||||
AccountRoleResource,
|
||||
RoleResource,
|
||||
RolesResource)
|
||||
from .resources.token import TokenResource, ValidateTokenResource
|
||||
from .resources.device import (DeviceResource,
|
||||
DeviceRecordingResource,
|
||||
DeviceListResource,
|
||||
DeviceTypeResource,
|
||||
DeviceTypeListResource,
|
||||
DeviceConfigurationResource)
|
||||
from .resources.dashboard import DashboardResource, DashboardListResource
|
||||
|
||||
api.add_resource(AccountResource, '/v1/accounts/<int:account_id>')
|
||||
api.add_resource(AccountListResource, '/v1/accounts')
|
||||
api.add_resource(AccountRoleResource, '/v1/accounts/<int:account_id>/role')
|
||||
api.add_resource(RoleResource, '/v1/roles/<int:role_id>')
|
||||
api.add_resource(RolesResource, '/v1/roles')
|
||||
api.add_resource(TokenResource, '/v1/token')
|
||||
api.add_resource(ValidateTokenResource, '/v1/token/validate')
|
||||
api.add_resource(DeviceResource, '/v1/devices/<int:device_id>')
|
||||
api.add_resource(DeviceRecordingResource,
|
||||
'/v1/devices/<int:device_id>/recordings')
|
||||
api.add_resource(DeviceListResource, '/v1/devices')
|
||||
api.add_resource(DeviceTypeResource,
|
||||
'/v1/devices/types/<int:device_type_id>')
|
||||
api.add_resource(DeviceTypeListResource, '/v1/devices/types')
|
||||
api.add_resource(DeviceConfigurationResource,
|
||||
'/v1/devices/<int:device_id>/configuration')
|
||||
api.add_resource(DashboardListResource, '/v1/dashboards')
|
||||
api.add_resource(DashboardResource,
|
||||
'/v1/dashboards/<int:dashboard_id>')
|
||||
|
||||
|
||||
add_resources()
|
||||
|
||||
|
||||
@api_bp.errorhandler(ValidationError)
|
||||
@api_bp.errorhandler(422)
|
||||
def handle_validation_error(e):
|
||||
return {'status': 'error', 'message': str(e)}, 422
|
||||
|
||||
|
||||
@api_bp.errorhandler(Exception)
|
||||
def handle_unknown_errors(e):
|
||||
return ({
|
||||
'status': 'failed',
|
||||
'message': 'Unknown error has occurred! ({0})'.format(str(e))
|
||||
}, 500)
|
|
@ -0,0 +1,32 @@
|
|||
import sys
|
||||
from functools import wraps
|
||||
from flask import request, g
|
||||
from flask_restful import Resource, abort
|
||||
from app.accounts.api import validate_token
|
||||
|
||||
|
||||
def protected(func):
|
||||
@wraps(func)
|
||||
def protected_function(*args, **kwargs):
|
||||
try:
|
||||
token = request.headers['Authorization'] or None
|
||||
|
||||
if not token:
|
||||
abort(401, message='Unauthorized', status='error')
|
||||
|
||||
g.current_account = validate_token(token.replace("Bearer ", ""))
|
||||
if not g.current_account:
|
||||
abort(401, message='Unauthorized', status='error')
|
||||
except Exception:
|
||||
error_type, error_instance, traceback = sys.exc_info()
|
||||
print(str(error_type))
|
||||
print(str(error_instance))
|
||||
abort(401, message='Unauthorized', status='error')
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return protected_function
|
||||
|
||||
|
||||
class ProtectedResource(Resource):
|
||||
method_decorators = [protected]
|
|
@ -0,0 +1,60 @@
|
|||
from flask_restful import Api
|
||||
from marshmallow import ValidationError
|
||||
from flask import Blueprint
|
||||
|
||||
|
||||
api_bp = Blueprint('api', __name__)
|
||||
api = Api(api_bp)
|
||||
|
||||
|
||||
def add_resources():
|
||||
from .resources.account import (AccountResource,
|
||||
AccountListResource,
|
||||
AccountRoleResource,
|
||||
RoleResource,
|
||||
RolesResource)
|
||||
from .resources.token import TokenResource, ValidateTokenResource
|
||||
from .resources.device import (DeviceResource,
|
||||
DeviceRecordingResource,
|
||||
DeviceListResource,
|
||||
DeviceTypeResource,
|
||||
DeviceTypeListResource,
|
||||
DeviceConfigurationResource)
|
||||
from .resources.dashboard import DashboardResource, DashboardListResource
|
||||
|
||||
api.add_resource(AccountResource, '/v1/accounts/<int:account_id>')
|
||||
api.add_resource(AccountListResource, '/v1/accounts')
|
||||
api.add_resource(AccountRoleResource, '/v1/accounts/<int:account_id>/role')
|
||||
api.add_resource(RoleResource, '/v1/roles/<int:role_id>')
|
||||
api.add_resource(RolesResource, '/v1/roles')
|
||||
api.add_resource(TokenResource, '/v1/token')
|
||||
api.add_resource(ValidateTokenResource, '/v1/token/validate')
|
||||
api.add_resource(DeviceResource, '/v1/devices/<int:device_id>')
|
||||
api.add_resource(DeviceRecordingResource,
|
||||
'/v1/devices/<int:device_id>/recordings')
|
||||
api.add_resource(DeviceListResource, '/v1/devices')
|
||||
api.add_resource(DeviceTypeResource,
|
||||
'/v1/devices/types/<int:device_type_id>')
|
||||
api.add_resource(DeviceTypeListResource, '/v1/devices/types')
|
||||
api.add_resource(DeviceConfigurationResource,
|
||||
'/v1/devices/<int:device_id>/configuration')
|
||||
api.add_resource(DashboardListResource, '/v1/dashboards')
|
||||
api.add_resource(DashboardResource,
|
||||
'/v1/dashboards/<int:dashboard_id>')
|
||||
|
||||
|
||||
add_resources()
|
||||
|
||||
|
||||
@api_bp.errorhandler(ValidationError)
|
||||
@api_bp.errorhandler(422)
|
||||
def handle_validation_error(e):
|
||||
return {'status': 'error', 'message': str(e)}, 422
|
||||
|
||||
|
||||
@api_bp.errorhandler(Exception)
|
||||
def handle_unknown_errors(e):
|
||||
return ({
|
||||
'status': 'failed',
|
||||
'message': 'Unknown error has occurred! ({0})'.format(str(e))
|
||||
}, 500)
|
|
@ -0,0 +1,32 @@
|
|||
from flask import g
|
||||
from flask_restful import abort
|
||||
from functools import wraps
|
||||
|
||||
|
||||
valid_permissions = [
|
||||
'CREATE_DEVICE_TYPE',
|
||||
'CREATE_ROLE',
|
||||
'ASSIGN_ROLE',
|
||||
'CREATE_DEVICE',
|
||||
'CREATE_DASHBOARD',
|
||||
'READ_DEVICE_TYPES',
|
||||
'READ_ROLES']
|
||||
|
||||
|
||||
def requires_permission(permission, action_name='Action'):
|
||||
if permission not in valid_permissions:
|
||||
raise ValueError('Permission ' + str(permission) + ' does not exist!')
|
||||
|
||||
def requires_permission_decorator(func):
|
||||
@wraps(func)
|
||||
def permission_protected_function(*args, **kwargs):
|
||||
if permission not in g.current_account.role.permissions:
|
||||
abort(403,
|
||||
message=(action_name+' is not allowed'),
|
||||
status='error')
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return permission_protected_function
|
||||
|
||||
return requires_permission_decorator
|
|
@ -3,8 +3,10 @@ from flask import g
|
|||
from marshmallow import Schema, fields
|
||||
from webargs.flaskparser import use_args
|
||||
from flasgger import swag_from
|
||||
import app.accounts as accounts
|
||||
from app.api import ProtectedResource, requires_permission
|
||||
import app.accounts.api as accounts
|
||||
from app.api.auth_protection import ProtectedResource
|
||||
from app.api.permission_protection import (requires_permission,
|
||||
valid_permissions)
|
||||
|
||||
|
||||
class UserSchema(Schema):
|
||||
|
@ -17,11 +19,16 @@ class RoleUpdateSchema(Schema):
|
|||
role_id = fields.Integer(required=True, load_only=True, location='json')
|
||||
|
||||
|
||||
def validate_role_permissions(permissions_list):
|
||||
return set(permissions_list).issubset(valid_permissions)
|
||||
|
||||
|
||||
class RoleSchema(Schema):
|
||||
id = fields.Integer(required=True, location='json')
|
||||
display_name = fields.String(required=True, location='json')
|
||||
permissions = fields.List(fields.String, required=True,
|
||||
location='json', many=True)
|
||||
location='json', many=True,
|
||||
validate=validate_role_permissions)
|
||||
|
||||
|
||||
class RoleWrapperSchema(Schema):
|
||||
|
|
|
@ -3,8 +3,8 @@ from flask_restful import abort
|
|||
from marshmallow import Schema, fields
|
||||
from webargs.flaskparser import use_args
|
||||
from flasgger import swag_from
|
||||
import app.dashboard as dashboard
|
||||
from app.api import ProtectedResource
|
||||
import app.dashboards.api as dashboard
|
||||
from app.api.auth_protection import ProtectedResource
|
||||
|
||||
|
||||
class DashboardSchema(Schema):
|
||||
|
|
|
@ -3,8 +3,8 @@ from marshmallow import Schema, fields
|
|||
from webargs.flaskparser import use_args
|
||||
from flasgger import swag_from
|
||||
from flask import g, request
|
||||
import app.devices as devices
|
||||
from app.api import ProtectedResource
|
||||
import app.devices.api as devices
|
||||
from app.api.auth_protection import ProtectedResource
|
||||
|
||||
|
||||
class DeviceTypeSchema(Schema):
|
||||
|
|
|
@ -2,8 +2,8 @@ from flask_restful import Resource, abort
|
|||
from webargs import fields
|
||||
from webargs.flaskparser import use_args
|
||||
from flasgger import swag_from
|
||||
from app.api import ProtectedResource
|
||||
import app.accounts as accounts
|
||||
from app.api.auth_protection import ProtectedResource
|
||||
import app.accounts.api as accounts
|
||||
|
||||
|
||||
class TokenResource(Resource):
|
||||
|
|
12
app/core.py
12
app/core.py
|
@ -4,7 +4,7 @@ from flask_sqlalchemy import SQLAlchemy
|
|||
from flask_bcrypt import Bcrypt
|
||||
from flasgger import Swagger
|
||||
from flask_cors import CORS
|
||||
from .tasks import celery as celery_configurator
|
||||
from .tasks import celery_configurator
|
||||
|
||||
app = FlaskAPI(__name__, instance_relative_config=True)
|
||||
app.config.from_object('config')
|
||||
|
@ -27,11 +27,11 @@ def setup_blueprints(app):
|
|||
They are exposed as blueprints just for consistency, otherwise
|
||||
they are just simple python packages/modules
|
||||
"""
|
||||
from .accounts import accounts_bp
|
||||
from .devices import devices_bp
|
||||
from .dashboard import dashboard_bp
|
||||
from .api import api_bp
|
||||
from .mqtt import mqtt_bp
|
||||
from .accounts.blueprint import accounts_bp
|
||||
from .devices.blueprint import devices_bp
|
||||
from .dashboards.blueprint import dashboard_bp
|
||||
from .api.blueprint import api_bp
|
||||
from .mqtt.blueprint import mqtt_bp
|
||||
|
||||
app.register_blueprint(accounts_bp)
|
||||
app.register_blueprint(devices_bp)
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
from flask import Blueprint
|
||||
from .models import Dashboard
|
||||
|
||||
dashboard_bp = Blueprint('dashboard', __name__)
|
||||
|
||||
|
||||
# Public interface
|
||||
def create_dashboard(dashboard_data, account_id):
|
|
@ -0,0 +1,3 @@
|
|||
from flask import Blueprint
|
||||
|
||||
dashboard_bp = Blueprint('dashboard', __name__)
|
|
@ -1,206 +0,0 @@
|
|||
import sys
|
||||
from flask import Blueprint
|
||||
from .models import Device, Recording, DeviceAssociation, DeviceType
|
||||
from app.core import app
|
||||
|
||||
devices_bp = Blueprint('devices', __name__)
|
||||
|
||||
|
||||
# Public interface
|
||||
def create_device(name, account_id, device_type=1):
|
||||
"""
|
||||
Tries to create device with given parameters
|
||||
|
||||
:param name: Desired device name
|
||||
:param device_type: Id of desired device type.
|
||||
By default it is 1 (STANDARD)
|
||||
:type name: string
|
||||
:type device_type: int
|
||||
:returns: True if device is successfully created
|
||||
:rtype: Boolean
|
||||
"""
|
||||
device = Device(name, None, device_type)
|
||||
device.save()
|
||||
device_association = DeviceAssociation(device.id, account_id)
|
||||
device_association.save()
|
||||
|
||||
|
||||
def create_device_type(name):
|
||||
"""
|
||||
Tries to create device type with given parameters
|
||||
|
||||
:param name: Desired device type name
|
||||
:type name: string
|
||||
:returns: True if device type is successfully created
|
||||
:rtype: Boolean
|
||||
"""
|
||||
device_type = DeviceType(name)
|
||||
device_type.save()
|
||||
|
||||
|
||||
def set_device_configuration(device_id, configuration_json):
|
||||
"""
|
||||
Tries to update configuration of device with given id
|
||||
|
||||
:param device_id: Id of device to change configuration
|
||||
:param configuration_json: New configuration
|
||||
:type device_id: int
|
||||
:type configuration_json: JSON
|
||||
:rtype: Boolean
|
||||
"""
|
||||
from app.celery_builder import send_config
|
||||
device = Device.get(id=device_id)
|
||||
device.configuration = configuration_json
|
||||
device.save()
|
||||
send_config.delay(device_id, str(configuration_json))
|
||||
|
||||
|
||||
def get_device_configuration(device_id):
|
||||
"""
|
||||
Tries to get configuration for device with given parameters.
|
||||
|
||||
:param device_id: Id of device
|
||||
:type device_id: int
|
||||
:returns: Configuration of given device
|
||||
:rtype: JSON Configuration
|
||||
"""
|
||||
return Device.get(id=device_id).configuration
|
||||
|
||||
|
||||
def get_device_recordings(device_id):
|
||||
"""
|
||||
Tries to get device recording for device with given parameters. Raises
|
||||
error on failure
|
||||
|
||||
:param device_id: Id of device
|
||||
:type device_id: int
|
||||
:returns: List of Recordings for given device
|
||||
:rtype: List of Recording
|
||||
:raises: ValueError if device does not exist
|
||||
"""
|
||||
if not Device.exists(id=device_id):
|
||||
raise ValueError("Device with id %s does not exist" % device_id)
|
||||
|
||||
return Recording.get_many(device_id=device_id)
|
||||
|
||||
|
||||
def get_device_recordings_filtered(device_id, record_type=None,
|
||||
start_date=None, end_date=None):
|
||||
"""
|
||||
Tries to get device recording for device with given parameters. Raises
|
||||
error on failure
|
||||
|
||||
:param device_id: Id of device
|
||||
:param record_type: Type of recording
|
||||
:param start_date: Lower date limit
|
||||
:param end_date: Upper date limit
|
||||
:type device_id: int
|
||||
:type record_type: int
|
||||
:type start_date: Date (string: %d-%m-%Y)
|
||||
:type end_date: Date (string: %d-%m-%Y)
|
||||
:returns: List of Recordings for given filters
|
||||
:rtype: List of Recording
|
||||
:raises: ValueError if device does not exist
|
||||
"""
|
||||
if not Device.exists(id=device_id):
|
||||
raise ValueError("Device with id %s does not exist" % device_id)
|
||||
|
||||
return Recording.get_many_filtered(device_id, record_type,
|
||||
start_date, end_date)
|
||||
|
||||
|
||||
def get_device(device_id):
|
||||
"""
|
||||
Tries to get device with given parameters. Raises error on failure
|
||||
|
||||
:param device_id: Id of device
|
||||
:type device_id: int
|
||||
:returns: Requested device
|
||||
:rtype: Device
|
||||
"""
|
||||
return Device.get(id=device_id)
|
||||
|
||||
|
||||
def can_user_access_device(account_id, device_id):
|
||||
"""
|
||||
Checks if user with given account_id can access device with given device_id
|
||||
|
||||
:param account_id: Id of account
|
||||
:param device_id: Id of device
|
||||
:type account_id: int
|
||||
:type device_id: int
|
||||
:returns: true if device is accessible by this account, false otherwise
|
||||
:rtype: Boolean
|
||||
"""
|
||||
return len(DeviceAssociation.get_many(account_id=account_id,
|
||||
device_id=device_id)) > 0
|
||||
|
||||
|
||||
def get_device_type(device_type_id):
|
||||
"""
|
||||
Tries to get device type with given parameters. Raises error on failure
|
||||
|
||||
:param device_type_id: Id of device type
|
||||
:type device_type_id: int
|
||||
:returns: Requested device type
|
||||
:rtype: DeviceType
|
||||
"""
|
||||
return DeviceType.get(id=device_type_id)
|
||||
|
||||
|
||||
def delete_device(device_id):
|
||||
"""
|
||||
Tries to delete device with given parameters. Does not raise errors
|
||||
"""
|
||||
Device.get(id=device_id).delete()
|
||||
|
||||
|
||||
def get_devices(account_id):
|
||||
"""
|
||||
Tries to get all devices associated to account. Raises error on
|
||||
failure
|
||||
|
||||
:returns: List of Devices associated to this account
|
||||
:rtype: List of Devices
|
||||
"""
|
||||
return Device.get_many_for_user(account_id)
|
||||
|
||||
|
||||
def get_device_types():
|
||||
"""
|
||||
Tries to get all device types. Raises error on failure
|
||||
|
||||
:returns: List of device types
|
||||
:rtype: List of DeviceTypes
|
||||
"""
|
||||
return DeviceType.get_many()
|
||||
|
||||
|
||||
def create_recording(device_id, raw_json):
|
||||
"""
|
||||
Tries to create recording with given parameters. Raises error on failure
|
||||
|
||||
:param device_id: Id of device
|
||||
:type device_id: int
|
||||
:param raw_json: Raw json received
|
||||
:type raw_json: json
|
||||
:raises: ValueError if parsing fails or device does not exist
|
||||
"""
|
||||
def parse_raw_json_recording(device_id, json_msg):
|
||||
try:
|
||||
return Recording(device_id=device_id,
|
||||
record_type=json_msg["record_type"],
|
||||
record_value=json_msg["record_value"],
|
||||
recorded_at=json_msg["recorded_at"],
|
||||
raw_json=json_msg)
|
||||
except KeyError:
|
||||
error_type, error_instance, traceback = sys.exc_info()
|
||||
raise ValueError("JSON parsing failed! Key error: "
|
||||
+ str(error_instance))
|
||||
|
||||
if not Device.exists(id=device_id):
|
||||
raise ValueError("Device does not exist!")
|
||||
|
||||
recording = parse_raw_json_recording(device_id, raw_json)
|
||||
with app.app_context():
|
||||
recording.save()
|
|
@ -0,0 +1,203 @@
|
|||
import sys
|
||||
from .models import Device, Recording, DeviceAssociation, DeviceType
|
||||
from app.core import app
|
||||
|
||||
|
||||
# Public interface
|
||||
def create_device(name, account_id, device_type=1):
|
||||
"""
|
||||
Tries to create device with given parameters
|
||||
|
||||
:param name: Desired device name
|
||||
:param device_type: Id of desired device type.
|
||||
By default it is 1 (STANDARD)
|
||||
:type name: string
|
||||
:type device_type: int
|
||||
:returns: True if device is successfully created
|
||||
:rtype: Boolean
|
||||
"""
|
||||
device = Device(name, None, device_type)
|
||||
device.save()
|
||||
device_association = DeviceAssociation(device.id, account_id)
|
||||
device_association.save()
|
||||
|
||||
|
||||
def create_device_type(name):
|
||||
"""
|
||||
Tries to create device type with given parameters
|
||||
|
||||
:param name: Desired device type name
|
||||
:type name: string
|
||||
:returns: True if device type is successfully created
|
||||
:rtype: Boolean
|
||||
"""
|
||||
device_type = DeviceType(name)
|
||||
device_type.save()
|
||||
|
||||
|
||||
def set_device_configuration(device_id, configuration_json):
|
||||
"""
|
||||
Tries to update configuration of device with given id
|
||||
|
||||
:param device_id: Id of device to change configuration
|
||||
:param configuration_json: New configuration
|
||||
:type device_id: int
|
||||
:type configuration_json: JSON
|
||||
:rtype: Boolean
|
||||
"""
|
||||
from app.celery_builder import send_config
|
||||
device = Device.get(id=device_id)
|
||||
device.configuration = configuration_json
|
||||
device.save()
|
||||
send_config.delay(device_id, str(configuration_json))
|
||||
|
||||
|
||||
def get_device_configuration(device_id):
|
||||
"""
|
||||
Tries to get configuration for device with given parameters.
|
||||
|
||||
:param device_id: Id of device
|
||||
:type device_id: int
|
||||
:returns: Configuration of given device
|
||||
:rtype: JSON Configuration
|
||||
"""
|
||||
return Device.get(id=device_id).configuration
|
||||
|
||||
|
||||
def get_device_recordings(device_id):
|
||||
"""
|
||||
Tries to get device recording for device with given parameters. Raises
|
||||
error on failure
|
||||
|
||||
:param device_id: Id of device
|
||||
:type device_id: int
|
||||
:returns: List of Recordings for given device
|
||||
:rtype: List of Recording
|
||||
:raises: ValueError if device does not exist
|
||||
"""
|
||||
if not Device.exists(id=device_id):
|
||||
raise ValueError("Device with id %s does not exist" % device_id)
|
||||
|
||||
return Recording.get_many(device_id=device_id)
|
||||
|
||||
|
||||
def get_device_recordings_filtered(device_id, record_type=None,
|
||||
start_date=None, end_date=None):
|
||||
"""
|
||||
Tries to get device recording for device with given parameters. Raises
|
||||
error on failure
|
||||
|
||||
:param device_id: Id of device
|
||||
:param record_type: Type of recording
|
||||
:param start_date: Lower date limit
|
||||
:param end_date: Upper date limit
|
||||
:type device_id: int
|
||||
:type record_type: int
|
||||
:type start_date: Date (string: %d-%m-%Y)
|
||||
:type end_date: Date (string: %d-%m-%Y)
|
||||
:returns: List of Recordings for given filters
|
||||
:rtype: List of Recording
|
||||
:raises: ValueError if device does not exist
|
||||
"""
|
||||
if not Device.exists(id=device_id):
|
||||
raise ValueError("Device with id %s does not exist" % device_id)
|
||||
|
||||
return Recording.get_many_filtered(device_id, record_type,
|
||||
start_date, end_date)
|
||||
|
||||
|
||||
def get_device(device_id):
|
||||
"""
|
||||
Tries to get device with given parameters. Raises error on failure
|
||||
|
||||
:param device_id: Id of device
|
||||
:type device_id: int
|
||||
:returns: Requested device
|
||||
:rtype: Device
|
||||
"""
|
||||
return Device.get(id=device_id)
|
||||
|
||||
|
||||
def can_user_access_device(account_id, device_id):
|
||||
"""
|
||||
Checks if user with given account_id can access device with given device_id
|
||||
|
||||
:param account_id: Id of account
|
||||
:param device_id: Id of device
|
||||
:type account_id: int
|
||||
:type device_id: int
|
||||
:returns: true if device is accessible by this account, false otherwise
|
||||
:rtype: Boolean
|
||||
"""
|
||||
return len(DeviceAssociation.get_many(account_id=account_id,
|
||||
device_id=device_id)) > 0
|
||||
|
||||
|
||||
def get_device_type(device_type_id):
|
||||
"""
|
||||
Tries to get device type with given parameters. Raises error on failure
|
||||
|
||||
:param device_type_id: Id of device type
|
||||
:type device_type_id: int
|
||||
:returns: Requested device type
|
||||
:rtype: DeviceType
|
||||
"""
|
||||
return DeviceType.get(id=device_type_id)
|
||||
|
||||
|
||||
def delete_device(device_id):
|
||||
"""
|
||||
Tries to delete device with given parameters. Does not raise errors
|
||||
"""
|
||||
Device.get(id=device_id).delete()
|
||||
|
||||
|
||||
def get_devices(account_id):
|
||||
"""
|
||||
Tries to get all devices associated to account. Raises error on
|
||||
failure
|
||||
|
||||
:returns: List of Devices associated to this account
|
||||
:rtype: List of Devices
|
||||
"""
|
||||
return Device.get_many_for_user(account_id)
|
||||
|
||||
|
||||
def get_device_types():
|
||||
"""
|
||||
Tries to get all device types. Raises error on failure
|
||||
|
||||
:returns: List of device types
|
||||
:rtype: List of DeviceTypes
|
||||
"""
|
||||
return DeviceType.get_many()
|
||||
|
||||
|
||||
def create_recording(device_id, raw_json):
|
||||
"""
|
||||
Tries to create recording with given parameters. Raises error on failure
|
||||
|
||||
:param device_id: Id of device
|
||||
:type device_id: int
|
||||
:param raw_json: Raw json received
|
||||
:type raw_json: json
|
||||
:raises: ValueError if parsing fails or device does not exist
|
||||
"""
|
||||
def parse_raw_json_recording(device_id, json_msg):
|
||||
try:
|
||||
return Recording(device_id=device_id,
|
||||
record_type=json_msg["record_type"],
|
||||
record_value=json_msg["record_value"],
|
||||
recorded_at=json_msg["recorded_at"],
|
||||
raw_json=json_msg)
|
||||
except KeyError:
|
||||
error_type, error_instance, traceback = sys.exc_info()
|
||||
raise ValueError("JSON parsing failed! Key error: "
|
||||
+ str(error_instance))
|
||||
|
||||
if not Device.exists(id=device_id):
|
||||
raise ValueError("Device does not exist!")
|
||||
|
||||
recording = parse_raw_json_recording(device_id, raw_json)
|
||||
with app.app_context():
|
||||
recording.save()
|
|
@ -0,0 +1,3 @@
|
|||
from flask import Blueprint
|
||||
|
||||
devices_bp = Blueprint('devices', __name__)
|
|
@ -1,19 +0,0 @@
|
|||
import atexit
|
||||
from flask import Blueprint
|
||||
from .mqtt_client import MqttClient
|
||||
|
||||
mqtt_bp = Blueprint('mqtt', __name__)
|
||||
|
||||
|
||||
# Setup
|
||||
@mqtt_bp.record
|
||||
def __on_blueprint_setup(setup_state):
|
||||
MqttClient.setup(setup_state.app)
|
||||
|
||||
|
||||
# When app dies, stop mqtt connection
|
||||
def on_stop():
|
||||
MqttClient.tear_down()
|
||||
|
||||
|
||||
atexit.register(on_stop)
|
|
@ -0,0 +1,19 @@
|
|||
import atexit
|
||||
from flask import Blueprint
|
||||
from .mqtt_client import MqttClient
|
||||
|
||||
mqtt_bp = Blueprint('mqtt', __name__)
|
||||
|
||||
|
||||
# Setup
|
||||
@mqtt_bp.record
|
||||
def __on_blueprint_setup(setup_state):
|
||||
MqttClient.setup(setup_state.app)
|
||||
|
||||
|
||||
# When app dies, stop mqtt connection
|
||||
def on_stop():
|
||||
MqttClient.tear_down()
|
||||
|
||||
|
||||
atexit.register(on_stop)
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
import json
|
||||
from flask_mqtt import Mqtt
|
||||
import app.devices as devices
|
||||
import app.devices.api as devices
|
||||
|
||||
|
||||
class MqttClient:
|
||||
|
|
Loading…
Reference in New Issue