From d687fa435200ffa0f85f3f9de5f9acac16138ce0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Fri, 30 Oct 2020 14:26:40 +0100
Subject: [PATCH] wcs: cards & custom views (#46865)

---
 combo/apps/wcs/forms.py  | 16 ++++++--
 combo/apps/wcs/models.py | 48 +++++++++++++++++------
 combo/apps/wcs/utils.py  |  5 ++-
 tests/test_wcs.py        | 85 +++++++++++++++++++++++++++++++++++++---
 4 files changed, 133 insertions(+), 21 deletions(-)

diff --git a/combo/apps/wcs/forms.py b/combo/apps/wcs/forms.py
index 6e10c8fb..abb0d7d3 100644
--- a/combo/apps/wcs/forms.py
+++ b/combo/apps/wcs/forms.py
@@ -14,16 +14,15 @@
 # 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/>.
 
-import django
 from django import forms
 from django.utils.datastructures import MultiValueDict
-from django.utils.safestring import mark_safe
 from django.utils.translation import ugettext_lazy as _
 
 from combo.utils.forms import MultiSortWidget
 
 from .models import (WcsFormCell, WcsCategoryCell, WcsFormsOfCategoryCell,
-                     WcsCurrentFormsCell, WcsCurrentDraftsCell, WcsCardsCell)
+                     WcsCurrentFormsCell, WcsCurrentDraftsCell, WcsCardsCell,
+                     WcsCardInfosCell)
 from .utils import get_wcs_options, get_wcs_services
 
 
@@ -43,6 +42,17 @@ class WcsCardsCellForm(forms.ModelForm):
         model = WcsCardsCell
         fields = ('carddef_reference',)
 
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        card_models = get_wcs_options('/api/cards/@list', include_custom_views=True)
+        self.fields['carddef_reference'].widget = forms.Select(choices=card_models)
+
+
+class WcsCardInfoCellForm(forms.ModelForm):
+    class Meta:
+        model = WcsCardInfosCell
+        fields = ('carddef_reference',)
+
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         card_models = get_wcs_options('/api/cards/@list')
diff --git a/combo/apps/wcs/models.py b/combo/apps/wcs/models.py
index fffa71b1..5af26b98 100644
--- a/combo/apps/wcs/models.py
+++ b/combo/apps/wcs/models.py
@@ -713,10 +713,6 @@ class CardMixin(object):
     def is_relevant(self, context):
         return bool(self.carddef_reference)
 
-    def get_default_form_class(self):
-        from .forms import WcsCardsCellForm
-        return WcsCardsCellForm
-
     @property
     def wcs_site(self):
         return self.carddef_reference.split(':')[0]
@@ -747,7 +743,8 @@ class WcsCardsCell(CardMixin, WcsBlurpMixin, CellBase):
 
         def populate_cache():
             if self.carddef_reference:
-                wcs_key, card_slug = self.carddef_reference.split(':')
+                parts = self.carddef_reference.split(':')
+                wcs_key, card_slug = parts[:2]
                 wcs_site = get_wcs_services().get(wcs_key)
                 card_models = get_wcs_json(wcs_site, 'api/cards/@list')
 
@@ -759,11 +756,29 @@ class WcsCardsCell(CardMixin, WcsBlurpMixin, CellBase):
                 card_found = False
                 for card in card_models.get('data') or []:
                     slug = card['slug']
-                    if slug == card_slug:
-                        self.cached_title = card['title']
-                        self.save(update_fields=['cached_title'])
-                        card_found = True
-                        break
+
+                    if slug != card_slug:
+                        continue
+
+                    card_title = card['title']
+
+                    if len(parts) > 2:
+                        custom_view = None
+
+                        for v in card.get('custom_views') or []:
+                            if v['id'] == parts[2]:
+                                custom_view = v
+                                break
+
+                        if custom_view is None:
+                            continue
+
+                        card_title = '%s - %s' % (card_title, custom_view['text'])
+
+                    self.cached_title = card_title
+                    self.save(update_fields=['cached_title'])
+                    card_found = True
+                    break
                 if card_found:
                     self.mark_as_valid()
                     return
@@ -775,7 +790,10 @@ class WcsCardsCell(CardMixin, WcsBlurpMixin, CellBase):
         populate_cache()
 
     def get_api_url(self, context):
-        return 'api/cards/%s/list' % self.carddef_reference.split(':')[1]
+        parts = self.carddef_reference.split(':')
+        if len(parts) > 2:
+            return 'api/cards/%s/list/%s' % (parts[1], parts[2])
+        return 'api/cards/%s/list' % parts[1]
 
     def get_cell_extra_context(self, context):
         extra_context = super().get_cell_extra_context(context)
