[profile generation] Add xml:id on repeatable elements
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 02 Nov 2017 15:29:15 +0100
changeset 2822 29b99ee6ff4c
parent 2821 aad582bae5c8
child 2824 9d9a6b34e029
[profile generation] Add xml:id on repeatable elements so client (e.g. GED sas) may rely on this to identify nodes unambiguously. Closes extranet #39304385
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	Fri Nov 03 08:14:05 2017 +0100
+++ b/cubicweb_seda/entities/profile_generation.py	Thu Nov 02 15:29:15 2017 +0100
@@ -773,10 +773,13 @@
 
     def xsd_archive(self, parent, archive_unit):
         """Append XSD elements for an archive to the given parent node."""
-        archive_node = self.element_schema(parent, 'Archive',
-                                           cardinality=archive_unit.user_cardinality,
-                                           documentation=archive_unit.user_annotation,
-                                           xsd_attributes=[XAttr('Id', 'xsd:ID')])
+        archive_node = self.element_schema(
+            parent, 'Archive',
+            cardinality=archive_unit.user_cardinality,
+            documentation=archive_unit.user_annotation,
+            xsd_attributes=[XAttr('Id', 'xsd:ID')],
+            extra_attributes={'xml:id': eid2xmlid(archive_unit.eid)},
+        )
         transfer = archive_unit.cw_adapt_to('ITreeBase').parent()
         self.xsd_archival_agreement(archive_node, transfer)
         # hard-coded description's language XXX fine, content language may be specified
@@ -804,10 +807,13 @@
 
     def xsd_archive_object(self, parent, archive_unit):
         """Append XSD elements for the archive object to the given parent node."""
-        ao_node = self.element_schema(parent, self.archive_object_tag_name,
-                                      cardinality=archive_unit.user_cardinality,
-                                      documentation=archive_unit.user_annotation,
-                                      xsd_attributes=[XAttr('Id', 'xsd:ID')])
+        ao_node = self.element_schema(
+            parent, self.archive_object_tag_name,
+            cardinality=archive_unit.user_cardinality,
+            documentation=archive_unit.user_annotation,
+            xsd_attributes=[XAttr('Id', 'xsd:ID')],
+            extra_attributes={'xml:id': eid2xmlid(archive_unit.eid)},
+        )
         content_entity = self.archive_unit_content(archive_unit)
         self.element_schema(ao_node, 'Name', 'udt:TextType',
                             fixed_value=content_entity.title.title,
@@ -833,10 +839,13 @@
 
     def xsd_document(self, parent, data_object):
         """Append XSD elements for the document to the given parent node."""
-        document_node = self.element_schema(parent, 'Document',
-                                            cardinality=data_object.user_cardinality,
-                                            documentation=data_object.user_annotation,
-                                            xsd_attributes=[XAttr('Id', 'xsd:ID')])
+        document_node = self.element_schema(
+            parent, 'Document',
+            cardinality=data_object.user_cardinality,
+            documentation=data_object.user_annotation,
+            xsd_attributes=[XAttr('Id', 'xsd:ID')],
+            extra_attributes={'xml:id': eid2xmlid(data_object.eid)},
+        )
 
         self.xsd_system_id(document_node, data_object)
         self.xsd_attachment(document_node, data_object)
@@ -1065,12 +1074,15 @@
                                           cardinality='0..1')
             for item in _sorted_children(content.custodial_history_items):
                 when_card = item.when.user_cardinality if item.when else None
-                self.element_schema(ch_node, 'CustodialHistoryItem', 'qdt:CustodialHistoryItemType',
-                                    cardinality=item.user_cardinality,
-                                    documentation=item.user_annotation,
-                                    xsd_attributes=[XAttr('when', 'udt:DateType',
-                                                          cardinality=when_card),
-                                                    XAttr('languageID', 'xsd:language')])
+                self.element_schema(
+                    ch_node, 'CustodialHistoryItem', 'qdt:CustodialHistoryItemType',
+                    cardinality=item.user_cardinality,
+                    documentation=item.user_annotation,
+                    xsd_attributes=[XAttr('when', 'udt:DateType',
+                                          cardinality=when_card),
+                                    XAttr('languageID', 'xsd:language')],
+                    extra_attributes={'xml:id': eid2xmlid(item.eid)},
+                )
 
     def xsd_originating_agency(self, parent, content):
         if content.originating_agency:
