def test_on_http_grid_response(self, server_session): (server, session) = server_session # Reset our counter session._on_http_grid_response_called = 0 session._on_http_grid_response_last_args = None # Fetch a grid op = session._get_grid("dummy", callback=lambda *a, **kwa: None) # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Make a grid to respond with expected = hszinc.Grid() expected.column["empty"] = {} rq.respond( status=200, headers={b"Content-Type": "text/zinc"}, content=hszinc.dump(expected, mode=hszinc.MODE_ZINC), ) # Our dummy function should have been called assert session._on_http_grid_response_called == 1 assert isinstance(session._on_http_grid_response_last_args, tuple) assert len(session._on_http_grid_response_last_args) == 3 (response, args, kwargs) = session._on_http_grid_response_last_args assert isinstance(response, HTTPResponse) assert len(args) == 0 assert len(kwargs) == 0
def _on_read(self, ids, filter_expr, limit, callback, **kwargs): if isinstance(ids, string_types) or isinstance(ids, hszinc.Ref): # Make sure we always pass a list. ids = [ids] if bool(ids): if filter_expr is not None: raise ValueError("Either specify ids or filter_expr, not both") ids = [self._obj_to_ref(r) for r in ids] if len(ids) == 1: # Reading a single entity return self._get_grid("read", callback, args={"id": ids[0]}, **kwargs) else: # Reading several entities grid = hszinc.Grid() grid.column["id"] = {} grid.extend([{"id": r} for r in ids]) return self._post_grid("read", grid, callback, **kwargs) else: args = {"filter": filter_expr} if limit is not None: args["limit"] = int(limit) return self._get_grid("read", callback, args=args, **kwargs)
def test_grid_types_json(): innergrid = hszinc.Grid(version=hszinc.VER_3_0) innergrid.column['comment'] = {} innergrid.extend([ { 'comment': 'A innergrid', }, ]) grid = hszinc.Grid(version=hszinc.VER_3_0) grid.column['inner'] = {} grid.extend([ { 'inner': innergrid, }, ]) grid_str = hszinc.dump(grid, mode=hszinc.MODE_JSON) grid_json = json.loads(grid_str) assert grid_json == { 'meta': { 'ver': '3.0' }, 'cols': [ { 'name': 'inner' }, ], 'rows': [ { 'inner': { 'meta': { 'ver': '3.0' }, 'cols': [ { 'name': 'comment' }, ], 'rows': [ { 'comment': 's:A innergrid' }, ], } }, ], }
def _on_watch_poll(self, watch, refresh, callback, **kwargs): grid = hszinc.Grid() grid.column["empty"] = {} if not isinstance(watch, string_types): watch = watch.id grid.metadata["watchId"] = watch return self._post_grid("watchPoll", grid, callback, **kwargs)
def make_grid_meta(version=hszinc.VER_2_0): grid = hszinc.Grid(version=version) grid.metadata['aString'] = 'aValue' grid.metadata['aNumber'] = 3.14159 grid.metadata['aNull'] = None grid.metadata['aMarker'] = hszinc.MARKER grid.metadata['aQuantity'] = hszinc.Quantity(123,'Hz') grid.column['empty'] = {} return grid
def dict_to_grid(d): if not "id" in d.keys(): raise ValueError('Dict must contain an "id" key.') new_grid = hszinc.Grid() new_grid.metadata["id"] = d["id"] for k, v in d.items(): new_grid.column[k] = {} new_grid.append(d) return new_grid
def nav_view(request): g = hszinc.Grid() navId = request.GET.get('navId') root = None if navId: try: root = Entity.objects.get(entity_id=navId) except Entity.DoesNotExist: return _hzinc_response(g, status=404) entities = None if root: eid = root.entity_id if 'id' in root.kv_tags: eid = root.kv_tags['id'] # list the entities linked to root if 'site' in root.m_tags: entities = Entity.objects.filter(kv_tags__contains={ 'siteRef': eid }).exclude(kv_tags__has_key='equipRef') elif 'equip' in root.m_tags: entities = Entity.objects.filter( kv_tags__contains={'equipRef': eid}) else: # list the sites entities = Entity.objects.filter(m_tags__contains=['site']) if not entities or len(entities) == 0: return _hzinc_response(g, status=404) # stores the entity_id added_fields = [] data = [] g.column['navId'] = {} # read which fields to include for e in entities: e_data = {'navId': e.entity_id} if e.kv_tags: for f in e.kv_tags.keys(): e_data[f] = e.kv_tags[f] if f not in added_fields: added_fields.append(f) g.column[f] = {} for f in e.m_tags: e_data[f] = hszinc.MARKER if f not in added_fields: added_fields.append(f) g.column[f] = {} logger.info("nav_view: add data %s", e_data) data.append(e_data) # set the data g.extend(data) return _hzinc_response(g)
def make_col_meta(version=hszinc.VER_2_0): grid = hszinc.Grid(version=version) col_meta = hszinc.MetadataObject() col_meta['aString'] = 'aValue' col_meta['aNumber'] = 3.14159 col_meta['aNull'] = None col_meta['aMarker'] = hszinc.MARKER col_meta['aQuantity'] = hszinc.Quantity(123,'Hz') grid.column['empty'] = col_meta return grid
def _on_invoke_action(self, entity, action, callback, action_args, **kwargs): grid = hszinc.Grid() grid.metadata["id"] = self._obj_to_ref(entity) grid.metadata["action"] = action for arg in action_args.keys(): grid.column[arg] = {} grid.append(action_args) return self._post_grid("invokeAction", grid, callback, **kwargs)
def _on_watch_sub(self, points, watch_id, watch_dis, lease, callback, **kwargs): grid = hszinc.Grid() grid.column["id"] = {} grid.extend([{"id": self._obj_to_ref(p)} for p in points]) if watch_id is not None: grid.metadata["watchId"] = watch_id if watch_dis is not None: grid.metadata["watchDis"] = watch_dis if lease is not None: grid.metadata["lease"] = lease return self._post_grid("watchSub", grid, callback, **kwargs)
def test_formats(self, server_session): (server, session) = server_session op = session.formats() # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Request shall be a GET assert rq.method == "GET", "Expecting GET, got %s" % rq # Request shall be for base + 'api/formats' assert rq.uri == BASE_URI + "api/formats" # Accept header shall be given assert rq.headers[b"Accept"] == "text/zinc" # Make a grid to respond with expected = hszinc.Grid() expected.column["mime"] = {} expected.column["receive"] = {} expected.column["send"] = {} expected.extend([ { "mime": "text/csv", "receive": hszinc.MARKER, "send": hszinc.MARKER }, { "mime": "text/zinc", "receive": hszinc.MARKER, "send": hszinc.MARKER }, { "mime": "application/json", "receive": hszinc.MARKER, "send": hszinc.MARKER, }, ]) rq.respond( status=200, headers={b"Content-Type": "text/zinc"}, content=hszinc.dump(expected, mode=hszinc.MODE_ZINC), ) # State machine should now be done assert op.is_done actual = op.result grid_cmp(expected, actual)
def test_about(self, server_session): (server, session) = server_session op = session.about() # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Request shall be a GET assert rq.method == 'GET', 'Expecting GET, got %s' % rq # Request shall be for base + 'api/about' assert rq.uri == BASE_URI + 'api/about' # Accept header shall be given assert rq.headers['Accept'] == 'text/zinc' # Make a grid to respond with expected = hszinc.Grid() expected.column['haystackVersion'] = {} expected.column['tz'] = {} expected.column['serverName'] = {} expected.column['serverTime'] = {} expected.column['serverBootTime'] = {} expected.column['productName'] = {} expected.column['productUri'] = {} expected.column['productVersion'] = {} expected.column['moduleName'] = {} expected.column['moduleVersion'] = {} expected.append({ 'haystackVersion': '2.0', 'tz': 'UTC', 'serverName': 'pyhaystack dummy server', 'serverTime': datetime.datetime.now(tz=pytz.UTC), 'serverBootTime': datetime.datetime.now(tz=pytz.UTC), 'productName': 'pyhaystack dummy server', 'productVersion': '0.0.1', 'productUri': hszinc.Uri('http://pyhaystack.readthedocs.io'), 'moduleName': 'tests.client.base', 'moduleVersion': '0.0.1', }) rq.respond(status=200, headers={ 'Content-Type': 'text/zinc', }, content=hszinc.dump(expected, mode=hszinc.MODE_ZINC)) # State machine should now be done assert op.is_done actual = op.result grid_cmp(expected, actual)
def formats_view(request): g = hszinc.Grid() g.column['mime'] = {} g.column['read'] = {} g.column['write'] = {} g.extend([{ 'mime': 'text/zinc', 'read': hszinc.MARKER, 'write': hszinc.MARKER }]) return HttpResponse(hszinc.dump(g), content_type="text/zinc;charset=utf-8")
def _on_watch_sub(self, points, watch_id, watch_dis, lease, callback, **kwargs): grid = hszinc.Grid() grid.column['id'] = {} grid.extend([{'id': self._obj_to_ref(p)} for p in points]) if watch_id is not None: grid.metadata['watchId'] = watch_id if watch_dis is not None: grid.metadata['watchDis'] = watch_dis if lease is not None: grid.metadata['lease'] = lease return self._post_grid('watchSub', grid, callback, **kwargs)
def test_data_types_v3(): grid = hszinc.Grid(version=hszinc.VER_3_0) grid.column['comment'] = {} grid.column['value'] = {} grid.extend([ { 'comment': 'A NA', 'value': hszinc.NA, }, { 'comment': 'An empty list', 'value': [], }, { 'comment': 'A null value in a list', 'value': [None], }, { 'comment': 'A marker in a list', 'value': [hszinc.MARKER], }, { 'comment': 'Booleans', 'value': [True, False], }, { 'comment': 'References', 'value': [hszinc.Ref('a-ref'), hszinc.Ref('a-ref', 'a value')], }, { 'comment': 'A quantity', 'value': [hszinc.Quantity(500, 'miles')], }, { 'comment': 'A XStr', 'value': [hszinc.XStr("hex", 'deadbeef')], }, ]) grid_str = hszinc.dump(grid) ref_str = '''ver:"3.0" comment,value "A NA",NA "An empty list",[] "A null value in a list",[N] "A marker in a list",[M] "Booleans",[T,F] "References",[@a-ref,@a-ref "a value"] "A quantity",[500miles] "A XStr",[hex("deadbeef")] ''' assert grid_str == ref_str
def test_list_zinc_v2(): try: grid = hszinc.Grid(version=hszinc.VER_2_0) grid.column['comment'] = {} grid.column['value'] = {} grid.extend([{ 'comment': 'An empty list', 'value': [], }]) hszinc.dump(grid, mode=hszinc.MODE_ZINC) assert False, 'Project Haystack 2.0 doesn\'t support lists' except ValueError: pass
def test_dict_json_v2(): try: grid = hszinc.Grid(version=hszinc.VER_2_0) grid.column['comment'] = {} grid.column['value'] = {} grid.extend([{ 'comment': 'An empty dict', 'value': {}, }]) hszinc.dump(grid, mode=hszinc.MODE_JSON) assert False, 'Project Haystack 2.0 doesn\'t support dict' except ValueError: pass
def test_grid_types_zinc(): innergrid = hszinc.Grid(version=hszinc.VER_3_0) innergrid.column['comment'] = {} innergrid.extend([ { 'comment': 'A innergrid', }, ]) grid = hszinc.Grid(version=hszinc.VER_3_0) grid.column['inner'] = {} grid.extend([ { 'inner': innergrid, }, ]) grid_str = hszinc.dump(grid, mode=hszinc.MODE_ZINC) assert grid_str == ('ver:"3.0"\n' 'inner\n' '<<ver:"3.0"\n' 'comment\n' '"A innergrid"\n' '>>\n')
def _on_watch_unsub(self, watch, points, callback, **kwargs): grid = hszinc.Grid() grid.column["id"] = {} if not isinstance(watch, string_types): watch = watch.id grid.metadata["watchId"] = watch if points is not None: grid.extend([{"id": self._obj_to_ref(p)} for p in points]) else: grid.metadata["close"] = hszinc.MARKER return self._post_grid("watchSub", grid, callback, **kwargs)
def invoke_action(self, entity, action, callback=None, **kwargs): """ entity is either the ID of the entity, or an instance of the entity to invoke the named action on. Keyword arguments give any additional parameters required for the user action. """ grid = hszinc.Grid() grid.metadata['id'] = self._obj_to_ref(entity) grid.metadata['action'] = action for arg in kwargs.keys(): grid.column[arg] = {} grid.append(kwargs) return self._post_grid('invokeAction', grid, callback)
def _on_his_write(self, point, timestamp_records, callback, **kwargs): grid = hszinc.Grid() grid.metadata['id'] = self._obj_to_ref(point) grid.column['ts'] = {} grid.column['val'] = {} if hasattr(timestamp_records, 'to_dict'): timestamp_records = timestamp_records.to_dict() timestamp_records = list(timestamp_records.items()) timestamp_records.sort(key=lambda rec: rec[0]) for (ts, val) in timestamp_records: grid.append({'ts': ts, 'val': val}) return self._post_grid('hisWrite', grid, callback, **kwargs)
def make_simple_grid(version=hszinc.VER_2_0): grid = hszinc.Grid(version=version) grid.column['firstName'] = {} grid.column['bday'] = {} grid.extend([ { 'firstName': 'Jack', 'bday': datetime.date(1973,7,23), }, { 'firstName': 'Jill', 'bday': datetime.date(1975,11,15), }, ]) return grid
def watch_poll(self, watch, refresh=False, callback=None): """ watch is either the value of watch_id given when creating a watch, or an instance of a Watch object. If refresh is True, then all points on the watch will be updated, not just those that have changed since the last poll. """ grid = hszinc.Grid() grid.column['empty'] = {} if not isinstance(watch, string_types): watch = watch.id grid.metadata['watchId'] = watch return self._post_grid('watchPoll', grid, callback)
def _on_his_write(self, point, timestamp_records, callback, **kwargs): grid = hszinc.Grid() grid.metadata["id"] = self._obj_to_ref(point) grid.column["ts"] = {} grid.column["val"] = {} if hasattr(timestamp_records, "to_dict"): timestamp_records = timestamp_records.to_dict() timestamp_records = list(timestamp_records.items()) timestamp_records.sort(key=lambda rec: rec[0]) for (ts, val) in timestamp_records: grid.append({"ts": ts, "val": val}) return self._post_grid("hisWrite", grid, callback, **kwargs)
def test_get_single_entity(self, server_session): (server, session) = server_session # Try retrieving an existing single entity op = session.get_entity("my.entity.id", single=True) # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Request shall be a GET assert rq.method == "GET", "Expecting GET, got %s" % rq # Request shall be for base + 'api/[email protected]' assert rq.uri == BASE_URI + "api/read?id=%40my.entity.id" # Accept header shall be given assert rq.headers[b"Accept"] == "text/zinc" # Make a grid to respond with response = hszinc.Grid() response.column["id"] = {} response.column["dis"] = {} response.column["randomTag"] = {} response.append({ "id": hszinc.Ref("my.entity.id", value="id"), "dis": "A test entity", "randomTag": hszinc.MARKER, }) rq.respond( status=200, headers={b"Content-Type": "text/zinc"}, content=hszinc.dump(response, mode=hszinc.MODE_ZINC), ) # State machine should now be done assert op.is_done entity = op.result # Response should be an entity assert isinstance(entity, Entity), "%r not an entity" % entity # The tags should be passed through from the response assert entity.id.name == "my.entity.id" assert entity.tags["dis"] == response[0]["dis"] assert entity.tags["randomTag"] == response[0]["randomTag"]
def test_get_single_entity(self, server_session): (server, session) = server_session # Try retrieving an existing single entity op = session.get_entity('my.entity.id', single=True) # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Request shall be a GET assert rq.method == 'GET', 'Expecting GET, got %s' % rq # Request shall be for base + 'api/[email protected]' assert rq.uri == BASE_URI + 'api/read?id=%40my.entity.id' # Accept header shall be given assert rq.headers[b'Accept'] == 'text/zinc' # Make a grid to respond with response = hszinc.Grid() response.column['id'] = {} response.column['dis'] = {} response.column['randomTag'] = {} response.append({ 'id': hszinc.Ref('my.entity.id', value='id'), 'dis': 'A test entity', 'randomTag': hszinc.MARKER }) rq.respond(status=200, headers={ b'Content-Type': 'text/zinc', }, content=hszinc.dump(response, mode=hszinc.MODE_ZINC)) # State machine should now be done assert op.is_done entity = op.result # Response should be an entity assert isinstance(entity, Entity), '%r not an entity' % entity # The tags should be passed through from the response assert entity.id.name == 'my.entity.id' assert entity.tags['dis'] == response[0]['dis'] assert entity.tags['randomTag'] == response[0]['randomTag']
def _crud_op(self, op, entities, callback, **kwargs): """ Perform a repeated operation on the given entities with the given values for each entity. `entities` should be a list of dicts, each dict having an `id` key that specifies the entity's ID and other keys giving the values. :param entities: The entities to be inserted. """ if isinstance(entities, dict): # Convert single entity to list. entities = [entities] # Construct a list of columns all_columns = set() list(map(all_columns.update, [e.keys() for e in entities])) # We'll put 'id' first sort the others. if "id" in all_columns: all_columns.discard("id") all_columns = ["id"] + sorted(all_columns) else: all_columns = sorted(all_columns) # Construct the grid grid = hszinc.Grid() for column in all_columns: grid.column[column] = {} for entity in entities: # Take a copy entity = entity.copy() # Ensure 'id' is a ref if "id" in entity: entity["id"] = self._obj_to_ref(entity["id"]) # Ensure all other columns are present for column in all_columns: if column not in entity: entity[column] = None # Add to the grid grid.append(entity) # Post the grid return self._post_grid(op, grid, callback, **kwargs)
def make_metadata_grid(version=hszinc.VER_2_0): grid = hszinc.Grid(version=version) grid.metadata['database'] = 'test' grid.metadata['dis'] = 'Site Energy Summary' grid.column['siteName'] = {'dis': 'Sites'} grid.column['val'] = hszinc.MetadataObject() grid.column['val']['dis'] = 'Value' grid.column['val']['unit'] = 'kW' grid.extend([ { 'siteName': 'Site 1', 'val': hszinc.Quantity(356.214,'kW'), }, { 'siteName': 'Site 2', 'val': hszinc.Quantity(463.028,'kW'), }, ]) return grid
def test_get_single_entity_missing(self, server_session): (server, session) = server_session # Try retrieving an existing single entity that does not exist op = session.get_entity("my.nonexistent.id", single=True) # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Request shall be a GET assert rq.method == "GET", "Expecting GET, got %s" % rq # Request shall be for base + 'api/[email protected]' assert rq.uri == BASE_URI + "api/read?id=%40my.nonexistent.id" # Accept header shall be given assert rq.headers[b"Accept"] == "text/zinc" # Make a grid to respond with. Note, server might also choose to # throw an error, but we'll pretend it doesn't. response = hszinc.Grid() response.column["id"] = {} response.column["dis"] = {} rq.respond( status=200, headers={b"Content-Type": "text/zinc"}, content=hszinc.dump(response, mode=hszinc.MODE_ZINC), ) # State machine should now be done assert op.is_done # This should trigger a name error: try: entity = op.result assert entity is None except NameError as e: assert str(e) == "No matching entity found"
def _create_equipment_grid(self, equip_name, markers): grid = hszinc.Grid() grid.metadata['commit'] = 'add' # grid.metadata['projName'] = 'Springfield' grid.column['dis'] = {} grid.column["equip"] = {} grid.column["siteRef"] = {} grid.column["navName"] = {} # grid.column['disMacro'] = {} g = { 'dis': equip_name, 'navName': equip_name, 'equip': hszinc.MARKER, "siteRef": self._site.id } for marker in markers: grid.column[marker] = {} g[marker] = hszinc.MARKER grid.append(g) return grid