[PATCH 5 of 5 saem_ref] [ark] Implement "IARKGenerator" adapters for qualified entity types

Denis Laxalde denis.laxalde at logilab.fr
Wed Feb 14 17:26:12 CET 2018


# HG changeset patch
# User Denis Laxalde <denis.laxalde at logilab.fr>
# Date 1518624926 -3600
#      Wed Feb 14 17:15:26 2018 +0100
# Node ID 4299910e12d33e96236bc103f20cf91d71d398c4
# Parent  b19f5bb5f1a93835b5522d586da88868de31c287
# Available At http://hg.logilab.org/review/cubes/saem_ref
#              hg pull http://hg.logilab.org/review/cubes/saem_ref -r 4299910e12d3
# EXP-Topic ark/qualifiers
[ark] Implement "IARKGenerator" adapters for qualified entity types

We (re-)introduce several implementations of IARKGenerator adapter
depending on whether we want a simple ARK identifier or if we want a
*qualified* ARK identifier built from another (parent) identifier.
Respective adapters are selected from a keyword argument matching the
relation through which the *parent* entity can be retrieved. This is
used to obtain the base *name* of the ARK identifier to generate (only
the *qualifier* part will be generated). Passing of relation information
is done by modifying how adapter is selected in
hooks.set_ark_and_cwuri().

We add a couple of tests for qualified ARK identifiers generations for
sub-entities in unittest_entities.py (concept and organization unit).

Notice the change in test_entities_rdf.py where we now let ARK
identifiers be completely generated in order to fulfil parent/child
constraints in ARK identifiers between organization and organization
unit.

Related to extranet #46880051.

diff --git a/cubicweb_saem_ref/entities/__init__.py b/cubicweb_saem_ref/entities/__init__.py
--- a/cubicweb_saem_ref/entities/__init__.py
+++ b/cubicweb_saem_ref/entities/__init__.py
@@ -27,6 +27,7 @@ from cubicweb.entities import AnyEntity,
 ARK_PREFIX = 'rf'
 ARK_CONTROLCHAR = 'g'
 ARK_NAME_LENGTH = 10