@@ -796,6 +814,10 @@ class WcsCardsCell(CardMixin, WcsBlurpMixin, CellBase):
 
         return extra_context
 
+    def get_default_form_class(self):
+        from .forms import WcsCardsCellForm
+        return WcsCardsCellForm
+
     def render_for_search(self):
         return ''
 
@@ -878,6 +900,10 @@ class WcsCardInfosCell(CardMixin, CellBase):
 
         return extra_context
 
+    def get_default_form_class(self):
+        from .forms import WcsCardInfoCellForm
+        return WcsCardInfoCellForm
+
 
 @register_cell_class
 class TrackingCodeInputCell(CellBase):
diff --git a/combo/apps/wcs/utils.py b/combo/apps/wcs/utils.py
index 619de7e6..f54a9507 100644
--- a/combo/apps/wcs/utils.py
+++ b/combo/apps/wcs/utils.py
@@ -46,7 +46,7 @@ def get_wcs_json(wcs_site, path):
     return response.json()
 
 
-def get_wcs_options(url, include_category_slug=False):
+def get_wcs_options(url, include_category_slug=False, include_custom_views=False):
     references = []
     for wcs_key, wcs_site in sorted(get_wcs_services().items(), key=lambda x: x[1]['title']):
         site_title = wcs_site.get('title')
@@ -68,4 +68,7 @@ def get_wcs_options(url, include_category_slug=False):
             else:
                 reference = '%s:%s' % (wcs_key, slug)
             references.append((reference, label))
+            if include_custom_views:
+                for custom_view in element.get('custom_views') or []:
+                    references.append(('%s:%s' % (reference, custom_view['id']), '%s - %s' % (label, custom_view['text'])))
     return references
diff --git a/tests/test_wcs.py b/tests/test_wcs.py
index 8ef4dbf2..8cf2f162 100644
--- a/tests/test_wcs.py
+++ b/tests/test_wcs.py
@@ -85,7 +85,7 @@ WCS_USER_DRAFTS_DATA = [
 ]
 
 WCS_CARDDEFS_DATA = [
-    {'title': 'Card Model 1', 'slug': 'card_model_1'},
+    {'title': 'Card Model 1', 'slug': 'card_model_1', 'custom_views': [{'id': 'foo', 'text': 'bar'}]},
     {'title': 'Card Model 2', 'slug': 'card_model_2'},
     {'title': 'Card Model 3', 'slug': 'card_model_3'},
 ]
@@ -117,6 +117,25 @@ WCS_CARDS_DATA = [
     },
 ]
 
