Implement motion of nodes in the SEDA tree
authorDenis Laxalde <denis.laxalde@logilab.fr>
Fri, 21 Oct 2016 17:05:45 +0200
changeset 1924 891700ba269e
parent 1923 c3f377955898
child 1925 d62432403bed
Implement motion of nodes in the SEDA tree With some adjustments in hooks: * in SetContainerOp, avoid inserting container relation when it's already being set (by node motion); * prevent EnsureChoiceNotEmptyOp from being run with the relation is not targetting a choice. Closes #15047080.
entities/__init__.py
hooks.py
test/test_views.py
views/sedatree.py
--- a/entities/__init__.py	Fri Oct 14 13:03:06 2016 +0200
+++ b/entities/__init__.py	Fri Oct 21 17:05:45 2016 +0200
@@ -40,7 +40,7 @@
         parent = entity.cw_adapt_to('IContained').parent
         if container is None:
             # entity may be both container and contained, and in this case is a container
-            assert entity.cw_adapt_to('IContainer')
+            assert entity.cw_adapt_to('IContainer'), entity
             container = entity
     else:
         req = entity._cw
--- a/hooks.py	Fri Oct 14 13:03:06 2016 +0200
+++ b/hooks.py	Fri Oct 21 17:05:45 2016 +0200
@@ -65,8 +65,10 @@
         cursor = cnx.cnxset.cu
         for ceid, eids in contained.items():
             args = [{'x': eid} for eid in eids]
-            cursor.executemany('INSERT INTO container_relation(eid_from, eid_to) '
-                               'VALUES (%%(x)s, %s)' % ceid, args)
+            cursor.executemany('INSERT INTO container_relation(eid_from, eid_to)'
+                               ' SELECT %%(x)s, %(c)s'
+                               ' WHERE NOT EXISTS(SELECT 1 FROM container_relation'
+                               ' WHERE eid_from=%%(x)s AND eid_to=%(c)s)' % {'c': ceid}, args)
 
 
 class SetContainerHook(hook.Hook):
@@ -180,7 +182,10 @@
     def __call__(self):
         eid = {'object': self.eidto, 'subject': self.eidfrom}[CHOICE_RTYPE_ROLE[self.rtype]]
         choice = self._cw.entity_from_eid(eid)
-        EnsureChoiceNotEmptyOp.get_instance(self._cw).add_data(choice)
+        if choice.cw_etype in CHOICE_RTYPE:
+            # Else this is an ambiguous relation, which in this case isn't
+            # targetting a choice.
+            EnsureChoiceNotEmptyOp.get_instance(self._cw).add_data(choice)
 
 
 class SetDefaultCodeListVersionsHook(hook.Hook):
--- a/test/test_views.py	Fri Oct 14 13:03:06 2016 +0200
+++ b/test/test_views.py	Fri Oct 21 17:05:45 2016 +0200
@@ -466,6 +466,63 @@
             self.assertCountEqual(annotations, ('plop', 'plouf'))
 
 
+class SEDATreeTC(CubicWebTC):
+
+    def setup_database(self):
+        with self.admin_access.cnx() as cnx:
+            self.transfer_eid = cnx.create_entity('SEDAArchiveTransfer',
+                                                  title=u'Test',
+                                                  simplified_profile=True).eid
+            cnx.commit()
+
+    def test_archiveunit_reparent_to_transfer(self):
+        with self.admin_access.cnx() as cnx:
+            transfer = cnx.entity_from_eid(self.transfer_eid)
+            archunit, _, _ = create_archive_unit(transfer)
+            transfer2 = cnx.create_entity('SEDAArchiveTransfer',
+                                          title=u'Test2',
+                                          simplified_profile=True)
+            cnx.commit()
+            archunit.cw_clear_all_caches()
+            archunit.cw_adapt_to('IJQTree').reparent(transfer2.eid)
+            cnx.commit()
+            transfer2.cw_clear_all_caches()
+            self.assertEqual([x.eid for x in transfer2.reverse_seda_archive_unit],
+                             [archunit.eid])
+
+    def test_archiveunit_reparent_to_archiveunit(self):
+        with self.admin_access.cnx() as cnx:
+            transfer = cnx.entity_from_eid(self.transfer_eid)
+            archunit, _, _ = create_archive_unit(transfer)
+            archunit2, _, _ = create_archive_unit(transfer, id=u'pouet')
+            cnx.commit()
+            archunit.cw_clear_all_caches()
+            archunit.cw_adapt_to('IJQTree').reparent(archunit2.eid)
+            cnx.commit()
+            seq = archunit2.first_level_choice.content_sequence
+            seq.cw_clear_all_caches()
+            self.assertEqual([x.eid for x in seq.reverse_seda_archive_unit],
+                             [archunit.eid])
+
+    def test_binarydataobject_reparent(self):
+        with self.admin_access.cnx() as cnx:
+            transfer = cnx.entity_from_eid(self.transfer_eid)
+            archunit, _, alt_seq = create_archive_unit(transfer)
+            archunit2, _, alt_seq2 = create_archive_unit(transfer, id=u'pouet')
+            bdo = create_data_object(transfer)
+            ref = cnx.create_entity('SEDADataObjectReference',
+                                    seda_data_object_reference=alt_seq,
+                                    seda_data_object_reference_id=bdo)
+            cnx.commit()
+            bdo.cw_clear_all_caches()
+            bdo.cw_adapt_to('IJQTree').reparent(archunit2.eid)
+            cnx.commit()
+            alt_seq2.cw_clear_all_caches()
+            self.assertEqual([x.eid for x in alt_seq2.reverse_seda_data_object_reference],
+                             [ref.eid])
+            alt_seq.cw_clear_all_caches()
+            self.assertFalse(alt_seq.reverse_seda_data_object_reference)
+
 
 if __name__ == '__main__':
     unittest.main()