@@ -1093,10 +1105,13 @@
 
     def xsd_keyword(self, parent, keyword):
         """Append XSD elements for a keyword to the given parent node"""
-        kw_node = self.element_schema(parent, self.kw_tag_name,
-                                      cardinality=keyword.user_cardinality,
-                                      documentation=keyword.user_annotation,
-                                      xsd_attributes=[XAttr('Id', 'xsd:ID')])
+        kw_node = self.element_schema(
+            parent, self.kw_tag_name,
+            cardinality=keyword.user_cardinality,
+            documentation=keyword.user_annotation,
+            xsd_attributes=[XAttr('Id', 'xsd:ID')],
+            extra_attributes={'xml:id': eid2xmlid(keyword.eid)},
+        )
         content = keyword.keyword_content
         url = None
         if keyword.reference:
@@ -1189,10 +1204,13 @@
 
     def xsd_archive(self, parent, archive_unit):
         """Append XSD elements for an archive to the given parent node."""
-        archive_node = self.element_schema(parent, 'Contains',
-                                           cardinality=archive_unit.user_cardinality,
-                                           documentation=archive_unit.user_annotation,
-                                           xsd_attributes=[XAttr('Id', 'xsd:ID')])
+        archive_node = self.element_schema(
+            parent, 'Contains',
+            cardinality=archive_unit.user_cardinality,
+            documentation=archive_unit.user_annotation,
+            xsd_attributes=[XAttr('Id', 'xsd:ID')],
+            extra_attributes={'xml:id': eid2xmlid(archive_unit.eid)},
+        )
         transfer = archive_unit.cw_adapt_to('ITreeBase').parent()
         self.xsd_archival_agreement(archive_node, transfer)
         # hard-coded description's language
@@ -1260,10 +1278,13 @@
     def xsd_custodial_history(self, parent, content):
         if content.custodial_history_items:
             item = content.custodial_history_items[0]
-            self.element_schema(parent, 'CustodialHistory', 'udt:TextType',
-                                cardinality=item.user_cardinality,
-                                documentation=item.user_annotation,
-                                xsd_attributes=[XAttr('languageID', 'xsd:language')])
+            self.element_schema(
+                parent, 'CustodialHistory', 'udt:TextType',
+                cardinality=item.user_cardinality,
+                documentation=item.user_annotation,
+                xsd_attributes=[XAttr('languageID', 'xsd:language')],
+                extra_attributes={'xml:id': eid2xmlid(item.eid)},
+            )
 
     def xsd_integrity(self, parent, data_object):
         integrity = self.element_schema(parent, 'Integrity', cardinality='0..1')
--- a/test/data/seda_02_export.rng	Fri Nov 03 08:14:05 2017 +0100
+++ b/test/data/seda_02_export.rng	Thu Nov 02 15:29:15 2017 +0100
@@ -160,7 +160,7 @@
         </rng:element>
       </rng:optional>
       <rng:oneOrMore>
-        <rng:element name="Contains">
+        <rng:element name="Contains" xml:id="id%(unit-eid)s">
           <xsd:annotation>
             <xsd:documentation>archive unit title</xsd:documentation>
           </xsd:annotation>
@@ -196,7 +196,7 @@
               </rng:attribute>
             </rng:optional>
             <rng:optional>
-              <rng:element name="CustodialHistory">
+              <rng:element name="CustodialHistory" xml:id="id%(chi-eid)s">
                 <rng:optional>
                   <rng:attribute name="languageID">
                     <rng:data type="language"/>
@@ -274,7 +274,7 @@
               </rng:element>
             </rng:optional>
             <rng:zeroOrMore>
-              <rng:element name="ContentDescriptive">
+              <rng:element name="ContentDescriptive" xml:id="id%(kw-eid)s">
                 <rng:optional>
                   <rng:attribute name="Id">
                     <rng:data type="ID"/>
