From e8fbb20df39474b27625c2cfed16bea576e9cb5d Mon Sep 17 00:00:00 2001
From: Valentin Deniaud <vdeniaud@entrouvert.com>
Date: Tue, 29 Oct 2019 17:46:21 +0100
Subject: [PATCH 2/3] views: grey out unavailable visualizations (#27412)

---
 bijoe/templates/bijoe/homepage.html           |  7 +-----
 bijoe/templates/bijoe/visualizations.html     |  6 +----
 .../templates/bijoe/visualizations_list.html  |  8 +++++++
 bijoe/views.py                                |  5 +++-
 bijoe/visualization/utils.py                  | 16 +++++++++++--
 bijoe/visualization/views.py                  |  7 ++++++
 tests/test_views.py                           | 23 ++++++++++++++++---
 7 files changed, 55 insertions(+), 17 deletions(-)
 create mode 100644 bijoe/templates/bijoe/visualizations_list.html

diff --git a/bijoe/templates/bijoe/homepage.html b/bijoe/templates/bijoe/homepage.html
index 22c850b..0eecec2 100644
--- a/bijoe/templates/bijoe/homepage.html
+++ b/bijoe/templates/bijoe/homepage.html
@@ -9,12 +9,7 @@
 {% block content %}
   {% if visualizations %}
     <h2>{% trans "Visualizations" %}</h2>
-    <ul class="objects-list single-links bijoe-visualizations">
-    {% for visualization in visualizations %}
-         {% url 'visualization' pk=visualization.pk as visualization_url %}
-         <li><a href="{{ visualization_url }}">{{ visualization.name }}</a></li>
-    {% endfor %}
-    </ul>
+    {% include "bijoe/visualizations_list.html" %}
   {% endif %}
   {% if warehouses %}
     <h2>{% trans "Data sources" %}</h2>
diff --git a/bijoe/templates/bijoe/visualizations.html b/bijoe/templates/bijoe/visualizations.html
index eecacaf..bd3f1c4 100644
--- a/bijoe/templates/bijoe/visualizations.html
+++ b/bijoe/templates/bijoe/visualizations.html
@@ -11,9 +11,5 @@
 {% endblock %}
 
 {% block content %}
-  <ul class="objects-list single-links bijoe-visualizations">
-  {% for visualization in object_list %}
-    <li><a href="{% url "visualization" pk=visualization.pk %}">{{ visualization.name }}</a></li>
-  {% endfor %}
-  </ul>
+{% include "bijoe/visualizations_list.html" %}
 {% endblock %}
diff --git a/bijoe/templates/bijoe/visualizations_list.html b/bijoe/templates/bijoe/visualizations_list.html
new file mode 100644
index 0000000..01dd1b7
--- /dev/null
+++ b/bijoe/templates/bijoe/visualizations_list.html
@@ -0,0 +1,8 @@
+{% load i18n %}
+
+<ul class="objects-list single-links bijoe-visualizations">
+  {% for visualization, exists in visualizations.items %}
+    {% url 'visualization' pk=visualization.pk as visualization_url %}
+    <li><a {% if exists %}href="{{ visualization_url }}"{% else %}class="disabled" title="{% trans "Form data not found" %}"{% endif %}>{{ visualization.name }}</a></li>
+  {% endfor %}
+</ul>
diff --git a/bijoe/views.py b/bijoe/views.py
index aa46b81..636e8f3 100644
--- a/bijoe/views.py
+++ b/bijoe/views.py
@@ -35,6 +35,7 @@ except ImportError:
 from .utils import get_warehouses
 from .engine import Engine
 from .visualization.models import Visualization
+from .visualization.utils import Visualization as VisuUtil
 
 
 class AuthorizationMixin(object):
@@ -58,7 +59,9 @@ class HomepageView(AuthorizationMixin, TemplateView):
 
     def get_context_data(self, **kwargs):
         ctx = super(HomepageView, self).get_context_data(**kwargs)
-        ctx['visualizations'] = Visualization.objects.all()
+        visualizations = Visualization.objects.all()
+        ctx['visualizations'] = {v: VisuUtil.exists(v, request=self.request)
+                                 for v in visualizations}
         ctx['warehouses'] = sorted((Engine(w) for w in get_warehouses(self.request)),
                                    key=lambda w: w.label)
         return ctx
diff --git a/bijoe/visualization/utils.py b/bijoe/visualization/utils.py
index 5ec242f..8f37557 100644
--- a/bijoe/visualization/utils.py
+++ b/bijoe/visualization/utils.py
@@ -75,7 +75,15 @@ class Visualization(object):
                              filters=copy.deepcopy(self.filters), loop=self.loop)
 
     @classmethod