+ARK_QUALIFIER_LENGTH = 10
 
 ARK_RGX = re.compile(
     r'^(ark:/)?(?P<naan>\d+)/(?P<name>{prefix}\w{{{length}}}{controlchar})$'.format(
@@ -37,8 +38,7 @@ ARK_RGX = re.compile(
 )
 
 
-class ARKIdentifierGenerator(Adapter):
-    """Adapter for ARK unique identifier generation"""
+class ARKIdentifierGeneratorMixin(object):
     __regid__ = 'IARKGenerator'
     __select__ = match_kwargs('naa_what')
 
@@ -46,6 +46,10 @@ class ARKIdentifierGenerator(Adapter):
         """Return a new ARK identifier as unicode"""
         return u'{0}/{1}'.format(self.cw_extra_kwargs['naa_what'], self.assign_name())
 
+
+class ARKIdentifierGenerator(ARKIdentifierGeneratorMixin, Adapter):
+    """Adapter for ARK unique identifier generation"""
+
     def assign_name(self):
         """Assign and return a new name part of the ARK identifier"""
         cu = self._cw.system_sql(
@@ -56,6 +60,68 @@ class ARKIdentifierGenerator(Adapter):
         return ark_name
 
 
+class QualifiedARKIdentifierGenerator(ARKIdentifierGeneratorMixin, Adapter):
+    """Adapter for ARK identifier generation using a qualifier from an
+    existing "base" ARK identifier.
+    """
+    __abstract__ = True
+
+    @property
+    def parent_entity(self):
+        raise NotImplementedError
+
+    def assign_name(self):
+        match = ARK_RGX.match(self.parent_entity.ark)
+        if not match:
+            raise ValueError(
+                "ARK identifier for parent entity #%d looks malformattted: %s"
+                % (self.parent_entity.eid, self.parent_entity.ark)
+            )
+        # Sanity check to make sure we're not producing ARK identifiers with
+        # different NAAN for entities that are supposed to be parent of each
+        # others.
+        if int(match.group('naan')) != int(self.cw_extra_kwargs['naa_what']):
+            raise ValueError(
+                "NAAN of parent entity #%d does not match the value of 'naa_what' parameter: %s"
+                % (self.parent_entity.eid, self.cw_extra_kwargs['naa_what'])
+            )
+        name = match.group('name')
+        cu = self._cw.system_sql(
+            'select * from gen_qualified_ark(%s, %s);',
+            (name, ARK_QUALIFIER_LENGTH, ),
+        )
+        ark_qualifier, = cu.fetchone()
+        return u'/'.join([name, ark_qualifier])
+
+
+class ConceptARKIdentifierGenerator(QualifiedARKIdentifierGenerator):
+    __select__ = (
+        ARKIdentifierGeneratorMixin.__select__
+        & match_kwargs('in_scheme')
+    )
+
+    @property
+    def parent_entity(self):
+        scheme = self.cw_extra_kwargs['in_scheme']
+        if hasattr(scheme, 'eid'):
+            return scheme
+        return self._cw.entity_from_eid(scheme)
+
+
+class OUARKIdentifierGenerator(QualifiedARKIdentifierGenerator):
+    __select__ = (
+        ARKIdentifierGeneratorMixin.__select__
+        & match_kwargs('authority')
+    )
+
+    @property
+    def parent_entity(self):
+        authority = self.cw_extra_kwargs['authority']
+        if hasattr(authority, 'eid'):
+            return authority
+        return self._cw.entity_from_eid(authority)
+
+
 class ArkNAALocator(EntityAdapter):
     """Adapter responsible to retrieve the proper ARK Name Assigning Authority depending on the
     entity type
diff --git a/cubicweb_saem_ref/hooks.py b/cubicweb_saem_ref/hooks.py
--- a/cubicweb_saem_ref/hooks.py
+++ b/cubicweb_saem_ref/hooks.py
@@ -222,8 +222,9 @@ def set_ark_and_cwuri(cw, eid, entity_at
                 msg = _('an ARK identifier has to be generated but no Name Assigning Authority is '
                         'specified')
                 raise ValidationError(None, {None: msg})
-            generator = cw.vreg['adapters'].select('IARKGenerator', cw,
-                                                   naa_what=naa_what)
+            generator = cw.vreg['adapters'].select(
+                'IARKGenerator', cw,
+                naa_what=naa_what, **entity_attrs)
             ark = generator.generate_ark()
         entity_attrs['ark'] = ark
     if 'cwuri' not in entity_attrs:
diff --git a/test/test_entities_rdf.py b/test/test_entities_rdf.py
--- a/test/test_entities_rdf.py
+++ b/test/test_entities_rdf.py
@@ -112,7 +112,9 @@ class RDFExportTC(testutils.XmlTestMixin
         self.reg.register_prefix('saem', 'http://www.logilab.org/saem/0#')
         with self.admin_access.client_cnx() as cnx:
             other_org = cnx.create_entity(
-                'Organization', name=u'Friends of default authority', ark=u'123')
+                'Organization', name=u'Friends of default authority',
+                ark_naa=testutils.naa(cnx),
+            )
             org = testutils.authority_with_naa(cnx)
             ou = testutils.organization_unit(cnx, u'archivists',
                                              archival_roles=[u'archival'],
diff --git a/test/unittest_entities.py b/test/unittest_entities.py
--- a/test/unittest_entities.py
+++ b/test/unittest_entities.py
@@ -19,7 +19,10 @@ from unittest import TestCase
 from cubicweb.devtools import PostgresApptestConfiguration
 from cubicweb.devtools.testlib import CubicWebTC
 
-from cubicweb_saem_ref.entities import ARK_RGX
+from cubicweb_saem_ref.entities import (
+    ARK_RGX,
+    ARK_QUALIFIER_LENGTH,
+)
 import testutils
 
 
@@ -56,6 +59,33 @@ class ArkGeneratorTC(CubicWebTC):
         name = ark[len(what) + 1:]
         self.assertTrue(testutils.match_ark_name(name), name)
 
+    def _check_ark_matches_parent(self, ark, parent_ark):
+        self.assertTrue(ark.startswith(parent_ark + '/'), ark)
+        qualifier = ark[len(parent_ark) + 1:]
+        self.assertEqual(len(qualifier), ARK_QUALIFIER_LENGTH)
+
+    def test_concept(self):
+        with self.admin_access.cnx() as cnx:
+            scheme = testutils.setup_scheme(cnx, u'test')
+            cnx.commit()
+            scheme_ark = scheme.ark
+            what = str(scheme.ark_naa[0].what)
+            generator = self.vreg['adapters'].select(
+                'IARKGenerator', cnx, naa_what=what, in_scheme=scheme)
+            ark = generator.generate_ark()
+            self._check_ark_matches_parent(ark, scheme_ark)
+
+    def test_organizationunit(self):
+        with self.admin_access.cnx() as cnx:
+            authority = testutils.authority_with_naa(cnx)
+            cnx.commit()
+            authority_ark = authority.ark
+            what = str(authority.ark_naa[0].what)
+            generator = self.vreg['adapters'].select(
+                'IARKGenerator', cnx, naa_what=what, authority=authority)
+            ark = generator.generate_ark()
+            self._check_ark_matches_parent(ark, authority_ark)
+
 
 if __name__ == '__main__':
     import unittest



More information about the saem-devel mailing list