@@ -409,7 +409,7 @@
               </rng:element>
             </rng:element>
           </rng:zeroOrMore>
-          <rng:element name="Contains">
+          <rng:element name="Contains" xml:id="id%(subunit2-eid)s">
             <rng:element name="DescriptionLevel">
               <rng:attribute name="listVersionID">
                 <rng:value type="token">edition 2009</rng:value>
@@ -477,7 +477,7 @@
             </rng:element>
           </rng:element>
           <rng:oneOrMore>
-            <rng:element name="Contains">
+            <rng:element name="Contains" xml:id="id%(subunit1-eid)s">
               <rng:element name="DescriptionLevel">
                 <rng:attribute name="listVersionID">
                   <rng:value type="token">edition 2009</rng:value>
--- a/test/data/seda_02_export.xsd	Fri Nov 03 08:14:05 2017 +0100
+++ b/test/data/seda_02_export.xsd	Thu Nov 02 15:29:15 2017 +0100
@@ -100,7 +100,7 @@
             </xsd:sequence>
           </xsd:complexType>
         </xsd:element>
-        <xsd:element maxOccurs="unbounded" name="Contains">
+        <xsd:element maxOccurs="unbounded" name="Contains" xml:id="id%(unit-eid)s">
           <xsd:annotation>
             <xsd:documentation>archive unit title</xsd:documentation>
           </xsd:annotation>
@@ -136,7 +136,7 @@
               <xsd:element name="ContentDescription">
                 <xsd:complexType>
                   <xsd:sequence>
-                    <xsd:element minOccurs="0" name="CustodialHistory">
+                    <xsd:element minOccurs="0" name="CustodialHistory" xml:id="id%(chi-eid)s">
                       <xsd:complexType>
                         <xsd:simpleContent>
                           <xsd:extension base="udt:TextType">
@@ -186,7 +186,7 @@
                         </xsd:sequence>
                       </xsd:complexType>
                     </xsd:element>
-                    <xsd:element maxOccurs="unbounded" minOccurs="0" name="ContentDescriptive">
+                    <xsd:element maxOccurs="unbounded" minOccurs="0" name="ContentDescriptive" xml:id="id%(kw-eid)s">
                       <xsd:complexType>
                         <xsd:sequence>
                           <xsd:element fixed="file" name="KeywordContent">
@@ -311,7 +311,7 @@
                   <xsd:attribute name="Id" type="xsd:ID" use="optional"/>
                 </xsd:complexType>
               </xsd:element>
-              <xsd:element name="Contains">
+              <xsd:element name="Contains" xml:id="id%(subunit2-eid)s">
                 <xsd:annotation>
                   <xsd:documentation>archive unit title</xsd:documentation>
                 </xsd:annotation>
@@ -381,7 +381,7 @@
                   <xsd:attribute name="Id" type="xsd:ID" use="optional"/>
                 </xsd:complexType>
               </xsd:element>
-              <xsd:element maxOccurs="unbounded" name="Contains">
+              <xsd:element maxOccurs="unbounded" name="Contains" xml:id="id%(subunit1-eid)s">
                 <xsd:annotation>
                   <xsd:documentation>archive unit title</xsd:documentation>
                 </xsd:annotation>
--- a/test/data/seda_1_export.rng	Fri Nov 03 08:14:05 2017 +0100
+++ b/test/data/seda_1_export.rng	Thu Nov 02 15:29:15 2017 +0100
@@ -147,7 +147,7 @@
         </rng:optional>
       </rng:element>
       <rng:oneOrMore>
-        <rng:element name="Archive">
+        <rng:element name="Archive" xml:id="id%(unit-eid)s">
           <xsd:annotation>
             <xsd:documentation>archive unit title</xsd:documentation>
           </xsd:annotation>
@@ -211,7 +211,7 @@
             <rng:optional>
               <rng:element name="CustodialHistory">
                 <rng:optional>
