From 8cb79a40a5b7ecbff7d5244741834fe12cc41e86 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Sun, 24 Feb 2019 09:39:15 +0100
Subject: [PATCH] api: add json view of visualization data (#26847)

---
 bijoe/visualization/urls.py  |  1 +
 bijoe/visualization/views.py | 64 ++++++++++++++++++++++++++++++++++++
 tests/test_views.py          | 27 +++++++++++++++
 3 files changed, 92 insertions(+)

diff --git a/bijoe/visualization/urls.py b/bijoe/visualization/urls.py
index 3bc5ee5..d55313b 100644
--- a/bijoe/visualization/urls.py
+++ b/bijoe/visualization/urls.py
@@ -30,6 +30,7 @@ urlpatterns = [
     url(r'warehouse/(?P<warehouse>[^/]*)/(?P<cube>[^/]*)/save/$',
         views.create_visualization, name='create-visualization'),
     url(r'(?P<pk>\d+)/$', views.visualization, name='visualization'),
+    url(r'(?P<pk>\d+)/json/$', views.visualization_json, name='visualization-json'),
     url(r'(?P<pk>\d+)/geojson/$', views.visualization_geojson, name='visualization-geojson'),
     url(r'(?P<pk>\d+)/iframe/$', views.visualization_iframe, name='visualization-iframe'),
     url(r'(?P<pk>\d+)/ods/$', views.visualization_ods, name='visualization-ods'),
diff --git a/bijoe/visualization/views.py b/bijoe/visualization/views.py
index b96b3a5..1c67c1c 100644
--- a/bijoe/visualization/views.py
+++ b/bijoe/visualization/views.py
@@ -14,6 +14,7 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+from collections import OrderedDict
 import hashlib
 import json
 
@@ -212,9 +213,11 @@ class VisualizationsJSONView(MultipleObjectMixin, View):
             path = reverse('visualization-iframe', kwargs={'pk': visualization.pk})
             sig = hashlib.sha1(path + settings.SECRET_KEY).hexdigest()
             path += '?signature=' + sig
+            data_uri = reverse('visualization-json', kwargs={'pk': visualization.pk})
             data.append({
                 'name': visualization.name,
                 'path': request.build_absolute_uri(path),
+                'data-url': request.build_absolute_uri(data_uri),
             })
         response = HttpResponse(content_type='application/json')
         response.write(json.dumps(data))
@@ -263,6 +266,66 @@ class VisualizationGeoJSONView(generics.GenericAPIView):
         return Response(geojson)
 
 
+class VisualizationJSONView(generics.GenericAPIView):
+    permission_classes = ()
+    queryset = models.Visualization.objects.all()
+
+    def get(self, request, pk, format=None):
+        instance = self.get_object()
+        loop = []
+        all_visualizations = Visualization.from_json(instance.parameters, request=request)
+        for visualization in all_visualizations:
+            drilldowns = visualization.drilldown
+            if len(drilldowns) == 2:
+                x_labels = [x.label for x in visualization.drilldown_x.members]
+                y_labels = [y.label for y in visualization.drilldown_y.members]
+                used_x_labels = OrderedDict()
+                used_y_labels = OrderedDict()
+                default = 0
+                grid = {(x, y): default for x in x_labels for y in y_labels}
+
+                for row in visualization.data():
+                    x_label = unicode(row[0]['value'])
+                    y_label = unicode(row[1]['value'])
+                    used_x_labels[x_label] = True
+                    used_y_labels[y_label] = True
+                    grid[(x_label, y_label)] = row[2]['value']
+
+                data = []
+                for y in used_y_labels.keys():
+                    data.append([grid[(x, y)] for x in used_x_labels.keys()])
+                axis = {
+                    'x_labels': [x.strip() for x in used_x_labels.keys()],
+                    'y_labels': [x.strip() for x in used_y_labels.keys()]
+                }
+            elif len(drilldowns) == 1:
+                table = list(visualization.data())
+                axis_data = [x[0]['value'].strip() for x in table]
+                data = [x[1]['value'] for x in table]
+                if visualization.drilldown_x:
+                    axis = {'x_labels': axis_data}
+                else:
+                    axis = {'y_labels': axis_data}
+            elif len(drilldowns) == 0:
+                data = list(list(visualization.data())[0])[0]['value']
+                axis = {}
+            loop.append({'data': data, 'axis': axis})
+
+        if not all_visualizations.loop:
+            data = loop[0]['data']
+            axis = loop[0]['axis']
+        else:
+            axis = loop[0]['axis']
+            axis['loop'] = [x.label for x in all_visualizations.loop.members]
+            data = [x['data'] for x in loop]
+
+        return Response({
+            'data': data,
+            'axis': axis,
+            'format': '1',
+            })
+
+
 warehouse = WarehouseView.as_view()
 cube = CubeView.as_view()
 cube_iframe = xframe_options_exempt(CubeIframeView.as_view())
@@ -275,6 +338,7 @@ visualization = VisualizationView.as_view()
 visualization_iframe = xframe_options_exempt(VisualizationIframeView.as_view())
 visualization_geojson = VisualizationGeoJSONView.as_view()
 visualization_ods = VisualizationODSView.as_view()
+visualization_json = VisualizationJSONView.as_view()
 
 cube_iframe.mellon_no_passive = True
 visualization_iframe.mellon_no_passive = True
diff --git a/tests/test_views.py b/tests/test_views.py
index 1275f44..054347e 100644
--- a/tests/test_views.py
+++ b/tests/test_views.py
@@ -14,6 +14,10 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+from django.core.urlresolvers import reverse
+
+from bijoe.visualization.models import Visualization
+
 from utils import login
 
 
@@ -29,3 +33,26 @@ def test_superuser(app, admin):
     assert len(resp.json) == 1
     assert resp.json[0]['slug'] == 'statistics'
     app.get('/', status=200)
+
+
+def test_visualization_json_api(schema1, app, admin):
+    visualization = Visualization(
+            name='test',
+            parameters={
+                'cube': 'facts1',
+                'warehouse': 'schema1',
+                'measure': 'simple_count',
+                'representation': 'table',
+                'loop': '',
+                'filters': {},
+                'drilldown_x': 'date__yearmonth'})
+    visualization.save()
+
+    login(app, admin)
+    resp = app.get(reverse('visualization-json', kwargs={'pk': visualization.id}))
+    # values from test_schem1/test_yearmonth_drilldown
+    assert resp.json == {
+        'axis': {'x_labels': ['01/2017', '02/2017', '03/2017', '04/2017', '05/2017', '06/2017', '07/2017', '08/2017']},
+        'data': [10, 1, 1, 1, 1, 1, 1, 1],
+        'format': '1'
+    }
-- 
2.20.1

