Add dashboards for users

master
esensar 2018-09-20 01:42:37 +02:00
parent 86c1d6c587
commit 6cee752f2b
11 changed files with 343 additions and 2 deletions

View File

@ -25,13 +25,15 @@ def setup_blueprints(app):
They are exposed as blueprints just for consistency, otherwise They are exposed as blueprints just for consistency, otherwise
they are just simple python packages/modules they are just simple python packages/modules
""" """
from .devices import devices_bp
from .accounts import accounts_bp from .accounts import accounts_bp
from .devices import devices_bp
from .dashboard import dashboard_bp
from .api import api_bp from .api import api_bp
from .mqtt import mqtt_bp from .mqtt import mqtt_bp
app.register_blueprint(devices_bp)
app.register_blueprint(accounts_bp) app.register_blueprint(accounts_bp)
app.register_blueprint(devices_bp)
app.register_blueprint(dashboard_bp)
app.register_blueprint(mqtt_bp) app.register_blueprint(mqtt_bp)
app.register_blueprint(api_bp, url_prefix='/api') app.register_blueprint(api_bp, url_prefix='/api')

View File

@ -46,6 +46,7 @@ def add_resources():
DeviceTypeResource, DeviceTypeResource,
DeviceTypeListResource, DeviceTypeListResource,
DeviceConfigurationResource) DeviceConfigurationResource)
from .resources.dashboard import DashboardResource, DashboardListResource
api.add_resource(AccountResource, '/v1/accounts/<int:account_id>') api.add_resource(AccountResource, '/v1/accounts/<int:account_id>')
api.add_resource(AccountListResource, '/v1/accounts') api.add_resource(AccountListResource, '/v1/accounts')
@ -60,6 +61,9 @@ def add_resources():
api.add_resource(DeviceTypeListResource, '/v1/devices/types') api.add_resource(DeviceTypeListResource, '/v1/devices/types')
api.add_resource(DeviceConfigurationResource, api.add_resource(DeviceConfigurationResource,
'/v1/devices/<int:device_id>/configuration') '/v1/devices/<int:device_id>/configuration')
api.add_resource(DashboardListResource, '/v1/dashboards')
api.add_resource(DashboardResource,
'/v1/dashboards/<int:dashboard_id>')
add_resources() add_resources()

View File

@ -0,0 +1,55 @@
from flask import g
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
class DashboardSchema(Schema):
id = fields.Integer(dump_only=True)
dashboard_data = fields.Raw()
class DashboardWrapperSchema(Schema):
dashboard = fields.Nested(DashboardSchema, required=True, location='json')
class DashboardsSchema(Schema):
dashboards = fields.Nested(DashboardSchema, required=True, location='json',
many=True)
class DashboardResource(ProtectedResource):
@swag_from('swagger/get_dashboard_spec.yaml')
def get(self, dashboard_id):
return DashboardWrapperSchema().dump(
{'dashboard': dashboard.get_dashboard(dashboard_id)}), 200
@use_args(DashboardWrapperSchema())
@swag_from('swagger/update_dashboard_spec.yaml')
def put(self, dashboard_id, args):
args = args['dashboard']
success = dashboard.update_dashboard(
dashboard_id,
args['dashboard_data'])
if success:
return '', 204
class DashboardListResource(ProtectedResource):
@use_args(DashboardWrapperSchema())
@swag_from('swagger/create_dashboard_spec.yaml')
def post(self, args):
args = args['dashboard']
success = dashboard.create_dashboard(
args['dashboard_data'],
g.current_account.id)
if success:
return '', 201
@swag_from('swagger/get_dashboards_spec.yaml')
def get(self):
return DashboardsSchema().dump(
{'dashboards':
dashboard.get_dashboards(g.current_account.id)}), 200

View File

@ -0,0 +1,19 @@
Creates new dashboard
Requires Dashboard object and creates dashboard
---
tags:
- Dashboard
parameters:
- in: body
name: body
required: true
schema:
type: object
required:
- dashboard
properties:
device:
$ref: '#/definitions/DashboardCreation'
responses:
201:
description: Successful creation

View File

@ -0,0 +1,21 @@
Gets a dashboard
---
tags:
- Dashboard
parameters:
- in: path
name: dashboard_id
required: true
type: integer
description: Id of the dashboard
responses:
200:
description: Success
schema:
type: object
required:
- dashboard
properties:
device:
$ref: '#/definitions/Dashboard'

View File

@ -0,0 +1,16 @@
Gets all associated dashboards
---
tags:
- Dashboard
responses:
200:
description: Success
schema:
type: object
required:
- dashboards
properties:
devices:
type: array
items:
$ref: '#/definitions/Dashboard'

View File