-                  <rng:element name="CustodialHistoryItem">
+                  <rng:element name="CustodialHistoryItem" xml:id="id%(chi-eid)s">
                     <rng:optional>
                       <rng:attribute name="when">
                         <rng:data type="string"/>
@@ -228,7 +228,7 @@
               </rng:element>
             </rng:optional>
             <rng:zeroOrMore>
-              <rng:element name="Keyword">
+              <rng:element name="Keyword" xml:id="id%(kw-eid)s">
                 <rng:optional>
                   <rng:attribute name="Id">
                     <rng:data type="ID"/>
@@ -372,7 +372,7 @@
               <rng:data type="string"/>
             </rng:element>
           </rng:element>
-          <rng:element name="ArchiveObject">
+          <rng:element name="ArchiveObject" xml:id="id%(subunit2-eid)s">
             <xsd:annotation>
               <xsd:documentation>archive unit title</xsd:documentation>
             </xsd:annotation>
@@ -453,7 +453,7 @@
             </rng:element>
           </rng:element>
           <rng:oneOrMore>
-            <rng:element name="ArchiveObject">
+            <rng:element name="ArchiveObject" xml:id="id%(subunit1-eid)s">
               <xsd:annotation>
                 <xsd:documentation>archive unit title</xsd:documentation>
               </xsd:annotation>
@@ -532,7 +532,7 @@
             </rng:element>
           </rng:oneOrMore>
           <rng:zeroOrMore>
-            <rng:element name="Document">
+            <rng:element name="Document" xml:id="id%(bdo-eid)s">
               <xsd:annotation>
                 <xsd:documentation>data object title</xsd:documentation>
               </xsd:annotation>
--- a/test/data/seda_1_export.xsd	Fri Nov 03 08:14:05 2017 +0100
+++ b/test/data/seda_1_export.xsd	Thu Nov 02 15:29:15 2017 +0100
@@ -84,7 +84,7 @@
             </xsd:sequence>
           </xsd:complexType>
         </xsd:element>
-        <xsd:element maxOccurs="unbounded" name="Archive">
+        <xsd:element maxOccurs="unbounded" name="Archive" xml:id="id%(unit-eid)s">
           <xsd:annotation>
             <xsd:documentation>archive unit title</xsd:documentation>
           </xsd:annotation>
@@ -143,7 +143,7 @@
                     <xsd:element minOccurs="0" name="CustodialHistory">
                       <xsd:complexType>
                         <xsd:sequence>
-                          <xsd:element minOccurs="0" name="CustodialHistoryItem">
+                          <xsd:element minOccurs="0" name="CustodialHistoryItem" xml:id="id%(chi-eid)s">
                             <xsd:complexType>
                               <xsd:simpleContent>
                                 <xsd:extension base="qdt:CustodialHistoryItemType">
@@ -156,7 +156,7 @@
                         </xsd:sequence>
                       </xsd:complexType>
                     </xsd:element>
-                    <xsd:element maxOccurs="unbounded" minOccurs="0" name="Keyword">
+                    <xsd:element maxOccurs="unbounded" minOccurs="0" name="Keyword" xml:id="id%(kw-eid)s">
                       <xsd:complexType>
                         <xsd:sequence>
                           <xsd:element fixed="file" name="KeywordContent">
@@ -263,7 +263,7 @@
                   <xsd:attribute name="Id" type="xsd:ID" use="optional"/>
                 </xsd:complexType>
               </xsd:element>
-              <xsd:element name="ArchiveObject">
+              <xsd:element name="ArchiveObject" xml:id="id%(subunit2-eid)s">
                 <xsd:annotation>
                   <xsd:documentation>archive unit title</xsd:documentation>
                 </xsd:annotation>
@@ -349,7 +349,7 @@
                   <xsd:attribute name="Id" type="xsd:ID" use="optional"/>
                 </xsd:complexType>
               </xsd:element>
-              <xsd:element maxOccurs="unbounded" name="ArchiveObject">
+              <xsd:element maxOccurs="unbounded" name="ArchiveObject" xml:id="id%(subunit1-eid)s">
                 <xsd:annotation>
                   <xsd:documentation>archive unit title</xsd:documentation>
                 </xsd:annotation>
