[profile gen] Account for bdo's user cardinality or its parent's in "Integrity" element (CONSEJIRA-663)
authorDenis Laxalde <denis.laxalde@logilab.fr>
Thu, 02 May 2019 09:29:04 +0200
changeset 3003 fcb890e6d9d7
parent 3002 1fe87336af7c
child 3004 0a565615f0d5
[profile gen] Account for bdo's user cardinality or its parent's in "Integrity" element (CONSEJIRA-663) See also https://jira.mtpl.bs.fr.atos.net/browse/CONSEJIRA-663 For more details, quoting verbatim the customer request for future reference: | Sur les documents multi occurrences (0-n ou 1-n) ou même si l’archive | est multi occurrence (0-n ou 1-n), il y a une balise integrity qui est | liée au document dans le bordereau de versement que l’on génère. | Or, dans le XSD du profil, la balise integrity du document ne possède | pas l'attribut : maxOccurs="unbounded" | Donc lors de l'envoi à Asalae du versement, Asalae fait une vérification | par rapport au profil et affiche une erreur a cause de la balise | integrity qui est présente plusieurs fois. | | Pour moi le correctif à faire doit porter sur le XSD et le RNG sur la | balise integrity pour qu'elle soit multi-occurrence possible dans les cas | suivants : | | * Si le document (Document) est en multi occurrence (0-n ou 1-n) | donc avec un attribut maxOccurs="unbounded" | * Si une UA (Contains) supérieure au document est en multi | occurrence (0-n ou 1-n) donc avec un attribut | maxOccurs="unbounded" | si une UA supérieur est en cardinalité max unbounded, alors | l'integrity sur les documents en dessous doit forcément être en | unbounded également | il faut que l'integrity soit en facultative ou multi occurrence si une | UA supérieure est facultative ou en multi occurrence. This is essentially achieved by looking for the upper parent archive units of a data object then their cardinality to produce the Integrity element. Adding tests for new integrity_cardinality() function based on customer examples.
cubicweb_seda/entities/profile_generation.py
test/data/seda_02_export.rng
test/data/seda_02_export.xsd
test/data/seda_1_export.rng
test/data/seda_1_export.xsd
test/test_profile_generation.py
--- a/cubicweb_seda/entities/profile_generation.py	Tue Apr 23 13:49:15 2019 +0200
+++ b/cubicweb_seda/entities/profile_generation.py	Thu May 02 09:29:04 2019 +0200
@@ -123,6 +123,24 @@
     return text_type(value)
 
 
