[PATCH eac] Add ParallelNames entity for <nameEntryParallel> tag

Guillaume Vandevelde guillaume.vandevelde at logilab.fr
Mon Aug 26 12:27:01 CEST 2019


# HG changeset patch
# User Guillaume Vandevelde <gvandevelde at logilab.fr>
# Date 1566290946 -7200
#      Tue Aug 20 10:49:06 2019 +0200
# Node ID 5ee1a2dcb62861282ce4694b99cb157636913176
# Parent  f6c054166efd558f4af6acec52975827beefe9ad
# Available At http://hg.logilab.org/review/cubes/eac
#              hg pull http://hg.logilab.org/review/cubes/eac -r 5ee1a2dcb628
Add  ParallelNames entity for <nameEntryParallel> tag

A hook have to be set on the NameEntry entity
verifying that the same object can not have a
`simple_name_relation` and a `name_entry_relation`

diff -r f6c054166efd -r 5ee1a2dcb628 cubicweb_eac/dataimport.py
--- a/cubicweb_eac/dataimport.py	Thu Jul 25 14:36:52 2019 +0200
+++ b/cubicweb_eac/dataimport.py	Tue Aug 20 10:49:06 2019 +0200
@@ -46,8 +46,8 @@
                      'Mandate', 'LegalStatus', 'History', 'HistoricalEvent', 'Structure',
                      'AgentFunction', 'Occupation', 'GeneralContext', 'AssociationRelation',
                      'ChronologicalRelation', 'HierarchicalRelation', 'EACResourceRelation',
-                     'EACFunctionRelation', 'ExternalUri', 'EACSource', 'Activity',
-                     'NameEntry')
+                     'EACFunctionRelation', 'ParallelNames', 'ExternalUri', 'EACSource',
+                     'Activity', 'NameEntry')
 
 
 class InvalidEAC(RuntimeError):
@@ -193,6 +193,8 @@
 
 add_citations_for = partial(add_child_for, relation='has_citation', builder='build_citation')
 add_events_for = partial(add_child_for, relation='has_event', builder='build_event')