@@ -431,7 +431,7 @@
                   <xsd:attribute name="Id" type="xsd:ID" use="optional"/>
                 </xsd:complexType>
               </xsd:element>
-              <xsd:element maxOccurs="unbounded" minOccurs="0" name="Document">
+              <xsd:element maxOccurs="unbounded" minOccurs="0" name="Document" xml:id="id%(bdo-eid)s">
                 <xsd:annotation>
                   <xsd:documentation>data object title</xsd:documentation>
                 </xsd:annotation>
--- a/test/test_profile_generation.py	Fri Nov 03 08:14:05 2017 +0100
+++ b/test/test_profile_generation.py	Thu Nov 02 15:29:15 2017 +0100
@@ -779,8 +779,8 @@
                    seda_seq_appraisal_rule_rule=appraisal_rule_rule,
                    user_annotation=u'detruire le document')
 
-            _, _, unit_alt_seq = testutils.create_archive_unit(transfer,
-                                                               user_cardinality=u'1..n')
+            unit, _, unit_alt_seq = testutils.create_archive_unit(transfer,
+                                                                  user_cardinality=u'1..n')
 
             unit_alt_seq.cw_set(reverse_seda_start_date=create('SEDAStartDate',
                                                                user_cardinality=u'0..1'),
@@ -811,15 +811,15 @@
                    seda_type_from=unit_alt_seq,
                    seda_type_to=concepts['CDO'])
 
-            create('SEDACustodialHistoryItem',
-                   user_cardinality=u'0..1',
-                   seda_custodial_history_item=unit_alt_seq,
-                   reverse_seda_when=create('SEDAwhen',
-                                            user_cardinality=u'0..1'))
+            chi = create('SEDACustodialHistoryItem',
+                         user_cardinality=u'0..1',
+                         seda_custodial_history_item=unit_alt_seq,
+                         reverse_seda_when=create('SEDAwhen',
+                                                  user_cardinality=u'0..1'))
 
             # Add sub archive unit
-            _, _, subunit_alt_seq = testutils.create_archive_unit(unit_alt_seq,
-                                                                  user_cardinality=u'1..n')
+            subunit1, _, subunit_alt_seq = testutils.create_archive_unit(
+                unit_alt_seq, user_cardinality=u'1..n')
 
             create('SEDAAppraisalRule',
                    user_cardinality=u'0..1',
@@ -854,13 +854,18 @@
                    seda_encoding_to=concepts['6'])
 
             # Add another sub archive unit
-            _, _, subunit2_alt_seq = testutils.create_archive_unit(unit_alt_seq,
-                                                                   user_cardinality=u'1')
+            subunit2, _, subunit2_alt_seq = testutils.create_archive_unit(
+                unit_alt_seq, user_cardinality=u'1')
 
             cnx.commit()
 
         self.transfer_eid = transfer.eid
+        self.unit_eid = unit.eid
+        self.subunit1_eid = subunit1.eid
+        self.subunit2_eid = subunit2.eid
         self.bdo_eid = bdo.eid
+        self.kw_eid = kw.eid
+        self.chi_eid = chi.eid
         self.file_concept_eid = concepts['file'].eid
         self.agent_eid = agent.eid
 
@@ -888,7 +893,12 @@
             self.assertXmlValid(root)
             with open(self.datapath(expected_file)) as expected:
                 self.assertXmlEqual(expected.read()
-                                    % {'bdo-eid': binary_type(self.bdo_eid),
+                                    % {'unit-eid': binary_type(self.unit_eid),
+                                       'subunit1-eid': binary_type(self.subunit1_eid),
+                                       'subunit2-eid': binary_type(self.subunit2_eid),
+                                       'bdo-eid': binary_type(self.bdo_eid),
+                                       'kw-eid': binary_type(self.kw_eid),
+                                       'chi-eid': binary_type(self.chi_eid),
                                        'concept-uri': binary_type(file_concept.cwuri),
                                        'scheme-uri': binary_type(file_concept.scheme.cwuri),
                                        'agent-id': binary_type(agent.eid),