diff --git a/ckanext/odsh/collection/controller.py b/ckanext/odsh/collection/controller.py index 5b049563195ed12b5a1b4f008b38fd95fc3af612..79b669afb67b03042be0c3c4b58cf5b5c1f63a0d 100644 --- a/ckanext/odsh/collection/controller.py +++ b/ckanext/odsh/collection/controller.py @@ -1,7 +1,7 @@ from ckan.lib.helpers import is_url, url_for import ckan.plugins.toolkit as toolkit from ckan.controllers.package import PackageController -from helpers import get_latest_resources_for_type, get_latest_dataset +from helpers import get_latest_resources_for_format, get_latest_dataset @@ -13,10 +13,10 @@ class LatestDatasetController(PackageController): class LatestRecourcesController(PackageController): - def latest_resource(self, id, type): - latest_resources = get_latest_resources_for_type(id, type) + def latest_resource(self, id, resource_format): + latest_resources = get_latest_resources_for_format(id, resource_format) if latest_resources is None: - abort(404) + toolkit.abort(404) url_type = latest_resources.get('url_type') if url_type is None: resource_url = latest_resources.get('url') @@ -35,4 +35,4 @@ class LatestRecourcesController(PackageController): filename=pre_resource_url, qualified = True) toolkit.redirect_to(url_resource) - abort(404) \ No newline at end of file + toolkit.abort(404) \ No newline at end of file diff --git a/ckanext/odsh/collection/helpers.py b/ckanext/odsh/collection/helpers.py index ec0f7dcbf05e7c37afa5491501e9d54f071f11d8..9c3024337c10aabcf5723f4bea75abd4adc0de08 100644 --- a/ckanext/odsh/collection/helpers.py +++ b/ckanext/odsh/collection/helpers.py @@ -1,161 +1,156 @@ from string import lower +from operator import itemgetter import ckan.lib.helpers as helpers import ckan.model as model import ckan.plugins.toolkit as toolkit +def get_collection(dataset_dict): + collection_id = get_collection_id(dataset_dict) + if collection_id: + return get_collection_info(collection_id, dataset_dict) + + return None + + +def get_collection_info(collection_id, dataset_dict=None): + collection_dict = get_package_dict(collection_id) + dataset_names = get_dataset_names(collection_dict) + datasets_in_collection = get_datasets_from_solr(dataset_names) + collection_info = gather_collection_info(collection_dict, datasets_in_collection, dataset_dict) + return collection_info + + +def get_collection_id(dataset_dict): + relationships_dataset = dataset_dict.get('relationships_as_subject') + if relationships_dataset and len(relationships_dataset): + return relationships_dataset[0]['__extras']['object_package_id'] + relationships_dataset = dataset_dict.get('relationships') + if relationships_dataset and len(relationships_dataset): + return relationships_dataset[0].get('object') + return None -#routine functions def get_package_dict(name): return model.Package.get(name).as_dict() -def get_relationships(name): - collection_dict = get_package_dict(name) - return collection_dict.get('relationships') -def get_all_datasets_belonging_to_collection(collection_name): - list_rel_collection = get_relationships(collection_name) - name_list = list() - for item in list_rel_collection: - item_object = item.get('object') - name_list.append(item_object) - return name_list +def get_dataset_names(collection_dict): + collection_dict = get_package_dict(collection_dict.get('id')) # needed to get full package_dict + relationships_collection = collection_dict.get('relationships') + names_collection_members = [relationship.get('object') for relationship in relationships_collection] + return names_collection_members -#for mapping latest resources and latest dataset -def get_latest_dataset(collection_name): - collection_list_relationships = get_all_datasets_belonging_to_collection(collection_name) - latest_issued = latest_name = None - for item in collection_list_relationships: - item_pkt_dict = get_package_dict(item) - if helpers.check_access('package_show', item_pkt_dict): - item_issued = item_pkt_dict.get('extras').get('issued') - if latest_issued < item_issued or (latest_issued == item_issued and latest_name < item): - latest_name=item - latest_issued=item_issued - return latest_name +def get_datasets_from_solr(dataset_names): + context = None + name_expression = ' OR '.join(dataset_names) + fq = 'name:({})'.format(name_expression) + + sort = 'name asc,extras_issued asc' + + # maximum possible number of results is 1000, + # see https://docs.ckan.org/en/ckan-2.7.3/api/index.html#ckan.logic.action.get.package_search + query_result = toolkit.get_action('package_search')(context, { + 'fq': fq, + 'sort': sort, + 'rows': 1000, + }) -def is_latest_resources(resource_format, type, resource_created,latest_created, resource_id, latest_id): - if lower(resource_format) == lower(type): - return (resource_created > latest_created or (resource_created == latest_created and resource_id > latest_id)) - else: - return False - -def get_latest_resources_for_type(collection_name, type): - latest_dataset_name = get_latest_dataset(collection_name) - latest_dataset = get_package_dict(latest_dataset_name) - resource_list = latest_dataset.get('resources') - latest_resource = latest_created = latest_id = None - for resource in resource_list: - resource_format = resource.get('format') - resource_created = resource.get('created') - resource_id = resource.get('id') - if is_latest_resources(resource_format, type, resource_created, latest_created, resource_id, latest_id): - latest_id=resource_id - latest_created=resource_created - latest_resource=resource - return latest_resource - -#for predecessor and successor - - - -def get_collection_name_by_dataset(dataset_name): - list_rel_dataset = get_relationships(dataset_name) - if len(list_rel_dataset): - return list_rel_dataset[0]['object'] - -def get_collection_title_by_dataset(pkg_dict_dataset): - dataset_name = pkg_dict_dataset.get('name') - collection_name = get_collection_name_by_dataset(dataset_name) - if not collection_name: - return None - context = None - pkg_dict_collection = toolkit.get_action('package_show')(context, {'id': collection_name}) - if not pkg_dict_collection: - return None - title_collection = pkg_dict_collection.get('title') - return title_collection - -def get_all_datasets_belonging_to_collection_by_dataset(dataset_name): - collection_name = get_collection_name_by_dataset(dataset_name) - if collection_name: - return get_all_datasets_belonging_to_collection(collection_name) - return [] - -def _get_siblings_dicts_with_access(pkg_dict): - dataset_name = pkg_dict.get('name') - list_of_siblings = get_all_datasets_belonging_to_collection_by_dataset(dataset_name) - n_siblings = len(list_of_siblings) - if n_siblings>0: - siblings_dicts = [get_package_dict(name) for name in list_of_siblings] - user_has_access = lambda pkg_dict:helpers.check_access('package_show', pkg_dict) - siblings_dicts_with_access = filter(user_has_access, siblings_dicts) - return siblings_dicts_with_access - return None + results = query_result.get('results') + datasets_found = results if results else [] + + return datasets_found -def _sort_siblings_by_name_and_date(siblings_dicts): - ''' - sort by name first and then by date to have a fallback if dates are the same - ''' - _get_name = lambda pkg_dict:pkg_dict.get('name') - _get_issued = lambda pkg_dict:pkg_dict.get('extras').get('issued') - siblings_dicts_sorted_by_name = sorted(siblings_dicts, key=_get_name) - siblings_dicts_sorted_by_date_issued = sorted(siblings_dicts_sorted_by_name, key=_get_issued) - return siblings_dicts_sorted_by_date_issued - -def get_successor_and_predecessor_dataset(pkg_dict): - dataset_name = pkg_dict.get('name') - siblings_dicts_with_access = _get_siblings_dicts_with_access(pkg_dict) - if siblings_dicts_with_access: - n_siblings = len(siblings_dicts_with_access) - siblings_dicts_sorted_by_date_issued = _sort_siblings_by_name_and_date(siblings_dicts_with_access) - siblings_names_sorted_by_date_issued = [d['name'] for d in siblings_dicts_sorted_by_date_issued] - id_current_dataset = siblings_names_sorted_by_date_issued.index(dataset_name) - predecessor_name = ( - siblings_names_sorted_by_date_issued[id_current_dataset-1] if (id_current_dataset > 0) - else None - ) - successor_name = ( - siblings_names_sorted_by_date_issued[id_current_dataset+1] if (id_current_dataset < n_siblings-1) - else None - ) - else: - predecessor_name, successor_name = None, None - return successor_name, predecessor_name - -def get_successor_and_predecessor_urls(pkg_dict): - successor_name, predecessor_name = get_successor_and_predecessor_dataset(pkg_dict) - successor_url, predecessor_url = ( - helpers.url_for(controller='package', action='read', id=name) - if name is not None - else None - for name in (successor_name, predecessor_name) - ) - return successor_url, predecessor_url -def get_successor(pkg_dict): - successor_and_predecessor = get_successor_and_predecessor_urls(pkg_dict) - return successor_and_predecessor[0] +def gather_collection_info(collection_dict, datasets_in_collection, dataset_dict=None): + name_first_dataset = datasets_in_collection[0].get('name') + url_first_dataset = url_from_id(name_first_dataset) -def get_predecessor(pkg_dict): - successor_and_predecessor = get_successor_and_predecessor_urls(pkg_dict) - return successor_and_predecessor[1] - -#link to latest collection member -def latest_collection_member_persistent_link(pkg_dict): - dataset_name = pkg_dict.get('name') - collection_name = get_collection_name_by_dataset( - dataset_name=dataset_name - ) - if not collection_name: - return None - url = helpers.url_for( + name_last_dataset = datasets_in_collection[-1].get('name') + url_last_dataset = url_from_id(name_last_dataset) + + name_collection = collection_dict.get('name') + persistent_link_last_member = url_last_member(name_collection) + + if dataset_dict: + name_current_dataset = dataset_dict.get('name') + dataset_names = [d.get('name') for d in datasets_in_collection] + + def get_predecessor(): + try: + id_current = dataset_names.index(name_current_dataset) + except ValueError: + return None + if id_current > 0: + return dataset_names[id_current - 1] + return None + + def get_successor(): + try: + id_current = dataset_names.index(name_current_dataset) + except ValueError: + return None + if id_current < len(dataset_names) - 1: + return dataset_names[id_current + 1] + return None + + name_predecessor = get_predecessor() + url_predecessor = url_from_id(name_predecessor) if name_predecessor else None + + name_successor = get_successor() + url_successor = url_from_id(name_successor) if name_successor else None + else: + url_predecessor = url_successor = None + + return { + 'title': collection_dict.get('title'), + 'members': datasets_in_collection, + 'first_member': { + 'name': name_first_dataset, + 'url': url_first_dataset, + }, + 'last_member': { + 'name': name_last_dataset, + 'url': url_last_dataset, + }, + 'predecessor': { + 'url': url_predecessor, + }, + 'successor': { + 'url': url_successor, + }, + 'persistent_link_last_member': persistent_link_last_member, + } + +def url_from_id(package_id): + return helpers.url_for(controller='package', action='read', id=package_id) + +def url_last_member(name_collection): + return helpers.url_for( controller='ckanext.odsh.collection.controller:LatestDatasetController', action='latest', - id=collection_name + id=name_collection ) - return url \ No newline at end of file + + +def get_latest_dataset(collection_name): + collection_info = get_collection_info(collection_name) + latest_name = collection_info['last_member']['name'] + return latest_name + + +def get_latest_resources_for_format(collection_name, resource_format): + collection_info = get_collection_info(collection_name) + members = collection_info.get('members') + if not members: + return None + latest_dataset = members[-1] + resources = latest_dataset.get('resources') + if not resources: + return None + resources_with_asked_type = [r for r in resources if r.get('format').upper() == resource_format.upper()] + resources_sorted = sorted(resources_with_asked_type, key=itemgetter('id','created'), reverse=True) + return resources_sorted[-1] diff --git a/ckanext/odsh/collection/plugin.py b/ckanext/odsh/collection/plugin.py index 2ebcefc1af2c164045af7e861856858644885df9..27a28eccf53d86da24b79a31732bf10c37dc8ff5 100644 --- a/ckanext/odsh/collection/plugin.py +++ b/ckanext/odsh/collection/plugin.py @@ -1,6 +1,7 @@ from ckan.lib.plugins import DefaultTranslation, DefaultDatasetForm -import ckan.plugins as plugins +import ckan.plugins as plugins +import ckan.plugins.toolkit as toolkit import helpers as collection_helpers from routes.mapper import SubMapper @@ -16,6 +17,9 @@ class CollectionsPlugin(plugins.SingletonPlugin, DefaultDatasetForm): def is_fallback(self): return False + + def read_template(self): + return 'package/collection_read.html' # IRoutes @@ -28,7 +32,7 @@ class CollectionsPlugin(plugins.SingletonPlugin, DefaultDatasetForm): ) map.connect( - '/collection/{id}/aktuell.{type}', + '/collection/{id}/aktuell.{resource_format}', controller='ckanext.odsh.collection.controller:LatestRecourcesController', action='latest_resource' ) @@ -39,7 +43,7 @@ class CollectionsPlugin(plugins.SingletonPlugin, DefaultDatasetForm): path_prefix='/collection/' ) as m: m.connect('latest', '{id}/aktuell', action='latest') - m.connect('latest_resource', '{id}/aktuell.{type}', action='latest_resource') + m.connect('latest_resource', '{id}/aktuell.{resource_format}', action='latest_resource') return map def after_map(self, map): @@ -53,8 +57,8 @@ class CollectionsPlugin(plugins.SingletonPlugin, DefaultDatasetForm): # other extensions. return { - 'collection_get_successor': collection_helpers.get_successor, - 'collection_get_predecessor': collection_helpers.get_predecessor, - 'collection_get_latest_member':collection_helpers.latest_collection_member_persistent_link, - 'collection_get_title': collection_helpers.get_collection_title_by_dataset, + 'get_collection': collection_helpers.get_collection, + 'get_collection_info': collection_helpers.get_collection_info, + 'url_from_id': collection_helpers.url_from_id, } + \ No newline at end of file diff --git a/ckanext/odsh/controller.py b/ckanext/odsh/controller.py index 20dbbd1b770a82310889a7adf8ce246535de73ee..daf5d9efba32a5dbb132ca4877dcee7fa1464d01 100644 --- a/ckanext/odsh/controller.py +++ b/ckanext/odsh/controller.py @@ -147,7 +147,7 @@ class OdshApiController(ApiController): if toolkit.asbool(config.get('ckanext.odsh.log_api_requests', 'false')): try: request_data = self._get_request_data(try_url_params=False) - log.info('POST request body: {}'.format(json.dumps(json.loads(request_data)))) + log.info('POST request body: {}'.format(request_data)) except Exception, e: log.error(e) if logic_function == 'resource_qv4yAI2rgotamXGk98gJ': diff --git a/ckanext/odsh/helper_pkg_dict.py b/ckanext/odsh/helper_pkg_dict.py index 912b0b83db873b0ed58b98e64ef21b83c3a59e4b..487a8ff5c89933829f2711203c08171df1a31f4a 100644 --- a/ckanext/odsh/helper_pkg_dict.py +++ b/ckanext/odsh/helper_pkg_dict.py @@ -102,18 +102,6 @@ class HelperPgkDict(object): return None - def get_collection_id(self): - ''' - construct a collection uri from the id of - the containing collection - ''' - package_name = self.pkg_dict.get('name') - collection_name = helpers_collection.get_collection_name_by_dataset(package_name) - collection_dict = helpers_collection.get_package_dict(collection_name) - collection_id = collection_dict.get('id') - return collection_id - - def add_uri_to_store(self): ''' store pair of uri and id for subsequent use diff --git a/ckanext/odsh/helpers_tpsh.py b/ckanext/odsh/helpers_tpsh.py index 608c91016c353ca9de29934c3b06a0c52dd1c4d8..927313ed03fa9623f92f4f7e2018ed425c918891 100644 --- a/ckanext/odsh/helpers_tpsh.py +++ b/ckanext/odsh/helpers_tpsh.py @@ -8,6 +8,8 @@ import json import re import urllib2 from collections import OrderedDict +import subprocess +import os from ckan.common import config import ckan.lib.helpers as helpers @@ -170,7 +172,11 @@ def get_language_for_selection(): return dict_for_select_box def get_package_dict(name): - return model.Package.get(name).as_dict() + ''' + raises ckan.logic.NotFound if not found + ''' + package_dict = toolkit.get_action('package_show')(None, {'id': name}) + return package_dict def size_of_fmt(num, suffix='B'): for unit in ['',' k',' M',' G',' T',' P',' E',' Z']: @@ -208,4 +214,12 @@ def get_body_mail(organization, package): mail_document = "Dokument-ID: " + package_name + "%0D%0A" mail_url = "URL: " + url + "%0D%0A" + "%0D%0A" message = mail_titel + mail_document + mail_url + "Mein Kommentar:" + "%0D%0A" + "%0D%0A" + "%0D%0A" + "%0D%0A" - return anrede + message \ No newline at end of file + return anrede + message + +def git_commit_hash(): + current_dir = os.path.dirname(os.path.abspath(__file__)) + try: + command = 'git log -n 1 --format=%H' + except: + return 'unknown' + return subprocess.check_output([command], shell=True, cwd=current_dir) \ No newline at end of file diff --git a/ckanext/odsh/logic/action.py b/ckanext/odsh/logic/action.py index 8bca8dd62c81372c2059a43152b46cf29fd9414d..0b4402dbcc986b67f8c91964d4c15820785c0172 100644 --- a/ckanext/odsh/logic/action.py +++ b/ckanext/odsh/logic/action.py @@ -15,6 +15,9 @@ import logging log = logging.getLogger(__name__) from ckanext.odsh.setup_proxy import setup_proxy, clear_proxy +from ckanext.odsh.collection.helpers import get_collection_id, get_package_dict +from ckanext.odsh.helpers_tpsh import add_pkg_to_collection +from ckanext.odsh.helpers import odsh_extract_value_from_extras def odsh_package_create(context, data_dict): @@ -22,15 +25,14 @@ def odsh_package_create(context, data_dict): if pkg_type == 'collection': return package_create(context, data_dict) munge_increment_name(data_dict) + related_package_name = data_dict.get('relatedPackage') + if related_package_name: + new_package = package_create(context, data_dict) + add_to_same_collection(related_package_name, new_package['name']) + return new_package if pkg_type != 'dataset': return package_create(context, data_dict) - issued = False - for extra in data_dict.get('extras'): - if extra['key'] == 'issued': - issued = True - break - if not issued: - data_dict['extras'].append({'key': 'issued', 'value': datetime.datetime.utcnow().isoformat()}) + add_issued_if_missing(data_dict) return package_create(context, data_dict) @@ -47,6 +49,43 @@ def munge_increment_name(data_dict): log.debug('name: %s' % name) data_dict['name'] = name + +def add_issued_if_missing(data_dict): + issued = odsh_extract_value_from_extras(data_dict.get('extras'), 'issued') + if issued is None: + data_dict['extras'].append({'key': 'issued', 'value': datetime.datetime.utcnow().isoformat()}) + + +def add_to_same_collection(related_package_name, package_name): + related_package = get_package_dict(related_package_name) + collection_id = get_collection_id(related_package) + if not collection_id: + collection_dict = auto_create_collection(related_package) + collection_id = collection_dict.get('id') + add_to_collection(collection_id, related_package_name) + add_to_collection(collection_id, package_name) + + +def auto_create_collection(related_package): + related_package_title = related_package.get('title') + owner_org = related_package.get('owner_org') + _collection_dict = { + 'title': related_package_title, + 'owner_org': owner_org, + 'type': 'collection', + } + munge_increment_name(_collection_dict) + collection_dict = package_create_via_toolkit(None, _collection_dict) + return collection_dict + + +def package_create_via_toolkit(context, package_dict): + return toolkit.get_action('package_create')(context, package_dict) + + +def add_to_collection(collection_id, package_name): + add_pkg_to_collection(package_name, collection_id) + def check_password(password): return (len(password) >= 8 and diff --git a/ckanext/odsh/pdf_to_thumbnail/action.py b/ckanext/odsh/pdf_to_thumbnail/action.py index bd5d713086311721f593ba276ea9d650f1e77f87..d77845bea62b3b0c99e3afeaeab0d7bccd8b6394 100644 --- a/ckanext/odsh/pdf_to_thumbnail/action.py +++ b/ckanext/odsh/pdf_to_thumbnail/action.py @@ -4,8 +4,7 @@ import ckan.lib.helpers as helpers from ckan.logic.action.update import package_update from ckan.logic.action.delete import package_delete -#from thumbnail -import thumbnail as thumbnail +import thumbnail def before_package_delete(context, package_id_dict): @@ -23,7 +22,7 @@ def before_package_update(context, pkg_dict): old_filename = package.get('thumbnail') if old_filename: if str(old_private) != str(new_private): - new_filename = thumbnail.change_filepath(old_filename) + new_filename = thumbnail.rename_thumbnail_to_random_name(old_filename) pkg_dict['extras'].append({'key': 'thumbnail', 'value': new_filename}) elif not pkg_dict.get('thumbnail'): pkg_dict['extras'].append({'key': 'thumbnail', 'value': old_filename}) diff --git a/ckanext/odsh/pdf_to_thumbnail/plugin.py b/ckanext/odsh/pdf_to_thumbnail/plugin.py index 715235990b6515a9b9d86a2a4f063c8da5e2bf7e..ecd4fdefda6bdd1054ba6e90efd9910ae9b66c7b 100644 --- a/ckanext/odsh/pdf_to_thumbnail/plugin.py +++ b/ckanext/odsh/pdf_to_thumbnail/plugin.py @@ -1,6 +1,5 @@ import os - #from ckan import ckan.plugins as plugins @@ -21,15 +20,16 @@ class ThumbnailPlugin(plugins.SingletonPlugin): #IResourceController - def after_create(self, context, resource): - _, filename = thumbnail.create_thumbnail(context, resource) - thumbnail.write_thumbnail_into_package(context, resource, filename) + def after_create(self, context, resource): + resources = thumbnail.resources_of_containing_package(resource) + thumbnail.create_thumbnail_if_none_in_package(context, resources) def after_update(self, context, resource): - thumbnail.check_and_create_thumbnail_after_update(context, resource) + resources = thumbnail.resources_of_containing_package(resource) + thumbnail.create_thumbnail_if_none_in_package(context, resources) def after_delete(self, context, resources): - thumbnail.create_thumbnail_for_last_resource(context, resources) + thumbnail.create_thumbnail_if_none_in_package(context, resources) #IConfigurer diff --git a/ckanext/odsh/pdf_to_thumbnail/thumbnail.py b/ckanext/odsh/pdf_to_thumbnail/thumbnail.py index ea0f06b32a7d8eba85a6ca9a486ebf1b7bf009b6..7a39dea04a486e5c8d3ee820a854ac0e67883c68 100644 --- a/ckanext/odsh/pdf_to_thumbnail/thumbnail.py +++ b/ckanext/odsh/pdf_to_thumbnail/thumbnail.py @@ -7,171 +7,221 @@ from ckan.common import config import urllib2 import requests -import binascii +from binascii import b2a_hex import ckan.plugins.toolkit as toolkit import ckan.logic as logic #from extension #from ckanext.odsh.lib.uploader import raise_validation_error_if_virus_found log = logging.getLogger(__name__) - -def get_filename_from_context(context): + +def create_thumbnail(context, resource): + ''' + main entry point into this module + this function is called from pdf_to_thumbnail.plugin + ''' + old_filename = _get_filename_from_context(context) + url_type = resource.get('url_type') + if url_type == 'upload': + is_PDF, filename = _create_thumbnail_from_memory(resource, old_filename) + else: + is_PDF, filename = (False, None) + return is_PDF, filename + + +def _get_filename_from_context(context): package = context.get('package') package_id = package.id - package= toolkit.get_action('package_show')(context, {'id': package_id}) + package= toolkit.get_action('package_show')(None, {'id': package_id}) thumbnail = package.get('thumbnail') return thumbnail -def get_filepath_for_thumbnail(filename): - if filename: - return config.get('ckan.storage_path') + "/thumbnail/" + filename - return config.get('ckan.storage_path') + "/thumbnail/" -def concatenate_filename(filename): - return filename + ".jpg" +def _create_thumbnail_from_memory(resource, old_filename): + filepath = get_resource_path(resource) + is_PDF = _is_pdf(filepath) + if is_PDF: + with open(filepath, 'rb') as file: + new_filename = _create_thumbnail_from_file(file) + if old_filename: + ThumbnailPath.from_filename(old_filename).remove() + return is_PDF, new_filename + else: + return is_PDF, None + -def get_filepath_to_resource(resource): +def get_resource_path(resource): + # see https://stackoverflow.com/questions/46572402/where-does-ckan-store-the-files-pushed-to-datastore-filestore resource_id = resource.get('id') - directory = config.get('ckan.storage_path') + '/resources/' - #looked up how resources are saved, by locating the keyword resources in the OS - path = directory + resource_id[0:3] + '/' + resource_id[3:6] + '/' + resource_id[6:] - return path - -def random_filename(): - number = binascii.b2a_hex(os.urandom(15)) - filename = 'thumbnail_picture_' + str(number) - full_filename = concatenate_filename(filename) - filepath = get_filepath_for_thumbnail(full_filename) - if os.path.exists(filepath): - filename = random_filename() - return filename - -def change_filepath(old_filename): - old_filepath = get_filepath_for_thumbnail(old_filename) - new_filename = concatenate_filename(random_filename()) - new_filepath = get_filepath_for_thumbnail(new_filename) - try: - os.renames(old_filepath, new_filepath) - return new_filename - except OSError: - log.warning('The file path "{}" of package was not found.'.format(old_filepath)) - + filepath = os.path.join( + config.get('ckan.storage_path'), + 'resources', + resource_id[0:3], + resource_id[3:6], + resource_id[6:] + ) + return filepath + + +def _is_pdf(filepath): + file_type = magic.from_file(filepath, mime = True) + return file_type == 'application/pdf' + -def create_thumbnail_from_file(file, old_filename): +def _create_thumbnail_from_file(file): width = config.get('ckan.thumbnail.size.width', 410) - filename = random_filename() + new_thumbnail = ThumbnailPath.from_unique_random_name() file.seek(0) file_read = file.read() - directory = get_filepath_for_thumbnail('') - if old_filename: - old_filepath = get_filepath_for_thumbnail(concatenate_filename(old_filename)) - if os.path.exists(old_filepath): - os.remove(old_filepath) - convert_from_bytes(file_read, - size=(width, None), - output_folder=directory, - output_file=filename, - single_file=True, - first_page=0, - last_page=0, - fmt='jpg' - ) - return concatenate_filename(filename) - - -def create_thumbnail_from_url(resource, old_filename): - return False, None -''' - # Requests does not work on a server but on a local test enviroment. - # With the given configuration of a wsgi server and apache it did not work - # Both libraries urllib2 and requests (based on urllib3) returned a timeout. - - - resource_url = resource.get('url') - request = urllib2.Request(resource_url) - response = urllib2.urlopen(request, timeout = 100000) - - - if response.code == 200: - filetowrite = response.read() - # function is set to private in ckanext.odsh.lib.uploader - raise_validation_error_if_virus_found(filetowrite, response.read()) - file_type = magic.from_buffer(response.read(), mime = True) - header = response.headers - resource_size = header.get('Content-Length') - - - max_available_memory = config.get('ckan.max_available_memory', 250000000) #In Bytes ca. 250 MB - with tempfile.SpooledTemporaryFile(max_size=max_available_memory) as file: - file.write(filetowrite) - - new_filename = create_thumbnail_from_file(file, old_filename) - return 'is PDF', resource_size, new_filename -''' - - -def create_thumbnail_from_memory(resource, old_filename): - path = get_filepath_to_resource(resource) - file_type = magic.from_file(path, mime = True) - if file_type == 'application/pdf': - with open(path, 'rb') as file: - new_filename = create_thumbnail_from_file(file, old_filename) - is_PDF = True - return is_PDF, new_filename - else: - is_PDF = False - return is_PDF, None + convert_from_bytes( + file_read, + size=(width, None), + output_folder=new_thumbnail.folder, + output_file=new_thumbnail.filename, + single_file=True, + first_page=0, + last_page=0, + fmt='jpg' + ) + return new_thumbnail.filename_with_extension + + +def thumbnail_folder(): + return os.path.join( + config.get('ckan.storage_path'), + 'thumbnail', + ) + + +def rename_thumbnail_to_random_name(old_filename): + ''' + used by pdf_to_thumbnail.action + ''' + old_filepath = ThumbnailPath.from_filename_with_extension(old_filename) + new_filepath = ThumbnailPath.from_unique_random_name() + try: + os.renames(old_filepath.full_filename, new_filepath.full_filename) + return new_filepath.filename_with_extension + except OSError: + log.warning('The file path "{}" of package was not found.'.format(old_filepath)) + def remove_thumbnail(context): - old_filename = get_filename_from_context(context) + ''' + used by pdf_to_thumbnail.action + ''' + old_filename = _get_filename_from_context(context) if old_filename: - old_filepath = get_filepath_for_thumbnail(old_filename) - if os.path.exists(old_filepath): - os.remove(old_filepath) + ThumbnailPath.from_filename_with_extension(old_filename).remove() -def create_thumbnail(context, resource): - old_filename = get_filename_from_context(context) - url_type = resource.get('url_type') - if url_type == 'upload': - is_PDF, filename = create_thumbnail_from_memory(resource, old_filename) - else: - is_PDF, filename = create_thumbnail_from_url(resource, old_filename) - return is_PDF, filename -def check_and_create_thumbnail_after_update(context, resource): +def resources_of_containing_package(resource): + #todo: change arg order + ''' + used by pdf_to_thumbnail.plugin + ''' package_id = resource.get('package_id') - package = toolkit.get_action('package_show')(context, {'id': package_id}) + package = toolkit.get_action('package_show')(None, {'id': package_id}) resources = package.get('resources') - if len(resources) > 0: - last_resource = resources.pop() - last_resource_id = last_resource.get('id') - resource_id = resource.get('id') - if last_resource_id == resource_id and resource.get('url_type') != 'upload': - is_PDF, filename = create_thumbnail(context, resource) - if is_PDF: - write_thumbnail_into_package(context, resource, filename) + return resources -def create_thumbnail_for_last_resource(context, resources): - if len(resources) > 0: - last_resource = resources.pop() - is_PDF, filename = create_thumbnail(context, last_resource) - if not is_PDF: - create_thumbnail_for_last_resource(context, resources) +def create_thumbnail_if_none_in_package(context, resources): + ''' + used by pdf_to_thumbnail.plugin + loops through a package's resources in the order they have been uploaded + and for each tries to create a thumbnail until it succeeds. + If the package already has a thumbnail the creation step is skipped + ''' + package_dict = _get_package_dict_from_context(context) + if not _has_thumbnail(package_dict): + any(_try_create_thumbnail(context, r) for r in resources) + + +def _get_package_dict_from_context(context): + package_id = context.get('package').id + package_dict = toolkit.get_action('package_show')(None, {'id': package_id}) + return package_dict + + +def _has_thumbnail(package_dict): + thumbnail = package_dict.get('thumbnail') + return bool(thumbnail) + + +def _try_create_thumbnail(context, resource): + is_PDF, filename = create_thumbnail(context, resource) + success = is_PDF + if success: + _write_thumbnail_into_package(context, filename) + return success + + +def _write_thumbnail_into_package(context, filename): + package_dict = _get_package_dict_from_context(context) + if filename: + package_dict.update({'thumbnail': filename}) + toolkit.get_action('package_update')(None, package_dict) + + +class ThumbnailPath(object): + ''' + utility class to manage the path of thumbnail pictures + ''' + + def __init__(self, folder, filename, extension): + self.folder = folder + self.filename = filename + self.extension = extension + + _EXTENSION = '.jpg' + + @staticmethod + def from_filename(filename): + ''' + filename without extension (i.e. '.jpg') + ''' + return ThumbnailPath(thumbnail_folder(), filename, ThumbnailPath._EXTENSION) + + @staticmethod + def from_filename_with_extension(filename_with_extension): + ''' + limited to one dot in filename + ''' + tokens = filename_with_extension.split('.') + if len(tokens) == 1: + filename = filename_with_extension + extension = '' else: - write_thumbnail_into_package(context, last_resource, filename) - else: - remove_thumbnail(context) - package = context.get('package') - package_id = package.id - package= toolkit.get_action('package_show')(context, {'id': package_id}) - package.update({'thumbnail': None}) - toolkit.get_action('package_update')(context, package) - -def write_thumbnail_into_package(context, resource, filename): - package_id = resource.get('package_id') - package = toolkit.get_action('package_show')(context, {'id': package_id}) - if filename: - package.update({'thumbnail': filename}) - toolkit.get_action('package_update')(context, package) \ No newline at end of file + filename = '.'.join(tokens[:-1]) + extension = '.'.join(['', tokens[-1]]) + return ThumbnailPath(thumbnail_folder(), filename, extension) + + @staticmethod + def from_unique_random_name(): + thumbnail_path = ThumbnailPath._from_random_name() + if thumbnail_path.exists(): + return ThumbnailPath.from_unique_random_name() + return thumbnail_path + + @staticmethod + def _from_random_name(): + number = b2a_hex(os.urandom(15)) + filename = 'thumbnail_picture_' + str(number) + return ThumbnailPath.from_filename(filename) + + @property + def filename_with_extension(self): + return self.filename + self.extension + + @property + def full_filename(self): + return os.path.join(self.folder, self.filename_with_extension) + + def exists(self): + return os.path.exists(self.full_filename) + + def remove(self): + if os.path.exists(self.full_filename): + os.remove(self.full_filename) diff --git a/ckanext/odsh/plugin.py b/ckanext/odsh/plugin.py index 4509a5982fff9a9edde8c99a9fa325839d144fe3..bd7c1688ead2feaf94a1f71f9b4ca484f5b45f08 100644 --- a/ckanext/odsh/plugin.py +++ b/ckanext/odsh/plugin.py @@ -133,7 +133,14 @@ class OdshPlugin(plugins.SingletonPlugin, DefaultTranslation, DefaultDatasetForm toolkit.get_validator('ignore_missing'), toolkit.get_converter('convert_to_extras') ], - + 'relatedPackage': [ + toolkit.get_validator('tpsh_validate_relatedPackage'), + toolkit.get_converter('convert_to_extras') + ], + 'accessibility': [ + toolkit.get_validator('ignore_missing'), + toolkit.get_converter('convert_to_extras') + ] }) return schema @@ -151,6 +158,14 @@ class OdshPlugin(plugins.SingletonPlugin, DefaultTranslation, DefaultDatasetForm toolkit.get_converter('convert_from_extras'), toolkit.get_validator('ignore_missing') ], + 'relatedPackage': [ + toolkit.get_converter('convert_from_extras'), + toolkit.get_validator('ignore_missing') + ], + 'accessibility': [ + toolkit.get_converter('convert_from_extras'), + toolkit.get_validator('ignore_missing') + ], }) return schema @@ -328,44 +343,46 @@ class OdshPlugin(plugins.SingletonPlugin, DefaultTranslation, DefaultDatasetForm # Template helper function names should begin with the name of the # extension they belong to, to avoid clashing with functions from # other extensions. - return {'odsh_main_groups': odsh_helpers.odsh_main_groups, - 'odsh_now': odsh_helpers.odsh_now, - 'odsh_group_id_selected': odsh_helpers.odsh_group_id_selected, - 'odsh_get_facet_items_dict': odsh_helpers.odsh_get_facet_items_dict, - 'odsh_openness_score_dataset_html': odsh_helpers.odsh_openness_score_dataset_html, - 'odsh_get_resource_details': odsh_helpers.odsh_get_resource_details, - 'odsh_get_resource_views': odsh_helpers.odsh_get_resource_views, - 'odsh_get_bounding_box': odsh_helpers.odsh_get_bounding_box, - 'odsh_get_spatial_text': odsh_helpers.odsh_get_spatial_text, - 'odsh_render_datetime': odsh_helpers.odsh_render_datetime, - 'odsh_upload_known_formats': odsh_helpers.odsh_upload_known_formats, - 'odsh_encodeurl': odsh_helpers.odsh_encodeurl, - 'odsh_extract_error': odsh_helpers.odsh_extract_error, - 'odsh_extract_error_new': odsh_helpers.odsh_extract_error_new, - 'odsh_extract_value_from_extras': odsh_helpers.odsh_extract_value_from_extras, - 'odsh_create_checksum': odsh_helpers.odsh_create_checksum, - 'presorted_license_options': odsh_helpers.presorted_license_options, - 'odsh_tracking_id': odsh_helpers.odsh_tracking_id, - 'odsh_tracking_url': odsh_helpers.odsh_tracking_url, - 'odsh_has_more_facets': odsh_helpers.odsh_has_more_facets, - 'odsh_public_url': odsh_helpers.odsh_public_url, - 'odsh_spatial_extends_available': odsh_helpers.spatial_extends_available, - 'odsh_public_resource_url': odsh_helpers.odsh_public_resource_url, - 'odsh_get_version_id': odsh_helpers.odsh_get_version_id, - 'odsh_show_testbanner': odsh_helpers.odsh_show_testbanner, - 'odsh_is_slave': odsh_helpers.odsh_is_slave, - 'odsh_use_matomo': helpers_tpsh.use_matomo, - 'tpsh_get_daterange_prettified': helper_pkg_dict.get_daterange_prettified, - 'tpsh_get_language_of_package': helpers_tpsh.get_language_of_package, - 'get_language_icon': helpers_tpsh.get_language_icon, - 'short_name_for_category': odsh_helpers.short_name_for_category, - 'get_spatial_for_selection': helpers_tpsh.get_spatial_for_selection, - 'get_subject_for_selection': helpers_tpsh.get_subject_for_selection, - 'get_language_for_selection': helpers_tpsh.get_language_for_selection, - 'tpsh_get_resource_size': helpers_tpsh.get_resource_size, - 'tpsh_get_address_org':helpers_tpsh.get_address_org, - 'tpsh_get_body_mail':helpers_tpsh.get_body_mail, - } + return { + 'odsh_main_groups': odsh_helpers.odsh_main_groups, + 'odsh_now': odsh_helpers.odsh_now, + 'odsh_group_id_selected': odsh_helpers.odsh_group_id_selected, + 'odsh_get_facet_items_dict': odsh_helpers.odsh_get_facet_items_dict, + 'odsh_openness_score_dataset_html': odsh_helpers.odsh_openness_score_dataset_html, + 'odsh_get_resource_details': odsh_helpers.odsh_get_resource_details, + 'odsh_get_resource_views': odsh_helpers.odsh_get_resource_views, + 'odsh_get_bounding_box': odsh_helpers.odsh_get_bounding_box, + 'odsh_get_spatial_text': odsh_helpers.odsh_get_spatial_text, + 'odsh_render_datetime': odsh_helpers.odsh_render_datetime, + 'odsh_upload_known_formats': odsh_helpers.odsh_upload_known_formats, + 'odsh_encodeurl': odsh_helpers.odsh_encodeurl, + 'odsh_extract_error': odsh_helpers.odsh_extract_error, + 'odsh_extract_error_new': odsh_helpers.odsh_extract_error_new, + 'odsh_extract_value_from_extras': odsh_helpers.odsh_extract_value_from_extras, + 'odsh_create_checksum': odsh_helpers.odsh_create_checksum, + 'presorted_license_options': odsh_helpers.presorted_license_options, + 'odsh_tracking_id': odsh_helpers.odsh_tracking_id, + 'odsh_tracking_url': odsh_helpers.odsh_tracking_url, + 'odsh_has_more_facets': odsh_helpers.odsh_has_more_facets, + 'odsh_public_url': odsh_helpers.odsh_public_url, + 'odsh_spatial_extends_available': odsh_helpers.spatial_extends_available, + 'odsh_public_resource_url': odsh_helpers.odsh_public_resource_url, + 'odsh_get_version_id': odsh_helpers.odsh_get_version_id, + 'odsh_show_testbanner': odsh_helpers.odsh_show_testbanner, + 'odsh_is_slave': odsh_helpers.odsh_is_slave, + 'odsh_use_matomo': helpers_tpsh.use_matomo, + 'tpsh_get_daterange_prettified': helper_pkg_dict.get_daterange_prettified, + 'tpsh_get_language_of_package': helpers_tpsh.get_language_of_package, + 'get_language_icon': helpers_tpsh.get_language_icon, + 'short_name_for_category': odsh_helpers.short_name_for_category, + 'get_spatial_for_selection': helpers_tpsh.get_spatial_for_selection, + 'get_subject_for_selection': helpers_tpsh.get_subject_for_selection, + 'get_language_for_selection': helpers_tpsh.get_language_for_selection, + 'tpsh_get_resource_size': helpers_tpsh.get_resource_size, + 'tpsh_get_address_org':helpers_tpsh.get_address_org, + 'tpsh_get_body_mail':helpers_tpsh.get_body_mail, + 'tpsh_git_commit_hash': helpers_tpsh.git_commit_hash, + } # IValidators diff --git a/ckanext/odsh/profiles/odsh_dcat_de_profile.py b/ckanext/odsh/profiles/odsh_dcat_de_profile.py index 7daa2732f5372d2ccd3f0685c40553d94ff10111..d4db9e11a2d64da660feaa7174e5e935b20808ff 100644 --- a/ckanext/odsh/profiles/odsh_dcat_de_profile.py +++ b/ckanext/odsh/profiles/odsh_dcat_de_profile.py @@ -10,7 +10,6 @@ from ckanext.dcatde.profiles import DCATdeProfile, DCATDE, DCAT, DCATDE_1_0 import ckanext.odsh.helpers_tpsh as helpers_tpsh import ckanext.odsh.collection.helpers as helpers_collection -from ckanext.odsh.helper_pkg_dict import HelperPgkDict DCT = rdflib.namespace.Namespace("http://purl.org/dc/terms/") @@ -154,9 +153,7 @@ class ODSHDCATdeProfile(DCATdeProfile): return is_collection def _get_dataset_refs_belonging_to_collection(self, dataset_dict): - dataset_names = helpers_collection.get_all_datasets_belonging_to_collection( - collection_name = dataset_dict.get('id') - ) + dataset_names = helpers_collection.get_dataset_names(dataset_dict) dataset_dicts = [model.Package.get(name).as_dict() for name in dataset_names] dataset_ids = [dataset_dict.get('id') for dataset_dict in dataset_dicts] dataset_refs = [self._construct_refs(id) for id in dataset_ids] @@ -175,12 +172,11 @@ class ODSHDCATdeProfile(DCATdeProfile): ''' if dataset_dict.get('type')=='collection': return False - id_dataset = dataset_dict.get('id') - collection_name = helpers_collection.get_collection_name_by_dataset(id_dataset) + collection_name = helpers_collection.get_collection_id(dataset_dict) return collection_name is not None def _add_collection(self, dataset_dict, dataset_ref): - collection_id = HelperPgkDict(dataset_dict).get_collection_id() + collection_id = helpers_collection.get_collection_id(dataset_dict) collection_uri = self._construct_refs(collection_id) self.g.set( (dataset_ref, DCT.isVersionOf, diff --git a/ckanext/odsh/public/odsh.css b/ckanext/odsh/public/odsh.css index 47a567da70b96179e763434aadc828e8d5ee3194..ddc13115e51f999a1f0ffdf7c128947a705ad147 100644 --- a/ckanext/odsh/public/odsh.css +++ b/ckanext/odsh/public/odsh.css @@ -1269,6 +1269,7 @@ body { .footer-icon{ float: left; margin-right: 40px; + margin-bottom: 1rem; } .footer-icon.last{ @@ -1313,7 +1314,7 @@ body { box-sizing: border-box; padding-bottom: 15px; background-image: none; - height: 139px; + height: 150px; display: flex; flex-direction: column-reverse; } @@ -1325,6 +1326,7 @@ body { display: flex; justify-content: space-between; padding-bottom: 15px; + flex-wrap: wrap; } .footer-icon a{ padding: 15px 0; @@ -1452,6 +1454,24 @@ body { color: #000000; } +.hint-newer-version { + margin-top: 30px; + margin-bottom: 30px; + font-size: 20px; + color: white; + background-color: #D4004B; + padding: 0.5rem 1rem; +} + +.hint-newer-version > a { + color: white; + text-decoration: underline; +} + +.hint-newer-version > a:hover { + color: lightgray; +} + .resource-list { margin: 0px; } @@ -2768,4 +2788,8 @@ body.filters-modal div.row > aside.secondary.span3 { height: auto; margin-right: 6px; } -} \ No newline at end of file +} + +.tpsh-collection-list { + list-style-type: none; +} diff --git a/ckanext/odsh/templates/footer.html b/ckanext/odsh/templates/footer.html index 03764a7c7b58782fb85faee0af0af365732205df..f719b26b265cfaba70b05dfc3b95888819a888b0 100644 --- a/ckanext/odsh/templates/footer.html +++ b/ckanext/odsh/templates/footer.html @@ -12,6 +12,7 @@ <div class="footer-right"> <div class='footer-icon'><a href='http://www.schleswig-holstein.de/tpkontakt'>Kontakt</a></div> <div class='footer-icon'><a href='http://www.schleswig-holstein.de/tpimpressum'>Impressum</a></div> + <div class='footer-icon'><a href='https://www.schleswig-holstein.de/DE/Serviceseiten/Barrierefreiheitserklaerung/barrierefreiheit_node.html'>Barrierefreiheit</a></div> <div class='footer-icon last'><a href='http://www.schleswig-holstein.de/tpdatenschutz'>Datenschutz</a></div> </div> </div> diff --git a/ckanext/odsh/templates/package/collection_read.html b/ckanext/odsh/templates/package/collection_read.html new file mode 100644 index 0000000000000000000000000000000000000000..7f1ef070954d4df8b125becfd81a30213ef81134 --- /dev/null +++ b/ckanext/odsh/templates/package/collection_read.html @@ -0,0 +1,22 @@ +{% extends "package/read.html" %} + + +{% block package_resources %} +{% endblock package_resources %} + + +{% block collection %} + +{% set collection_id = pkg.get('id') %} +{% set collection = h.get_collection_info(collection_id) %} +{% set collection_title = collection['title'] if collection else None %} + +{% if collection['members'] %} +<p> + Dies ist die Übersichtsseite der Dokumentensammlung {{ collection_title }}. + Sie enthält folgende Dokumente (neuere Dokumente zuerst): +</p> +{% snippet 'snippets/package_list.html', packages=collection['members'] | reverse() %} +{% endif %} + +{% endblock collection %} diff --git a/ckanext/odsh/templates/package/read.html b/ckanext/odsh/templates/package/read.html index d057aa20f97049e6ba6a1fb00a53bbc81c7e9b70..fd94229c593a5ceb09e219f32f13b9babeff6458 100644 --- a/ckanext/odsh/templates/package/read.html +++ b/ckanext/odsh/templates/package/read.html @@ -1,10 +1,11 @@ {% extends "package/read_base.html" %} {% set pkg = c.pkg_dict %} -{% set collection_title = h.collection_get_title(pkg) %} -{% set successor_url = h.collection_get_successor(pkg) %} -{% set predecessor_url = h.collection_get_predecessor(pkg) %} -{% set latest_collection_member = h.collection_get_latest_member(pkg) %} +{% set collection = h.get_collection(pkg) %} +{% set collection_title = collection['title'] if collection else None %} +{% set successor_url = collection['successor']['url'] if collection else None %} +{% set predecessor_url = collection['predecessor']['url'] if collection else None %} +{% set latest_collection_member = collection['persistent_link_last_member'] if collection else None %} {% set thumbnail_url = h.thumbail_get_download_link(pkg) %} {% block breadcrumb_content %} @@ -22,7 +23,7 @@ <li>{% link_for _('Documents'), controller='package', action='search' %}</li> <li class="active"><a href="">{{ _('Create Dataset') }}</a></li> {% endif %} -{% endblock %} +{% endblock breadcrumb_content %} {% block primary_content_inner %} {{ super() }} @@ -37,7 +38,7 @@ {% if pkg.state == 'deleted' %} [{{ _('Deleted') }}] {% endif %} - {% endblock %} + {% endblock page_heading%} <div class="odsh-dataset-edit-button"> {% if h.check_access('package_update', {'id':pkg.id }) %} <div> @@ -61,6 +62,12 @@ <img src="/base/images/icon_info.svg" aria-hidden="true"></img> {{ _('Detailinformationen') }} </div> +{% if successor_url %} +<p class="hint-newer-version"> + Hinweis: Es ist eine <a href="{{ latest_collection_member }}"> + neuere Version</a> dieses Dokuments verfügbar. +</p> +{% endif %} {% if pkg.notes %} <div class="notes embedded-content"> {{ h.render_markdown(h.get_translated(pkg, 'notes')) }} @@ -71,7 +78,7 @@ {% block package_resources %} {% snippet "package/snippets/resources_list.html", pkg=pkg, resources=pkg.resources %} -{% endblock %} +{% endblock package_resources %} {% block thumbnail %} {% set thumbnail = pkg.get('thumbnail') %} @@ -139,5 +146,5 @@ {% endfor %} </div> -{% endblock %} +{% endblock primary_content_inner %} diff --git a/ckanext/odsh/templates/snippets/package_item.html b/ckanext/odsh/templates/snippets/package_item.html index 419e685bc6eedfcb77f671a559ec19f408fbee75..6448db8f4582829b7620110bda14e20f865c414c 100644 --- a/ckanext/odsh/templates/snippets/package_item.html +++ b/ckanext/odsh/templates/snippets/package_item.html @@ -36,7 +36,7 @@ Example: <div class="preview-image-container"> {% if thumbnail %} <a href={{ h.url_for(controller='package', action='read', id=package.name) }}> - <img src= "{{ thumbnail }}" alt= "Vorschau" /> + <img src= "/{{ thumbnail }}" alt= "Vorschau" /> </a> {% else %} diff --git a/ckanext/odsh/templates/user/login.html b/ckanext/odsh/templates/user/login.html index 874fd1bf692c79658d66e7946c0a69d8eb6f4e3e..d9fb0ad86d0f83803f17eba3fc3175d1825e733d 100644 --- a/ckanext/odsh/templates/user/login.html +++ b/ckanext/odsh/templates/user/login.html @@ -1,7 +1,16 @@ {% extends "page.html" %} +{% set commit_hash = h.tpsh_git_commit_hash() %} + {% block subtitle %} {{ _('Login') }} {% endblock %} +{% block skip %} +<p hidden> + version={{ commit_hash }} +</p> +{{ super() }} +{% endblock skip %} + {% block breadcrumb_content %} <li class="active">{{ h.nav_link(_('Login'), controller='user', action='login') }}</li> {% endblock %} @@ -30,4 +39,4 @@ {% endblock %} </section> {% endblock %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/ckanext/odsh/tests_tpsh/_test_thumbnail_action.py b/ckanext/odsh/tests_tpsh/_test_thumbnail_action.py new file mode 100644 index 0000000000000000000000000000000000000000..ec7165f0df6ff289047a2e13c6b901b251a37625 --- /dev/null +++ b/ckanext/odsh/tests_tpsh/_test_thumbnail_action.py @@ -0,0 +1,11 @@ +import ckanext.odsh.pdf_to_thumbnail.action as thumbnail_action + +class TestBeforePackageDelete(object): + def test_deletes_if_access_granted(self): + # todo: implement unit test + assert True + +class TestBeforePackageUpdate(object): + def test_does_whatever(self): + # todo: implement unit test + assert True \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/resources/ki_strategie.jpg b/ckanext/odsh/tests_tpsh/resources/ki_strategie.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2c88dbd220cee45db52655f18ea4a6213e8a954f Binary files /dev/null and b/ckanext/odsh/tests_tpsh/resources/ki_strategie.jpg differ diff --git a/ckanext/odsh/tests_tpsh/resources/ki_strategie.pdf b/ckanext/odsh/tests_tpsh/resources/ki_strategie.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e25ddb95a1e95769c32507c8fb61b5dc7eb5dd6c Binary files /dev/null and b/ckanext/odsh/tests_tpsh/resources/ki_strategie.pdf differ diff --git a/ckanext/odsh/tests_tpsh/resources/ki_strategie_jz.pdf b/ckanext/odsh/tests_tpsh/resources/ki_strategie_jz.pdf new file mode 100644 index 0000000000000000000000000000000000000000..50b4e1db2f7bd402ce2faf99b2434d328a913942 Binary files /dev/null and b/ckanext/odsh/tests_tpsh/resources/ki_strategie_jz.pdf differ diff --git a/ckanext/odsh/tests_tpsh/resources/test_copy_pdf b/ckanext/odsh/tests_tpsh/resources/test_copy_pdf new file mode 100644 index 0000000000000000000000000000000000000000..10451cf0fba2fea7ac87ae52cc9b350b48c67198 Binary files /dev/null and b/ckanext/odsh/tests_tpsh/resources/test_copy_pdf differ diff --git a/ckanext/odsh/tests_tpsh/test_action.py b/ckanext/odsh/tests_tpsh/test_action.py new file mode 100644 index 0000000000000000000000000000000000000000..a652c16a9a39cd0c0863ab5f534a893da60a9dfd --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_action.py @@ -0,0 +1,207 @@ +import nose.tools as nt +from mock import patch +from copy import deepcopy + +import ckanext.odsh.logic.action as action + + +patch_package_create = patch.object( + action, + 'package_create', + return_value={'id': 'new_package_id', 'name': 'new_package_name'}, +) + +patch_munge_increment_name = patch.object( + action, + 'munge_increment_name', +) + +patch_add_to_collection = patch.object( + action, + 'add_to_same_collection', +) + + +class TestOdshPackageCreate(object): + + @patch_package_create + def test_it_calls_package_create_for_collection(self, mock): + action.odsh_package_create('context', {'type': 'collection'}) + mock.assert_called_with('context', {'type': 'collection'}) + + @patch_package_create + @patch_munge_increment_name + def test_it_does_not_call_munge_increment_name_for_collection(self, mock_min, mock_pc): + action.odsh_package_create('context', {'type': 'collection'}) + nt.assert_false(mock_min.called) + + @patch_package_create + @patch_munge_increment_name + def test_it_calls_munge_increment_name_for_dataset(self, mock_min, mock_pc): + action.odsh_package_create( + 'context', {'type': 'dataset', 'extras': []}) + nt.assert_true(mock_min.called) + + @patch_package_create + @patch_munge_increment_name + def test_does_not_modify_dict_if_extras_contain_issued(self, mock_min, mock_pc): + dataset_dict = { + 'type': 'dataset', + 'extras': [ + {'key': 'issued', 'value': 'some_date'} + ], + } + action.odsh_package_create('context', deepcopy(dataset_dict)) + mock_pc.assert_called_with('context', dataset_dict) + + @patch_package_create + @patch_munge_increment_name + def test_does_modify_dict_if_extras_does_not_contain_issued(self, mock_min, mock_pc): + dataset_dict = { + 'type': 'dataset', + 'extras': [], + } + action.odsh_package_create('context', dataset_dict) + nt.assert_equal(dataset_dict['extras'][0]['key'], 'issued') + + @patch_add_to_collection + @patch_package_create + @patch_munge_increment_name + def test_does_NOT_call_add_to_same_collection(self, mock_min, mock_pc, mock_atsc): + dataset_dict = { + 'type': 'dataset', + 'extras': [], + } + action.odsh_package_create('context', deepcopy(dataset_dict)) + nt.assert_false(mock_atsc.called) + + @patch_add_to_collection + @patch_package_create + @patch_munge_increment_name + def test_does_call_add_to_same_collection(self, mock_min, mock_pc, mock_atsc): + dataset_dict = { + 'type': 'dataset', + 'extras': [], + 'relatedPackage': 'some_package_name', + } + action.odsh_package_create('context', deepcopy(dataset_dict)) + mock_atsc.assert_called_with('some_package_name', 'new_package_name') + + +patch_get_package_dict = patch.object( + action, + 'get_package_dict', +) + +patch_add_to_collection = patch.object( + action, + 'add_to_collection', +) + +patch_auto_create_collection = patch.object( + action, + 'auto_create_collection', + return_value={ + 'id': 'auto_created_collection', + } +) + + +class TestAddToSameCollection(object): + + @patch_auto_create_collection + @patch_add_to_collection + @patch_get_package_dict + def test_creates_collection_if_not_exists(self, patch_gpd, patch_atc, patch_acc): + patch_gpd.return_value = { + 'relationships_as_subject': [] + } + action.add_to_same_collection('related_package_name', 'package_name') + nt.assert_true(patch_acc.called) + patch_atc.assert_called_with('auto_created_collection', 'package_name') + + @patch_auto_create_collection + @patch_add_to_collection + @patch_get_package_dict + def test_adds_new_package_to_collection(self, patch_gpd, patch_atc, patch_acc): + patch_gpd.return_value = { + 'relationships': [ + { + 'object': 'collection_id' + } + ] + } + action.add_to_same_collection('related_package_name', 'package_name') + nt.assert_false(patch_acc.called) + patch_atc.assert_called_with('collection_id', 'package_name') + + @patch_auto_create_collection + @patch_add_to_collection + @patch_get_package_dict + def test_adds_existing_package_to_new_collection(self, patch_gpd, patch_atc, patch_acc): + patch_gpd.return_value = { + 'relationships_as_subject': [] + } + action.add_to_same_collection('related_package_name', 'package_name') + patch_atc.assert_any_call('auto_created_collection', 'related_package_name') + + +patch_package_create_via_toolkit = patch.object( + action, + 'package_create_via_toolkit', +) + +def my_munge_increment_name(data_dict): + data_dict['name'] = 'munged_name' + +patch_munge_increment_name = patch.object( + action, + 'munge_increment_name', + side_effect=my_munge_increment_name, +) +class TestAutoCreateCollection(object): + + @patch_munge_increment_name + @patch_package_create_via_toolkit + def test_calls_package_create_with_default_context(self, patch_pc, patch_min): + action.auto_create_collection({ + 'title': 'package_title', + 'name': 'package_name', + 'owner_org': 'owner_org', + }) + patch_pc.assert_called_with(None, { + 'owner_org': 'owner_org', + 'type': 'collection', + 'name': 'munged_name', + 'title': 'package_title', + }) + + @patch_munge_increment_name + @patch_package_create_via_toolkit + def test_returns_collection_dict(self, patch_pc, patch_min): + collection_dict_expected = { + 'name': 'new_collection', + 'type': 'collection', + } + patch_pc.return_value = collection_dict_expected + collection_dict = action.auto_create_collection({ + 'title': 'package_title', + 'name': 'package_name', + 'owner_org': 'owner_org', + }) + nt.assert_equal(collection_dict, collection_dict_expected) + + +patch_add_pkg_to_collection = patch.object( + action, + 'add_pkg_to_collection', +) + + +class TestAddToCollection(object): + + @patch_add_pkg_to_collection + def test_reminder(self, patch_aptc): + action.add_to_collection('collection_id', 'package_name') + nt.assert_true(patch_aptc.called) + patch_aptc.assert_called_once_with('package_name', 'collection_id') diff --git a/ckanext/odsh/tests_tpsh/test_dcat_de_profile.py b/ckanext/odsh/tests_tpsh/test_dcat_de_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..c384ec3e03ade5131c2ea662ec32311be4e3ed49 --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_dcat_de_profile.py @@ -0,0 +1,160 @@ +import mock +from copy import deepcopy +import nose.tools as nt +import ckanext.odsh.profiles.odsh_dcat_de_profile as odsh_profile + +dataset_dict_not_in_collection = { + u'author': None, + u'author_email': None, + u'creator_user_id': u'ff8d002d-3908-45e5-ba7b-445381860957', + u'extras': [{u'key': u'groups', u'value': u''}, + {u'key': u'issued', u'value': u'2020-05-14T00:00:00'}, + {u'key': u'licenseAttributionByText', u'value': u''}, + {u'key': u'spatial', + u'value': u'{"type": "Polygon", "coordinates": [[[9.5557, 54.6361], [9.5576, 54.6395], [9.5739, 54.6428], [9.593, 54.6508], [9.6041, 54.6497], [9.6049, 54.6533], [9.6132, 54.6556], [9.6261, 54.6632], [9.627, 54.6671], [9.6349, 54.6672], [9.6264, 54.6548], [9.6257, 54.6494], [9.6235, 54.6496], [9.624, 54.6426], [9.619, 54.6349], [9.6231, 54.6354], [9.6222, 54.6343], [9.6263, 54.6338], [9.6441, 54.6399], [9.6459, 54.6378], [9.6529, 54.6368], [9.657, 54.6327], [9.6629, 54.6312], [9.667, 54.6281], [9.6699, 54.6283], [9.6703, 54.6249], [9.6874, 54.6304], [9.6963, 54.6245], [9.6948, 54.6183], [9.7011, 54.6132], [9.6962, 54.6101], [9.6933, 54.6125], [9.6904, 54.6116], [9.6867, 54.6085], [9.6897, 54.6038], [9.6826, 54.6007], [9.6881, 54.599], [9.6857, 54.5943], [9.6911, 54.5913], [9.695, 54.591], [9.6965, 54.5932], [9.7065, 54.5957], [9.7101, 54.5935], [9.7148, 54.5931], [9.7169, 54.5948], [9.7232, 54.5928], [9.7241, 54.587], [9.718, 54.585], [9.7195, 54.5831], [9.7159, 54.5817], [9.7186, 54.5783], [9.7162, 54.5746], [9.7194, 54.5694], [9.7125, 54.5655], [9.7227, 54.5599], [9.7218, 54.5544], [9.7265, 54.5521], [9.726, 54.5492], [9.7289, 54.5497], [9.7322, 54.5478], [9.7413, 54.5402], [9.7412, 54.5375], [9.7374, 54.5357], [9.7389, 54.5321], [9.7373, 54.5303], [9.7211, 54.5299], [9.718, 54.5286], [9.714, 54.5187], [9.7119, 54.5183], [9.7024, 54.5179], [9.6932, 54.5224], [9.6731, 54.5153], [9.6439, 54.5155], [9.6344, 54.511], [9.6252, 54.5159], [9.6159, 54.5159], [9.616, 54.5236], [9.6035, 54.5269], [9.6036, 54.5282], [9.5986, 54.5282], [9.5927, 54.526], [9.5916, 54.5342], [9.5941, 54.5354], [9.5869, 54.5374], [9.5917, 54.5454], [9.5849, 54.5459], [9.576, 54.544], [9.5757, 54.5422], [9.5679, 54.5444], [9.5598, 54.5417], [9.5487, 54.5438], [9.546, 54.5413], [9.5472, 54.5402], [9.5434, 54.5391], [9.5456, 54.5394], [9.5445, 54.5367], [9.5399, 54.5357], [9.5364, 54.5393], [9.538, 54.5414], [9.5313, 54.5449], [9.5347, 54.5467], [9.5286, 54.5467], [9.5291, 54.5454], [9.5263, 54.5447], [9.5154, 54.5442], [9.5127, 54.5454], [9.5123, 54.5485], [9.5079, 54.549], [9.5054, 54.5526], [9.4992, 54.5549], [9.4941, 54.5647], [9.4899, 54.5617], [9.4798, 54.5643], [9.4824, 54.5965], [9.472, 54.5981], [9.4581, 54.5978], [9.4566, 54.6104], [9.4594, 54.6117], [9.4708, 54.6073], [9.4856, 54.6099], [9.491, 54.6094], [9.5065, 54.6153], [9.5059, 54.6205], [9.4983, 54.6206], [9.4995, 54.6246], [9.5109, 54.6234], [9.5116, 54.6258], [9.5009, 54.644], [9.4923, 54.6453], [9.4968, 54.6488], [9.4947, 54.6503], [9.493, 54.6494], [9.4844, 54.656], [9.4759, 54.6758], [9.5057, 54.6693], [9.5166, 54.6771], [9.5156, 54.6792], [9.5206, 54.6786], [9.5256, 54.6821], [9.5302, 54.683], [9.5301, 54.6842], [9.5359, 54.6846], [9.5297, 54.6703], [9.5299, 54.6676], [9.5328, 54.6672], [9.5328, 54.6652], [9.537, 54.6626], [9.5314, 54.6618], [9.5305, 54.6595], [9.5377, 54.6598], [9.5411, 54.6518], [9.5499, 54.6507], [9.5485, 54.6478], [9.5507, 54.6447], [9.5471, 54.6401], [9.5508, 54.6367], [9.5557, 54.6361]]]}'}, + {u'key': u'spatial_text', u'value': u'Amt S\xfcdangeln'}, + {u'key': u'spatial_uri', + u'value': u'http://dcat-ap.de/def/politicalGeocoding/regionalKey/010595987'}, + {u'key': u'subject_text', u'value': u'Brosch\xfcre'}, + {u'key': u'temporal_end', + u'value': u'2020-05-31T00:00:00'}, + {u'key': u'temporal_start', u'value': u'2020-05-01T00:00:00'}], + u'groups': [], + u'id': u'ff55a3ee-6dcd-4f76-b5a3-08055d5241f4', + 'is_new': True, + u'isopen': True, + u'language': u'http://publications.europa.eu/resource/authority/language/DEU', + u'license_id': u'http://dcat-ap.de/def/licenses/dl-zero-de/2.0', + u'license_title': u'Datenlizenz Deutschland \u2013 Zero \u2013 Version 2.0', + u'license_url': u'https://www.govdata.de/dl-de/zero-2-0', + u'maintainer': None, + u'maintainer_email': None, + u'metadata_created': u'2020-05-14T12:34:05.551655', + u'metadata_modified': u'2020-05-14T12:53:06.484131', + u'name': u'test-thumbnail', + u'notes': u'Test-Package f\xfcr Thumbnail-Gernerierung', + u'num_resources': 1, + u'num_tags': 0, + u'organization': {u'approval_status': u'approved', + u'created': u'2019-07-29T08:11:32.697127', + u'description': u'', + u'id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + u'image_url': u'', + u'is_organization': True, + u'name': u'test-organisation', + u'revision_id': u'3040af6c-d3f6-462d-b48a-329d63e17a28', + u'state': u'active', + u'title': u'Test-Organisation', + u'type': u'organization'}, + u'owner_org': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + u'private': False, + u'relationships_as_object': [], + u'relationships_as_subject': [], + u'resources': [{u'cache_last_updated': None, + u'cache_url': None, + u'created': u'2020-05-14T12:34:19.974216', + u'datastore_active': False, + u'description': u'', + u'format': u'PDF', + u'hash': u'66123edf64fabf1c073fc45478bf4a57', + u'hash_algorithm': u'http://dcat-ap.de/def/hashAlgorithms/md/5', + u'id': u'3ec53c20-f038-4ad6-a9fd-265cbe6faa27', + u'last_modified': u'2020-05-14T12:34:19.817460', + u'mimetype': u'application/pdf', + u'mimetype_inner': None, + u'name': u'Checkliste-Barrierefreies-PDF.pdf', + u'number_of_pages': u'3', + u'package_id': u'ff55a3ee-6dcd-4f76-b5a3-08055d5241f4', + u'position': 0, + u'resource_type': None, + u'revision_id': u'efba6d3b-6204-4872-b6d1-a9e7b6d05a35', + u'size': 112437, + u'state': u'active', + u'url': u'http://192.168.152.133:5000/dataset/ff55a3ee-6dcd-4f76-b5a3-08055d5241f4/resource/3ec53c20-f038-4ad6-a9fd-265cbe6faa27/download/checkliste-barrierefreies-pdf.pdf', + u'url_type': u'upload'}], + u'revision_id': u'c8cb03d3-ab84-418f-90e6-d7ec1a0a3ed9', + u'state': u'active', + u'subject': u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Broschuere', + u'tags': [], + u'thumbnail': u'thumbnail_picture_67373b78b2f66339b9e2708577da52.jpg', + u'title': u'Test Thumbnail', + u'type': u'dataset', + u'url': None, + u'version': None +} + + +dataset_dict_in_collection = { + u'author': None, + u'author_email': None, + u'creator_user_id': u'ff8d002d-3908-45e5-ba7b-445381860957', + u'extras': [{u'key': u'issued', u'value': u'2019-07-07T00:00:00'}, + {u'key': u'licenseAttributionByText', u'value': u''}, + {u'key': u'subject_text', u'value': u'Verwaltungsvorschrift'}], + u'groups': [], + u'id': u'd1948d21-df84-4dd5-b273-1f10240c7e6b', + 'is_new': False, + u'isopen': True, + u'language': u'http://publications.europa.eu/resource/authority/language/DEU', + u'license_id': u'http://dcat-ap.de/def/licenses/dl-zero-de/2.0', + u'license_title': u'Datenlizenz Deutschland \u2013 Zero \u2013 Version 2.0', + u'license_url': u'https://www.govdata.de/dl-de/zero-2-0', + u'maintainer': None, + u'maintainer_email': None, + u'metadata_created': u'2020-04-08T19:34:15.692996', + u'metadata_modified': u'2020-04-08T19:34:15.693001', + u'name': u'test-911', + u'notes': u'Testdatensatz aus Skript', + u'num_resources': 0, + u'num_tags': 0, + u'organization': {u'approval_status': u'approved', + u'created': u'2019-07-29T08:11:32.697127', + u'description': u'', + u'id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + u'image_url': u'', + u'is_organization': True, + u'name': u'test-organisation', + u'revision_id': u'3040af6c-d3f6-462d-b48a-329d63e17a28', + u'state': u'active', + u'title': u'Test-Organisation', + u'type': u'organization'}, + u'owner_org': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + u'private': False, + u'relationships_as_object': [], + u'relationships_as_subject': [{'__extras': {'object_package_id': u'299b7d18-ec59-4dc5-81c4-081c37b6e82d', + 'revision_id': u'7f8c1e02-daee-4c1b-95f5-85c1e0306c1b', + 'subject_package_id': u'd1948d21-df84-4dd5-b273-1f10240c7e6b'}, + 'comment': u'd1948d21-df84-4dd5-b273-1f10240c7e6b', + 'id': u'9154e659-d069-4f9d-b7bc-a56231829eb2', + 'type': u'child_of'}], + u'resources': [], + u'revision_id': u'397df935-7abe-4e72-8e8e-db2067958302', + u'state': u'active', + u'subject': u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Verwaltungsvorschrift', + u'tags': [], + u'title': u'Test 91', + u'type': u'dataset', + u'url': None, + u'version': None +} + + +class Test_dataset_belongs_to_collection(object): + def setUp(self): + self.graph = mock.Mock() + self.profile = odsh_profile.ODSHDCATdeProfile(self.graph) + + def test_returns_False_if_collection(self): + is_collection = self.profile._dataset_belongs_to_collection( + {'type': 'collection'}) + nt.assert_false(is_collection) + + def test_returns_False_if_no_collection_name(self): + is_collection = self.profile._dataset_belongs_to_collection( + deepcopy(dataset_dict_not_in_collection)) + nt.assert_false(is_collection) + + def test_returns_True_if_has_collection_name(self): + is_collection = self.profile._dataset_belongs_to_collection( + deepcopy(dataset_dict_in_collection)) + nt.assert_true(is_collection) diff --git a/ckanext/odsh/tests_tpsh/test_helpers_collection.py b/ckanext/odsh/tests_tpsh/test_helpers_collection.py new file mode 100644 index 0000000000000000000000000000000000000000..b48bb9da2601560835e653e16005eb82646ed502 --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_helpers_collection.py @@ -0,0 +1,212 @@ +import nose.tools as nt +from mock import patch +from copy import deepcopy + +import ckanext.odsh.collection.helpers as helpers_collection + + +class TestHelpersCollection(object): + def setUp(self): + self.datasets_in_collection = [ + { + 'name': 'dataset 1', + }, + { + 'name': 'dataset 2', + }, + { + 'name': 'dataset 3', + }, + ] + + self.collection_dict = { + 'name': 'collection name', + 'title': 'collection title', + 'relationships': [ + { + 'object': 'dataset 1', + }, + { + 'object': 'dataset 2', + }, + { + 'object': 'dataset 3', + }, + ] + } + + self.patch_get_package_dict = patch.object( + helpers_collection, + 'get_package_dict', + return_value=self.collection_dict + ) + self.patch_get_package_dict.start() + + self.patch_url_from_id = patch.object( + helpers_collection, + 'url_from_id', + new=lambda id: 'http://{}'.format(id) + ) + self.patch_url_from_id.start() + + self.patch_url_last_member = patch.object( + helpers_collection, + 'url_last_member', + new=lambda collection_name: 'http://{}/last'.format( + collection_name) + ) + self.patch_url_last_member.start() + + self.patch_get_datasets_from_solr = patch.object( + helpers_collection, + 'get_datasets_from_solr', + return_value=self.datasets_in_collection + ) + self.patch_get_datasets_from_solr.start() + + def tearDown(self): + self.patch_get_package_dict.stop() + self.patch_url_from_id.stop() + self.patch_url_last_member.stop() + self.patch_get_datasets_from_solr.stop() + + def test_patch_get_package_dict(self): + collection_dict = helpers_collection.get_package_dict( + 'collection_name') + nt.assert_equal(collection_dict, self.collection_dict) + + def test_patch_url_from_id(self): + nt.assert_equal(helpers_collection.url_from_id( + 'some_id'), 'http://some_id') + + def test_patch_url_last_member(self): + nt.assert_equal(helpers_collection.url_last_member( + 'some_collection'), 'http://some_collection/last') + + def test_get_collection_info_WITHOUT_dataset_dict(self): + collection_info = helpers_collection.get_collection_info('some_id') + collection_info_expected = { + 'first_member': { + 'name': 'dataset 1', + 'url': 'http://dataset 1' + }, + 'last_member': {'name': 'dataset 3', 'url': 'http://dataset 3'}, + 'members': [{'name': 'dataset 1'}, {'name': 'dataset 2'}, {'name': 'dataset 3'}], + 'persistent_link_last_member': 'http://collection name/last', + 'predecessor': {'url': None}, + 'successor': {'url': None}, + 'title': 'collection title' + } + nt.assert_equal(collection_info, collection_info_expected) + + def test_get_collection_info_WITH_dataset_dict(self): + collection_info = helpers_collection.get_collection_info('some_id', {'name': 'dataset 2'}) + collection_info_expected = { + 'first_member': {'name': 'dataset 1', 'url': 'http://dataset 1'}, + 'last_member': {'name': 'dataset 3', 'url': 'http://dataset 3'}, + 'members': [{'name': 'dataset 1'}, {'name': 'dataset 2'}, {'name': 'dataset 3'}], + 'persistent_link_last_member': 'http://collection name/last', + 'predecessor': {'url': 'http://dataset 1'}, + 'successor': {'url': 'http://dataset 3'}, + 'title': 'collection title' + } + nt.assert_equal(collection_info, collection_info_expected) + + def test_get_collection_id_if_in_collection(self): + dataset_dict = { + 'relationships_as_subject': [ + {'__extras': { + 'object_package_id': 'some_id' + }} + ] + } + collection_id = helpers_collection.get_collection_id(dataset_dict) + nt.assert_equal(collection_id, 'some_id') + + def test_get_collection_id_if_in_collection_alternative_version(self): + dataset_dict = { + 'relationships': [ + {'object': 'some_id'} + ] + } + collection_id = helpers_collection.get_collection_id(dataset_dict) + nt.assert_equal(collection_id, 'some_id') + + def test_get_collection_id_if_NOT_in_collection(self): + dataset_dict = { + 'relationships_as_subject': [] + } + collection_id = helpers_collection.get_collection_id(dataset_dict) + nt.assert_equal(collection_id, None) + + def test_get_dataset_names(self): + dataset_names = helpers_collection.get_dataset_names(dict()) + nt.assert_equal(dataset_names, ['dataset 1', 'dataset 2', 'dataset 3']) + + def test_gather_collection_info_without_reference_package(self): + collection_info = helpers_collection.gather_collection_info( + self.collection_dict, self.datasets_in_collection, dataset_dict=None) + collection_info_expected = { + 'first_member': {'name': 'dataset 1', 'url': 'http://dataset 1'}, + 'last_member': {'name': 'dataset 3', 'url': 'http://dataset 3'}, + 'members': [ + {'name': 'dataset 1'}, + {'name': 'dataset 2'}, + {'name': 'dataset 3'}], + 'persistent_link_last_member': 'http://collection name/last', + 'predecessor': {'url': None}, + 'successor': {'url': None}, + 'title': 'collection title' + } + nt.assert_equal(collection_info, collection_info_expected) + + def test_gather_collection_info_with_reference_package_1(self): + collection_info = helpers_collection.gather_collection_info( + self.collection_dict, self.datasets_in_collection, dataset_dict={'name': 'dataset 1'}) + collection_info_expected = { + 'first_member': {'name': 'dataset 1', 'url': 'http://dataset 1'}, + 'last_member': {'name': 'dataset 3', 'url': 'http://dataset 3'}, + 'members': [ + {'name': 'dataset 1'}, + {'name': 'dataset 2'}, + {'name': 'dataset 3'}], + 'persistent_link_last_member': 'http://collection name/last', + 'predecessor': {'url': None}, + 'successor': {'url': 'http://dataset 2'}, + 'title': 'collection title' + } + nt.assert_equal(collection_info, collection_info_expected) + + def test_gather_collection_info_with_reference_package_2(self): + collection_info = helpers_collection.gather_collection_info( + self.collection_dict, self.datasets_in_collection, dataset_dict={'name': 'dataset 2'}) + collection_info_expected = { + 'first_member': {'name': 'dataset 1', 'url': 'http://dataset 1'}, + 'last_member': {'name': 'dataset 3', 'url': 'http://dataset 3'}, + 'members': [ + {'name': 'dataset 1'}, + {'name': 'dataset 2'}, + {'name': 'dataset 3'}], + 'persistent_link_last_member': 'http://collection name/last', + 'predecessor': {'url': 'http://dataset 1'}, + 'successor': {'url': 'http://dataset 3'}, + 'title': 'collection title' + } + nt.assert_equal(collection_info, collection_info_expected) + + def test_gather_collection_info_with_reference_package_3(self): + collection_info = helpers_collection.gather_collection_info( + self.collection_dict, self.datasets_in_collection, dataset_dict={'name': 'dataset 3'}) + collection_info_expected = { + 'first_member': {'name': 'dataset 1', 'url': 'http://dataset 1'}, + 'last_member': {'name': 'dataset 3', 'url': 'http://dataset 3'}, + 'members': [ + {'name': 'dataset 1'}, + {'name': 'dataset 2'}, + {'name': 'dataset 3'}], + 'persistent_link_last_member': 'http://collection name/last', + 'predecessor': {'url': 'http://dataset 2'}, + 'successor': {'url': None}, + 'title': 'collection title' + } + nt.assert_equal(collection_info, collection_info_expected) diff --git a/ckanext/odsh/tests_tpsh/test_odsh_helpers.py b/ckanext/odsh/tests_tpsh/test_odsh_helpers.py index a4c488230d254139ec4b3c7da1df93dd762a41dc..c11be582df29ba0bd8ebeb5390573950941c59e3 100644 --- a/ckanext/odsh/tests_tpsh/test_odsh_helpers.py +++ b/ckanext/odsh/tests_tpsh/test_odsh_helpers.py @@ -1,161 +1,39 @@ import datetime import nose.tools as nt -from mock import patch import ckan.lib.helpers as helpers import ckanext.odsh.helpers as odsh_helpers from ckanext.odsh.helpers import is_within_last_month -import ckanext.odsh.collection.helpers as helpers_collection import ckan.model as model -class Test_tpsh_get_successor_and_predecessor_dataset(object): - - def setUp(self): - - # construct datasets that shall be in following order: - # name date - # public2 2014-07-01 - # public3 2014-07-01 - # public4 2014-07-02 - # public1 2014-07-03 - - self.names_collection_members = [ - u'public3', u'public2', u'public1', u'public4', - u'private3', u'private2', u'private1'] - self.dates_collection_members = [ - u'2014-07-01T00:00:00', #public3 - u'2014-07-01T00:00:00', #public2 - u'2014-07-03T00:00:00', #public1 - u'2014-07-02T00:00:00', #public4 - u'2014-07-05T00:00:00', #private3 - u'2014-07-06T00:00:00', #private2 - u'2014-07-07T00:00:00', #private1 - ] - self.pkg_dicts_collection_members = [ - { - u'name': name, - u'extras': {u'issued': date} - } - for (name, date) in zip(self.names_collection_members, self.dates_collection_members) - ] - - def fake_access_checker(access_type, pkg_dict): - pkg_name = pkg_dict.get('name') - if 'public' in pkg_name: - return True - return False - - def fake_get_package_dict(name): - package_list = filter(lambda pkg_dict:pkg_dict.get('name')==name, self.pkg_dicts_collection_members) - return package_list[0] - - self.patch_get_datasets_belonging_to_collection_by_dataset = patch.object( - helpers_collection, - 'get_all_datasets_belonging_to_collection_by_dataset', - return_value = self.names_collection_members) - self.patch_get_datasets_belonging_to_collection_by_dataset.start() - - self.patch_check_access = patch.object( - helpers, - 'check_access', - new=fake_access_checker, - ) - self.patch_check_access.start() - - self.patch_get_package_dict = patch.object( - helpers_collection, - 'get_package_dict', - new=fake_get_package_dict, - ) - self.patch_get_package_dict.start() - - def tearDown(self): - self.patch_get_datasets_belonging_to_collection_by_dataset.stop() - self.patch_check_access.stop() - self.patch_get_package_dict.stop() - - def test_patch_get_datasets_belonging_to_collection_by_dataset(self): - return_value = helpers_collection.get_all_datasets_belonging_to_collection_by_dataset() - nt.assert_equal(return_value, self.names_collection_members) - - def test_patch_access_checker_returns_True(self): - pkg_dict = {u'name': u'public1'} - has_access = helpers.check_access('package_show', pkg_dict) - nt.assert_true(has_access) - - def test_patch_access_checker_returns_False(self): - pkg_dict = {u'name': u'private1'} - has_access = helpers.check_access('package_show', pkg_dict) - nt.assert_false(has_access) - - def test_patch_package_get(self): - pkg_dict = helpers_collection.get_package_dict('public1') - nt.assert_equal(pkg_dict.get('name'), 'public1') - - def test_it_returns_correct_for_public2(self): - pkg_dict = {u'name': u'public2'} - successor, predecessor = helpers_collection.get_successor_and_predecessor_dataset(pkg_dict) - nt.assert_equal(successor, 'public3') - nt.assert_equal(predecessor, None) - - def test_it_returns_correct_for_public3(self): - pkg_dict = {u'name': u'public3'} - successor, predecessor = helpers_collection.get_successor_and_predecessor_dataset(pkg_dict) - nt.assert_equal(successor, 'public4') - nt.assert_equal(predecessor, 'public2') - - def test_it_returns_correct_for_public4(self): - pkg_dict = {u'name': u'public4'} - successor, predecessor = helpers_collection.get_successor_and_predecessor_dataset(pkg_dict) - nt.assert_equal(successor, 'public1') - nt.assert_equal(predecessor, 'public3') - - def test_it_returns_correct_for_public1(self): - pkg_dict = {u'name': u'public1'} - successor, predecessor = helpers_collection.get_successor_and_predecessor_dataset(pkg_dict) - nt.assert_equal(successor, None) - nt.assert_equal(predecessor, 'public4') - - def test_it_returns_None_if_no_siblings(self): - with patch.object( - helpers_collection, - 'get_all_datasets_belonging_to_collection_by_dataset', - return_value = list() - ): - pkg_dict = {u'name': u'some_name'} - successor, predecessor = helpers_collection.get_successor_and_predecessor_dataset(pkg_dict) - nt.assert_equal(successor, None) - nt.assert_equal(predecessor, None) - - class Test_is_within_last_month(object): def test_it_returns_true_for_simple_query(self): date = datetime.date(2019, 4, 15) date_ref = datetime.date(2019, 4, 29) assert is_within_last_month(date, date_ref) - + def test_it_uses_today_if_date_ref_missing(self): date = datetime.date.today() - datetime.timedelta(days=20) assert is_within_last_month(date) - + def test_it_returns_true_for_dates_in_different_years(self): date = datetime.date(2018, 12, 16) date_ref = datetime.date(2019, 1, 15) assert is_within_last_month(date, date_ref) - + def test_it_returns_false_for_dates_in_different_years(self): date = datetime.date(2018, 12, 15) date_ref = datetime.date(2019, 1, 15) - assert is_within_last_month(date, date_ref)==False - + assert is_within_last_month(date, date_ref) == False + def test_it_returns_true_for_dates_in_differen_months(self): date = datetime.date(2018, 6, 16) date_ref = datetime.date(2018, 7, 10) assert is_within_last_month(date, date_ref) - + def test_it_return_false_for_date_in_different_months(self): date = datetime.date(2018, 6, 8) date_ref = datetime.date(2018, 7, 10) - assert is_within_last_month(date, date_ref)==False \ No newline at end of file + assert is_within_last_month(date, date_ref) == False diff --git a/ckanext/odsh/tests_tpsh/test_thumbnail.py b/ckanext/odsh/tests_tpsh/test_thumbnail.py new file mode 100644 index 0000000000000000000000000000000000000000..f12f0cbd06732483fce5c2ff84ebd7f2216d4eaf --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_thumbnail.py @@ -0,0 +1,275 @@ +# encoding: utf-8 + +import os +import os.path as path +from mock import patch, Mock, call +import nose.tools as nt +from ckan.common import config + +import ckanext.odsh.pdf_to_thumbnail.thumbnail as thumbnail +import ckanext.odsh.tests_tpsh.thumbnail_patches as p + + +class TestCreateThumbnailFromFile(p.WithTempFolder): + + def test_pass(self): + pass + + def test_thumbnail_for_test_pdf(self): + with open(os.path.join(p.resources_path, 'test.pdf')) as f: + filename_thumbnail = thumbnail._create_thumbnail_from_file(f) + nt.assert_true(path.exists( + path.join(p.thumbnail_folder, filename_thumbnail) + )) + + def test_thumbnail_for_ki_strategie(self): + with open(os.path.join(p.resources_path, 'ki_strategie.pdf')) as f: + thumbnail._create_thumbnail_from_file(f) + + +class TestGetFilepathToResource(p.WithTempFolder): + + def test_it_returns_correct_path(self): + resource = { + 'id': '3F2504E0-4F89-11D3-9A0C-0305E82C3301' + } + filepath = thumbnail.get_resource_path(resource) + filepath_expected = os.path.join( + p.temp_path, 'resources/3F2/504/E0-4F89-11D3-9A0C-0305E82C3301') + nt.assert_equal(filepath, filepath_expected) + + + +class TestChangeFilepath(p.WithTempFolder): + + def test_renames_thumbnail(self): + open(os.path.join(p.thumbnail_folder, 'some_filename'), 'a').close() + thumbnail.rename_thumbnail_to_random_name('some_filename') + thumbnails = os.listdir(p.thumbnail_folder) + nt.assert_equal(len(thumbnails), 1) + filename = thumbnails[0] + nt.assert_true(filename.startswith('thumbnail_picture_')) + nt.assert_true(filename.endswith('.jpg')) + + +class TestIsPdf(object): + + def test_true_for_pdf(self): + filepath = os.path.join(p.resources_path, 'test.pdf') + is_pdf = thumbnail._is_pdf(filepath) + nt.assert_true(is_pdf) + + def test_false_for_text_file(self): + filepath = os.path.join(p.resources_path, 'transparenz.rdf') + is_pdf = thumbnail._is_pdf(filepath) + nt.assert_false(is_pdf) + + +class TestCreateThumbnailFromMemory(p.WithTempFolder): + + @p.patch_get_resource_path + def test_creates_thumbnail_for_pdf(self, _patch): + is_pdf, filename = thumbnail._create_thumbnail_from_memory(dict(), '') + nt.assert_true(filename.startswith('thumbnail_picture_')) + nt.assert_true(filename.endswith('.jpg')) + nt.assert_true(is_pdf) + nt.assert_true(os.path.exists(os.path.join(p.thumbnail_folder, filename))) + + @p.patch_get_resource_path + def test_removes_old_thumbnail(self, _patch): + open(os.path.join(p.thumbnail_folder, 'old_thumbnail.jpg'), 'a').close() + thumbnail._create_thumbnail_from_memory(dict(), 'old_thumbnail') + thumbnails = os.listdir(p.thumbnail_folder) + nt.assert_equal(len(thumbnails), 1) + nt.assert_not_in('old_thumbnail.jpg', thumbnails) + + @p.patch_get_resource_path + def test_does_not_remove_old_thumbnail_for_textfile(self, _patch): + _patch.return_value = os.path.join(p.resources_path, 'transparenz.rdf') + open(os.path.join(p.thumbnail_folder, 'old_thumbnail.jpg'), 'a').close() + thumbnail._create_thumbnail_from_memory(dict(), 'old_thumbnail') + thumbnails = os.listdir(p.thumbnail_folder) + nt.assert_equal(len(thumbnails), 1) + nt.assert_in('old_thumbnail.jpg', thumbnails) + + + @p.patch_get_resource_path + def test_returns_None_for_textfile(self, _patch): + _patch.return_value = os.path.join(p.resources_path, 'transparenz.rdf') + is_pdf, filename = thumbnail._create_thumbnail_from_memory(dict(), '') + nt.assert_false(is_pdf) + nt.assert_is(filename, None) + + +class TestRemoveThumbnail(p.WithTempFolder): + + @p.patch_get_filename_from_context + def test_it_removes_thumbnail(self, _patch): + open(os.path.join(p.thumbnail_folder, 'some_thumbnail.jpg'), 'a').close() + thumbnails = os.listdir(p.thumbnail_folder) + nt.assert_equal(len(thumbnails), 1) + thumbnail.remove_thumbnail(dict()) + thumbnails = os.listdir(p.thumbnail_folder) + nt.assert_equal(len(thumbnails), 0) + + +class TestCreateThumbnail(object): + + @p.patch_create_thumbnail_from_memory + @p.patch_get_filename_from_context + def test_creates_thumbnail_from_memory(self, patch_gffc, patch_ctfm): + resource = { + 'url_type': 'upload', + } + thumbnail.create_thumbnail(dict(), resource) + patch_ctfm.assert_called_once_with(resource, 'some_thumbnail.jpg') + + @p.patch_get_filename_from_context + def test_returns_None_if_type_not_upload(self, patch_gffc): + resource = { + 'url_type': 'something', + } + is_pdf, filename = thumbnail.create_thumbnail(dict(), resource) + nt.assert_false(is_pdf) + nt.assert_equal(filename, None) + + +class TestThumbnailPath(p.WithTempFolder): + + def test_from_filename(self): + tp = thumbnail.ThumbnailPath.from_filename('filename') + nt.assert_equal(tp.folder, p.thumbnail_folder) + nt.assert_equal(tp.filename, 'filename') + nt.assert_equal(tp.extension, '.jpg') + + def test_from_filename_with_extension(self): + tp = thumbnail.ThumbnailPath.from_filename_with_extension('filename.jpg') + nt.assert_equal(tp.filename, 'filename') + nt.assert_equal(tp.extension, '.jpg') + + def test_from_filename_with_extension_no_extension(self): + tp = thumbnail.ThumbnailPath.from_filename_with_extension('filename') + nt.assert_equal(tp.filename, 'filename') + nt.assert_equal(tp.extension, '') + + def test_full_filename(self): + tp = thumbnail.ThumbnailPath('folder', 'filename', '.jpg') + nt.assert_equal(tp.full_filename, 'folder/filename.jpg') + + @p.patch_random_generator + def test_from_random_name(self, _patch): + tp = thumbnail.ThumbnailPath._from_random_name() + nt.assert_equal(tp.folder, p.thumbnail_folder) + nt.assert_equal(tp.filename, 'thumbnail_picture_some_random') + nt.assert_equal(tp.extension, '.jpg') + + +class TestWriteThumbnailIntoPackage(object): + + def setUp(self): + p.mock_action_handler.reset_mock() + p.mock_action_handler.return_value = {'id': 'foo'} + self.context = { + 'package': Mock(id='foo') + } + + @p.patch_get_action + def test_with_filename(self, _patch): + thumbnail._write_thumbnail_into_package(self.context, 'filename') + _patch.assert_has_calls([ + call('package_show'), + call('package_update') + ]) + p.mock_action_handler.assert_has_calls([ + call(None, {'id': 'foo'}), + call(None, {'id': 'foo', 'thumbnail': 'filename'}) + ]) + + @p.patch_get_action + def test_without_filename(self, _patch): + thumbnail._write_thumbnail_into_package(self.context, '') + _patch.assert_has_calls([ + call('package_show'), + call('package_update') + ]) + p.mock_action_handler.assert_has_calls([ + call(None, {'id': 'foo'}), + call(None, {'id': 'foo'}) + ]) + + +class TestCreateThumbnailIfNoneInPackage(object): + + def setUp(self): + p.mock_action_handler.reset_mock() + p.mock_action_handler.return_value = { + 'id': 'foo', + 'resources': [ + {'id': 'id1'}, + {'id': 'id2'}, + ] + } + self.context = { + 'package': Mock(id='foo') + } + + @p.patch_get_action + @p.patch_create_thumbnail + @p.patch_get_package_dict_from_context + def test_creates_thumbnail_for_first_resource(self, patch_gpdfc, patch_ct, patch_ga): + patch_ct.side_effect = [(True, 'filename1'), (True, 'filename2')] + patch_gpdfc.return_value = {} + thumbnail.create_thumbnail_if_none_in_package(self.context, ['resource1', 'resource2']) + patch_ct.assert_called_once_with(self.context, 'resource1') + + @p.patch_get_action + @p.patch_create_thumbnail + @p.patch_get_package_dict_from_context + def test_creates_thumbnail_for_first_pdf(self, patch_gpdfc, patch_ct, patch_ga): + patch_ct.side_effect = [(False, 'filename1'), (True, 'filename2'), (True, 'filename3')] + patch_gpdfc.return_value = {} + thumbnail.create_thumbnail_if_none_in_package(self.context, ['resource1', 'resource2', 'resource3']) + patch_ct.assert_called_with(self.context, 'resource2') + + @p.patch_get_action + @p.patch_create_thumbnail + @p.patch_get_package_dict_from_context + def test_does_not_create_thumbnail_if_exists(self, patch_gpdfc, patch_ct, patch_ga): + patch_gpdfc.return_value = { + 'thumbnail': 'foo.jpg', + } + thumbnail.create_thumbnail_if_none_in_package(self.context, ['resource1', 'resource2']) + patch_ct.assert_not_called() + + @p.patch_get_action + @p.patch_create_thumbnail + @p.patch_get_package_dict_from_context + def test_does_not_create_thumbnail_if_no_resources(self, patch_gpdfc, patch_ct, patch_ga): + patch_gpdfc.return_value = {} + thumbnail.create_thumbnail_if_none_in_package(self.context, []) + patch_ct.assert_not_called() + + @p.patch_write_thumbnail_into_package + @p.patch_get_action + @p.patch_create_thumbnail + @p.patch_get_package_dict_from_context + def test_writes_filename_into_package(self, patch_gpdfc, patch_ct, patch_ga, patch_wtip): + patch_ct.side_effect = [(True, 'filename1'), (True, 'filename2')] + patch_gpdfc.return_value = {} + thumbnail.create_thumbnail_if_none_in_package(self.context, ['resource1']) + patch_wtip.assert_called_once_with(self.context, 'filename1') + + +class TestHasThumbnail(object): + + def test_true_for_nonempty_string(self): + nt.assert_true(thumbnail._has_thumbnail({'thumbnail': 'foo'})) + + def test_false_for_None(self): + nt.assert_false(thumbnail._has_thumbnail({'thumbnail': None})) + + def test_false_for_empty_string(self): + nt.assert_false(thumbnail._has_thumbnail({'thumbnail': ''})) + + def test_false_if_not_in_dict(self): + nt.assert_false(thumbnail._has_thumbnail({})) \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/test_thumbnail_plugin.py b/ckanext/odsh/tests_tpsh/test_thumbnail_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..59ada72ca6f995fce2b37a74df854a42fc5fe74b --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_thumbnail_plugin.py @@ -0,0 +1,59 @@ +from mock import call +import nose.tools as nt + +import ckanext.odsh.tests_tpsh.thumbnail_patches as p +from ckanext.odsh.pdf_to_thumbnail.plugin import ThumbnailPlugin + +class TestAfterCreateFrontend(p.WithAfterCreateDataFrontend): + + def setUp(self): + super(TestAfterCreateFrontend, self).setUp() + p.mock_action_handler.reset_mock() + p.mock_action_handler.return_value = self.package_dict + + @p.patch_write_thumbnail_into_package + @p.patch_create_thumbnail_from_memory + @p.patch_get_action + def test_with_data_from_frontend(self, patch_ga, patch_ctfm, patch_wtip): + ThumbnailPlugin().after_create(self.context, self.resource) + patch_ctfm.assert_called_once_with(self.package_dict.get('resources')[0], None) + patch_ga.assert_has_calls([ + call('package_show'), + call('package_show'), + call('package_show'), + ]) + patch_wtip.assert_called_once_with(self.context, 'filename') + + +class TestAfterCreateAPI(p.WithAfterCreateDataAPI): + + def setUp(self): + super(TestAfterCreateAPI, self).setUp() + p.mock_action_handler.reset_mock() + p.mock_action_handler.return_value = self.package_dict + + @p.patch_write_thumbnail_into_package + @p.patch_create_thumbnail_from_memory + @p.patch_get_action + def test_with_data_from_API_first_resource(self, patch_ga, patch_ctfm, patch_wtip): + ThumbnailPlugin().after_create(self.context, self.resource) + patch_ctfm.assert_called_once_with(self.package_dict.get('resources')[0], None) + patch_ga.assert_has_calls([ + call('package_show'), + call('package_show'), + call('package_show'), + ]) + patch_wtip.assert_called_once_with(self.context, 'filename') + + @p.patch_write_thumbnail_into_package + @p.patch_create_thumbnail_from_memory + @p.patch_get_action + def test_with_data_from_API_second_resource(self, patch_ga, patch_ctfm, patch_wtip): + p.mock_action_handler.return_value = self.package_dict_second_call + ThumbnailPlugin().after_create(self.context, self.resource) + patch_ga.assert_has_calls([ + call('package_show'), + call('package_show'), + ]) + patch_ctfm.assert_not_called() + patch_wtip.assert_not_called() diff --git a/ckanext/odsh/tests_tpsh/test_validation.py b/ckanext/odsh/tests_tpsh/test_validation.py index 3c4d145405acb554fe8e4d233654d768f9fbe266..af27c9ddce151f41aff3537bfffa69021fbe3f25 100644 --- a/ckanext/odsh/tests_tpsh/test_validation.py +++ b/ckanext/odsh/tests_tpsh/test_validation.py @@ -1,9 +1,20 @@ import nose.tools as nt from ckan.common import config -from .test_validation_mocks import WithFrontendValidationMocks, WithAPIValidationMocks -from ckanext.odsh.validation import validate_licenseAttributionByText, validate_extra_groups +import ckan.logic as logic import ckan.plugins.toolkit as toolkit +from .test_validation_mocks import ( + WithFrontendValidationMocks, + WithAPIValidationMocks, + patch_get_dataset, + patch_get_dataset_not_found, +) +from ckanext.odsh.validation import ( + validate_licenseAttributionByText, + validate_extra_groups, + validate_relatedPackage, +) + class Test_validate_licenseAttributionByText(WithFrontendValidationMocks): def test_it_passes(self): @@ -196,3 +207,27 @@ class Test_validate_extra_groupsAPI(WithAPIValidationMocks): errors=errors ) nt.assert_equal(len(errors), 0) + + + +class Test_validate_relatedPackage(object): + + @patch_get_dataset_not_found + def test_passes_if_empty(self, patch_gd): + validate_relatedPackage(None) + validate_relatedPackage('') + + + @patch_get_dataset + def test_calls_get_dataset(self, patch_gd): + validate_relatedPackage('some_id') + patch_gd.assert_called_once_with('some_id') + + + @patch_get_dataset_not_found + def test_throws_error_if_package_does_not_exist(self, patch_gd): + with nt.assert_raises(toolkit.Invalid) as err: + validate_relatedPackage('some_id') + nt.assert_equal( + err.exception.error, "relatedPackage: package 'some_id' not found") + \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/test_validation_mocks.py b/ckanext/odsh/tests_tpsh/test_validation_mocks.py index 6d61d04e4125ac7dd352fd048bb8d9d7a216d4de..550dca4592c8c6c98e506f324ba7fc9d0a5fb2d4 100644 --- a/ckanext/odsh/tests_tpsh/test_validation_mocks.py +++ b/ckanext/odsh/tests_tpsh/test_validation_mocks.py @@ -1,6 +1,10 @@ import os from ckan.common import config from ckan.lib.navl.dictization_functions import Missing +import ckan.logic as logic +from mock import patch + +import ckanext.odsh.validation as validation def resource_dir(): @@ -314,3 +318,16 @@ class WithAPIValidationMocks(WithConfig): } self.errors_mock = {} + + +patch_get_dataset = patch.object( + validation, + 'get_package_dict', +) + + +patch_get_dataset_not_found = patch.object( + validation, + 'get_package_dict', + side_effect=logic.NotFound, +) \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/thumbnail_patches.py b/ckanext/odsh/tests_tpsh/thumbnail_patches.py new file mode 100644 index 0000000000000000000000000000000000000000..ed3bbf2b7dc214ffc20a9747ad06bdf5024a9fab --- /dev/null +++ b/ckanext/odsh/tests_tpsh/thumbnail_patches.py @@ -0,0 +1,531 @@ +import os +import os.path as path +from shutil import rmtree +from mock import Mock, patch + +from ckan.common import config +import ckan.plugins.toolkit as toolkit + +import ckanext.odsh.pdf_to_thumbnail.thumbnail as thumbnail + + +module_path = path.abspath(__file__) +dir_path = path.dirname(module_path) +resources_path = path.join(dir_path, 'resources') +temp_path = path.join(dir_path, 'temp_thumbnail') +thumbnail_folder = path.join(temp_path, 'thumbnail') + + +patch_get_resource_path = patch.object( + thumbnail, + 'get_resource_path', + return_value=os.path.join(resources_path, 'test_copy_pdf') +) + + +patch_get_filename_from_context = patch.object( + thumbnail, + '_get_filename_from_context', + return_value='some_thumbnail.jpg' +) + + +patch_create_thumbnail_from_memory = patch.object( + thumbnail, + '_create_thumbnail_from_memory', + return_value=(True, 'filename'), +) + + +patch_random_generator = patch.object( + thumbnail, + 'b2a_hex', + return_value='some_random', +) + + +mock_action_handler = Mock() +patch_get_action = patch.object( + toolkit, 'get_action', return_value=mock_action_handler) + + +patch_create_thumbnail = patch.object( + thumbnail, 'create_thumbnail', + side_effect=[(True, 'filename'), ] +) + + +patch_write_thumbnail_into_package = patch.object( + thumbnail, '_write_thumbnail_into_package') + + +patch_remove_thumbnail = patch.object(thumbnail, 'remove_thumbnail') + + +patch_get_package_dict_from_context = patch.object(thumbnail, '_get_package_dict_from_context', + return_value={}) + + +patch_is_pdf = patch.object(thumbnail, '_is_pdf', return_value=True) + + +class WithTempFolder(object): + + def setUp(self): + config.update({ + 'ckan.thumbnail.size.width': 410, + 'ckan.storage_path': temp_path, + }) + if not path.exists(temp_path): + os.mkdir(temp_path) + if not path.exists(thumbnail_folder): + os.mkdir(thumbnail_folder) + + def tearDown(self): + config.clear() + rmtree(temp_path) + + +class WithAfterCreateDataFrontend(object): + + def setUp(self): + config.update({ + 'ckan.thumbnail.size.width': 410, + 'ckan.storage_path': temp_path, + }) + + # trapped in plugin.py + self.resource = { + u'cache_last_updated': None, + u'cache_url': None, + u'created': '2020-05-14T12:34:19.974216', + u'datastore_active': False, + u'description': u'', + u'format': u'PDF', + u'hash': u'', + u'id': u'3ec53c20-f038-4ad6-a9fd-265cbe6faa27', + u'last_modified': '2020-05-14T12:34:19.817460', + u'mimetype': u'application/pdf', + u'mimetype_inner': None, + u'name': u'Checkliste-Barrierefreies-PDF.pdf', + u'package_id': u'ff55a3ee-6dcd-4f76-b5a3-08055d5241f4', + u'position': 0, + u'resource_type': None, + u'revision_id': u'0c3b309e-5179-4b4d-91f6-26221bba85e1', + u'size': 112437L, + u'state': u'active', + u'url': u'http://192.168.152.133:5000/dataset/ff55a3ee-6dcd-4f76-b5a3-08055d5241f4/resource/3ec53c20-f038-4ad6-a9fd-265cbe6faa27/download/checkliste-barrierefreies-pdf.pdf', + u'url_type': u'upload' + } + self.context = { + 'package': Mock( + id=u'ff55a3ee-6dcd-4f76-b5a3-08055d5241f4', + is_private=False, + private=False, + type=u'dataset', + url=None, + ), + 'resources': [ + Mock( + id=u'3ec53c20-f038-4ad6-a9fd-265cbe6faa27', + url_type=u'upload', + ) + ] + } + + # return value of resources_of_containing_package + self.resources = [ + { + u'cache_last_updated': None, + u'cache_url': None, + u'created': '2020-05-14T12:34:19.974216', + u'datastore_active': False, + u'description': u'', + u'format': u'PDF', + u'hash': u'', + u'id': u'3ec53c20-f038-4ad6-a9fd-265cbe6faa27', + u'last_modified': '2020-05-14T12:34:19.817460', + u'mimetype': u'application/pdf', + u'mimetype_inner': None, + u'name': u'Checkliste-Barrierefreies-PDF.pdf', + u'package_id': u'ff55a3ee-6dcd-4f76-b5a3-08055d5241f4', + u'position': 0, + u'resource_type': None, + u'revision_id': u'0c3b309e-5179-4b4d-91f6-26221bba85e1', + u'revision_timestamp': u'2020-05-14T12:34:19.950162', + u'size': 112437L, + u'state': u'active', + u'url': u'http://192.168.152.133:5000/dataset/ff55a3ee-6dcd-4f76-b5a3-08055d5241f4/resource/3ec53c20-f038-4ad6-a9fd-265cbe6faa27/download/checkliste-barrierefreies-pdf.pdf', + u'url_type': u'upload' + } + ] + + # return value of _get_package_dict_from_context + self.package_dict = { + 'author': None, + 'author_email': None, + 'creator_user_id': u'ff8d002d-3908-45e5-ba7b-445381860957', + 'extras': [{u'key': u'groups', u'value': u''}, + {u'key': u'issued', u'value': u'2020-05-14T00:00:00'}, + {u'key': u'licenseAttributionByText', u'value': u''}, + {u'key': u'spatial', + u'value': u'{"type": "Polygon", "coordinates": [[[9.5557, 54.6361], [9.5576, 54.6395], [9.5739, 54.6428], [9.593, 54.6508], [9.6041, 54.6497], [9.6049, 54.6533], [9.6132, 54.6556], [9.6261, 54.6632], [9.627, 54.6671], [9.6349, 54.6672], [9.6264, 54.6548], [9.6257, 54.6494], [9.6235, 54.6496], [9.624, 54.6426], [9.619, 54.6349], [9.6231, 54.6354], [9.6222, 54.6343], [9.6263, 54.6338], [9.6441, 54.6399], [9.6459, 54.6378], [9.6529, 54.6368], [9.657, 54.6327], [9.6629, 54.6312], [9.667, 54.6281], [9.6699, 54.6283], [9.6703, 54.6249], [9.6874, 54.6304], [9.6963, 54.6245], [9.6948, 54.6183], [9.7011, 54.6132], [9.6962, 54.6101], [9.6933, 54.6125], [9.6904, 54.6116], [9.6867, 54.6085], [9.6897, 54.6038], [9.6826, 54.6007], [9.6881, 54.599], [9.6857, 54.5943], [9.6911, 54.5913], [9.695, 54.591], [9.6965, 54.5932], [9.7065, 54.5957], [9.7101, 54.5935], [9.7148, 54.5931], [9.7169, 54.5948], [9.7232, 54.5928], [9.7241, 54.587], [9.718, 54.585], [9.7195, 54.5831], [9.7159, 54.5817], [9.7186, 54.5783], [9.7162, 54.5746], [9.7194, 54.5694], [9.7125, 54.5655], [9.7227, 54.5599], [9.7218, 54.5544], [9.7265, 54.5521], [9.726, 54.5492], [9.7289, 54.5497], [9.7322, 54.5478], [9.7413, 54.5402], [9.7412, 54.5375], [9.7374, 54.5357], [9.7389, 54.5321], [9.7373, 54.5303], [9.7211, 54.5299], [9.718, 54.5286], [9.714, 54.5187], [9.7119, 54.5183], [9.7024, 54.5179], [9.6932, 54.5224], [9.6731, 54.5153], [9.6439, 54.5155], [9.6344, 54.511], [9.6252, 54.5159], [9.6159, 54.5159], [9.616, 54.5236], [9.6035, 54.5269], [9.6036, 54.5282], [9.5986, 54.5282], [9.5927, 54.526], [9.5916, 54.5342], [9.5941, 54.5354], [9.5869, 54.5374], [9.5917, 54.5454], [9.5849, 54.5459], [9.576, 54.544], [9.5757, 54.5422], [9.5679, 54.5444], [9.5598, 54.5417], [9.5487, 54.5438], [9.546, 54.5413], [9.5472, 54.5402], [9.5434, 54.5391], [9.5456, 54.5394], [9.5445, 54.5367], [9.5399, 54.5357], [9.5364, 54.5393], [9.538, 54.5414], [9.5313, 54.5449], [9.5347, 54.5467], [9.5286, 54.5467], [9.5291, 54.5454], [9.5263, 54.5447], [9.5154, 54.5442], [9.5127, 54.5454], [9.5123, 54.5485], [9.5079, 54.549], [9.5054, 54.5526], [9.4992, 54.5549], [9.4941, 54.5647], [9.4899, 54.5617], [9.4798, 54.5643], [9.4824, 54.5965], [9.472, 54.5981], [9.4581, 54.5978], [9.4566, 54.6104], [9.4594, 54.6117], [9.4708, 54.6073], [9.4856, 54.6099], [9.491, 54.6094], [9.5065, 54.6153], [9.5059, 54.6205], [9.4983, 54.6206], [9.4995, 54.6246], [9.5109, 54.6234], [9.5116, 54.6258], [9.5009, 54.644], [9.4923, 54.6453], [9.4968, 54.6488], [9.4947, 54.6503], [9.493, 54.6494], [9.4844, 54.656], [9.4759, 54.6758], [9.5057, 54.6693], [9.5166, 54.6771], [9.5156, 54.6792], [9.5206, 54.6786], [9.5256, 54.6821], [9.5302, 54.683], [9.5301, 54.6842], [9.5359, 54.6846], [9.5297, 54.6703], [9.5299, 54.6676], [9.5328, 54.6672], [9.5328, 54.6652], [9.537, 54.6626], [9.5314, 54.6618], [9.5305, 54.6595], [9.5377, 54.6598], [9.5411, 54.6518], [9.5499, 54.6507], [9.5485, 54.6478], [9.5507, 54.6447], [9.5471, 54.6401], [9.5508, 54.6367], [9.5557, 54.6361]]]}'}, + {u'key': u'spatial_text', u'value': u'Amt S\xfcdangeln'}, + {u'key': u'spatial_uri', + u'value': u'http://dcat-ap.de/def/politicalGeocoding/regionalKey/010595987'}, + {u'key': u'subject_text', u'value': u'Brosch\xfcre'}, + {u'key': u'temporal_end', u'value': u'2020-05-31T00:00:00'}, + {u'key': u'temporal_start', u'value': u'2020-05-01T00:00:00'}], + 'groups': [], + 'id': u'ff55a3ee-6dcd-4f76-b5a3-08055d5241f4', + 'is_new': True, + 'isopen': True, + 'language': u'http://publications.europa.eu/resource/authority/language/DEU', + 'license_id': u'http://dcat-ap.de/def/licenses/dl-zero-de/2.0', + 'license_title': u'Datenlizenz Deutschland \u2013 Zero \u2013 Version 2.0', + 'license_url': u'https://www.govdata.de/dl-de/zero-2-0', + 'maintainer': None, + 'maintainer_email': None, + 'metadata_created': '2020-05-14T12:34:05.551655', + 'metadata_modified': '2020-05-14T12:34:20.551455', + 'name': u'test-thumbnail', + 'notes': u'Test-Package f\xfcr Thumbnail-Gernerierung', + 'num_resources': 1, + 'num_tags': 0, + 'organization': {u'approval_status': u'approved', + u'created': '2019-07-29T08:11:32.697127', + u'description': u'', + u'id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + u'image_url': u'', + u'is_organization': True, + u'name': u'test-organisation', + u'revision_id': u'3040af6c-d3f6-462d-b48a-329d63e17a28', + u'state': u'active', + u'title': u'Test-Organisation', + u'type': u'organization'}, + 'owner_org': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + 'private': False, + 'relationships_as_object': [], + 'relationships_as_subject': [], + 'resources': [{u'cache_last_updated': None, + u'cache_url': None, + u'created': '2020-05-14T12:34:19.974216', + u'datastore_active': False, + u'description': u'', + u'format': u'PDF', + u'hash': u'66123edf64fabf1c073fc45478bf4a57', + u'hash_algorithm': u'http://dcat-ap.de/def/hashAlgorithms/md/5', + u'id': u'3ec53c20-f038-4ad6-a9fd-265cbe6faa27', + u'last_modified': '2020-05-14T12:34:19.817460', + u'mimetype': u'application/pdf', + u'mimetype_inner': None, + u'name': u'Checkliste-Barrierefreies-PDF.pdf', + u'number_of_pages': u'3', + u'package_id': u'ff55a3ee-6dcd-4f76-b5a3-08055d5241f4', + u'position': 0, + u'resource_type': None, + u'revision_id': u'efba6d3b-6204-4872-b6d1-a9e7b6d05a35', + u'size': 112437L, + u'state': u'active', + u'url': u'http://192.168.152.133:5000/dataset/ff55a3ee-6dcd-4f76-b5a3-08055d5241f4/resource/3ec53c20-f038-4ad6-a9fd-265cbe6faa27/download/checkliste-barrierefreies-pdf.pdf', + u'url_type': u'upload'}], + 'revision_id': u'cdedc6a9-92d9-4a9a-9511-12c2458cbfe5', + 'state': u'draft', + 'subject': u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Broschuere', + 'tags': [], + 'title': u'Test Thumbnail', + 'type': u'dataset', + 'url': None, + 'version': None + } + + +class WithAfterCreateDataAPI(object): + + def setUp(self): + config.update({ + 'ckan.thumbnail.size.width': 410, + 'ckan.storage_path': temp_path, + }) + + self.resource = { + u'cache_last_updated': None, + u'cache_url': None, + u'created': '2020-05-18T12:06:59.559870', + u'datastore_active': False, + u'description': u'', + u'format': u'PDF', + u'hash': u'', + u'id': u'341d8f18-00fc-4cd8-b6b1-0fc68f6b1c2e', + u'last_modified': '2020-05-18T12:06:59.487842', + u'mimetype': u'application/pdf', + u'mimetype_inner': None, + u'name': None, + u'package_id': u'18eab091-64ce-44ad-b1cb-c8801017ce90', + u'position': 0, + u'resource_type': None, + u'revision_id': u'5cdf9119-e88d-4a7e-a737-6b3f8c2b3877', + u'size': 222997L, + u'state': u'active', + u'url': u'http://192.168.152.133:5000/dataset/18eab091-64ce-44ad-b1cb-c8801017ce90/resource/341d8f18-00fc-4cd8-b6b1-0fc68f6b1c2e/download/manual.pdf', + u'url_type': u'upload' + } + + self.resources = [{ + u'cache_last_updated': None, + u'cache_url': None, + u'created': '2020-05-18T12:06:59.559870', + u'datastore_active': False, + u'description': u'', + u'format': u'PDF', + u'hash': u'', + u'id': u'341d8f18-00fc-4cd8-b6b1-0fc68f6b1c2e', + u'last_modified': '2020-05-18T12:06:59.487842', + u'mimetype': u'application/pdf', + u'mimetype_inner': None, + u'name': None, + u'package_id': u'18eab091-64ce-44ad-b1cb-c8801017ce90', + u'position': 0, + u'resource_type': None, + u'revision_id': u'5cdf9119-e88d-4a7e-a737-6b3f8c2b3877', + u'revision_timestamp': u'2020-05-18T12:06:59.538388', + u'size': 222997L, + u'state': u'active', + u'url': u'http://192.168.152.133:5000/dataset/18eab091-64ce-44ad-b1cb-c8801017ce90/resource/341d8f18-00fc-4cd8-b6b1-0fc68f6b1c2e/download/manual.pdf', + u'url_type': u'upload' + }] + + self.resources_second_call = [ + { + u'cache_last_updated': None, + u'cache_url': None, + u'created': '2020-05-18T12:24:19.871546', + u'datastore_active': False, + u'description': u'', + u'format': u'PDF', + u'hash': u'f6c4aea4feff08461c5847788ec58c16', + u'hash_algorithm': u'http://dcat-ap.de/def/hashAlgorithms/md/5', + u'id': u'ddda4166-6618-4708-96ed-edb000096b58', + u'last_modified': '2020-05-18T12:24:19.801966', + u'mimetype': u'application/pdf', + u'mimetype_inner': None, + u'name': None, + u'number_of_pages': u'11', + u'package_id': u'941e2cc8-491c-48c5-8007-8a99041e230d', + u'position': 0, + u'resource_type': None, + u'revision_id': u'60174ab3-419c-48dc-a56a-2bb633bbe6cb', + u'revision_timestamp': u'2020-05-18T12:24:20.307640', + u'size': 222997L, + u'state': u'active', + u'url': u'http://192.168.152.133:5000/dataset/941e2cc8-491c-48c5-8007-8a99041e230d/resource/ddda4166-6618-4708-96ed-edb000096b58/download/manual.pdf', + u'url_type': u'upload'}, + { + u'cache_last_updated': None, + u'cache_url': None, + u'created': '2020-05-18T12:26:24.185953', + u'datastore_active': False, + u'description': u'', + u'format': u'PDF', + u'hash': u'', + u'id': u'8697b2f0-27b0-43ff-acfc-b7a1803db3b7', + u'last_modified': '2020-05-18T12:26:24.113533', + u'mimetype': u'application/pdf', + u'mimetype_inner': None, + u'name': None, + u'package_id': u'941e2cc8-491c-48c5-8007-8a99041e230d', + u'position': 1, + u'resource_type': None, + u'revision_id': u'3225d910-6a0d-48c9-8bb8-a362c7c94d59', + u'revision_timestamp': u'2020-05-18T12:26:24.163859', + u'size': 222997L, + u'state': u'active', + u'url': u'http://192.168.152.133:5000/dataset/941e2cc8-491c-48c5-8007-8a99041e230d/resource/8697b2f0-27b0-43ff-acfc-b7a1803db3b7/download/manual.pdf', + u'url_type': u'upload' + } + ] + + self.context = { + 'package': Mock( + id=u'18eab091-64ce-44ad-b1cb-c8801017ce90', + is_private=False, + private=False, + type=u'dataset', + url=None, + ), + 'resources': [ + Mock( + id=u'341d8f18-00fc-4cd8-b6b1-0fc68f6b1c2e', + url_type=u'upload', + ) + ] + } + + self.package_dict = { + 'author': None, + 'author_email': None, + 'creator_user_id': u'ff8d002d-3908-45e5-ba7b-445381860957', + 'extras': [{u'key': u'issued', u'value': u'2019-07-07T00:00:00'}, + {u'key': u'licenseAttributionByText', u'value': u''}, + {u'key': u'subject_text', u'value': u'Verwaltungsvorschrift'}], + 'groups': [], + 'id': u'18eab091-64ce-44ad-b1cb-c8801017ce90', + 'is_new': False, + 'isopen': True, + 'language': u'http://publications.europa.eu/resource/authority/language/DEU', + 'license_id': u'http://dcat-ap.de/def/licenses/dl-zero-de/2.0', + 'license_title': u'Datenlizenz Deutschland \u2013 Zero \u2013 Version 2.0', + 'license_url': u'https://www.govdata.de/dl-de/zero-2-0', + 'maintainer': None, + 'maintainer_email': None, + 'metadata_created': '2020-05-18T12:06:59.137351', + 'metadata_modified': '2020-05-18T12:07:00.060791', + 'name': u'test-resource1', + 'notes': u'Testdatensatz aus Skript', + 'num_resources': 1, + 'num_tags': 0, + 'organization': {u'approval_status': u'approved', + u'created': '2019-07-29T08:11:32.697127', + u'description': u'', + u'id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + u'image_url': u'', + u'is_organization': True, + u'name': u'test-organisation', + u'revision_id': u'3040af6c-d3f6-462d-b48a-329d63e17a28', + u'state': u'active', + u'title': u'Test-Organisation', + u'type': u'organization'}, + 'owner_org': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + 'private': False, + 'relationships_as_object': [], + 'relationships_as_subject': [], + 'resources': [{u'cache_last_updated': None, + u'cache_url': None, + u'created': '2020-05-18T12:06:59.559870', + u'datastore_active': False, + u'description': u'', + u'format': u'PDF', + u'hash': u'f6c4aea4feff08461c5847788ec58c16', + u'hash_algorithm': u'http://dcat-ap.de/def/hashAlgorithms/md/5', + u'id': u'341d8f18-00fc-4cd8-b6b1-0fc68f6b1c2e', + u'last_modified': '2020-05-18T12:06:59.487842', + u'mimetype': u'application/pdf', + u'mimetype_inner': None, + u'name': None, + u'number_of_pages': u'11', + u'package_id': u'18eab091-64ce-44ad-b1cb-c8801017ce90', + u'position': 0, + u'resource_type': None, + u'revision_id': u'33c0df08-52cc-4774-b8d2-b283038b2891', + u'size': 222997L, + u'state': u'active', + u'url': u'http://192.168.152.133:5000/dataset/18eab091-64ce-44ad-b1cb-c8801017ce90/resource/341d8f18-00fc-4cd8-b6b1-0fc68f6b1c2e/download/manual.pdf', + u'url_type': u'upload'}], + 'revision_id': u'81152f7d-b942-42db-b1f1-f9e61cdfc43f', + 'state': u'active', + 'subject': u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Verwaltungsvorschrift', + 'tags': [], + 'title': u'test resource', + 'type': u'dataset', + 'url': None, + 'version': None + } + + self.package_dict_second_call = { + 'author': None, + 'author_email': None, + 'creator_user_id': u'ff8d002d-3908-45e5-ba7b-445381860957', + 'extras': [{u'key': u'issued', u'value': u'2019-07-07T00:00:00'}, + {u'key': u'licenseAttributionByText', u'value': u''}, + {u'key': u'subject_text', u'value': u'Verwaltungsvorschrift'}], + 'groups': [], + 'id': u'941e2cc8-491c-48c5-8007-8a99041e230d', + 'is_new': False, + 'isopen': True, + 'language': u'http://publications.europa.eu/resource/authority/language/DEU', + 'license_id': u'http://dcat-ap.de/def/licenses/dl-zero-de/2.0', + 'license_title': u'Datenlizenz Deutschland \u2013 Zero \u2013 Version 2.0', + 'license_url': u'https://www.govdata.de/dl-de/zero-2-0', + 'maintainer': None, + 'maintainer_email': None, + 'metadata_created': '2020-05-18T12:24:19.451803', + 'metadata_modified': '2020-05-18T12:26:24.637389', + 'name': u'test-resource3', + 'notes': u'Testdatensatz aus Skript', + 'num_resources': 2, + 'num_tags': 0, + 'organization': {u'approval_status': u'approved', + u'created': '2019-07-29T08:11:32.697127', + u'description': u'', + u'id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + u'image_url': u'', + u'is_organization': True, + u'name': u'test-organisation', + u'revision_id': u'3040af6c-d3f6-462d-b48a-329d63e17a28', + u'state': u'active', + u'title': u'Test-Organisation', + u'type': u'organization'}, + 'owner_org': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + 'private': False, + 'relationships_as_object': [], + 'relationships_as_subject': [], + 'resources': [ + { + u'cache_last_updated': None, + u'cache_url': None, + u'created': '2020-05-18T12:24:19.871546', + u'datastore_active': False, + u'description': u'', + u'format': u'PDF', + u'hash': u'f6c4aea4feff08461c5847788ec58c16', + u'hash_algorithm': u'http://dcat-ap.de/def/hashAlgorithms/md/5', + u'id': u'ddda4166-6618-4708-96ed-edb000096b58', + u'last_modified': '2020-05-18T12:24:19.801966', + u'mimetype': u'application/pdf', + u'mimetype_inner': None, + u'name': None, + u'number_of_pages': u'11', + u'package_id': u'941e2cc8-491c-48c5-8007-8a99041e230d', + u'position': 0, + u'resource_type': None, + u'revision_id': u'60174ab3-419c-48dc-a56a-2bb633bbe6cb', + u'size': 222997L, + u'state': u'active', + u'url': u'http://192.168.152.133:5000/dataset/941e2cc8-491c-48c5-8007-8a99041e230d/resource/ddda4166-6618-4708-96ed-edb000096b58/download/manual.pdf', + u'url_type': u'upload' + }, + { + u'cache_last_updated': None, + u'cache_url': None, + u'created': '2020-05-18T12:26:24.185953', + u'datastore_active': False, + u'description': u'', + u'format': u'PDF', + u'hash': u'f6c4aea4feff08461c5847788ec58c16', + u'hash_algorithm': u'http://dcat-ap.de/def/hashAlgorithms/md/5', + u'id': u'8697b2f0-27b0-43ff-acfc-b7a1803db3b7', + u'last_modified': '2020-05-18T12:26:24.113533', + u'mimetype': u'application/pdf', + u'mimetype_inner': None, + u'name': None, + u'number_of_pages': u'11', + u'package_id': u'941e2cc8-491c-48c5-8007-8a99041e230d', + u'position': 1, + u'resource_type': None, + u'revision_id': u'c9157e0f-4516-462e-9fb2-ebfe51d17b82', + u'size': 222997L, + u'state': u'active', + u'url': u'http://192.168.152.133:5000/dataset/941e2cc8-491c-48c5-8007-8a99041e230d/resource/8697b2f0-27b0-43ff-acfc-b7a1803db3b7/download/manual.pdf', + u'url_type': u'upload' + } + ], + 'revision_id': u'68008d04-edcb-4040-bcec-b327ee39cdcc', + 'state': u'active', + 'subject': u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Verwaltungsvorschrift', + 'tags': [], + 'thumbnail': u'thumbnail_picture_35c5b0113d495444a843162e72fa53.jpg', + 'title': u'test resource', + 'type': u'dataset', + 'url': None, + 'version': None + } diff --git a/ckanext/odsh/tools.py b/ckanext/odsh/tools.py index ab7c1ba60885d1a4589b1bc533f9d22e4d74d27b..d6fa856871a78fda448cd49f289be6fc024cf2c9 100644 --- a/ckanext/odsh/tools.py +++ b/ckanext/odsh/tools.py @@ -1,5 +1,5 @@ import os -from ckanext.odsh.pdf_to_thumbnail.thumbnail import get_filepath_to_resource +from ckanext.odsh.pdf_to_thumbnail.thumbnail import get_resource_path from ckanext.odsh.lib.uploader import calculate_hash import ckan.plugins.toolkit as toolkit import magic @@ -12,7 +12,7 @@ def add_attributes_resources(context, resource): i = 0 for item in resources: if item.get('id') == resource.get('id'): - path = get_filepath_to_resource(resource) + path = get_resource_path(resource) if os.path.exists(path): with open(path, 'rb') as file: diff --git a/ckanext/odsh/validation.py b/ckanext/odsh/validation.py index 8c2f955ebdd6664beb4a22387ee68d4f191d56f9..6b248cf3e619002b01a1ad70ad0afcb6f75b1822 100644 --- a/ckanext/odsh/validation.py +++ b/ckanext/odsh/validation.py @@ -6,14 +6,14 @@ import urllib2 import json from itertools import count from dateutil.parser import parse +from pylons import config import ckan.plugins.toolkit as toolkit import ckan.model as model +import ckan.logic as logic from ckan.lib.navl.dictization_functions import Missing -from pylons import config - -import pdb +from ckanext.odsh.helpers_tpsh import get_package_dict _ = toolkit._ @@ -222,7 +222,6 @@ def known_spatial_uri(key, data, errors, context): poly = None # some harvesters might import a polygon directly... - # pdb.set_trace() poly = _extract_value(data, 'spatial') has_old_uri = False @@ -377,6 +376,14 @@ def validate_subject(key, flattened_data, errors, context): flattened_data = _convert_subjectID_to_subjectText(subject_id, flattened_data) +def validate_relatedPackage(data): + if data: + try: + get_package_dict(data) + except logic.NotFound: + raise toolkit.Invalid("relatedPackage: package '{}' not found".format(data)) + + def get_validators(): return { 'known_spatial_uri': known_spatial_uri, @@ -384,4 +391,5 @@ def get_validators(): 'odsh_validate_extras': validate_extras, 'validate_licenseAttributionByText': validate_licenseAttributionByText, 'tpsh_validate_subject': validate_subject, + 'tpsh_validate_relatedPackage': validate_relatedPackage, } diff --git a/test.log b/test.log deleted file mode 100644 index 094310a6f3a6b209217cf45ba0d6ffd594e46e13..0000000000000000000000000000000000000000 --- a/test.log +++ /dev/null @@ -1,46 +0,0 @@ -2019-04-25 12:18:32,933 WARNI [ckan.lib.celery_app] ckan.lib.celery_app is deprecated, use ckan.lib.jobs instead. -2019-04-25 12:18:33,115 DEBUG [ckanext.harvest.model] Harvest tables defined in memory -2019-04-25 12:18:33,124 DEBUG [ckanext.harvest.model] Harvest tables already exist -2019-04-25 12:18:33,154 DEBUG [ckanext.spatial.plugin] Setting up the spatial model -2019-04-25 12:18:33,159 DEBUG [ckanext.spatial.model.package_extent] Spatial tables defined in memory -2019-04-25 12:18:33,162 DEBUG [ckanext.spatial.model.package_extent] Spatial tables already exist -2019-04-25 12:18:36,194 ERROR [ckanext.odsh.plugin] got exception ... -... : Traceback (most recent call last): -... : File \\"/usr/lib/ckan/default/src/ckanext-odsh/ckanext/odsh/plugin.py\\", line 358, in before_search -... : raise BaseException('boom') -... : BaseException: boom -... : "; -2019-04-25 12:18:36,743 INFO [ckan.lib.base] /dataset render time 0.559 seconds -2019-04-25 12:18:37,307 INFO [ckan.lib.base] /api/i18n/de render time 0.007 seconds -2019-04-25 12:18:37,664 ERROR [ckanext.odsh.plugin] got exception ... -... : Traceback (most recent call last): -... : File \\"/usr/lib/ckan/default/src/ckanext-odsh/ckanext/odsh/plugin.py\\", line 358, in before_search -... : raise BaseException('boom') -... : BaseException: boom -... : "; -2019-04-25 12:18:37,887 INFO [ckan.lib.base] /dataset render time 0.230 seconds -2019-04-25 12:18:38,320 INFO [ckan.lib.base] /api/i18n/de render time 0.002 seconds -2019-04-25 12:18:38,913 ERROR [ckanext.odsh.plugin] got exception ... -... : Traceback (most recent call last): -... : File \\"/usr/lib/ckan/default/src/ckanext-odsh/ckanext/odsh/plugin.py\\", line 358, in before_search -... : raise BaseException('boom') -... : BaseException: boom -... : "; -2019-04-25 12:18:39,040 INFO [ckan.lib.base] /dataset render time 0.132 seconds -2019-04-25 12:18:39,518 INFO [ckan.lib.base] /api/i18n/de render time 0.002 seconds -2019-04-25 12:18:40,249 ERROR [ckanext.odsh.plugin] got exception ... -... : Traceback (most recent call last): -... : File \\"/usr/lib/ckan/default/src/ckanext-odsh/ckanext/odsh/plugin.py\\", line 358, in before_search -... : raise BaseException('boom') -... : BaseException: boom -... : "; -2019-04-25 12:18:40,394 INFO [ckan.lib.base] /dataset render time 0.151 seconds -2019-04-25 12:18:40,865 INFO [ckan.lib.base] /api/i18n/de render time 0.003 seconds -2019-04-25 12:18:41,044 ERROR [ckanext.odsh.plugin] got exception ... -... : Traceback (most recent call last): -... : File \\"/usr/lib/ckan/default/src/ckanext-odsh/ckanext/odsh/plugin.py\\", line 358, in before_search -... : raise BaseException('boom') -... : BaseException: boom -... : "; -2019-04-25 12:18:41,194 INFO [ckan.lib.base] /dataset render time 0.158 seconds -2019-04-25 12:18:41,606 INFO [ckan.lib.base] /api/i18n/de render time 0.002 seconds diff --git a/test2.log b/test2.log deleted file mode 100644 index d655cced37f21be73dda3649588a9b9572fe64ef..0000000000000000000000000000000000000000 --- a/test2.log +++ /dev/null @@ -1,40 +0,0 @@ -Traceback (most recent call last): - File "/usr/lib/ckan/default/bin/paster", line 11, in <module> - sys.exit(run()) - File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/paste/script/command.py", line 102, in run - invoke(command, command_name, options, args[1:]) - File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/paste/script/command.py", line 141, in invoke - exit_code = runner.run(args) - File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/paste/script/command.py", line 236, in run - result = self.command() - File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/paste/script/serve.py", line 284, in command - relative_to=base, global_conf=vars) - File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/paste/script/serve.py", line 329, in loadapp - **kw) - File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/paste/deploy/loadwsgi.py", line 247, in loadapp - return loadobj(APP, uri, name=name, **kw) - File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/paste/deploy/loadwsgi.py", line 272, in loadobj - return context.create() - File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/paste/deploy/loadwsgi.py", line 710, in create - return self.object_type.invoke(self) - File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/paste/deploy/loadwsgi.py", line 146, in invoke - return fix_call(context.object, context.global_conf, **context.local_conf) - File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/paste/deploy/util.py", line 55, in fix_call - val = callable(*args, **kw) - File "/usr/lib/ckan/default/src/ckan/ckan/config/middleware/__init__.py", line 46, in make_app - load_environment(conf, app_conf) - File "/usr/lib/ckan/default/src/ckan/ckan/config/environment.py", line 99, in load_environment - p.load_all() - File "/usr/lib/ckan/default/src/ckan/ckan/plugins/core.py", line 139, in load_all - load(*plugins) - File "/usr/lib/ckan/default/src/ckan/ckan/plugins/core.py", line 153, in load - service = _get_service(plugin) - File "/usr/lib/ckan/default/src/ckan/ckan/plugins/core.py", line 255, in _get_service - return plugin.load()(name=plugin_name) - File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/pkg_resources/__init__.py", line 2305, in load - return self.resolve() - File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/pkg_resources/__init__.py", line 2311, in resolve - module = __import__(self.module_name, fromlist=['__name__'], level=0) - File "/usr/lib/ckan/default/src/ckanext-odsh/ckanext/odsh/plugin.py", line 29, in <module> - class OdshLogger(multiline_formatter.formatter.MultilineMessagesFormatter): -NameError: name 'multiline_formatter' is not defined diff --git a/uls.conf b/uls.conf deleted file mode 100644 index a94f2d66b6912e16f9b3121114f0b5e32a59e260..0000000000000000000000000000000000000000 --- a/uls.conf +++ /dev/null @@ -1,16 +0,0 @@ -inputfile = /var/log/ckan/bulk.log -inputfile = /var/log/ckan/std/gather_consumer.log -inputfile = /var/log/ckan/std/fetch_consumer.log - -#outputfile = ./logs/out.uls - -iformat0 = %F %T,%c%c%c ERROR [%q] got exception ...%W -nreadformat0 =... : %W - -# 'got exception...' ist die erste Zeile eines Tracebacks -reg0 = got exception ... -# alle forgenden Zeilen haben diesen Präfix -nreadreg0 =^... : - -write0 = V;%F %T;%h;"odsh";"Exception";"traceback";"in %p -nreadwrite0 = %W \ No newline at end of file