From 22a39d0ce7bd150458b27c065bc64d591048344a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 22 Oct 2020 11:48:59 +0200
Subject: [PATCH] backoffice: list datasources from carddefs (#47849)

---
 tests/test_datasources_admin_pages.py         | 31 +++++++++++++++++++
 wcs/admin/data_sources.py                     |  6 +++-
 wcs/carddef.py                                | 14 ++++-----
 wcs/data_sources.py                           |  2 +-
 .../wcs/backoffice/data-sources.html          | 18 +++++++++++
 5 files changed, 62 insertions(+), 9 deletions(-)

diff --git a/tests/test_datasources_admin_pages.py b/tests/test_datasources_admin_pages.py
index 0bfaa80c..6318b2e1 100644
--- a/tests/test_datasources_admin_pages.py
+++ b/tests/test_datasources_admin_pages.py
@@ -65,6 +65,37 @@ def test_data_sources(pub):
     app.get('/backoffice/workflows/data-sources/42/', status=404)
 
 
+def test_data_sources_from_carddefs(pub):
+    create_superuser(pub)
+    CardDef.wipe()
+    pub.custom_view_class.wipe()
+
+    app = login(get_app(pub))
+    resp = app.get('/backoffice/settings/data-sources/')
+    assert 'Data Sources from Card Models' in resp.text
+    assert 'There are no data sources from card models.' in resp.text
+
+    carddef = CardDef()
+    carddef.name = 'foo'
+    carddef.fields = []
+    carddef.digest_template = 'foo bar'
+    carddef.store()
+
+    custom_view = pub.custom_view_class()
+    custom_view.title = 'datasource card view'
+    custom_view.formdef = carddef
+    custom_view.columns = {'list': [{'id': 'id'}]}
+    custom_view.filters = {}
+    custom_view.visibility = 'datasource'
+    custom_view.store()
+
+    resp = app.get('/backoffice/settings/data-sources/')
+    assert 'Data Sources from Card Models' in resp.text
+    assert 'There are no data sources from card models.' not in resp.text
+    assert '<li><a href="http://example.net/backoffice/cards/1/">foo</a></li>' in resp.text
+    assert '<li><a href="http://example.net/backoffice/cards/1/">foo - datasource card view</a></li>' in resp.text
+
+
 def test_data_sources_new(pub):
     create_superuser(pub)
     NamedDataSource.wipe()
diff --git a/wcs/admin/data_sources.py b/wcs/admin/data_sources.py
index f0aefeae..826340ac 100644
--- a/wcs/admin/data_sources.py
+++ b/wcs/admin/data_sources.py
@@ -30,6 +30,7 @@ from wcs.qommon.form import get_response
 from wcs.qommon.form import get_session
 from wcs.qommon import misc
 from wcs.qommon.backoffice.menu import html_top
+from wcs.carddef import CardDef
 from wcs.data_sources import (NamedDataSource, DataSourceSelectionWidget,
         get_structured_items)
 from wcs.formdef import FormDef, get_formdefs_of_all_kinds
@@ -293,9 +294,12 @@ class NamedDataSourcesDirectory(Directory):
 
     def _q_index(self):
         html_top('datasources', title=_('Data Sources'))
+        generated_data_sources = list(CardDef.get_carddefs_as_data_source())
+        generated_data_sources.sort(key=lambda x: misc.simplify(x[1]))
         return template.QommonTemplateResponse(
                 templates=['wcs/backoffice/data-sources.html'],
-                context={'data_sources': NamedDataSource.select(order_by='name')})
+                context={'data_sources': NamedDataSource.select(order_by='name'),
+                         'generated_data_sources': generated_data_sources})
 
     def new(self):
         get_response().breadcrumb.append( ('new', _('New')) )
diff --git a/wcs/carddef.py b/wcs/carddef.py
index 71db46a4..b0e7cb5c 100644
--- a/wcs/carddef.py
+++ b/wcs/carddef.py
@@ -137,21 +137,21 @@ class CardDef(FormDef):
         return super().store(comment=comment)
 
     @classmethod
-    def get_as_data_source_options(cls):
-        carddefs = {}
+    def get_carddefs_as_data_source(cls):
+        carddefs_by_id = {}
         for carddef in cls.select(lightweight=True, ignore_errors=True, order_by='name'):
             if not carddef.digest_template:
                 continue
             data_source_id = 'carddef:%s' % carddef.url_name
-            carddefs[carddef.id] = {'url_name': carddef.url_name, 'name': carddef.name}
-            yield (data_source_id, carddef.name, data_source_id)
+            carddefs_by_id[carddef.id] = carddef
+            yield (carddef, carddef.name, data_source_id)
         clauses = [Equal('formdef_type', 'carddef'), Equal('visibility', 'datasource')]
         for custom_view in get_publisher().custom_view_class.select(clauses):
-            carddef = carddefs.get(custom_view.formdef_id)
+            carddef = carddefs_by_id.get(custom_view.formdef_id)
             if not carddef:
                 continue
-            data_source_id = 'carddef:%s:%s' % (carddef['url_name'], custom_view.slug)
-            yield (data_source_id, '%s - %s' % (carddef['name'], custom_view.title), data_source_id)
+            data_source_id = 'carddef:%s:%s' % (carddef.url_name, custom_view.slug)
+            yield (carddef, '%s - %s' % (carddef.name, custom_view.title), data_source_id)
 
     @classmethod
     def get_data_source_items(cls, data_source_id, query=None, limit=None,
diff --git a/wcs/data_sources.py b/wcs/data_sources.py
index 5e9d2b1b..bba1ac73 100644
--- a/wcs/data_sources.py
+++ b/wcs/data_sources.py
@@ -62,7 +62,7 @@ class DataSourceSelectionWidget(CompositeWidget):
         if allow_named_sources:
             options.extend([(x.slug, x.name, x.slug) for x in NamedDataSource.select()])
             from wcs.carddef import CardDef
-            options.extend(list(CardDef.get_as_data_source_options()))
+            options.extend([(t[2], t[1], t[2]) for t in CardDef.get_carddefs_as_data_source()])
             options.sort(key=lambda x: misc.simplify(x[1]))
 
         options.insert(0, (None, _('None'), None))
diff --git a/wcs/templates/wcs/backoffice/data-sources.html b/wcs/templates/wcs/backoffice/data-sources.html
index 660d3b6e..ff7adc2d 100644
--- a/wcs/templates/wcs/backoffice/data-sources.html
+++ b/wcs/templates/wcs/backoffice/data-sources.html
@@ -9,6 +9,23 @@
 {% endblock %}
 
 {% block content %}
+<div class="section">
+<h2>{% trans "Data Sources from Card Models" %}</h2>
+{% if generated_data_sources %}
+<ul class="objects-list single-links">
+  {% for data_source in generated_data_sources %}
+  <li><a href="{{ data_source.0.get_admin_url }}">{{ data_source.1 }}</a></li>
+  {% endfor %}
+</ul>
+{% else %}
+<div class="infonotice">
+{% trans "There are no data sources from card models." %}
+</div>
+{% endif %}
+</div>
+
+<div class="section">
+<h2>{% trans "Manually Configured Data Sources" %}</h2>
 {% if data_sources %}
 <ul class="objects-list single-links">
   {% for data_source in data_sources %}
@@ -20,4 +37,5 @@
 {% trans "There are no data sources defined." %}
 </div>
 {% endif %}
+</div>
 {% endblock %}
-- 
2.28.0

