From 00a331f1175e41c8a6a15e6ea34d484230452066 Mon Sep 17 00:00:00 2001 From: Conrad Date: Mon, 4 Dec 2017 11:41:44 -0500 Subject: [PATCH 01/11] Add senseware_node endpoint --- app/controllers/senseware_node.py | 14 +++++++++++ app/forms/senseware_node.py | 39 +++++++++++++++++++++++++++++++ app/models/senseware_node.py | 23 ++++++++++++++++++ app/views/__init__.py | 2 ++ app/views/senseware_node.py | 12 ++++++++++ 5 files changed, 90 insertions(+) create mode 100644 app/controllers/senseware_node.py create mode 100644 app/forms/senseware_node.py create mode 100644 app/models/senseware_node.py create mode 100644 app/views/senseware_node.py diff --git a/app/controllers/senseware_node.py b/app/controllers/senseware_node.py new file mode 100644 index 0000000..4257ce3 --- /dev/null +++ b/app/controllers/senseware_node.py @@ -0,0 +1,14 @@ +from .base import RestController +from ..forms.senseware_node import SensewareNodeForm +from ..models.senseware_node import SensewareNode + +class SensewareNodeController(RestController): + """A senseware node controller.""" + Model = SensewareNode + filters = { + 'gateway_id': lambda d: SensewareNode.gateway_id == d['gateway_id'], + } + + def get_form(self, filter_data): + """Return the senseware node form.""" + return SensewareNodeForm diff --git a/app/forms/senseware_node.py b/app/forms/senseware_node.py new file mode 100644 index 0000000..896a7ba --- /dev/null +++ b/app/forms/senseware_node.py @@ -0,0 +1,39 @@ +import wtforms as wtf +from .base import Form, UserForm + +class SensewareNodeForm(Form, UserForm): + """ A form for validating senseware node requests.""" + + id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + gateway_id = wtf.IntegerField( + validators=[wtf.validators.Required()] + ) + node_id = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + space_id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + repeater = wtf.BooleanField( + validators=[wtf.validators.Optional()] + ) + surface_type = 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/senseware_node.py b/app/models/senseware_node.py new file mode 100644 index 0000000..57fbe27 --- /dev/null +++ b/app/models/senseware_node.py @@ -0,0 +1,23 @@ +from .base import Model, Tracked, User +from ..lib.database import db + +from sqlalchemy.ext.declarative import declared_attr + +class GatewayNode(Model, User, Tracked, db.Model): + + @declared_attr + def __tablename__(cls): + return "gateway_node" + + __table_args__ = {"schema": "sensor"} + + gateway_id = db.Column(db.Integer) + node_id = db.Column(db.Unicode(30)) + space_id = db.Column(db.Integer) + repeater = db.Column(db.Boolean()) + surface_type = db.Column(db.Unicode(50)) + 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 80bafc5..3ffe834 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -6,6 +6,7 @@ from . import ( repeater_node, sensor_image, data, + senseware_node ) def register(app): @@ -21,3 +22,4 @@ def register(app): repeater_node.RepeaterNodeView.register(app) sensor_image.SensorImageView.register(app) data.DataView.register(app) + senseware_node.SensewareNodeView.register(app) diff --git a/app/views/senseware_node.py b/app/views/senseware_node.py new file mode 100644 index 0000000..972d32b --- /dev/null +++ b/app/views/senseware_node.py @@ -0,0 +1,12 @@ +"""Views for working with accounts.""" +from .base import RestView +from ..controllers.senseware_node import SensewareNodeController + +from flask import request + +class SensewareNodeView(RestView): + """The senseware node view.""" + + def get_controller(self): + """Return an instance of the senseware node controller.""" + return SensewareNodeController() -- GitLab From 27ed16bb787c323673495882bc14e9ac991d07e4 Mon Sep 17 00:00:00 2001 From: Conrad Date: Mon, 4 Dec 2017 11:42:26 -0500 Subject: [PATCH 02/11] Remove old node types --- app/controllers/apartment_node.py | 14 ---------- app/controllers/boiler_node.py | 14 ---------- app/controllers/repeater_node.py | 14 ---------- app/forms/apartment_node.py | 45 ------------------------------- app/forms/boiler_node.py | 33 ----------------------- app/forms/repeater_node.py | 24 ----------------- app/models/apartment_node.py | 25 ----------------- app/models/boiler_node.py | 21 --------------- app/models/repeater_node.py | 18 ------------- app/views/apartment_node.py | 12 --------- app/views/boiler_node.py | 12 --------- app/views/repeater_node.py | 12 --------- 12 files changed, 244 deletions(-) delete mode 100644 app/controllers/apartment_node.py delete mode 100644 app/controllers/boiler_node.py delete mode 100644 app/controllers/repeater_node.py delete mode 100644 app/forms/apartment_node.py delete mode 100644 app/forms/boiler_node.py delete mode 100644 app/forms/repeater_node.py delete mode 100644 app/models/apartment_node.py delete mode 100644 app/models/boiler_node.py delete mode 100644 app/models/repeater_node.py delete mode 100644 app/views/apartment_node.py delete mode 100644 app/views/boiler_node.py delete mode 100644 app/views/repeater_node.py diff --git a/app/controllers/apartment_node.py b/app/controllers/apartment_node.py deleted file mode 100644 index 84404b4..0000000 --- a/app/controllers/apartment_node.py +++ /dev/null @@ -1,14 +0,0 @@ -from .base import RestController -from ..forms.apartment_node import ApartmentNodeForm -from ..models.apartment_node import ApartmentNode - -class ApartmentNodeController(RestController): - """An apartment node controller.""" - Model = ApartmentNode - filters = { - 'parent_id': lambda d: ApartmentNode.parent_id == d['parent_id'], - } - - def get_form(self, filter_data): - """Return the apartment node form.""" - return ApartmentNodeForm diff --git a/app/controllers/boiler_node.py b/app/controllers/boiler_node.py deleted file mode 100644 index f1c87df..0000000 --- a/app/controllers/boiler_node.py +++ /dev/null @@ -1,14 +0,0 @@ -from .base import RestController -from ..forms.boiler_node import BoilerNodeForm -from ..models.boiler_node import BoilerNode - -class BoilerNodeController(RestController): - """A boiler node controller.""" - Model = BoilerNode - filters = { - 'parent_id': lambda d: BoilerNode.parent_id == d['parent_id'], - } - - def get_form(self, filter_data): - """Return the boiler node form.""" - return BoilerNodeForm diff --git a/app/controllers/repeater_node.py b/app/controllers/repeater_node.py deleted file mode 100644 index 8cd4825..0000000 --- a/app/controllers/repeater_node.py +++ /dev/null @@ -1,14 +0,0 @@ -from .base import RestController -from ..forms.repeater_node import RepeaterNodeForm -from ..models.repeater_node import RepeaterNode - -class RepeaterNodeController(RestController): - """A repeater node controller.""" - Model = RepeaterNode - filters = { - 'parent_id': lambda d: RepeaterNode.parent_id == d['parent_id'], - } - - def get_form(self, filter_data): - """Return the repeater node form.""" - return RepeaterNodeForm diff --git a/app/forms/apartment_node.py b/app/forms/apartment_node.py deleted file mode 100644 index ca61f84..0000000 --- a/app/forms/apartment_node.py +++ /dev/null @@ -1,45 +0,0 @@ -import wtforms as wtf -from .base import Form, UserForm - -class ApartmentNodeForm(Form, UserForm): - """ 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.StringField( - validators=[wtf.validators.Optional()] - ) - node_location = 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/boiler_node.py b/app/forms/boiler_node.py deleted file mode 100644 index 8350a89..0000000 --- a/app/forms/boiler_node.py +++ /dev/null @@ -1,33 +0,0 @@ -import wtforms as wtf -from .base import Form, UserForm - -class BoilerNodeForm(Form, UserForm): - """ 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.StringField( - 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/forms/repeater_node.py b/app/forms/repeater_node.py deleted file mode 100644 index d3c7835..0000000 --- a/app/forms/repeater_node.py +++ /dev/null @@ -1,24 +0,0 @@ -import wtforms as wtf -from .base import Form, UserForm - -class RepeaterNodeForm(Form, UserForm): - """ 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/apartment_node.py b/app/models/apartment_node.py deleted file mode 100644 index 7c134b4..0000000 --- a/app/models/apartment_node.py +++ /dev/null @@ -1,25 +0,0 @@ -from .base import Model, Tracked, User -from ..lib.database import db - -from sqlalchemy.ext.declarative import declared_attr - -class ApartmentNode(Model, Tracked, User, db.Model): - - @declared_attr - def __tablename__(cls): - return "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)) - 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/boiler_node.py b/app/models/boiler_node.py deleted file mode 100644 index dd76224..0000000 --- a/app/models/boiler_node.py +++ /dev/null @@ -1,21 +0,0 @@ -from .base import Model, Tracked, User -from ..lib.database import db - -from sqlalchemy.ext.declarative import declared_attr - -class BoilerNode(Model, User, Tracked, db.Model): - - @declared_attr - def __tablename__(cls): - return "boiler_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)) - 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/models/repeater_node.py b/app/models/repeater_node.py deleted file mode 100644 index 301180a..0000000 --- a/app/models/repeater_node.py +++ /dev/null @@ -1,18 +0,0 @@ -from .base import Model, Tracked, User -from ..lib.database import db - -from sqlalchemy.ext.declarative import declared_attr - -class RepeaterNode(Model, User, Tracked, db.Model): - - @declared_attr - def __tablename__(cls): - return "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/apartment_node.py b/app/views/apartment_node.py deleted file mode 100644 index fa2466b..0000000 --- a/app/views/apartment_node.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Views for working with accounts.""" -from .base import RestView -from ..controllers.apartment_node import ApartmentNodeController - -from flask import request - -class ApartmentNodeView(RestView): - """The apartment node view.""" - - def get_controller(self): - """Return an instance of the apartment node controller.""" - return ApartmentNodeController() diff --git a/app/views/boiler_node.py b/app/views/boiler_node.py deleted file mode 100644 index 1ac2aa1..0000000 --- a/app/views/boiler_node.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Views for working with accounts.""" -from .base import RestView -from ..controllers.boiler_node import BoilerNodeController - -from flask import request - -class BoilerNodeView(RestView): - """The boiler node view.""" - - def get_controller(self): - """Return an instance of the boiler node controller.""" - return BoilerNodeController() diff --git a/app/views/repeater_node.py b/app/views/repeater_node.py deleted file mode 100644 index a491af4..0000000 --- a/app/views/repeater_node.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Views for working with accounts.""" -from .base import RestView -from ..controllers.repeater_node import RepeaterNodeController - -from flask import request - -class RepeaterNodeView(RestView): - """The repeater node view.""" - - def get_controller(self): - """Return an instance of the repeater node controller.""" - return RepeaterNodeController() -- GitLab From 246626e13b7b416b21e21e5c2291af7c4449ec71 Mon Sep 17 00:00:00 2001 From: Conrad Date: Mon, 4 Dec 2017 12:36:42 -0500 Subject: [PATCH 03/11] Fix imports --- app/models/senseware_node.py | 4 ++-- app/views/__init__.py | 14 ++++---------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/app/models/senseware_node.py b/app/models/senseware_node.py index 57fbe27..95e7f48 100644 --- a/app/models/senseware_node.py +++ b/app/models/senseware_node.py @@ -3,11 +3,11 @@ from ..lib.database import db from sqlalchemy.ext.declarative import declared_attr -class GatewayNode(Model, User, Tracked, db.Model): +class SensewareNode(Model, User, Tracked, db.Model): @declared_attr def __tablename__(cls): - return "gateway_node" + return "senseware_node" __table_args__ = {"schema": "sensor"} diff --git a/app/views/__init__.py b/app/views/__init__.py index 3ffe834..31c75cc 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -1,12 +1,9 @@ """Flask-classy views for the flask application.""" from . import ( gateway, - boiler_node, - apartment_node, - repeater_node, - sensor_image, + senseware_node, data, - senseware_node + sensor_image, ) def register(app): @@ -17,9 +14,6 @@ def register(app): in the application. """ gateway.GatewayView.register(app) - boiler_node.BoilerNodeView.register(app) - apartment_node.ApartmentNodeView.register(app) - repeater_node.RepeaterNodeView.register(app) - sensor_image.SensorImageView.register(app) - data.DataView.register(app) senseware_node.SensewareNodeView.register(app) + data.DataView.register(app) + sensor_image.SensorImageView.register(app) -- GitLab From 161b839bf69d2c922a55f67a4d5cb4c5814bcd6d Mon Sep 17 00:00:00 2001 From: Conrad Date: Mon, 4 Dec 2017 12:57:41 -0500 Subject: [PATCH 04/11] Update gateway controller to get all nodes associated with the gateway --- app/controllers/gateway.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/app/controllers/gateway.py b/app/controllers/gateway.py index f668e1b..f314fc0 100644 --- a/app/controllers/gateway.py +++ b/app/controllers/gateway.py @@ -2,6 +2,7 @@ from .base import RestController from ..forms.gateway import GatewayForm from ..models.gateway import Gateway from ..controllers.data import DataController +from ..controllers.senseware_node import SensewareNodeController import datetime import json import psycopg2 @@ -27,22 +28,32 @@ class GatewayController(RestController): # If the user is also requesting data, get data if 'data' in filter_data and 'from' in filter_data and result: date = filter_data.get('from') - args = [('gateway_serial[]', gateway['gateway_serial']) for gateway in result] - args.append(('from', date)) - multi_dict_args = MultiDict(args) - data_result = DataController().index(multi_dict_args) - - # Map data to gateway serial numbers + args = [] + for gateway in result: + if gateway['gateway_serial']: + args.append(('gateway_serial[]', gateway['gateway_serial'])) data_mapping = {} - for record in data_result: - if record['sn'] not in data_mapping: - data_mapping[record['sn']] = [] - data_mapping[record['sn']].append(record) + # Continue if any of the gateways have serials + if args: + args.append(('from', date)) + multi_dict_args = MultiDict(args) + data_result = DataController().index(multi_dict_args) + + # Map data to gateway serial numbers + for record in data_result: + if record['sn'] not in data_mapping: + data_mapping[record['sn']] = [] + data_mapping[record['sn']].append(record) # Add the data to the gateways for gateway in result: if gateway['gateway_serial'] in data_mapping: gateway['data'] = data_mapping[gateway['gateway_serial']] else: gateway['data'] = [] + if 'nodes' in filter_data and result: + for gateway in result: + multi_dict_args = MultiDict([('gateway_id', gateway['id'])]) + node_result = SensewareNodeController().index(multi_dict_args) + gateway['nodes'] = node_result return result -- GitLab From 0e330500b3e57dae9395e62dc5d038a713bd3278 Mon Sep 17 00:00:00 2001 From: Conrad Date: Mon, 4 Dec 2017 13:20:27 -0500 Subject: [PATCH 05/11] Bump bpvalve --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7f1e1df..29029e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ arrow==0.7.0 blessed==1.9.5 botocore==1.5.48 -git+ssh://git@github.com/Blocp/bpvalve.git@v1.3.0 +git+ssh://git@github.com/Blocp/bpvalve.git@v1.3.1 cement==2.4.0 colorama==0.3.3 docker-py==1.1.0 -- GitLab From 4c04debd60684160d9df568a327a9e53772e8691 Mon Sep 17 00:00:00 2001 From: Conrad Date: Mon, 4 Dec 2017 13:51:19 -0500 Subject: [PATCH 06/11] Map to nodes instead of gateway --- app/controllers/data.py | 2 +- app/controllers/gateway.py | 51 ++++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/app/controllers/data.py b/app/controllers/data.py index 04ad7bf..b0f3150 100644 --- a/app/controllers/data.py +++ b/app/controllers/data.py @@ -29,7 +29,7 @@ class DataController(RestController): where_tuple = where_tuple + (node_module_number,) where_statement = where_statement[:-4] + ')' - sql = "SELECT * FROM data {}".format(where_statement) + sql = "SELECT ts, value, unit, name, sn, mod FROM data {} ORDER BY ts".format(where_statement) cur = self.redshift.db.cursor(cursor_factory=psycopg2.extras.DictCursor) try: cur.execute(sql, where_tuple) diff --git a/app/controllers/gateway.py b/app/controllers/gateway.py index f314fc0..f1e706c 100644 --- a/app/controllers/gateway.py +++ b/app/controllers/gateway.py @@ -26,34 +26,37 @@ class GatewayController(RestController): """ result = super().index(filter_data) # If the user is also requesting data, get data - if 'data' in filter_data and 'from' in filter_data and result: - date = filter_data.get('from') - args = [] - for gateway in result: - if gateway['gateway_serial']: - args.append(('gateway_serial[]', gateway['gateway_serial'])) - data_mapping = {} - # Continue if any of the gateways have serials - if args: - args.append(('from', date)) - multi_dict_args = MultiDict(args) - data_result = DataController().index(multi_dict_args) - - # Map data to gateway serial numbers - for record in data_result: - if record['sn'] not in data_mapping: - data_mapping[record['sn']] = [] - data_mapping[record['sn']].append(record) - # Add the data to the gateways - for gateway in result: - if gateway['gateway_serial'] in data_mapping: - gateway['data'] = data_mapping[gateway['gateway_serial']] - else: - gateway['data'] = [] if 'nodes' in filter_data and result: + node_ids = [] for gateway in result: multi_dict_args = MultiDict([('gateway_id', gateway['id'])]) node_result = SensewareNodeController().index(multi_dict_args) + for node in node_result: + if (node['node_id']): + node_ids.append(node['node_id']) gateway['nodes'] = node_result + if 'data' in filter_data and 'from' in filter_data and result: + date = filter_data.get('from') + args = [('node_id[]', node_id) for node_id in node_ids] + data_mapping = {} + # Continue if any of the gateways have serials + if args: + args.append(('from', date)) + multi_dict_args = MultiDict(args) + data_result = DataController().index(multi_dict_args) + # Map data to gateway serial numbers + for record in data_result: + key = str(record['mod']) + if key not in data_mapping: + data_mapping[key] = [] + data_mapping[key].append(record) + # Add the data to the nodes + for gateway in result: + for node in gateway['nodes']: + if node['node_id'] in data_mapping: + node['data'] = data_mapping[node['node_id']] + else: + node['data'] = [] + return result -- GitLab From 48b5ae995f75f2f2c3a35347208323d832b6fc3c Mon Sep 17 00:00:00 2001 From: Conrad Date: Tue, 5 Dec 2017 17:50:29 -0500 Subject: [PATCH 07/11] Update senseware-node form --- app/forms/senseware_node.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/app/forms/senseware_node.py b/app/forms/senseware_node.py index 896a7ba..01f5e14 100644 --- a/app/forms/senseware_node.py +++ b/app/forms/senseware_node.py @@ -1,6 +1,23 @@ import wtforms as wtf from .base import Form, UserForm +class NullableIntegerField(wtf.IntegerField): + """ + An IntegerField where the field can be null if the input data is an empty + string. + """ + + def process_formdata(self, valuelist): + if valuelist: + if valuelist[0] == '' or valuelist[0] is None: + self.data = None + else: + try: + self.data = int(valuelist[0]) + except ValueError: + self.data = None + raise ValueError(self.gettext('Not a valid integer value')) + class SensewareNodeForm(Form, UserForm): """ A form for validating senseware node requests.""" @@ -22,16 +39,16 @@ class SensewareNodeForm(Form, UserForm): surface_type = wtf.StringField( validators=[wtf.validators.Optional()] ) - temperature_probe_1 = wtf.IntegerField( + temperature_probe_1 = NullableIntegerField( validators=[wtf.validators.Optional()] ) - temperature_probe_2 = wtf.IntegerField( + temperature_probe_2 = NullableIntegerField( validators=[wtf.validators.Optional()] ) - temperature_probe_3 = wtf.IntegerField( + temperature_probe_3 = NullableIntegerField( validators=[wtf.validators.Optional()] ) - temperature_probe_4 = wtf.IntegerField( + temperature_probe_4 = NullableIntegerField( validators=[wtf.validators.Optional()] ) notes = wtf.StringField( -- GitLab From 3507fa6d86788fc40f6af23a3022300294937970 Mon Sep 17 00:00:00 2001 From: Conrad Date: Fri, 8 Dec 2017 12:07:31 -0500 Subject: [PATCH 08/11] Make space id a nullable integer field --- app/forms/senseware_node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/forms/senseware_node.py b/app/forms/senseware_node.py index 01f5e14..e25ab78 100644 --- a/app/forms/senseware_node.py +++ b/app/forms/senseware_node.py @@ -30,7 +30,7 @@ class SensewareNodeForm(Form, UserForm): node_id = wtf.StringField( validators=[wtf.validators.Optional()] ) - space_id = wtf.IntegerField( + space_id = wtf.NullableIntegerField( validators=[wtf.validators.Optional()] ) repeater = wtf.BooleanField( -- GitLab From cd36fa1055f03ddca727391c5f50ec03c66defed Mon Sep 17 00:00:00 2001 From: Conrad Date: Fri, 8 Dec 2017 12:08:39 -0500 Subject: [PATCH 09/11] Remove wtf prefix from form --- app/forms/senseware_node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/forms/senseware_node.py b/app/forms/senseware_node.py index e25ab78..98b278e 100644 --- a/app/forms/senseware_node.py +++ b/app/forms/senseware_node.py @@ -30,7 +30,7 @@ class SensewareNodeForm(Form, UserForm): node_id = wtf.StringField( validators=[wtf.validators.Optional()] ) - space_id = wtf.NullableIntegerField( + space_id = NullableIntegerField( validators=[wtf.validators.Optional()] ) repeater = wtf.BooleanField( -- GitLab From 7dbddbca24b68fbbdf05329e2440eb91a92f7407 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 11 Dec 2017 13:04:18 -0500 Subject: [PATCH 10/11] Combine redshift database uri --- app/config/development.default.py | 6 +----- app/config/local.default.py | 6 +----- app/config/production.default.py | 6 +----- app/config/staging.default.py | 6 +----- app/lib/redshift_database.py | 24 +++++++++++------------- 5 files changed, 15 insertions(+), 33 deletions(-) diff --git a/app/config/development.default.py b/app/config/development.default.py index 3df45be..ae446d6 100644 --- a/app/config/development.default.py +++ b/app/config/development.default.py @@ -3,6 +3,7 @@ import os SQLALCHEMY_TRACK_MODIFICATIONS = False SQLALCHEMY_DATABASE_URI = os.environ['DBURI'] +REDSHIFT_DATABASE_URI = os.environ['REDSHIFT_DATABASE_URI'] REDIS_URI = os.environ['REDISURI'] @@ -32,11 +33,6 @@ AUTH0_CLAIMS_NAMESPACE = os.environ['AUTH0_CLAIMS_NAMESPACE'] AUTH0_CLIENT_ID = os.environ['AUTH0_CLIENT_ID'] AUTH0_CLIENT_SECRET = os.environ['AUTH0_CLIENT_SECRET'] -REDSHIFT_DB_NAME = os.environ['REDSHIFT_DB_NAME'] -REDSHIFT_USER = os.environ['REDSHIFT_USER'] -REDSHIFT_PASSWORD = os.environ['REDSHIFT_PASSWORD'] -REDSHIFT_HOST = os.environ['REDSHIFT_HOST'] - # Blocpower auth information. HEADER_AUTH_KEY = 'x-blocpower-auth-key' HEADER_AUTH_TOKEN = 'x-blocpower-auth-token' diff --git a/app/config/local.default.py b/app/config/local.default.py index 5c175ce..9272f39 100644 --- a/app/config/local.default.py +++ b/app/config/local.default.py @@ -1,5 +1,6 @@ SQLALCHEMY_TRACK_MODIFICATIONS = False SQLALCHEMY_DATABASE_URI = 'sqlite://' +REDSHIFT_DATABASE_URI = 'redshift://' REDIS_URI = 'redis://127.0.0.1:6379/' @@ -29,11 +30,6 @@ AUTH0_CLAIMS_NAMESPACE = '$AUTH0_CLAIMS_NAMESPACE' AUTH0_CLIENT_ID = '$AUTH0_CLIENT_ID' AUTH0_CLIENT_SECRET = '$AUTH0_CLIENT_SECRET' -REDSHIFT_DB_NAME = '$REDSHIFT_DB_NAME' -REDSHIFT_USER = '$REDSHIFT_USER' -REDSHIFT_PASSWORD = '$REDSHIFT_PASSWORD' -REDSHIFT_HOST = '$REDSHIFT_HOST' - # Blocpower auth information. HEADER_AUTH_KEY = 'x-blocpower-auth-key' HEADER_AUTH_TOKEN = 'x-blocpower-auth-token' diff --git a/app/config/production.default.py b/app/config/production.default.py index a1d8c87..b0beed6 100644 --- a/app/config/production.default.py +++ b/app/config/production.default.py @@ -3,6 +3,7 @@ import os SQLALCHEMY_TRACK_MODIFICATIONS = False SQLALCHEMY_DATABASE_URI = os.environ['DBURI'] +REDSHIFT_DATABASE_URI = os.environ['REDSHIFT_DATABASE_URI'] REDIS_URI = os.environ['REDISURI'] @@ -32,11 +33,6 @@ AUTH0_CLAIMS_NAMESPACE = os.environ['AUTH0_CLAIMS_NAMESPACE'] AUTH0_CLIENT_ID = os.environ['AUTH0_CLIENT_ID'] AUTH0_CLIENT_SECRET = os.environ['AUTH0_CLIENT_SECRET'] -REDSHIFT_DB_NAME = os.environ['REDSHIFT_DB_NAME'] -REDSHIFT_USER = os.environ['REDSHIFT_USER'] -REDSHIFT_PASSWORD = os.environ['REDSHIFT_PASSWORD'] -REDSHIFT_HOST = os.environ['REDSHIFT_HOST'] - # Blocpower auth information. HEADER_AUTH_KEY = 'x-blocpower-auth-key' HEADER_AUTH_TOKEN = 'x-blocpower-auth-token' diff --git a/app/config/staging.default.py b/app/config/staging.default.py index 86482c6..1e5d971 100644 --- a/app/config/staging.default.py +++ b/app/config/staging.default.py @@ -3,6 +3,7 @@ import os SQLALCHEMY_TRACK_MODIFICATIONS = False SQLALCHEMY_DATABASE_URI = os.environ['DBURI'] +REDSHIFT_DATABASE_URI = os.environ['REDSHIFT_DATABASE_URI'] REDIS_URI = os.environ['REDISURI'] @@ -32,11 +33,6 @@ AUTH0_CLAIMS_NAMESPACE = os.environ['AUTH0_CLAIMS_NAMESPACE'] AUTH0_CLIENT_ID = os.environ['AUTH0_CLIENT_ID'] AUTH0_CLIENT_SECRET = os.environ['AUTH0_CLIENT_SECRET'] -REDSHIFT_DB_NAME = os.environ['REDSHIFT_DB_NAME'] -REDSHIFT_USER = os.environ['REDSHIFT_USER'] -REDSHIFT_PASSWORD = os.environ['REDSHIFT_PASSWORD'] -REDSHIFT_HOST = os.environ['REDSHIFT_HOST'] - # Blocpower auth information. HEADER_AUTH_KEY = 'x-blocpower-auth-key' HEADER_AUTH_TOKEN = 'x-blocpower-auth-token' diff --git a/app/lib/redshift_database.py b/app/lib/redshift_database.py index f118c19..d5409f2 100644 --- a/app/lib/redshift_database.py +++ b/app/lib/redshift_database.py @@ -1,26 +1,24 @@ +from urllib.parse import urlparse import psycopg2 + class RedshiftWrapper(object): """A wrapper for Redshift.""" db = None def init_app(self, app): """Stores information about the redis database from the URI.""" - REDSHIFT_DB_NAME = app.config.get('REDSHIFT_DB_NAME') - REDSHIFT_USER = app.config.get('REDSHIFT_USER') - REDSHIFT_PASSWORD = app.config.get('REDSHIFT_PASSWORD') - REDSHIFT_HOST = app.config.get('REDSHIFT_HOST') - conn_string = """ - dbname='{}' - port='5439' - user='{}' - password='{}' - host='{}' - """.format(REDSHIFT_DB_NAME, REDSHIFT_USER, REDSHIFT_PASSWORD, REDSHIFT_HOST) - self.db = psycopg2.connect(conn_string) + uri = urlparse(app.config.get('REDSHIFT_DATABASE_URI')) + + self.db = psycopg2.connect( + user=uri.username, + password=uri.password, + host=uri.hostname, + port=5439, + dbname=uri.path[1:] + ) redshift = RedshiftWrapper() def register(app): redshift.init_app(app) - -- GitLab From 3270a3fcb45754332a6910d4c79f7d0091bc84fa Mon Sep 17 00:00:00 2001 From: Conrad Date: Mon, 11 Dec 2017 15:24:14 -0500 Subject: [PATCH 11/11] Add unit id --- app/controllers/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/data.py b/app/controllers/data.py index b0f3150..5b9c6cc 100644 --- a/app/controllers/data.py +++ b/app/controllers/data.py @@ -29,7 +29,7 @@ class DataController(RestController): where_tuple = where_tuple + (node_module_number,) where_statement = where_statement[:-4] + ')' - sql = "SELECT ts, value, unit, name, sn, mod FROM data {} ORDER BY ts".format(where_statement) + sql = "SELECT ts, value, unit, unit_id, name, sn, mod FROM data {} ORDER BY ts".format(where_statement) cur = self.redshift.db.cursor(cursor_factory=psycopg2.extras.DictCursor) try: cur.execute(sql, where_tuple) -- GitLab