Support cloning of SEDA entities graph
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 18 Oct 2016 17:07:02 +0200
changeset 1896 46987d1729b3
parent 1895 1d3ccf2d7108
child 1907 3eaf32ca3e95
Support cloning of SEDA entities graph
entities/__init__.py
hooks.py
schema/__init__.py
site_cubicweb.py
test/test_entities.py
--- a/entities/__init__.py	Thu Oct 13 12:54:26 2016 +0200
+++ b/entities/__init__.py	Tue Oct 18 17:07:02 2016 +0200
@@ -19,7 +19,7 @@
 
 from logilab.common.registry import objectify_predicate
 
-from cubes.compound.entities import IContainer, IContained
+from cubes.compound.entities import IContainer, IContained, IClonableAdapter
 
 from cubes.seda import seda_profile_container_def
 from cubes.seda.xsd import XSDMMapping
@@ -130,8 +130,47 @@
         return container and container[0] or None
 
 
+class SEDAArchiveUnitIClonableAdapter(IClonableAdapter):
+    """Cloning adapter for SEDA components."""
+    rtype = 'clone_of'
+    skiprtypes = ()
+
+    def clone_into(self, clone):
+        """Recursivily clone the container graph of this entity into `clone`."""
+        if clone.seda_archive_unit and (
+                clone.seda_archive_unit[0].cw_etype == 'SEDAArchiveTransfer'
+                or clone.seda_archive_unit[0].container[0].cw_etype == 'SEDAArchiveTransfer'):
+            # clone is parented to a transfer profile, we need to propery handle binary/physical
+            # data objects
+            data_objects = self._cw.execute(
+                'Any X WHERE X is IN (SEDABinaryDataObject, SEDAPhysicalDataObject),'
+                ' X container %(c)s', {'c': self.entity.eid})
+        else:
+            data_objects = None
+        clones = super(SEDAArchiveUnitIClonableAdapter, self).clone_into(clone)
+        if data_objects is not None:
+            if clone.seda_archive_unit[0].cw_etype == 'SEDAArchiveTransfer':
+                transfer = clone.seda_archive_unit[0]
+            else:
+                transfer = clone.seda_archive_unit[0].container[0]
+            for data_object in data_objects.entities():
+                rtype = {
+                    'SEDABinaryDataObject': 'seda_binary_data_object',
+                    'SEDAPhysicalDataObject': 'seda_physical_data_object',
+                }[data_object.cw_etype]
+                clones[data_object].cw_set(**{rtype: transfer})
+
+
 def registration_callback(vreg):
+    vreg.register(SEDAArchiveUnitIClonableAdapter)
     vreg.register(IContainer.build_class('SEDAArchiveTransfer'))
     vreg.register(IContainer.build_class('SEDAArchiveUnit'))  # archive unit may also be a container
     for etype, parent_relations in sorted(seda_profile_container_def(vreg.schema)):
-        DirectLinkIContained.register_class(vreg, etype, parent_relations)
+        cls = DirectLinkIContained.build_class(etype, parent_relations)
+        assert cls
+        vreg.register(cls)
+        if etype in ('SEDABinaryDataObject', 'SEDAPhysicalDataObject'):
+            # control parent relation order
+            cls.parent_relations = list(cls.parent_relations)
+            if cls.parent_relations[0][0] == 'seda_data_object_reference_id':
+                cls.parent_relations = cls.parent_relations[::-1]
--- a/hooks.py	Thu Oct 13 12:54:26 2016 +0200
+++ b/hooks.py	Tue Oct 18 17:07:02 2016 +0200
@@ -73,6 +73,7 @@
     __regid__ = 'seda.graph.updated'
     __select__ = hook.Hook.__select__ & hook.match_rtype_sets(SEDA_PARENT_RTYPES)
     events = ('before_add_relation',)
+    category = 'metadata'
 
     def __call__(self):
         if SEDA_PARENT_RTYPES[self.rtype] == 'subject':
--- a/schema/__init__.py	Thu Oct 13 12:54:26 2016 +0200
+++ b/schema/__init__.py	Tue Oct 18 17:07:02 2016 +0200
@@ -83,6 +83,13 @@
     inlined = False
 
 