+WCS_CARDS_CUSTOM_VIEW_DATA = [
+    {
+        'id': 11,
+        'display_id': '10-11',
+        'display_name': 'Card Model 1 - n°10-11',
+        'digest': 'a a a',
+        'text': 'aa',
+        'url': '/backoffice/data/card_model_1/11/'
+    },
+    {
+        'id': 12,
+        'display_id': '10-12',
+        'display_name': 'Card Model 1 - n°10-12',
+        'digest': 'b b b',
+        'text': 'bb',
+        'url': '/backoffice/data/card_model_1/12/'
+    },
+]
+
 WCS_CARDDEF_SCHEMA = {
     'name': 'Card Model 1',
     'fields': [
@@ -187,6 +206,8 @@ def get_data_from_url(url):
         return WCS_CARDDEF_SCHEMA
     if 'api/cards/card_model_1/11/' in url:
         return WCS_CARD_DATA
+    if 'api/cards/card_model_1/list/foo' in url:
+        return WCS_CARDS_CUSTOM_VIEW_DATA
     if 'api/cards/card_model_1/list' in url:
         return WCS_CARDS_DATA
     return []
@@ -1018,17 +1039,18 @@ def test_manager_current_forms(app, admin_user):
         settings.KNOWN_SERVICES = temp_settings
 
 
-@pytest.mark.parametrize('cell_class', [WcsCardsCell, WcsCardInfosCell])
 @mock.patch('combo.apps.wcs.utils.requests.send', side_effect=mocked_requests_send)
-def test_card_cells_setup(mock_send, cell_class):
-    cell = cell_class()
+def test_cards_cell_setup(mock_send):
+    cell = WcsCardsCell()
     form_class = cell.get_default_form_class()
     form = form_class()
     assert form.fields['carddef_reference'].widget.choices == [
         ('default:card_model_1', 'test : Card Model 1'),
+        ('default:card_model_1:foo', 'test : Card Model 1 - bar'),
         ('default:card_model_2', 'test : Card Model 2'),
         ('default:card_model_3', 'test : Card Model 3'),
         ('other:card_model_1', 'test2 : Card Model 1'),
+        ('other:card_model_1:foo', 'test2 : Card Model 1 - bar'),
         ('other:card_model_2', 'test2 : Card Model 2'),
         ('other:card_model_3', 'test2 : Card Model 3'),
     ]
@@ -1046,6 +1068,11 @@ def test_cards_cell_save_cache(mock_send):
     # make sure cached attributes are removed from serialized pages
     assert 'cached_' not in json.dumps(page.get_serialized_page())
 
+    cell.carddef_reference = 'default:card_model_1:foo'
+    cell.save()
+    assert cell.cached_title == 'Card Model 1 - bar'
+    assert cell.get_additional_label() == 'Card Model 1 - bar'
+
     # check content provided to search engine
     assert cell.render_for_search() == ''
 
@@ -1055,7 +1082,7 @@ def test_cards_cell_save_cache(mock_send):
     # run update db cache
     appconfig = apps.get_app_config('wcs')
     appconfig.update_db_cache()
-    assert WcsCardsCell.objects.get(pk=cell.pk).cached_title == 'Card Model 1'
+    assert WcsCardsCell.objects.get(pk=cell.pk).cached_title == 'Card Model 1 - bar'
 
 
 @mock.patch('combo.apps.wcs.utils.requests.send', side_effect=mocked_requests_send)
@@ -1096,6 +1123,16 @@ def test_cards_cell_validity(mock_send):
     assert validity_info.invalid_reason_code == 'wcs_card_not_found'
     assert validity_info.invalid_since is not None
 
+    cell.carddef_reference = 'default:card_model_1:foo'
+    cell.save()
+    assert ValidityInfo.objects.exists() is False
+
+    cell.carddef_reference = 'default:card_model_1:bar'
+    cell.save()
+    validity_info = ValidityInfo.objects.latest('pk')
+    assert validity_info.invalid_reason_code == 'wcs_card_not_found'
+    assert validity_info.invalid_since is not None
+
 
 @mock.patch('combo.apps.wcs.utils.requests.send', side_effect=mocked_requests_send)
 def test_cards_cell_load(mock_send):
@@ -1113,6 +1150,18 @@ def test_cards_cell_load(mock_send):
     cell = cells[0]
     assert cell.cached_title == 'Card Model 1'
 
+    cell.carddef_reference = 'default:card_model_1:foo'
+    cell.save()
+    site_export = [page.get_serialized_page()]
+    cell.delete()
+    assert not Page.objects.get(pk=page.pk).get_cells()
+    Page.load_serialized_pages(site_export)
+    page = Page.objects.get(slug='test_cards')
+    cells = page.get_cells()
+    assert len(cells) == 1
+    cell = cells[0]
+    assert cell.cached_title == 'Card Model 1 - bar'
+
 
 @mock.patch('combo.apps.wcs.utils.requests.send', side_effect=mocked_requests_send)
 def test_cards_cell_render(mock_send, context):
@@ -1143,10 +1192,34 @@ def test_cards_cell_render(mock_send, context):
     assert '<a href="/foo/12"><span class="card-title">bb</span></a>' in result
     assert '<a href="/foo/13"><span class="card-title">cc</span></a>' in result
 
+    cell.carddef_reference = u'default:card_model_1:foo'
+    cell.save()
+
+    result = cell.render(context)
+    assert '<h2>Card Model 1 - bar</h2>' in result
+    assert '<a href="/foo/11"><span class="card-title">aa</span></a>' in result
+    assert '<a href="/foo/12"><span class="card-title">bb</span></a>' in result
+    assert '<a href="/foo/13"><span class="card-title">cc</span></a>' not in result
+
+
+@mock.patch('combo.apps.wcs.utils.requests.send', side_effect=mocked_requests_send)
+def test_card_cell_setup(mock_send):
+    cell = WcsCardInfosCell()
+    form_class = cell.get_default_form_class()
+    form = form_class()
+    assert form.fields['carddef_reference'].widget.choices == [
+        ('default:card_model_1', 'test : Card Model 1'),
+        ('default:card_model_2', 'test : Card Model 2'),
+        ('default:card_model_3', 'test : Card Model 3'),
+        ('other:card_model_1', 'test2 : Card Model 1'),
+        ('other:card_model_2', 'test2 : Card Model 2'),
+        ('other:card_model_3', 'test2 : Card Model 3'),
+    ]
+
 
 @mock.patch('combo.apps.wcs.utils.requests.send', side_effect=mocked_requests_send)
 def test_card_cell_save_cache(mock_send):
-    page = Page.objects.create(title='xxx', slug='test_cards_cell_save_cache', template_name='standard')
+    page = Page.objects.create(title='xxx', slug='test_card_cell_save_cache', template_name='standard')
     cell = WcsCardInfosCell(page=page, placeholder='content', order=0)
     assert cell.get_additional_label() is None
     cell.carddef_reference = 'default:card_model_1'
-- 
2.28.0

