Skip to content

Commit 9f1eb9c

Browse files
committed
Add possibility to acquire bulk readings in JSON format
1 parent 0247da5 commit 9f1eb9c

File tree

5 files changed

+109
-2
lines changed

5 files changed

+109
-2
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ in progress
99
- Fix logging when running under pytest with "--capture=no"
1010
- Address compatibility with Grafana 7.4.0
1111
- Fix error logging after migration to Python 3
12+
- Add possibility to acquire bulk readings in JSON format
1213

1314

1415
.. _kotori-0.25.0:

kotori/daq/graphing/grafana/dashboard.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,19 @@ def collect_fields(data, prefixes=None, sorted=True):
275275
# time is from intercom.mqtt
276276
blacklist = ['_hex_', 'time']
277277

278+
# Compute list of unique attribute names.
279+
if isinstance(data, dict):
280+
keys = data.keys()
281+
elif isinstance(data, list):
282+
keys = set()
283+
for item in data:
284+
for key in item.keys():
285+
keys.add(key)
286+
else:
287+
raise ValueError(f"Type of data {type(data)} not accepted")
288+
278289
fields = []
279-
for field in data.keys():
290+
for field in keys:
280291
if field in blacklist:
281292
continue
282293

kotori/daq/storage/influx.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ def is_udp_database(self, name):
6464
return False
6565

6666
def write(self, meta, data):
67+
if isinstance(data, dict):
68+
self.write_single(meta, data)
69+
elif isinstance(data, list):
70+
for item in data:
71+
self.write_single(meta, item)
72+
else:
73+
raise ValueError(f"Type of data {type(data)} not accepted")
74+
75+
def write_single(self, meta, data):
6776

6877
meta_copy = deepcopy(dict(meta))
6978
data_copy = deepcopy(data)

test/test_daq_grafana.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
@pytest_twisted.inlineCallbacks
1515
@pytest.mark.grafana
16-
def test_mqtt_to_grafana(machinery, create_influxdb, reset_influxdb, reset_grafana):
16+
def test_mqtt_to_grafana_single(machinery, create_influxdb, reset_influxdb, reset_grafana):
1717
"""
1818
Publish single reading in JSON format to MQTT broker and proof
1919
that a corresponding datasource and a dashboard was created in Grafana.
@@ -44,3 +44,55 @@ def test_mqtt_to_grafana(machinery, create_influxdb, reset_influxdb, reset_grafa
4444
target = dashboard['dashboard']['rows'][0]['panels'][0]['targets'][0]
4545
assert target['measurement'] == settings.influx_measurement_sensors
4646
assert 'temperature' in target['query'] or 'humidity' in target['query']
47+
48+
49+
@pytest_twisted.inlineCallbacks
50+
@pytest.mark.grafana
51+
def test_mqtt_to_grafana_bulk(machinery, create_influxdb, reset_influxdb, reset_grafana):
52+
"""
53+
Publish multiple readings in JSON format to MQTT broker and proof
54+
that a corresponding datasource and a dashboard was created in Grafana.
55+
"""
56+
57+
# Submit multiple measurements, without timestamp.
58+
data = [
59+
{
60+
'temperature': 21.42,
61+
'humidity': 41.55,
62+
},
63+
{
64+
'temperature': 42.84,
65+
'humidity': 83.1,
66+
'voltage': 4.2,
67+
},
68+
{
69+
'weight': 10.10,
70+
},
71+
]
72+
yield mqtt_json_sensor(settings.mqtt_topic_json, data)
73+
74+
# Wait for some time to process the message.
75+
yield sleep(PROCESS_DELAY)
76+
yield sleep(PROCESS_DELAY)
77+
yield sleep(PROCESS_DELAY)
78+
79+
# Proof that Grafana is well provisioned.
80+
logger.info('Grafana: Checking datasource')
81+
datasource_names = []
82+
for datasource in grafana.client.datasources.get():
83+
datasource_names.append(datasource['name'])
84+
assert settings.influx_database in datasource_names
85+
86+
logger.info('Grafana: Checking dashboard')
87+
dashboard_name = settings.grafana_dashboards[0]
88+
dashboard = grafana.client.dashboards.db[dashboard_name].get()
89+
targets = dashboard['dashboard']['rows'][0]['panels'][0]['targets']
90+
91+
# Validate table name.
92+
assert targets[0]['measurement'] == settings.influx_measurement_sensors
93+
94+
# Validate field names.
95+
fields = set()
96+
for target in targets:
97+
fields.add(target["fields"][0]["name"])
98+
assert fields == set(["temperature", "humidity", "weight", "voltage"])

test/test_daq_mqtt.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,40 @@ def test_mqtt_to_influxdb_json_single(machinery, create_influxdb, reset_influxdb
3737
yield record
3838

3939

40+
@pytest_twisted.inlineCallbacks
41+
@pytest.mark.mqtt
42+
def test_mqtt_to_influxdb_json_bulk(machinery, create_influxdb, reset_influxdb):
43+
"""
44+
Publish multiple readings in JSON format to MQTT broker
45+
and proof it is stored in the InfluxDB database.
46+
"""
47+
48+
# Submit multiple measurements, without timestamp.
49+
data = [
50+
{
51+
'temperature': 21.42,
52+
'humidity': 41.55,
53+
},
54+
{
55+
'temperature': 42.84,
56+
'humidity': 83.1,
57+
},
58+
]
59+
yield threads.deferToThread(mqtt_json_sensor, settings.mqtt_topic_json, data)
60+
61+
# Wait for some time to process the message.
62+
yield sleep(PROCESS_DELAY)
63+
64+
# Proof that data arrived in InfluxDB.
65+
record = influx_sensors.get_record(index=0)
66+
del record['time']
67+
assert record == {u'temperature': 21.42, u'humidity': 41.55}
68+
69+
record = influx_sensors.get_record(index=1)
70+
del record['time']
71+
assert record == {u'temperature': 42.84, u'humidity': 83.1}
72+
73+
4074
@pytest_twisted.inlineCallbacks
4175
@pytest.mark.mqtt
4276
@pytest.mark.legacy

0 commit comments

Comments
 (0)