From fcc2f863920024a48b6c2751e2c826a9e029983f Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 9 Oct 2017 12:51:56 -0400 Subject: [PATCH 01/19] Update base controller class to use bpvalve --- app/controllers/base.py | 146 +++------------------------------------- requirements.txt | 1 + 2 files changed, 10 insertions(+), 137 deletions(-) diff --git a/app/controllers/base.py b/app/controllers/base.py index 546e7de..2cdbaf1 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -1,140 +1,12 @@ -from sqlalchemy.exc import IntegrityError -from werkzeug.datastructures import MultiDict -from werkzeug.exceptions import NotFound, BadRequest -from flask import current_app +"""Flask RestController wrapper""" +from bpvalve.flask.controllers import RestController as BaseRestController +from app.lib.database import db +from app.lib.red import redis -from ..lib.database import db, commit - -class RestController(object): - """A RESTful class for manipulating database objects. - - Controllers should be request-agnostic. The can be initialized and used - outside of a request context. This provides a convenient place to dump - complicated filters or additional data manipulation that happens before - or after a database fetch. +class RestController(BaseRestController): """ - # The model associated with this REST resource. - Model = None - # A dictionary of available filters on the resource. - # - # For each key in this dictionary, if the key is present in the dictionary, - # the value associated with that key (a function taking one argument) is - # passed the filter_data dictionary and the return value is used as a - # database filter. For example, - # - # { - # 'id_': lambda d: Model.id_ == d['id_'] - # } - # - # There is no requirement that the filter function only manipulate its - # key, so these filters can be arbitrarily complex as needed. Multiline - # filters should be defined as separate functions and referenced here. - filters = {} - - # The primary key to use to retrieve the model in get/put/delete requests. - key = 'id' - - def query(self, filter_data): - """Construct a query for the model. - - Args: - filter_data - A dictionary representing the query string - parameters. This is unused at the moment. - """ - return db.session.query(self.Model) - - def filter(self, q, filter_data): - """Parse a dictionary representing the query parameters into database - filters. - - q - A SQLAlchemy query. - filter_data - A dictionary representing the query string parameters. - """ - for k, f in self.filters.items(): - if k in filter_data: - q = q.filter(f(filter_data)) - - if 'order_by' in filter_data: - order_by = getattr(self.Model, filter_data['order_by']) - sort = '' - - if 'sort' in filter_data: - sort = filter_data['sort'] - - q = q.order_by('{} {}'.format(order_by, sort)) - - if 'limit' in filter_data: - q = q.limit(int(filter_data['limit'])) - - return q - - def get_form(self, filter_data): - """Get the WTForms form for the model.""" - pass - - def index(self, filter_data): - """Get a query for all models matching filter_data.""" - q = self.query(filter_data) - q = self.filter(q, filter_data) - return q.all() - - def get(self, id_, filter_data): - """Get a single model matching an id.""" - model = db.session.query(self.Model)\ - .filter(getattr(self.Model, self.key)==id_).first() - if not model: - raise NotFound - return model - - def create(self, data, filter_data): - """Creates a model, but does not add it to the database.""" - model = self.Model() - form = self.get_form(filter_data)(formdata=MultiDict(data)) - - if not form.validate(): - raise BadRequest(form.errors) - - form.populate_obj(model) - return model - - def post(self, data, filter_data): - """Post a new model from a dictionary.""" - model = self.create(data, filter_data) - db.session.add(model) - try: - commit() - except IntegrityError as e: - raise ( - BadRequest(str(e)) if current_app.config['DEBUG'] else - BadRequest) - - return model - - def put(self, id_, data, filter_data): - """Change an existing model using an id and dictionary data.""" - model = self.get(id_, filter_data) - form = self.get_form(filter_data)(formdata=MultiDict(data)) - - if not str(form.id.data) == str(id_): - raise BadRequest(['The id in the model and the uri do not match.']) - if not form.validate(): - raise BadRequest(form.errors) - - form.populate_obj(model) - db.session.add(model) - try: - commit() - except IntegrityError as e: - raise ( - BadRequest(str(e)) if current_app.config['DEBUG'] else - BadRequest) - - return model - - def delete(self, id_, filter_data): - """Delete a model given its id.""" - model = self.get(id_, filter_data) - db.session.delete(model) - commit() - return {} + Wrapper for BaseRestController + """ + db = db + redis = redis diff --git a/requirements.txt b/requirements.txt index 0a28abd..34ee5f4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ arrow==0.7.0 blessed==1.9.5 botocore==1.3.28 +https://github.com/Blocp/bpvalve@v1.1.0 cement==2.4.0 colorama==0.3.3 docker-py==1.1.0 -- GitLab From 13c6edcbd9e798bcc15bc5a0e2bbda167f8d66fe Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 9 Oct 2017 12:58:00 -0400 Subject: [PATCH 02/19] Update dev requirements --- requirements-dev.txt | 3 +++ setup.cfg | 9 +++++++++ 2 files changed, 12 insertions(+) create mode 100644 setup.cfg diff --git a/requirements-dev.txt b/requirements-dev.txt index 5660f02..2748f38 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,9 @@ -r requirements.txt Flask-Testing==0.4.2 nose==1.3.7 +pycodestyle>=2.3.1 +pydocstyle>=1.1.1 pytest==2.9.1 pylint>=1.6.5 pylint-flask>=0.5 +yapf>=0.16.1 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..64a0dfb --- /dev/null +++ b/setup.cfg @@ -0,0 +1,9 @@ +[pycodestyle] +max-line-length=100 + +[isort] +line_length=100 + +[yapf] +based_on_style=pep8 +spaces_before_comment=1 -- GitLab From 7a46dd4eeb327d1e842b91e8a5447cb79d433bd5 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 9 Oct 2017 12:59:31 -0400 Subject: [PATCH 03/19] Use ssh for bpvalve --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 34ee5f4..e760f52 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ arrow==0.7.0 blessed==1.9.5 botocore==1.3.28 -https://github.com/Blocp/bpvalve@v1.1.0 +git+ssh://git@github.com/Blocp/bpvalve.git@v1.1.0 cement==2.4.0 colorama==0.3.3 docker-py==1.1.0 -- GitLab From 11331ead79c97369c82e3f656e88ed4be086220b Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 9 Oct 2017 14:03:17 -0400 Subject: [PATCH 04/19] Import UnprotectedRestView from bpvalve --- app/views/base.py | 69 +++-------------------------------------------- 1 file changed, 3 insertions(+), 66 deletions(-) diff --git a/app/views/base.py b/app/views/base.py index 1a175c5..4700da8 100644 --- a/app/views/base.py +++ b/app/views/base.py @@ -1,71 +1,8 @@ -import json -from werkzeug.exceptions import NotFound -from flask import jsonify, request, current_app -from flask.ext.classy import FlaskView -from app.lib.database import db -from app.lib.red import redis +from bpvalve.flask.views import UnprotectedRestView from app.permissions.auth import standard_login_need -class View(FlaskView): - """A base view to provide convenience methods to subclasses.""" - def request_json(self): - """Return json parsed from the request.""" - return json.loads(request.data.decode('utf-8')) - - def json(self, obj, status=200): - """A wrapper for Flask's jsonify().""" - response = jsonify(data=obj) - return response, status - - def parse(self, model): - """Parse the model into a dictionary.""" - return model.get_dictionary() - - -class UnprotectedRestView(View): - """A view wrapper for RESTful controllers that does not offer API - protection. - - Views should remain agnostic to the database (with the exception of - permissions). In general, the view is responsible for parsing the - request, checking permissions, calling the controller, and parsing that - return into a json output. - """ - def get_controller(self): - """Return a controller instance to use for database interactions.""" - pass - - def index(self): - """/ GET - Retrieve a list of resources.""" - return self.json([ - self.parse(m) for m in self.get_controller().index(request.args) - ]) - - def get(self, id_): - """/{id} GET - Retrieve a resource by id.""" - return self.json(self.parse( - self.get_controller().get(id_, request.args) - )) - - def post(self): - """/ POST - Post a new resource.""" - return self.json(self.parse( - self.get_controller().post(self.request_json(), request.args) - ), 201) - - def put(self, id_): - """/{id} PUT - Modify a resource by id.""" - return self.json(self.parse( - self.get_controller().put(id_, self.request_json(), request.args) - )) - - def delete(self, id_): - """/{id} DELETE - Delete a resource by id.""" - return self.json(self.get_controller().delete(id_, request.args), 204) - - class RestView(UnprotectedRestView): - """A view wrapper for RESTful controllers that _does_ offer API protection. - """ + """A view wrapper for RESTful controllers that _does_ offer API protection.""" + decorators = (standard_login_need,) -- GitLab From 3be40b7f4236da36d4326b0625f5ccd1dab528ed Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 9 Oct 2017 18:49:44 -0400 Subject: [PATCH 05/19] Add auth0 and update init of lib and RestController imports --- app/controllers/base.py | 4 ++-- app/lib/__init__.py | 3 +++ app/lib/auth0.py | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 app/lib/auth0.py diff --git a/app/controllers/base.py b/app/controllers/base.py index 2cdbaf1..cf86692 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -1,7 +1,6 @@ """Flask RestController wrapper""" from bpvalve.flask.controllers import RestController as BaseRestController -from app.lib.database import db -from app.lib.red import redis +from app.lib import db, redis, auth0_ class RestController(BaseRestController): @@ -10,3 +9,4 @@ class RestController(BaseRestController): """ db = db redis = redis + auth0 = auth0_ diff --git a/app/lib/__init__.py b/app/lib/__init__.py index e69de29..3fac200 100644 --- a/app/lib/__init__.py +++ b/app/lib/__init__.py @@ -0,0 +1,3 @@ +from .database import db +from .red import redis +from .auth0 import auth0_ diff --git a/app/lib/auth0.py b/app/lib/auth0.py new file mode 100644 index 0000000..3014a0a --- /dev/null +++ b/app/lib/auth0.py @@ -0,0 +1,19 @@ +from bpvalve.auth0 import Auth0Wrapper + + +class FlaskAuth0: + """Initilalization class for Auth0Wrapper""" + + def init_app(self, app): + """Intialize Auth0Wrapper""" + self.auth = Auth0Wrapper(app.config.get('AUTH0_DOMAIN'), + app.config.get('AUTH0_CLIENT_ID'), + app.config.get('AUTH0_CLIENT_SECRET'), + 'https://{}/api/v2/'.format(app.config.get('AUTH0_DOMAIN'))) + +auth0_ = FlaskAuth0() + + +def register(app): + """Configure the Auth0Wrapper based on the app configuration.""" + auth0_.init_app(app) -- GitLab From 1c2cc4239442a6301f58b7a207fc8ab7a2e43642 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 9 Oct 2017 18:50:26 -0400 Subject: [PATCH 06/19] Condense registeration of flask app --- app/__init__.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index da6671a..cbc3607 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -22,25 +22,15 @@ def create_app(config): app.logger.info('Setting up application...') - from .lib import database - database.register(app) - - from .lib import red - red.register(app) - from . import views views.register(app) - from .lib import exceptions - exceptions.register(app) - - from .lib import service - service.register(app) - - from .lib import session - session.register(app) + from app.lib import database, red, exceptions, service, session, auth0 + services = (database, red, exceptions, service, session, auth0) + for service in services: + service.register(app) - with app.app_context(): - database.db.create_all() + # with app.app_context(): + # database.db.create_all() return app -- GitLab From 6f2794035a842f14cc1912807cebffaf9a9867cd Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 9 Oct 2017 18:53:18 -0400 Subject: [PATCH 07/19] Update package init with module docstring --- app/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/__init__.py b/app/__init__.py index cbc3607..dc103fa 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,3 +1,4 @@ +"""BP flask microservices""" import logging from logging.handlers import RotatingFileHandler from flask import Flask @@ -6,6 +7,7 @@ MEGABYTE = 10**6 LOG_FORMAT = '[%(asctime)s] %(pathname)s:%(lineno)d %(levelname)s - %(message)s' LOG_PATH = '/var/log/flask.log' + def create_app(config): """Set up the application.""" app = Flask(__name__) -- GitLab From 3c0e43c73c97c984b29ef65abd5b587a569e3f04 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 9 Oct 2017 18:56:40 -0400 Subject: [PATCH 08/19] Import RedisWrapper from bpvalve --- app/lib/red.py | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/app/lib/red.py b/app/lib/red.py index 43c02fa..2a55615 100644 --- a/app/lib/red.py +++ b/app/lib/red.py @@ -1,32 +1,4 @@ -"""A library for interacting with redis.""" -from redis import StrictRedis -from parse import parse - - -class RedisWrapper(object): - """A wrapper for Redis.""" - def get_database(self, database): - """Gets an actual database.""" - return StrictRedis(host=self.host, port=self.port, db=database) - - def init_app(self, app): - """Stores information about the redis database from the URI.""" - uri = app.config.get('REDIS_URI') - self.host, self.port = parse('redis://{}:{}/', uri) - - # key/tokens for applications that this service serves. - self.server_apps = self.get_database(0) - # key/tokens for applications that this service consumes. - self.client_apps = self.get_database(1) - # key/tokens for users - self.users = self.get_database(2) - - def flushdb(self): - """Flushes each database.""" - self.server_apps.flushdb() - self.client_apps.flushdb() - self.users.flushdb() - +from bpvalve.flask.lib.red import RedisWrapper redis = RedisWrapper() -- GitLab From db156712a8d0ad8cb5827d3d98e4d4503a4a7c72 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Tue, 10 Oct 2017 16:04:18 -0400 Subject: [PATCH 09/19] Add user model and form --- app/forms/base.py | 5 +++++ app/models/base.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/app/forms/base.py b/app/forms/base.py index 8118e93..a886f9c 100644 --- a/app/forms/base.py +++ b/app/forms/base.py @@ -9,6 +9,11 @@ class Form(wtf.Form): id = wtf.IntegerField() +class UserForm: + user_created = wtf.StringField(validators=[wtf.validators.Length(max=64)]) + user_modified = wtf.StringField(validators=[wtf.validators.Length(max=64)]) + + class AddressForm(object): """A form for validating address information.""" street_address = wtf.StringField( diff --git a/app/models/base.py b/app/models/base.py index e304c90..c4470e2 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -59,6 +59,11 @@ class Model(BaseModel): id = db.Column(db.Integer, primary_key=True) +class User: + user_created = db.Column(db.String(64)) + user_modified = db.Column(db.String(64)) + + class Tracked(object): """A mixin to include tracking datetime fields.""" created = db.Column(columns.Arrow, default=func.now()) -- GitLab From d7a8ab852257a8b6f294edc58530a852d4635dfb Mon Sep 17 00:00:00 2001 From: Conrad Date: Mon, 16 Oct 2017 17:44:19 -0400 Subject: [PATCH 10/19] Initial building_sensor endpoint --- app/controllers/building_sensor.py | 20 ++++++++++++++++++++ app/forms/building_sensor.py | 14 ++++++++++++++ app/models/building_sensor.py | 9 +++++++++ app/views/__init__.py | 7 ++++--- app/views/building_sensor.py | 17 +++++++++++++++++ run.py | 2 +- 6 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 app/controllers/building_sensor.py create mode 100644 app/forms/building_sensor.py create mode 100644 app/models/building_sensor.py create mode 100644 app/views/building_sensor.py diff --git a/app/controllers/building_sensor.py b/app/controllers/building_sensor.py new file mode 100644 index 0000000..6384602 --- /dev/null +++ b/app/controllers/building_sensor.py @@ -0,0 +1,20 @@ +from .base import RestController +from ..forms.building_sensor import BuildingSensorForm +from ..models.building_sensor import BuildingSensor + +class BuildingSensorController(RestController): + """A sensor controller.""" + Model = BuildingSensor + filters = { + 'building_id': lambda d: BuildingSensor.building_id == d['building_id'], + } + + def get_form(self, filter_data): + """Return the account form.""" + return BuildingSensorForm + + def delete(self, id_, filter_data): + super().delete(id_, filter_data) + return { + 'id': id_, + } diff --git a/app/forms/building_sensor.py b/app/forms/building_sensor.py new file mode 100644 index 0000000..be1c0ba --- /dev/null +++ b/app/forms/building_sensor.py @@ -0,0 +1,14 @@ +import wtforms as wtf + +class BuildingSensorForm(wtf.Form): + """ A form for validating building sensor requests.""" + + id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + building_id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + sensor_type = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) diff --git a/app/models/building_sensor.py b/app/models/building_sensor.py new file mode 100644 index 0000000..01e6a75 --- /dev/null +++ b/app/models/building_sensor.py @@ -0,0 +1,9 @@ +from .base import Model, Tracked +from ..lib.database import db + +class BuildingSensor(Model, Tracked, db.Model): + + __table_args__ = {"schema": "sensor"} + + building_id = db.Column(db.Integer) + sensor_type = db.Column(db.Integer) diff --git a/app/views/__init__.py b/app/views/__init__.py index 1dcf967..6864c81 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -1,10 +1,11 @@ """Flask-classy views for the flask application.""" - +from . import building_sensor def register(app): + """Register each view to the application. - + This can be used as a comprehensive list of all views that are present in the application. """ - pass + building_sensor.BuildingSensorView.register(app) diff --git a/app/views/building_sensor.py b/app/views/building_sensor.py new file mode 100644 index 0000000..5680e59 --- /dev/null +++ b/app/views/building_sensor.py @@ -0,0 +1,17 @@ +"""Views for working with accounts.""" +from .base import UnprotectedRestView +from ..controllers.building_sensor import BuildingSensorController + +from flask import request + +class BuildingSensorView(UnprotectedRestView): + """The account view.""" + + def get_controller(self): + """Return an instance of the sensor controller.""" + return BuildingSensorController() + + def delete(self, id_): + """/{id} DELETE - Delete a resource by id.""" + self.get_controller().delete(id_, request.args) + return self.json({ 'id': id_ }, 200) diff --git a/run.py b/run.py index 1ab56a6..d3a3765 100644 --- a/run.py +++ b/run.py @@ -4,4 +4,4 @@ from app.lib.database import db app = create_app('config/local.py') if __name__ == '__main__': - app.run(host='0.0.0.0', port=5300) + app.run(host='0.0.0.0', port=5308) -- GitLab From dfacb506f14e83cf43dd9310cd1c0b35f2f76838 Mon Sep 17 00:00:00 2001 From: Conrad Date: Tue, 17 Oct 2017 16:23:15 -0400 Subject: [PATCH 11/19] Add endpoints for boiler and apartment nodes --- app/controllers/building_sensor.py | 8 +--- .../building_sensor_apartment_node.py | 14 ++++++ .../building_sensor_boiler_node.py | 14 ++++++ app/forms/building_sensor.py | 21 ++++++++- app/forms/building_sensor_apartment_node.py | 47 +++++++++++++++++++ app/forms/building_sensor_boiler_node.py | 32 +++++++++++++ app/models/building_sensor.py | 12 +++++ app/models/building_sensor_apartment_node.py | 26 ++++++++++ app/models/building_sensor_boiler_node.py | 21 +++++++++ app/views/__init__.py | 4 +- app/views/building_sensor.py | 2 +- app/views/building_sensor_apartment_node.py | 17 +++++++ app/views/building_sensor_boiler_node.py | 17 +++++++ 13 files changed, 225 insertions(+), 10 deletions(-) create mode 100644 app/controllers/building_sensor_apartment_node.py create mode 100644 app/controllers/building_sensor_boiler_node.py create mode 100644 app/forms/building_sensor_apartment_node.py create mode 100644 app/forms/building_sensor_boiler_node.py create mode 100644 app/models/building_sensor_apartment_node.py create mode 100644 app/models/building_sensor_boiler_node.py create mode 100644 app/views/building_sensor_apartment_node.py create mode 100644 app/views/building_sensor_boiler_node.py diff --git a/app/controllers/building_sensor.py b/app/controllers/building_sensor.py index 6384602..7c03599 100644 --- a/app/controllers/building_sensor.py +++ b/app/controllers/building_sensor.py @@ -10,11 +10,5 @@ class BuildingSensorController(RestController): } def get_form(self, filter_data): - """Return the account form.""" + """Return the sensor form.""" return BuildingSensorForm - - def delete(self, id_, filter_data): - super().delete(id_, filter_data) - return { - 'id': id_, - } diff --git a/app/controllers/building_sensor_apartment_node.py b/app/controllers/building_sensor_apartment_node.py new file mode 100644 index 0000000..1e70282 --- /dev/null +++ b/app/controllers/building_sensor_apartment_node.py @@ -0,0 +1,14 @@ +from .base import RestController +from ..forms.building_sensor_apartment_node import BuildingSensorApartmentNodeForm +from ..models.building_sensor_apartment_node import BuildingSensorApartmentNode + +class BuildingSensorApartmentNodeController(RestController): + """An apartment node controller.""" + Model = BuildingSensorApartmentNode + filters = { + 'parent_id': lambda d: BuildingSensorApartmentNode.parent_id == d['parent_id'], + } + + def get_form(self, filter_data): + """Return the apartment node form.""" + return BuildingSensorApartmentNodeForm diff --git a/app/controllers/building_sensor_boiler_node.py b/app/controllers/building_sensor_boiler_node.py new file mode 100644 index 0000000..ffebb37 --- /dev/null +++ b/app/controllers/building_sensor_boiler_node.py @@ -0,0 +1,14 @@ +from .base import RestController +from ..forms.building_sensor_boiler_node import BuildingSensorBoilerNodeForm +from ..models.building_sensor_boiler_node import BuildingSensorBoilerNode + +class BuildingSensorBoilerNodeController(RestController): + """A boiler node controller.""" + Model = BuildingSensorBoilerNode + filters = { + 'parent_id': lambda d: BuildingSensorBoilerNode.parent_id == d['parent_id'], + } + + def get_form(self, filter_data): + """Return the boiler node form.""" + return BuildingSensorBoilerNodeForm diff --git a/app/forms/building_sensor.py b/app/forms/building_sensor.py index be1c0ba..793544e 100644 --- a/app/forms/building_sensor.py +++ b/app/forms/building_sensor.py @@ -7,8 +7,27 @@ class BuildingSensorForm(wtf.Form): validators=[wtf.validators.Optional()] ) building_id = wtf.IntegerField( - validators=[wtf.validators.Optional()] + validators=[wtf.validators.Required()] ) sensor_type = wtf.IntegerField( validators=[wtf.validators.Optional()] ) + date_of_install = wtf.DateField( + validators=[wtf.validators.Optional()] + ) + heating_system_type = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + gateway_id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + installer_name = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + placement_type = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + notes = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + diff --git a/app/forms/building_sensor_apartment_node.py b/app/forms/building_sensor_apartment_node.py new file mode 100644 index 0000000..359d3aa --- /dev/null +++ b/app/forms/building_sensor_apartment_node.py @@ -0,0 +1,47 @@ +import wtforms as wtf + +class BuildingSensorApartmentNodeForm(wtf.Form): + """ A form for validating apartment node requests.""" + + id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + parent_id = wtf.IntegerField( + validators=[wtf.validators.Required()] + ) + node_id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + node_location = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + display_name = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + apartment_number = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + apartment_floor = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + number_of_bedrooms = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + tenant_name = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + tenant_contact = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + apartment_orientation = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + node_wall_type = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + tenant_heating_issue = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + notes = wtf.StringField( + validators=[wtf.validators.Optional()] + ) diff --git a/app/forms/building_sensor_boiler_node.py b/app/forms/building_sensor_boiler_node.py new file mode 100644 index 0000000..6e5d6ad --- /dev/null +++ b/app/forms/building_sensor_boiler_node.py @@ -0,0 +1,32 @@ +import wtforms as wtf + +class BuildingSensorBoilerNodeForm(wtf.Form): + """ A form for validating boiler node requests.""" + + id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + parent_id = wtf.IntegerField( + validators=[wtf.validators.Required()] + ) + node_id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + node_location = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + temperature_probe_1 = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + temperature_probe_2 = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + temperature_probe_3 = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + temperature_probe_4 = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + notes = wtf.StringField( + validators=[wtf.validators.Optional()] + ) diff --git a/app/models/building_sensor.py b/app/models/building_sensor.py index 01e6a75..6d7e1e9 100644 --- a/app/models/building_sensor.py +++ b/app/models/building_sensor.py @@ -1,9 +1,21 @@ from .base import Model, Tracked from ..lib.database import db +from sqlalchemy.ext.declarative import declared_attr + class BuildingSensor(Model, Tracked, db.Model): + @declared_attr + def __tablename__(cls): + return "building_sensor" + __table_args__ = {"schema": "sensor"} building_id = db.Column(db.Integer) + date_of_install = db.Column(db.Date) sensor_type = db.Column(db.Integer) + heating_system_type = db.Column(db.Integer) + gateway_id = db.Column(db.Integer) + installer_name = db.Column(db.Unicode(50)) + placement_type = db.Column(db.Integer) + notes = db.Column(db.Unicode(500)) diff --git a/app/models/building_sensor_apartment_node.py b/app/models/building_sensor_apartment_node.py new file mode 100644 index 0000000..3e340de --- /dev/null +++ b/app/models/building_sensor_apartment_node.py @@ -0,0 +1,26 @@ +from .base import Model, Tracked +from ..lib.database import db + +from sqlalchemy.ext.declarative import declared_attr + +class BuildingSensorApartmentNode(Model, Tracked, db.Model): + + @declared_attr + def __tablename__(cls): + return "building_sensor_apartment_node" + + __table_args__ = {"schema": "sensor"} + + parent_id = db.Column(db.Integer) + node_id = db.Column(db.Integer) + node_location = db.Column(db.Unicode(100)) + display_name = db.Column(db.Unicode(100)) + apartment_number = db.Column(db.Unicode(10)) + apartment_floor = db.Column(db.Unicode(10)) + number_of_bedrooms = db.Column(db.Unicode(10)) + tenant_name = db.Column(db.Unicode(50)) + tenant_contact = db.Column(db.Unicode(100)) + apartment_orientation = db.Column(db.Integer) + node_wall_type = db.Column(db.Unicode(50)) + tenant_heating_issue = db.Column(db.Unicode(50)) + notes = db.Column(db.Unicode(500)) diff --git a/app/models/building_sensor_boiler_node.py b/app/models/building_sensor_boiler_node.py new file mode 100644 index 0000000..e04b4c8 --- /dev/null +++ b/app/models/building_sensor_boiler_node.py @@ -0,0 +1,21 @@ +from .base import Model, Tracked +from ..lib.database import db + +from sqlalchemy.ext.declarative import declared_attr + +class BuildingSensorBoilerNode(Model, Tracked, db.Model): + + @declared_attr + def __tablename__(cls): + return "building_sensor_boiler_node" + + __table_args__ = {"schema": "sensor"} + + parent_id = db.Column(db.Integer) + node_id = db.Column(db.Integer) + node_location = db.Column(db.Unicode(100)) + temperature_probe_1 = db.Column(db.Integer) + temperature_probe_2 = db.Column(db.Integer) + temperature_probe_3 = db.Column(db.Integer) + temperature_probe_4 = db.Column(db.Integer) + notes = db.Column(db.Unicode(500)) diff --git a/app/views/__init__.py b/app/views/__init__.py index 6864c81..5a981e5 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -1,5 +1,5 @@ """Flask-classy views for the flask application.""" -from . import building_sensor +from . import building_sensor, building_sensor_boiler_node, building_sensor_apartment_node def register(app): @@ -9,3 +9,5 @@ def register(app): in the application. """ building_sensor.BuildingSensorView.register(app) + building_sensor_boiler_node.BuildingSensorBoilerNodeView.register(app) + building_sensor_apartment_node.BuildingSensorApartmentNodeView.register(app) diff --git a/app/views/building_sensor.py b/app/views/building_sensor.py index 5680e59..9b13000 100644 --- a/app/views/building_sensor.py +++ b/app/views/building_sensor.py @@ -5,7 +5,7 @@ from ..controllers.building_sensor import BuildingSensorController from flask import request class BuildingSensorView(UnprotectedRestView): - """The account view.""" + """The sensor view.""" def get_controller(self): """Return an instance of the sensor controller.""" diff --git a/app/views/building_sensor_apartment_node.py b/app/views/building_sensor_apartment_node.py new file mode 100644 index 0000000..c19a96f --- /dev/null +++ b/app/views/building_sensor_apartment_node.py @@ -0,0 +1,17 @@ +"""Views for working with accounts.""" +from .base import UnprotectedRestView +from ..controllers.building_sensor_apartment_node import BuildingSensorApartmentNodeController + +from flask import request + +class BuildingSensorApartmentNodeView(UnprotectedRestView): + """The apartment node view.""" + + def get_controller(self): + """Return an instance of the apartment node controller.""" + return BuildingSensorApartmentNodeController() + + def delete(self, id_): + """/{id} DELETE - Delete a resource by id.""" + self.get_controller().delete(id_, request.args) + return self.json({ 'id': id_ }, 200) diff --git a/app/views/building_sensor_boiler_node.py b/app/views/building_sensor_boiler_node.py new file mode 100644 index 0000000..3f6ea34 --- /dev/null +++ b/app/views/building_sensor_boiler_node.py @@ -0,0 +1,17 @@ +"""Views for working with accounts.""" +from .base import UnprotectedRestView +from ..controllers.building_sensor_boiler_node import BuildingSensorBoilerNodeController + +from flask import request + +class BuildingSensorBoilerNodeView(UnprotectedRestView): + """The boiler node view.""" + + def get_controller(self): + """Return an instance of the boiler node controller.""" + return BuildingSensorBoilerNodeController() + + def delete(self, id_): + """/{id} DELETE - Delete a resource by id.""" + self.get_controller().delete(id_, request.args) + return self.json({ 'id': id_ }, 200) -- GitLab From b3abdd6ab1c33f82a09a42f832bc89d62a60d708 Mon Sep 17 00:00:00 2001 From: Conrad Date: Wed, 18 Oct 2017 15:28:35 -0400 Subject: [PATCH 12/19] Use RestView and delete cascade --- app/controllers/building_sensor.py | 15 +++++++++++++++ app/forms/building_sensor.py | 2 +- app/forms/building_sensor_boiler_node.py | 2 +- app/models/building_sensor_boiler_node.py | 2 +- app/views/building_sensor.py | 4 ++-- app/views/building_sensor_apartment_node.py | 4 ++-- app/views/building_sensor_boiler_node.py | 4 ++-- 7 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app/controllers/building_sensor.py b/app/controllers/building_sensor.py index 7c03599..04bea59 100644 --- a/app/controllers/building_sensor.py +++ b/app/controllers/building_sensor.py @@ -1,6 +1,8 @@ from .base import RestController from ..forms.building_sensor import BuildingSensorForm from ..models.building_sensor import BuildingSensor +from .building_sensor_boiler_node import BuildingSensorBoilerNodeController +from .building_sensor_apartment_node import BuildingSensorApartmentNodeController class BuildingSensorController(RestController): """A sensor controller.""" @@ -12,3 +14,16 @@ class BuildingSensorController(RestController): def get_form(self, filter_data): """Return the sensor form.""" return BuildingSensorForm + + def delete(self, id_, filter_data): + boiler_node_controller = BuildingSensorBoilerNodeController() + boiler_nodes = boiler_node_controller.index({ 'parent_id': id_ }) + for node in boiler_nodes: + boiler_node_controller.delete(node.id, None) + apartment_node_controller = BuildingSensorApartmentNodeController() + apartment_nodes = apartment_node_controller.index({ 'parent_id': id_ }) + for node in apartment_nodes: + apartment_node_controller.delete(node.id, None) + return super().delete(id_, filter_data) + + diff --git a/app/forms/building_sensor.py b/app/forms/building_sensor.py index 793544e..c7d1cf0 100644 --- a/app/forms/building_sensor.py +++ b/app/forms/building_sensor.py @@ -18,7 +18,7 @@ class BuildingSensorForm(wtf.Form): heating_system_type = wtf.IntegerField( validators=[wtf.validators.Optional()] ) - gateway_id = wtf.IntegerField( + gateway_id = wtf.StringField( validators=[wtf.validators.Optional()] ) installer_name = wtf.StringField( diff --git a/app/forms/building_sensor_boiler_node.py b/app/forms/building_sensor_boiler_node.py index 6e5d6ad..1ef101c 100644 --- a/app/forms/building_sensor_boiler_node.py +++ b/app/forms/building_sensor_boiler_node.py @@ -9,7 +9,7 @@ class BuildingSensorBoilerNodeForm(wtf.Form): parent_id = wtf.IntegerField( validators=[wtf.validators.Required()] ) - node_id = wtf.IntegerField( + node_id = wtf.StringField( validators=[wtf.validators.Optional()] ) node_location = wtf.StringField( diff --git a/app/models/building_sensor_boiler_node.py b/app/models/building_sensor_boiler_node.py index e04b4c8..4d40bfe 100644 --- a/app/models/building_sensor_boiler_node.py +++ b/app/models/building_sensor_boiler_node.py @@ -12,7 +12,7 @@ class BuildingSensorBoilerNode(Model, Tracked, db.Model): __table_args__ = {"schema": "sensor"} parent_id = db.Column(db.Integer) - node_id = db.Column(db.Integer) + node_id = db.Column(db.Unicode(30)) node_location = db.Column(db.Unicode(100)) temperature_probe_1 = db.Column(db.Integer) temperature_probe_2 = db.Column(db.Integer) diff --git a/app/views/building_sensor.py b/app/views/building_sensor.py index 9b13000..79a1899 100644 --- a/app/views/building_sensor.py +++ b/app/views/building_sensor.py @@ -1,10 +1,10 @@ """Views for working with accounts.""" -from .base import UnprotectedRestView +from .base import RestView from ..controllers.building_sensor import BuildingSensorController from flask import request -class BuildingSensorView(UnprotectedRestView): +class BuildingSensorView(RestView): """The sensor view.""" def get_controller(self): diff --git a/app/views/building_sensor_apartment_node.py b/app/views/building_sensor_apartment_node.py index c19a96f..7c25643 100644 --- a/app/views/building_sensor_apartment_node.py +++ b/app/views/building_sensor_apartment_node.py @@ -1,10 +1,10 @@ """Views for working with accounts.""" -from .base import UnprotectedRestView +from .base import RestView from ..controllers.building_sensor_apartment_node import BuildingSensorApartmentNodeController from flask import request -class BuildingSensorApartmentNodeView(UnprotectedRestView): +class BuildingSensorApartmentNodeView(RestView): """The apartment node view.""" def get_controller(self): diff --git a/app/views/building_sensor_boiler_node.py b/app/views/building_sensor_boiler_node.py index 3f6ea34..c10dbf0 100644 --- a/app/views/building_sensor_boiler_node.py +++ b/app/views/building_sensor_boiler_node.py @@ -1,10 +1,10 @@ """Views for working with accounts.""" -from .base import UnprotectedRestView +from .base import RestView from ..controllers.building_sensor_boiler_node import BuildingSensorBoilerNodeController from flask import request -class BuildingSensorBoilerNodeView(UnprotectedRestView): +class BuildingSensorBoilerNodeView(RestView): """The boiler node view.""" def get_controller(self): -- GitLab From adb192ddfac997ed8ba43f0229670098624efe03 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Thu, 19 Oct 2017 17:02:08 -0400 Subject: [PATCH 13/19] Update bpvalve to v1.1.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e760f52..87be95d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ arrow==0.7.0 blessed==1.9.5 botocore==1.3.28 -git+ssh://git@github.com/Blocp/bpvalve.git@v1.1.0 +git+ssh://git@github.com/Blocp/bpvalve.git@v1.1.1 cement==2.4.0 colorama==0.3.3 docker-py==1.1.0 -- GitLab From 5b0310b9a318e4603422637dc88567cff1269e65 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 23 Oct 2017 12:02:41 -0400 Subject: [PATCH 14/19] Add auth0 client id and secret env vars --- app/config/development.default.py | 2 ++ app/config/local.default.py | 2 ++ app/config/production.default.py | 2 ++ app/config/staging.default.py | 2 ++ 4 files changed, 8 insertions(+) diff --git a/app/config/development.default.py b/app/config/development.default.py index 9333a24..18a4f9e 100644 --- a/app/config/development.default.py +++ b/app/config/development.default.py @@ -28,6 +28,8 @@ AUTH0_AUTH_HEADER = 'x-blocpower-auth0-token' AUTH0_DOMAIN = os.environ['AUTH0_DOMAIN'] AUTH0_AUDIENCE = os.environ['AUTH0_AUDIENCE'] AUTH0_CLAIMS_NAMESPACE = os.environ['AUTH0_CLAIMS_NAMESPACE'] +AUTH0_CLIENT_ID = os.environ['AUTH0_CLIENT_ID'] +AUTH0_CLIENT_SECRET = os.environ['AUTH0_CLIENT_SECRET'] # Blocpower auth information. HEADER_AUTH_KEY = 'x-blocpower-auth-key' diff --git a/app/config/local.default.py b/app/config/local.default.py index d3e4d2b..41819cb 100644 --- a/app/config/local.default.py +++ b/app/config/local.default.py @@ -25,6 +25,8 @@ AUTH0_AUTH_HEADER = 'x-blocpower-auth0-token' AUTH0_DOMAIN = '$AUTH0_DOMAIN' AUTH0_AUDIENCE = '$AUTH0_AUDIENCE' AUTH0_CLAIMS_NAMESPACE = '$AUTH0_CLAIMS_NAMESPACE' +AUTH0_CLIENT_ID = '$AUTH0_CLIENT_ID' +AUTH0_CLIENT_SECRET = '$AUTH0_CLIENT_SECRET' # Blocpower auth information. HEADER_AUTH_KEY = 'x-blocpower-auth-key' diff --git a/app/config/production.default.py b/app/config/production.default.py index b382b44..74fbbb2 100644 --- a/app/config/production.default.py +++ b/app/config/production.default.py @@ -28,6 +28,8 @@ AUTH0_AUTH_HEADER = 'x-blocpower-auth0-token' AUTH0_DOMAIN = os.environ['AUTH0_DOMAIN'] AUTH0_AUDIENCE = os.environ['AUTH0_AUDIENCE'] AUTH0_CLAIMS_NAMESPACE = os.environ['AUTH0_CLAIMS_NAMESPACE'] +AUTH0_CLIENT_ID = os.environ['AUTH0_CLIENT_ID'] +AUTH0_CLIENT_SECRET = os.environ['AUTH0_CLIENT_SECRET'] # Blocpower auth information. HEADER_AUTH_KEY = 'x-blocpower-auth-key' diff --git a/app/config/staging.default.py b/app/config/staging.default.py index c8a4b81..1e82cec 100644 --- a/app/config/staging.default.py +++ b/app/config/staging.default.py @@ -28,6 +28,8 @@ AUTH0_AUTH_HEADER = 'x-blocpower-auth0-token' AUTH0_DOMAIN = os.environ['AUTH0_DOMAIN'] AUTH0_AUDIENCE = os.environ['AUTH0_AUDIENCE'] AUTH0_CLAIMS_NAMESPACE = os.environ['AUTH0_CLAIMS_NAMESPACE'] +AUTH0_CLIENT_ID = os.environ['AUTH0_CLIENT_ID'] +AUTH0_CLIENT_SECRET = os.environ['AUTH0_CLIENT_SECRET'] # Blocpower auth information. HEADER_AUTH_KEY = 'x-blocpower-auth-key' -- GitLab From a908533a57828c9d7a98a2b5c887d7291156ba80 Mon Sep 17 00:00:00 2001 From: Conrad Date: Tue, 24 Oct 2017 12:34:39 -0400 Subject: [PATCH 15/19] Add repeater node --- .../building_sensor_repeater_node.py | 14 +++++++++++ app/forms/building_sensor_repeater_node.py | 23 +++++++++++++++++++ app/models/building_sensor_repeater_node.py | 18 +++++++++++++++ app/views/__init__.py | 8 ++++++- app/views/building_sensor_repeater_node.py | 17 ++++++++++++++ 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 app/controllers/building_sensor_repeater_node.py create mode 100644 app/forms/building_sensor_repeater_node.py create mode 100644 app/models/building_sensor_repeater_node.py create mode 100644 app/views/building_sensor_repeater_node.py diff --git a/app/controllers/building_sensor_repeater_node.py b/app/controllers/building_sensor_repeater_node.py new file mode 100644 index 0000000..25d0866 --- /dev/null +++ b/app/controllers/building_sensor_repeater_node.py @@ -0,0 +1,14 @@ +from .base import RestController +from ..forms.building_sensor_repeater_node import BuildingSensorRepeaterNodeForm +from ..models.building_sensor_repeater_node import BuildingSensorRepeaterNode + +class BuildingSensorRepeaterNodeController(RestController): + """A repeater node controller.""" + Model = BuildingSensorRepeaterNode + filters = { + 'parent_id': lambda d: BuildingSensorRepeaterNode.parent_id == d['parent_id'], + } + + def get_form(self, filter_data): + """Return the repeater node form.""" + return BuildingSensorRepeaterNodeForm diff --git a/app/forms/building_sensor_repeater_node.py b/app/forms/building_sensor_repeater_node.py new file mode 100644 index 0000000..8568bb3 --- /dev/null +++ b/app/forms/building_sensor_repeater_node.py @@ -0,0 +1,23 @@ +import wtforms as wtf + +class BuildingSensorRepeaterNodeForm(wtf.Form): + """ A form for validating repeater node requests.""" + + id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + parent_id = wtf.IntegerField( + validators=[wtf.validators.Required()] + ) + node_id = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + node_location = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + apartment_floor = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + notes = wtf.StringField( + validators=[wtf.validators.Optional()] + ) diff --git a/app/models/building_sensor_repeater_node.py b/app/models/building_sensor_repeater_node.py new file mode 100644 index 0000000..6d5f664 --- /dev/null +++ b/app/models/building_sensor_repeater_node.py @@ -0,0 +1,18 @@ +from .base import Model, Tracked +from ..lib.database import db + +from sqlalchemy.ext.declarative import declared_attr + +class BuildingSensorRepeaterNode(Model, Tracked, db.Model): + + @declared_attr + def __tablename__(cls): + return "building_sensor_repeater_node" + + __table_args__ = {"schema": "sensor"} + + parent_id = db.Column(db.Integer) + node_id = db.Column(db.Unicode(30)) + node_location = db.Column(db.Unicode(100)) + apartment_floor = db.Column(db.Unicode(100)) + notes = db.Column(db.Unicode(500)) diff --git a/app/views/__init__.py b/app/views/__init__.py index 5a981e5..928230c 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -1,5 +1,10 @@ """Flask-classy views for the flask application.""" -from . import building_sensor, building_sensor_boiler_node, building_sensor_apartment_node +from . import ( + building_sensor, + building_sensor_boiler_node, + building_sensor_apartment_node, + building_sensor_repeater_node, +) def register(app): @@ -11,3 +16,4 @@ def register(app): building_sensor.BuildingSensorView.register(app) building_sensor_boiler_node.BuildingSensorBoilerNodeView.register(app) building_sensor_apartment_node.BuildingSensorApartmentNodeView.register(app) + building_sensor_repeater_node.BuildingSensorRepeaterNodeView.register(app) diff --git a/app/views/building_sensor_repeater_node.py b/app/views/building_sensor_repeater_node.py new file mode 100644 index 0000000..99ea728 --- /dev/null +++ b/app/views/building_sensor_repeater_node.py @@ -0,0 +1,17 @@ +"""Views for working with accounts.""" +from .base import RestView +from ..controllers.building_sensor_repeater_node import BuildingSensorRepeaterNodeController + +from flask import request + +class BuildingSensorRepeaterNodeView(RestView): + """The repeater node view.""" + + def get_controller(self): + """Return an instance of the repeater node controller.""" + return BuildingSensorRepeaterNodeController() + + def delete(self, id_): + """/{id} DELETE - Delete a resource by id.""" + self.get_controller().delete(id_, request.args) + return self.json({ 'id': id_ }, 200) -- GitLab From b13c9af060b2989aaefe148b3e7c1a2ea5d501c5 Mon Sep 17 00:00:00 2001 From: Conrad Date: Tue, 24 Oct 2017 14:44:29 -0400 Subject: [PATCH 16/19] Change node id to string --- app/forms/building_sensor_apartment_node.py | 5 +---- app/models/building_sensor_apartment_node.py | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/forms/building_sensor_apartment_node.py b/app/forms/building_sensor_apartment_node.py index 359d3aa..4a29fa8 100644 --- a/app/forms/building_sensor_apartment_node.py +++ b/app/forms/building_sensor_apartment_node.py @@ -9,15 +9,12 @@ class BuildingSensorApartmentNodeForm(wtf.Form): parent_id = wtf.IntegerField( validators=[wtf.validators.Required()] ) - node_id = wtf.IntegerField( + node_id = wtf.StringField( validators=[wtf.validators.Optional()] ) node_location = wtf.StringField( validators=[wtf.validators.Optional()] ) - display_name = wtf.StringField( - validators=[wtf.validators.Optional()] - ) apartment_number = wtf.StringField( validators=[wtf.validators.Optional()] ) diff --git a/app/models/building_sensor_apartment_node.py b/app/models/building_sensor_apartment_node.py index 3e340de..863dd52 100644 --- a/app/models/building_sensor_apartment_node.py +++ b/app/models/building_sensor_apartment_node.py @@ -14,7 +14,6 @@ class BuildingSensorApartmentNode(Model, Tracked, db.Model): parent_id = db.Column(db.Integer) node_id = db.Column(db.Integer) node_location = db.Column(db.Unicode(100)) - display_name = db.Column(db.Unicode(100)) apartment_number = db.Column(db.Unicode(10)) apartment_floor = db.Column(db.Unicode(10)) number_of_bedrooms = db.Column(db.Unicode(10)) -- GitLab From ccad86746418b38d4cd066dd629aa7a02c3ed8cf Mon Sep 17 00:00:00 2001 From: Conrad Date: Wed, 25 Oct 2017 11:33:36 -0400 Subject: [PATCH 17/19] Fix BPvalve changes --- app/controllers/building_sensor.py | 11 ----------- app/views/building_sensor.py | 5 ----- app/views/building_sensor_apartment_node.py | 5 ----- app/views/building_sensor_boiler_node.py | 5 ----- app/views/building_sensor_repeater_node.py | 5 ----- requirements.txt | 2 +- 6 files changed, 1 insertion(+), 32 deletions(-) diff --git a/app/controllers/building_sensor.py b/app/controllers/building_sensor.py index 04bea59..31acdb6 100644 --- a/app/controllers/building_sensor.py +++ b/app/controllers/building_sensor.py @@ -15,15 +15,4 @@ class BuildingSensorController(RestController): """Return the sensor form.""" return BuildingSensorForm - def delete(self, id_, filter_data): - boiler_node_controller = BuildingSensorBoilerNodeController() - boiler_nodes = boiler_node_controller.index({ 'parent_id': id_ }) - for node in boiler_nodes: - boiler_node_controller.delete(node.id, None) - apartment_node_controller = BuildingSensorApartmentNodeController() - apartment_nodes = apartment_node_controller.index({ 'parent_id': id_ }) - for node in apartment_nodes: - apartment_node_controller.delete(node.id, None) - return super().delete(id_, filter_data) - diff --git a/app/views/building_sensor.py b/app/views/building_sensor.py index 79a1899..d8c63d5 100644 --- a/app/views/building_sensor.py +++ b/app/views/building_sensor.py @@ -10,8 +10,3 @@ class BuildingSensorView(RestView): def get_controller(self): """Return an instance of the sensor controller.""" return BuildingSensorController() - - def delete(self, id_): - """/{id} DELETE - Delete a resource by id.""" - self.get_controller().delete(id_, request.args) - return self.json({ 'id': id_ }, 200) diff --git a/app/views/building_sensor_apartment_node.py b/app/views/building_sensor_apartment_node.py index 7c25643..3f4a6c5 100644 --- a/app/views/building_sensor_apartment_node.py +++ b/app/views/building_sensor_apartment_node.py @@ -10,8 +10,3 @@ class BuildingSensorApartmentNodeView(RestView): def get_controller(self): """Return an instance of the apartment node controller.""" return BuildingSensorApartmentNodeController() - - def delete(self, id_): - """/{id} DELETE - Delete a resource by id.""" - self.get_controller().delete(id_, request.args) - return self.json({ 'id': id_ }, 200) diff --git a/app/views/building_sensor_boiler_node.py b/app/views/building_sensor_boiler_node.py index c10dbf0..df8c7a0 100644 --- a/app/views/building_sensor_boiler_node.py +++ b/app/views/building_sensor_boiler_node.py @@ -10,8 +10,3 @@ class BuildingSensorBoilerNodeView(RestView): def get_controller(self): """Return an instance of the boiler node controller.""" return BuildingSensorBoilerNodeController() - - def delete(self, id_): - """/{id} DELETE - Delete a resource by id.""" - self.get_controller().delete(id_, request.args) - return self.json({ 'id': id_ }, 200) diff --git a/app/views/building_sensor_repeater_node.py b/app/views/building_sensor_repeater_node.py index 99ea728..e0588f6 100644 --- a/app/views/building_sensor_repeater_node.py +++ b/app/views/building_sensor_repeater_node.py @@ -10,8 +10,3 @@ class BuildingSensorRepeaterNodeView(RestView): def get_controller(self): """Return an instance of the repeater node controller.""" return BuildingSensorRepeaterNodeController() - - def delete(self, id_): - """/{id} DELETE - Delete a resource by id.""" - self.get_controller().delete(id_, request.args) - return self.json({ 'id': id_ }, 200) diff --git a/requirements.txt b/requirements.txt index 87be95d..8fce59b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ arrow==0.7.0 blessed==1.9.5 botocore==1.3.28 -git+ssh://git@github.com/Blocp/bpvalve.git@v1.1.1 +git+ssh://git@github.com/Blocp/bpvalve.git@v1.1.2 cement==2.4.0 colorama==0.3.3 docker-py==1.1.0 -- GitLab From 85ce38e921027a32ace0920752a6bdb34d642650 Mon Sep 17 00:00:00 2001 From: Conrad Date: Wed, 25 Oct 2017 18:08:11 -0400 Subject: [PATCH 18/19] Add sensor image endpoint --- app/controllers/building_sensor.py | 2 -- app/controllers/sensor_image.py | 17 ++++++++++++++++ app/forms/building_sensor.py | 3 ++- app/forms/building_sensor_apartment_node.py | 3 ++- app/forms/building_sensor_boiler_node.py | 3 ++- app/forms/building_sensor_repeater_node.py | 3 ++- app/forms/sensor_image.py | 21 ++++++++++++++++++++ app/models/building_sensor.py | 4 ++-- app/models/building_sensor_apartment_node.py | 4 ++-- app/models/building_sensor_boiler_node.py | 4 ++-- app/models/building_sensor_repeater_node.py | 4 ++-- app/models/sensor_image.py | 17 ++++++++++++++++ app/views/__init__.py | 2 ++ app/views/sensor_image.py | 20 +++++++++++++++++++ 14 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 app/controllers/sensor_image.py create mode 100644 app/forms/sensor_image.py create mode 100644 app/models/sensor_image.py create mode 100644 app/views/sensor_image.py diff --git a/app/controllers/building_sensor.py b/app/controllers/building_sensor.py index 31acdb6..d821317 100644 --- a/app/controllers/building_sensor.py +++ b/app/controllers/building_sensor.py @@ -1,8 +1,6 @@ from .base import RestController from ..forms.building_sensor import BuildingSensorForm from ..models.building_sensor import BuildingSensor -from .building_sensor_boiler_node import BuildingSensorBoilerNodeController -from .building_sensor_apartment_node import BuildingSensorApartmentNodeController class BuildingSensorController(RestController): """A sensor controller.""" diff --git a/app/controllers/sensor_image.py b/app/controllers/sensor_image.py new file mode 100644 index 0000000..2f218f5 --- /dev/null +++ b/app/controllers/sensor_image.py @@ -0,0 +1,17 @@ +from .base import RestController +from ..forms.sensor_image import SensorImageForm +from ..models.sensor_image import SensorImage + +class SensorImageController(RestController): + """A sensor image controller.""" + Model = SensorImage + filters = { + 'entity_id': lambda d: SensorImage.entity_id == d['entity_id'], + 'entity_type': lambda d: SensorImage.entity_type == d['entity_type'], + } + + def get_form(self, filter_data): + """Return the sensor image form.""" + return SensorImageForm + + diff --git a/app/forms/building_sensor.py b/app/forms/building_sensor.py index c7d1cf0..5db2b97 100644 --- a/app/forms/building_sensor.py +++ b/app/forms/building_sensor.py @@ -1,6 +1,7 @@ import wtforms as wtf +from .base import Form, UserForm -class BuildingSensorForm(wtf.Form): +class BuildingSensorForm(Form, UserForm): """ A form for validating building sensor requests.""" id = wtf.IntegerField( diff --git a/app/forms/building_sensor_apartment_node.py b/app/forms/building_sensor_apartment_node.py index 4a29fa8..3229dd0 100644 --- a/app/forms/building_sensor_apartment_node.py +++ b/app/forms/building_sensor_apartment_node.py @@ -1,6 +1,7 @@ import wtforms as wtf +from .base import Form, UserForm -class BuildingSensorApartmentNodeForm(wtf.Form): +class BuildingSensorApartmentNodeForm(Form, UserForm): """ A form for validating apartment node requests.""" id = wtf.IntegerField( diff --git a/app/forms/building_sensor_boiler_node.py b/app/forms/building_sensor_boiler_node.py index 1ef101c..c919b43 100644 --- a/app/forms/building_sensor_boiler_node.py +++ b/app/forms/building_sensor_boiler_node.py @@ -1,6 +1,7 @@ import wtforms as wtf +from .base import Form, UserForm -class BuildingSensorBoilerNodeForm(wtf.Form): +class BuildingSensorBoilerNodeForm(Form, UserForm): """ A form for validating boiler node requests.""" id = wtf.IntegerField( diff --git a/app/forms/building_sensor_repeater_node.py b/app/forms/building_sensor_repeater_node.py index 8568bb3..4421d93 100644 --- a/app/forms/building_sensor_repeater_node.py +++ b/app/forms/building_sensor_repeater_node.py @@ -1,6 +1,7 @@ import wtforms as wtf +from .base import Form, UserForm -class BuildingSensorRepeaterNodeForm(wtf.Form): +class BuildingSensorRepeaterNodeForm(Form, UserForm): """ A form for validating repeater node requests.""" id = wtf.IntegerField( diff --git a/app/forms/sensor_image.py b/app/forms/sensor_image.py new file mode 100644 index 0000000..8f99715 --- /dev/null +++ b/app/forms/sensor_image.py @@ -0,0 +1,21 @@ +import wtforms as wtf +from .base import Form, UserForm + +class SensorImageForm(Form, UserForm): + """ A form for validating sensor image requests.""" + + id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + entity_id = wtf.IntegerField( + validators=[wtf.validators.Required()] + ) + entity_type = wtf.IntegerField( + validators=[wtf.validators.Required()] + ) + image_type = wtf.IntegerField( + validators=[wtf.validators.Required()] + ) + box_id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) diff --git a/app/models/building_sensor.py b/app/models/building_sensor.py index 6d7e1e9..f56b98c 100644 --- a/app/models/building_sensor.py +++ b/app/models/building_sensor.py @@ -1,9 +1,9 @@ -from .base import Model, Tracked +from .base import Model, Tracked, User from ..lib.database import db from sqlalchemy.ext.declarative import declared_attr -class BuildingSensor(Model, Tracked, db.Model): +class BuildingSensor(Model, User, Tracked, db.Model): @declared_attr def __tablename__(cls): diff --git a/app/models/building_sensor_apartment_node.py b/app/models/building_sensor_apartment_node.py index 863dd52..9d1ae5f 100644 --- a/app/models/building_sensor_apartment_node.py +++ b/app/models/building_sensor_apartment_node.py @@ -1,9 +1,9 @@ -from .base import Model, Tracked +from .base import Model, Tracked, User from ..lib.database import db from sqlalchemy.ext.declarative import declared_attr -class BuildingSensorApartmentNode(Model, Tracked, db.Model): +class BuildingSensorApartmentNode(Model, Tracked, User, db.Model): @declared_attr def __tablename__(cls): diff --git a/app/models/building_sensor_boiler_node.py b/app/models/building_sensor_boiler_node.py index 4d40bfe..bfb6564 100644 --- a/app/models/building_sensor_boiler_node.py +++ b/app/models/building_sensor_boiler_node.py @@ -1,9 +1,9 @@ -from .base import Model, Tracked +from .base import Model, Tracked, User from ..lib.database import db from sqlalchemy.ext.declarative import declared_attr -class BuildingSensorBoilerNode(Model, Tracked, db.Model): +class BuildingSensorBoilerNode(Model, User, Tracked, db.Model): @declared_attr def __tablename__(cls): diff --git a/app/models/building_sensor_repeater_node.py b/app/models/building_sensor_repeater_node.py index 6d5f664..05a8b98 100644 --- a/app/models/building_sensor_repeater_node.py +++ b/app/models/building_sensor_repeater_node.py @@ -1,9 +1,9 @@ -from .base import Model, Tracked +from .base import Model, Tracked, User from ..lib.database import db from sqlalchemy.ext.declarative import declared_attr -class BuildingSensorRepeaterNode(Model, Tracked, db.Model): +class BuildingSensorRepeaterNode(Model, User, Tracked, db.Model): @declared_attr def __tablename__(cls): diff --git a/app/models/sensor_image.py b/app/models/sensor_image.py new file mode 100644 index 0000000..fd59b1c --- /dev/null +++ b/app/models/sensor_image.py @@ -0,0 +1,17 @@ +from .base import Model, Tracked, User +from ..lib.database import db + +from sqlalchemy.ext.declarative import declared_attr + +class SensorImage(Model, User, Tracked, db.Model): + + @declared_attr + def __tablename__(cls): + return "sensor_image" + + __table_args__ = {"schema": "sensor"} + + entity_id = db.Column(db.Integer) + entity_type = db.Column(db.Integer) + image_type = db.Column(db.Integer) + box_id = db.Column(db.Integer) diff --git a/app/views/__init__.py b/app/views/__init__.py index 928230c..6e99f08 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -4,6 +4,7 @@ from . import ( building_sensor_boiler_node, building_sensor_apartment_node, building_sensor_repeater_node, + sensor_image, ) def register(app): @@ -17,3 +18,4 @@ def register(app): building_sensor_boiler_node.BuildingSensorBoilerNodeView.register(app) building_sensor_apartment_node.BuildingSensorApartmentNodeView.register(app) building_sensor_repeater_node.BuildingSensorRepeaterNodeView.register(app) + sensor_image.SensorImageView.register(app) diff --git a/app/views/sensor_image.py b/app/views/sensor_image.py new file mode 100644 index 0000000..6fb2fb9 --- /dev/null +++ b/app/views/sensor_image.py @@ -0,0 +1,20 @@ +"""Views for working with sensor images.""" +from .base import RestView +from ..controllers.sensor_image import SensorImageController + +from flask import request +from werkzeug.exceptions import MethodNotAllowed + +class SensorImageView(RestView): + """The sensor image view.""" + + def get_controller(self): + """Return an instance of the sensor image controller.""" + return SensorImageController() + + def delete(self, id_): + raise MethodNotAllowed() + + def put(self, id_): + raise MethodNotAllowed() + -- GitLab From 2bbf18d9ddd56ce285640f660bc57c2a11514604 Mon Sep 17 00:00:00 2001 From: Conrad Date: Fri, 27 Oct 2017 11:08:50 -0400 Subject: [PATCH 19/19] Add gateway_serial --- app/forms/building_sensor.py | 3 +++ app/models/building_sensor.py | 1 + 2 files changed, 4 insertions(+) diff --git a/app/forms/building_sensor.py b/app/forms/building_sensor.py index 5db2b97..9bf2b6d 100644 --- a/app/forms/building_sensor.py +++ b/app/forms/building_sensor.py @@ -19,6 +19,9 @@ class BuildingSensorForm(Form, UserForm): heating_system_type = wtf.IntegerField( validators=[wtf.validators.Optional()] ) + gateway_serial = wtf.StringField( + validators=[wtf.validators.Optional()] + ) gateway_id = wtf.StringField( validators=[wtf.validators.Optional()] ) diff --git a/app/models/building_sensor.py b/app/models/building_sensor.py index f56b98c..0436ce5 100644 --- a/app/models/building_sensor.py +++ b/app/models/building_sensor.py @@ -15,6 +15,7 @@ class BuildingSensor(Model, User, Tracked, db.Model): date_of_install = db.Column(db.Date) sensor_type = db.Column(db.Integer) heating_system_type = db.Column(db.Integer) + gateway_serial = db.Column(db.Integer) gateway_id = db.Column(db.Integer) installer_name = db.Column(db.Unicode(50)) placement_type = db.Column(db.Integer) -- GitLab