hooks.py
author Denis Laxalde <denis.laxalde@logilab.fr>
Wed, 19 Nov 2014 14:45:39 +0100
changeset 14 50e1c7ac3e59
parent 13 133dda956327
child 21 616d34b27c9b
child 23 0b0329078c95
child 33 751c79a1ddae
child 40 d7f97a999d12
permissions -rw-r--r--
[hooks] Turn the error about incomplete configuration into a warning To avoid cluttering the console.

# copyright 2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr -- mailto:contact@logilab.fr
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.

"""cubicweb-ckanpublish specific hooks and operations"""

from requests.exceptions import RequestException

from cubicweb import ValidationError
from cubicweb.predicates import adaptable, score_entity
from cubicweb.server import hook

from cubes.ckanpublish.utils import (ckan_post, CKANPostError,
                                     ckan_instance_configured)


def _ckan_action(config, eid, action, data=None):
    """Run `ckan_post` and eventually raise ValidationError."""
    try:
        return ckan_post(config, action, data=data)
    except (CKANPostError, RequestException) as exc:
        raise ValidationError(eid, {'ckan_dataset_id': unicode(exc)})


def create_dataset(config, eid, data):
    """Create a CKAN dataset and set `ckan_dataset_id` attribute or
    respective entity. Return the dataset id.
    """
    res = _ckan_action(config, eid, 'package_create', data)
    return res['id']


def update_dataset(config, eid, datasetid, udata):
    """Update an existing CKAN dataset"""
    data = _ckan_action(config, eid, 'package_show', data={'id': datasetid})
    data.update(udata)
    _ckan_action(config, eid, 'package_update', data=data)


def delete_dataset(config, eid, datasetid):
    """Delete a CKAN dataset"""
    _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'
    __select__ = (hook.Hook.__select__ & ckan_instance_configured &
                  adaptable('ICKANPublishable') &
                  score_entity(lambda x: x.ckan_dataset_id))
    events = ('before_delete_entity', )

    def __call__(self):
        CKANDatasetOp.get_instance(self._cw).add_data(self.entity.eid)


class AddOrUpdateCKANDataSetHook(hook.Hook):
    """Add or update a CKAN dataset upon addition or update of an entity"""
    __regid__ = 'ckanpublish.add-update-ckan-dataset'
    __select__ = (hook.Hook.__select__ & ckan_instance_configured &
                  adaptable('ICKANPublishable'))
    events = ('after_add_entity', 'after_update_entity', )

    def __call__(self):
        CKANDatasetOp.get_instance(self._cw).add_data(self.entity.eid)


class CKANDatasetOp(hook.DataOperationMixIn, hook.Operation):
    """Operation to create, update or delete a CKAN dataset"""

    def precommit_event(self):
        for eid in self.get_data():
            entity = self.cnx.entity_from_eid(eid)
            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:
                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',
                              data.keys(), datasetid)
                else:
                    datasetid = create_dataset(config, eid, data)
                    self.cnx.execute(
                        '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))