+class clone_of(RelationDefinition):
+    subject = 'SEDAArchiveUnit'
+    object = 'SEDAArchiveUnit'
+    cardinality = '?*'
+    inlined = True
+
+
 class title(RelationDefinition):
     subject = 'SEDAArchiveTransfer'
     object = 'String'
@@ -126,6 +133,11 @@
         name = String(required=True, fulltextindexed=True)
 
 
+from cubes.seda.schema.seda2 import data_object_reference_data_object_reference_id  # noqa
+
+data_object_reference_data_object_reference_id.composite = 'subject'
+
+
 def post_build_callback(schema):
     from cubes.seda import seda_profile_container_def, iter_all_rdefs
 
--- a/site_cubicweb.py	Thu Oct 13 12:54:26 2016 +0200
+++ b/site_cubicweb.py	Tue Oct 18 17:07:02 2016 +0200
@@ -23,7 +23,9 @@
 import traceback
 
 from logilab.common import registry
+
 from cubicweb import rtags
+from cubicweb.entity import Entity
 
 
 @monkeypatch(registry.RegistrableInstance, methodname='__new__')
@@ -47,6 +49,9 @@
     self._tagdefs = {}
 
 
+Entity.cw_skip_copy_for.append(('container', 'subject'))
+
+
 # auto-configuration of custom fields ##############################################################
 # (https://www.cubicweb.org/ticket/14474840)
 # other part in views/patches
--- a/test/test_entities.py	Thu Oct 13 12:54:26 2016 +0200
+++ b/test/test_entities.py	Tue Oct 18 17:07:02 2016 +0200
@@ -69,7 +69,7 @@
                 entity.cw_clear_all_caches()
                 self.assertEqual(entity.cw_adapt_to('IContained').container.eid, transfer.eid)
 
-    def test_archive_unit_container(self):
+    def test_archive_unit_container_clone(self):
         """Functional test for SEDA component clone."""
         with self.admin_access.repo_cnx() as cnx:
             unit, unit_alt, unit_alt_seq = create_archive_unit(None, cnx=cnx)
@@ -86,6 +86,36 @@
             bdo.cw_clear_all_caches()
             self.assertEqual(bdo.container[0].eid, unit.eid)
 
+            # test clone without reparenting
+            cloned = cnx.create_entity(unit.cw_etype, id=u'hop', user_annotation=u'clone',
+                                       clone_of=unit)
+            cnx.commit()
+            cloned.cw_clear_all_caches()
+            self.assertEqual(cloned.id, 'hop')
+            self.assertEqual(cloned.user_annotation, 'clone')
+            cloned_unit_alt_seq = cloned.first_level_choice.content_sequence
+            self.assertEqual(cloned_unit_alt_seq.container[0].eid, cloned.eid)
+            cloned_bdo = (cloned_unit_alt_seq.reverse_seda_data_object_reference[0]
+                          .seda_data_object_reference_id[0])
+            self.assertEqual(cloned_bdo.container[0].eid, cloned.eid)
+
+            # test clone with reparenting
+            transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'test profile')
+            cloned = cnx.create_entity(unit.cw_etype, id=u'hop',
+                                       clone_of=unit,
+                                       seda_archive_unit=transfer)
+            cnx.commit()
+            cloned.cw_clear_all_caches()
+            self.assertEqual(cloned.container[0].eid, transfer.eid)
+            cloned.cw_clear_all_caches()
+            cloned_unit_alt_seq = cloned.first_level_choice.content_sequence
+            self.assertEqual(cloned_unit_alt_seq.container[0].eid, transfer.eid)
+            cloned_bdo = (cloned_unit_alt_seq.reverse_seda_data_object_reference[0]
+                          .seda_data_object_reference_id[0])
+            self.assertEqual(cloned_bdo.container[0].eid, transfer.eid)
+            cloned_bdo.cw_clear_all_caches()
+            self.assertEqual(cloned_bdo.seda_binary_data_object[0].eid, transfer.eid)
+
 
 class FakeEntity(object):
     cw_etype = 'Whatever'