[diag] Add a new warning about entities with 1 cardinality being followed by others
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 06 Dec 2017 16:24:28 +0100
changeset 2894 77695437f8b8
parent 2893 4d7eeccd9f55
child 2895 f5bf7c4bcd62
[diag] Add a new warning about entities with 1 cardinality being followed by others This will be desired in following csets since we'll stop ordering RNG export using cardinality to follow user enforced ordering, hence consistently with the UI.
cubicweb_seda/entities/diag.py
cubicweb_seda/i18n/en.po
cubicweb_seda/i18n/fr.po
test/test_diag.py
--- a/cubicweb_seda/entities/diag.py	Wed Dec 06 16:22:16 2017 +0100
+++ b/cubicweb_seda/entities/diag.py	Wed Dec 06 16:24:28 2017 +0100
@@ -115,6 +115,12 @@
           "this may cause RNG validation problems."),
         None,  # generated at run time
         []),  # have it's own hook
+    'rng_mandatory_not_first': Rule(
+        set(['RNG']),
+        _("Children with cardinality equal to '1' should appear before other "
+          "to avoid potential RNG validation problems."),
+        None,  # generated at run time
+        []),  # have it's own hook
 }
 
 
@@ -234,18 +240,19 @@
             yield 'use_archive_unit_ref', archive_unit
 
     def _check_rng_ambiguities(self):
-        for eid, rtype in self._cw.execute(_AMBIGUOUS_RQL,
-                                           {'c': self.entity.eid}):
-            # if the container is a simplified profile, allow to have several
-            # entities with cardinality != 1 under the seda_binary_data_object
-            # relation because data objects are all linked to the transfer
-            # through this relation, but this is only relevant for SEDA 2 export
-            # and we don't want to prevent this in profiles for SEDA 0.2 and 1.0
-            # export.
-            if rtype == 'seda_binary_data_object' and self.entity.simplified_profile:
-                continue
-            tab_id = _RTYPE_TO_TAB.get(rtype)
-            yield 'rng_ambiguity', _parent(self._cw.entity_from_eid(eid)), {'tab_id': tab_id}
+        for rql, error_id in [(_AMBIGUOUS_RQL, 'rng_ambiguity'),
+                              (_MANDATORY_NOT_LAST_AMBIGUOUS_RQL, 'rng_mandatory_not_first')]:
+            for eid, rtype in self._cw.execute(rql, {'c': self.entity.eid}):
+                # if the container is a simplified profile, allow to have several
+                # entities with cardinality != 1 under the seda_binary_data_object
+                # relation because data objects are all linked to the transfer
+                # through this relation, but this is only relevant for SEDA 2 export
+                # and we don't want to prevent this in profiles for SEDA 0.2 and 1.0
+                # export.
+                if rtype == 'seda_binary_data_object' and self.entity.simplified_profile:
+                    continue
+                tab_id = _RTYPE_TO_TAB.get(rtype)
+                yield error_id, _parent(self._cw.entity_from_eid(eid)), {'tab_id': tab_id}
 
 
 def _parent(entity):
@@ -296,3 +303,21 @@
                    'seda_binary_data_object', 'seda_physical_data_object',
                    'seda_related_transfer_reference')),
 ))
+
+_MANDATORY_NOT_LAST_AMBIGUOUS_RQL = ' UNION '.join(chain(
+    ('(Any P, "{rtype}" WHERE EXISTS(X1 {rtype} P, X2 {rtype} P, '
+     'P container C, C eid %(c)s, '
+     'X1 user_cardinality "1", NOT X2 user_cardinality "1", '
+     'X1 ordering ORD, X2 ordering < ORD))'.format(rtype=rtype)
+     for rtype in _RTYPE_TO_TAB
+     if rtype not in ('seda_binary_data_object',
+                      'seda_physical_data_object',
+                      'seda_related_transfer_reference')),
+    # we need another RQL for top level entities without container relation
+    ('(Any P, "{rtype}" WHERE EXISTS(X1 {rtype} P, X2 {rtype} P, P eid %(c)s, '
+     'X1 user_cardinality "1", NOT X2 user_cardinality "1", '
+     'X1 ordering ORD, X2 ordering < ORD))'.format(rtype=rtype)
+     for rtype in ('seda_archive_unit',
+                   'seda_binary_data_object', 'seda_physical_data_object',
+                   'seda_related_transfer_reference')),
+))
--- a/cubicweb_seda/i18n/en.po	Wed Dec 06 16:22:16 2017 +0100
+++ b/cubicweb_seda/i18n/en.po	Wed Dec 06 16:24:28 2017 +0100
@@ -79,6 +79,11 @@
 msgstr ""
 
 msgid ""
+"Children with cardinality equal to '1' should appear before other to avoid "
+"potential RNG validation problems."
+msgstr ""
+
+msgid ""
 "Custodial history is text with SEDA 0.2, hence date information isn't "
 "considered."
 msgstr ""
--- a/cubicweb_seda/i18n/fr.po	Wed Dec 06 16:22:16 2017 +0100
+++ b/cubicweb_seda/i18n/fr.po	Wed Dec 06 16:24:28 2017 +0100
@@ -79,6 +79,13 @@
 msgstr "Les références ne sont supportées qu'en SEDA 2."
 
 msgid ""
+"Children with cardinality equal to '1' should appear before other to avoid "
+"potential RNG validation problems."
+msgstr ""
+"Les sous-éléments avec une cardinalité égale à '1' devrait apparaître avant "
+"les autres pour éviter des potentiels problèmes lors de la validation RNG"
+
+msgid ""
 "Custodial history is text with SEDA 0.2, hence date information isn't "
 "considered."
 msgstr ""
--- a/test/test_diag.py	Wed Dec 06 16:22:16 2017 +0100
+++ b/test/test_diag.py	Wed Dec 06 16:24:28 2017 +0100
@@ -151,8 +151,8 @@
             transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'test profile')
             doctor = transfer.cw_adapt_to('ISEDACompatAnalyzer')
             testutils.create_archive_unit(transfer)
-            testutils.create_archive_unit(transfer, user_cardinality=u'0..1')
             unit2 = testutils.create_archive_unit(transfer, user_cardinality=u'0..1')[0]
+            testutils.create_archive_unit(transfer, user_cardinality=u'1..n')
             cnx.commit()
 
             self.assertIsRNGAmbiguous(transfer, True)
@@ -234,6 +234,21 @@
 
             self.assertErrorOn(doctor, 'rng_ambiguity', [(unit, 'seda_indexation_tab')])
 
+    def test_single_cardinality_not_first(self):
+        with self.admin_access.cnx() as cnx:
+            transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'test profile')
+            doctor = transfer.cw_adapt_to('ISEDACompatAnalyzer')
+            u1 = testutils.create_archive_unit(transfer, user_cardinality=u'1..n')
+            u2 = testutils.create_archive_unit(transfer, user_cardinality=u'1')
+            cnx.commit()
+            self.assertEqual(u1[0].ordering, 1)
+            self.assertEqual(u2[0].ordering, 2)
+            self.assertEqual(u1[0].user_cardinality, '1..n')
+            self.assertEqual(u2[0].user_cardinality, '1')
+            self.assertIsRNGAmbiguous(transfer, True)
+            self.assertErrorOn(doctor, 'rng_mandatory_not_first',
+                               [(transfer, 'seda_archive_units_tab')])
+
     def assertErrorOn(self, doctor, error_id, expected_errors):
         errors = set()
         for error in doctor.detect_problems():