+add_names_for = partial(add_child_for, relation='simple_name_relation',
+                        builder='build_name_child')
 add_dates_for = partial(add_child_for, relation='date_relation',
                         builder='build_date_entry')
 add_place_entries_for = partial(add_child_for, relation='place_entry_relation',
@@ -445,6 +447,20 @@
             raise MissingTag('nameEntry', 'identity')
         for name_entry in name_entries:
             yield self.build_name_entry(name_entry)
+        parallel_name_entries = self._elem_findall(identity, 'eac:nameEntryParallel')
+        for parallel_name_entry in parallel_name_entries:
+            for extentity in self.build_parallel(parallel_name_entry):
+                yield extentity
+
+    @relate_to_record_through('ParallelNames', 'parallel_names_of')
+    @add_names_for('ParallelNames')
+    @add_dates_for('ParallelNames')
+    def build_parallel(self, elem):
+        """For each nameEntryParallel build a new object linked to the
+        EAC-CPF document and get relations for all childrens"""
+        values = self.values_from_xpaths(elem, (('authorized_form', 'eac:authorizedForm'),
+                                                ('alternative_form', 'eac:alternativeForm')))
+        yield ExtEntity('ParallelNames', self._gen_extid(), values)
 
     def extract_dates_from(self, elem, tag):
         for date in self.find_nested(elem, 'eac:date', tag):
@@ -464,6 +480,24 @@
             for extentity in self.extract_dates_from(date_set, 'eac:dateSet'):
                 yield extentity
 
+    @filter_empty
+    @filter_none
+    @elem_maybe_none
+    def build_name_child(self, elem):
+        """Build NameEntry external entity"""
+        for elem in self._elem_findall(elem, 'eac:nameEntry'):
+            values = self.values_from_attrib(elem, (('language', 'lang'),
+                                                    ('script_code', 'scriptCode')))
+            values.update(self.values_from_xpaths(elem,
+                                                  (('preferred_form', 'eac:preferredForm'),
+                                                   ('alternative_form', 'eac:alternativeForm'),
+                                                   ('authorized_form', 'eac:authorizedForm'))))
+            parts = self._elem_findall(elem, 'eac:part')
+            if not parts:
+                raise MissingTag('part', 'nameEntry')
+            values.update({'parts': set([u', '.join(text_type(p.text) for p in parts)])})
+            yield ExtEntity('NameEntry', self._gen_extid(), values)
+
     @filter_none
     def parse_description(self, description):
         """Parse the `description` tag and yield external entities, possibly
diff -r f6c054166efd -r 5ee1a2dcb628 cubicweb_eac/entities.py
--- a/cubicweb_eac/entities.py	Thu Jul 25 14:36:52 2019 +0200
+++ b/cubicweb_eac/entities.py	Tue Aug 20 10:49:06 2019 +0200
@@ -251,6 +251,12 @@
                                                 'longitude', 'local_type'))
 
 
+class ParallelNames(DateRelationMixin, AnyEntity):
+    __regid__ = 'ParallelNames'
+    fetch_attrs, cw_fetch_order = fetch_config(('authorized_form',
+                                                'alternative_form'))
+
+
 # XML export
 
 def substitute_xml_prefix(prefix_name, namespaces):
diff -r f6c054166efd -r 5ee1a2dcb628 cubicweb_eac/migration/0.9.0_Any.py
--- a/cubicweb_eac/migration/0.9.0_Any.py	Thu Jul 25 14:36:52 2019 +0200
+++ b/cubicweb_eac/migration/0.9.0_Any.py	Tue Aug 20 10:49:06 2019 +0200
@@ -1,5 +1,6 @@
 add_entity_type('HistoricalEvent')
 add_entity_type('Convention')
+add_entity_type('ParallelNames')
 add_entity_type('EACFunctionRelation')
 add_entity_type('DateEntity')
 add_entity_type('PlaceEntry')
diff -r f6c054166efd -r 5ee1a2dcb628 cubicweb_eac/schema.py
--- a/cubicweb_eac/schema.py	Thu Jul 25 14:36:52 2019 +0200
+++ b/cubicweb_eac/schema.py	Tue Aug 20 10:49:06 2019 +0200
@@ -79,17 +79,38 @@
                           vocabulary=[_('authorized'), _('alternative')])
 
 
+class ParallelNames(EntityType):
+    authorized_form = String(fulltextindexed=True)
+    alternative_form = String(fulltextindexed=True)
+
+
+class simple_name_relation(RelationDefinition):
+    subject = 'ParallelNames'
+    object = 'NameEntry'
+    cardinality = '*?'
+    composite = 'subject'
+    fulltext_container = 'subject'
+
+
+class parallel_names_of(RelationDefinition):
+    subject = 'ParallelNames'
+    object = 'AuthorityRecord'
+    cardinality = '1*'
+    composite = 'object'
+    fulltext_container = 'object'
+
+
 class name_entry_for(RelationDefinition):
     subject = 'NameEntry'
     object = 'AuthorityRecord'
-    cardinality = '1+'
+    cardinality = '?+'
     composite = 'object'
     fulltext_container = 'object'
     inlined = True
 
 
 class date_relation(RelationDefinition):
-    subject = ('HierarchicalRelation', 'HistoricalEvent',
+    subject = ('ParallelNames', 'HierarchicalRelation', 'HistoricalEvent',
                'ChronologicalRelation', 'AssociationRelation',
                'AgentFunction', 'EACFunctionRelation', 'LegalStatus',
                'Mandate', 'Occupation', 'AgentPlace', 'EACResourceRelation')
diff -r f6c054166efd -r 5ee1a2dcb628 test/data/FRAD033_EAC_00001_simplified.xml
--- a/test/data/FRAD033_EAC_00001_simplified.xml	Thu Jul 25 14:36:52 2019 +0200
+++ b/test/data/FRAD033_EAC_00001_simplified.xml	Tue Aug 20 10:49:06 2019 +0200
@@ -119,7 +119,51 @@
           <alternativeForm>conventionDeclaration</alternativeForm>
 
       </nameEntry>
+      <nameEntryParallel>
+	<nameEntry lang="fr" scriptCode="Latn">
+	  <part>Institut international des droits de
+	  l'homme
+	  </part>
+	  <preferredForm>AFNOR_Z44-060
+	  </preferredForm>
+	</nameEntry>
+	<nameEntry lang="en" scriptCode="Latn">
+	  <part>International institute of human
+	  rights
+	  </part>
+	</nameEntry>
+	<authorizedForm>AFNOR_Z44-060
+	</authorizedForm>
+      </nameEntryParallel>
+      <nameEntryParallel>
+	<nameEntry localType="authorized">
+	  <part lang="eng"
+		localType="corpname">Federal Chancellery
+	  of Germany
+	  </part>
+	</nameEntry>
+	<nameEntry localType="authorized">
+	  <part lang="fre"
+		localType="corpname">Chancellerie fédérale
+	  d'Allemagne
+	  </part>
+	</nameEntry>
+	<nameEntry localType="abbreviation">
+	  <part lang="ger"
+		localType="corpname">BK
+	  </part>
+	</nameEntry>
 
+	<useDates>
+	  <dateRange localType="open">
+	    <fromDate
+		standardDate="1949">1949
+	    </fromDate>
+	    <toDate>open
+	    </toDate>
+	  </dateRange>
+	  </useDates>	  
+	</nameEntryParallel>
     </identity>
 
     <description>
diff -r f6c054166efd -r 5ee1a2dcb628 test/test_dataimport.py
--- a/test/test_dataimport.py	Thu Jul 25 14:36:52 2019 +0200
+++ b/test/test_dataimport.py	Tue Aug 20 10:49:06 2019 +0200
@@ -84,7 +84,7 @@
         return importer.external_entities()
 
     def test_parse_FRAD033_EAC_00001(self):
-        _gen_extid = map(str, (x for x in count() if x not in (2, 28))).next
+        _gen_extid = map(str, (x for x in count() if x not in (2, 36))).next
         expected = [
             ('AuthorityRecord', 'FRAD033_EAC_00001',
              {'isni': set([u'22330001300016']),
@@ -189,6 +189,47 @@
               'name_entry_for': set(['FRAD033_EAC_00001']),
               },
              ),
+            ('ParallelNames', _gen_extid(),
+             {'parallel_names_of': set(['FRAD033_EAC_00001']),
+              'simple_name_relation': set(['15', '14']),
+              'authorized_form': set([u'AFNOR_Z44-060\n\t'])
+              },
+             ),
+            ('NameEntry', _gen_extid(),
+             {'script_code': set([u'Latn']),
+              'preferred_form': [u'AFNOR_Z44-060\n\t  '],
+              'parts': set([u"Institut international des droits de\n\t  l'homme\n\t  "]),
+              'language': set([u'fr'])
+              },
+             ),
+            ('NameEntry', _gen_extid(),
+             {'script_code': set([u'Latn']),
+              'parts': set([u'International institute of human\n\t  rights\n\t  ']),
+              'language': set([u'en'])
+              },
+             ),
+            ('ParallelNames', _gen_extid(),
+             {'parallel_names_of': set(['FRAD033_EAC_00001']),
+              'date_relation': set(['17']),
+              'simple_name_relation': set(['19', '18', '20'])
+              },
+             ),
+            ('DateEntity', _gen_extid(),
+             {'start_date': set([datetime.date(1949, 1, 1)])
+              },
+             ),
+            ('NameEntry', _gen_extid(),
+             {'parts': [u'Federal Chancellery\n\t  of Germany\n\t  ']
+              },
+             ),
+            ('NameEntry', _gen_extid(),
+             {'parts': [u"Chancellerie f\xe9d\xe9rale\n\t  d'Allemagne\n\t  "]
+              },
+             ),
+            ('NameEntry', _gen_extid(),
+             {'parts': set([u'BK\n\t  '])
+              },
+             ),
             ('PostalAddress', _gen_extid(),
              {'street': set([u'1 Esplanade Charles de Gaulle']),
               'postalcode': set([u'33074']),
@@ -198,8 +239,8 @@
             ('AgentPlace', _gen_extid(),
              {'role': set([u'siege']),
               'place_agent': set(['FRAD033_EAC_00001']),
-              'place_entry_relation': set(['15']),
-              'place_address': set(['13']),
+              'place_entry_relation': set(['23']),
+              'place_address': set(['21']),
               },
              ),
             ('PlaceEntry', _gen_extid(),
@@ -209,7 +250,7 @@
              ),
             ('AgentPlace', _gen_extid(),
              {'place_agent': set(['FRAD033_EAC_00001']),
-              'place_entry_relation': set(['17']),
+              'place_entry_relation': set(['25']),
               'role': set([u'domicile']),
               },
              ),
@@ -223,7 +264,7 @@
             ('AgentPlace', _gen_extid(),
              {'place_agent': set(['FRAD033_EAC_00001']),
               'role': set([u'dodo']),
-              'place_entry_relation': set(['19']),
+              'place_entry_relation': set(['27']),
               },
              ),
             ('PlaceEntry', _gen_extid(),
@@ -232,7 +273,7 @@
              ),
             ('LegalStatus', _gen_extid(),
              {'term': set([u'Collectivité territoriale']),
-              'date_relation': set(['21']),
+              'date_relation': set(['29']),
               'description': set([u'Description du statut']),
               'description_format': set([u'text/plain']),
               'legal_status_agent': set(['FRAD033_EAC_00001']),
@@ -252,6 +293,8 @@
              ),
             ('History', _gen_extid(),
              {'abstract': set([u'Test of an abstract element']),
+              'has_citation': set(['33', '32']),
+              'has_event': set(['34', '35']),
               'text': set(["\n".join((
                   u'<p xmlns="urn:isbn:1-931666-33-4" '
                   u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
@@ -272,8 +315,6 @@
                             u'      (1931)\n\t    </li>\n\t  </ul>\n      ']),
               'items_format': set([u'text/html']),
               'history_agent': set(['FRAD033_EAC_00001']),
-              'has_citation': set(['24', '25']),
-              'has_event': set(['26', '27']),
               },
              ),
             ('Citation', _gen_extid(),
@@ -334,11 +375,11 @@
              ),
             ('Occupation', _gen_extid(),
              {'term': set([u'Réunioniste']),
-              'date_relation': set(['34']),
+              'date_relation': set(['42']),
               'description': set([u'Organisation des réunions ...']),
               'description_format': set([u'text/plain']),
               'occupation_agent': set(['FRAD033_EAC_00001']),
-              'has_citation': set(['35']),
+              'has_citation': set(['43']),
               'equivalent_concept': set(['http://pifgadget.com']),
               },
              ),
@@ -356,7 +397,7 @@
                               u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
                               u'xmlns:xlink="http://www.w3.org/1999/xlink">very famous</p>']),
               'content_format': set([u'text/html']),
-              'has_citation': set(['37']),
+              'has_citation': set(['45']),
               'general_context_of': set(['FRAD033_EAC_00001']),
               }
              ),
@@ -372,7 +413,7 @@
             ('HierarchicalRelation', _gen_extid(),
              {'entry': set([u"Gironde. Conseil général. Direction de l'administration et de "
                             u"la sécurité juridique"]),
-              'date_relation': set(['39']),
+              'date_relation': set(['47']),
               'description': set([u'Coucou']),
               'description_format': set([u'text/plain']),
               'hierarchical_parent': set(['CG33-DIRADSJ']),
@@ -397,7 +438,7 @@
             ('ChronologicalRelation', _gen_extid(),
              {'chronological_predecessor': set(['whatever']),
               'chronological_successor': set(['FRAD033_EAC_00001']),
-              'date_relation': set(['41']),
+              'date_relation': set(['49']),
               'entry': set([u'CG32']),
               },
              ),
@@ -409,7 +450,7 @@
             ('ChronologicalRelation', _gen_extid(),
              {'chronological_predecessor': set(['FRAD033_EAC_00001']),
               'chronological_successor': set(['/dev/null']),
-              'date_relation': set(['43']),
+              'date_relation': set(['51']),
               'xml_wrap': set(['<gloups xmlns="urn:isbn:1-931666-33-4"'
                                u' xmlns:xsi="http://www.w3.org/2001/XML'
                                u'Schema-instance" xmlns:xlink="http://'
@@ -428,7 +469,7 @@
              ),
             ('EACResourceRelation', _gen_extid(),
              {'agent_role': set([u'creatorOf']),
-              'date_relation': set(['46']),
+              'date_relation': set(['54']),
               'xml_attributes': set([u'{"{http://www.w3.org/1999/xlink}actuate": "onRequest", '
                                      u'"{http://www.w3.org/1999/xlink}show": "new", '
                                      u'"{http://www.w3.org/1999/xlink}type": "simple"}']),
@@ -490,7 +531,7 @@
                                   'and abolishment of schools.\n\t  </p>']),
               'r_type': set([u'controls']),
               'description_format': set([u'text/html']),
-              'date_relation': set(['49']),
+              'date_relation': set(['57']),
               'relation_entry': set([u'Establishment and abolishment\n\tof schools\n\t']),
               'xml_attributes': set([u'{}'])
               },
@@ -511,7 +552,7 @@
               'description_format': set([u'text/html']),
               'relation_entry': set([u'Some relation entry\n          ']),
               'xml_attributes': set([u'{}']),
-              'date_relation': ['51'],
+              'date_relation': ['59'],
               },
              ),
             ('DateEntity', _gen_extid(),
@@ -530,7 +571,7 @@
               'description_format': set([u'text/html']),
               'relation_entry': set([u'Some relation entry\n          ']),
               'xml_attributes': set([u'{}']),
-              'date_relation': ['53'],
+              'date_relation': ['61'],
               },
              ),
             ('DateEntity', _gen_extid(),
@@ -596,8 +637,8 @@
                           'languageDeclaration': set([21]),
                           'localControl': set([54]),
                           'source': set([76]),  # empty.
-                          'structureOrGenealogy': set([189]),  # empty.
-                          'biogHist': set([249, 252]),  # empty.
+                          'structureOrGenealogy': set([233]),  # empty.
+                          'biogHist': set([293, 296]),  # empty.
                           })
 
     def check_order_entities(self, entities, expected):
@@ -741,7 +782,7 @@
                                cwuri=u'http://data.culture.fr/thesaurus/page/ark:/67717/T1-1074')
             cnx.commit()
             created, updated = testutils.eac_import(cnx, fpath)
-            self.assertEqual(len(created), 63)
+            self.assertEqual(len(created), 71)
             self.assertEqual(updated, set())
             rset = cnx.find('AuthorityRecord', isni=u'22330001300016')
             self.assertEqual(len(rset), 1)
@@ -766,7 +807,8 @@
             self.assertEqual(len(record.reverse_function_agent), 3)
             for related in ('structure', 'history', 'mandate', 'occupation',
                             'generalcontext', 'legal_status', 'eac_relations',
-                            'equivalent_concept', 'control', 'convention'):
+                            'equivalent_concept', 'control', 'convention',
+                            'parallel_relations'):
                 with self.subTest(related=related):
                     checker = getattr(self, '_check_' + related)
                     checker(cnx, record)
@@ -889,6 +931,18 @@
         self.assertEqual(func_relation.function_relation_function[0].uri,
                          u'FRAD033_IR_N')
 
+    def _check_parallel_relations(self, cnx, record):
+        rset = cnx.find('ParallelNames', parallel_names_of=record).sorted_rset(lambda x: x.eid)
+        self.assertEqual(len(rset), 2)
+        p_entity = rset.get_entity(0, 0)
+        self.assertEqual(p_entity.parallel_names_of[0], record)
+        self.assertEqual(len(p_entity.simple_name_relation), 2)
+        self.assertEqual(len(p_entity.date_relation), 0)
+        p_entity = rset.get_entity(1, 0)
+        self.assertEqual(p_entity.parallel_names_of[0], record)
+        self.assertEqual(len(p_entity.simple_name_relation), 3)
+        self.assertEqual(len(p_entity.date_relation), 1)
+
     def _check_equivalent_concept(self, cnx, record):
         functions = dict((f.name, f) for f in record.reverse_function_agent)
         self.assertEqual(functions['action sociale'].equivalent_concept[0].cwuri,
diff -r f6c054166efd -r 5ee1a2dcb628 test/test_schema.py
--- a/test/test_schema.py	Thu Jul 25 14:36:52 2019 +0200
+++ b/test/test_schema.py	Tue Aug 20 10:49:06 2019 +0200
@@ -105,7 +105,8 @@
             ])},
             'DateEntity': {('date_relation', 'object'): set([
                 'HistoricalEvent', 'AgentFunction', 'EACFunctionRelation',
-                'LegalStatus', 'Mandate', 'Occupation', 'AgentPlace', 'EACResourceRelation'
+                'LegalStatus', 'Mandate', 'Occupation', 'AgentPlace', 'EACResourceRelation',
+                'ParallelNames'
             ])},
             'PlaceEntry': {('place_entry_relation', 'object'): set([
                 'HistoricalEvent', 'AgentFunction', 'EACFunctionRelation',
@@ -124,8 +125,10 @@
             'History': {('history_agent', 'subject'): set(['AuthorityRecord'])},
             'LegalStatus': {('legal_status_agent', 'subject'): set(['AuthorityRecord'])},
             'Mandate': {('mandate_agent', 'subject'): set(['AuthorityRecord'])},
-            'NameEntry': {('name_entry_for', 'subject'): set(['AuthorityRecord'])},
+            'NameEntry': {('name_entry_for', 'subject'): set(['AuthorityRecord']),
+                          ('simple_name_relation', 'object'): set(['ParallelNames'])},
             'Occupation': {('occupation_agent', 'subject'): set(['AuthorityRecord'])},
+            'ParallelNames': {('parallel_names_of', 'subject'): set(['AuthorityRecord'])},
             'PostalAddress': {('place_address', 'object'): set(['AgentPlace'])},
             'Structure': {('structure_agent', 'subject'): set(['AuthorityRecord'])},
         }
@@ -138,7 +141,8 @@
         graph = AuthorityRecordGraph(self.schema)
         structure = graph.parent_structure('AuthorityRecord')
         opts = optional_relations(self.schema, structure)
-        expected = {}
+        expected = {'NameEntry': set([('name_entry_for', 'subject'),
+                                      ('simple_name_relation', 'object')])}
         self.assertEqual(opts, expected)
 
     def test_relations_consistency(self):


More information about the saem-devel mailing list