[profile gen] No output for jumped elements without children
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 20 Jul 2016 11:47:19 +0200
changeset 1436 9d3aae1b9c60
parent 1435 7081d6248f81
child 1437 f8446b60db7f
[profile gen] No output for jumped elements without children Following the policy "no entity, not allowed", we should not allow jumped elements when those have no content. This was allowing 'FileInfo' accidentally in the functional test, as well as Gps intermediary tag. Test and fix this.
entities/profile_generation.py
test/data/BV2.0_min.xml
test/test_profile_generation.py
xsd.py
--- a/entities/profile_generation.py	Wed Jul 20 11:40:51 2016 +0200
+++ b/entities/profile_generation.py	Wed Jul 20 11:47:19 2016 +0200
@@ -25,7 +25,7 @@
 from cubicweb.predicates import is_instance
 from cubicweb.view import EntityAdapter
 
-from cubes.seda.xsd import BASE_TYPES, XSDM_MAPPING
+from cubes.seda.xsd import BASE_TYPES, XSDM_MAPPING, JUMP_ELEMENTS
 
 
 JUMPED_OPTIONAL_ELEMENTS = set(('FileInfo', 'PhysicalDimensions', 'Coverage'))
@@ -124,6 +124,23 @@
     return minimum
 
 
+def iter_path_children(xselement, entity):
+    """Return an iterator on `entity` children entities according to `xselement` definition.
+
+    (`path`, `target`) is returned with `path` the path definition leading to the target, and
+    `target` either a final value in case of attributes or a list of entities.
+    """
+    for rtype, role, _path in XSDM_MAPPING.iter_rtype_role(xselement.local_name):
+        if _path[0][2] in BASE_TYPES:
+            # entity attribute
+            if getattr(entity, rtype) is not None:
+                yield _path, getattr(entity, rtype)
+        else:
+            related = entity.related(rtype, role, entities=True)
+            if related:
+                yield _path, related
+
+
 class SEDA2ExportAdapter(EntityAdapter):
     """Abstract base class for export of SEDA profile"""
     __abstract__ = True
@@ -221,22 +238,23 @@
             #    [x[:-1] for x in path]
             if not path:
                 assert not isinstance(occ.target, graph_nodes.XMLAttribute)
+                assert occ.target.local_name in JUMP_ELEMENTS, occ.target
+                if not any(iter_path_children(occ.target, entity)):
+                    # element has no children, skip it
+                    continue
                 if occ.target.local_name in JUMPED_OPTIONAL_ELEMENTS:
                     # elements in JUMPED_OPTIONAL_ELEMENTS are jumped but have optional cardinality,
                     # so search in all it's child element, and mark it as mandatory if one of them
                     # is mandatory, else keep it optional
                     cardinality = '0..1'
-                    for rtype, role, _path in XSDM_MAPPING.iter_rtype_role(occ.target.local_name):
+                    for _path, target in iter_path_children(occ.target, entity):
                         if _path[0][2] in BASE_TYPES:
                             # special case of a mandatory attribute: parent element will be
                             # mandatory if some value is specified, else that's fine
-                            if getattr(entity, rtype) is not None:
+                            if target is not None:
                                 cardinality = '1'
                                 break
-                            else:
-                                continue
-                        targets = entity.related(rtype, role, entities=True)
-                        if any(target.user_cardinality == '1' for target in targets):
+                        elif any(te.user_cardinality == '1' for te in target):
                             cardinality = '1'
                             break
                 else:
--- a/test/data/BV2.0_min.xml	Wed Jul 20 11:40:51 2016 +0200
+++ b/test/data/BV2.0_min.xml	Wed Jul 20 11:47:19 2016 +0200
@@ -21,9 +21,9 @@
             <FormatIdentification>
                 <Encoding>ASCII</Encoding>
             </FormatIdentification>
-            <FileInfo>
-                <Filename></Filename>
-            </FileInfo>
+            <!-- <FileInfo> -->
+            <!--     <Filename></Filename> -->
+            <!-- </FileInfo> -->
         </BinaryDataObject>
         <BinaryDataObject id="Bin2">
             <Uri>documentationBat.ascii</Uri>
--- a/test/test_profile_generation.py	Wed Jul 20 11:40:51 2016 +0200
+++ b/test/test_profile_generation.py	Wed Jul 20 11:47:19 2016 +0200
@@ -619,6 +619,8 @@
             root = self.profile_etree(transfer)
         self.check_xsd_profile(root, self.datapath('BV2.0_min.xml'),
                                mda_scheme_url=mda_scheme.absolute_url())
+        # ensure jumped element without content are not there
+        self.assertEqual(len(self.get_elements(root, 'Gps')), 0)
         self.assertProfileDetails(root)
 
 
--- a/xsd.py	Wed Jul 20 11:40:51 2016 +0200
+++ b/xsd.py	Wed Jul 20 11:47:19 2016 +0200
@@ -371,8 +371,12 @@
         for occ, path in subelement_defs:
             if occ.target.local_name == 'id':
                 continue
-            rtype, role, _, _ = path[0]
-            yield rtype, role, path
+            if not path:
+                for rtype, role, path in self.iter_rtype_role(occ.target.local_name):
+                    yield rtype, role, path
+            else:
+                rtype, role, _, _ = path[0]
+                yield rtype, role, path
 
     def elements_by_name(self, element_name):
         if self._elements_by_name_index is None: