From 8381608f8c6efa35db7e07412ae0caef9ef8d017 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 18 Jan 2022 14:51:24 +0100
Subject: [PATCH 8/8] formdef: factorize iter_fields methods (#60706)

---
 wcs/backoffice/management.py | 70 ++++++++++++++----------------------
 wcs/formdef.py               | 61 +++++++++++++++++--------------
 wcs/forms/backoffice.py      | 28 +++------------
 3 files changed, 66 insertions(+), 93 deletions(-)

diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py
index 3a71a132..dfa3f166 100644
--- a/wcs/backoffice/management.py
+++ b/wcs/backoffice/management.py
@@ -1352,6 +1352,8 @@ class FormPage(Directory):
                 if field.id in field_ids:
                     r += htmltext(' checked="checked"')
                 r += htmltext('/>')
+                if getattr(field, 'block_field', None):
+                    field.label = '%s / %s' % (field.block_field.label, field.label)
                 r += htmltext('%s</label>') % misc.ellipsize(field.label, 70)
                 if getattr(field, 'has_relations', False):
                     r += htmltext('<button class="expand-relations"></button>')
@@ -1545,51 +1547,33 @@ class FormPage(Directory):
                 field.has_relations = True
                 yield UserRelatedField(field)
 
-        def iter_fields(fields, block_field=None):
-            for field in fields:
-                # add contextual_id/contextual_varname attributes
-                # they are id/varname for normal fields
-                # but in case of blocks they are concatenation of block id/varname + field id/varname
-                field.contextual_id = field.id
-                field.contextual_varname = None
-                if block_field:
-                    if field.key == 'items':
-                        # not yet
-                        continue
-                    field.block_field = block_field
-                    field.contextual_id = '%s-%s' % (field.block_field.id, field.id)
-                    if field.varname and field.block_field.varname:
-                        field.contextual_varname = '%s_%s' % (
-                            field.block_field.varname,
-                            field.varname,
-                        )
-                else:
-                    field.contextual_varname = field.varname
-                yield field
-                if not get_publisher().is_using_postgresql():
-                    continue
-                if field.key == 'block':
-                    yield from iter_fields(field.block.fields, block_field=field)
-                    continue
-                if block_field:
-                    continue
-                if not (
-                    field.type == 'item'
-                    and field.data_source
-                    and field.data_source.get('type', '').startswith('carddef:')
-                ):
+        for field in self.formdef.iter_fields(include_block_fields=True):
+            if getattr(field, 'block_field', None):
+                if field.key == 'items':
+                    # not yet
                     continue
-                try:
-                    carddef = CardDef.get_by_urlname(field.data_source['type'][8:])
-                except KeyError:
+            yield field
+            if not get_publisher().is_using_postgresql():
+                continue
+            if field.key == 'block':
+                continue
+            if getattr(field, 'block_field', None):
+                continue
+            if not (
+                field.type == 'item'
+                and field.data_source
+                and field.data_source.get('type', '').startswith('carddef:')
+            ):
+                continue
+            try:
+                carddef = CardDef.get_by_urlname(field.data_source['type'][8:])
+            except KeyError:
+                continue
+            for card_field in carddef.get_all_fields():
+                if not hasattr(card_field, 'get_view_value'):
                     continue
-                for card_field in carddef.get_all_fields():
-                    if not hasattr(card_field, 'get_view_value'):
-                        continue
-                    field.has_relations = True
-                    yield RelatedField(carddef, card_field, field)
-
-        yield from iter_fields(self.formdef.get_all_fields())
+                field.has_relations = True
+                yield RelatedField(carddef, card_field, field)
 
         yield FakeField('status', 'status', _('Status'))
         yield FakeField('anonymised', 'anonymised', _('Anonymised'))
diff --git a/wcs/formdef.py b/wcs/formdef.py
index 36b579a8..55bdeb20 100644
--- a/wcs/formdef.py
+++ b/wcs/formdef.py
@@ -536,40 +536,23 @@ class FormDef(StorableObject):
         self_ref = '%s:%s' % (self.xml_root_node, self.url_name)
         relations_by_ref = collections.defaultdict(list)
 
-        def _check_field(field, in_block_field=False, prefix=''):
+        def _check_field(field):
             data_source = getattr(field, 'data_source', None)
             if not data_source or not data_source.get('type', '').startswith('carddef:'):
                 return
-            varname = field.varname or ''
-            if in_block_field:
-                if prefix and field.varname:
-                    varname = '%s_%s' % (prefix, field.varname)
-                else:
-                    varname = ''
             # reverse relation of data_source['type'] to this object
             obj_ref = ':'.join(data_source['type'].split(':')[:2])  # remove possible custom-view
             relations_by_ref[obj_ref].append(
                 {
-                    'varname': varname,
+                    'varname': field.contextual_varname or '',
                     'type': field.key,
                     'obj': self_ref,
                 }
             )
 
-        for field in self.get_all_fields():
+        for field in self.iter_fields(include_block_fields=True):
             if field.key in ['item', 'items', 'computed']:
                 _check_field(field)
-            if field.key == 'block':
-                try:
-                    field.block  # load block
-                except KeyError:
-                    # blockdef not found
-                    continue
-                for _field in field.block.fields:
-                    if _field.key not in ['item', 'items', 'computed']:
-                        continue
-                    _check_field(_field, in_block_field=True, prefix=field.varname or '')
-                field._block = None  # reset cache
 
         # remove duplicated items
         return {
@@ -584,6 +567,36 @@ class FormDef(StorableObject):
     def get_all_fields(self):
         return (self.fields or []) + self.workflow.get_backoffice_fields()
 
+    def iter_fields(self, include_block_fields=False):
+        def _iter_fields(fields, block_field=None):
+            for field in fields:
+                # add contextual_id/contextual_varname attributes
+                # they are id/varname for normal fields
+                # but in case of blocks they are concatenation of block id/varname + field id/varname
+                field.contextual_id = field.id
+                field.contextual_varname = None
+                if block_field:
+                    field.block_field = block_field
+                    field.contextual_id = '%s-%s' % (field.block_field.id, field.id)
+                    if field.varname and field.block_field.varname:
+                        field.contextual_varname = '%s_%s' % (
+                            field.block_field.varname,
+                            field.varname,
+                        )
+                else:
+                    field.contextual_varname = field.varname
+                yield field
+                if field.key == 'block' and include_block_fields:
+                    try:
+                        field.block  # load block
+                    except KeyError:
+                        # blockdef not found
+                        continue
+                    yield from _iter_fields(field.block.fields, block_field=field)
+                    field._block = None  # reset cache
+
+        yield from _iter_fields(self.get_all_fields())
+
     def get_widget_fields(self):
         return [field for field in self.fields or [] if isinstance(field, fields.WidgetField)]
 
@@ -904,13 +917,7 @@ class FormDef(StorableObject):
     def set_live_condition_sources(self, form, fields):
         live_condition_fields = {}
 
-        def get_all_fields():
-            for field in fields:
-                yield field
-                if field.key == 'block':
-                    yield from field.block.fields
-
-        for field in get_all_fields():
+        for field in self.iter_fields(include_block_fields=True):
             if field.condition:
                 field.varnames = field.get_condition_varnames(formdef=self)
                 for varname in field.varnames:
diff --git a/wcs/forms/backoffice.py b/wcs/forms/backoffice.py
index e39f8ca0..5a036f59 100644
--- a/wcs/forms/backoffice.py
+++ b/wcs/forms/backoffice.py
@@ -217,35 +217,17 @@ class FormDefUI:
             # get_sorted_ids is only implemented in the SQL backend
             order_by = None
 
-        def iter_fields(fields, block_field=None):
-            for field in fields:
-                field.contextual_id = field.id
-                field.contextual_varname = None
-                if block_field:
-                    if field.key == 'items':
-                        # not yet
-                        continue
-                    field.block_field = block_field
-                    field.contextual_id = '%s-%s' % (field.block_field.id, field.id)
-                    if field.varname and field.block_field.varname:
-                        field.contextual_varname = '%s_%s' % (
-                            field.block_field.varname,
-                            field.varname,
-                        )
-                else:
-                    field.contextual_varname = field.varname
-                yield field
-                if field.key == 'block':
-                    yield from iter_fields(field.block.fields, block_field=field)
-                    continue
-
         if order_by and not anonymise:
             direction = ''
             if order_by.startswith('-'):
                 order_by = order_by[1:]
                 direction = '-'
             field_found = False
-            for field in iter_fields(self.formdef.get_all_fields()):
+            for field in self.formdef.iter_fields(include_block_fields=True):
+                if getattr(field, 'block_field', None):
+                    if field.key == 'items':
+                        # not yet
+                        continue
                 if order_by in [field.contextual_varname, 'f%s' % field.contextual_id]:
                     field_found = True
                 if field.contextual_varname == order_by:
-- 
2.34.1

