--- ckanclient-0.9.orig/ckanclient/__init__.py +++ ckanclient-0.9/ckanclient/__init__.py @@ -13,7 +13,9 @@ import ckanclient # Instantiate the CKAN client. - ckan = ckanclient.CkanClient(api_key=my_key) + ckan = ckanclient.CkanClient(base_location='http://thedatahub.org/api', + api_key='adbc-1c0d') + # (use your own api_key from http://thedatahub.org/user/me ) # Get the package list. package_list = ckan.package_register_get() @@ -72,6 +74,14 @@ Changelog ========= +vXX +--------------- + + * Action API functions + * GET methods now send API key too + * Removed changeset functions + + v0.9 2011-08-09 --------------- @@ -161,7 +171,13 @@ try: # since python 2.6 import json except ImportError: - import simplejson as json + try: + import simplejson as json + except ImportError: + class _json(object): + def __getattr__(self, name): + import simplejson as json + json = _json() import logging logger = logging.getLogger('ckanclient') @@ -172,6 +188,7 @@ class CkanApiNotFoundError(CkanApiError): pass class CkanApiNotAuthorizedError(CkanApiError): pass class CkanApiConflictError(CkanApiError): pass +class CkanApiActionError(Exception): pass class ApiRequest(Request): @@ -202,6 +219,9 @@ self.last_message = None self.last_http_error = None self.last_url_error = None + self.last_help = None # Action API only + self.last_result = None # Action API only + self.last_ckan_error = None # Action API only def open_url(self, location, data=None, headers={}, method=None): if self.is_verbose: @@ -267,6 +287,9 @@ path += '/' + entity2_id return base + path + def get_action_location(self, action_name): + return '%s/action/%s' % (self.base_location, action_name) + def _dumpstr(self, data): return json.dumps(data) @@ -348,6 +371,26 @@ raise CkanApiError(self.last_message) return result + def open_action_url(self, url, data_dict): + data_json = self._dumpstr(data_dict) + result = super(CkanClient, self).open_url(url, data=data_json) + if self.last_status not in (200, 201): + if self.last_status == 404: + raise CkanApiNotFoundError(self.last_status) + elif self.last_status == 403: + raise CkanApiNotAuthorizedError(self.last_status) + elif self.last_status == 409: + raise CkanApiConflictError(self.last_status) + else: + raise CkanApiError(self.last_message) + self.last_help = self.last_message['help'] + if self.last_message['success']: + self.last_result = self.last_message['result'] + else: + self.last_ckan_error = self.last_message['error'] + raise CkanApiActionError(self.last_ckan_error) + return self.last_result + def api_version_get(self): self.reset() url = self.get_location('Base') @@ -363,7 +406,8 @@ def package_register_get(self): self.reset() url = self.get_location('Package Register') - self.open_url(url) + headers = self._auth_headers() + self.open_url(url, headers=headers) return self.last_message def package_register_post(self, package_dict): @@ -450,13 +494,15 @@ def tag_register_get(self): self.reset() url = self.get_location('Tag Register') - self.open_url(url) + headers = self._auth_headers() + self.open_url(url, headers=headers) return self.last_message def tag_entity_get(self, tag_name): self.reset() url = self.get_location('Tag Entity', tag_name) - self.open_url(url) + headers = self._auth_headers() + self.open_url(url, headers=headers) return self.last_message def group_register_post(self, group_dict): @@ -470,13 +516,15 @@ def group_register_get(self): self.reset() url = self.get_location('Group Register') - self.open_url(url) + headers = self._auth_headers() + self.open_url(url, headers=headers) return self.last_message def group_entity_get(self, group_name): self.reset() url = self.get_location('Group Entity', group_name) - self.open_url(url) + headers = self._auth_headers() + self.open_url(url, headers=headers) return self.last_message def group_entity_put(self, group_dict, group_name=None): @@ -536,7 +584,8 @@ def package_create_form_get(self): self.reset() url = self.get_location('Package Create Form') - self.open_url(url) + headers = self._auth_headers() + self.open_url(url, headers=headers) return self.last_message def package_create_form_post(self, form_submission): @@ -550,7 +599,8 @@ def package_edit_form_get(self, package_ref): self.reset() url = self.get_location('Package Edit Form', package_ref) - self.open_url(url) + headers = self._auth_headers() + self.open_url(url, headers=headers) return self.last_message def package_edit_form_post(self, package_ref, form_submission): @@ -562,22 +612,6 @@ return self.last_message # - # Changeset API - # - - def changeset_register_get(self): - self.reset() - url = self.get_location('Changeset Register') - self.open_url(url) - return self.last_message - - def changeset_entity_get(self, changeset_name): - self.reset() - url = self.get_location('Changeset Entity', changeset_name) - self.open_url(url) - return self.last_message - - # # data API # def _storage_metadata_url(self, path): @@ -589,17 +623,20 @@ return url def storage_metadata_get(self, path): url = self._storage_metadata_url(path) - self.open_url(url) + headers = self._auth_headers() + self.open_url(url, headers=headers) return self._loadstr(self.last_message) def storage_metadata_set(self, path, metadata): url = self._storage_metadata_url(path) payload = self._dumpstr(metadata) - self.open_url(url, payload, method="PUT") + headers = self._auth_headers() + self.open_url(url, payload, headers=headers, method="PUT") return self._loadstr(self.last_message) def storage_metadata_update(self, path, metadata): url = self._storage_metadata_url(path) payload = self._dumpstr(metadata) - self.open_url(url, payload, method="POST") + headers = self._auth_headers() + self.open_url(url, payload, headers=headers, method="POST") return self._loadstr(self.last_message) def _storage_auth_url(self, path): @@ -612,11 +649,36 @@ def storage_auth_get(self, path, headers): url = self._storage_auth_url(path) payload = self._dumpstr(headers) - self.open_url(url, payload, method="POST") + headers = self._auth_headers() + self.open_url(url, payload, headers=headers, method="POST") return self._loadstr(self.last_message) + + # + # Action API + # + + # for any action + def action(self, action_name, **kwargs): + self.reset() + url = self.get_action_location(action_name) + self.open_action_url(url, kwargs) + return self.last_result + + def package_list(self): + return self.action('package_list') + + def package_show(self, package_id): + return self.action('package_show', id=package_id) + + def status_show(self): + return self.action('status_show') + + def ckan_version(self): + return self.action('status_show')['ckan_version'] + # - # Utils + # CkanClient utils # def is_id(self, id_string): '''Tells the client if the string looks like an id or not''' --- ckanclient-0.9.orig/ckanclient/contrib/resources_csv_dump.py +++ ckanclient-0.9/ckanclient/contrib/resources_csv_dump.py @@ -0,0 +1,55 @@ +''' +Create a CSV dump of a CKAN instance, resource by resource. + +Compare with the 'paster db simple-dump-csv' command which is one line per dataset (although it requires access to the CKAN machine). + +Author: Friedrich +''' +import argparse +import ckanclient +import csv + +parser = argparse.ArgumentParser(description= + 'Create a CSV dump of a CKAN instance, resource by resource.') +parser.add_argument('url', metavar='API_URL', type=str, + help='CKAN API endpoint') +parser.add_argument('outfile', metavar='OUTPUT_FILE', type=str, + help='output CSV file name') + +def main(): + args = parser.parse_args() + client = ckanclient.CkanClient(args.url) + rows = [] + for pkg_name in client.package_register_get(): + pkg = client.package_entity_get(pkg_name) + for extra, value in pkg.get('extras', {}).items(): + pkg['extras_' + extra] = value + if 'extras' in pkg: + del pkg['extras'] + resources = pkg.get('resources', []) + for resource in resources: + rpkg = pkg.copy() + for resprop, value in resource.items(): + rpkg['resource_' + resprop] = value + rows.append(rpkg) + if not len(resources): + rows.append(pkg) + del pkg['resources'] + print pkg_name + headers = set() + for row in rows: + headers.update(row.keys()) + fh = open(args.outfile, 'wb') + writer = csv.DictWriter(fh, headers) + writer.writerow(dict(zip(headers, headers))) + for row in rows: + row_ = {} + for column, value in row.items(): + if isinstance(value, unicode): + value = value.encode('utf-8') + row_[column] = value + writer.writerow(row_) + fh.close() + +if __name__ == '__main__': + main() --- ckanclient-0.9.orig/ckanclient/tests/test_ckanclient.py +++ ckanclient-0.9/ckanclient/tests/test_ckanclient.py @@ -359,3 +359,17 @@ assert set(david['packages']) == set((u'annakarenina', u'warandpeace')), david roger = self.c.group_entity_get('roger') assert roger['packages'] == [u'annakarenina'], roger + + def test_17_package_list(self): + res = self.c.package_list() + status = self.c.last_status + assert status == 200 + assert_equal(res, self.c.last_result) + message = self.c.last_message + assert set(message.keys()) >= set(('help', 'success', 'result')), message + assert 'annakarenina' in res, res + + def test_18_ckan_version(self): + res = self.c.ckan_version() + assert res[0] in '12345' + --- ckanclient-0.9.orig/debian/changelog +++ ckanclient-0.9/debian/changelog @@ -0,0 +1,13 @@ +ckanclient (0.9-1.1) unstable; urgency=medium + + * Non-maintainer upload. + * Replace python-support with dh-python in Build-Depends (Closes: #785979). + * Drop obsolete debian/pycompat. + + -- Andrey Rahmatullin Tue, 25 Aug 2015 22:47:13 +0500 + +ckanclient (0.9-1) unstable; urgency=low + + * Initial release + + -- J. Félix Ontañón Thu, 26 Jan 2012 20:20:34 +0100 --- ckanclient-0.9.orig/debian/compat +++ ckanclient-0.9/debian/compat @@ -0,0 +1 @@ +7 --- ckanclient-0.9.orig/debian/control +++ ckanclient-0.9/debian/control @@ -0,0 +1,22 @@ +Source: ckanclient +Section: python +Priority: optional +Maintainer: J. Félix Ontañón +Build-Depends: + debhelper (>= 7), + python-all-dev, + dh-python, + python-setuptools +Standards-Version: 3.9.2 +Homepage: http://thedatahub.org/dataset/ckanclient +Vcs-Git: http://github.com/okfn/ckanclient + +Package: python-ckanclient +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, + python-simplejson, python-gdata +Suggests: python-nose +Description: CKAN client Python package + The CKAN client software may be used to make requests on the Comprehensive + Knowledge Archive Network (CKAN) API including its REST interface to all + primary objects (packages, groups, tags) and its search interface. --- ckanclient-0.9.orig/debian/copyright +++ ckanclient-0.9/debian/copyright @@ -0,0 +1,25 @@ +This package was debianized by J. Félix Ontañón on +Thu, 26 Jan 2012 20:48:25 +0100. + +It was downloaded from http://github.com/okfn/ckanclient + +Copyright (c) 2005-2012, Open Knowledge Foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + --- ckanclient-0.9.orig/debian/docs +++ ckanclient-0.9/debian/docs @@ -0,0 +1,2 @@ +README.txt + --- ckanclient-0.9.orig/debian/rules +++ ckanclient-0.9/debian/rules @@ -0,0 +1,7 @@ +#!/usr/bin/make -f + +#export DH_VERBOSE=1 +#export DH_OPTIONS=-v + +%: + dh $@ --with python2 --- ckanclient-0.9.orig/debian/watch +++ ckanclient-0.9/debian/watch @@ -0,0 +1,2 @@ +version=3 +http://pypi.python.org/packages/source/c/ckanclient/ckanclient-([0-9a-z.]+)\.tar.gz --- ckanclient-0.9.orig/setup.py +++ ckanclient-0.9/setup.py @@ -9,20 +9,25 @@ import os +install_requires = [] +try: + __import__('json') +except ImportError: + # The json module isn't available in the standard library until 2.6; + # use simplejson instead, + install_requires.append('simplejson') + setup( name='ckanclient', version=__version__, - author='Appropriate Software Foundation, Open Knowledge Foundation', + author='Open Knowledge Foundation', author_email='info@okfn.org', license=__license__, url='http://www.okfn.org/ckan/', description=__description__, keywords='data packaging component tool client', long_description =__long_description__, - install_requires=[ - # only required if python <= 2.5 (as json library in python >= 2.6) - # 'simplejson', - ], + install_requires=install_requires, packages=find_packages(exclude=['ez_setup']), include_package_data=True, always_unzip=True,