@ -0,0 +1,23 @@
Updates a dashboard
---
tags:
- Dashboard
parameters:
- in: path
name: dashboard_id
required: true
type: integer
description: Id of the device
- in: body
name: body
required: true
schema:
type: object
required:
- dashboard
properties:
device:
$ref: '#/definitions/DashboardCreation'
responses:
204:
description: Success

View File

@ -0,0 +1,58 @@
from flask import Blueprint
from .models import Dashboard
dashboard_bp = Blueprint('dashboard', __name__)
# Public interface
def create_dashboard(dashboard_data, account_id):
"""
Tries to create dashboard with given parameters
:param dashboard_data: JSON dashboard data
:param account_id: Id of owner of this dashboard
:type name: JSON
:type account_id: int
:returns: True if dashboard is successfully created
:rtype: Boolean
"""
dashboard = Dashboard(account_id, dashboard_data)
dashboard.save()
def get_dashboard(dashboard_id):
"""
Tries to fetch dashboard with given id
:param dashboard_id: Id of requested dashboard
:type name: int
:returns: Dashboard object
:rtype: Dashboard
"""
return Dashboard.get(id=dashboard_id)
def update_dashboard(dashboard_id, dashboard_data):
"""
Tries to update dashboard with given parameters
:param dashboard_data: JSON dashboard data
:param dashboard_id: Id of the dashboard
:type name: JSON
:type dashboard_id: int
"""
dashboard = Dashboard.get(id=dashboard_id)
dashboard.dashboard_data = dashboard_data
dashboard_data.save()
def get_dashboards(account_id):
"""
Tries to fetch dashboards owned by account with given id
:param account_id: Id of owner account
:type name: int
:returns: Dashboard list
:rtype: List of Dashboard
"""
return Dashboard.get_many(account_id=account_id)

View File

@ -0,0 +1,83 @@
from app import db
from sqlalchemy.dialects.postgresql import JSON
class Dashboard(db.Model):
__tablename__ = 'dashboards'
id = db.Column(db.Integer, primary_key=True)
dashboard_data = db.Column(JSON, nullable=False)
account_id = db.Column(db.Integer, db.ForeignKey('accounts.id'),
primary_key=True)
created_at = db.Column(db.DateTime,
nullable=False,
default=db.func.current_timestamp())
modified_at = db.Column(db.DateTime,
nullable=False,
default=db.func.current_timestamp(),
onupdate=db.func.current_timestamp())
def __init__(self, account_id, dashboard_data):
self.account_id = account_id
self.dashboard_data = dashboard_data
def save(self):
"""
Stores this dashboard to database
This may raise errors
"""
db.session.add(self)
db.session.commit()
@staticmethod
def exists_with_any_of(**kwargs):
"""
Checks if dashboard with any of the given arguments exists
"""
for key, value in kwargs.items():
args = {key: value}
if Dashboard.query.filter_by(**args).first():
return True
return False
@staticmethod
def exists(**kwargs):
"""
Checks if dashboard with all of the given arguments exists
"""
if Dashboard.query.filter_by(**kwargs).first():
return True
return False
@staticmethod
def get_all():
"""
Get all stored dashboards
"""
return Dashboard.query.all()
@staticmethod
def get_many(**kwargs):
"""
Get dashboards with given filters
Available filters:
* id
* account_id
"""
return Dashboard.query.filter_by(**kwargs).all()
@staticmethod
def get(**kwargs):
"""
Get dashboard with given filters
Available filters:
* id
* account_id
"""
return Dashboard.query.filter_by(**kwargs).first_or_404()
def __repr__(self):
return '<Dashboard (dashboard_data=%s, account_id=%s)>' % (
self.dashboard_data, self.account_id)

View File

@ -61,6 +61,11 @@ definitions:
description: Configuration description: Configuration
default: {} default: {}
dashboarddata:
type: string
description: Dashboard data
default: {}
Credentials: Credentials:
type: object type: object
required: required:
@ -155,6 +160,25 @@ definitions:
device_type_id: device_type_id:
$ref: '#/definitions/id' $ref: '#/definitions/id'
Dashboard:
type: object
required:
- id
- dashboard_data
properties:
id:
$ref: '#/definitions/id'
dashboard_data:
$ref: '#/definitions/dashboarddata'
DashboardCreation:
type: object
required:
- dashboard_data
properties:
dashboard_data:
$ref: '#/definitions/dashboarddata'
UnauthorizedError: UnauthorizedError:
type: object type: object
required: required:

View File

@ -0,0 +1,36 @@
"""empty message
Revision ID: 5aa58dcd7a2c
Revises: efbd47fca2fd
Create Date: 2018-09-20 01:29:06.797906
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = '5aa58dcd7a2c'
down_revision = 'efbd47fca2fd'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('dashboards',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('dashboard_data', postgresql.JSON(astext_type=sa.Text()), nullable=False),
sa.Column('account_id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('modified_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['account_id'], ['accounts.id'], ),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('dashboards')
# ### end Alembic commands ###