diff --git a/app/__init__.py b/app/__init__.py index dc103fa2f517a1a3731ecb86483afcd5e3ad0cbd..6d91b7d125c1bf9bd920c639dd3cd5e2578b0677 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -27,11 +27,12 @@ def create_app(config): from . import views views.register(app) - from app.lib import database, red, exceptions, service, session, auth0 - services = (database, red, exceptions, service, session, auth0) + from app.lib import database, red, exceptions, service, session, auth0, redshift_database + services = (database, red, exceptions, service, session, auth0, redshift_database) for service in services: service.register(app) + # with app.app_context(): # database.db.create_all() diff --git a/app/controllers/base.py b/app/controllers/base.py index cf86692dd25dcda3e91e3b7f0defdc7a89b4c12b..2e60fb7a7a78c713ad6c4d02ed70b605b8e8885e 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -1,6 +1,6 @@ """Flask RestController wrapper""" from bpvalve.flask.controllers import RestController as BaseRestController -from app.lib import db, redis, auth0_ +from app.lib import db, redis, auth0_, redshift class RestController(BaseRestController): @@ -10,3 +10,4 @@ class RestController(BaseRestController): db = db redis = redis auth0 = auth0_ + redshift = redshift diff --git a/app/controllers/data.py b/app/controllers/data.py new file mode 100644 index 0000000000000000000000000000000000000000..04ad7bf70d84a8283e36df09683f1c4de12aeb1e --- /dev/null +++ b/app/controllers/data.py @@ -0,0 +1,41 @@ +from .base import RestController +import datetime +import json +import psycopg2 +from werkzeug.exceptions import BadRequest + +class DataController(RestController): + """A data controller.""" + + def index(self, filter_data): + if 'from' not in filter_data: + raise BadRequest("'from' is a required field in the query params") + date = filter_data.get('from') + gateway_serials = filter_data.getlist('gateway_serial[]') + node_module_numbers = filter_data.getlist('node_id[]') + + # Set up the WHERE statement with date info + where_statement = 'WHERE ts>%s AND (' + where_tuple = (date,) + + # Add the gateway serials to hte WHERE statement + for gateway_serial in gateway_serials: + where_statement += "sn=%s OR " + where_tuple = where_tuple + (gateway_serial,) + + # Add the node_module_ids to hte WHERE statement + for node_module_number in node_module_numbers: + where_statement += "mod=%s OR " + where_tuple = where_tuple + (node_module_number,) + + where_statement = where_statement[:-4] + ')' + sql = "SELECT * FROM data {}".format(where_statement) + cur = self.redshift.db.cursor(cursor_factory=psycopg2.extras.DictCursor) + try: + cur.execute(sql, where_tuple) + except Exception as e: + cur.close() + self.redshift.db.rollback() + raise e + + return [dict(record) for record in cur] diff --git a/app/controllers/gateway.py b/app/controllers/gateway.py index eefece40cea97267861aebc12d7978ac0c6eb845..f668e1bb9d877280d49970fa597d4c9827ad9df4 100644 --- a/app/controllers/gateway.py +++ b/app/controllers/gateway.py @@ -1,6 +1,11 @@ from .base import RestController from ..forms.gateway import GatewayForm from ..models.gateway import Gateway +from ..controllers.data import DataController +import datetime +import json +import psycopg2 +from werkzeug.datastructures import MultiDict class GatewayController(RestController): """A sensor controller.""" @@ -13,3 +18,31 @@ class GatewayController(RestController): def get_form(self, filter_data): """Return the sensor form.""" return GatewayForm + + def index(self, filter_data): + """ + Retrieve a list of gateways + """ + 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 = [('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 + data_mapping = {} + 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'] = [] + + return result diff --git a/app/lib/__init__.py b/app/lib/__init__.py index 3fac2006f832124ac650884f896339cd69d51619..90e9f86aab7bab7275fc8aaff0ec80e319422608 100644 --- a/app/lib/__init__.py +++ b/app/lib/__init__.py @@ -1,3 +1,4 @@ from .database import db from .red import redis from .auth0 import auth0_ +from .redshift_database import redshift diff --git a/app/lib/redshift_database.py b/app/lib/redshift_database.py new file mode 100644 index 0000000000000000000000000000000000000000..ba2fb79b9dee548c6bd1752632a10be8b66b09d1 --- /dev/null +++ b/app/lib/redshift_database.py @@ -0,0 +1,26 @@ +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.""" + DB_NAME = 'senseware' + USER = 'blocpower' + PASSWORD = 't106DZpVcCO9' + HOST = 'senseware.cz6saalxbzqr.us-east-1.redshift.amazonaws.com' + conn_string = """ + dbname='{}' + port='5439' + user='{}' + password='{}' + host='{}' + """.format(DB_NAME, USER, PASSWORD, HOST) + self.db = psycopg2.connect(conn_string) + +redshift = RedshiftWrapper() + +def register(app): + redshift.init_app(app) + diff --git a/app/views/__init__.py b/app/views/__init__.py index 8cb25c8a6a36f5604c11aa5124cacbb31ffd5b03..80bafc5409aeddb378daec69936ef467bcaac340 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -5,6 +5,7 @@ from . import ( apartment_node, repeater_node, sensor_image, + data, ) def register(app): @@ -19,3 +20,4 @@ def register(app): apartment_node.ApartmentNodeView.register(app) repeater_node.RepeaterNodeView.register(app) sensor_image.SensorImageView.register(app) + data.DataView.register(app) diff --git a/app/views/data.py b/app/views/data.py new file mode 100644 index 0000000000000000000000000000000000000000..c50e6d3fbde3911880051921d02a13f251063c06 --- /dev/null +++ b/app/views/data.py @@ -0,0 +1,25 @@ +"""Views for working with accounts.""" +from .base import RestView +from ..controllers.data import DataController + +from flask import request +from werkzeug.exceptions import MethodNotAllowed + +class DataView(RestView): + """The data view.""" + + def get_controller(self): + """Return an instance of the data controller.""" + return DataController() + + def get(self, id_): + raise MethodNotAllowed() + + def put(self, id_): + raise MethodNotAllowed() + + def delete(self, id_): + raise MethodNotAllowed() + + def post(self): + raise MethodNotAllowed()