+def integrity_cardinality(data_object):
+    minvalue, maxvalue = minmax_cardinality(data_object.user_cardinality)
+    itree = data_object.cw_adapt_to('ITreeBase')
+    for parent in itree.iterancestors():
+        try:
+            parent_cardinality = parent.user_cardinality
+        except AttributeError:
+            continue
+        minc, maxc = minmax_cardinality(parent_cardinality)
+        minvalue = min(minc, minvalue)
+        maxvalue = max(maxc, maxvalue)
+    if maxvalue == graph_nodes.INFINITY:
+        maxvalue = 'n'
+    if minvalue == maxvalue == 1:
+        return '1'
+    return '{}..{}'.format(minvalue, maxvalue)
+
+
 def minmax_cardinality(string_cardinality, _allowed=('0..1', '0..n', '1', '1..n')):
     """Return (minimum, maximum) cardinality for the cardinality as string (one of '0..1', '0..n',
     '1' or '1..n').
@@ -1096,9 +1114,10 @@
             self.xsd_agency(parent, 'OriginatingAgency', content.originating_agency)
 
     def xsd_integrity(self, parent, data_object):
+        cardinality = integrity_cardinality(data_object)
         algorithm = data_object.seda_algorithm[0] if data_object.seda_algorithm else None
         self.element_schema(parent, 'Integrity', 'qdt:ArchivesHashcodeBinaryObjectType',
-                            cardinality='0..1',
+                            cardinality=cardinality,
                             xsd_attributes=[XAttr('algorithme', 'xsd:string',
                                                   cardinality='1',
                                                   fixed_value=_concept_value(
@@ -1298,7 +1317,8 @@
             )
 
     def xsd_integrity(self, parent, data_object):
-        integrity = self.element_schema(parent, 'Integrity', cardinality='0..1')
+        cardinality = integrity_cardinality(data_object)
+        integrity = self.element_schema(parent, 'Integrity', cardinality=cardinality)
         algorithm = data_object.seda_algorithm[0] if data_object.seda_algorithm else None
         self.element_schema(integrity, 'Contains', 'qdt:ArchivesHashcodeBinaryObjectType',
                             xsd_attributes=[XAttr('algorithme', 'xsd:string',
--- a/test/data/seda_02_export.rng	Tue Apr 23 13:49:15 2019 +0200
+++ b/test/data/seda_02_export.rng	Thu May 02 09:29:04 2019 +0200
@@ -148,7 +148,7 @@
           </rng:element>
         </rng:optional>
       </rng:element>
-      <rng:optional>
+      <rng:zeroOrMore>
         <rng:element name="Integrity">
           <rng:element name="Contains">
             <rng:attribute name="algorithme">
@@ -160,7 +160,7 @@
             <rng:data type="string"/>
           </rng:element>
         </rng:element>
-      </rng:optional>
+      </rng:zeroOrMore>
       <rng:oneOrMore>
         <rng:element name="Contains" xml:id="id%(unit-eid)s">
           <xsd:annotation>
--- a/test/data/seda_02_export.xsd	Tue Apr 23 13:49:15 2019 +0200
+++ b/test/data/seda_02_export.xsd	Thu May 02 09:29:04 2019 +0200
@@ -86,7 +86,7 @@
             </xsd:sequence>
           </xsd:complexType>
         </xsd:element>
-        <xsd:element minOccurs="0" name="Integrity">
+        <xsd:element maxOccurs="unbounded" minOccurs="0" name="Integrity">
           <xsd:complexType>
             <xsd:sequence>
               <xsd:element name="Contains">
--- a/test/data/seda_1_export.rng	Tue Apr 23 13:49:15 2019 +0200
+++ b/test/data/seda_1_export.rng	Thu May 02 09:29:04 2019 +0200
@@ -563,14 +563,14 @@
                 </rng:attribute>
                 <rng:data type="string"/>
               </rng:element>
-              <rng:optional>
+              <rng:zeroOrMore>
                 <rng:element name="Integrity">
                   <rng:attribute name="algorithme">
                     <rng:value type="string">md5</rng:value>
                   </rng:attribute>
                   <rng:data type="string"/>
                 </rng:element>
-              </rng:optional>
+              </rng:zeroOrMore>
               <rng:element name="Type">
                 <rng:attribute name="listVersionID">
                   <rng:value type="token">edition 2009</rng:value>
--- a/test/data/seda_1_export.xsd	Tue Apr 23 13:49:15 2019 +0200
+++ b/test/data/seda_1_export.xsd	Thu May 02 09:29:04 2019 +0200
@@ -460,7 +460,7 @@
                         </xsd:simpleContent>
                       </xsd:complexType>
                     </xsd:element>
-                    <xsd:element minOccurs="0" name="Integrity">
+                    <xsd:element minOccurs="0" maxOccurs="unbounded" name="Integrity">
                       <xsd:complexType>
                         <xsd:simpleContent>
                           <xsd:extension base="qdt:ArchivesHashcodeBinaryObjectType">
--- a/test/test_profile_generation.py	Tue Apr 23 13:49:15 2019 +0200
+++ b/test/test_profile_generation.py	Thu May 02 09:29:04 2019 +0200
@@ -312,6 +312,50 @@
             self.assertEqual(target_value[0], None)
             self.assertEqual(target_value[1].eid, bdo.eid)
 
+    def test_integrity_cardinality(self):
+        with self.admin_access.cnx() as cnx:
+            transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'test profile')
+            bdo = cnx.create_entity('SEDABinaryDataObject',
+                                    user_annotation=u'I am mandatory',
+                                    seda_binary_data_object=transfer)
+            assert pg.integrity_cardinality(bdo) == '1'
+            bdo = cnx.create_entity('SEDABinaryDataObject',
+                                    user_cardinality=u'0..1',
+                                    user_annotation=u'opt',
+                                    seda_binary_data_object=transfer)
+            assert pg.integrity_cardinality(bdo) == '0..1'
+
+            _, _, unit_alt_seq = testutils.create_archive_unit(
+                None, cnx=cnx)
+            bdo = testutils.create_data_object(unit_alt_seq)
+            assert pg.integrity_cardinality(bdo) == '0..n'
+
+            _, _, unit_alt_seq = testutils.create_archive_unit(
+                None, cnx=cnx, user_cardinality=u'1..n')
+            bdo = testutils.create_data_object(unit_alt_seq, user_cardinality=u'0..1')
+            assert pg.integrity_cardinality(bdo) == '0..n'
+
+            _, _, unit_alt_seq = testutils.create_archive_unit(
+                None, cnx=cnx, user_cardinality=u'1..n')
+            _, _, unit_alt_seq2 = testutils.create_archive_unit(
+                unit_alt_seq, cnx=cnx, user_cardinality=u'0..n')
+            bdo = testutils.create_data_object(unit_alt_seq2, user_cardinality=u'1')
+            assert pg.integrity_cardinality(bdo) == '0..n'
+
+            _, _, unit_alt_seq = testutils.create_archive_unit(
+                None, cnx=cnx, user_cardinality=u'1')
+            _, _, unit_alt_seq2 = testutils.create_archive_unit(
+                unit_alt_seq, cnx=cnx, user_cardinality=u'1..n')
+            bdo = testutils.create_data_object(unit_alt_seq2, user_cardinality=u'0..1')
+            assert pg.integrity_cardinality(bdo) == '0..n'
+
+            _, _, unit_alt_seq = testutils.create_archive_unit(
+                None, cnx=cnx, user_cardinality=u'0..1')
+            _, _, unit_alt_seq2 = testutils.create_archive_unit(
+                unit_alt_seq, cnx=cnx, user_cardinality=u'1')
+            bdo = testutils.create_data_object(unit_alt_seq2, user_cardinality=u'1..n')
+            assert pg.integrity_cardinality(bdo) == '0..n'
+
 
 class SEDA2RNGExportTC(RelaxNGTestMixin, CubicWebTC):