Add dashboards for users
parent
86c1d6c587
commit
6cee752f2b
|
@ -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')
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
||||||
|
|
|
@ -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'
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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:
|
||||||
|
|
|
@ -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 ###
|
Loading…
Reference in New Issue