-    def from_json(cls, d, request=None):
+    def exists(cls, visualization_obj, request=None):
+        try:
+            cls.get_cube(visualization_obj.parameters, request)
+            return True
+        except Http404:
+            return False
+
+    @staticmethod
+    def get_cube(d, request=None):
         for warehouse in get_warehouses(request=request):
             if d['warehouse'] == warehouse.name:
                 break
@@ -83,9 +91,13 @@ class Visualization(object):
             raise Http404('warehouse %s not found' % d['warehouse'])
         engine = Engine(warehouse)
         try:
-            cube = engine[d['cube']]
+            return engine[d['cube']]
         except KeyError:
             raise Http404('cube %s not found' % d['cube'])
+
+    @classmethod
+    def from_json(cls, d, request=None):
+        cube = cls.get_cube(d, request)
         representation = d['representation']
         measure = cube.measures[d['measure']]
         drilldown_x = cube.dimensions[d['drilldown_x']] if 'drilldown_x' in d else None
diff --git a/bijoe/visualization/views.py b/bijoe/visualization/views.py
index 0cd7c26..c3e201c 100644
--- a/bijoe/visualization/views.py
+++ b/bijoe/visualization/views.py
@@ -186,6 +186,13 @@ class DeleteVisualizationView(views.AuthorizationMixin, DeleteView):
 class VisualizationsView(views.AuthorizationMixin, ListView):
     template_name = 'bijoe/visualizations.html'
     model = models.Visualization
+    context_object_name = 'visualizations'
+
+    def get_context_data(self, **kwargs):
+        ctx = super(VisualizationsView, self).get_context_data(**kwargs)
+        ctx[self.context_object_name] = {v: Visualization.exists(v, request=self.request)
+                                         for v in ctx[self.context_object_name]}
+        return ctx
 
 
 class RenameVisualization(views.AuthorizationMixin, UpdateView):
diff --git a/tests/test_views.py b/tests/test_views.py
index 89ee37f..0d88fcd 100644
--- a/tests/test_views.py
+++ b/tests/test_views.py
@@ -36,9 +36,9 @@ def test_superuser(app, admin):
 
 
 def test_visualizations_json_api(schema1, app, admin):
-    Visualization(name='test', parameters={}).save()
-    Visualization(name='test', parameters={}).save()
-    Visualization(name='test', parameters={}).save()
+    Visualization(name='test', parameters={'warehouse': 'schema1', 'cube': 'test'}).save()
+    Visualization(name='test', parameters={'warehouse': 'schema1', 'cube': 'test'}).save()
+    Visualization(name='test', parameters={'warehouse': 'schema1', 'cube': 'test'}).save()
     login(app, admin)
     resp = app.get(reverse('visualizations-json'))
     assert set([x['slug'] for x in resp.json]) == set(['test', 'test-2', 'test-3'])
@@ -90,3 +90,20 @@ def test_visualization_json_api_duration(schema1, app, admin):
         'format': '1',
         'unit': 'seconds',
     }
+
+
+def test_missing_data(schema1, app, admin):
+    visualization = Visualization(
+        name='test',
+        parameters={
+            'cube': 'missing_cube',
+            'warehouse': 'schema1',
+            'measure': 'duration',
+            'representation': 'table',
+            'loop': '',
+            'filters': {}})
+    visualization.save()
+    login(app, admin)
+
+    response = app.get('/')
+    assert response.pyquery('ul li a.disabled').text() == visualization.name
-- 
2.20.1

