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

---
 bijoe/templates/bijoe/homepage.html            |  7 +------
 bijoe/templates/bijoe/visualizations.html      |  6 +-----
 bijoe/templates/bijoe/visualizations_list.html | 10 ++++++++++
 bijoe/views.py                                 |  5 ++++-
 bijoe/visualization/utils.py                   | 18 +++++++++++++++---
 bijoe/visualization/views.py                   |  8 ++++++++
 tests/test_schema2.py                          | 17 +++++++++++++++++
 tox.ini                                        |  1 +
 8 files changed, 57 insertions(+), 15 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..c7bd963
--- /dev/null
+++ b/bijoe/templates/bijoe/visualizations_list.html
@@ -0,0 +1,10 @@
+{% 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..e2896ea 100644
--- a/bijoe/visualization/utils.py
+++ b/bijoe/visualization/utils.py
@@ -74,8 +74,16 @@ class Visualization(object):
                              drilldown_x=self.drilldown_x, drilldown_y=self.drilldown_y,
                              filters=copy.deepcopy(self.filters), loop=self.loop)
 
-    @classmethod
-    def from_json(cls, d, request=None):
+    @staticmethod
+    def exists(visualization_obj, request=None):
+        try:
+            Visualization.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 2794b7c..1a57023 100644
--- a/bijoe/visualization/views.py
+++ b/bijoe/visualization/views.py
@@ -186,6 +186,14 @@ 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_schema2.py b/tests/test_schema2.py
index a060f6b..072ad85 100644
--- a/tests/test_schema2.py
+++ b/tests/test_schema2.py
@@ -1,8 +1,11 @@
 import json
 import os
 
+import mock
 import pytest
 
+from bijoe.visualization.models import Visualization
+
 from utils import login, get_table
 
 
@@ -32,3 +35,17 @@ def test_simple(schema2, app, admin, visualization):
     visualization_page = response.click(lambda x: x == visualization)
     assert 'big-msg-info' not in visualization_page
     assert schema2['tables'][visualization] == get_table(visualization_page)
+
+
+def test_missing_data(schema2, app, admin, visualization):
+    login(app, admin)
+
+    params = Visualization.objects.get(name=visualization).parameters
+    params['cube'] = 'missing_cube'
+
+    with mock.patch('bijoe.visualization.models.Visualization.parameters',
+                    new_callable=mock.PropertyMock) as mocked:
+        mocked.return_value = params
+        response = app.get('/')
+
+    assert len(response.pyquery('ul li a.disabled')) == len(schema2['tables'])
diff --git a/tox.ini b/tox.ini
index b571306..220a745 100644
--- a/tox.ini
+++ b/tox.ini
@@ -24,6 +24,7 @@ deps =
 	WebTest
 	django-webtest<1.9.3
 	pyquery
+	mock
 commands =
         dj111: py.test {posargs: --junitxml=test_{envname}_results.xml --cov-report xml --cov-report html --cov=bijoe tests/}
 [pytest]
-- 
2.20.1

