Repository: kangasta/fdbk

Issue:
Data tools aggregation raises error on empty data

Comments:

Patch:
diff --git a/fdbk/data_tools/_aggregate.py b/fdbk/data_tools/_aggregate.py
index 007d604..2283bbe 100644
--- a/fdbk/data_tools/_aggregate.py
+++ b/fdbk/data_tools/_aggregate.py
@@ -3,7 +3,8 @@ from dateutil.parser import isoparse
 
 from fdbk.utils import timestamp_as_str
 from fdbk.utils.messages import (
-    method_not_supported)
+    method_not_supported,
+    no_data)
 
 from .functions import functions as data_functions, VALUE_FUNCS
 
@@ -27,6 +28,10 @@ def aggregate(data, aggregate_to, aggregate_with=None):
     warnings = []
     aggregated = []
 
+    if not data:
+        warnings.append(no_data())
+        return ([], warnings,)
+
     if aggregate_with not in VALUE_FUNCS:
         warnings.append(method_not_supported(aggregate_with))
         return ([], warnings,)
diff --git a/fdbk/data_tools/_utils.py b/fdbk/data_tools/_utils.py
index 1158389..d55fe52 100644
--- a/fdbk/data_tools/_utils.py
+++ b/fdbk/data_tools/_utils.py
@@ -1,5 +1,8 @@
 from fdbk.utils.messages import (
-    method_not_supported, field_is_undefined, collection_name_is_undefined)
+    method_not_supported,
+    field_is_undefined,
+    collection_name_is_undefined,
+    no_data)
 
 from .functions import functions as data_functions, CHART_FUNCS
 from .functions.utils import chart_dict, statistics_dict
@@ -185,6 +188,10 @@ def run_data_tools(topic_d, data, aggregate_to=None, aggregate_with=None):
     results = []
     warnings = []
 
+    if not data:
+        warnings.append(no_data(topic_d))
+        return ([], warnings,)
+
     if aggregate_to:
         chart_data, aggregate_warnings = aggregate(
             data, aggregate_to, aggregate_with)
diff --git a/fdbk/utils/messages.py b/fdbk/utils/messages.py
index 533c2f2..6814c52 100644
--- a/fdbk/utils/messages.py
+++ b/fdbk/utils/messages.py
@@ -1,14 +1,22 @@
-def topic_not_found(id_):
-    return f'Topic ID "{id_}" not found from database'
+def collection_name_is_undefined(method, field):
+    return f'No target list name specified for {method} {field}.'
+
+
+def field_is_undefined(field):
+    return f'The requested field "{field}" is undefined.'
 
 
 def method_not_supported(method):
     return f'The requested method "{method}" is not supported.'
 
 
-def field_is_undefined(field):
-    return f'The requested field "{field}" is undefined.'
+def no_data(topic_d=None):
+    if topic_d:
+        topic_details = f' for topic {topic_d["name"]} ({topic_d["id"]})'
+    else:
+        topic_details = ''
+    return f'No data found{topic_details}.'
 
 
-def collection_name_is_undefined(method, field):
-    return f'No target list name specified for {method} {field}.'
+def topic_not_found(id_):
+    return f'Topic ID "{id_}" not found from database'


Test patch:
diff --git a/tst/test_data_tools.py b/tst/test_data_tools.py
index c253a03..0f3f273 100644
--- a/tst/test_data_tools.py
+++ b/tst/test_data_tools.py
@@ -1,8 +1,8 @@
 from unittest import TestCase
 from unittest.mock import Mock, patch
 
-from fdbk.data_tools import aggregate, functions
-from fdbk.utils.messages import method_not_supported
+from fdbk.data_tools import aggregate, functions, run_data_tools
+from fdbk.utils.messages import method_not_supported, no_data
 
 def _test_timestamp(i):
     return f'2020-08-23T00:{i // 60:02}:{i % 60:02}Z'
@@ -15,6 +15,23 @@ class DataToolsTest(TestCase):
         for fn in functions.values():
             self.assertIsNone(fn([], 'field'))
 
+    def test_run_data_tools_empty_data(self):
+        topic_d = dict(id='test_id', name='Test name')
+        data = []
+
+        def _check_results(results, warnings, topic_d=None):
+            self.assertEqual(result, [])
+            self.assertEqual(
+                warnings[0],
+                no_data(topic_d))
+
+        result, warnings = aggregate(data, 3)
+        _check_results(result, warnings)
+
+        result, warnings = run_data_tools(topic_d, data)
+        _check_results(result, warnings, topic_d)
+
+
     def test_value_functions(self):
         data = generate_test_data()