def get_rule_matches(id): """ This route can operate in 3 ways. With no parameters, it looks at yesterday (from the second most recent midnight till the most recent midnight). With a `since` parameter, it uses the range from `since` until now. With `start_time` and `end_time`, it uses that range. It also checks if there are any values at all since the start time, and if there are none, this likely means that there's some problem with the Value Pipeline, and today's values haven't been imported yet. To just report 0 would be a false negative, so we throw a 404 instead, so the client knows that there was a failure. """ since = trycast_int(request.values.get("since")) start_time = trycast_int(request.values.get("start_time")) end_time = trycast_int(request.values.get("end_time")) if since is not None: start = since end = int(dt.datetime.now().timestamp()) elif None not in (start_time, end_time): start = start_time end = end_time else: end = int(dt.datetime.combine(dt.date.today(), dt.time()).timestamp()) start = end - 60 * 60 * 24 if not Values.has_any_since(start): abort(404, "No values since start time %s" % start) (point_search, value_search) = Rules.searches(id) point_ids = Points.ids_where(Search.points(point_search)) return Values.get(point_ids, start, end, Search.values(value_search))
def test_where_tag(self): self.assertEqual( Rooms.where(Search.rooms("#11")), """[{"room_id":3,"room_name":"102","building_id":1,"building_name":"CMC","floor":1,"description":null,"tags":["math_stats","academic","computer_science"]}, {"room_id":2,"room_name":"304","building_id":1,"building_name":"CMC","floor":3,"description":null,"tags":["math_stats","academic","computer_science"]}, {"room_id":1,"room_name":"328","building_id":1,"building_name":"CMC","floor":3,"description":"Fishbowl","tags":["classroom","math_stats","academic","computer_science"]}]""" )
def test_where_tag(self): self.assertEqual( Devices.where(Search.devices("#11")), """[{"device_id":2,"device_name":"Fishbowl vav","room_id":1,"room_name":"328","building_id":1,"building_name":"CMC","tags":["classroom","math_stats","academic","computer_science"],"description":null}, {"device_id":1,"device_name":"Fishbowl thermostat","room_id":1,"room_name":"328","building_id":1,"building_name":"CMC","tags":["classroom","math_stats","academic","thermostat","computer_science"],"description":null}, {"device_id":4,"device_name":"102 Lab thermostat","room_id":3,"room_name":"102","building_id":1,"building_name":"CMC","tags":["math_stats","academic","computer_science"],"description":null}]""" )
def test_where_tag(self): self.assertEqual( Points.where(Search.points("#11")), """[{"point_id":2,"point_name":"CMC.328.SP","device_id":1,"device_name":"Fishbowl thermostat","room_id":1,"room_name":"328","building_id":1,"building_name":"CMC","value_type":{"value_type_id":4,"type":["NRML", "FAULT", "OFFNRML", "HIGH", "LOW"]},"value_unit":{"value_unit_id":1,"measurement":"temperature","unit":"fahrenheit"},"tags":["classroom","math_stats","academic","thermostat","computer_science","room_temp","set"],"description":"Thermostat Set Point in CMC 328"}, {"point_id":1,"point_name":"CMC.328.RT","device_id":1,"device_name":"Fishbowl thermostat","room_id":1,"room_name":"328","building_id":1,"building_name":"CMC","value_type":{"value_type_id":2,"type":1},"value_unit":{"value_unit_id":1,"measurement":"temperature","unit":"fahrenheit"},"tags":["classroom","math_stats","academic","thermostat","computer_science","get","room_temp"],"description":"Room Temp in CMC 328"}, {"point_id":5,"point_name":"CMC.102.SP","device_id":4,"device_name":"102 Lab thermostat","room_id":3,"room_name":"102","building_id":1,"building_name":"CMC","value_type":{"value_type_id":2,"type":1},"value_unit":{"value_unit_id":1,"measurement":"temperature","unit":"fahrenheit"},"tags":["math_stats","academic","computer_science","set"],"description":"Thermostat Set Point in CMC 102"}]""" )
def test_where_building_or_device(self): self.assertEqual( Points.where(Search.points("@2 or %4")), """[{"point_id":4,"point_name":"EV.RM107.SP","device_id":3,"device_name":"Thermostat in Evans 107","room_id":4,"room_name":"107","building_id":2,"building_name":"Evans","value_type":{"value_type_id":3,"type":1.0},"value_unit":{"value_unit_id":1,"measurement":"temperature","unit":"fahrenheit"},"tags":["thermostat","residential","single","residence","room_temp","set"],"description":"Thermostat Set Point in Evans 107"}, {"point_id":3,"point_name":"EV.RM107.RT","device_id":3,"device_name":"Thermostat in Evans 107","room_id":4,"room_name":"107","building_id":2,"building_name":"Evans","value_type":{"value_type_id":2,"type":1},"value_unit":{"value_unit_id":1,"measurement":"temperature","unit":"fahrenheit"},"tags":["thermostat","residential","get","single","residence","room_temp"],"description":"Room Temp in Evans 107"}, {"point_id":5,"point_name":"CMC.102.SP","device_id":4,"device_name":"102 Lab thermostat","room_id":3,"room_name":"102","building_id":1,"building_name":"CMC","value_type":{"value_type_id":2,"type":1},"value_unit":{"value_unit_id":1,"measurement":"temperature","unit":"fahrenheit"},"tags":["math_stats","academic","computer_science","set"],"description":"Thermostat Set Point in CMC 102"}]""" )
def search_verify(): search_query = request.values.get('search') if not search_query: abort(400, "Request must include a `search` argument") try: return Points.counts_where(Search.points(search_query)) or "[]" except psycopg2.Error as e: # Any InvalidSearchException will be thrown and result in a 500. return "Invalid Point Search: " + str(e.pgerror)
def get_buildings(): search_query = request.values.get('search') if search_query: try: return Buildings.where(Search.buildings(search_query)) or "[]" except InvalidSearchException as e: abort(400, e) else: return Buildings.all() or "[]"
def get_points_ids(): search_query = request.values.get('search') if not search_query: abort(400, "Must include search parameter") try: return jsonify(Points.ids_where(Search.points(search_query))) or "[]" except InvalidSearchException as e: abort(400, e)
def get_values(): # Some clients put `[]` at the end of array parameters. We can handle either case. point_ids = request.values.getlist('point_ids') or request.values.getlist( 'point_ids[]') start_time = request.values.get('start_time') end_time = request.values.get('end_time') search = request.values.get('search') if None in (point_ids, start_time, end_time): abort(400, "Missing required parameter") search_sql = Search.values(search) return Values.get(tuple(point_ids), start_time, end_time, search_sql) or "[]"
def values_verify(): try: point_ids = request.values.getlist( 'point_ids') or request.values.getlist('point_ids[]') start_time = request.values.get('start_time') end_time = request.values.get('end_time') search = request.values.get('search') if search is None: abort(400, "Request must include a `search` argument") search_sql = Search.values(search) return "%s values found" % Values.get_count( tuple(point_ids), start_time, end_time, search_sql) except InvalidSearchException: return "Invalid Value Search Syntax" except psycopg2.Error as e: # Any InvalidSearchException will be thrown and result in a 500. return "Invalid Value Search: " + str(e.pgerror)
def test_ids_where_tag(self): self.assertEqual(set(Rooms.ids_where(Search.rooms("#11"))), {1, 2, 3})
def test_simple_tag(self): self.assertEqual( Search.rooms("#1"), " (rooms_tags.tag_id = 1 OR buildings_tags.tag_id = 1)")
def test_ids_where_building_or_device(self): with self.assertRaises(Exception): Rooms.ids_where(Search.rooms("@2 or %4"))
def test_simple_unit(self): with self.assertRaises(Exception): Search.rooms(":unit 5")
def test_simple_or(self): self.assertEqual(Search.rooms("or"), " OR")
def test_parenthesis_building_room_device(self): self.assertEqual( Search.rooms("(:floor = 2 or @3) and $2"), "( rooms.floor = 2 OR buildings.building_id = 3) AND rooms.room_id = 2" )
def test_simple_building(self): self.assertEqual(Search.rooms("@12"), " buildings.building_id = 12")
def test_building_room_device(self): self.assertEqual(Search.rooms("@3 and $2"), " buildings.building_id = 3 AND rooms.room_id = 2")
def test_device_point(self): with self.assertRaises(Exception): Search.rooms("%310 or *78")
def test_building_room(self): self.assertEqual(Search.rooms("@3 and $7"), " buildings.building_id = 3 AND rooms.room_id = 7")
def test_simple_measurement(self): with self.assertRaises(Exception): Search.rooms(":measurement 'temperature'")
def test_ids_where_building_and_tag(self): self.assertEqual(set(Rooms.ids_where(Search.rooms("@1 and #2"))), set())
def test_ids_where_tag_not_building(self): self.assertEqual(set(Rooms.ids_where(Search.rooms("#7 and not @1"))), {4})
def test_simple_floor(self): self.assertEqual(Search.rooms(":floor = 3"), " rooms.floor = 3")
def test_simple_not(self): self.assertEqual(Search.rooms("not"), " NOT")
def test_building_floor(self): self.assertEqual(Search.rooms("@3 and :floor > 2"), " buildings.building_id = 3 AND rooms.floor > 2")
def test_ids_where_building(self): self.assertEqual(set(Rooms.ids_where(Search.rooms("@2"))), {4})
def test_ids_where_building_floor(self): self.assertEqual( set(Rooms.ids_where(Search.rooms("@1 and :floor > 2"))), {1, 2})
def test_nested_parenthesis_building_room_device(self): self.assertEqual( Search.rooms(":floor = 2 or (@3 or $2)"), " rooms.floor = 2 OR( buildings.building_id = 3 OR rooms.room_id = 2)" )
def test_simple_type(self): with self.assertRaises(Exception): Search.rooms(":type 4")