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 just simple python packages/modules
|
||||
"""
|
||||
from .devices import devices_bp
|
||||
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
|
||||
|
||||
app.register_blueprint(devices_bp)
|
||||
app.register_blueprint(accounts_bp)
|
||||
app.register_blueprint(devices_bp)
|
||||
app.register_blueprint(dashboard_bp)
|
||||
app.register_blueprint(mqtt_bp)
|
||||
app.register_blueprint(api_bp, url_prefix='/api')
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ def add_resources():
|
|||
DeviceTypeResource,
|
||||
DeviceTypeListResource,
|
||||
DeviceConfigurationResource)
|
||||
from .resources.dashboard import DashboardResource, DashboardListResource
|
||||
|
||||
api.add_resource(AccountResource, '/v1/accounts/<int:account_id>')
|
||||
api.add_resource(AccountListResource, '/v1/accounts')
|
||||
|
@ -60,6 +61,9 @@ def add_resources():
|
|||
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()
|
||||
|
|
|
@ -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
|
||||
default: {}
|
||||
|
||||
dashboarddata:
|
||||
type: string
|
||||
description: Dashboard data
|
||||
default: {}
|
||||
|
||||
Credentials:
|
||||
type: object
|
||||
required:
|
||||
|
@ -155,6 +160,25 @@ definitions:
|
|||
device_type_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:
|
||||
type: object
|
||||
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