[hooks,entities] Handle resources for CKAN dataset
authorDenis Laxalde <denis.laxalde@logilab.fr>
Mon, 20 Oct 2014 16:06:38 +0200
changeset 13 133dda956327
parent 12 183145b6bee7
child 14 50e1c7ac3e59
[hooks,entities] Handle resources for CKAN dataset Closes #4502967.
entities.py
hooks.py
test/data/bootstrap_cubes
test/data/entities.py
test/data/hooks.py
test/data/schema.py
test/unittest_hooks.py
--- a/entities.py	Mon Oct 20 16:05:36 2014 +0200
+++ b/entities.py	Mon Oct 20 16:06:38 2014 +0200
@@ -113,3 +113,25 @@
         dataset-like entity.
         """
         return None
+
+    def dataset_resources(self):
+        """May return a list of entities adaptable as IDownloadable to be set
+        as resources of the CKAN dataset.
+        """
+        return []
+
+    def ckan_resources(self):
+        """Yield dicts of CKAN dataset resource info"""
+        for entity in self.dataset_resources():
+            adapted = entity.cw_adapt_to('IDownloadable')
+            if adapted is None:
+                self.warning(
+                    'invalid resource %r, could not adapt to IDownloadable',
+                    entity)
+                continue
+            yield {'url': adapted.download_url(),
+                   'name': adapted.download_file_name(),
+                   'mimetype': adapted.download_content_type(),
+                   'created': str(entity.creation_date),
+                   'last_modified': str(entity.modification_date),
+                  }
--- a/hooks.py	Mon Oct 20 16:05:36 2014 +0200
+++ b/hooks.py	Mon Oct 20 16:06:38 2014 +0200
@@ -54,6 +54,23 @@
     _ckan_action(config, eid, 'package_delete', data={'id': datasetid})
 
 
+def add_dataset_resource(config, eid, datasetid, resource_data):
+    """Add a resource to an existing CKAN dataset"""
+    resource_data['package_id'] = datasetid
+    return _ckan_action(config, eid, 'resource_create', data=resource_data)
+
+
+def delete_dataset_resources(config, eid, datasetid):
+    """Delete resources of a CKAN dataset"""
+    res = _ckan_action(config, eid, 'package_show', data={'id': datasetid})
+    resources = res['resources']
+    deleted = set([])
+    for resource in resources:
+        _ckan_action(config, eid, 'resource_delete', {'id': resource['id']})
+        deleted.add(resource['id'])
+    return deleted
+
+
 class DeleteCKANDataSetHook(hook.Hook):
     """Delete CKAN dataset upon deletion of the corresponding entity"""
     __regid__ = 'ckanpublish.delete-ckan-dataset'
@@ -86,10 +103,13 @@
             datasetid = entity.ckan_dataset_id
             config = self.cnx.vreg.config
             if self.cnx.deleted_in_transaction(eid):
+                deleted = delete_dataset_resources(config, eid, datasetid)
+                self.info('deleted CKAN resources %s', ', '.join(deleted))
                 delete_dataset(config, eid, datasetid)
                 self.info('deleted CKAN dataset %s', datasetid)
             else:
-                data = entity.cw_adapt_to('ICKANPublishable').ckan_data()
+                cpublish = entity.cw_adapt_to('ICKANPublishable')
+                data = cpublish.ckan_data()
                 if datasetid is not None:
                     update_dataset(config, eid, datasetid, data)
                     self.info('updated %s fields in CKAN dataset %s',
@@ -100,3 +120,8 @@
                         'SET X ckan_dataset_id %(dsid)s WHERE X eid %(eid)s',
                         {'eid': eid, 'dsid': datasetid})
                     self.info('created CKAN dataset %s', datasetid)
+                for resource_data in cpublish.ckan_resources():
+                    resource_id = add_dataset_resource(config, eid, datasetid,
+                                                       resource_data)
+                    self.info('add resource %s to CKAN dataset %s' %
+                              (resource_id, datasetid))
--- a/test/data/bootstrap_cubes	Mon Oct 20 16:05:36 2014 +0200
+++ b/test/data/bootstrap_cubes	Mon Oct 20 16:06:38 2014 +0200
@@ -1,1 +1,1 @@
-ckanpublish
+ckanpublish, file
--- a/test/data/entities.py	Mon Oct 20 16:05:36 2014 +0200
+++ b/test/data/entities.py	Mon Oct 20 16:06:38 2014 +0200
@@ -9,3 +9,6 @@
     def dataset_maintainer(self):
         if self.entity.maintainer:
             return self.entity.maintainer[0]
+
+    def dataset_resources(self):
+        return self.entity.resources
--- a/test/data/hooks.py	Mon Oct 20 16:05:36 2014 +0200
+++ b/test/data/hooks.py	Mon Oct 20 16:06:38 2014 +0200
@@ -11,3 +11,13 @@
 
     def __call__(self):
         CKANDatasetOp.get_instance(self._cw).add_data(self.eidfrom)
+
+
+class AddDeleteFileResourceHook(hook.Hook):
+    __regid__ = 'ckanpublish-tests.add-delete-file-resource'
+    __select__ = (hook.Hook.__select__ &
+                  hook.match_rtype('resources', frometypes=('CWDataSet')))
+    events = ('after_add_relation', 'after_delete_relation')
+
+    def __call__(self):
+        CKANDatasetOp.get_instance(self._cw).add_data(self.eidfrom)
--- a/test/data/schema.py	Mon Oct 20 16:05:36 2014 +0200
+++ b/test/data/schema.py	Mon Oct 20 16:06:38 2014 +0200
@@ -10,3 +10,4 @@
                          'update': ()},
         )
     maintainer = SubjectRelation('CWUser', cardinality='?*')
+    resources = SubjectRelation('File', cardinality='*?', composite='subject')
--- a/test/unittest_hooks.py	Mon Oct 20 16:05:36 2014 +0200
+++ b/test/unittest_hooks.py	Mon Oct 20 16:06:38 2014 +0200
@@ -1,5 +1,6 @@
 """cubicweb-ckanpublish unit tests for hooks"""
 
+from cubicweb import Binary
 from cubicweb.devtools.testlib import CubicWebTC
 
 from cubes.ckanpublish.utils import ckan_post, CKANPostError
@@ -42,6 +43,7 @@
             cnx.commit()
             yield self._check_entity_create, cnx, entity
             yield self._check_entity_update, cnx, entity
+            yield self._check_entity_resources, cnx, entity
             yield self._check_entity_delete, cnx, entity
 
     def _check_entity_create(self, cnx, entity):
@@ -73,14 +75,35 @@
         self.assertEqual(result['maintainer'], 'T. Oto')
         self.assertEqual(result['maintainer_email'], 'to@t.o')
 
+    def _check_entity_resources(self, cnx, entity):
+        self.set_description('entity resources')
+        resource = cnx.create_entity('File', data=Binary('yui'),
+                                     data_format=u'text/plain',
+                                     data_name=u'blurp',
+                                     reverse_resources=entity)
+        cnx.commit()
+        result = ckan_post(self.ckan_config, 'package_show',
+                           {'id': entity.ckan_dataset_id})
+        resources = result['resources']
+        self.assertEqual(len(resources), 1)
+        r0 = resources[0]
+        iresource = resource.cw_adapt_to('IDownloadable')
+        self.assertEqual(r0['url'], iresource.download_url())
+
     def _check_entity_delete(self, cnx, entity):
         self.set_description('entity deletion')
         ckanid = entity.ckan_dataset_id
+        result = ckan_post(self.ckan_config, 'package_show',
+                           {'id': ckanid})
+        resource_id = result['resources'][0]['id']
         entity.cw_delete()
         cnx.commit()
         result = ckan_post(self.ckan_config, 'package_show',
                            {'id': ckanid})
         self.assertEqual(result['state'], 'deleted')
+        result = ckan_post(self.ckan_config, 'resource_show',
+                           {'id': resource_id})
+        self.assertEqual(result['state'], 'deleted')
 
 
 if __name__ == '__main__':