--- a/views/sedatree.py	Fri Oct 14 13:03:06 2016 +0200
+++ b/views/sedatree.py	Fri Oct 21 17:05:45 2016 +0200
@@ -16,9 +16,12 @@
 """cubicweb-seda views for ArchiveUnit"""
 
 from cubicweb import _
-from cubicweb.predicates import adaptable
+from cubicweb.predicates import adaptable, is_instance
 from cubicweb.web import component
 
+from cubes.seda.entities import simplified_profile
+from cubes.seda.views import jqtree
+
 
 class SEDAArchiveUnitTree(component.EntityCtxComponent):
     """Display the whole archive unit tree."""
@@ -31,3 +34,60 @@
     def render_body(self, w):
         self._cw.add_css('cubes.jqtree.css')
         self.entity.view('jqtree.treeview', w=w)
+
+
+class ArchiveTransferIJQTreeAdapter(jqtree.IJQTreeAdapter):
+    __select__ = (jqtree.IJQTreeAdapter.__select__
+                  & is_instance('SEDAArchiveTransfer') & simplified_profile())
+
+    def maybe_parent_of(self):
+        return ['SEDAArchiveUnit']
+
+
+class ArchiveUnitIJQTreeAdapter(jqtree.IJQTreeAdapter):
+    __select__ = (jqtree.IJQTreeAdapter.__select__
+                  & is_instance('SEDAArchiveUnit') & simplified_profile())
+
+    def maybe_parent_of(self):
+        return ['SEDAArchiveUnit',
+                'SEDAPhysicalDataObject', 'SEDABinaryDataObject']
+
+    def maybe_child(self):
+        return True
+
+    def reparent(self, peid):
+        parent = self._cw.entity_from_eid(peid)
+        if parent.cw_etype == 'SEDAArchiveUnit':
+            parent = parent.first_level_choice.content_sequence
+        else:
+            assert parent.cw_etype == 'SEDAArchiveTransfer', (
+                'cannot re-parent to entity type {0}'.format(parent.cw_etype))
+        rset = self._cw.execute(
+            'SET X seda_archive_unit P WHERE X eid %(x)s, P eid %(p)s',
+            {'x': self.entity.eid, 'p': parent.eid})
+        return rset.rows
+
+
+class DataObjectIJQTreeAdapter(jqtree.IJQTreeAdapter):
+    __select__ = (jqtree.IJQTreeAdapter.__select__
+                  & is_instance('SEDABinaryDataObject') & simplified_profile())
+    rtype_to_archivetransfer = 'seda_binary_data_object'
+
+    def maybe_child(self):
+        return True
+
+    def reparent(self, peid):
+        archunit = self._cw.entity_from_eid(peid)
+        parent = archunit.first_level_choice.content_sequence
+        rset = self._cw.execute(
+            'SET REF seda_data_object_reference SEQ WHERE'
+            ' REF seda_data_object_reference_id X,'
+            ' X eid %(x)s, SEQ eid %(seq)s',
+            {'x': self.entity.eid, 'seq': parent.eid})
+        return rset.rows
+
+
+class PhysicalDataObjectIJQTreeAdapter(DataObjectIJQTreeAdapter):
+    __select__ = (jqtree.IJQTreeAdapter.__select__
+                  & is_instance('SEDAPhysicalDataObject'))
+    rtype_to_archivetransfer = 'seda_physical_data_object'