diff --git a/.gitignore b/.gitignore index 1f6580a7e7ebe22e5d7259ef49dfb33ad0fe338e..2bcbcdda3dd15b98c8f1fadc3bcf844f86cf7dad 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,12 @@ coverage.xml # Sphinx documentation docs/_build/ .vscode/settings.json + +# VS Code +.vscode/ + +# symlink to env +env + +# nose +.noseids diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3e20b46d5b2c9e7b7153e2af6c9bcad183335306 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# ckanext-odsh +Diese CKAN-Extension enthält die wichtigsten Features und das Layout für das Tranzparenzportal Schleswig-Holstein. +Sie ist eine Weiterentwicklung der gleichnamigen Extension, die im Zuge der Entwicklung des Open Data Portals Schleswig-Holstein entwickelt wurde. + +## Branches +### master +Dieser Branch enthält den aktuell auf den Stage- bzw. Prod-Servern laufenden Code. + +### dev +Dieser Branch enthält den aktuellsten Stand mit allen fertig entwickelten Features. + +### andere Branches +Für die Entwicklung neuer Features soll jeweils ein eigener Branch vom dev-Branch abgezweigt werden. Für den Branch soll ein sprechender Name gewählt werden. + +## Deployment auf Produktivsystem +Das Deployment auf das Produktivsystem geschieht über Ansible. Die dafür benötigten Skripte befinden sich im Repository `tpsh_deploy`. + +## Manuelle Installation + + +## Konfiguration +Die Extension benötigt Konfigurationsparameter in der CKAN-Konfigurationsdatei (z.B. `production.ini`). Die korrekten Parameter für das Produktivsystem befinden sich im Repository `tpsh_deploy` unter `resources/production.ini`. Folgende Parameter sollten für Enwicklungssysteme geändert werden: + +| Parameter | Erläuterung | Wert für Entwicklungssysteme | +|---------------------------------------|---------------------------------------------------------------|-------------------------------------------| +| ckanext.odsh.use_matomo | `true` schaltet das matomo-Tracking ein. | `false` | +| ckanext.odsh.skip_icap_virus_check | `false` schaltet den Virus-Check ein. | `true` | +| ckanext.odsh.showtestbanner | `true` schaltet das Banner "Testsystem" ein, Muss `false` für Production-Server sein. | - | \ No newline at end of file diff --git a/ckanext/odsh/collection/__init__.py b/ckanext/odsh/collection/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ckanext/odsh/collection/controller.py b/ckanext/odsh/collection/controller.py new file mode 100644 index 0000000000000000000000000000000000000000..5b049563195ed12b5a1b4f008b38fd95fc3af612 --- /dev/null +++ b/ckanext/odsh/collection/controller.py @@ -0,0 +1,38 @@ +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 + + + +class LatestDatasetController(PackageController): + + def latest_dataset(self, id): + latest_dataset= get_latest_dataset(id) + toolkit.redirect_to(controller='package', action='read', id=latest_dataset) + +class LatestRecourcesController(PackageController): + + def latest_resource(self, id, type): + latest_resources = get_latest_resources_for_type(id, type) + if latest_resources is None: + abort(404) + url_type = latest_resources.get('url_type') + if url_type is None: + resource_url = latest_resources.get('url') + toolkit.redirect_to(resource_url) + if url_type == 'upload': + download_package_id = latest_resources.get('package_id') + download_resource_id = latest_resources.get('id') + pre_resource_url = latest_resources.get('url') + if is_url(pre_resource_url): + url_resource = pre_resource_url + else: + url_resource = url_for(controller='package', + action='resource_download', + id=download_package_id, + resource_id=download_resource_id, + filename=pre_resource_url, + qualified = True) + toolkit.redirect_to(url_resource) + abort(404) \ No newline at end of file diff --git a/ckanext/odsh/collection/helpers.py b/ckanext/odsh/collection/helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..ec0f7dcbf05e7c37afa5491501e9d54f071f11d8 --- /dev/null +++ b/ckanext/odsh/collection/helpers.py @@ -0,0 +1,161 @@ +from string import lower + +import ckan.lib.helpers as helpers +import ckan.model as model +import ckan.plugins.toolkit as toolkit + + + +#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 + +#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 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 + +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 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( + controller='ckanext.odsh.collection.controller:LatestDatasetController', + action='latest', + id=collection_name + ) + return url \ No newline at end of file diff --git a/ckanext/odsh/collection/plugin.py b/ckanext/odsh/collection/plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..2ebcefc1af2c164045af7e861856858644885df9 --- /dev/null +++ b/ckanext/odsh/collection/plugin.py @@ -0,0 +1,60 @@ + +from ckan.lib.plugins import DefaultTranslation, DefaultDatasetForm +import ckan.plugins as plugins +import helpers as collection_helpers +from routes.mapper import SubMapper + +class CollectionsPlugin(plugins.SingletonPlugin, DefaultDatasetForm): + plugins.implements(plugins.IDatasetForm, inherit=True) + plugins.implements(plugins.IRoutes, inherit=True) + plugins.implements(plugins.ITemplateHelpers) + + + # IDataSetForm + def package_types(self): + return ('collection', ) + + def is_fallback(self): + return False + + + # IRoutes + def before_map(self, map): + + map.connect( + '/collection/{id}/aktuell', + controller='ckanext.odsh.collection.controller:LatestDatasetController', + action='latest_dataset' + ) + + map.connect( + '/collection/{id}/aktuell.{type}', + controller='ckanext.odsh.collection.controller:LatestRecourcesController', + action='latest_resource' + ) + + with SubMapper( + map, + controller='ckanext.odsh.collection.controller:LatestDatasetController', + path_prefix='/collection/' + ) as m: + m.connect('latest', '{id}/aktuell', action='latest') + m.connect('latest_resource', '{id}/aktuell.{type}', action='latest_resource') + return map + + def after_map(self, map): + return map + + + # ITemplateHelpers + def get_helpers(self): + # Template helper function names should begin with the name of the + # extension they belong to, to avoid clashing with functions from + # 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, + } diff --git a/ckanext/odsh/fanstatic/datepicker.css b/ckanext/odsh/fanstatic/datepicker.css index c2a0a0a74d66a307be8fea00105c8f66079454d2..408f3afa97f78e22ce09b9173ca376589209f20d 100644 --- a/ckanext/odsh/fanstatic/datepicker.css +++ b/ckanext/odsh/fanstatic/datepicker.css @@ -3,308 +3,5 @@ * * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) */ - .datepicker{ - padding:4px; - -webkit-border-radius:4px; - -moz-border-radius:4px; - border-radius:4px; - direction:ltr -} -.datepicker-inline{ - width:220px -} -.datepicker-rtl{ - direction:rtl -} -.datepicker-rtl.dropdown-menu{ - left:auto -} -.datepicker-rtl table tr td span{ - float:right -} -.datepicker-dropdown{ - top:0; - left:0 -} -.datepicker-dropdown:before{ - content:''; - display:inline-block; - border-left:7px solid transparent; - border-right:7px solid transparent; - border-bottom:7px solid #999; - border-top:0; - border-bottom-color:rgba(0,0,0,.2); - position:absolute -} -.datepicker-dropdown:after{ - content:''; - display:inline-block; - border-left:6px solid transparent; - border-right:6px solid transparent; - border-bottom:6px solid #fff; - border-top:0; - position:absolute -} -.datepicker-dropdown.datepicker-orient-left:before{ - left:6px -} -.datepicker-dropdown.datepicker-orient-left:after{ - left:7px -} -.datepicker-dropdown.datepicker-orient-right:before{ - right:6px -} -.datepicker-dropdown.datepicker-orient-right:after{ - right:7px -} -.datepicker-dropdown.datepicker-orient-bottom:before{ - top:-7px -} -.datepicker-dropdown.datepicker-orient-bottom:after{ - top:-6px -} -.datepicker-dropdown.datepicker-orient-top:before{ - bottom:-7px; - border-bottom:0; - border-top:7px solid #999 -} -.datepicker-dropdown.datepicker-orient-top:after{ - bottom:-6px; - border-bottom:0; - border-top:6px solid #fff -} -.datepicker table{ - margin:0; - -webkit-touch-callout:none; - -webkit-user-select:none; - -khtml-user-select:none; - -moz-user-select:none; - -ms-user-select:none; - user-select:none -} -.datepicker td,.datepicker th{ - text-align:center; - width:20px; - height:20px; - -webkit-border-radius:4px; - -moz-border-radius:4px; - border-radius:4px; - border:none -} -.table-striped .datepicker table tr td,.table-striped .datepicker table tr th{ - background-color:transparent -} -.datepicker table tr td.day.focused,.datepicker table tr td.day:hover{ - background:#eee; - cursor:pointer -} -.datepicker table tr td.new,.datepicker table tr td.old{ - color:#999 -} -.datepicker table tr td.disabled,.datepicker table tr td.disabled:hover{ - background:0 0; - color:#999; - cursor:default -} -.datepicker table tr td.highlighted{ - background:#d9edf7; - border-radius:0 -} -.datepicker table tr td.today,.datepicker table tr td.today.disabled,.datepicker table tr td.today.disabled:hover,.datepicker table tr td.today:hover{ - background-color:#fde19a; - background-image:-moz-linear-gradient(to bottom,#fdd49a,#fdf59a); - background-image:-ms-linear-gradient(to bottom,#fdd49a,#fdf59a); - background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdd49a),to(#fdf59a)); - background-image:-webkit-linear-gradient(to bottom,#fdd49a,#fdf59a); - background-image:-o-linear-gradient(to bottom,#fdd49a,#fdf59a); - background-image:linear-gradient(to bottom,#fdd49a,#fdf59a); - background-repeat:repeat-x; - filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0); - border-color:#fdf59a #fdf59a #fbed50; - border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25); - filter:progid:DXImageTransform.Microsoft.gradient(enabled=false); - color:#000 -} -.datepicker table tr td.today.active,.datepicker table tr td.today.disabled,.datepicker table tr td.today.disabled.active,.datepicker table tr td.today.disabled.disabled,.datepicker table tr td.today.disabled:active,.datepicker table tr td.today.disabled:hover,.datepicker table tr td.today.disabled:hover.active,.datepicker table tr td.today.disabled:hover.disabled,.datepicker table tr td.today.disabled:hover:active,.datepicker table tr td.today.disabled:hover:hover,.datepicker table tr td.today.disabled:hover[disabled],.datepicker table tr td.today.disabled[disabled],.datepicker table tr td.today:active,.datepicker table tr td.today:hover,.datepicker table tr td.today:hover.active,.datepicker table tr td.today:hover.disabled,.datepicker table tr td.today:hover:active,.datepicker table tr td.today:hover:hover,.datepicker table tr td.today:hover[disabled],.datepicker table tr td.today[disabled]{ - background-color:#fdf59a -} -.datepicker table tr td.today.active,.datepicker table tr td.today.disabled.active,.datepicker table tr td.today.disabled:active,.datepicker table tr td.today.disabled:hover.active,.datepicker table tr td.today.disabled:hover:active,.datepicker table tr td.today:active,.datepicker table tr td.today:hover.active,.datepicker table tr td.today:hover:active{ - background-color:#fbf069\9 -} -.datepicker table tr td.today:hover:hover{ - color:#000 -} -.datepicker table tr td.today.active:hover{ - color:#fff -} -.datepicker table tr td.range,.datepicker table tr td.range.disabled,.datepicker table tr td.range.disabled:hover,.datepicker table tr td.range:hover{ - background:#eee; - -webkit-border-radius:0; - -moz-border-radius:0; - border-radius:0 -} -.datepicker table tr td.range.today,.datepicker table tr td.range.today.disabled,.datepicker table tr td.range.today.disabled:hover,.datepicker table tr td.range.today:hover{ - background-color:#f3d17a; - background-image:-moz-linear-gradient(to bottom,#f3c17a,#f3e97a); - background-image:-ms-linear-gradient(to bottom,#f3c17a,#f3e97a); - background-image:-webkit-gradient(linear,0 0,0 100%,from(#f3c17a),to(#f3e97a)); - background-image:-webkit-linear-gradient(to bottom,#f3c17a,#f3e97a); - background-image:-o-linear-gradient(to bottom,#f3c17a,#f3e97a); - background-image:linear-gradient(to bottom,#f3c17a,#f3e97a); - background-repeat:repeat-x; - filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0); - border-color:#f3e97a #f3e97a #edde34; - border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25); - filter:progid:DXImageTransform.Microsoft.gradient(enabled=false); - -webkit-border-radius:0; - -moz-border-radius:0; - border-radius:0 -} -.datepicker table tr td.range.today.active,.datepicker table tr td.range.today.disabled,.datepicker table tr td.range.today.disabled.active,.datepicker table tr td.range.today.disabled.disabled,.datepicker table tr td.range.today.disabled:active,.datepicker table tr td.range.today.disabled:hover,.datepicker table tr td.range.today.disabled:hover.active,.datepicker table tr td.range.today.disabled:hover.disabled,.datepicker table tr td.range.today.disabled:hover:active,.datepicker table tr td.range.today.disabled:hover:hover,.datepicker table tr td.range.today.disabled:hover[disabled],.datepicker table tr td.range.today.disabled[disabled],.datepicker table tr td.range.today:active,.datepicker table tr td.range.today:hover,.datepicker table tr td.range.today:hover.active,.datepicker table tr td.range.today:hover.disabled,.datepicker table tr td.range.today:hover:active,.datepicker table tr td.range.today:hover:hover,.datepicker table tr td.range.today:hover[disabled],.datepicker table tr td.range.today[disabled]{ - background-color:#f3e97a -} -.datepicker table tr td.range.today.active,.datepicker table tr td.range.today.disabled.active,.datepicker table tr td.range.today.disabled:active,.datepicker table tr td.range.today.disabled:hover.active,.datepicker table tr td.range.today.disabled:hover:active,.datepicker table tr td.range.today:active,.datepicker table tr td.range.today:hover.active,.datepicker table tr td.range.today:hover:active{ - background-color:#efe24b\9 -} -.datepicker table tr td.selected,.datepicker table tr td.selected.disabled,.datepicker table tr td.selected.disabled:hover,.datepicker table tr td.selected:hover{ - background-color:#9e9e9e; - background-image:-moz-linear-gradient(to bottom,#b3b3b3,grey); - background-image:-ms-linear-gradient(to bottom,#b3b3b3,grey); - background-image:-webkit-gradient(linear,0 0,0 100%,from(#b3b3b3),to(grey)); - background-image:-webkit-linear-gradient(to bottom,#b3b3b3,grey); - background-image:-o-linear-gradient(to bottom,#b3b3b3,grey); - background-image:linear-gradient(to bottom,#b3b3b3,grey); - background-repeat:repeat-x; - filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0); - border-color:grey grey #595959; - border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25); - filter:progid:DXImageTransform.Microsoft.gradient(enabled=false); - color:#fff; - text-shadow:0 -1px 0 rgba(0,0,0,.25) -} -.datepicker table tr td.selected.active,.datepicker table tr td.selected.disabled,.datepicker table tr td.selected.disabled.active,.datepicker table tr td.selected.disabled.disabled,.datepicker table tr td.selected.disabled:active,.datepicker table tr td.selected.disabled:hover,.datepicker table tr td.selected.disabled:hover.active,.datepicker table tr td.selected.disabled:hover.disabled,.datepicker table tr td.selected.disabled:hover:active,.datepicker table tr td.selected.disabled:hover:hover,.datepicker table tr td.selected.disabled:hover[disabled],.datepicker table tr td.selected.disabled[disabled],.datepicker table tr td.selected:active,.datepicker table tr td.selected:hover,.datepicker table tr td.selected:hover.active,.datepicker table tr td.selected:hover.disabled,.datepicker table tr td.selected:hover:active,.datepicker table tr td.selected:hover:hover,.datepicker table tr td.selected:hover[disabled],.datepicker table tr td.selected[disabled]{ - background-color:grey -} -.datepicker table tr td.selected.active,.datepicker table tr td.selected.disabled.active,.datepicker table tr td.selected.disabled:active,.datepicker table tr td.selected.disabled:hover.active,.datepicker table tr td.selected.disabled:hover:active,.datepicker table tr td.selected:active,.datepicker table tr td.selected:hover.active,.datepicker table tr td.selected:hover:active{ - background-color:#666\9 -} -.datepicker table tr td.active,.datepicker table tr td.active.disabled,.datepicker table tr td.active.disabled:hover,.datepicker table tr td.active:hover{ - background-color:#006dcc; - background-image:-moz-linear-gradient(to bottom,#08c,#04c); - background-image:-ms-linear-gradient(to bottom,#08c,#04c); - background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c)); - background-image:-webkit-linear-gradient(to bottom,#08c,#04c); - background-image:-o-linear-gradient(to bottom,#08c,#04c); - background-image:linear-gradient(to bottom,#08c,#04c); - background-repeat:repeat-x; - filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#08c', endColorstr='#0044cc', GradientType=0); - border-color:#04c #04c #002a80; - border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25); - filter:progid:DXImageTransform.Microsoft.gradient(enabled=false); - color:#fff; - text-shadow:0 -1px 0 rgba(0,0,0,.25) -} -.datepicker table tr td.active.active,.datepicker table tr td.active.disabled,.datepicker table tr td.active.disabled.active,.datepicker table tr td.active.disabled.disabled,.datepicker table tr td.active.disabled:active,.datepicker table tr td.active.disabled:hover,.datepicker table tr td.active.disabled:hover.active,.datepicker table tr td.active.disabled:hover.disabled,.datepicker table tr td.active.disabled:hover:active,.datepicker table tr td.active.disabled:hover:hover,.datepicker table tr td.active.disabled:hover[disabled],.datepicker table tr td.active.disabled[disabled],.datepicker table tr td.active:active,.datepicker table tr td.active:hover,.datepicker table tr td.active:hover.active,.datepicker table tr td.active:hover.disabled,.datepicker table tr td.active:hover:active,.datepicker table tr td.active:hover:hover,.datepicker table tr td.active:hover[disabled],.datepicker table tr td.active[disabled]{ - background-color:#04c -} -.datepicker table tr td.active.active,.datepicker table tr td.active.disabled.active,.datepicker table tr td.active.disabled:active,.datepicker table tr td.active.disabled:hover.active,.datepicker table tr td.active.disabled:hover:active,.datepicker table tr td.active:active,.datepicker table tr td.active:hover.active,.datepicker table tr td.active:hover:active{ - background-color:#039\9 -} -.datepicker table tr td span{ - display:block; - width:23%; - height:54px; - line-height:54px; - float:left; - margin:1%; - cursor:pointer; - -webkit-border-radius:4px; - -moz-border-radius:4px; - border-radius:4px -} -.datepicker table tr td span.focused,.datepicker table tr td span:hover{ - background:#eee -} -.datepicker table tr td span.disabled,.datepicker table tr td span.disabled:hover{ - background:0 0; - color:#999; - cursor:default -} -.datepicker table tr td span.active,.datepicker table tr td span.active.disabled,.datepicker table tr td span.active.disabled:hover,.datepicker table tr td span.active:hover{ - background-color:#006dcc; - background-image:-moz-linear-gradient(to bottom,#08c,#04c); - background-image:-ms-linear-gradient(to bottom,#08c,#04c); - background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c)); - background-image:-webkit-linear-gradient(to bottom,#08c,#04c); - background-image:-o-linear-gradient(to bottom,#08c,#04c); - background-image:linear-gradient(to bottom,#08c,#04c); - background-repeat:repeat-x; - filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#08c', endColorstr='#0044cc', GradientType=0); - border-color:#04c #04c #002a80; - border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25); - filter:progid:DXImageTransform.Microsoft.gradient(enabled=false); - color:#fff; - text-shadow:0 -1px 0 rgba(0,0,0,.25) -} -.datepicker table tr td span.active.active,.datepicker table tr td span.active.disabled,.datepicker table tr td span.active.disabled.active,.datepicker table tr td span.active.disabled.disabled,.datepicker table tr td span.active.disabled:active,.datepicker table tr td span.active.disabled:hover,.datepicker table tr td span.active.disabled:hover.active,.datepicker table tr td span.active.disabled:hover.disabled,.datepicker table tr td span.active.disabled:hover:active,.datepicker table tr td span.active.disabled:hover:hover,.datepicker table tr td span.active.disabled:hover[disabled],.datepicker table tr td span.active.disabled[disabled],.datepicker table tr td span.active:active,.datepicker table tr td span.active:hover,.datepicker table tr td span.active:hover.active,.datepicker table tr td span.active:hover.disabled,.datepicker table tr td span.active:hover:active,.datepicker table tr td span.active:hover:hover,.datepicker table tr td span.active:hover[disabled],.datepicker table tr td span.active[disabled]{ - background-color:#04c -} -.datepicker table tr td span.active.active,.datepicker table tr td span.active.disabled.active,.datepicker table tr td span.active.disabled:active,.datepicker table tr td span.active.disabled:hover.active,.datepicker table tr td span.active.disabled:hover:active,.datepicker table tr td span.active:active,.datepicker table tr td span.active:hover.active,.datepicker table tr td span.active:hover:active{ - background-color:#039\9 -} -.datepicker table tr td span.new,.datepicker table tr td span.old{ - color:#999 -} -.datepicker .datepicker-switch{ - width:145px -} -.datepicker .datepicker-switch,.datepicker .next,.datepicker .prev,.datepicker tfoot tr th{ - cursor:pointer -} -.datepicker .datepicker-switch:hover,.datepicker .next:hover,.datepicker .prev:hover,.datepicker tfoot tr th:hover{ - background:#eee -} -.datepicker .next.disabled,.datepicker .prev.disabled{ - visibility:hidden -} -.datepicker .cw{ - font-size:10px; - width:12px; - padding:0 2px 0 5px; - vertical-align:middle -} -.input-append.date .add-on,.input-prepend.date .add-on{ - cursor:pointer -} -.input-append.date .add-on i,.input-prepend.date .add-on i{ - margin-top:3px -} -.input-daterange input{ - text-align:center -} -.input-daterange input:first-child{ - -webkit-border-radius:3px 0 0 3px; - -moz-border-radius:3px 0 0 3px; - border-radius:3px 0 0 3px -} -.input-daterange input:last-child{ - -webkit-border-radius:0 3px 3px 0; - -moz-border-radius:0 3px 3px 0; - border-radius:0 3px 3px 0 -} -.input-daterange .add-on{ - display:inline-block; - width:auto; - min-width:16px; - height:18px; - padding:4px 5px; - font-weight:400; - line-height:18px; - text-align:center; - text-shadow:0 1px 0 #fff; - vertical-align:middle; - background-color:#eee; - border:1px solid #ccc; - margin-left:-5px; - margin-right:-5px -} \ No newline at end of file + +.datepicker{padding:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;direction:ltr}.datepicker-inline{width:220px}.datepicker-rtl{direction:rtl}.datepicker-rtl.dropdown-menu{left:auto}.datepicker-rtl table tr td span{float:right}.datepicker-dropdown{top:0;left:0}.datepicker-dropdown:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #999;border-top:0;border-bottom-color:rgba(0,0,0,.2);position:absolute}.datepicker-dropdown:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;border-top:0;position:absolute}.datepicker-dropdown.datepicker-orient-left:before{left:6px}.datepicker-dropdown.datepicker-orient-left:after{left:7px}.datepicker-dropdown.datepicker-orient-right:before{right:6px}.datepicker-dropdown.datepicker-orient-right:after{right:7px}.datepicker-dropdown.datepicker-orient-bottom:before{top:-7px}.datepicker-dropdown.datepicker-orient-bottom:after{top:-6px}.datepicker-dropdown.datepicker-orient-top:before{bottom:-7px;border-bottom:0;border-top:7px solid #999}.datepicker-dropdown.datepicker-orient-top:after{bottom:-6px;border-bottom:0;border-top:6px solid #fff}.datepicker table{margin:0;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.datepicker td,.datepicker th{text-align:center;width:20px;height:20px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;border:none}.table-striped .datepicker table tr td,.table-striped .datepicker table tr th{background-color:transparent}.datepicker table tr td.day.focused,.datepicker table tr td.day:hover{background:#eee;cursor:pointer}.datepicker table tr td.new,.datepicker table tr td.old{color:#999}.datepicker table tr td.disabled,.datepicker table tr td.disabled:hover{background:0 0;color:#999;cursor:default}.datepicker table tr td.highlighted{background:#d9edf7;border-radius:0}.datepicker table tr td.today,.datepicker table tr td.today.disabled,.datepicker table tr td.today.disabled:hover,.datepicker table tr td.today:hover{background-color:#fde19a;background-image:-moz-linear-gradient(to bottom,#fdd49a,#fdf59a);background-image:-ms-linear-gradient(to bottom,#fdd49a,#fdf59a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdd49a),to(#fdf59a));background-image:-webkit-linear-gradient(to bottom,#fdd49a,#fdf59a);background-image:-o-linear-gradient(to bottom,#fdd49a,#fdf59a);background-image:linear-gradient(to bottom,#fdd49a,#fdf59a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);border-color:#fdf59a #fdf59a #fbed50;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#000}.datepicker table tr td.today.active,.datepicker table tr td.today.disabled,.datepicker table tr td.today.disabled.active,.datepicker table tr td.today.disabled.disabled,.datepicker table tr td.today.disabled:active,.datepicker table tr td.today.disabled:hover,.datepicker table tr td.today.disabled:hover.active,.datepicker table tr td.today.disabled:hover.disabled,.datepicker table tr td.today.disabled:hover:active,.datepicker table tr td.today.disabled:hover:hover,.datepicker table tr td.today.disabled:hover[disabled],.datepicker table tr td.today.disabled[disabled],.datepicker table tr td.today:active,.datepicker table tr td.today:hover,.datepicker table tr td.today:hover.active,.datepicker table tr td.today:hover.disabled,.datepicker table tr td.today:hover:active,.datepicker table tr td.today:hover:hover,.datepicker table tr td.today:hover[disabled],.datepicker table tr td.today[disabled]{background-color:#fdf59a}.datepicker table tr td.today.active,.datepicker table tr td.today.disabled.active,.datepicker table tr td.today.disabled:active,.datepicker table tr td.today.disabled:hover.active,.datepicker table tr td.today.disabled:hover:active,.datepicker table tr td.today:active,.datepicker table tr td.today:hover.active,.datepicker table tr td.today:hover:active{background-color:#fbf069\9}.datepicker table tr td.today:hover:hover{color:#000}.datepicker table tr td.today.active:hover{color:#fff}.datepicker table tr td.range,.datepicker table tr td.range.disabled,.datepicker table tr td.range.disabled:hover,.datepicker table tr td.range:hover{background:#eee;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.datepicker table tr td.range.today,.datepicker table tr td.range.today.disabled,.datepicker table tr td.range.today.disabled:hover,.datepicker table tr td.range.today:hover{background-color:#f3d17a;background-image:-moz-linear-gradient(to bottom,#f3c17a,#f3e97a);background-image:-ms-linear-gradient(to bottom,#f3c17a,#f3e97a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f3c17a),to(#f3e97a));background-image:-webkit-linear-gradient(to bottom,#f3c17a,#f3e97a);background-image:-o-linear-gradient(to bottom,#f3c17a,#f3e97a);background-image:linear-gradient(to bottom,#f3c17a,#f3e97a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);border-color:#f3e97a #f3e97a #edde34;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.datepicker table tr td.range.today.active,.datepicker table tr td.range.today.disabled,.datepicker table tr td.range.today.disabled.active,.datepicker table tr td.range.today.disabled.disabled,.datepicker table tr td.range.today.disabled:active,.datepicker table tr td.range.today.disabled:hover,.datepicker table tr td.range.today.disabled:hover.active,.datepicker table tr td.range.today.disabled:hover.disabled,.datepicker table tr td.range.today.disabled:hover:active,.datepicker table tr td.range.today.disabled:hover:hover,.datepicker table tr td.range.today.disabled:hover[disabled],.datepicker table tr td.range.today.disabled[disabled],.datepicker table tr td.range.today:active,.datepicker table tr td.range.today:hover,.datepicker table tr td.range.today:hover.active,.datepicker table tr td.range.today:hover.disabled,.datepicker table tr td.range.today:hover:active,.datepicker table tr td.range.today:hover:hover,.datepicker table tr td.range.today:hover[disabled],.datepicker table tr td.range.today[disabled]{background-color:#f3e97a}.datepicker table tr td.range.today.active,.datepicker table tr td.range.today.disabled.active,.datepicker table tr td.range.today.disabled:active,.datepicker table tr td.range.today.disabled:hover.active,.datepicker table tr td.range.today.disabled:hover:active,.datepicker table tr td.range.today:active,.datepicker table tr td.range.today:hover.active,.datepicker table tr td.range.today:hover:active{background-color:#efe24b\9}.datepicker table tr td.selected,.datepicker table tr td.selected.disabled,.datepicker table tr td.selected.disabled:hover,.datepicker table tr td.selected:hover{background-color:#9e9e9e;background-image:-moz-linear-gradient(to bottom,#b3b3b3,grey);background-image:-ms-linear-gradient(to bottom,#b3b3b3,grey);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b3b3b3),to(grey));background-image:-webkit-linear-gradient(to bottom,#b3b3b3,grey);background-image:-o-linear-gradient(to bottom,#b3b3b3,grey);background-image:linear-gradient(to bottom,#b3b3b3,grey);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);border-color:grey grey #595959;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td.selected.active,.datepicker table tr td.selected.disabled,.datepicker table tr td.selected.disabled.active,.datepicker table tr td.selected.disabled.disabled,.datepicker table tr td.selected.disabled:active,.datepicker table tr td.selected.disabled:hover,.datepicker table tr td.selected.disabled:hover.active,.datepicker table tr td.selected.disabled:hover.disabled,.datepicker table tr td.selected.disabled:hover:active,.datepicker table tr td.selected.disabled:hover:hover,.datepicker table tr td.selected.disabled:hover[disabled],.datepicker table tr td.selected.disabled[disabled],.datepicker table tr td.selected:active,.datepicker table tr td.selected:hover,.datepicker table tr td.selected:hover.active,.datepicker table tr td.selected:hover.disabled,.datepicker table tr td.selected:hover:active,.datepicker table tr td.selected:hover:hover,.datepicker table tr td.selected:hover[disabled],.datepicker table tr td.selected[disabled]{background-color:grey}.datepicker table tr td.selected.active,.datepicker table tr td.selected.disabled.active,.datepicker table tr td.selected.disabled:active,.datepicker table tr td.selected.disabled:hover.active,.datepicker table tr td.selected.disabled:hover:active,.datepicker table tr td.selected:active,.datepicker table tr td.selected:hover.active,.datepicker table tr td.selected:hover:active{background-color:#666\9}.datepicker table tr td.active,.datepicker table tr td.active.disabled,.datepicker table tr td.active.disabled:hover,.datepicker table tr td.active:hover{background-color:#006dcc;background-image:-moz-linear-gradient(to bottom,#08c,#04c);background-image:-ms-linear-gradient(to bottom,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(to bottom,#08c,#04c);background-image:-o-linear-gradient(to bottom,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#08c', endColorstr='#0044cc', GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td.active.active,.datepicker table tr td.active.disabled,.datepicker table tr td.active.disabled.active,.datepicker table tr td.active.disabled.disabled,.datepicker table tr td.active.disabled:active,.datepicker table tr td.active.disabled:hover,.datepicker table tr td.active.disabled:hover.active,.datepicker table tr td.active.disabled:hover.disabled,.datepicker table tr td.active.disabled:hover:active,.datepicker table tr td.active.disabled:hover:hover,.datepicker table tr td.active.disabled:hover[disabled],.datepicker table tr td.active.disabled[disabled],.datepicker table tr td.active:active,.datepicker table tr td.active:hover,.datepicker table tr td.active:hover.active,.datepicker table tr td.active:hover.disabled,.datepicker table tr td.active:hover:active,.datepicker table tr td.active:hover:hover,.datepicker table tr td.active:hover[disabled],.datepicker table tr td.active[disabled]{background-color:#04c}.datepicker table tr td.active.active,.datepicker table tr td.active.disabled.active,.datepicker table tr td.active.disabled:active,.datepicker table tr td.active.disabled:hover.active,.datepicker table tr td.active.disabled:hover:active,.datepicker table tr td.active:active,.datepicker table tr td.active:hover.active,.datepicker table tr td.active:hover:active{background-color:#039\9}.datepicker table tr td span{display:block;width:23%;height:54px;line-height:54px;float:left;margin:1%;cursor:pointer;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.datepicker table tr td span.focused,.datepicker table tr td span:hover{background:#eee}.datepicker table tr td span.disabled,.datepicker table tr td span.disabled:hover{background:0 0;color:#999;cursor:default}.datepicker table tr td span.active,.datepicker table tr td span.active.disabled,.datepicker table tr td span.active.disabled:hover,.datepicker table tr td span.active:hover{background-color:#006dcc;background-image:-moz-linear-gradient(to bottom,#08c,#04c);background-image:-ms-linear-gradient(to bottom,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(to bottom,#08c,#04c);background-image:-o-linear-gradient(to bottom,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#08c', endColorstr='#0044cc', GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td span.active.active,.datepicker table tr td span.active.disabled,.datepicker table tr td span.active.disabled.active,.datepicker table tr td span.active.disabled.disabled,.datepicker table tr td span.active.disabled:active,.datepicker table tr td span.active.disabled:hover,.datepicker table tr td span.active.disabled:hover.active,.datepicker table tr td span.active.disabled:hover.disabled,.datepicker table tr td span.active.disabled:hover:active,.datepicker table tr td span.active.disabled:hover:hover,.datepicker table tr td span.active.disabled:hover[disabled],.datepicker table tr td span.active.disabled[disabled],.datepicker table tr td span.active:active,.datepicker table tr td span.active:hover,.datepicker table tr td span.active:hover.active,.datepicker table tr td span.active:hover.disabled,.datepicker table tr td span.active:hover:active,.datepicker table tr td span.active:hover:hover,.datepicker table tr td span.active:hover[disabled],.datepicker table tr td span.active[disabled]{background-color:#04c}.datepicker table tr td span.active.active,.datepicker table tr td span.active.disabled.active,.datepicker table tr td span.active.disabled:active,.datepicker table tr td span.active.disabled:hover.active,.datepicker table tr td span.active.disabled:hover:active,.datepicker table tr td span.active:active,.datepicker table tr td span.active:hover.active,.datepicker table tr td span.active:hover:active{background-color:#039\9}.datepicker table tr td span.new,.datepicker table tr td span.old{color:#999}.datepicker .datepicker-switch{width:145px}.datepicker .datepicker-switch,.datepicker .next,.datepicker .prev,.datepicker tfoot tr th{cursor:pointer}.datepicker .datepicker-switch:hover,.datepicker .next:hover,.datepicker .prev:hover,.datepicker tfoot tr th:hover{background:#eee}.datepicker .next.disabled,.datepicker .prev.disabled{visibility:hidden}.datepicker .cw{font-size:10px;width:12px;padding:0 2px 0 5px;vertical-align:middle}.input-append.date .add-on,.input-prepend.date .add-on{cursor:pointer}.input-append.date .add-on i,.input-prepend.date .add-on i{margin-top:3px}.input-daterange input{text-align:center}.input-daterange input:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-daterange input:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-daterange .add-on{display:inline-block;width:auto;min-width:16px;height:18px;padding:4px 5px;font-weight:400;line-height:18px;text-align:center;text-shadow:0 1px 0 #fff;vertical-align:middle;background-color:#eee;border:1px solid #ccc;margin-left:-5px;margin-right:-5px} \ No newline at end of file diff --git a/ckanext/odsh/fanstatic/odsh.js b/ckanext/odsh/fanstatic/odsh.js index b3081447568642b2391fd02b4bdd1713ce456f31..97cd432650dbdf5926c53649449d47eab09ced7b 100644 --- a/ckanext/odsh/fanstatic/odsh.js +++ b/ckanext/odsh/fanstatic/odsh.js @@ -2,7 +2,7 @@ $(document).ready(function () { $('.mylabel').click(function () { - window.location = $(this).siblings('a').attr('href'); + window.location = $(this).siblings('label').children('a').attr('href'); }); let search = function (score) diff --git a/ckanext/odsh/fanstatic/tpsh_toggle_menu.js b/ckanext/odsh/fanstatic/tpsh_toggle_menu.js new file mode 100644 index 0000000000000000000000000000000000000000..1a8a8c82d0f4849ca49b8bf525247cca7cdbf755 --- /dev/null +++ b/ckanext/odsh/fanstatic/tpsh_toggle_menu.js @@ -0,0 +1,21 @@ +"use strict"; + +ckan.module('tpsh_toggle_menu', function ($) { + return { + initialize: function () { + $.proxyAll(this, /_on/); + this.el.on('click', this._onClick); + }, + + _onClick: function(event) { + var element = $("body"); + var className = "menu-modal" + if (element.hasClass(className)){ + element.removeClass(className) + } + else { + element.addClass(className); + } + } + }; +}); \ No newline at end of file diff --git a/ckanext/odsh/fanstatic/tpsh_validate_password.js b/ckanext/odsh/fanstatic/tpsh_validate_password.js new file mode 100644 index 0000000000000000000000000000000000000000..f0f8f71b55e4f6a2cd9e52c6a39b09ab302d2dab --- /dev/null +++ b/ckanext/odsh/fanstatic/tpsh_validate_password.js @@ -0,0 +1,71 @@ +"use strict"; + +function isPasswordValid(password) { + if (password.length == 0) return true; + var minimumLength = 8; + var isValid = + (password.length >= minimumLength) && + (atLeastOneUpperCaseLetter(password)) && + (atLeastOneLowerCaseLetter(password)) && + (atLeastOneNoneLetter(password)); + return isValid + + function atLeastOneUpperCaseLetter(password) { + return (password !== password.toLowerCase()) + } + + function atLeastOneLowerCaseLetter(password) { + return (password !== password.toUpperCase()) + } + + function atLeastOneNoneLetter(password) { + return /[\W\d_]/.test(password) + } +} + + +function showPasswordStatus(isValid, inputElement) { + if (isValid) { + messageText = ''; + } else { + messageText = 'Passwörter müssen länger als 8 Zeichen sein und Großbuchstaben, Kleinbuchstaben und andere Zeichen enthalten.' + } + get_error_element(inputElement).innerHTML = messageText; + + function get_error_element(inputElement) { + // assumes that there is an element after input_element's parent that + // contains a class "inline-error" + var currentNode = inputElement.parentNode + do { + currentNode = currentNode.nextElementSibling; + } while ( + (currentNode !== null) && + !(currentNode.classList.contains('inline-error')) + ) + return currentNode + } +} + + +function setSubmitButtonState(isPasswordValid) { + var submitButton = document.getElementsByName('save')[0]; + submitButton.disabled = !isPasswordValid; +} + + +ckan.module('tpsh_validate_password', function ($) { + return { + initialize: function () { + $.proxyAll(this, /_on/); + this.el.on('input', this._onChange); + }, + + _onChange: function(event) { + var inputElement = event.target; + var newPassword = inputElement.value; + var isValid = isPasswordValid(newPassword); + showPasswordStatus(isValid, inputElement); + setSubmitButtonState(isValid); + } + }; +}); \ No newline at end of file diff --git a/ckanext/odsh/harvesters/kielharvester.py b/ckanext/odsh/harvesters/kielharvester.py index 55a7317e8bd51ddbb5b411545ee111fe1f9d1694..c1dfb652a9a2703b4dbd579cb38413298fad5298 100755 --- a/ckanext/odsh/harvesters/kielharvester.py +++ b/ckanext/odsh/harvesters/kielharvester.py @@ -112,7 +112,7 @@ class KielHarvester(ODSHBaseHarvester): context.copy(), {'id': harvest_object.source.id}) package_dict['owner_org'] = source_dataset.get('owner_org') - if package_dict['type'] == 'datensatz': + if package_dict['type'] in ('datensatz', 'dokument', 'document'): package_dict['type'] = 'dataset' package_dict['id'] = harvest_object.guid diff --git a/ckanext/odsh/harvesters/number_dcat_de_hamburg.json b/ckanext/odsh/harvesters/number_dcat_de_hamburg.json index 8ccefed472c2594f5ef89cf4d0e639712b4ed8be..51c0efe1abd42364f81a7ce47dfdb6ad1e13d947 100644 --- a/ckanext/odsh/harvesters/number_dcat_de_hamburg.json +++ b/ckanext/odsh/harvesters/number_dcat_de_hamburg.json @@ -28,7 +28,7 @@ "32" : ["soci"], "33" : ["soci"], "34" : ["soci"], -"35" : ["regi"], +"35" : ["econ","regi","agri"], "36" : ["educ"], "37" : ["educ"], "38" : ["educ"], @@ -64,16 +64,16 @@ "68" : ["econ"], "69" : ["econ"], "70" : ["econ"], -"71" : ["educ"], +"71" : ["econ"], "72" : ["econ"], "73" : ["econ"], -"74" : ["econ"], +"74" : ["econ","intr"], "75" : ["econ"], "76" : ["econ"], "77" : ["econ"], "78" : ["econ"], "79" : ["gove"], -"80" : ["gove"], +"80" : ["gove","econ"], "81" : ["gove"], "82" : ["gove"], "83" : ["envi"], @@ -91,7 +91,9 @@ "95" : ["educ"], "97" : ["gove"], "98" : ["gove"], -"104" : ["agri"], +"100" : ["gove","intr"], +"101" : ["gove"], +"104" : ["gove","regi"], "105" : ["regi"], "106" : ["regi"], "108" : ["econ"], @@ -101,7 +103,7 @@ "115" : ["agri"], "116" : ["soci"], "117" : ["econ"], -"122" : ["econ"], +"122" : ["gove","intr"], "124" : ["econ"], "127" : ["agri"], "128" : ["agri"], diff --git a/ckanext/odsh/harvesters/statistikamtnordharvester.py b/ckanext/odsh/harvesters/statistikamtnordharvester.py index 50a082a66b9796a6d3c5c93f89644d851b93d486..64ffbc985f33282bca88dce18e7310e539576df8 100755 --- a/ckanext/odsh/harvesters/statistikamtnordharvester.py +++ b/ckanext/odsh/harvesters/statistikamtnordharvester.py @@ -225,7 +225,7 @@ class StatistikamtNordHarvester(ODSHBaseHarvester): resource_file = urllib2.urlopen(resource['url']) resource_dict['file_size'] = resource_file['Content-Length'] else: - file_size = int(round(float(resource['Dateigroesse']) * 1000000000)) + file_size = int(round(float(resource['Dateigroesse']) * 1048576 )) resource_dict['size'] = file_size package_dict['resources'].append(resource_dict) diff --git a/ckanext/odsh/helper_pkg_dict.py b/ckanext/odsh/helper_pkg_dict.py new file mode 100644 index 0000000000000000000000000000000000000000..912b0b83db873b0ed58b98e64ef21b83c3a59e4b --- /dev/null +++ b/ckanext/odsh/helper_pkg_dict.py @@ -0,0 +1,197 @@ +import re + +import ckanext.odsh.helpers as helpers_odsh +import datetime + +import ckanext.odsh.helpers_tpsh as helpers_tpsh +import ckanext.odsh.collection.helpers as helpers_collection + +import ckanext.odsh.uri_store as uri_store + +from ckanext.odsh.pretty_daterange.date_range_formatter import DateRangeFormatter + +import logging +log = logging.getLogger(__name__) + + +class HelperPgkDict(object): + ''' + a convenience class for all operations related to pkg_dict + aka dataset_dict, dict_pkg ... + ''' + def __init__(self, pkg_dict): + self.pkg_dict = pkg_dict + + def is_collection(self): + ''' + return True if self.pkg_dict['type'] is collection, + false otherwise + ''' + dataset_type = self.pkg_dict.get(u'type') + return dataset_type == 'collection' + + def shall_be_part_of_collection(self): + ''' + return the flag 'add_to_collection', + this one is set by ODSHDCATdeProfile._mark_for_adding_to_ckan_collection + ''' + shall_be_part_of_collection = self.pkg_dict.get('add_to_collection') + return shall_be_part_of_collection + + def update_relations_to_collection_members(self): + ''' + update a collection's relationships to its members + + ''' + + id_collection = self.pkg_dict.get('id') + uris_collection_members = self.get_uris_collection_members() + ckan_ids_collection_members = [self.get_id_from_store(uri) for uri in uris_collection_members] + for id_pkg in ckan_ids_collection_members: + helpers_tpsh.add_pkg_to_collection(id_pkg, id_collection) + log.info('Added package with id {} to collection with id {}'.format(id_pkg, id_collection)) + + def get_uris_collection_members(self): + ''' + get the uris of a collection's members + + Returns: + list of uris taken from the 'has_version' in in self.pkg_dict['extras'] + ''' + extras = self.pkg_dict.get('extras') + uris_collection_members_as_string = helpers_odsh.odsh_extract_value_from_extras( + extras, 'has_version' + ) + uris_collection_members_as_string_cleaned = re.sub( + r'[\"\[\] ]', + '', + uris_collection_members_as_string, + 0, 0, + ) + uris_collection_members = uris_collection_members_as_string_cleaned.split(',') + return uris_collection_members + + + def update_relation_to_collection(self): + ''' + update a package's relation to its collection + ''' + id_pkg = self.pkg_dict.get('id') + uri_collection = self.get_collection_uri() + id_collection = uri_store.get_id_from_uri(uri_collection) + helpers_tpsh.add_pkg_to_collection(id_pkg, id_collection) + log.info('Added package with id {} to collection with id {}'.format(id_pkg, id_collection)) + + + def get_collection_uri(self): + ''' + return the collection uri stored in 'extras + ''' + extras = self.pkg_dict.get('extras') + uri_collection = helpers_odsh.odsh_extract_value_from_extras( + extras, 'is_version_of' + ) + if uri_collection: + uri_collection_cleaned = re.sub( + r'[\"\[\] ]', + '', + uri_collection, + 0, 0, + ) + return uri_collection_cleaned + 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 + ''' + uri_store.add_uri(self.pkg_dict) + + @staticmethod + def get_id_from_store(uri): + ''' + get id from known uri + ''' + id = uri_store.get_id_from_uri(uri) + return id + + def is_package_new(self): + date_package_created_as_str = self._get_date_of_package_creation_from_pkg_dict() + if date_package_created_as_str == None: + is_new = False + else: + date_package_created = self._get_date_from_string(date_package_created_as_str) + if date_package_created == None: + is_new = False + else: + is_new = helpers_odsh.is_within_last_month(date_package_created) + return is_new + + def _get_date_of_package_creation_from_pkg_dict(self): + if 'extras' in self.pkg_dict: + extras = self.pkg_dict['extras'] + issued = helpers_odsh.odsh_extract_value_from_extras(extras=extras, key='issued') # is None if issued not in extras + return issued + else: + return None + + @staticmethod + def _get_date_from_string(date_time_str): + date_time_format = '%Y-%m-%dT%H:%M:%S' #e.g. u'2019-06-12T11:56:25' + try: + date_time = datetime.datetime.strptime(date_time_str, date_time_format) + date = date_time.date() + except (ValueError, TypeError): + # if date cannot be converted from string return None + date = None + return date + + def get_prettified_daterange(self): + date_start, date_end = self._get_date_start_and_end_from_pkg_dict() + prettified_daterange = self._construct_prettified_daterange(date_start, date_end) + return prettified_daterange + + @staticmethod + def _construct_prettified_daterange(date_start, date_end): + try: + prettified_daterange = DateRangeFormatter(date_start, date_end).get_formatted_str() + except ValueError as err: + log.warning(err.message) + return '-' + return prettified_daterange + + def _get_date_start_and_end_from_pkg_dict(self): + if 'extras' in self.pkg_dict: + extras = self.pkg_dict['extras'] + date_start_as_str, date_end_as_str = ( + helpers_odsh.odsh_extract_value_from_extras( + extras=extras, key=key) + for key in ('temporal_start', 'temporal_end') + ) + date_start = self._get_date_from_string(date_start_as_str) + date_end = self._get_date_from_string(date_end_as_str) + else: + date_start = None + date_end = None + return date_start, date_end + + +def get_daterange_prettified(pkg_dict): + ''' + wrapper function to use as a template helper + ''' + daterange_prettified = HelperPgkDict(pkg_dict).get_prettified_daterange() + return daterange_prettified \ No newline at end of file diff --git a/ckanext/odsh/helpers.py b/ckanext/odsh/helpers.py index 4acbb1902493fdb026ab908bb670cc49d8d62932..672ff8208dbd99c17da4e17dcb0ed01bc36cbc40 100644 --- a/ckanext/odsh/helpers.py +++ b/ckanext/odsh/helpers.py @@ -1,9 +1,13 @@ +# encoding: utf-8 + import logging import traceback import ast import ckan.plugins.toolkit as toolkit import ckan.logic as logic import ckan.model as model +import ckan.logic.action as action +import ckan.lib.helpers as helpers import json from ckan.common import c import datetime @@ -18,6 +22,7 @@ from ckan.common import request import pdb from urlparse import urlsplit, urlunsplit import subprocess +import ckan.lib.helpers as helpers get_action = logic.get_action log = logging.getLogger(__name__) @@ -116,7 +121,7 @@ def extend_search_convert_local_to_utc_timestamp(str_timestamp): return None if not re.match(r'\d\d\d\d-\d\d-\d\d', str_timestamp): - raise 'wrong format' + raise ValueError('wrong format') dt = parser.parse(str_timestamp, dayfirst=False).isoformat() @@ -268,6 +273,59 @@ def odsh_is_slave(): return 1 if c == 'True' else 0 +def odsh_get_facet_items_dict(name, limit=None): + ''' + Gets all facets like 'get_facet_items_dict' but sorted alphabetically + instead by count. + ''' + if name == 'groups': + limit = 20 + facets = helpers.get_facet_items_dict(name, limit) + facets.sort(key=lambda it: (it['display_name'].lower(), -it['count'])) + return facets + + +def odsh_main_groups(): + '''Return a list of the groups to be shown on the start page.''' + + # Get a list of all the site's groups from CKAN, sorted by number of + # datasets. + groups = toolkit.get_action('group_list')( + data_dict={'all_fields': True}) + + return groups + + +def odsh_now(): + return helpers.render_datetime(datetime.datetime.now(), "%Y-%m-%d") + + +def odsh_group_id_selected(selected, group_id): + if type(selected) is not list: + selected = [selected] + for g in selected: + if (isinstance(g, basestring) and group_id == g) or (type(g) is dict and group_id == g['id']): + return True + + return False + + +def odsh_remove_route(map, routename): + route = None + for i, r in enumerate(map.matchlist): + + if r.name == routename: + route = r + break + if route is not None: + map.matchlist.remove(route) + for key in map.maxkeys: + if key == route.maxkeys: + map.maxkeys.pop(key) + map._routenames.pop(route.name) + break + + def is_within_last_month(date, date_ref=None): ''' date is a datetime.date object containing the date to be checked @@ -293,3 +351,105 @@ def is_within_last_month(date, date_ref=None): if date > one_month_ago: return True return False + +def tpsh_get_all_datasets_belonging_to_collection(context, collection_name): + rel_collection_dict = dict({"id": collection_name}) + name_list = list() + try: + list_rel_collection = get_action('package_relationships_list')(context, rel_collection_dict) + except AssertionError: + #if there does not exist an relationship, returns an empty list + return name_list + for item in list_rel_collection: + item_object = item.get('object') + name_list.append(item_object) + return name_list + +def tpsh_get_all_datasets_belonging_to_collection_by_dataset(context, dataset_name): + collection_name = tpsh_get_collection_name_by_dataset(context, dataset_name) + if collection_name: + name_list = tpsh_get_all_datasets_belonging_to_collection(context, collection_name) + return name_list + return list() + +def tpsh_get_collection_name_by_dataset(context, dataset_name): + rel_dataset_dict = dict({"id" : dataset_name}) + list_rel_dataset = toolkit.get_action('package_relationships_list')(context, rel_dataset_dict) + if not len(list_rel_dataset): + return None + collection_name = list_rel_dataset[0]['object'] + return collection_name + +def tpsh_get_successor_and_predecessor_dataset(context, pkg_dict): + dataset_name = pkg_dict.get('name') + siblings_dicts_with_access = _get_siblings_dicts_with_access(context, 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_siblings_dicts_with_access(context, pkg_dict): + dataset_name = pkg_dict.get('name') + list_of_siblings = tpsh_get_all_datasets_belonging_to_collection_by_dataset(context, 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 + + +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:odsh_extract_value_from_extras(pkg_dict.get('extras'), '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_package_dict(name): + return model.Package.get(name).as_dict() + +def tpsh_get_successor_and_predecessor_urls(context, pkg_dict): + successor_name, predecessor_name = tpsh_get_successor_and_predecessor_dataset(context, 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 short_name_for_category(category_name): + translations = { + 'soci': u'Bevölkerung', + 'educ': u'Bildung', + 'ener': u'Energie', + 'heal': u'Gesundheit', + 'intr': u'Internationales', + 'just': u'Justiz', + 'agri': u'Landwirtschaft', + 'gove': u'Regierung', + 'regi': u'Regionales', + 'envi': u'Umwelt', + 'tran': u'Verkehr', + 'econ': u'Wirtschaft', + 'tech': u'Wissenschaft', + } + return translations.get(category_name) diff --git a/ckanext/odsh/helpers_tpsh.py b/ckanext/odsh/helpers_tpsh.py new file mode 100644 index 0000000000000000000000000000000000000000..608c91016c353ca9de29934c3b06a0c52dd1c4d8 --- /dev/null +++ b/ckanext/odsh/helpers_tpsh.py @@ -0,0 +1,211 @@ +# encoding: utf-8 + +import csv +import datetime +import logging +from string import lower +import json +import re +import urllib2 +from collections import OrderedDict + +from ckan.common import config +import ckan.lib.helpers as helpers +import ckan.logic.action.create as create +import ckan.model as model +import ckan.plugins.toolkit as toolkit + + +import ckanext.odsh.helpers as odsh_helpers + +log = logging.getLogger(__name__) + +CKAN_TYPES = {'http://dcat-ap.de/def/datasetTypes/collection': 'collection'} + + +def map_dct_type_to_ckan_type(dct_type): + ''' + matches the field dct:type from a harvested rdf file + to the corresponding ckan package type + ''' + ckan_type = CKAN_TYPES.get(dct_type) + return ckan_type + +def map_ckan_type_to_dct_type(ckan_type): + DCT_TYPES = _revert_dict(CKAN_TYPES) + dct_type = DCT_TYPES.get(ckan_type) + return dct_type + +def _revert_dict(d): + d_inverse = {v: k for k, v in d.iteritems()} + return d_inverse + +def add_pkg_to_collection(id_pkg, id_collection): + if id_pkg and id_collection: + relationship_dict = { + 'subject': id_pkg, + 'object': id_collection, + 'type': 'child_of', + } + toolkit.get_action('package_relationship_create')(None, relationship_dict) + +def use_matomo(): + '''Return the value of the use_matomo config setting. + + To enable using matomo, add this line to the + [app:main] section of your CKAN config file:: + + ckanext.odsh.use_matomo = True + + Returns ``False`` by default, if the setting is not in the config file. + + :rtype: bool + + ''' + value = config.get('ckanext.odsh.use_matomo', False) + value = toolkit.asbool(value) + return value + +def correct_missing_relationship(pkg_dict, pkg_relationships_from_model): + ''' + This function corrects missing relationship in show package. + Note this fix is only good with one or non relationship. + This error is well known but was not fixed. https://github.com/ckan/ckan/issues/3114 + The error causes the deletation of relationships, because package_show is + used in resource_create to get the package. + ''' + if pkg_relationships_from_model: + relationship_from_model = pkg_relationships_from_model[0] + relationship_list_from_dict = pkg_dict.get('relationships_as_subject') + type_pkg = pkg_dict.get('type') + needs_update = type_pkg == 'dataset' and not relationship_list_from_dict + if needs_update: + relationship_for_package = { + '__extras': { + 'object_package_id': relationship_from_model.object_package_id, + 'revision_id': relationship_from_model.revision_id, + 'subject_package_id': relationship_from_model.subject_package_id, + }, + 'comment': relationship_from_model.subject_package_id, + 'id': relationship_from_model.id, + 'type': relationship_from_model.type, + } + pkg_dict['relationships_as_subject'].append(relationship_for_package) + return pkg_dict + +def get_pkg_relationships_from_model(pkg_dict): + pkg_id = pkg_dict.get('id') + return model.Package.get(pkg_id).get_relationships() + +def load_language_mapping(): + with open(config.get('ckanext.odsh.language_mapping')) as language_mapping_json: + LANGUAGE_MAPPING = json.loads(language_mapping_json.read()) + return LANGUAGE_MAPPING + +def load_json_to_ordered_dict(json_str): + return json.loads(json_str, object_pairs_hook=OrderedDict) + +def load_subject_mapping(): + with open(config.get('ckanext.odsh.subject_mapping')) as subject_mapping_json: + SUBJECT_MAPPING = load_json_to_ordered_dict(subject_mapping_json.read()) + return SUBJECT_MAPPING + +def get_language_of_package(pkg_dict): + LANGUAGE_MAPPING = load_language_mapping() + language_id = _get_language_id(pkg_dict) + if not language_id: + return None + language = LANGUAGE_MAPPING.get(language_id) + return language + +def get_language_icon(pkg_dict): + ICONS = { + "http://publications.europa.eu/resource/authority/language/DAN": '/base/images/icon_lang_danish.png', + "http://publications.europa.eu/resource/authority/language/ENG": '/base/images/icon_lang_english.png', + } + language_id = _get_language_id(pkg_dict) + if not language_id: + return None + return ICONS.get(language_id) + +def _get_language_id(pkg_dict): + language_id = odsh_helpers.odsh_extract_value_from_extras(pkg_dict.get('extras'), 'language') + language_id = pkg_dict.get('language') + if not language_id: + language_id = odsh_helpers.odsh_extract_value_from_extras( + pkg_dict.get('extras'), 'language' + ) + if not language_id: + return None + language_id_cleaned = re.sub('[\[\]\"]', '', language_id) + return language_id_cleaned + +def get_spatial_for_selection(): + mapping_path = config.get('ckanext.odsh.spatial.mapping') + try: + mapping_file = urllib2.urlopen(mapping_path) + except urllib2.URLError: + log.error('Could not load spatial mapping file') + raise + cr = csv.reader(mapping_file, delimiter="\t") + spatial_mapping = list() + for row in cr: + key = row[0].decode('UTF-8') + value = row[1].decode('UTF-8') + spatial_mapping.append({'key':key, 'value':value}) + spatial_mapping.append({'key':'', 'value':''}) + return spatial_mapping + +def get_subject_for_selection(): + SUBJECT_MAPPING = load_subject_mapping() + dict_for_select_box = [{'key': 'empty', 'value':' '}, ] + dict_for_select_box.extend( + [{'key': key, 'value': SUBJECT_MAPPING[key]} for key in SUBJECT_MAPPING] + ) + return dict_for_select_box + +def get_language_for_selection(): + LANGUAGE_MAPPING = load_language_mapping() + dict_for_select_box = [{'key': key, 'value': LANGUAGE_MAPPING[key]} for key in LANGUAGE_MAPPING] + return dict_for_select_box + +def get_package_dict(name): + return model.Package.get(name).as_dict() + +def size_of_fmt(num, suffix='B'): + for unit in ['',' k',' M',' G',' T',' P',' E',' Z']: + if abs(num) < 1000.0: + return "%3.1f%s%s" % (num, unit, suffix) + num /= 1000.0 + return "%.1f%s%s" % (num, 'Y', suffix) + +def get_resource_size(resource): + resource_size = resource.get('size') + if resource_size: + return size_of_fmt(resource_size) + + +def get_address_org(organization): + list_extras = organization.get('extras') + address = dict() + if not list_extras: + return address + for extra in list_extras: + address.update({extra.get('key'):extra.get('value')}) + web = address.get('web') + if web and not web.startswith('http'): + web = 'http://' + web + address.update({'web':web}) + return address + + +def get_body_mail(organization, package): + package_name = package.get('name') + url = helpers.url_for(controller='package', action='read', id=package_name, qualified = True) + title = package.get('title') + anrede = "Sehr geehrte Damen und Herren," + "%0D%0A" + "%0D%0A" + "zu folgendem Eintrag habe ich eine Anmerkung/Frage:" + "%0D%0A" + "%0D%0A" + mail_titel = "Titel: " + title + "%0D%0A" + 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 diff --git a/ckanext/odsh/i18n/ckanext-odsh.pot b/ckanext/odsh/i18n/ckanext-odsh.pot index 1885142a55a7226b363bb5704dd72d4f2b18ed22..32e57994a11c31e0a7e4d6ffb4bd0a9ab4536676 100644 --- a/ckanext/odsh/i18n/ckanext-odsh.pot +++ b/ckanext/odsh/i18n/ckanext-odsh.pot @@ -1,14 +1,14 @@ # Translations template for ckanext-odsh. -# Copyright (C) 2018 ORGANIZATION +# Copyright (C) 2019 ORGANIZATION # This file is distributed under the same license as the ckanext-odsh project. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2018. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2019. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: ckanext-odsh 0.0.1\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2018-09-28 13:50+0000\n" +"POT-Creation-Date: 2019-10-08 12:09+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,151 +17,1154 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" -#: ckanext/odsh/plugin.py:52 -msgid "Groups" +#: ckanext/odsh/plugin.py:158 ckanext/odsh/plugin.py:165 ckanext/odsh/plugin.py:171 +msgid "Herausgeber" msgstr "" -#: ckanext/odsh/templates/header.html:43 -msgid "Search Datasets" +#: ckanext/odsh/plugin.py:159 ckanext/odsh/plugin.py:168 ckanext/odsh/plugin.py:174 +msgid "Kategorie" msgstr "" -#: ckanext/odsh/templates/header.html:46 -msgid "Search" +#: ckanext/odsh/plugin.py:160 +msgid "Informationsgegenstand" msgstr "" -#: ckanext/odsh/templates/header.html:65 +#: ckanext/odsh/plugin.py:161 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:223 +msgid "Tags" +msgstr "" + +#: ckanext/odsh/plugin.py:166 ckanext/odsh/plugin.py:172 +msgid "Dateiformat" +msgstr "" + +#: ckanext/odsh/plugin.py:167 ckanext/odsh/plugin.py:173 +msgid "Lizenz" +msgstr "" + +#: ckanext/odsh/validation.py:261 +#, python-format +msgid "Tag \"%s\" must be alphanumeric characters or symbols: -_.:()" +msgstr "" + +#: ckanext/odsh/validation.py:312 +msgid "Subject must be a known URI." +msgstr "" + +#: ckanext/odsh/validation.py:334 +msgid "Subject must not be empty." +msgstr "" + +#: ckanext/odsh/fanstatic/moment.js:6 +msgid "Weeks" +msgstr "" + +#: ckanext/odsh/fanstatic/odsh_image-upload.js:63 +msgid "Link" +msgstr "" + +#: ckanext/odsh/fanstatic/odsh_image-upload.js:64 +msgid "Link to a URL on the internet (you can also link to an API)" +msgstr "" + +#: ckanext/odsh/fanstatic/odsh_image-upload.js:71 +#: ckanext/odsh/templates/header.html:56 +msgid "Upload" +msgstr "" + +#: ckanext/odsh/fanstatic/odsh_image-upload.js:75 +#: ckanext/odsh/templates/macros/form.html:342 +msgid "Remove" +msgstr "" + +#: ckanext/odsh/fanstatic/odsh_image-upload.js:83 +#: ckanext/odsh/templates/macros/form.html:528 +msgid "Image" +msgstr "" + +#: ckanext/odsh/fanstatic/odsh_image-upload.js:90 +msgid "Upload a file on your computer" +msgstr "" + +#: ckanext/odsh/fanstatic/odsh_image-upload.js:115 +#: ckanext/odsh/fanstatic/odsh_image-upload.js:186 +#: ckanext/odsh/templates/organization/snippets/organization_form.html:18 +#: ckanext/odsh/templates/package/snippets/resource_form.html:33 +msgid "URL" +msgstr "" + +#: ckanext/odsh/fanstatic/odsh_image-upload.js:125 +#: ckanext/odsh/fanstatic/odsh_image-upload.js:221 +msgid "File" +msgstr "" + +#: ckanext/odsh/templates/error_document_template.html:3 +#, python-format +msgid "Error %(error_code)s" +msgstr "" + +#: ckanext/odsh/templates/header.html:47 ckanext/odsh/templates/home/index.html:7 msgid "Start" msgstr "" -#: ckanext/odsh/templates/header.html:66 -msgid "Datasets" +#: ckanext/odsh/templates/header.html:48 +msgid "Dokumente" msgstr "" -#: ckanext/odsh/templates/header.html:67 -#: ckanext/odsh/templates/snippets/package_item.html:82 -msgid "Organizations" +#: ckanext/odsh/templates/header.html:49 +msgid "Infos" +msgstr "" + +#: ckanext/odsh/templates/datarequests/base.html:12 +#: ckanext/odsh/templates/datarequests/close.html:6 +#: ckanext/odsh/templates/datarequests/edit.html:6 +#: ckanext/odsh/templates/datarequests/new.html:6 +msgid "Data Proposal" +msgstr "" + +#: ckanext/odsh/templates/datarequests/close.html:3 +#: ckanext/odsh/templates/datarequests/close.html:8 +#: ckanext/odsh/templates/datarequests/close.html:12 +msgid "Close Data Proposal" +msgstr "" + +#: ckanext/odsh/templates/datarequests/comment.html:18 +#: ckanext/odsh/templates/datarequests/show.html:15 +#: ckanext/odsh/templates/organization/read_base.html:18 +#: ckanext/odsh/templates/package/resource_read.html:31 +msgid "Manage" +msgstr "" + +#: ckanext/odsh/templates/datarequests/comment.html:22 +#: ckanext/odsh/templates/datarequests/show.html:19 +msgid "Close" +msgstr "" + +#: ckanext/odsh/templates/datarequests/comment.html:37 +#: ckanext/odsh/templates/datarequests/snippets/comment_form.html:25 +#: ckanext/odsh/templates/datarequests/snippets/datarequest_item.html:38 +msgid "Add New Comment" +msgstr "" + +#: ckanext/odsh/templates/datarequests/comment.html:53 +msgid "Released" +msgstr "" + +#: ckanext/odsh/templates/datarequests/comment.html:54 +msgid "Suggester" +msgstr "" + +#: ckanext/odsh/templates/datarequests/comment.html:54 +msgid "None" +msgstr "" + +#: ckanext/odsh/templates/datarequests/comment.html:56 +msgid "Status" +msgstr "" + +#: ckanext/odsh/templates/datarequests/comment.html:60 +#: ckanext/odsh/templates/datarequests/snippets/datarequest_item.html:13 +msgid "Done" +msgstr "" + +#: ckanext/odsh/templates/datarequests/comment.html:64 +#: ckanext/odsh/templates/datarequests/show.html:40 +#: ckanext/odsh/templates/datarequests/snippets/datarequest_item.html:17 +msgid "Open" +msgstr "" + +#: ckanext/odsh/templates/datarequests/edit.html:3 +#: ckanext/odsh/templates/datarequests/edit.html:8 +#: ckanext/odsh/templates/datarequests/edit.html:12 +msgid "Edit Data Proposal" +msgstr "" + +#: ckanext/odsh/templates/datarequests/index.html:8 +msgid "Search Data Requests..." +msgstr "" + +#: ckanext/odsh/templates/datarequests/index.html:12 +msgid "Add Request" +msgstr "" + +#: ckanext/odsh/templates/datarequests/new.html:3 +#: ckanext/odsh/templates/datarequests/snippets/datarequest_form.html:33 +msgid "Create Data Request" +msgstr "" + +#: ckanext/odsh/templates/datarequests/new.html:7 +msgid "Create Data Proposal" +msgstr "" + +#: ckanext/odsh/templates/datarequests/new.html:11 +msgid "Create New Data Request" +msgstr "" + +#: ckanext/odsh/templates/datarequests/show.html:3 +#: ckanext/odsh/templates/datarequests/show.html:25 +msgid "Data Request" +msgstr "" + +#: ckanext/odsh/templates/datarequests/show.html:8 +msgid "Data Requests" +msgstr "" + +#: ckanext/odsh/templates/datarequests/show.html:35 +msgid "Closed" +msgstr "" + +#: ckanext/odsh/templates/datarequests/show.html:64 +#: ckanext/odsh/templates/datarequests/snippets/comments.html:11 +msgid "This data request has not been commented yet" msgstr "" -#: ckanext/odsh/templates/header.html:68 -msgid "Info" +#: ckanext/odsh/templates/datarequests/snippets/comment_form.html:17 +msgid "Add a new Comment" msgstr "" -#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:5 +#: ckanext/odsh/templates/datarequests/snippets/comment_form.html:22 +msgid "Cancel" +msgstr "" + +#: ckanext/odsh/templates/datarequests/snippets/comment_form.html:23 +msgid "Update Comment" +msgstr "" + +#: ckanext/odsh/templates/datarequests/snippets/comments.html:3 +msgid "Comments" +msgstr "" + +#: ckanext/odsh/templates/datarequests/snippets/datarequest_form.html:17 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:9 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:13 msgid "Title" msgstr "" -#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:17 -msgid "URL" +#: ckanext/odsh/templates/datarequests/snippets/datarequest_form.html:17 +msgid "eg. Data Request Name" msgstr "" -#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:23 +#: ckanext/odsh/templates/datarequests/snippets/datarequest_form.html:21 +#: ckanext/odsh/templates/organization/snippets/organization_form.html:20 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:27 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:32 msgid "Description" msgstr "" -#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:33 -#: ckanext/odsh/templates/package/snippets/package_metadata_fields.html:18 +#: ckanext/odsh/templates/datarequests/snippets/datarequest_form.html:21 +msgid "eg. Data Request description" +msgstr "" + +#: ckanext/odsh/templates/datarequests/snippets/datarequest_form.html:29 +msgid "Are you sure you want to delete this data request?" +msgstr "" + +#: ckanext/odsh/templates/datarequests/snippets/datarequest_form.html:30 +#: ckanext/odsh/templates/organization/snippets/organization_form.html:60 +#: ckanext/odsh/templates/package/edit_view.html:19 +#: ckanext/odsh/templates/package/snippets/package_form.html:32 +#: ckanext/odsh/templates/package/snippets/resource_form.html:96 +msgid "Delete" +msgstr "" + +#: ckanext/odsh/templates/datarequests/snippets/datarequest_list.html:11 +msgid "No Data Requests found with the given criteria" +msgstr "" + +#: ckanext/odsh/templates/datarequests/snippets/datarequest_list.html:13 +#: ckanext/odsh/templates/organization/index.html:50 +msgid "How about creating one?" +msgstr "" + +#: ckanext/odsh/templates/datarequests/snippets/new_datarequest_form.html:7 +msgid "Suggest New Data Request" +msgstr "" + +#: ckanext/odsh/templates/home/index.html:4 +msgid "Welcome" +msgstr "" + +#: ckanext/odsh/templates/macros/form.html:374 +msgid "Custom" +msgstr "" + +#: ckanext/odsh/templates/macros/form.html:374 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:59 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:94 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:249 msgid "This field is required" msgstr "" -#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:34 -#: ckanext/odsh/templates/snippets/package_item.html:83 -msgid "License" +#: ckanext/odsh/templates/macros/form.html:510 +msgid "Required field" +msgstr "" + +#: ckanext/odsh/templates/macros/form.html:526 +msgid "http://example.com/my-image.jpg" +msgstr "" + +#: ckanext/odsh/templates/macros/form.html:527 +msgid "Image URL" +msgstr "" + +#: ckanext/odsh/templates/macros/form.html:544 +msgid "Clear Upload" +msgstr "" + +#: ckanext/odsh/templates/organization/index.html:3 +#: ckanext/odsh/templates/organization/index.html:6 +#: ckanext/odsh/templates/organization/index.html:33 +#: ckanext/odsh/templates/organization/read_base.html:3 +#: ckanext/odsh/templates/organization/read_base.html:6 +#: ckanext/odsh/templates/package/base.html:14 +msgid "Organizations" +msgstr "" + +#: ckanext/odsh/templates/organization/index.html:17 +msgid "Search organizations" msgstr "" +#: ckanext/odsh/templates/organization/index.html:30 +msgid "Add Organization" +msgstr "" + +#: ckanext/odsh/templates/organization/index.html:37 +msgid "Search organizations..." +msgstr "" + +#: ckanext/odsh/templates/organization/index.html:48 +msgid "There are currently no organizations for this site" +msgstr "" + +#: ckanext/odsh/templates/organization/read.html:5 +msgid "Add Dataset" +msgstr "" + +#: ckanext/odsh/templates/organization/read.html:27 +#: ckanext/odsh/templates/snippets/search_form.html:11 +msgid "Relevance" +msgstr "" + +#: ckanext/odsh/templates/organization/read.html:28 +#: ckanext/odsh/templates/snippets/search_form.html:6 +#: ckanext/odsh/templates/snippets/search_form.html:12 +msgid "Name Ascending" +msgstr "" + +#: ckanext/odsh/templates/organization/read.html:29 +#: ckanext/odsh/templates/snippets/search_form.html:6 +#: ckanext/odsh/templates/snippets/search_form.html:13 +msgid "Name Descending" +msgstr "" + +#: ckanext/odsh/templates/organization/read.html:30 +#: ckanext/odsh/templates/snippets/search_form.html:14 +msgid "Issued Ascending" +msgstr "" + +#: ckanext/odsh/templates/organization/read.html:31 +#: ckanext/odsh/templates/snippets/search_form.html:15 +msgid "Issued Descending" +msgstr "" + +#: ckanext/odsh/templates/organization/read.html:32 +#: ckanext/odsh/templates/snippets/package_item.html:90 +#: ckanext/odsh/templates/snippets/search_form.html:20 +msgid "Popular" +msgstr "" + +#: ckanext/odsh/templates/organization/read.html:36 +#: ckanext/odsh/templates/snippets/search_form.html:3 +msgid "Search datasets..." +msgstr "" + +#: ckanext/odsh/templates/organization/read.html:37 +#: ckanext/odsh/templates/package/base.html:7 +#: ckanext/odsh/templates/package/base.html:17 +#: ckanext/odsh/templates/package/read.html:17 +#: ckanext/odsh/templates/package/read.html:22 +#: ckanext/odsh/templates/package/search.html:5 +#: ckanext/odsh/templates/snippets/organization.html:59 +msgid "Documents" +msgstr "" + +#: ckanext/odsh/templates/organization/read_base.html:29 +#: ckanext/odsh/templates/snippets/organization.html:47 +msgid "There is no description for this organization" +msgstr "" + +#: ckanext/odsh/templates/organization/snippets/organization_form.html:10 +#: ckanext/odsh/templates/package/snippets/resource_form.html:39 +msgid "Name" +msgstr "" + +#: ckanext/odsh/templates/organization/snippets/organization_form.html:10 +msgid "My Organization" +msgstr "" + +#: ckanext/odsh/templates/organization/snippets/organization_form.html:18 +msgid "my-organization" +msgstr "" + +#: ckanext/odsh/templates/organization/snippets/organization_form.html:20 +msgid "A little information about my organization..." +msgstr "" + +#: ckanext/odsh/templates/organization/snippets/organization_form.html:35 +#: ckanext/odsh/templates/organization/snippets/organization_form.html:48 +msgid "Custom Field" +msgstr "" + +#: ckanext/odsh/templates/organization/snippets/organization_form.html:60 +msgid "" +"Are you sure you want to delete this Organization? This will delete all the " +"public and private datasets belonging to this organization." +msgstr "" + +#: ckanext/odsh/templates/organization/snippets/organization_form.html:63 +msgid "Save Organization" +msgstr "" + +#: ckanext/odsh/templates/organization/snippets/organization_item.html:15 +#: ckanext/odsh/templates/snippets/package_item.html:75 +msgid "View {organization_name}" +msgstr "" + +#: ckanext/odsh/templates/package/base.html:22 +msgid "Dokuments" +msgstr "" + +#: ckanext/odsh/templates/package/base.html:23 +#: ckanext/odsh/templates/package/read.html:23 +msgid "Create Dataset" +msgstr "" + +#: ckanext/odsh/templates/package/edit_base.html:10 +#: ckanext/odsh/templates/package/resource_edit.html:3 +#: ckanext/odsh/templates/package/resource_edit_base.html:12 +msgid "Edit" +msgstr "" + +#: ckanext/odsh/templates/package/edit_base.html:12 +#: ckanext/odsh/templates/package/new_package_form.html:8 +#: ckanext/odsh/templates/package/snippets/resource_form.html:14 +msgid "odsh Create Dataset" +msgstr "" + +#: ckanext/odsh/templates/package/edit_base.html:26 +#: ckanext/odsh/templates/package/resource_edit_base.html:28 +#: ckanext/odsh/templates/package/snippets/resource_form.html:86 +msgid "back" +msgstr "" + +#: ckanext/odsh/templates/package/edit_view.html:3 +#: ckanext/odsh/templates/package/edit_view.html:4 +#: ckanext/odsh/templates/package/edit_view.html:8 +#: ckanext/odsh/templates/package/edit_view.html:12 +msgid "Edit view" +msgstr "" + +#: ckanext/odsh/templates/package/edit_view.html:21 +#: ckanext/odsh/templates/package/new_view.html:31 +#: ckanext/odsh/templates/package/read.html:81 +msgid "Preview" +msgstr "" + +#: ckanext/odsh/templates/package/edit_view.html:22 +msgid "Update" +msgstr "" + +#: ckanext/odsh/templates/package/new_package_form.html:18 +msgid "next" +msgstr "" + +#: ckanext/odsh/templates/package/new_package_form.html:20 +msgid "Update Dataset" +msgstr "" + +#: ckanext/odsh/templates/package/new_resource.html:5 +msgid "Add data to the dataset" +msgstr "" + +#: ckanext/odsh/templates/package/new_resource.html:11 +#: ckanext/odsh/templates/package/new_resource_not_draft.html:8 +msgid "Add New Resource" +msgstr "" + +#: ckanext/odsh/templates/package/new_resource_not_draft.html:3 +#: ckanext/odsh/templates/package/new_resource_not_draft.html:4 +msgid "Add resource" +msgstr "" + +#: ckanext/odsh/templates/package/new_view.html:3 +#: ckanext/odsh/templates/package/new_view.html:4 +#: ckanext/odsh/templates/package/new_view.html:8 +#: ckanext/odsh/templates/package/new_view.html:12 +msgid "Add view" +msgstr "" + +#: ckanext/odsh/templates/package/new_view.html:19 +msgid "" +" Data Explorer views may be slow and unreliable unless the DataStore " +"extension is enabled. For more information, please see the <a " +"href='http://docs.ckan.org/en/latest/maintaining/data-viewer.html#viewing-" +"structured-data-the-data-explorer' target='_blank'>Data Explorer " +"documentation</a>. " +msgstr "" + +#: ckanext/odsh/templates/package/new_view.html:32 +#: ckanext/odsh/templates/package/snippets/resource_form.html:111 +msgid "Add" +msgstr "" + +#: ckanext/odsh/templates/package/read.html:35 +#: ckanext/odsh/templates/snippets/package_item.html:79 +msgid "Draft" +msgstr "" + +#: ckanext/odsh/templates/package/read.html:38 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:279 +#: ckanext/odsh/templates/snippets/organization.html:25 +#: ckanext/odsh/templates/snippets/package_item.html:81 +msgid "Deleted" +msgstr "" + +#: ckanext/odsh/templates/package/read.html:44 +msgid "Manage Dataset" +msgstr "" + +#: ckanext/odsh/templates/package/read.html:51 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:255 +#: ckanext/odsh/templates/snippets/package_item.html:70 +msgid "Private" +msgstr "" + +#: ckanext/odsh/templates/package/read.html:62 +msgid "Publisher" +msgstr "" + +#: ckanext/odsh/templates/package/read.html:101 +msgid "latest collection member" +msgstr "" + +#: ckanext/odsh/templates/package/read.html:106 +#: ckanext/odsh/templates/package/read.html:111 +msgid "predecessor" +msgstr "" + +#: ckanext/odsh/templates/package/read.html:116 +#: ckanext/odsh/templates/package/read.html:121 +msgid "successor" +msgstr "" + +#: ckanext/odsh/templates/package/resource_edit_base.html:32 +msgid "Edit resource" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:37 +msgid "View" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:39 +msgid "API Endpoint" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:41 +msgid "Go to resource" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:43 +msgid "Download" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:56 +#: ckanext/odsh/templates/package/resource_read.html:58 +msgid "URL:" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:66 +msgid "From the dataset abstract" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:68 +#, python-format +msgid "Source: <a href=\"%(url)s\">%(dataset)s</a>" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:109 +msgid "There are no views created for this resource yet." +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:113 +msgid "Not seeing the views you were expecting?" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:115 +msgid "Click here for more information." +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:118 +msgid "Here are some reasons you may not be seeing expected views:" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:120 +msgid "No view has been created that is suitable for this resource" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:121 +msgid "The site administrators may not have enabled the relevant view plugins" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:122 +msgid "" +"If a view requires the DataStore, the DataStore plugin may not be enabled, or" +" the data may not have been pushed to the DataStore, or the DataStore hasn't " +"finished processing the data yet" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:144 +msgid "Additional Information" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:148 +msgid "Field" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:149 +msgid "Value" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:154 +msgid "Data last updated" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:155 +#: ckanext/odsh/templates/package/resource_read.html:159 +#: ckanext/odsh/templates/package/resource_read.html:163 +#: ckanext/odsh/templates/package/resource_read.html:167 +msgid "unknown" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:158 +msgid "Metadata last updated" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:162 +msgid "Created" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:166 +#: ckanext/odsh/templates/package/snippets/resource_form.html:75 +#: ckanext/odsh/templates/package/snippets/resource_info.html:26 +msgid "Format" +msgstr "" + +#: ckanext/odsh/templates/package/resource_read.html:170 +#: ckanext/odsh/templates/package/snippets/info.html:94 #: ckanext/odsh/templates/package/snippets/package_basic_fields.html:93 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:99 +msgid "License" +msgstr "" + +#: ckanext/odsh/templates/package/search.html:63 +#: ckanext/odsh/templates/package/snippets/info.html:72 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:142 +msgid "timerange" +msgstr "" + +#: ckanext/odsh/templates/package/search.html:78 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:160 +#: ckanext/odsh/templates/snippets/search_form.html:117 +msgid "from" +msgstr "" + +#: ckanext/odsh/templates/package/search.html:81 +msgid "date start" +msgstr "" + +#: ckanext/odsh/templates/package/search.html:85 +#: ckanext/odsh/templates/package/search.html:100 +msgid "Date" +msgstr "" + +#: ckanext/odsh/templates/package/search.html:89 +#: ckanext/odsh/templates/snippets/search_form.html:130 +msgid "wrong_start_date_for_search" +msgstr "" + +#: ckanext/odsh/templates/package/search.html:93 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:174 +#: ckanext/odsh/templates/snippets/search_form.html:144 +msgid "to" +msgstr "" + +#: ckanext/odsh/templates/package/search.html:96 +msgid "date end" +msgstr "" + +#: ckanext/odsh/templates/package/search.html:104 +#: ckanext/odsh/templates/snippets/search_form.html:157 +msgid "wrong_end_date_for_search" +msgstr "" + +#: ckanext/odsh/templates/package/search.html:107 +msgid "submit date search" +msgstr "" + +#: ckanext/odsh/templates/package/view_edit_base.html:9 +msgid "All views" +msgstr "" + +#: ckanext/odsh/templates/package/view_edit_base.html:12 +msgid "View view" +msgstr "" + +#: ckanext/odsh/templates/package/view_edit_base.html:37 +msgid "View preview" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/info.html:27 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:85 +#: ckanext/odsh/templates/snippets/package_item.html:114 +msgid "subject" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/info.html:38 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:237 +msgid "language" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/info.html:50 +#: ckanext/odsh/templates/snippets/package_item.html:97 +msgid " category: " +msgid_plural " categories: " +msgstr[0] "" +msgstr[1] "" + +#: ckanext/odsh/templates/package/snippets/info.html:82 +#: ckanext/odsh/templates/snippets/package_item.html:115 +msgid "issued" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/info.html:87 +msgid "modified" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/info.html:110 +msgid "share this dataset" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/info.html:125 +msgid "send an email" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:19 +msgid "Enter title" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:36 +msgid "Enter description" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:58 msgid "Organization" msgstr "" -#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:97 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:66 msgid "No organization" msgstr "" -#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:113 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:119 +#: ckanext/odsh/templates/user/snippets/login_form.html:23 +msgid "enter name" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:229 +msgid "odsh tags placeholder" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:243 +msgid "Spatial uri" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:248 +msgid "Visibility" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:255 +msgid "Public" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:269 msgid "State" msgstr "" -#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:117 +#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:276 msgid "Active" msgstr "" -#: ckanext/odsh/templates/package/snippets/package_basic_fields.html:119 -#: ckanext/odsh/templates/snippets/package_item.html:49 -msgid "Deleted" +#: ckanext/odsh/templates/package/snippets/package_form.html:31 +msgid "Are you sure you want to delete this dataset?" msgstr "" -#: ckanext/odsh/templates/package/snippets/package_metadata_fields.html:47 -msgid "Visibility" +#: ckanext/odsh/templates/package/snippets/package_form.html:38 +msgid "Next: Add Data" msgstr "" -#: ckanext/odsh/templates/package/snippets/package_metadata_fields.html:50 -#: ckanext/odsh/templates/snippets/package_item.html:37 -msgid "Private" +#: ckanext/odsh/templates/package/snippets/resource_form.html:30 +msgid "odsh_resource_upload_error_label" msgstr "" -#: ckanext/odsh/templates/package/snippets/package_metadata_fields.html:50 -msgid "Public" +#: ckanext/odsh/templates/package/snippets/resource_form.html:33 +msgid "Data-Upload" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:33 +msgid "http://example.com/external-data.csv" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:38 +msgid "odsh_resource_name_error_label" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:39 +msgid "Enter name of the dataset" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:54 +msgid "Last Modified" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:54 +msgid "eg. 2012-06-05" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:57 +msgid "File Size" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:57 +msgid "eg. 1024" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:60 +#: ckanext/odsh/templates/package/snippets/resource_form.html:63 +msgid "MIME Type" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:60 +#: ckanext/odsh/templates/package/snippets/resource_form.html:63 +msgid "eg. application/json" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:73 +msgid "odsh_resource_format_error_label" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:75 +msgid "eg. CSV, XML or JSON" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:95 +msgid "Are you sure you want to delete this resource?" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_form.html:106 +msgid "Upload dataset" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_info.html:14 +msgid "document" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_info.html:18 +msgid "Name Resource" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_info.html:22 +msgid "Resource count" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resource_item.html:24 +#: ckanext/odsh/templates/package/snippets/resource_item.html:41 +msgid "download file" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resources_list.html:13 +msgid "Dateien" +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resources_list.html:26 +#, python-format +msgid "" +" <p class=\"empty\">This dataset has no data, <a href=\"%(url)s\">why not add" +" some?</a></p> " +msgstr "" + +#: ckanext/odsh/templates/package/snippets/resources_list.html:30 +msgid "This dataset has no data" msgstr "" -#: ckanext/odsh/templates/snippets/facet_list.html:26 -msgid "Show More {facet_type}" +#: ckanext/odsh/templates/package/snippets/stages.html:23 +#: ckanext/odsh/templates/package/snippets/stages.html:25 +msgid "Enter data" msgstr "" -#: ckanext/odsh/templates/snippets/facet_list.html:30 -msgid "Show Only Popular {facet_type}" +#: ckanext/odsh/templates/package/snippets/stages.html:30 +#: ckanext/odsh/templates/package/snippets/stages.html:34 +#: ckanext/odsh/templates/package/snippets/stages.html:36 +msgid "Add dataset" msgstr "" -#: ckanext/odsh/templates/snippets/facet_list.html:34 +#: ckanext/odsh/templates/snippets/custom_search_form.html:6 +msgid "{number} data request found for \"{query}\"" +msgid_plural "{number} data requests found for \"{query}\"" +msgstr[0] "" +msgstr[1] "" + +#: ckanext/odsh/templates/snippets/custom_search_form.html:7 +msgid "No data requests found for \"{query}\"" +msgstr "" + +#: ckanext/odsh/templates/snippets/custom_search_form.html:8 +msgid "{number} data proposal found" +msgid_plural "{number} data proposals found" +msgstr[0] "" +msgstr[1] "" + +#: ckanext/odsh/templates/snippets/custom_search_form.html:9 +msgid "No data proposal found" +msgstr "" + +#: ckanext/odsh/templates/snippets/facet_list.html:65 +msgid "Show More" +msgstr "" + +#: ckanext/odsh/templates/snippets/facet_list.html:69 +msgid "Show Less" +msgstr "" + +#: ckanext/odsh/templates/snippets/facet_list.html:75 msgid "There are no {facet_type} that match this search" msgstr "" -#: ckanext/odsh/templates/snippets/language_selector.html:13 -#: ckanext/odsh/templates/snippets/search_form.html:34 -msgid "Go" +#: ckanext/odsh/templates/snippets/follow_button.html:9 +msgid "Unfollow" msgstr "" -#: ckanext/odsh/templates/snippets/package_item.html:47 -msgid "Draft" +#: ckanext/odsh/templates/snippets/follow_button.html:14 +msgid "Follow" msgstr "" -#: ckanext/odsh/templates/snippets/package_item.html:58 -msgid "Popular" +#: ckanext/odsh/templates/snippets/home_breadcrumb_item.html:5 +msgid "Home" +msgstr "" + +#: ckanext/odsh/templates/snippets/map.html:4 +msgid "map" +msgstr "" + +#: ckanext/odsh/templates/snippets/organization.html:44 +msgid "read more" +msgstr "" + +#: ckanext/odsh/templates/snippets/organization.html:55 +msgid "Followers" msgstr "" #: ckanext/odsh/templates/snippets/package_item.html:65 -msgid "This dataset has no description" +msgid "NEW" msgstr "" -#: ckanext/odsh/templates/snippets/search_form.html:3 -msgid "Search datasets..." +#: ckanext/odsh/templates/snippets/search_box.html:5 +msgid "enter search query for datasets" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_box.html:11 +#: ckanext/odsh/templates/snippets/search_box.html:13 +msgid "Search dataset" msgstr "" #: ckanext/odsh/templates/snippets/search_form.html:4 msgid "Order by" msgstr "" -#: ckanext/odsh/templates/snippets/search_form.html:5 -msgid "Name Ascending" +#: ckanext/odsh/templates/snippets/search_form.html:8 +msgid "Date Ascending" msgstr "" -#: ckanext/odsh/templates/snippets/search_form.html:5 -msgid "Name Descending" +#: ckanext/odsh/templates/snippets/search_form.html:8 +msgid "Date Descending" msgstr "" -#: ckanext/odsh/templates/snippets/search_form.html:60 -msgid "Remove" +#: ckanext/odsh/templates/snippets/search_form.html:16 +msgid "Start Date Descending" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_form.html:17 +msgid "Start Date Ascending" msgstr "" -#: ckanext/odsh/templates/snippets/search_form.html:65 +#: ckanext/odsh/templates/snippets/search_form.html:18 +msgid "End Date Descending" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_form.html:19 +msgid "End Date Ascending" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_form.html:45 +msgid "rss feeds" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_form.html:67 +msgid "Go" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_form.html:93 +#: ckanext/odsh/templates/snippets/search_form.html:94 +#: ckanext/odsh/templates/snippets/search_form.html:121 +#: ckanext/odsh/templates/snippets/search_form.html:122 +#: ckanext/odsh/templates/snippets/search_form.html:134 +#: ckanext/odsh/templates/snippets/search_form.html:135 +#: ckanext/odsh/templates/snippets/search_form.html:148 +#: ckanext/odsh/templates/snippets/search_form.html:149 +#: ckanext/odsh/templates/snippets/search_form.html:161 +#: ckanext/odsh/templates/snippets/search_form.html:162 +#: ckanext/odsh/templates/snippets/search_form.html:182 +#: ckanext/odsh/templates/snippets/search_form.html:183 +msgid "Remove filters" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_form.html:102 msgid "Filter Results" msgstr "" -#: ckanext/odsh/templates/snippets/search_form.html:72 +#: ckanext/odsh/templates/snippets/search_form.html:117 +#: ckanext/odsh/templates/snippets/search_form.html:130 +#: ckanext/odsh/templates/snippets/search_form.html:144 +#: ckanext/odsh/templates/snippets/search_form.html:157 +msgid "daterange" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_form.html:178 +msgid "map search active" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_form.html:196 msgid " <p class=\"extra\">Please try another search.</p> " msgstr "" -#: ckanext/odsh/templates/snippets/search_form.html:78 +#: ckanext/odsh/templates/snippets/search_form.html:202 msgid "" " <p id=\"search-error\"><strong>There was an error while searching.</strong> " "Please try again.</p> " msgstr "" +#: ckanext/odsh/templates/snippets/search_result_text.html:15 +msgid "{number} dataset found for \"{query}\"" +msgid_plural "{number} datasets found for \"{query}\"" +msgstr[0] "" +msgstr[1] "" + +#: ckanext/odsh/templates/snippets/search_result_text.html:17 +msgid "No datasets found for \"{query}\"" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_result_text.html:18 +msgid "{number} dataset found" +msgid_plural "{number} datasets found" +msgstr[0] "" +msgstr[1] "" + +#: ckanext/odsh/templates/snippets/search_result_text.html:19 +msgid "No datasets found" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_result_text.html:22 +msgid "{number} group found for \"{query}\"" +msgid_plural "{number} groups found for \"{query}\"" +msgstr[0] "" +msgstr[1] "" + +#: ckanext/odsh/templates/snippets/search_result_text.html:23 +msgid "No groups found for \"{query}\"" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_result_text.html:24 +msgid "{number} group found" +msgid_plural "{number} groups found" +msgstr[0] "" +msgstr[1] "" + +#: ckanext/odsh/templates/snippets/search_result_text.html:25 +msgid "No groups found" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_result_text.html:28 +msgid "{number} organization found for \"{query}\"" +msgid_plural "{number} organizations found for \"{query}\"" +msgstr[0] "" +msgstr[1] "" + +#: ckanext/odsh/templates/snippets/search_result_text.html:30 +msgid "No organizations found for \"{query}\"" +msgstr "" + +#: ckanext/odsh/templates/snippets/search_result_text.html:31 +msgid "{number} organization found" +msgid_plural "{number} organizations found" +msgstr[0] "" +msgstr[1] "" + +#: ckanext/odsh/templates/snippets/search_result_text.html:32 +msgid "No organizations found" +msgstr "" + +#: ckanext/odsh/templates/user/login.html:3 +#: ckanext/odsh/templates/user/login.html:6 +#: ckanext/odsh/templates/user/login.html:12 +msgid "Login" +msgstr "" + +#: ckanext/odsh/templates/user/login.html:25 +msgid "Forgotten your password?" +msgstr "" + +#: ckanext/odsh/templates/user/login.html:28 +msgid "change password" +msgstr "" + +#: ckanext/odsh/templates/user/logout.html:4 +msgid "Logout" +msgstr "" + +#: ckanext/odsh/templates/user/logout.html:7 +#: ckanext/odsh/templates/user/logout.html:14 +msgid "Logged Out" +msgstr "" + +#: ckanext/odsh/templates/user/logout.html:16 +msgid "You are now logged out." +msgstr "" + +#: ckanext/odsh/templates/user/snippets/login_form.html:22 +msgid "Username" +msgstr "" + +#: ckanext/odsh/templates/user/snippets/login_form.html:29 +msgid "Password" +msgstr "" + +#: ckanext/odsh/templates/user/snippets/login_form.html:30 +msgid "enter password" +msgstr "" + +#: ckanext/odsh/templates/user/snippets/login_form.html:34 +msgid "Remember me" +msgstr "" + +#: ckanext/odsh/templates/user/snippets/login_form.html:40 +msgid "do Login" +msgstr "" + diff --git a/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-datarequest.po b/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-datarequest.po index fd21f87655c476ef50e6cff6dd164628a3ee79e0..bee83c54a7a62620fb761664ca9ef2dbb311abde 100644 --- a/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-datarequest.po +++ b/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-datarequest.po @@ -4,4 +4,4 @@ #: ckanext/datarequests/templates/datarequests/snippets/datarequest_form.html:47 #: ckanext/datarequests/templates/datarequests/snippets/new_datarequest_form.html:7 msgid "Create Data Request" -msgstr "Datenanfrage " +msgstr "Dokumentanfrage " diff --git a/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-odsh.1.po b/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-odsh.1.po index aa5b97f3ba5f2e3045614f6a71261353a1271a00..a749e5df932a6c2c92a64805d8ba3be4d8033ec0 100644 --- a/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-odsh.1.po +++ b/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-odsh.1.po @@ -27,7 +27,7 @@ msgid "Start" msgstr "Start" msgid "Datasets" -msgstr "Daten" +msgstr "Dokumente" #: ckanext/odsh/templates/header.html:12 msgid "Organizations" diff --git a/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-odsh.mo b/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-odsh.mo index 8be79d9fd27fdd19815a279d577a8af7bd65b6ed..e5231b5c445d99ba368944a4870058a2d41ed820 100644 Binary files a/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-odsh.mo and b/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-odsh.mo differ diff --git a/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-odsh.po b/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-odsh.po index 43b3fa3427fa45fda80caa70fad17077ee4f9521..63e6dee0ae0ba2803ce21295de817c91331f6462 100644 --- a/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-odsh.po +++ b/ckanext/odsh/i18n/de/LC_MESSAGES/ckanext-odsh.po @@ -24,8 +24,17 @@ msgid "Start typing…" msgstr "Bitte Schlagwort eingeben" + +msgid "datasets" +msgstr "Datenästze" + + +msgid "dataset" +msgstr "Datensatz" + + msgid "Search dataset" -msgstr "Nach Datensatz suchen" +msgstr "Nach Datensätzen suchen" msgid "Search organizations" msgstr "Nach Herausgeber suchen" @@ -115,7 +124,7 @@ msgid "to" msgstr "bis" msgid "Data-Upload" -msgstr "Daten-Upload" +msgstr "Datensatz-Upload" msgid "Enter name of the dataset" msgstr "Namen der Distribution eingeben" @@ -339,10 +348,10 @@ msgid "Score" msgstr "Open-Data-Eigenschaften" msgid "Issued Ascending" -msgstr "Veröffentlichungsdatum aufsteigend" +msgstr "Veröffentlichungsdatum: Älteste zuerst" msgid "Issued Descending" -msgstr "Veröffentlichungsdatum absteigend" +msgstr "Veröffentlichungsdatum: Neueste zuerst" msgid "Name Resource" msgstr "Name Ressource" @@ -407,7 +416,7 @@ msgid "tran" msgstr "Verkehr" msgid "Datarequest" -msgstr "Datenvorschlag" +msgstr "Datensatz" msgid "Add Request" msgstr "Datensatz vorschlagen" @@ -461,3 +470,75 @@ msgstr "Datum absteigend" msgid "Date Ascending" msgstr "Datum aufsteigend" + +msgid "subject" +msgstr "Informationsgegenstand" + +msgid "Subject must not be empty." +msgstr "Bitte geben Sie einen Informationsgegenstand ein." + +msgid "type subject" +msgstr "Informationsgegenstand eingeben" + +msgid "date start" +msgstr "Startdatum" + +msgid "date end" +msgstr "Enddatum" + +msgid "main nav menu" +msgstr "Navigations-Hauptmenü" + +msgid "Start Date Descending" +msgstr "Startdatum: Neueste zuerst" + +msgid "Start Date Ascending" +msgstr "Startdatum: Älteste zuerst" + +msgid "End Date Descending" +msgstr "Enddatum: Neueste zuerst" + +msgid "End Date Ascending" +msgstr "Enddatum: Älteste zuerst" + +msgid "send an email" +msgstr "E-Mail senden" + +msgid "enter search query for datasets" +msgstr "Suchanfrage für Datensätze eingeben" + +msgid "Remove filters" +msgstr "Filter deaktivieren" + +msgid "successor" +msgstr "Nachfolger" + +msgid "predecessor" +msgstr "Vorgänger" + +msgid "latest collection member" +msgstr "Neuester Datensatz" + +msgid "language" +msgstr "Sprache" + + +msgid "Subject must be a known URI." +msgstr "Bitte geben Sie einen Informationsgegenstand an" + +msgid "Documents" +msgstr "Dokumente" + +msgid " category: " +msgid_plural " categories: " +msgstr[0] "Kategorie:" +msgstr[1] "Kategorien:" + +msgid "Publisher" +msgstr "Herausgeber" + +msgid "Name Ascending" +msgstr "Name: A – Z" + +msgid "Name Descending" +msgstr "Name: Z – A" diff --git a/ckanext/odsh/lib/odsh_icap_client.cfg b/ckanext/odsh/lib/odsh_icap_client.cfg deleted file mode 100644 index 654b57a83fc705ebff6769c89035df98f583432a..0000000000000000000000000000000000000000 --- a/ckanext/odsh/lib/odsh_icap_client.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[DEFAULT] -# the IP of the ICAP-Server -host = 10.61.127.77 - -# The port of the ICAP-Server -port = 1344 - -# the IP of the client-machine -clientip = 127.0.0.1 diff --git a/ckanext/odsh/lib/odsh_icap_client.py b/ckanext/odsh/lib/odsh_icap_client.py index fe8101eb25237f0a3ffb410f17821703e2e0f029..df3df673829d5ae31c74c860da45ff0f01ffb68f 100644 --- a/ckanext/odsh/lib/odsh_icap_client.py +++ b/ckanext/odsh/lib/odsh_icap_client.py @@ -1,37 +1,48 @@ import socket import sys import time -#from ckan.common import config -#import logging +import logging +from ckan.common import config +import ckan.plugins.toolkit as toolkit -#log = logging.getLogger(__name__) +log = logging.getLogger(__name__) + +def _read_from_config(key): + value = config.get(key, None) + if value is None: + _raise_KeyError_if_not_in_config(key) + return value + +def _raise_KeyError_if_not_in_config(key): + raise KeyError('key {} is not defined in ckan config file.'.format(key)) class ODSHICAPRequest(object): def __init__(self, FILENAME, FILEBUFF): - config = [] - self.HOST = "10.61.127.77" #'10.61.127.77' - self.PORT = 1344 - - self.CLIENTIP = '127.0.0.1' + try: + self.HOST = _read_from_config('ckanext.odsh.icap.host') + self.PORT = toolkit.asint(_read_from_config('ckanext.odsh.icap.port')) + self.CLIENTIP = _read_from_config('ckanext.odsh.icap.clientip') + except KeyError, e: + log.error(e) self.FILENAME = FILENAME self.FILEBUFF = FILEBUFF - + def send(self): - #log.info("----- Starting ICAP-Request via RESPMOD -----") + print("----- Starting ICAP-Request via RESPMOD -----") # socket connect try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error as msg: - #log.error(msg[1]) + sys.stderr.write("[ERROR] %s\n" % msg[1]) sys.exit(1) try: sock.connect((self.HOST, self.PORT)) except socket.error as msg: - #log.error(msg[1]) + sys.stderr.write("[ERROR] %s\n" % msg[1]) sys.exit(2) # create and send header @@ -46,7 +57,7 @@ class ODSHICAPRequest(object): data_response = self._recvall(sock) response_object = self._parse_response(data_response) - #log.info("----- Finished ICAP-Request via RESPMOD -----") + print("----- Finished ICAP-Request via RESPMOD -----") return response_object @@ -74,36 +85,20 @@ class ODSHICAPRequest(object): return icapRequest def _sendfile(self, fileBuffer, sock): - #log.info('Start sending file.') + print('start sending file') PACK_SIZE = 1024 # in bytes l = fileBuffer.read(PACK_SIZE) while(l): - #log.info('Sending {} bytes of data...'.format(len(l))) + print('sending %d bytes of data...' % len(l)) sock.send('{:02X}'.format(len(l)).encode()) sock.send("\r\n".encode()) sock.send(l) sock.send("\r\n".encode()) l = fileBuffer.read(PACK_SIZE) - - def _sendfile_old(self, fileName, sock): - #log.info('OLD: Start sending file.') - PACK_SIZE = 1024 # in bytes - - with open(fileName) as f: - l = f.read(PACK_SIZE) - while(l): - #log.info('Sending {} bytes of data...'.format(len(l))) - sock.send('{:02X}'.format(len(l)).encode()) - sock.send("\r\n".encode()) - sock.send(l) - sock.send("\r\n".encode()) - l = f.read(PACK_SIZE) - #log.info('Done sending.') - def _recvall(self, sock): - #log.info('Receiving response from icap server...') + print('receiving response from icap server') BUFF_SIZE = 4096 # 4 KiB data = b'' while True: @@ -115,7 +110,7 @@ class ODSHICAPRequest(object): return data def _parse_response(self, data_response): - #log.info('Parsing response...') + print('parsing response') lines = data_response.split('\r\n') http_status_code = self._parse_response_http_statuscode(lines) http_block = self._parse_block(lines, 'HTTP/1.1') @@ -164,9 +159,7 @@ class ODSHParsedICAPResponse(object): def virus_found(self): if (self.http_status_code != 200) and (self.http_status_code != 403): - msg = 'Received an unknown http response code: {}'.format(self.http_status_code) - #log.warning(msg) - raise UnknownResponseException(msg) + raise UnknownResponseException('Received an unknown http response code: %d' % self.http_status_code) return self.http_status_code != 200 diff --git a/ckanext/odsh/lib/uploader.py b/ckanext/odsh/lib/uploader.py index 93452f933405aec491cf34ffdff2881284c92618..ab89260a54cada77b3de43f81834e2ab600bd67f 100644 --- a/ckanext/odsh/lib/uploader.py +++ b/ckanext/odsh/lib/uploader.py @@ -1,23 +1,77 @@ import ckan.logic as logic -from ckan.lib.uploader import ResourceUpload +from ckan.lib.uploader import ResourceUpload, Upload +import ckan.plugins.toolkit as toolkit +from pylons import config + from odsh_icap_client import ODSHICAPRequest +import logging +import hashlib + +log = logging.getLogger(__name__) + + +def _icap_virus_found(filename, upload_file): + # the flag skip_icap_virus_check in can be used during development + skip_icap_virus_check = toolkit.asbool( + config.get('ckanext.odsh.skip_icap_virus_check', 'False') + ) + if skip_icap_virus_check: + log.debug("WARNING: icap virus check skipped, remove parameter ckanext.odsh.skip_icap_virus_check from ckan's ini file") + return False + if filename and upload_file: + response_object = ODSHICAPRequest(filename, upload_file).send() + return response_object.virus_found() + -#import logging +def _raise_validation_error_if_virus_found(filename, upload_file): + if _icap_virus_found(filename, upload_file): + raise logic.ValidationError({'upload': ['Virus gefunden']}) -#log = logging.getLogger(__name__) +def calculate_hash(upload_file): + upload_file.seek(0) + hash_md5 = hashlib.md5() + for chunk in iter(lambda: upload_file.read(4096), b""): + hash_md5.update(chunk) + return hash_md5.hexdigest() + #return hashlib.md5(upload_file.read()).hexdigest() + + +def _raise_validation_error_if_hash_values_differ(upload_file, resource): + hash_from_resource = resource.get('hash') + if hash_from_resource: + hash_from_calculation = calculate_hash(upload_file) + if not hash_from_calculation == hash_from_resource: + log.debug('hash from calculation: {}'.format(hash_from_calculation)) + log.debug('hash from resource: {}'.format(hash_from_resource)) + raise logic.ValidationError({'upload': ['Berechneter Hash und mitgelieferter Hash sind unterschiedlich']}) + class ODSHResourceUpload(ResourceUpload): def __init__(self, resource): - #log.info("Resource({}) uploaded.".format(resource)) + log.debug("Resource({}) uploaded.".format(resource)) super(ODSHResourceUpload, self).__init__(resource) - if self._icap_virus_found(): - #log.info("Found Virus!") - raise logic.ValidationError({'upload': ['Virus gefunden']}) - - def _icap_virus_found(self): - if self.filename and self.upload_file: - response_object = ODSHICAPRequest(self.filename, self.upload_file).send() - return response_object.virus_found() - + if hasattr(self, 'filename') and hasattr(self, 'upload_file'): + _raise_validation_error_if_virus_found(self.filename, self.upload_file) + _raise_validation_error_if_hash_values_differ(self.upload_file, resource) + + +class ODSHUpload(Upload): + ''' + custom uploader to upload resources and group images + see https://docs.ckan.org/en/ckan-2.7.3/extensions/plugin-interfaces.html?highlight=iuploader#ckan.plugins.interfaces.IUploader + this uploader object hooks into the upload method in order to + scan for viruses within the uploaded content + ''' + + def __init__(self, upload_to, old_filename=None): + super(ODSHUpload, self).__init__(upload_to, old_filename) + + def update_data_dict(self, data_dict, url_field, file_field, clear_field): + super(ODSHUpload, self).update_data_dict(data_dict, url_field, file_field, clear_field) + + def upload(self, max_size=2): + if hasattr(self, 'filename') and hasattr(self, 'upload_file'): + _raise_validation_error_if_virus_found(self.filename, self.upload_file) + super(ODSHUpload, self).upload(max_size) \ No newline at end of file diff --git a/ckanext/odsh/logic/action.py b/ckanext/odsh/logic/action.py index 6b952764c6422b470c93eb1103c3f3fecc1a6396..481a72ac130bf8447c89be96dcc27f6ddde28832 100644 --- a/ckanext/odsh/logic/action.py +++ b/ckanext/odsh/logic/action.py @@ -1,7 +1,10 @@ import logging +import ckan.logic as logic +from ckan.logic.action.update import user_update from ckan.logic.action.create import package_create, user_create, group_member_create import ckan.model as model import ckan.lib.dictization.model_dictize as model_dictize +from ckan.lib.munge import munge_title_to_name import ckan.plugins.toolkit as toolkit from ckan.lib.search.common import ( make_connection, SearchError, SearchQueryError @@ -13,8 +16,11 @@ log = logging.getLogger(__name__) def odsh_package_create(context, data_dict): + pkg_type = data_dict.get('type', None) + if pkg_type == 'collection': + return package_create(context, data_dict) munge_increment_name(data_dict) - if data_dict.get('type', None) != 'dataset': + if pkg_type != 'dataset': return package_create(context, data_dict) issued = False for extra in data_dict.get('extras'): @@ -27,26 +33,51 @@ def odsh_package_create(context, data_dict): def munge_increment_name(data_dict): - from ckan.lib.munge import munge_title_to_name - - name_base = name = munge_title_to_name(data_dict['title']) - pkg = model.Package.get(name) - i = 0 - while pkg: - i += 1 - name = name_base + str(i) + title_from_dict = data_dict.get('title') + if title_from_dict: + name_base = name = munge_title_to_name(title_from_dict) pkg = model.Package.get(name) - log.debug('name: %s' % name) - data_dict['name'] = name + i = 0 + while pkg: + i += 1 + name = name_base + str(i) + pkg = model.Package.get(name) + log.debug('name: %s' % name) + data_dict['name'] = name + +def check_password(password): + return (len(password) >= 8 and + any(c.islower() for c in password) and + any(c.isupper() for c in password) and + any((c.isalpha()==False) for c in password)) #Number or Special character + +PASSWORD_ERROR_MESSAGE = {'security': ['Passwort muss mindestens acht Zeichen, einen Gross-, einen Kleinbuchstaben und entweder eine Zahl oder ein Sondernzeichen enthalten!']} def odsh_user_create(context, data_dict): model = context['model'] - user = user_create(context, data_dict) - groups = toolkit.get_action('group_list')(data_dict={'all_fields': False}) - for group in groups: - group_member_create(context, {'id': group, 'username': user.get('name'), 'role': 'member'}) - return model_dictize.user_dictize(model.User.get(user.get('name')), context) + password = data_dict.get('password') + if not password: + password = data_dict.get('password1') + if check_password(password): + user = user_create(context, data_dict) + groups = toolkit.get_action('group_list')(data_dict={'all_fields': False}) + + for group in groups: + group_member_create(context, {'id': group, 'username': user.get('name'), 'role': 'member'}) + return model_dictize.user_dictize(model.User.get(user.get('name')), context) + else: + raise logic.ValidationError(PASSWORD_ERROR_MESSAGE) + +def tpsh_user_update(context, data_dict): + password = data_dict.get('password') + if not password: + password = data_dict.get('password1') + if password and not check_password(password): + raise logic.ValidationError(PASSWORD_ERROR_MESSAGE) + return user_update(context, data_dict) + + @toolkit.side_effect_free diff --git a/ckanext/odsh/matomo.py b/ckanext/odsh/matomo.py index a3fd6fc1ae16564f5ad5eeed7de94e04f5d2c5a3..15ac1b5c7c70e3413fab7051a14bb6f13e4b0c93 100644 --- a/ckanext/odsh/matomo.py +++ b/ckanext/odsh/matomo.py @@ -4,7 +4,16 @@ from ckan.common import c, request from pylons import config import logging from ckan.plugins.toolkit import enqueue_job +import ckanext.odsh.helpers_tpsh as helpers_tpsh +def do_if_use_matomo(func): + def wrapper(*args, **kwargs): + use_matomo = helpers_tpsh.use_matomo() + if use_matomo: + return func(*args, **kwargs) + return wrapper + +@do_if_use_matomo def create_matomo_request(userId=None): headers = { 'HTTP_USER_AGENT': request.headers.get('User-Agent'), diff --git a/ckanext/odsh/odsh_logger.py b/ckanext/odsh/odsh_logger.py new file mode 100644 index 0000000000000000000000000000000000000000..54eea1b0bd88889c57c8b9d761580e83f654ff34 --- /dev/null +++ b/ckanext/odsh/odsh_logger.py @@ -0,0 +1,53 @@ +from multiline_formatter.formatter import MultilineMessagesFormatter +import ckan.plugins as plugins +import ckan.plugins.toolkit as toolkit + + +class OdshLogger(MultilineMessagesFormatter): + multiline_marker = '...' + multiline_fmt = multiline_marker + ' : %(message)s' + + def format(self, record): + """ + This is mostly the same as logging.Formatter.format except for the splitlines() thing. + This is done so (copied the code) to not make logging a bottleneck. It's not lots of code + after all, and it's pretty straightforward. + """ + endl_marker = '\n... : ";' + record.message = record.getMessage() + if self.usesTime(): + record.asctime = self.formatTime(record, self.datefmt) + if '\n' in record.message: + splitted = record.message.splitlines() + output = self._fmt % dict(record.__dict__, message=splitted.pop(0)) + output += ' ' + self.multiline_marker % record.__dict__ + '\n' + output += '\n'.join( + self.multiline_fmt % dict(record.__dict__, message=line) + for line in splitted + ) + output = output.replace('"', '\\"') + output += endl_marker + else: + output = self._fmt % record.__dict__ + + if record.exc_info: + # Cache the traceback text to avoid converting it multiple times + # (it's constant anyway) + if not record.exc_text: + record.exc_text = self.formatException(record.exc_info) + if record.exc_text: + output += ' ' + self.multiline_marker % record.__dict__ + '\n' + try: + output += '\n'.join( + self.multiline_fmt % dict(record.__dict__, message=line) + for index, line in enumerate(record.exc_text.splitlines()) + ) + output = output.replace('"', '\\"') + output += endl_marker + except UnicodeError: + output += '\n'.join( + self.multiline_fmt % dict(record.__dict__, message=line) + for index, line + in enumerate(record.exc_text.decode(sys.getfilesystemencoding(), 'replace').splitlines()) + ) + return output \ No newline at end of file diff --git a/ckanext/odsh/pdf_to_thumbnail/__init__.py b/ckanext/odsh/pdf_to_thumbnail/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ckanext/odsh/pdf_to_thumbnail/action.py b/ckanext/odsh/pdf_to_thumbnail/action.py new file mode 100644 index 0000000000000000000000000000000000000000..bd5d713086311721f593ba276ea9d650f1e77f87 --- /dev/null +++ b/ckanext/odsh/pdf_to_thumbnail/action.py @@ -0,0 +1,30 @@ +# ckan +import ckan.plugins.toolkit as toolkit +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 + + +def before_package_delete(context, package_id_dict): + pkg_dict = toolkit.get_action('package_show')(context, package_id_dict) + if helpers.check_access('package_delete', pkg_dict): + thumbnail.remove_thumbnail(context) + return package_delete(context, package_id_dict) + +def before_package_update(context, pkg_dict): + if helpers.check_access('package_update', pkg_dict): + package_id =pkg_dict.get('id') + package = toolkit.get_action('package_show')(context, {'id': package_id}) + old_private = package.get('private') + new_private = pkg_dict.get('private') + old_filename = package.get('thumbnail') + if old_filename: + if str(old_private) != str(new_private): + new_filename = thumbnail.change_filepath(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}) + return package_update(context, pkg_dict) \ No newline at end of file diff --git a/ckanext/odsh/pdf_to_thumbnail/helpers.py b/ckanext/odsh/pdf_to_thumbnail/helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..faddfaa0691b19f158df50b13b352e9ff2a642c5 --- /dev/null +++ b/ckanext/odsh/pdf_to_thumbnail/helpers.py @@ -0,0 +1,27 @@ + +from ckan.lib.helpers import is_url, url_for + +def thumbnail_namespace(filename): + return "/" + filename + +def get_download_link_for_thumbnail(package): + resources = package.get('resources') + for resource in resources[::-1]: + url_type =resource.get('url_type') + mimetype = resource.get('mimetype') + if url_type == 'upload' and mimetype == 'application/pdf': + package_id = resource.get('package_id') + resource_id = resource.get('id') + pre_resource_url = resource.get('url') + if is_url(pre_resource_url): + url_resource = pre_resource_url + else: + url_resource = url_for(controller='package', + action='resource_download', + id=package_id, + resource_id=resource_id, + filename=pre_resource_url, + qualified = True) + + + return url_resource diff --git a/ckanext/odsh/pdf_to_thumbnail/plugin.py b/ckanext/odsh/pdf_to_thumbnail/plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..715235990b6515a9b9d86a2a4f063c8da5e2bf7e --- /dev/null +++ b/ckanext/odsh/pdf_to_thumbnail/plugin.py @@ -0,0 +1,58 @@ +import os + + +#from ckan +import ckan.plugins as plugins + +#pdf_to_thumbnail +import thumbnail +import action as thumbnail_action +import helpers as thumbnail_helpers + +import logging +log = logging.getLogger(__name__) + + +class ThumbnailPlugin(plugins.SingletonPlugin): + plugins.implements(plugins.IResourceController, inherit=True) + plugins.implements(plugins.IConfigurer, inherit=True) + plugins.implements(plugins.IActions, inherit=True) + plugins.implements(plugins.ITemplateHelpers) + + +#IResourceController + def after_create(self, context, resource): + _, filename = thumbnail.create_thumbnail(context, resource) + thumbnail.write_thumbnail_into_package(context, resource, filename) + + def after_update(self, context, resource): + thumbnail.check_and_create_thumbnail_after_update(context, resource) + + def after_delete(self, context, resources): + thumbnail.create_thumbnail_for_last_resource(context, resources) + +#IConfigurer + + def update_config(self, config_): + storage_path = config_.get('ckan.storage_path') + public_dir = os.path.join(storage_path, 'thumbnail') + if config_.get('extra_public_paths'): + config_['extra_public_paths'] += ',' + public_dir + else: + config_['extra_public_paths'] = public_dir + +#IActions + + def get_actions(self): + return {'package_delete': thumbnail_action.before_package_delete, + 'package_update': thumbnail_action.before_package_update + } + +#ITemplateHelpers + + def get_helpers(self): + + return { + 'thumbnail_namespace':thumbnail_helpers.thumbnail_namespace, + 'thumbail_get_download_link':thumbnail_helpers.get_download_link_for_thumbnail + } diff --git a/ckanext/odsh/pdf_to_thumbnail/thumbnail.py b/ckanext/odsh/pdf_to_thumbnail/thumbnail.py new file mode 100644 index 0000000000000000000000000000000000000000..768cfdbfcfedc0a9b8e84fb6bea8c71472fd4524 --- /dev/null +++ b/ckanext/odsh/pdf_to_thumbnail/thumbnail.py @@ -0,0 +1,170 @@ +import os +import tempfile +import magic +from pdf2image import convert_from_bytes +import logging +from ckan.common import config +import urllib2 +import requests + +import binascii +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): + package = context.get('package') + package_id = package.id + package= toolkit.get_action('package_show')(context, {'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 get_filepath_to_resource(resource): + 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)) + + +def create_thumbnail_from_file(file, old_filename): + width = config.get('ckan.thumbnail.size.width', 410) + filename = random_filename() + 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): + 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 True, 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 + +def remove_thumbnail(context): + 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) + +def create_thumbnail(context, resource): + log.debug('create_thumbnail') + 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): + log.debug('check_and_create_thumbnail_after_update') + package_id = resource.get('package_id') + package = toolkit.get_action('package_show')(context, {'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) + + +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) + 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) diff --git a/ckanext/odsh/plugin.py b/ckanext/odsh/plugin.py index add7226bd682d4e49eaff5ff131bf3975f875378..ea003f69bb8abac9affd2421dd9e33613f81c3f3 100644 --- a/ckanext/odsh/plugin.py +++ b/ckanext/odsh/plugin.py @@ -1,341 +1,266 @@ -from multiline_formatter.formatter import MultilineMessagesFormatter -import datetime -import json -import ckan.plugins as plugins -import ckan.plugins.toolkit as toolkit -from ckan.lib.plugins import DefaultTranslation -from ckan.lib.plugins import DefaultDatasetForm -from ckan.logic.validators import tag_string_convert -from ckan.logic.schema import default_extras_schema -from ckan.common import OrderedDict -from ckanext.odsh.lib.uploader import ODSHResourceUpload -import ckan.lib.helpers as helpers -import helpers as odsh_helpers -import ckanext.odsh.logic.action as action -from ckanext.dcat.interfaces import IDCATRDFHarvester -from ckanext.dcatde.extras import Extras - -from routes.mapper import SubMapper -from pylons import config +# import from third parties from dateutil.parser import parse -from ckan import model - -import ckan.plugins as p - +import json import logging -import validation -import precondition - +from multiline_formatter.formatter import MultilineMessagesFormatter +import os +from pylons import config +from routes.mapper import SubMapper import sys -log = logging.getLogger(__name__) - -# from functools import wraps -# from flask import Flask, redirect, jsonify -# app = Flask(__name__) - -# def get_http_exception_handler(app): -# """Overrides the default http exception handler to return JSON.""" -# handle_http_exception = app.handle_http_exception -# @wraps(handle_http_exception) -# def ret_val(exception): -# print("HEHREHR") -# exc = handle_http_exception(exception) -# return jsonify({'code':exc.code, 'message':exc.description}), exc.code -# return ret_val +# imports from ckan +from ckan.common import OrderedDict +import ckan.lib.helpers as helpers +from ckan.lib.plugins import DefaultTranslation, DefaultDatasetForm +from ckan.logic.validators import tag_string_convert +import ckan.plugins as plugins +import ckan.plugins.toolkit as toolkit +import ckan.model as model -# # Override the HTTP exception handler. -# app.handle_http_exception = get_http_exception_handler(app) +# imports from this extension +import ckanext.odsh.helpers as odsh_helpers +import ckanext.odsh.helpers_tpsh as helpers_tpsh +import ckanext.odsh.helper_pkg_dict as helper_pkg_dict +from helper_pkg_dict import HelperPgkDict +import ckanext.odsh.logic.action as action +import ckanext.odsh.validation as validation +import ckanext.odsh.search as search +from ckanext.odsh.odsh_logger import OdshLogger +import ckanext.odsh.tools as tools -# def my_except_hook(exctype, value, traceback): -# print('GOT excepton') -# log.exception(value) -# sys.__excepthook__(exctype, value, traceback) -# print('INSTALL EX') -# sys.excepthook = my_except_hook +log = logging.getLogger(__name__) _ = toolkit._ - -class OdshLogger(MultilineMessagesFormatter): - multiline_marker = '...' - multiline_fmt = multiline_marker + ' : %(message)s' - - def format(self, record): - """ - This is mostly the same as logging.Formatter.format except for the splitlines() thing. - This is done so (copied the code) to not make logging a bottleneck. It's not lots of code - after all, and it's pretty straightforward. - """ - endl_marker = '\n... : ";' - record.message = record.getMessage() - if self.usesTime(): - record.asctime = self.formatTime(record, self.datefmt) - if '\n' in record.message: - splitted = record.message.splitlines() - output = self._fmt % dict(record.__dict__, message=splitted.pop(0)) - output += ' ' + self.multiline_marker % record.__dict__ + '\n' - output += '\n'.join( - self.multiline_fmt % dict(record.__dict__, message=line) - for line in splitted - ) - output = output.replace('"', '\\"') - output += endl_marker - else: - output = self._fmt % record.__dict__ - - if record.exc_info: - # Cache the traceback text to avoid converting it multiple times - # (it's constant anyway) - if not record.exc_text: - record.exc_text = self.formatException(record.exc_info) - if record.exc_text: - output += ' ' + self.multiline_marker % record.__dict__ + '\n' - try: - output += '\n'.join( - self.multiline_fmt % dict(record.__dict__, message=line) - for index, line in enumerate(record.exc_text.splitlines()) - ) - output = output.replace('"', '\\"') - output += endl_marker - except UnicodeError: - output += '\n'.join( - self.multiline_fmt % dict(record.__dict__, message=line) - for index, line - in enumerate(record.exc_text.decode(sys.getfilesystemencoding(), 'replace').splitlines()) - ) - return output - - -def odsh_get_facet_items_dict(name, limit=None): - ''' - Gets all facets like 'get_facet_items_dict' but sorted alphabetically - instead by count. - ''' - facets = helpers.get_facet_items_dict(name, limit) - facets.sort(key=lambda it: (it['display_name'].lower(), -it['count'])) - return facets - - -def odsh_main_groups(): - '''Return a list of the groups to be shown on the start page.''' - - # Get a list of all the site's groups from CKAN, sorted by number of - # datasets. - groups = toolkit.get_action('group_list')( - data_dict={'all_fields': True}) - - return groups - - -def odsh_now(): - return helpers.render_datetime(datetime.datetime.now(), "%Y-%m-%d") - - -def odsh_group_id_selected(selected, group_id): - if type(selected) is not list: - selected = [selected] - for g in selected: - if (isinstance(g, basestring) and group_id == g) or (type(g) is dict and group_id == g['id']): - return True - - return False - - -def remove_route(map, routename): - route = None - for i, r in enumerate(map.matchlist): - - if r.name == routename: - route = r - break - if route is not None: - map.matchlist.remove(route) - for key in map.maxkeys: - if key == route.maxkeys: - map.maxkeys.pop(key) - map._routenames.pop(route.name) - break - - -class OdshIcapPlugin(plugins.SingletonPlugin): - plugins.implements(plugins.IUploader, inherit=True) - - def get_resource_uploader(self, data_dict): - return ODSHResourceUpload(data_dict) - - -class OdshAutocompletePlugin(plugins.SingletonPlugin): +class OdshPlugin(plugins.SingletonPlugin, DefaultTranslation, DefaultDatasetForm): plugins.implements(plugins.IActions) + plugins.implements(plugins.IConfigurer) + plugins.implements(plugins.IDatasetForm) + plugins.implements(plugins.IFacets) + plugins.implements(plugins.IPackageController, inherit=True) + plugins.implements(plugins.IRoutes, inherit=True) + plugins.implements(plugins.ITemplateHelpers) + plugins.implements(plugins.ITranslation) + plugins.implements(plugins.IValidators) + plugins.implements(plugins.IResourceController, inherit=True) - def get_actions(self): - return {'autocomplete': action.autocomplete} + + # IActions + def get_actions(self): + return {'package_create': action.odsh_package_create, + 'user_update':action.tpsh_user_update, + 'user_create': action.odsh_user_create} -class OdshHarvestPlugin(plugins.SingletonPlugin): - plugins.implements(plugins.IRoutes, inherit=True) - plugins.implements(plugins.IConfigurer) + + # IConfigurer def update_config(self, config_): - toolkit.add_template_directory(config_, 'harvest_templates') - plugins.implements(plugins.IRoutes, inherit=True) + toolkit.add_template_directory(config_, 'templates') + toolkit.add_public_directory(config_, 'public') + toolkit.add_resource('fanstatic', 'odsh') - def before_map(self, map): - DATASET_TYPE_NAME = 'harvest' - controller = 'ckanext.odsh.controller:OdshHarvestController' - - map.connect('{0}_delete'.format(DATASET_TYPE_NAME), '/' + - DATASET_TYPE_NAME + '/delete/:id', controller=controller, action='delete') - map.connect('{0}_refresh'.format(DATASET_TYPE_NAME), '/' + DATASET_TYPE_NAME + '/refresh/:id', controller=controller, - action='refresh') - map.connect('{0}_admin'.format(DATASET_TYPE_NAME), '/' + - DATASET_TYPE_NAME + '/admin/:id', controller=controller, action='admin') - map.connect('{0}_about'.format(DATASET_TYPE_NAME), '/' + - DATASET_TYPE_NAME + '/about/:id', controller=controller, action='about') - map.connect('{0}_clear'.format(DATASET_TYPE_NAME), '/' + - DATASET_TYPE_NAME + '/clear/:id', controller=controller, action='clear') - - map.connect('harvest_job_list', '/' + DATASET_TYPE_NAME + - '/{source}/job', controller=controller, action='list_jobs') - map.connect('harvest_job_show_last', '/' + DATASET_TYPE_NAME + - '/{source}/job/last', controller=controller, action='show_last_job') - map.connect('harvest_job_show', '/' + DATASET_TYPE_NAME + - '/{source}/job/{id}', controller=controller, action='show_job') - map.connect('harvest_job_abort', '/' + DATASET_TYPE_NAME + - '/{source}/job/{id}/abort', controller=controller, action='abort_job') - - map.connect('harvest_object_show', '/' + DATASET_TYPE_NAME + - '/object/:id', controller=controller, action='show_object') - map.connect('harvest_object_for_dataset_show', '/dataset/harvest_object/:id', - controller=controller, action='show_object', ref_type='dataset') - - org_controller = 'ckanext.harvest.controllers.organization:OrganizationController' - map.connect('{0}_org_list'.format(DATASET_TYPE_NAME), '/organization/' + - DATASET_TYPE_NAME + '/' + '{id}', controller=org_controller, action='source_list') - return map - def after_map(self, map): - return map + # IDatasetForm + def package_types(self): + # This plugin doesn't handle any special package types, it just + # registers itself as the default (above). + return [] -class OdshDCATHarvestPlugin(plugins.SingletonPlugin): - plugins.implements(IDCATRDFHarvester, inherit=True) + def is_fallback(self): + # Return True to register this plugin as the default handler for + # package types not handled by any other IDatasetForm plugin. + return True + + def create_package_schema(self): + schema = super(OdshPlugin, self).create_package_schema() + self._update_schema(schema) + self._tpsh_update_create_or_update_package_schema(schema) + return schema - def before_update(self, harvest_object, dataset_dict, temp_dict): + def update_package_schema(self): + schema = super(OdshPlugin, self).update_package_schema() + self._update_schema(schema) + self._tpsh_update_create_or_update_package_schema(schema) + return schema - existing_package_dict = self._get_existing_dataset(harvest_object.guid) - new_dataset_extras = Extras(dataset_dict['extras']) - if new_dataset_extras.key('modified') and \ - new_dataset_extras.value('modified') < existing_package_dict.get('metadata_modified'): - log.info("Modified date of new dataset is not newer than " - + "the already exisiting dataset, ignoring new one.") - dataset_dict.clear() + def show_package_schema(self): + schema = super(OdshPlugin, self).show_package_schema() + self._tpsh_update_show_package_schema(schema) + return schema - def _get_existing_dataset(self, guid): - ''' - Checks if a dataset with a certain guid extra already exists + def _update_schema(self, schema): + for field in ['title', 'license_id']: + schema.update({field: [toolkit.get_converter('not_empty')]}) + + for i, item in enumerate(schema['tags']['name']): + if item == toolkit.get_validator('tag_name_validator'): + schema['tags']['name'][i] = toolkit.get_validator( + 'odsh_tag_name_validator') + for i, item in enumerate(schema['tag_string']): + if item == tag_string_convert: + schema['tag_string'][i] = validation.tag_string_convert - Returns a dict as the ones returned by package_show - ''' + schema['resources'].update({ + 'url': [toolkit.get_converter('not_empty')], + 'format': [toolkit.get_converter('not_empty')], + }) - datasets = model.Session.query(model.Package.id) \ - .join(model.PackageExtra) \ - .filter(model.PackageExtra.key == 'guid') \ - .filter(model.PackageExtra.value == guid) \ - .filter(model.Package.state == 'active') \ - .all() + schema['extras'].update({ + 'key': [ + toolkit.get_converter('known_spatial_uri'), + toolkit.get_converter('validate_licenseAttributionByText'), + ] + }) + schema.update( + {'__extras': [toolkit.get_converter('odsh_validate_extras')]}) - if not datasets: - return None - elif len(datasets) > 1: - log.error('Found more than one dataset with the same guid: {0}' - .format(guid)) + def _tpsh_update_create_or_update_package_schema(self, schema): + schema.update({ + 'language': [ + toolkit.get_validator('ignore_missing'), + toolkit.get_converter('convert_to_extras') + ], + 'thumbnail': [ + toolkit.get_validator('ignore_missing'), + toolkit.get_converter('convert_to_extras') + ], - return p.toolkit.get_action('package_show')({}, {'id': datasets[0][0]}) + }) + return schema + + def _tpsh_update_show_package_schema(self, schema): + schema.update({ + 'language': [ + toolkit.get_converter('convert_from_extras'), + toolkit.get_validator('ignore_missing') + ], + 'thumbnail': [ + toolkit.get_converter('convert_from_extras'), + toolkit.get_validator('ignore_missing') + ], + }) + return schema -class OdshPlugin(plugins.SingletonPlugin, DefaultTranslation, DefaultDatasetForm): - plugins.implements(plugins.IConfigurer) - plugins.implements(plugins.ITemplateHelpers) - plugins.implements(plugins.IRoutes, inherit=True) - plugins.implements(plugins.ITranslation) - plugins.implements(plugins.IFacets) - plugins.implements(plugins.IDatasetForm) - plugins.implements(plugins.IValidators) - plugins.implements(plugins.IPackageController, inherit=True) - plugins.implements(plugins.IActions) + # IFacets + + def dataset_facets(self, facets_dict, package_type): + return OrderedDict({'organization': _('Herausgeber'), + 'groups': _('Kategorie'), + 'res_format': _('Dateiformat'), + 'license_title': _('Lizenz'), + 'tags': _('Tags'), + 'openness': _('Open-Data-Eigenschaften') + }) - # IActions + def group_facets(self, facets_dict, group_type, package_type): + return OrderedDict({'organization': _('Herausgeber'), + 'res_format': _('Dateiformat'), + 'license_title': _('Lizenz'), + 'groups': _('Kategorie')}) - def get_actions(self): - return {'package_create': action.odsh_package_create, - 'user_create': action.odsh_user_create} + def organization_facets(self, facets_dict, organization_type, package_type): + return OrderedDict({'organization': _('Herausgeber'), + 'res_format': _('Dateiformat'), + 'license_title': _('Lizenz'), + 'groups': _('Kategorie')}) + - # IConfigurer + # IPackageController - def update_config(self, config_): - toolkit.add_template_directory(config_, 'templates') - toolkit.add_public_directory(config_, 'public') - toolkit.add_resource('fanstatic', 'odsh') + def after_show(self, context, pkg_dict): + ''' + corrects missing relationships in pkg dict + adds the following key-value-pairs to pkg_dict: + # key: 'is_new', value: True if the dataset has been created within the last month + ''' + pkg_dict = helpers_tpsh.correct_missing_relationship( + pkg_dict, + helpers_tpsh.get_pkg_relationships_from_model(pkg_dict) + ) + self._update_is_new_in_pkg_dict(pkg_dict) + + return pkg_dict - def get_helpers(self): - # 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_main_groups, - 'odsh_now': odsh_now, - 'odsh_group_id_selected': odsh_group_id_selected, - 'odsh_get_facet_items_dict': 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 - } + def before_view(self, pkg_dict): + ''' + adds the following key-value-pairs to pkg_dict: + # key: 'is_new', value: True if the dataset has been created within the last month + ''' + self._update_is_new_in_pkg_dict(pkg_dict) + return pkg_dict + + def after_create(self, context, resource): + if resource.get('package_id'): + tools.add_attributes_resources(context, resource) - def after_map(self, map): - return map + def after_update(self, context, resource): + if resource.get('package_id'): + tools.add_attributes_resources(context, resource) - def before_map(self, map): - map.connect('info_page', '/info_page', - controller='ckanext.odsh.controller:OdshRouteController', action='info_page') - map.connect('home', '/', - controller='ckanext.odsh.controller:OdshRouteController', action='start') + @staticmethod + def _update_is_new_in_pkg_dict(pkg_dict): + is_new = HelperPgkDict(pkg_dict).is_package_new() + pkg_dict.update({'is_new': is_new}) + + + def before_index(self, dict_pkg): + # make special date fields solr conform + fields = ["issued", "temporal_start", "temporal_end"] + for field in fields: + field = 'extras_' + field + if field in dict_pkg and dict_pkg[field]: + d = parse(dict_pkg[field]) + dict_pkg[field] = '{0.year:04d}-{0.month:02d}-{0.day:02d}T00:00:00Z'.format( + d) - map.redirect('/dataset/{id}/resource/{resource_id}', '/dataset/{id}') + self.map_qa_score(dict_pkg) - if p.toolkit.asbool(config.get('ckanext.dcat.enable_rdf_endpoints', True)): - remove_route(map, 'dcat_catalog') - map.connect('dcat_catalog', - config.get('ckanext.dcat.catalog_endpoint', - '/catalog.{_format}'), - controller='ckanext.odsh.controller:OdshDCATController', action='read_catalog', - requirements={'_format': 'xml|rdf|n3|ttl|jsonld'}) + return dict_pkg + - # with SubMapper(map, controller='ckanext.odsh.controller:OdshApiController') as m: - # m.connect('/catalog2', action='read_catalog') + # IRoutes + + def before_map(self, map): + map.connect( + 'info_page', + '/info_page', + controller='ckanext.odsh.controller:OdshRouteController', + action='info_page' + ) + map.connect( + 'home', + '/', + controller='ckanext.odsh.controller:OdshRouteController', + action='start' + ) + + map.redirect('/dataset/{id}/resource/{resource_id}', '/dataset/{id}') + + if plugins.toolkit.asbool(config.get('ckanext.dcat.enable_rdf_endpoints', True)): + odsh_helpers.odsh_remove_route(map, 'dcat_catalog') + map.connect( + 'dcat_catalog', + config.get( + 'ckanext.dcat.catalog_endpoint', + '/catalog.{_format}' + ), + controller='ckanext.odsh.controller:OdshDCATController', + action='read_catalog', + requirements={'_format': 'xml|rdf|n3|ttl|jsonld'} + ) # /api ver 3 or none with matomo GET_POST = dict(method=['GET', 'POST']) - with SubMapper(map, controller='ckanext.odsh.controller:OdshApiController', path_prefix='/api{ver:/3|}', ver='/3') as m: + with SubMapper( + map, + controller='ckanext.odsh.controller:OdshApiController', + path_prefix='/api{ver:/3|}', + ver='/3' + ) as m: m.connect('/action/{logic_function}', action='action', conditions=GET_POST) @@ -364,89 +289,79 @@ class OdshPlugin(plugins.SingletonPlugin, DefaultTranslation, DefaultDatasetForm m.connect('/user/logged_in', action='logged_in') m.connect('/user/logged_out', action='logged_out') m.connect('/user/logged_out_redirect', action='logged_out_page') - m.connect('user_datasets', '/user/{id:.*}', action='read', + m.connect('user_datasets', '/user/{id:(?!(generate_key|activity)).*}', action='read', ckan_icon='sitemap') - map.connect('comment_datarequest', '/datarequest/new', - controller='ckanext.datarequests.controllers.ui_controller:DataRequestsUI', - action='new', conditions=dict(method=['GET', 'POST']), ckan_icon='comment') - map.connect('comment_datarequest', '/datarequest/{id}', - controller='ckanext.datarequests.controllers.ui_controller:DataRequestsUI', - action='comment', conditions=dict(method=['GET', 'POST']), ckan_icon='comment') + map.connect( + 'comment_datarequest', + '/datarequest/new', + controller='ckanext.datarequests.controllers.ui_controller:DataRequestsUI', + action='new', + conditions=dict(method=['GET', 'POST']), + ckan_icon='comment' + ) + map.connect( + 'comment_datarequest', + '/datarequest/{id}', + controller='ckanext.datarequests.controllers.ui_controller:DataRequestsUI', + action='comment', + conditions=dict(method=['GET', 'POST']), + ckan_icon='comment' + ) + return map + + def after_map(self, map): return map - def dataset_facets(self, facets_dict, package_type): - return OrderedDict({'organization': _('Herausgeber'), - 'res_format': _('Dateiformat'), - 'license_title': _('Lizenz'), - 'groups': _('Kategorie'), - 'openness': _('Open-Data-Eigenschaften')}) - - def organization_facets(self, facets_dict, organization_type, package_type): - return OrderedDict({'organization': _('Herausgeber'), - 'res_format': _('Dateiformat'), - 'license_title': _('Lizenz'), - 'groups': _('Kategorie')}) - - def group_facets(self, facets_dict, group_type, package_type): - return OrderedDict({'organization': _('Herausgeber'), - 'res_format': _('Dateiformat'), - 'license_title': _('Lizenz'), - 'groups': _('Kategorie')}) - - def _update_schema(self, schema): - for field in ['title', 'notes', 'license_id']: - schema.update({field: [toolkit.get_converter('not_empty')]}) - - for i, item in enumerate(schema['tags']['name']): - if item == toolkit.get_validator('tag_name_validator'): - schema['tags']['name'][i] = toolkit.get_validator( - 'odsh_tag_name_validator') - for i, item in enumerate(schema['tag_string']): - if item == tag_string_convert: - schema['tag_string'][i] = validation.tag_string_convert - - schema['resources'].update({ - 'url': [toolkit.get_converter('not_empty')], - 'format': [toolkit.get_converter('not_empty')], - }) - - schema['extras'].update({ - 'key': [ - toolkit.get_converter('known_spatial_uri'), - toolkit.get_converter('validate_licenseAttributionByText'), - ] - }) - schema.update( - {'__extras': [toolkit.get_converter('odsh_validate_extras')]}) - - # only to make sure the spatial field is there for validation - # schema.update({'spatial': [toolkit.get_converter('convert_from_extras')]}) - - def create_package_schema(self): - schema = super(OdshPlugin, self).create_package_schema() - self._update_schema(schema) - return schema - - def update_package_schema(self): - schema = super(OdshPlugin, self).update_package_schema() - self._update_schema(schema) - return schema - - def show_package_schema(self): - schema = super(OdshPlugin, self).show_package_schema() - return schema - def is_fallback(self): - # Return True to register this plugin as the default handler for - # package types not handled by any other IDatasetForm plugin. - return True - - def package_types(self): - # This plugin doesn't handle any special package types, it just - # registers itself as the default (above). - return [] + # ITemplateHelpers + + def get_helpers(self): + # 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, + } + + # IValidators + def get_validators(self): return validation.get_validators() @@ -454,67 +369,7 @@ class OdshPlugin(plugins.SingletonPlugin, DefaultTranslation, DefaultDatasetForm # use several daterange queries agains temporal_start and temporal_end field # TODO: use field of type date_range in solr index instead def before_search(self, search_params): - search_params['facet.mincount'] = 0 - extras = search_params.get('extras') - print(search_params) - if not extras: - # There are no extras in the search params, so do nothing. - return search_params - - fq = search_params['fq'] - - start_date = None - end_date = None - try: - start_date = odsh_helpers.extend_search_convert_local_to_utc_timestamp( - extras.get('ext_startdate')) - end_date = odsh_helpers.extend_search_convert_local_to_utc_timestamp( - extras.get('ext_enddate')) - except: - return search_params - - empty_range = start_date and end_date and start_date > end_date - - if not start_date and not end_date: - return search_params - - do_enclosing_query = True - if not start_date: - do_enclosing_query = False - start_date = '*' - if not end_date: - do_enclosing_query = False - end_date = '*' - - start_query = '+extras_temporal_start:[{start_date} TO {end_date}]'.format( - start_date=start_date, end_date=end_date) - - end_query = '+extras_temporal_end:[{start_date} TO {end_date}]'.format( - start_date=start_date, end_date=end_date) - - enclosing_query = '' - if do_enclosing_query and not empty_range: - enclosing_query_start = 'extras_temporal_start:[* TO {start_date}]'.format( - start_date=start_date) - - enclosing_query_end = 'extras_temporal_end:[{end_date} TO *]'.format( - end_date=end_date) - - enclosing_query = ' OR ({enclosing_query_start} AND {enclosing_query_end})'.format( - enclosing_query_start=enclosing_query_start, enclosing_query_end=enclosing_query_end) - - if end_date is '*': - open_end_query = '(*:* NOT extras_temporal_end:[* TO *])' - else: - open_end_query = '((*:* NOT extras_temporal_end:[* TO *]) AND extras_temporal_start:[* TO {end_date}])'.format( - end_date=end_date) - - fq = u'{fq} ({start_query} OR {end_query} {enclosing_query} OR {open_end_query})'.format( - fq=fq, start_query=start_query, end_query=end_query, enclosing_query=enclosing_query, open_end_query=open_end_query) - - search_params['fq'] = fq - - return search_params + return search.before_search(search_params) scores = [['0OL'], ['0OL', '1RE'], ['0OL', '1RE', '2OF'], [ '0OL', '1RE', '2OF', '3URI'], ['0OL', '1RE', '2OF', '3URI', '4LD']] @@ -532,64 +387,6 @@ class OdshPlugin(plugins.SingletonPlugin, DefaultTranslation, DefaultDatasetForm if score > 0: dict_pkg['openness'] = OdshPlugin.scores[score-1] - # @precondition.not_on_slave - def before_index(self, dict_pkg): - # make special date fields solr conform - fields = ["issued", "temporal_start", "temporal_end"] - for field in fields: - field = 'extras_' + field - if field in dict_pkg and dict_pkg[field]: - d = parse(dict_pkg[field]) - dict_pkg[field] = '{0.year:04d}-{0.month:02d}-{0.day:02d}T00:00:00Z'.format( - d) - # if 'res_format' in dict_pkg: - # dict_pkg['res_format']=[e.lower() for e in dict_pkg['res_format']] - - self.map_qa_score(dict_pkg) - - return dict_pkg - - - # IPackageController - def before_view(self, pkg_dict): - ''' - add a key 'is_new' to pkg_dict - the value for this key is True if the dataset has been created within the last month - the value is used in the snippet package_item.html - ''' - is_new = self._is_package_new(pkg_dict) - pkg_dict.update({'is_new':is_new}) - return pkg_dict - - def _is_package_new(self, pkg_dict): - date_package_created_as_str = self._get_date_of_package_creation_from_pkg_dict(pkg_dict) - if date_package_created_as_str == None: - is_new = False - else: - date_package_created = self._get_date_from_string(date_package_created_as_str) - if date_package_created == None: - is_new = False - else: - is_new = odsh_helpers.is_within_last_month(date_package_created) - return is_new - - def _get_date_of_package_creation_from_pkg_dict(self, pkg_dict): - if 'extras' in pkg_dict: - extras = pkg_dict['extras'] - issued = odsh_helpers.odsh_extract_value_from_extras(extras=extras, key='issued') # is None if issued not in extras - return issued - else: - return None - - def _get_date_from_string(self, date_time_str): - date_time_format = '%Y-%m-%dT%H:%M:%S' #e.g. u'2019-06-12T11:56:25' - try: - date_time = datetime.datetime.strptime(date_time_str, date_time_format) - date = date_time.date() - except ValueError: - # if date cannot be converted from string return None - date = None - return date diff --git a/ckanext/odsh/plugin_odsh_autocomplete.py b/ckanext/odsh/plugin_odsh_autocomplete.py new file mode 100644 index 0000000000000000000000000000000000000000..e34d7f0ee54ea0572f52db38eda72bdaee4a334d --- /dev/null +++ b/ckanext/odsh/plugin_odsh_autocomplete.py @@ -0,0 +1,8 @@ +import ckan.plugins as plugins +import ckanext.odsh.logic.action as action + +class OdshAutocompletePlugin(plugins.SingletonPlugin): + plugins.implements(plugins.IActions) + + def get_actions(self): + return {'autocomplete': action.autocomplete} \ No newline at end of file diff --git a/ckanext/odsh/plugin_odsh_dcat_harvest.py b/ckanext/odsh/plugin_odsh_dcat_harvest.py new file mode 100644 index 0000000000000000000000000000000000000000..3b03b6e7b66f2084911ae20378833453dc9e18ce --- /dev/null +++ b/ckanext/odsh/plugin_odsh_dcat_harvest.py @@ -0,0 +1,65 @@ +import ckan.plugins as plugins +import ckan.plugins.toolkit as toolkit +from ckanext.dcat.interfaces import IDCATRDFHarvester +from ckanext.dcatde.extras import Extras + +from ckanext.odsh.helper_pkg_dict import HelperPgkDict + +from ckan import model + +import ckan.plugins as p + +_ = toolkit._ + +import logging +log = logging.getLogger(__name__) + + +class OdshDCATHarvestPlugin(plugins.SingletonPlugin): + plugins.implements(IDCATRDFHarvester, inherit=True) + + def before_update(self, harvest_object, dataset_dict, temp_dict): + existing_package_dict = self._get_existing_dataset(harvest_object.guid) + new_dataset_extras = Extras(dataset_dict['extras']) + if new_dataset_extras.key('modified') and \ + new_dataset_extras.value('modified') < existing_package_dict.get('metadata_modified'): + log.info("Modified date of new dataset is not newer than " + + "the already exisiting dataset, ignoring new one.") + dataset_dict.clear() + + def _get_existing_dataset(self, guid): + ''' + Checks if a dataset with a certain guid extra already exists + Returns a dict as the ones returned by package_show + ''' + datasets = model.Session.query(model.Package.id) \ + .join(model.PackageExtra) \ + .filter(model.PackageExtra.key == 'guid') \ + .filter(model.PackageExtra.value == guid) \ + .filter(model.Package.state == 'active') \ + .all() + + if not datasets: + return None + elif len(datasets) > 1: + log.error('Found more than one dataset with the same guid: {0}' + .format(guid)) + return p.toolkit.get_action('package_show')({}, {'id': datasets[0][0]}) + + + def after_create(self, harvest_object, dataset_dict, temp_dict): + ''' + Called just after a successful ``package_create`` action has been + performed. + + This method sets relationships between packages and collections. + ''' + dd = HelperPgkDict(dataset_dict) + if dd.is_collection(): + dd.update_relations_to_collection_members() + dd.add_uri_to_store() + if dd.shall_be_part_of_collection(): + dd.update_relation_to_collection() + dd.add_uri_to_store() + + diff --git a/ckanext/odsh/plugin_odsh_harvest.py b/ckanext/odsh/plugin_odsh_harvest.py new file mode 100644 index 0000000000000000000000000000000000000000..6c7d326da317cd8c88254457b5adf380bd62bb81 --- /dev/null +++ b/ckanext/odsh/plugin_odsh_harvest.py @@ -0,0 +1,48 @@ +import ckan.plugins as plugins +import ckan.plugins.toolkit as toolkit +import ckanext.odsh.logic.action as action + + +class OdshHarvestPlugin(plugins.SingletonPlugin): + plugins.implements(plugins.IRoutes, inherit=True) + plugins.implements(plugins.IConfigurer) + + def update_config(self, config_): + toolkit.add_template_directory(config_, 'harvest_templates') + + def before_map(self, map): + DATASET_TYPE_NAME = 'harvest' + controller = 'ckanext.odsh.controller:OdshHarvestController' + + map.connect('{0}_delete'.format(DATASET_TYPE_NAME), '/' + + DATASET_TYPE_NAME + '/delete/:id', controller=controller, action='delete') + map.connect('{0}_refresh'.format(DATASET_TYPE_NAME), '/' + DATASET_TYPE_NAME + '/refresh/:id', controller=controller, + action='refresh') + map.connect('{0}_admin'.format(DATASET_TYPE_NAME), '/' + + DATASET_TYPE_NAME + '/admin/:id', controller=controller, action='admin') + map.connect('{0}_about'.format(DATASET_TYPE_NAME), '/' + + DATASET_TYPE_NAME + '/about/:id', controller=controller, action='about') + map.connect('{0}_clear'.format(DATASET_TYPE_NAME), '/' + + DATASET_TYPE_NAME + '/clear/:id', controller=controller, action='clear') + + map.connect('harvest_job_list', '/' + DATASET_TYPE_NAME + + '/{source}/job', controller=controller, action='list_jobs') + map.connect('harvest_job_show_last', '/' + DATASET_TYPE_NAME + + '/{source}/job/last', controller=controller, action='show_last_job') + map.connect('harvest_job_show', '/' + DATASET_TYPE_NAME + + '/{source}/job/{id}', controller=controller, action='show_job') + map.connect('harvest_job_abort', '/' + DATASET_TYPE_NAME + + '/{source}/job/{id}/abort', controller=controller, action='abort_job') + + map.connect('harvest_object_show', '/' + DATASET_TYPE_NAME + + '/object/:id', controller=controller, action='show_object') + map.connect('harvest_object_for_dataset_show', '/dataset/harvest_object/:id', + controller=controller, action='show_object', ref_type='dataset') + + org_controller = 'ckanext.harvest.controllers.organization:OrganizationController' + map.connect('{0}_org_list'.format(DATASET_TYPE_NAME), '/organization/' + + DATASET_TYPE_NAME + '/' + '{id}', controller=org_controller, action='source_list') + return map + + def after_map(self, map): + return map \ No newline at end of file diff --git a/ckanext/odsh/plugin_odsh_icap.py b/ckanext/odsh/plugin_odsh_icap.py new file mode 100644 index 0000000000000000000000000000000000000000..d6462db4af0e09a46a2ac900a91dc923cb59728b --- /dev/null +++ b/ckanext/odsh/plugin_odsh_icap.py @@ -0,0 +1,11 @@ +import ckan.plugins as plugins +from ckanext.odsh.lib.uploader import ODSHResourceUpload, ODSHUpload + +class OdshIcapPlugin(plugins.SingletonPlugin): + plugins.implements(plugins.IUploader, inherit=True) + + def get_resource_uploader(self, data_dict): + return ODSHResourceUpload(data_dict) + + def get_uploader(self, upload_to, old_filename): + return ODSHUpload(upload_to, old_filename) \ No newline at end of file diff --git a/ckanext/odsh/pretty_daterange/README.md b/ckanext/odsh/pretty_daterange/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6fc2eaac24fcf80d9f3c271fe4c235d3fc5736fd --- /dev/null +++ b/ckanext/odsh/pretty_daterange/README.md @@ -0,0 +1,62 @@ +# pretty_daterange package +Dieses Package erlaubt die "hübsche" Darstellung von Datumsbereichen. + +## Beispiel +``` +from datetime import date +from pretty_daterange.date_range_formatter import DateRangeFormatter + +drf = DateRangeFormatter( + date_start = date(2019, 1, 1), + date_end = date(2019, 3, 31) +) +print(drf.get_formatted_str()) +``` +prints +``` +1. Quartal 2019 +``` + +# Spezifikation +Viele Datumsangaben lassen sich eleganter als TT.MM.JJJJ-TT.MM.JJJJ darstellen. Bei der Anzeige auf der Suchergebnisseite und der Datensatzdetailsseite sollen Datumsangaben nach den folgenden Regeln umgeformt werden. + +## Regel zum Kürzen von Datumsangaben +### Jahr ist gleich +Startdatum = a.b.n und Enddatum = a.b.n -> a.b.n + +Starddatum = 01.01.n und Enddatum = 31.12.n -> n + +Startdatum = 01.01.n und Enddatum = 31.01.n -> Januar n + +Startdatum = 01.02.n und ( Enddatum = 28.02.n oder Enddatum = 28.02.n ) -> Februar n + +Startdatum = 01.03.n und Enddatum = 31.03.n -> März n + +[...] + +Startdatum = 01.12.n und Enddatum = 31.12.n -> Dezember n + +Startdatum = 01.01.n und Enddatum = 31.03.n -> 1. Quartal n + +Startdatum = 01.04.n und Enddatum = 30.06.n -> 2. Quartal n + +Startdatum = 01.07.n und Enddatum = 30.09.n -> 3. Quartal n + +Startdatum = 01.10.n und Enddatum = 31.12.n -> 4. Quartal n + +Startdatum = 01.01.n und Enddatum = 30.06.n -> 1. Halbjahr n + +Startdatum = 01.07.n und Enddatum = 31.12.n -> 2. Halbjahr n + +Startdatum = 01.01.n und ( Enddatum = 28.02.n oder Enddatum = 28.02.n ) -> Januar bis Februar n + +Startdatum = 01.01.n und Enddatum = 31.03.n -> Januar bis März n + +[...] + +Startdatum = 01.01.n und Enddatum = 30.11.n -> Januar bis November n + +### Jahr ist unterschiedlich +Startdatum = 01.01.n und Enddatum = 31.12.m -> n-m + +Stardatum = 01.a.n und Enddatum ist letzer des Monats b im Jahr m -> [Text für a] n bis [Text für b] m diff --git a/ckanext/odsh/pretty_daterange/__init__.py b/ckanext/odsh/pretty_daterange/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ckanext/odsh/pretty_daterange/date_range.py b/ckanext/odsh/pretty_daterange/date_range.py new file mode 100644 index 0000000000000000000000000000000000000000..d43d7e28c0bf7e4b4845be549e30c1aa061deb2b --- /dev/null +++ b/ckanext/odsh/pretty_daterange/date_range.py @@ -0,0 +1,181 @@ + # -*- coding: utf-8 -*- + +import datetime + +class DateRange(object): + def __init__(self, date_start, date_end): + if date_end < date_start: + raise ValueError('date_end may not be before date_start') + self.date_start = date_start + self.date_end = date_end + + + def is_one_year(self): + is_one_year = ( + self._are_start_and_end_in_same_year() and + self._is_first_of_year(self.date_start) and + self._is_last_of_year(self.date_end) + ) + return is_one_year + + + def _are_start_and_end_in_same_year(self): + in_same_year = self.date_start.year==self.date_end.year + return in_same_year + + + @staticmethod + def _is_first_of_year(date): + return date.day==1 and date.month==1 + + + @staticmethod + def _is_last_of_year(date): + return date.day==31 and date.month==12 + + + def is_range_of_multiple_years(self): + is_range_of_multiple_years = ( + not self._are_start_and_end_in_same_year() and + self._is_first_of_year(self.date_start) and + self._is_last_of_year(self.date_end) + ) + return is_range_of_multiple_years + + def is_one_half_of_year(self): + is_one_half_year = ( + self._are_start_and_end_in_same_half_year() and + self._is_first_of_half_year(self.date_start) and + self._is_last_of_half_year(self.date_end) + ) + return is_one_half_year + + + def _are_start_and_end_in_same_half_year(self): + in_same_half_year = ( + self._are_start_and_end_in_same_year() and + self.get_half_year(self.date_start) == self.get_half_year(self.date_end) + ) + return in_same_half_year + + + @staticmethod + def get_half_year(date): + year = date.year + if date < datetime.date(year, 7, 1): + return 1 + return 2 + + + @staticmethod + def _is_first_of_half_year(date): + year = date.year + return date in ( + datetime.date(year, 1, 1), + datetime.date(year, 7, 1), + ) + + + @staticmethod + def _is_last_of_half_year(date): + year = date.year + return date in ( + datetime.date(year, 6, 30), + datetime.date(year, 12, 31), + ) + + + def is_one_quarter_of_year(self): + is_one_quarter_of_year = ( + self._are_start_and_end_in_same_quarter() and + self._is_first_of_quarter(self.date_start) and + self._is_last_of_quarter(self.date_end) + ) + return is_one_quarter_of_year + + + def _are_start_and_end_in_same_quarter(self): + in_same_quarter = ( + self._are_start_and_end_in_same_year() and + self.get_quarter(self.date_start) == self.get_quarter(self.date_end) + ) + return in_same_quarter + + + @staticmethod + def get_quarter(date): + year = date.year + if date < datetime.date(year, 4, 1): + return 1 + if date < datetime.date(year, 7, 1): + return 2 + if date < datetime.date(year, 10, 1): + return 3 + return 4 + + + @staticmethod + def _is_first_of_quarter(date): + year = date.year + return date in ( + datetime.date(year, 1, 1), + datetime.date(year, 4, 1), + datetime.date(year, 7, 1), + datetime.date(year, 10, 1), + ) + + + @staticmethod + def _is_last_of_quarter(date): + year = date.year + return date in ( + datetime.date(year, 3, 31), + datetime.date(year, 6, 30), + datetime.date(year, 9, 30), + datetime.date(year, 12, 31), + ) + + + def is_one_month(self): + is_one_month = ( + self._are_start_and_end_in_same_year() and + self._are_start_and_end_in_same_month() and + self._is_first_of_month(self.date_start) and + self._is_last_of_month(self.date_end) + ) + return is_one_month + + + def _are_start_and_end_in_same_month(self): + is_in_same_month = self.date_start.month == self.date_end.month + return is_in_same_month + + + @staticmethod + def _is_first_of_month(date): + return date.day==1 + + + @staticmethod + def _is_last_of_month(date): + day_after_date = date + datetime.timedelta(days=1) + is_last_of_month = day_after_date.day==1 + return is_last_of_month + + def is_range_of_multiple_months(self): + is_range_of_multiple_months = ( + self._is_first_of_month(self.date_start) and + self._is_last_of_month(self.date_end) and + not self._are_start_and_end_in_same_month() + ) + return is_range_of_multiple_months + + def is_range_of_multiple_months_in_same_year(self): + is_range_of_multiple_months_in_same_year = ( + self._is_first_of_month(self.date_start) and + self._is_last_of_month(self.date_end) and + not self._are_start_and_end_in_same_month() and + self._are_start_and_end_in_same_year() + ) + return is_range_of_multiple_months_in_same_year + diff --git a/ckanext/odsh/pretty_daterange/date_range_formatter.py b/ckanext/odsh/pretty_daterange/date_range_formatter.py new file mode 100644 index 0000000000000000000000000000000000000000..dcd513f32257bb5e339a89470fae64b5389d5c86 --- /dev/null +++ b/ckanext/odsh/pretty_daterange/date_range_formatter.py @@ -0,0 +1,99 @@ + # -*- coding: utf-8 -*- + +import datetime +from babel.dates import format_date + +from .date_range import DateRange + +class DateRangeFormatter(object): + def __init__(self, date_start, date_end): + if all((date_start, date_end)): + self._date_range = DateRange(date_start, date_end) + self.locale_for_date_strings = 'de_DE.UTF-8' + self.date_start = date_start + self.date_end = date_end + self._format_full_date = 'dd.MM.yyyy' + self._format_only_year = 'yyyy' + self._format_month_year = 'MMMM yyyy' + self._format_only_month = 'MMMM' + + + def get_formatted_str(self): + if not any((self.date_start, self.date_end, )): + return self._construct_empty_date_string() + + if not self.date_end: + return self._construct_open_end_date_string() + + if not self.date_start: + return self._construct_open_start_date_string() + + if self.date_start == self.date_end: + return self._construct_single_date_string(self.date_start, self._format_full_date) + + if self._date_range.is_range_of_multiple_years(): + return self._construct_date_range_string(self._format_only_year, self._format_only_year) + + if self._date_range.is_one_year(): + return self._construct_single_date_string(self.date_start, self._format_only_year) + + if self._date_range.is_one_half_of_year(): + return self._construct_half_of_year_date_string() + + if self._date_range.is_one_quarter_of_year(): + return self._construct_quarter_of_year_date_string() + + if self._date_range.is_range_of_multiple_months_in_same_year(): + return self._construct_date_range_string(self._format_only_month, self._format_month_year) + + if self._date_range.is_range_of_multiple_months(): + return self._construct_date_range_string(self._format_month_year, self._format_month_year) + + if self._date_range.is_one_month(): + return self._construct_single_date_string(self.date_start, self._format_month_year) + + format_date_start = self._format_full_date + format_date_end = self._format_full_date + formatted_date_range = self._construct_date_range_string(format_date_start, format_date_end) + return formatted_date_range + + + @staticmethod + def _construct_empty_date_string(): + return "" + + def _construct_open_end_date_string(self): + date_start_formatted = self._construct_single_date_string(self.date_start, self._format_full_date) + return "ab {}".format(date_start_formatted) + + def _construct_open_start_date_string(self): + date_end_formatted = self._construct_single_date_string(self.date_end, self._format_full_date) + return "bis {}".format(date_end_formatted) + + def _construct_single_date_string(self, date, format): + return format_date(date, format=format, locale=self.locale_for_date_strings) + + @staticmethod + def _as_utf_8(s): + return u'' + s.decode('utf-8') + + + def _construct_half_of_year_date_string(self): + year = self.date_start.year + half = self._date_range.get_half_year(self.date_start) + half_of_year_date_string = u'{}. Halbjahr {}'.format(half, year) + return DateRangeFormatter._as_utf_8(half_of_year_date_string) + + + def _construct_quarter_of_year_date_string(self): + year = self.date_start.year + quarter = self._date_range.get_quarter(self.date_start) + quarter_of_year_date_string = u'{}. Quartal {}'.format(quarter, year) + return DateRangeFormatter._as_utf_8(quarter_of_year_date_string) + + + def _construct_date_range_string(self, format_date_start, format_date_end): + formatted_date_range = u'{} - {}'.format( + self._construct_single_date_string(self.date_start, format_date_start), + self._construct_single_date_string(self.date_end, format_date_end)) + return formatted_date_range diff --git a/ckanext/odsh/profiles/__init__.py b/ckanext/odsh/profiles/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f87530b7c8b2c3d7eb8dc717ef414fad44ba1a55 --- /dev/null +++ b/ckanext/odsh/profiles/__init__.py @@ -0,0 +1,2 @@ +from odsh_dcat_de_profile import ODSHDCATdeProfile +from odsh_european_dcatap_profile import ODSHEuropeanDCATAPProfile \ No newline at end of file diff --git a/ckanext/odsh/profiles/odsh_dcat_de_profile.py b/ckanext/odsh/profiles/odsh_dcat_de_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..c475ac6a823c6bac61712051a89fecae077cdcd3 --- /dev/null +++ b/ckanext/odsh/profiles/odsh_dcat_de_profile.py @@ -0,0 +1,169 @@ +import rdflib + +from ckan.common import config +import ckan.lib.helpers as helpers +import ckan.model as model +from ckanext.dcat.profiles import DCT +from ckanext.dcat.utils import resource_uri +import ckanext.dcatde.dataset_utils as ds_utils +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/") +DCAT = rdflib.namespace.Namespace("http://www.w3.org/ns/dcat#") + + +class ODSHDCATdeProfile(DCATdeProfile): + + # from RDF + + def parse_dataset(self, dataset_dict, dataset_ref): + dataset_dict = super(ODSHDCATdeProfile, self).parse_dataset( + dataset_dict, dataset_ref + ) + self._parse_distributions(dataset_dict, dataset_ref) + self._parse_type(dataset_dict, dataset_ref) + if self._belongs_to_collection(dataset_dict, dataset_ref): + self._mark_for_adding_to_ckan_collection(dataset_dict, dataset_ref) + return dataset_dict + + def _parse_distributions(self, dataset_dict, dataset_ref): + for distribution in self.g.objects(dataset_ref, DCAT.distribution): + for resource_dict in dataset_dict.get('resources', []): + # Match distribution in graph and distribution in ckan-dict + if unicode(distribution) == resource_uri(resource_dict): + for namespace in [DCATDE, DCATDE_1_0]: + value = self._object_value( + distribution, namespace.licenseAttributionByText) + if value: + ds_utils.insert_new_extras_field( + dataset_dict, 'licenseAttributionByText', value) + return + + def _parse_type(self, dataset_dict, dataset_ref): + dct_type = self._object(dataset_ref, DCT.type) + if dct_type: + ckan_type = helpers_tpsh.map_dct_type_to_ckan_type(str(dct_type)) + dataset_dict.update({'type': ckan_type}) + + def _belongs_to_collection(self, dataset_dict, dataset_ref): + dct_is_version_of = self._object(dataset_ref, DCT.isVersionOf) + belongs_to_collection = True if dct_is_version_of else False + return belongs_to_collection + + def _mark_for_adding_to_ckan_collection(self, dataset_dict, dataset_ref): + dataset_dict.update({'add_to_collection': True}) + + + # to RDF + + def graph_from_dataset(self, dataset_dict, dataset_ref): + ''' + this class inherits from ODSHDCATdeProfile + it has been extended to add information to + the rdf export + + ''' + super(ODSHDCATdeProfile, self).graph_from_dataset( + dataset_dict, dataset_ref) + self._add_contributor_id(dataset_dict, dataset_ref) + self._add_license_attribution_by_text(dataset_dict, dataset_ref) + self._add_type(dataset_dict, dataset_ref) + if self._is_dataset_collection(dataset_dict): + self._remove_predefined_collection_members() + self._add_collection_members(dataset_dict, dataset_ref) + if self._dataset_belongs_to_collection(dataset_dict): + self._add_collection(dataset_dict, dataset_ref) + + def _add_contributor_id(self, dataset_dict, dataset_ref): + contributorID = 'http://dcat-ap.de/def/contributors/schleswigHolstein' + self.g.add( + (dataset_ref, DCATDE.contributorID, + rdflib.URIRef(contributorID) + ) + ) + + def _add_license_attribution_by_text(self, dataset_dict, dataset_ref): + licenseAttributionByText = self._get_dataset_value(dataset_dict, 'licenseAttributionByText') + if licenseAttributionByText: + self.g.set( + (dataset_ref, DCATDE.licenseAttributionByText, rdflib.Literal(licenseAttributionByText)) + ) + for distribution in self.g.objects(dataset_ref, DCAT.distribution): + self.g.set( + (distribution, DCATDE.licenseAttributionByText, rdflib.Literal(licenseAttributionByText)) + ) + + def _add_type(self, dataset_dict, dataset_ref): + ''' + adds the type if there is a known mapping from ckan type to + dct:type + ''' + ckan_type = self._get_ckan_type(dataset_dict) + dct_type = helpers_tpsh.map_ckan_type_to_dct_type(ckan_type) + if dct_type: + self.g.set( + (dataset_ref, DCT.type, + rdflib.URIRef(dct_type) + ) + ) + + def _get_ckan_type(self, dataset_dict): + ckan_type = self._get_dataset_value(dataset_dict, 'type') + return ckan_type + + def _remove_predefined_collection_members(self): + for s, p, o in self.g: + if p==DCT.hasVersion: + self.g.remove((s, p, o)) + + def _add_collection_members(self, dataset_dict, dataset_ref): + dataset_refs_belonging_to_collection = self._get_dataset_refs_belonging_to_collection(dataset_dict) + for ref in dataset_refs_belonging_to_collection: + self.g.add( + (dataset_ref, DCT.hasVersion, rdflib.URIRef(ref)) + ) + + def _is_dataset_collection(self, dataset_dict): + ckan_type = self._get_ckan_type(dataset_dict) + is_collection = ckan_type=='collection' + 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_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] + return dataset_refs + + @staticmethod + def _construct_refs(id): + public_url = config.get('ckan.site_url') + url_to_id = helpers.url_for(controller='package', action ='read', id=id) + ref = public_url + url_to_id + return ref + + def _dataset_belongs_to_collection(self, dataset_dict): + ''' + returns True if a containing collection is found + ''' + 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) + return collection_name is not None + + def _add_collection(self, dataset_dict, dataset_ref): + collection_id = HelperPgkDict(dataset_dict).get_collection_id() + collection_uri = self._construct_refs(collection_id) + self.g.set( + (dataset_ref, DCT.isVersionOf, + rdflib.URIRef(collection_uri) + ) + ) diff --git a/ckanext/odsh/profiles.py b/ckanext/odsh/profiles/odsh_european_dcatap_profile.py similarity index 70% rename from ckanext/odsh/profiles.py rename to ckanext/odsh/profiles/odsh_european_dcatap_profile.py index d9532758d517c39d9a995a3cad70b24df7bf2390..544323bd1d9b32db11f84e78b93f4a5fb364f92d 100644 --- a/ckanext/odsh/profiles.py +++ b/ckanext/odsh/profiles/odsh_european_dcatap_profile.py @@ -1,20 +1,10 @@ -from ckanext.dcatde.profiles import DCATdeProfile, DCATDE, DCAT, VCARD, dcat_theme_prefix, DCATDE_1_0 -from ckanext.dcat.utils import resource_uri -from ckanext.dcat.profiles import EuropeanDCATAPProfile, DCT, URIRefOrLiteral -from ckan.model.license import LicenseRegister -import rdflib -import ckanext.dcatde.dataset_utils as ds_utils import logging -from ckan.plugins import toolkit -from ckan.common import config, json -from ckanext.dcat.interfaces import IDCATRDFHarvester -import pdb +import rdflib -import sys -if sys.version_info[0] == 2: - import urllib2 -elif sys.version_info[0] == 3: # >=Python3.1 - import urllib +from ckan.common import config, json +from ckan.model.license import LicenseRegister +from ckanext.dcat.profiles import EuropeanDCATAPProfile, DCT, URIRefOrLiteral +from ckanext.dcatde.profiles import DCAT log = logging.getLogger(__name__) DCT = rdflib.namespace.Namespace("http://purl.org/dc/terms/") @@ -75,51 +65,10 @@ class ODSHEuropeanDCATAPProfile(EuropeanDCATAPProfile): for dist in self.g.objects(dataset_ref, DCAT.distribution): self.g.add((dist, DCT.license, rdflib.URIRef(license))) -class ODSHDCATdeProfile(DCATdeProfile): - def parse_dataset(self, dataset_dict, dataset_ref): - dataset_dict = super(ODSHDCATdeProfile, self).parse_dataset( - dataset_dict, dataset_ref) - # Enhance Distributions - for distribution in self.g.objects(dataset_ref, DCAT.distribution): - for resource_dict in dataset_dict.get('resources', []): - # Match distribution in graph and distribution in ckan-dict - if unicode(distribution) == resource_uri(resource_dict): - for namespace in [DCATDE, DCATDE_1_0]: - value = self._object_value( - distribution, namespace.licenseAttributionByText) - if value: - ds_utils.insert_new_extras_field( - dataset_dict, 'licenseAttributionByText', value) - return dataset_dict - return dataset_dict - - def graph_from_dataset(self, dataset_dict, dataset_ref): - super(ODSHDCATdeProfile, self).graph_from_dataset( - dataset_dict, dataset_ref) - # Enhance Distributions - # <dcatde:contributorID rdf:resource="http://dcat-ap.de/def/contributors/schleswigHolstein"/> - self.g.add((dataset_ref, DCATDE.contributorID, rdflib.URIRef( - "http://dcat-ap.de/def/contributors/schleswigHolstein"))) - - extras = dataset_dict.get('extras', None) - if extras: - attr = None - for d in extras: - if d['key'] == 'licenseAttributionByText': - attr = d['value'] - break - if attr: - self.g.set( - (dataset_ref, DCATDE.licenseAttributionByText, rdflib.Literal(attr))) - for dist in self.g.objects(dataset_ref, DCAT.distribution): - self.g.set( - (dist, DCATDE.licenseAttributionByText, rdflib.Literal(attr))) - _RESOURCE_FORMATS_IMPORT = None _RESOURCE_FORMATS_EXPORT = None - def resource_formats(): global _RESOURCE_FORMATS_IMPORT global _RESOURCE_FORMATS_EXPORT @@ -142,14 +91,12 @@ def resource_formats(): _RESOURCE_FORMATS_EXPORT[elem.split('/')[-1]] = elem _RESOURCE_FORMATS_IMPORT[elem] = elem.split('/')[-1] - def resource_formats_export(): global _RESOURCE_FORMATS_EXPORT if not _RESOURCE_FORMATS_EXPORT: resource_formats() return _RESOURCE_FORMATS_EXPORT - def resource_formats_import(): global _RESOURCE_FORMATS_IMPORT if not _RESOURCE_FORMATS_IMPORT: @@ -159,7 +106,6 @@ def resource_formats_import(): _LANGUAGES = None - def get_language(): ''' When datasets are exported in rdf-format, their language-tag should be given as @@ -190,4 +136,4 @@ def get_language(): for language_line in language_mapping_table: _LANGUAGES[language_line[0]] = language_line[1] - return _LANGUAGES + return _LANGUAGES \ No newline at end of file diff --git a/ckanext/odsh/public/base/images/icon_arrow_left.svg b/ckanext/odsh/public/base/images/icon_arrow_left.svg new file mode 100644 index 0000000000000000000000000000000000000000..b6444d840d6a95b0d9bb9086315e5950762ae8e8 --- /dev/null +++ b/ckanext/odsh/public/base/images/icon_arrow_left.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="19.692" viewBox="0 0 16 19.692"><defs><style>.a{fill:#fff;}</style></defs><path class="a" d="M23,8.923v9.846H16.846v4.923L7,13.846,16.846,4V8.923Z" transform="translate(-7 -4)"/></svg> \ No newline at end of file diff --git a/ckanext/odsh/public/base/images/icon_arrow_right.svg b/ckanext/odsh/public/base/images/icon_arrow_right.svg new file mode 100644 index 0000000000000000000000000000000000000000..8100a08444b09bac12d5e37715d8cd9218e5e6ee --- /dev/null +++ b/ckanext/odsh/public/base/images/icon_arrow_right.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="19.692" viewBox="0 0 16 19.692"><defs><style>.a{fill:#fff;}</style></defs><path class="a" d="M13.154,8.923V4L23,13.846l-9.846,9.846V18.769H7V8.923Z" transform="translate(-7 -4)"/></svg> \ No newline at end of file diff --git a/ckanext/odsh/public/base/images/icon_close.svg b/ckanext/odsh/public/base/images/icon_close.svg new file mode 100644 index 0000000000000000000000000000000000000000..37a57bf148cc3a6b5cf1372e663be7a0bef2f700 --- /dev/null +++ b/ckanext/odsh/public/base/images/icon_close.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18" height="17.999" viewBox="0 0 18 17.999"><defs><style>.a{fill:#444;}</style></defs><g transform="translate(-3910 -11)"><path class="a" d="M101.5,172.4l-.885-.885-3.553,3.552-3.706-3.706-.9.9,3.7,3.709L92.6,179.524l.447.449.438.436,3.558-3.556,3.549,3.551.9-.9-3.553-3.551Z" transform="translate(3821.947 -156.081)"/><path class="a" d="M96.6,167.766a7.877,7.877,0,1,0,7.877,7.876,7.888,7.888,0,0,0-7.877-7.876m0,16.875a9,9,0,1,1,9-9,9.009,9.009,0,0,1-9,9" transform="translate(3822.398 -155.642)"/></g></svg> \ No newline at end of file diff --git a/ckanext/odsh/public/base/images/icon_close_white.svg b/ckanext/odsh/public/base/images/icon_close_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..aebfd442e542ff008eb891eba0b6a4a5729277f5 --- /dev/null +++ b/ckanext/odsh/public/base/images/icon_close_white.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18" height="17.999" viewBox="0 0 18 17.999"><defs><style>.a{fill:#fff;}</style></defs><g transform="translate(-87.602 -166.642)"><path class="a" d="M101.5,172.4l-.885-.885-3.553,3.552-3.706-3.706-.9.9,3.7,3.709L92.6,179.524l.447.449.438.436,3.558-3.556,3.549,3.551.9-.9-3.553-3.551Z" transform="translate(-0.451 -0.439)"/><path class="a" d="M96.6,167.766a7.877,7.877,0,1,0,7.877,7.876,7.888,7.888,0,0,0-7.877-7.876m0,16.875a9,9,0,1,1,9-9,9.009,9.009,0,0,1-9,9" transform="translate(0 0)"/></g></svg> \ No newline at end of file diff --git a/ckanext/odsh/public/base/images/icon_funnel.svg b/ckanext/odsh/public/base/images/icon_funnel.svg new file mode 100644 index 0000000000000000000000000000000000000000..f5ae5fc47cceaf6e8cba427dfbcf0e00614047f5 --- /dev/null +++ b/ckanext/odsh/public/base/images/icon_funnel.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="19.528" height="18" viewBox="0 0 19.528 18"><defs><style>.a{fill:#fff;}</style></defs><path class="a" d="M19.471,10.022a.592.592,0,0,0-.535-.339H.591a.592.592,0,0,0-.457.968L7.187,19.2v7.887a.592.592,0,0,0,.855.53L12,25.658a.592.592,0,0,0,.329-.53L12.34,19.2l7.052-8.553A.592.592,0,0,0,19.471,10.022Zm-8.179,8.593a.592.592,0,0,0-.135.376l-.008,5.769L8.37,26.137V18.992a.592.592,0,0,0-.135-.377L1.847,10.867H17.681Z" transform="translate(0 -9.683)"/></svg> \ No newline at end of file diff --git a/ckanext/odsh/public/base/images/icon_info.svg b/ckanext/odsh/public/base/images/icon_info.svg new file mode 100644 index 0000000000000000000000000000000000000000..61cfea800270ee0698df56ad8371b0ff6fc51046 --- /dev/null +++ b/ckanext/odsh/public/base/images/icon_info.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><defs><style>.a{fill:#fff;}</style></defs><path class="a" d="M12,2A10,10,0,1,1,2,12,10.011,10.011,0,0,1,12,2Zm0-2A12,12,0,1,0,24,12,12,12,0,0,0,12,0Zm0,5.75A1.25,1.25,0,1,1,10.75,7,1.251,1.251,0,0,1,12,5.75ZM14,18H10V17c.484-.179,1-.2,1-.735V11.8c0-.534-.516-.618-1-.8V10h3v6.265c0,.535.517.558,1,.735Z"/></svg> \ No newline at end of file diff --git a/ckanext/odsh/public/base/images/icon_lang_danish.png b/ckanext/odsh/public/base/images/icon_lang_danish.png new file mode 100644 index 0000000000000000000000000000000000000000..8a6a9f60bfaa2ab1e7f448063810e93b6a427d1f Binary files /dev/null and b/ckanext/odsh/public/base/images/icon_lang_danish.png differ diff --git a/ckanext/odsh/public/base/images/icon_lang_english.png b/ckanext/odsh/public/base/images/icon_lang_english.png new file mode 100644 index 0000000000000000000000000000000000000000..5999e319634f3a4c68006c44c828ddcc441bafb6 Binary files /dev/null and b/ckanext/odsh/public/base/images/icon_lang_english.png differ diff --git a/ckanext/odsh/public/base/images/icon_latest.svg b/ckanext/odsh/public/base/images/icon_latest.svg new file mode 100644 index 0000000000000000000000000000000000000000..a2741441aec25f95904483f7f44ea6324e51bc74 --- /dev/null +++ b/ckanext/odsh/public/base/images/icon_latest.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="17.999" height="18" viewBox="0 0 17.999 18"><defs><style>.a{fill:#fff;}</style></defs><path class="a" d="M21,10.12H14.22L16.96,7.3a7.039,7.039,0,0,0-9.88-.1,6.875,6.875,0,0,0,0,9.79,7.02,7.02,0,0,0,9.88,0A6.513,6.513,0,0,0,19,12.1h2a9.341,9.341,0,0,1-2.64,6.29,9.054,9.054,0,0,1-12.72,0A8.84,8.84,0,0,1,5.62,5.81a8.987,8.987,0,0,1,12.65,0L21,3ZM12.5,8v4.25L16,14.33l-.72,1.21L11,13V8Z" transform="translate(-3.001 -3)"/></svg> \ No newline at end of file diff --git a/ckanext/odsh/public/base/images/icon_phone.svg b/ckanext/odsh/public/base/images/icon_phone.svg new file mode 100644 index 0000000000000000000000000000000000000000..3f8189d6c89ee21adb6af5d2a895600fd0204756 --- /dev/null +++ b/ckanext/odsh/public/base/images/icon_phone.svg @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 52.5 (67469) - http://www.bohemiancoding.com/sketch --> + <title>local_phone</title> + <desc>Created with Sketch.</desc> + <g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="Rounded" transform="translate(-341.000000, -3213.000000)"> + <g id="Maps" transform="translate(100.000000, 3068.000000)"> + <g id="-Round-/-Maps-/-local_phone" transform="translate(238.000000, 142.000000)"> + <g> + <polygon id="Path" points="0 0 24 0 24 24 0 24"></polygon> + <path d="M19.23,15.26 L16.69,14.97 C16.08,14.9 15.48,15.11 15.05,15.54 L13.21,17.38 C10.38,15.94 8.06,13.63 6.62,10.79 L8.47,8.94 C8.9,8.51 9.11,7.91 9.04,7.3 L8.75,4.78 C8.63,3.77 7.78,3.01 6.76,3.01 L5.03,3.01 C3.9,3.01 2.96,3.95 3.03,5.08 C3.56,13.62 10.39,20.44 18.92,20.97 C20.05,21.04 20.99,20.1 20.99,18.97 L20.99,17.24 C21,16.23 20.24,15.38 19.23,15.26 Z" id="🔹-Icon-Color" fill="#003064"></path> + </g> + </g> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/ckanext/odsh/public/base/images/logo-quer.png b/ckanext/odsh/public/base/images/logo-quer.png new file mode 100644 index 0000000000000000000000000000000000000000..a3baa7b1f60ef757a92924772fe7bd160c68dcc2 Binary files /dev/null and b/ckanext/odsh/public/base/images/logo-quer.png differ diff --git a/ckanext/odsh/public/base/images/logo-quer@2x.png b/ckanext/odsh/public/base/images/logo-quer@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7a77936ab8710e3a940e9f93d859a3992525d719 Binary files /dev/null and b/ckanext/odsh/public/base/images/logo-quer@2x.png differ diff --git a/ckanext/odsh/public/base/images/platzhalter.svg b/ckanext/odsh/public/base/images/platzhalter.svg new file mode 100644 index 0000000000000000000000000000000000000000..746fb2026e686850a5b0c295612f98768f866e28 --- /dev/null +++ b/ckanext/odsh/public/base/images/platzhalter.svg @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="svg10" + xml:space="preserve" + width="793.70074" + height="1122.5197" + viewBox="0 0 793.70074 1122.5197" + sodipodi:docname="platzhalter.svg" + inkscape:version="0.92.4 (unknown)"><metadata + id="metadata16"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs + id="defs14" /><sodipodi:namedview + pagecolor="#f1f1f1;" + bordercolor="#f1f1f1;" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1855" + inkscape:window-height="1056" + id="namedview12" + showgrid="false" + inkscape:zoom="0.81423964" + inkscape:cx="205.87465" + inkscape:cy="561.25983" + inkscape:window-x="65" + inkscape:window-y="24" + inkscape:window-maximized="1" + inkscape:current-layer="g18" /><g + id="g18" + inkscape:groupmode="layer" + inkscape:label="plazthalter" + transform="matrix(1.3333333,0,0,-1.3333333,0,1122.5197)"><g + id="g20"><g + id="g22" + transform="matrix(1,0,0,-1,0,841.88977)"><path + d="M 2.145,1.891 H 597.856 V 844.032 H 2.145 Z" + style="fill:#f1f1f1;fill-opacity:1;fill-rule:nonzero;stroke:#c8b7b7;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path24" + inkscape:connector-curvature="0" /></g></g></g></svg> \ No newline at end of file diff --git a/ckanext/odsh/public/odsh.css b/ckanext/odsh/public/odsh.css index de9f959d270ec810ef6e65d121615a208d3a2ce6..cb3f12fa070bae5751abb1c24ac28242f10eac05 100644 --- a/ckanext/odsh/public/odsh.css +++ b/ckanext/odsh/public/odsh.css @@ -1,11 +1,5 @@ /* FONTS */ -@media (max-width: 767px){ -.span9 { - width: 100%; -} -} - @font-face{ font-family:"MuseoSans"; src:url("/base/fonts/Fonts/905300/fd479df2-01b0-4e3a-a411-915c477f1352.eot?#iefix"); @@ -29,6 +23,17 @@ html,body { height: 100%; } +@media (max-width: 767px) { + body { + padding-left: 0px; + padding-right: 0px; + } + + .hide-on-mobile { + display: none; + } +} + /* FROM: https://stackoverflow.com/questions/2610497/change-an-html5-inputs-placeholder-color-with-css */ /* do not group these rules */ *::-webkit-input-placeholder { @@ -64,7 +69,8 @@ input, button, select, textarea { h1{ color:#003064; - font-weight: normal ; + font-size: 38px; + font-weight: 500; } h3{ @@ -73,6 +79,18 @@ h3{ font-size:20px; } +.skip-link { + position: fixed; + top: -200px; + text-decoration: none; + padding: 10px; + display: inline-block; + } + +.skip-link:focus { + top: 0; +} + .secondary h1.heading { color:black; font-weight: normal ; @@ -83,19 +101,74 @@ h3{ /* same width as schleswig-holstein.de */ .container { - width: 960px; + max-width: 960px; } -@media (max-width: 767px){ -.container { - width: 100%; + + +@media (max-width: 1023px){ + .container, + .masthead .container + { + padding-right: 80px; + padding-left: 80px; + box-sizing: border-box; + max-width: 100%; + width: auto; + } } + +@media (max-width: 767px) { + .container + { + padding-right: 15px; + padding-left: 15px; + } + .masthead .container + { + padding-right: 0; + padding-left: 0; + } } .search-form, .odsh-dataset-heading { + display: flex; + align-items: center; + flex-wrap: wrap; border-bottom: 4px solid #dbdbdb; - padding-bottom: 5px; + padding-bottom: 26px; + margin-bottom: 0px; } +@media (min-width: 1024px){ + .search-form, .odsh-dataset-heading { + border-bottom: none; + margin-bottom: 0px; + margin-left: -15px; + margin-right: -15px; + padding-bottom: 0; + } + + .search-form:after { + content: ""; + display: block; + margin: 0 auto; + width: calc(100% - 30px); + padding-top: 25px; + border-bottom: 4px solid #dbdbdb; + order: 100; + } + + .search-form h2 { + margin-left: 15px; + } + + .form-select.control-group.control-order-by { + margin-left: 15px; + margin-right: 15px; + } +} + + .organizaion-full-description{ padding-bottom: 50px; @@ -112,6 +185,9 @@ h3{ margin-bottom: 10px; } +.control-order-by { + margin: 0; +} .search-form .control-order-by select { width: 225px; @@ -121,14 +197,109 @@ h3{ color: #003064; } -.search-form h2, +.search-form h2, .odsh-dataset-heading h2, .odsh-dataset-heading .title { margin-top: 0px; - font-size: 25px; + margin-bottom: 10px; + margin-right: 22px; + font-size: 24px; + letter-spacing: 0.01em; color: #003064; font-weight: 300; + line-height: 28px; +} + + +.odsh-dataset-heading > h2 { + margin: 0px; +} + +.resources h3 { + font-size: 18px; + letter-spacing: 0.01em; + color: #000000; +} + +section#dataset-preview { + margin-top: 26px; +} + +@media (max-width: 767px) { + section#dataset-preview { + display: none; + } +} + +section#dataset-preview p { + color: black; + font-size: 14px; + letter-spacing: 0.01em; +} + +.container-preview-large { + padding-top: 10px; + padding-bottom: 10px; + height: 287px; + background-color: #F2F2F2; + display: flex; +} + +.container-preview-large a { + margin-left: auto; +} + +.container-preview-large img { + height: 100%; + margin-left: auto; + margin-right: auto; + border: 1px solid #666666; +} + +section#dataset-collection { + padding-top: 17px; + border-top: 5px solid #DBDBDB; + margin-top: 38px; + color: black; +} + +section#dataset-collection .button-container { + margin-top: 20px; + display: flex; +} + +section#dataset-collection .button-container a { + margin-right: 24px; + padding-left: 12px; + padding-right: 12px; +} + +section#dataset-collection .button-container a:nth-child(2) { + /* second a */ + margin-left: auto; +} + +section#dataset-collection .button-container a:last-child { + /* second a */ + margin-right: 0; +} + +section#dataset-collection .button-container img { + width: 18px; + height: auto; + margin-right: 5px; + margin-left: 5px; +} + +@media (max-width: 767px) { + .search-form h2, + .odsh-dataset-heading h2, + .odsh-dataset-heading .title + { + width: 100%; + margin-bottom: 8px; + } } .odsh-dataset-heading .title @@ -145,11 +316,50 @@ h3{ padding: 0; } +#section-org.module-content { + padding-bottom: 87px; + border-bottom: solid 3px #DBDBDB; +} + +.information-organisation { + margin-top: 20px; + font-size: 12px; + text-align: left; + letter-spacing: 0.12px; + color: #000000; +} + +.information-organisation p { + margin-bottom: 0; +} + +.information-organisation a { + color: inherit; +} + +.information-organisation .phone::before{ + content: ""; + display: inline-block; + background-image: url(/base/images/icon_phone.svg); + background-repeat: no-repeat; + background-size: .7rem .7rem; + background-position: 50% 50%; + width: 0.8rem; + height: 0.8rem; + vertical-align: middle; +} + + .module-narrow .module-footer { padding: 0; border: none; font-size: 12px; } +@media (max-width: 767px) { + .js [role="main"] .secondary .filters > div .module-footer { + display: block; + } +} .module .read-more{ font-weight: normal; @@ -186,10 +396,11 @@ input[type=radio], input[type=checkbox] { background-color: #f2f2f2; } -.is-table-row .secondary.span3 { - width: 191px; - max-width: 1970px; - padding: 20px 16px 0px; +.secondary.span3 { + box-sizing: border-box; + min-width: 300px; + max-width: 300px; + padding: 40.66px 16px 0px; } .secondary .module-narrow .module-content{ padding-left: 0px; @@ -197,16 +408,15 @@ input[type=radio], input[type=checkbox] { } .primary { - padding-left: 20px; + padding-left: 31px; } .primary.organization { padding-left: 0px; } -.is-table-row .primary.span9 { - width: 717px; - float: right; +.primary.span9 { + max-width: 640px; } .primary.logout { @@ -214,8 +424,8 @@ input[type=radio], input[type=checkbox] { padding-left: 0px; } -@media (max-width: 767px){ -.is-table-row .primary.span9 { +@media (max-width: 1023px){ +.primary{ padding-left: 0px; width: 100%; margin-left: 0; @@ -223,9 +433,6 @@ input[type=radio], input[type=checkbox] { .primary.span9{ width: 100%; } -.primary{ - padding: 0px; -} } .wrapper { @@ -235,6 +442,12 @@ input[type=radio], input[type=checkbox] { border: none; } +@media (max-width: 1023px){ + .wrapper { + width: 100%; + } +} + .karte_inner { background-color: lightgrey; height: 220px; @@ -243,20 +456,25 @@ input[type=radio], input[type=checkbox] { .filter-reset { padding: 0px 0px 10px 0px; + margin-top: 18px; } .filter-reset-label { float: left; + font-size: 14px; + letter-spacing: 0.14px; + color: black; } .filter-reset-box { float: right; background-color: white; - padding: 0px 10px 0px 10px; + padding: 1px 10px 4px 10px; } .filter-reset-box a { font-size: 12px; + color: #003064; } .filters section nav { @@ -271,28 +489,74 @@ input[type=radio], input[type=checkbox] { .nav-simple>li { border-bottom: 0px; - padding: 15px 0px 0px 0px; - width: 193px; + padding: 10px 0px 0px 0px; + width: 100%; white-space: nowrap; display: flex; justify-content: space-between; + align-items: center; } .nav-item .facet_label { - flex:1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + max-width: 240px; } -.nav-item .facet_label a{ +.nav-item .facet_label label.odsh-visible-label{ + display: inline; + font-size: inherit; +} + +.nav-item .facet_label label a{ color: black; + display: inline; } .nav-item .facet_count { display: inline-block; background-color: white; - padding: 0px 2px 0px 2px; + padding: 1px 2px; +} + +.package-info-category { + margin: 0; +} + +.category-with-icon { + display: inline-block; + /* padding-bottom: 0.5rem; */ + vertical-align: middle; +} + +.group-icon-container { + display: inline-block; + width: 16px; + height: 16px; + margin-right: 8px; + margin-left: 3px; +} + +.group-icon-container img { + vertical-align: top; +} + +.category-header { + display: inline-block; + padding-right: 6px; + vertical-align: top; +} + +.category-icon-container { + display: inline-block; + width: 16px; + height: 16px; + margin-right: 3px; +} + +.category-name { + padding-right: 15px; } @@ -304,7 +568,7 @@ input[type=radio], input[type=checkbox] { label.label-score, label.rangesearch, -input[type=checkbox] + label.mylabel { +input[type=checkbox] + span.mylabel { background: #fff; border: 1px solid black; height: 8px; @@ -317,7 +581,7 @@ input[type=checkbox] + label.mylabel { input[type=checkbox]:checked + label.label-score, label.rangesearch.checked, -input[type=checkbox]:checked + label.mylabel { +input[type=checkbox]:checked + span.mylabel { background: url(/base/images/checked.png); background-color: #fff; background-size: 8px 8px; @@ -330,28 +594,6 @@ input[type=checkbox]:checked + label.mylabel { margin-bottom: 0px; } - -@media (max-width: 768px) -{ -input[type=checkbox] + label.mylabel, -input[type=checkbox]:checked + label.mylabel { - height: 20px; - width: 20px; - background-size: 20px 20px; - vertical-align: text-bottom; -} -.facet_label { - font-size: 18px; - vertical-align: text-bottom; -} -.nav-simple>li{ - width: 100%; -} -.js [role=main] .secondary .filters { - background-color: white; -} -} - .checklabel input[type="checkbox"] { float: inherit; @@ -359,7 +601,7 @@ input[type=checkbox]:checked + label.mylabel { label.label-score, label.rangesearch, -label.mylabel { +span.mylabel { margin-right: 3px; margin-bottom: 0px; -webkit-user-select: none; /* Safari */ @@ -369,7 +611,7 @@ label.mylabel { } label.rangesearch::after, -label.mylabel::after { +span.mylabel::after { content: ""; } @@ -386,6 +628,10 @@ label.rangesearch.disabled { display: inline-block; } +.controls.rangesearch label { + display: block; +} + .search-score { color:black; @@ -399,13 +645,15 @@ label.rangesearch.disabled { .rangesearch a{ color:black; - font-size:14px; + font-size:12px; } .controls input.rangesearch{ - font-style: normal; - width:187px; + box-sizing: border-box; + width: 100%; + height: 1.5rem; padding: 2px; + font-style: normal; font-size: 12px; line-height: 1.16666667em; } @@ -446,20 +694,45 @@ label.rangesearch.disabled { font-size: 10px; position: relative; top: -8px; - right: -8px; + right: -8px; + margin-right: 0; } .pill.error .remove .fa.fa-times{ background-color: #d4004b; } -.container-fluid.odsh-dataset-item { - padding: 20px 0px 20px 0px; +.odsh-dataset-item { + display: flex; + padding: 27px 0px; border-bottom: 2px solid #DBDBDB; - display: table; + width: 100%; } -.container-fluid.odsh-dataset-item:hover { +.odsh-dataset-item:hover { background-color: rgb(246,247,249); } +.odsh-dataset-item:last-child { + border-bottom: none; +} + +.preview-image-container { + display: flex; + flex-direction: column; + margin-right: 25px; + width: auto; +} + +@media (max-width: 767px) { + .preview-image-container { + display: none; + } +} + +.preview-image-container img { + margin-top: auto; + height: auto; + max-width: 102px; + border: 1px solid #666666; +} .organization-item{ padding: 5px 0px 5px 0px; @@ -490,29 +763,28 @@ a:hover.organization-item ,a:focus.organization-item width: 705px; } -.dataset-content { - width: 470px; - display: table-cell; - position: relative; -} - .dataset-heading { - margin-right: 30px; font-weight: normal; font-size: 20px; word-wrap: break-word; - max-width: 310px; + width: 100%; + margin-bottom: 12px; +} +.dataset-content { + flex-grow: 1; } .dataset-content p{ font-size: 12px; - margin: 0 0 5px; + /* margin: 0 0 5px; */ } .dataset-heading a{ + color: black; text-decoration: none; + padding-right: 30px; + letter-spacing: 0.18px; } .dataset-spacer{ - display: table-cell; width: 25px; } @@ -522,9 +794,14 @@ a:hover.organization-item ,a:focus.organization-item } .dataset-meta { - width: 225px; - height: 100%; - display: table-cell; + position: relative; +} + +.package-info-pill img { + padding-right: 5px; + height: auto; + width: 18px; + bottom: 1.5px; position: relative; } @@ -555,8 +832,6 @@ ul.dataset-resources { } -element.style { -} .dataformat-label:hover { background-color: #d4004b!important; } @@ -564,13 +839,10 @@ element.style { .dataset-stars { + width: 190px; margin-bottom: 10px; } - -.popover-content{ - -} .popover{ width: unset; display:inline-table!important; @@ -671,6 +943,11 @@ label:after { font-style: normal; } +input#field-username { + margin-bottom: 30px; + vertical-align: super; +} + .control-group.error input, .control-group.error select, .control-group.error .select2-choices, @@ -965,15 +1242,10 @@ label:after { .footer a{ color:white; } -@media (max-width: 767px){ -.footer{ - border-top:0px; - width: 100%; -} body { background-color: white; } -} + .footer .container{ width: 100%; max-width: 1000px; @@ -1015,6 +1287,50 @@ body { margin-top: 10px; } +@media (max-width: 1023px){ + .footer{ + border-top-width: 30px; + width: 100%; + } + .footer-line{ + border-top: 1px solid white; + } +} + +@media (max-width: 767px){ + .footer{ + border-top-width: 20px; + } + .footer .container { + padding-bottom: 0; + } + .footer-line{ + border-top: none; + margin-top: 0; + margin-bottom: 0; + } + .footer-content{ + box-sizing: border-box; + padding-bottom: 15px; + background-image: none; + height: 139px; + display: flex; + flex-direction: column-reverse; + } + .footer-first-row{ + padding-top: 0; + height: 0; + } + .footer-right{ + display: flex; + justify-content: space-between; + padding-bottom: 15px; + } + .footer-icon a{ + padding: 15px 0; + } +} + .toolbar .breadcrumb li:after { background-image: url(/base/images/arrow-right.png); background-repeat: no-repeat; @@ -1079,6 +1395,12 @@ body { border: none; } +@media(maxwidth: 767px) { + .page-header .nav-tabs { + margin: 0; + } +} + .module .module-content { margin: 0px; } @@ -1086,8 +1408,9 @@ body { .info-detail { margin-bottom: 20px; font-size: 12px; + letter-spacing: 0.01em; color: black; - max-width: 193px; + max-width: 100%; word-break: break-word; } .info-detail p{ @@ -1097,9 +1420,22 @@ body { margin-top: 5px; margin-bottom: 5px; } +.info-detail img { + margin-right: 5px; +} + +.info-detail.groups-detail > div { + margin-bottom: 5px; +} + .odsh-dataset-heading { word-wrap: break-word; - margin-bottom: 30px; + padding-bottom: 16px; + margin-bottom: 18px; +} + +.odsh-dataset-edit-button { + margin-top: 10px; } .odsh-dataset-heading .dataset-stars { @@ -1110,11 +1446,18 @@ body { margin-bottom: 30px; } +.notes > p { + font-size: 16px; + letter-spacing: 0.01em; + color: #000000; +} + .resource-list { margin: 0px; } .resource-item { + display: flex; background: #f2f2f2; margin-top: 10px; padding: 15px 15px 15px 15px; @@ -1135,14 +1478,10 @@ body { text-align: center; } -element.style { -} .dataformat-label:hover { background-color: #d4004b!important; } - - .resource-title { float: left; margin-left: 5px; @@ -1153,18 +1492,34 @@ element.style { cursor: pointer; text-decoration: none; min-height: 1em; + color: black; } .resource-title a{ color: black; + letter-spacing: 0.16px; } .resource-title a:hover{ text-decoration: none } +.resource-title p:first-of-type{ + font-size: 12px; + letter-spacing: 0.12px; + padding-top: 12px; + margin-bottom: 0; +} +.resource-title p{ + font-size: 12px; + letter-spacing: 0.12px; + padding-top: 0; +} + .resource-title-container{ display: flex; + justify-content: space-between; + width: 100%; } .resource-title-container .dataformat-label:hover{ background-color: #003064 !important; @@ -1185,15 +1540,6 @@ element.style { float:left; } -@media (max-width: 767px) -{ -.is-table-row .resource-details-left { - /* padding-right: 0px; */ - width: 100%; - /* float:none; */ -} -} - .resource-icons { font-size: 24px; margin-left: 5px; @@ -1230,6 +1576,12 @@ element.style { vertical-align: 0px; } +@media (max-width: 767px) { + .toolbar .breadcrumb .home { + display: inline-block; + } +} + .resource-icons a { color: #003064; @@ -1246,19 +1598,31 @@ element.style { } .search-box-container{ - width:100%; - max-width: 430px; + box-sizing: border-box; + width: auto; + height: 40px; + max-width: 470px; margin-bottom: 60px; - margin-left: 250px; + margin-left:330px; +} + +.search-field { + display: flex; + align-items: center; } #field-sitewide-search { - font-size: 87%; + flex: 1; + font-size: 14px; font-weight: 300; - padding: 0px; + padding-top: 0px; + padding-bottom: 0px; padding-right: 35px; - padding-left: 5px; - width: 100%; + padding-left: 14.24px; + margin: 0px; + margin-left: 0px; + width: inherit; + min-width: 210px; line-height: 1.1; border: 1px solid #003064!important; color:#003064; @@ -1266,30 +1630,57 @@ element.style { box-shadow: none !important; height: 40px; border-radius: 0px; + box-sizing: border-box; } .simple-input .field .btn-search { - height: 25px; - width: 25px; + height: 36px; + width: 36px; background: url(/base/images/Icon-Suche.png); - background-size: 25px 25px; - right: -28px; - top: -25px; - position: relative; - float: right; + background-size: 36px 36px; + top: 3px; + margin: 0px 5px; +} + +.simple-input .field .btn-search:focus { + border-color: #003064; + border-width: 1px; + border-style: dotted; } -.search-box-container .field{ +/* .search-box-container .field{ width: 100%; max-width: 465px; +} */ + +@media (max-width: 1023px){ + .search-box-container { + flex: 1; + box-sizing: border-box; + margin-left: auto; + margin-bottom: 20px; + padding: 0px; + display: inline-block; + height: 40px; + width: 100%; + min-width: 210px; + } + + .filter-search-row { + display: flex; + flex-wrap: wrap; + } } @media (max-width: 767px){ -.search-box-container{ - margin-bottom: 20px; - padding: 0px; - margin-left: 0px; -} + .search-box-container:focus-within { + position: absolute; + margin-right: auto; + margin-left: auto; + padding: 0; + width: 80%; + min-width: 95%; + } } @@ -1501,22 +1892,6 @@ element.style { margin-bottom: 0px; } -#dataset-map .module-heading [class^="icon-"], -#dataset-map .module-heading [class*=" icon-"] { - font-size: 14px; - line-height: 1; - width: 14px; - text-align: left; - display: inherit; -} -@media (max-width: 767px){ -#dataset-map { -display: none; -} -#dataset-map { -display: none; -} -} @media (min-width: 768px){ .span9 div.module-content { @@ -1524,7 +1899,9 @@ display: none; } } -.field-organization .select2-container .select2-choice { +.field-organization .select2-container .select2-choice, +.field-spatial_uri .select2-container .select2-choice +{ background-color: #F6F7F9; background-image: none; background-image: none; @@ -1537,36 +1914,110 @@ display: none; .logo{ margin-left: -7px; - height: 70px; + height: auto; + box-sizing: border-box; + width: 155px; + display: block; } -.logo img{ - height: 65px; - width:auto; - padding: 6px; +.img-logo-large { + display: initial; +} +.img-logo-small { + display: none; } -.site-title{ - color:black; - font-size: 2.5rem; - margin-top: 30px; - color: #333; - text-align: center; +@media (max-width: 1023px){ + .logo { + width: 121.1px; + height: auto; + display: block; + } +} + +@media (max-width: 767px){ + .logo { + display: initial; + } + .img-logo-large { + display: none; + } + .img-logo-small { + display: initial; + } +} + +.topline { + margin-bottom: 25px; +} +@media (max-width: 767px){ + .topline { + margin-bottom: 0px; + } +} + +.site-title { + color:#003064; + font-size: 38px; + position: absolute; + left: 340px; + text-align: left; line-height: 1.47; - font-weight: 100; + font-weight: 500; + letter-spacing: 0.38px; + padding-top: 24px; +} + +@media (max-width: 1023px){ + .site-title{ + font-size: 24px; + line-height: 1.0; + display: inline-block; + left: initial; + margin-left: 33px; + } +} + +#dataset-search-box-form{ + margin-bottom: 0; } @media (max-width: 767px){ -.logo img{ - height: 25px; - width:auto; - padding: 6px; + .site-title{ + display: none; + } +} + +#menu-button-and-title { + display: none; +} +@media (max-width: 767px){ + #menu-button-and-title { + display: flex; + align-items: center; + padding-left: 15px; + padding-right: 15px; + background-color: #f2f2f2; + } } -.site-title{ - font-size: 1.5rem; - margin-top: 15px; - color: #333; - line-height: 1.0; +#menu-button-and-title span { + display: inline-block; + color: #003064; + padding-right: 20px; } +#menu-button-and-title h1 { + display: inline-block; +} + + +.site-title-mobile{ + font-size: 18px; + line-height: 1.0; + position: relative; + margin-left: 0; +} + +.search-form .filter-list { + width: 100% } .filter-list .filtered.pill, @@ -1574,6 +2025,14 @@ display: none; background-color: #003064; color:white; } + +@media (min-width: 1024px){ + .search-form .filter-list { + margin-left: 15px; + margin-right: 15px; + } +} + .filtered.pill.error{ background-color: #d4004b; } @@ -1582,6 +2041,7 @@ display: none; .share-links { display: flex; justify-content: space-between; + margin-top: 10px; } .share-links a{ @@ -1591,7 +2051,7 @@ display: none; } .share-links img { - width: 20px; + width: 23px; } .home-title{ @@ -1774,11 +2234,9 @@ a:hover.tooltips_cls span { #testsystem p { margin:0 } .tag-container { - border-top: 1px solid #DBDBDB; padding-top:10px; margin-top:20px; display: flex; - width: 720px; flex-wrap: wrap; flex-direction: row; justify-content: start; @@ -1859,15 +2317,23 @@ span.clear { .package-info-pill { display: inline-block; + box-sizing: border-box; + height: 20px; padding: 3px 10px; background-color: #dbdbdb; - border-radius: 5px; + border-radius: 3px; font-size: 12px; + margin-right: 10px; + margin-bottom: 17.84px; } -p.package-info-categorie +p.package-info-subject { - margin-top: 15px; + margin-top: 12px; + margin-bottom: 0px; +} + +p.package-info-issued { margin-bottom: 0px; } @@ -2001,9 +2467,34 @@ p.package-info-categorie font-size: 20px; cursor: pointer; } + .search-form a { color: #003064; + margin-left: auto; + margin-right: 22px; +} +@media (max-width: 767px) { + .search-form a + { + order: 4; + margin-right: 0; + } + + .search-form .filter-list { + order: 5; + break-before: always; + } + + .search-form #datesearch-filter { + order: 6; + } +} + +.search-form a:focus i { + border-color: #003064; + border-width: 1px; + border-style: dotted; } .error-title @@ -2075,6 +2566,7 @@ p.package-info-categorie padding: 3px 3px 1px 3px; font-size: 14px; margin-right: 4px; + text-shadow: none; } .comments-heading{ @@ -2167,96 +2659,113 @@ p.package-info-categorie width: 715px; } -@media (max-width: 767px) -{ -[role=main] .secondary.span3 { - display: block; - width: 100%; - margin-left: 0; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - padding: 0; - order: 2; -} -#testsystem -{ - display: none; -} -.simple-input .field .btn-search -{ - display: none; -} -#field-sitewide-search { - padding: 0px; - width: 100%; - margin: 0px; -} -} +@media (max-width: 1023px) { + [role=main] .secondary.span3 { + max-width: 300px; + margin-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + position: fixed; + top: 0; + left: 0; + display: none; + z-index: 3; + height: 100%; + overflow: scroll; + filter: blur(0) brightness(100%); + } -@media (max-width: 767px) -{ - .masthead .container, - body { - padding-left: 0px; - padding-right: 0px; -} -#content -{ - margin-left: 0px; - padding-right: 0px; -} -.dataset-heading { - max-width: unset; - width: 100%; - margin: 0px; -} -/* .dataset-heading a { - max-width: unset; - width: 100%; - margin: 0px; -} */ -.js body.filters-modal .secondary .filters .hide-filters i { - font-size: 32px; -} -.container-fluid.odsh-dataset-item { - width: 100%; - padding: 20px 15px 20px 15px; - box-sizing: border-box; -} -.row.wrapper.is-table-row -{ - width: 100%; - display: flex; - flex-direction: column; -} -.dataset-info.issued -{ - position: relative; -} -ul.dataset-resources -{ - margin: 5px 0px 5px 0px; -} -.stages li .highlight { - text-indent: unset; -} -.tag-container -{ - width: 100%; -} -} -.module-content.page-header -{ - background-color: unset; + [role=main] .secondary.span3 > .filters { + position: initial; + background-color: inherit; + padding: 0px; + } + + .js [role=main] .secondary > .filters > div { + background-color: inherit; + } + + div.hide-filters-style > a.hide-filters { + display: block; + position: absolute; + top: 18px; + right: 18px; + } + @media (max-width: 767px) { + .js body.filters-modal .secondary .filters > .hide-filters-style > a.hide-filters { + display: block; + position: absolute; + top: 18px; + right: 18px; + opacity: 1; + } + } + + body.menu-modal { + overflow: hidden; + } + + body.filters-modal div.primary, + body.filters-modal header, + body.filters-modal .search-box-container, + body.filters-modal .toolbar, + body.menu-modal div.primary, + body.menu-modal .search-box-container, + body.menu-modal .toolbar + { + filter: blur(3px); + -webkit-backdrop-filter: blur(3px); + } + + body.filters-modal div.blur-layer, + body.menu-modal div.blur-layer + { + position: absolute; + width: 100vw; + height: 100vh; + background-color: #000000; + opacity: 0.6; + z-index: 2; + } + body.filters-modal div.blur-layer + { + top: 0; + } } -@media (max-width: 768px) -{ -.wrapper { - margin: 0; +body.filters-modal div.row > aside.secondary.span3 { + display: block; } -.dataset-edit-form .span6 { - width: 100%; + + +@media (max-width: 1023px) +{ + .js .show-filters.btn { + box-sizing: border-box; + display: inline-block; + background-image: url("/base/images/icon_funnel.svg"); + background-size: 18px 18px; + background-repeat: no-repeat; + background-position: 15px center; + padding-left: 49px; + margin-right: 28px; + display: inline-block; + height: 40px; + background-color: #0089CA; + color: white; + font-size: 18px; + margin-bottom: 12px; + } + + .js .module-content > .show-filters.btn { + background-image: none; + padding-left: 15px; + } + + .js .module-content > .show-filters.btn > img { + width: 24px; + height: auto; + margin-right: 6px; + } } -} \ No newline at end of file diff --git a/ckanext/odsh/public/odsh_header.css b/ckanext/odsh/public/odsh_header.css index 13406da5d0baef850855331ea4dc2a51513ed0ad..31822582d5b095bb524b579579b44830b182786a 100644 --- a/ckanext/odsh/public/odsh_header.css +++ b/ckanext/odsh/public/odsh_header.css @@ -10,6 +10,13 @@ max-width: 1280px; } +@media (max-width: 767px) { + .navbar-static-top .container.navigation-container { + padding-left: 15px; + padding-right: 15px; + } +} + [class*="span"] { margin-left: 0px; } @@ -31,9 +38,15 @@ padding-right: 0; position: relative; top: 0; + left: 18px; padding: 1em 0; } +/* @media (max-width: 767px){ + .header-image { + display: block; + } */ + .language-switch { float: right; background-color: #f2f2f2; @@ -100,6 +113,13 @@ background: white; } +@media (max-width: 1023px) { + .masthead { + margin-left: 0; + margin-right: 0; + } +} + .navigation-container { background: #f2f2f2; @@ -107,8 +127,21 @@ border-bottom: 15px solid #003064; } +@media (max-width: 1023px) { + .navigation-container { + border-bottom: 10px solid #003064; + } +} + +@media (max-width: 767px) { + .navigation-container { + border-bottom: 8px solid #003064; + } +} + .navigation-row { width: 960px; + max-width: 100%; padding-left: 0px; margin-left: auto; margin-right: auto; @@ -128,6 +161,75 @@ margin: 0px; } +.header-menu-mobile { + display: none; +} +@media (max-width: 767px) { + .menu-modal .navbar.masthead .nav > li.header-menu-mobile, + .menu-modal .navbar.masthead .nav > li.header-menu-mobile:hover { + display: initial; + background-color: #003064; + border-top-width: 0; + position: relative; + } + .menu-modal .navbar.masthead .navigation ul.nav-pills > li.header-menu-mobile > a { + color: white; + font-size: 18px; + } + .menu-modal .navbar.masthead .navigation ul.nav-pills > li.header-menu-mobile > img { + display: inline-block; + position: absolute; + margin-top: -10px; + top: 50%; + right: 17px; + } +} + +@media (max-width: 767px) { + .navbar .nav { + display: none; + } + + .menu-modal .navbar nav { + max-width: 250px; + background-color: white; + } + + .menu-modal .navbar .nav { + display: block; + } + + .menu-modal .navbar.masthead .navigation { + position: absolute; + top: 8px; + left: 0; + z-index: 3; + } + + .menu-modal .navbar.masthead .navigation ul.nav-pills > li { + box-sizing: border-box; + min-width: 100%; + padding-left: 21px; + margin-left: 0; + } + + .menu-modal .navbar.masthead .navigation ul.nav-pills > li.active { + border-top: 2px solid #f2f2f2; + } + + .menu-modal .navbar.masthead .navigation .nav-pills > li > a, + .menu-modal .navbar.masthead .navigation .nav-pills > li.active > a { + color: #003064; + font-size: 14px; + font-weight: 500; + } + .menu-modal .navbar.masthead .navigation .nav-pills > li:hover > a { + color: #ffffff; + font-size: 14px; + font-weight: 500; + } +} + .navbar.masthead .navigation .nav-pills, .navbar.masthead .navigation { width: 100%; @@ -139,8 +241,10 @@ background-color: #003064 !important; } -.navbar.masthead .navigation .nav-pills li:not(:first-child) { - margin-left: 22px; +@media (min-width: 768px) { + .navbar.masthead .navigation .nav-pills li:not(:first-child) { + margin-left: 22px; + } } /* Default border to align font vertically to active navigation tab */ @@ -251,34 +355,12 @@ float: right; } -@media (max-width: 767px) -{ -.navbar.masthead .navigation .nav-pills li.dropdown -{ - float: none; -} -.nav-pills li a:hover, .nav-pills li:hover { - background-color: rgb(242, 242, 242)!important; -} -.dropdown-backdrop -{ - display: none; -} -.navigation-row { - width: 100%; - padding-left: 0px; - margin-left: auto; - margin-right: auto; -} -} - .navbar.masthead .navigation .nav-pills li.dropdown > .dropdown-toggle { padding-bottom: 12px; padding-top: 12px; } - .user-icon { fill:#003064; @@ -308,19 +390,3 @@ { fill:white; } - -@media (max-width: 767px) -{ -.navbar-fixed-top, .navbar-fixed-bottom, .navbar-static-top { - margin-left: 0px; - margin-right: 0px; -} - -.navbar.masthead .nav>li:hover a, -.navbar.masthead .navigation .nav-pills li.open .user-icon, .navbar.masthead .navigation .nav-pills li a:hover .user-icon, -.navbar.masthead .navigation .nav-pills li.dropdown.open > a.dropdown-toggle:hover, .navbar .nav li.dropdown.open > .dropdown-toggle -{ - color: #003064; - fill: #003064; -} -} \ No newline at end of file diff --git a/ckanext/odsh/search.py b/ckanext/odsh/search.py new file mode 100644 index 0000000000000000000000000000000000000000..bc68443ccb0fab2453156e65712d2c293fc9bdd5 --- /dev/null +++ b/ckanext/odsh/search.py @@ -0,0 +1,80 @@ +import ckanext.odsh.helpers as odsh_helpers + +def before_search(search_params): + _update_facet_mincount(search_params) + _update_daterange_query(search_params) + return search_params + +def _update_facet_mincount(search_params): + search_params.update({'facet.mincount': 0}) + +def _update_daterange_query(search_params): + start_date, end_date = _get_start_and_end_date(search_params) + is_no_range = not start_date and not end_date + if is_no_range: + return search_params + start_date_or_star = start_date or '*' + end_date_or_star = end_date or '*' + start_query, end_query = _get_start_and_end_query(start_date_or_star, end_date_or_star) + is_enclosing_range = start_date and end_date and start_date < end_date + if is_enclosing_range: + enclosing_query = _get_enclosing_query(start_date_or_star, end_date_or_star) + else: + enclosing_query = '' + open_end_query = _get_open_end_query(end_date_or_star) + fq_preset = search_params.get('fq') + fq = _combine_query(fq_preset, start_query, end_query, enclosing_query, open_end_query) + search_params.update({'fq': fq}) + +def _get_start_and_end_date(search_params): + extras = search_params.get('extras') + start_date_local, end_date_local = ( + extras.get(key) + for key in ('ext_startdate', 'ext_enddate') + ) + try: + start_date, end_date = ( + odsh_helpers.extend_search_convert_local_to_utc_timestamp(date) + for date in (start_date_local, end_date_local) + ) + except ValueError: + start_date = end_date = None + return start_date, end_date + +def _get_start_and_end_query(start_date, end_date): + start_query, end_query = ( + '+extras_temporal_{start_or_end}:[{start_date} TO {end_date}]'.format( + start_or_end=__, + start_date=start_date, + end_date=end_date + ) + for __ in ('start', 'end') + ) + return start_query, end_query + +def _get_open_end_query(end_date): + if end_date is '*': + open_end_query = '(*:* NOT extras_temporal_end:[* TO *])' + else: + open_end_query = '((*:* NOT extras_temporal_end:[* TO *]) AND extras_temporal_start:[* TO {end_date}])'.format( + end_date=end_date) + return open_end_query + +def _get_enclosing_query(start_date, end_date): + enclosing_query_start = 'extras_temporal_start:[* TO {start_date}]'.format( + start_date=start_date) + enclosing_query_end = 'extras_temporal_end:[{end_date} TO *]'.format( + end_date=end_date) + enclosing_query = ' OR ({enclosing_query_start} AND {enclosing_query_end})'.format( + enclosing_query_start=enclosing_query_start, enclosing_query_end=enclosing_query_end) + return enclosing_query + +def _combine_query(fq_preset, start_query, end_query, enclosing_query, open_end_query): + combined_query = u'{fq_preset} ({start_query} OR {end_query} {enclosing_query} OR {open_end_query})'.format( + fq_preset=fq_preset, + start_query=start_query, + end_query=end_query, + enclosing_query=enclosing_query, + open_end_query=open_end_query + ) + return combined_query \ No newline at end of file diff --git a/ckanext/odsh/templates/base.html b/ckanext/odsh/templates/base.html index debf3e341031fb6cf5bdfd328b4accc3fa23eb20..8191f4ce7851b544041bd209461165c8e8ab0fed 100644 --- a/ckanext/odsh/templates/base.html +++ b/ckanext/odsh/templates/base.html @@ -11,10 +11,13 @@ {% block head_extras %} {{ super() }} +{% if h.odsh_use_matomo() %} {% set matomo_url = h.odsh_tracking_url()%} {% set matomo_id = h.odsh_tracking_id()%} +{% endif %} <meta data-name="type" content="{{h.odsh_is_slave()}}"> +{% if h.odsh_use_matomo() %} <!-- Matomo --> <script type="text/javascript"> var _paq = _paq || []; @@ -29,6 +32,7 @@ })(); </script> <!-- End Matomo Code --> +{% endif %} {% endblock %} {% block bodytag %} data-site-root="{{ h.odsh_public_url() }}" data-locale-root="{{ h.odsh_public_url() }}" {% endblock %} {% block page %} diff --git a/ckanext/odsh/templates/footer.html b/ckanext/odsh/templates/footer.html index 5aad688f3330a8af36d0c7f481684f30f531fb80..03764a7c7b58782fb85faee0af0af365732205df 100644 --- a/ckanext/odsh/templates/footer.html +++ b/ckanext/odsh/templates/footer.html @@ -1,4 +1,4 @@ -<div class="footer"> +<footer class="footer"> <div class="container"> <div class="footer-content"> <div class='footer-first-row'> @@ -8,12 +8,12 @@ <div class='footer-line'></div> <div class="footer-left"> <div class="footer-icon">© {{g.site_title}}</div> - <div class="footer-right"> - <div class='footer-icon'><a href='http://www.schleswig-holstein.de/odpkontakt'>Kontakt</a></div> - <div class='footer-icon'><a href='http://www.schleswig-holstein.de/odpimpressum'>Impressum</a></div> - <div class='footer-icon last'><a href='http://www.schleswig-holstein.de/odpdatenschutz'>Datenschutz</a></div> - </div> + </div> + <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 last'><a href='http://www.schleswig-holstein.de/tpdatenschutz'>Datenschutz</a></div> </div> </div> </div> -</div> \ No newline at end of file +</footer> \ No newline at end of file diff --git a/ckanext/odsh/templates/header.html b/ckanext/odsh/templates/header.html index 1c06ef6108440d9f462939575fd0cc7c29bbc8df..afc0250f21f7591cf03625c0fe9dd7e305bd033c 100644 --- a/ckanext/odsh/templates/header.html +++ b/ckanext/odsh/templates/header.html @@ -1,112 +1,117 @@ +{% resource 'odsh/tpsh_toggle_menu.js' %} + {% block header_wrapper %} {% block header_account %} <header class="navbar navbar-static-top masthead"> - {% block header_debug %} - {% if g.debug and not g.debug_supress_header %} - <div class="debug">Controller : {{ c.controller }}<br />Action : {{ c.action }}</div> - {% endif %} - {% endblock %} - <div class="container"> - {# The .header-image class hides the main text and uses image replacement for the title #} - <div class='row'> - - <div class='span3 span-navigation'> - <hgroup class="{{ g.header_class }} pull-left"> + {% block header_debug %} + {% if g.debug and not g.debug_supress_header %} + <div class="debug">Controller : {{ c.controller }}<br />Action : {{ c.action }}</div> + {% endif %} + {% endblock %} + <div class="container"> + {# The .header-image class hides the main text and uses image replacement for the title #} + <div class='row topline'> - {% block header_logo %} - <a class="logo" href="{{ h.url_for('home') }}"><img src="{{ h.url_for_static_or_external(g.site_logo) }}" alt="{{ g.site_title }}" - title="{{ g.site_title }}" /></a> - {% endblock %} - </hgroup> - </div> - <button data-target=".nav-collapse" data-toggle="collapse" class="btn btn-navbar" type="button"> - <span class="fa fa-bars"></span> - </button> + <div class='span3 span-navigation'> + <hgroup class="{{ g.header_class }} pull-left"> - <div class='site-title'> {{ g.site_title }} </div> + {% block header_logo %} + <a class="logo" href="{{ h.url_for('home') }}"> + <img class="img-logo-large" src="{{ h.url_for_static_or_external(g.site_logo) }}" alt="Logo Schleswig Holstein" + title="{{ g.site_title }}" /> + <img class="img-logo-small" src="/base/images/logo-quer.png" alt="Logo Schleswig Holstein" + title="{{ g.site_title }}" /> + </a> + {% endblock %} + </hgroup> + </div> + <h1 class='site-title'> {{ g.site_title }} </h1> + </div> + <div id="menu-button-and-title" data-module="tpsh_toggle_menu"> + <span class="fa fa-bars"></span> + <h1 class='site-title-mobile'> {{ g.site_title }} </h1> + </div> </div> - </div> - <div class="container navigation-container"> - <div class='row navigation-row'> - <div class="nav-collapse collapse"> - <nav class="section navigation"> - <ul class="nav nav-pills"> - {% block header_site_navigation_tabs %} - {{ h.build_nav_main( - ('home', _('Start')), - ('search', _('Daten')))}} - {% if c.userobj %} - <li > - <a href="{{ h.url_for('/datarequest/') }}" >{{_('Datarequest')}}</a> - </li> - {% endif %} - {{ h.build_nav_main( - ('organizations_index', _('Herausgeber')), - ('info_page', _('Infos')) - ) }} - {% endblock %} - {% set clazz='active' if c.action=='login' else ''%} - {% if h.check_access('package_create') %} - <li class='{{clazz}}'> - {% link_for _('Upload'), controller='package', action='new', group=c.group_dict.id, class_='text' %} - </li> - {% endif %} - {% if c.userobj %} - <li class='{{clazz}} dropdown navbar-right'> - {% set name = c.userobj.fullname if c.userobj.fullname else c.userobj.email%} - <a class="dropdown-toggle" data-toggle="dropdown" href="#"> - <svg class='user-icon' viewBox="0 0 54 54"> - <circle class='user-icon' cx="27" cy="17" r="7.5"/> - <path class='user-icon' d="M42.5,44.5h-31v-6.4c0-4.7,3.9-8.6,8.6-8.6h13.9c4.7,0,8.6,3.9,8.6,8.6V44.5z"/> - </svg> - {{name}}</a> - <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"> - <li> - <svg class='user-icon-small' viewBox="0 0 54 54"> - <circle cx="27" cy="17" r="7.5"/> - <path d="M42.5,44.5h-31v-6.4c0-4.7,3.9-8.6,8.6-8.6h13.9c4.7,0,8.6,3.9,8.6,8.6V44.5z"/> - </svg> - <a href="{{ h.url_for('/user/') }}" title="logout"> - <span class="text"> - Mein Profil einsehen - </span> - </a> - </li> - <li> - <a href="{{ h.url_for('/user/edit') }}" title="logout"> - <i class='fa fa-edit'></i> - <span class="text"> - Mein Profil bearbeiten - </span> - </a> - </li> - <li> - <a href="{{ h.url_for('/user/_logout') }}" title="logout"> - <i class='fa fa-sign-out'></i> - <span class="text">Logout</span> - </a> - </li> + <div class="container navigation-container"> + <div class='row navigation-row'> + <nav class="section navigation"> + <ul class="nav nav-pills"> + <li class="header-menu-mobile" data-module="tpsh_toggle_menu"> + <a>Menü</a> + <img src="/base/images/icon_close_white.svg" alt="Menü schließen" aria-label="Menü schließen"> + </li> + {% block header_site_navigation_tabs %} + {{ + h.build_nav_main( + ('home', _('Start')), + ('search', _('Datensätze')), + ('organizations_index', _('Herausgeber')), + ('info_page', _('Infos')) + ) + }} + {% endblock %} + {% set clazz='active' if c.action=='login' else ''%} + {% if h.check_access('package_create') %} + <li class='{{clazz}}'> + {% link_for _('Upload'), controller='package', action='new', group=c.group_dict.id, class_='text' %} + </li> + {% endif %} + {% if c.userobj %} + <li class='{{clazz}} dropdown navbar-right'> + {% set name = c.userobj.fullname if c.userobj.fullname else c.userobj.email%} + <a class="dropdown-toggle" data-toggle="dropdown" href="#"> + <svg class='user-icon' viewBox="0 0 54 54"> + <circle class='user-icon' cx="27" cy="17" r="7.5" /> + <path class='user-icon' + d="M42.5,44.5h-31v-6.4c0-4.7,3.9-8.6,8.6-8.6h13.9c4.7,0,8.6,3.9,8.6,8.6V44.5z" /> + </svg> + {{name}}</a> + <ul class="dropdown-menu" role="menu"> + <li> + <svg class='user-icon-small' viewBox="0 0 54 54"> + <circle cx="27" cy="17" r="7.5" /> + <path + d="M42.5,44.5h-31v-6.4c0-4.7,3.9-8.6,8.6-8.6h13.9c4.7,0,8.6,3.9,8.6,8.6V44.5z" /> + </svg> + <a href="{{ h.url_for('/user/') }}" title="logout" role="menuitem"> + <span class="text"> + Mein Profil einsehen + </span> + </a> + </li> + <li> + <a href="{{ h.url_for('/user/edit') }}" title="logout" role="menuitem"> + <i class='fa fa-edit'></i> + <span class="text"> + Mein Profil bearbeiten + </span> + </a> + </li> + <li> + <a href="{{ h.url_for('/user/_logout') }}" title="logout" role="menuitem"> + <i class='fa fa-sign-out'></i> + <span class="text">Logout</span> + </a> + </li> + </ul> + </a> + </li> + {% else %} + <li class='{{clazz}}'> + <a href="{{ h.url_for('/user/login') }}" title="login"> + <span class="text">Login</span> + </a> + </li> + {% endif %} </ul> - </a> - </li> - {% else %} - <li class='{{clazz}}'> - <a href="{{ h.url_for('/user/login') }}" title="login"> - <span class="text">Login</span> - </a> - </li> - {% endif %} - </ul> - </nav> - <nav class="section navigation"> - <ul class="nav nav-pills"> - </ul> - </nav> - {% endblock %} - </div> + </nav> + <nav class="section navigation"> + <ul class="nav nav-pills"> + </ul> + </nav> + {% endblock %} + </div> </div> - </div> - </div> </header> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/ckanext/odsh/templates/macros/form.html b/ckanext/odsh/templates/macros/form.html index cc01867dab57de4dc4fd41785ab9df68579b6791..862c69eee75b6e20effeb981da4e4287fec103c3 100644 --- a/ckanext/odsh/templates/macros/form.html +++ b/ckanext/odsh/templates/macros/form.html @@ -95,6 +95,21 @@ is_required=false) %} </div> {% endmacro %} + +{% macro select(name, id='', label='', options='', selected='', error='', classes=[], attrs={'class': 'form-control'}, is_required=false) %} +{% set classes = (classes|list) %} +{% do classes.append('control-select') %} + +{%- set extra_html = caller() if caller -%} +{% call input_block(id or name, label or name, error, classes, extra_html=extra_html, is_required=is_required) %} +<select id="{{ id or name }}" name="{{ name }}" {{ attributes(attrs) }}> + {% for option in options %} +<option value="{{ option.value }}"{% if option.value == selected %} selected{% endif %}>{{ option.text or option.value }}</option> +{% endfor %} +</select> +{% endcall %} +{% endmacro %} + {# Creates all the markup required for an select element. Handles matching labels to inputs and error messages. @@ -107,401 +122,461 @@ name - The name of the form parameter. id - The id to use on the input and label. Convention is to prefix with 'field-'. label - The human readable label. options - A list/tuple of fields to be used as <options>. - selected - The value of the selected <option>. - error - A list of error strings for the field or just true to highlight the field. - classes - An array of classes to apply to the control-group. - is_required - Boolean of whether this input is requred for the form to validate - - Examples: - - {% import 'macros/form.html' as form %} - {{ form.select('year', label=_('Year'), options=[{'name':2010, 'value': 2010},{'name': 2011, 'value': 2011}], - selected=2011, error=errors.year) }} - - #} - {% macro select(name, id='', label='', options='', selected='', error='', classes=[], attrs={}, - is_required=false) %} - {% set classes = (classes|list) %} - {% do classes.append('control-select') %} - - {%- set extra_html = caller() if caller -%} - {% call input_block(id or name, label or name, error, classes, extra_html=extra_html, is_required=is_required) - %} +selected - The value of the selected <option>. +error - A list of error strings for the field or just true to highlight the field. +classes - An array of classes to apply to the control-group. +is_required - Boolean of whether this input is requred for the form to validate + +Examples: + +{% import 'macros/form.html' as form %} +{{ form.select('year', label=_('Year'), options=[{'name':2010, 'value': 2010},{'name': 2011, 'value': 2011}], +selected=2011, error=errors.year) }} + +#} +{% macro tpsh_select(name, id='', label='', options='', selected='', error='', classes=[], attrs={}, +is_required=false) %} +{% set classes = (classes|list) %} +{% do classes.append('control-select') %} + +{%- set extra_html = caller() if caller -%} +{% call input_block(id or name, label or name, error, classes, extra_html=extra_html, is_required=is_required) +%} +<div class="row-fluid"> + <div class="span6"> <select id="{{ id or name }}" name="{{ name }}" {{ attributes(attrs) }}> {% for option in options %} - <option value="{{ option.value }}" {% if option.value==selected %} selected{% endif %}>{{ option.text or + <option value="{{ option.key }}" {% if option.key==selected %} selected{% endif %}>{{ option.text or option.value }}</option> {% endfor %} </select> - {% endcall %} - {% endmacro %} - - {# - Creates all the markup required for a Markdown textarea element. Handles - matching labels to inputs, selected item and error messages. - - name - The name of the form parameter. - id - The id to use on the input and label. Convention is to prefix with 'field-'. - label - The human readable label. - value - The value of the input. - placeholder - Some placeholder text. - error - A list of error strings for the field or just true to highlight the field. - classes - An array of classes to apply to the control-group. - is_required - Boolean of whether this input is requred for the form to validate - - Examples: - - {% import 'macros/form.html' as form %} - {{ form.markdown('desc', id='field-description', label=_('Description'), value=data.desc, error=errors.desc) }} - - #} - {% macro markdown(name, id='', label='', value='', placeholder='', error="", classes=[], attrs={}, - is_required=false, cols=20, rows=5) %} - {% set classes = (classes|list) %} - {% do classes.append('control-full') %} - {% set markdown_tooltip = " - <pre><p>__Bold text__ or _italic text_</p><p># title<br>## secondary title<br>### etc</p><p>* list<br>* of<br>* items</p><p>http://auto.link.ed/</p></pre> - <p><b><a href='http://daringfireball.net/projects/markdown/syntax' target='_blank'>Full markdown syntax</a></b></p> - <p class='muted'><b>Please note:</b> HTML tags are stripped out for security reasons</p>" %} - - {%- set extra_html = caller() if caller -%} - {% call input_block(id or name, label or name, error, classes, control_classes=["editor"], - extra_html=extra_html, is_required=is_required) %} - <div class="row-fluid"> - <div class="span6"> - <textarea id="{{ id or name }}" name="{{ name }}" cols="{{cols}}" rows="{{rows}}" placeholder="{{ placeholder }}" - {{ attributes(attrs) }}>{{ value | empty_and_escape }}</textarea> - </div> - <div class="span6 inline-error"> - {{error}} - </div> - </div> - {% endcall %} - {% endmacro %} - - {# - Creates all the markup required for a plain textarea element. Handles - matching labels to inputs, selected item and error messages. - - name - The name of the form parameter. - id - The id to use on the input and label. Convention is to prefix with 'field-'. - label - The human readable label. - value - The value of the input. - placeholder - Some placeholder text. - error - A list of error strings for the field or just true to highlight the field. - classes - An array of classes to apply to the control-group. - is_required - Boolean of whether this input is requred for the form to validate - - Examples: - - {% import 'macros/form.html' as form %} - {{ form.textarea('desc', id='field-description', label=_('Description'), value=data.desc, error=errors.desc) }} - - #} - {% macro textarea(name, id='', label='', value='', placeholder='', error="", classes=[], attrs={}, - is_required=false, rows=5, cols=20) %} - {% set classes = (classes|list) %} - {% do classes.append('control-full') %} - - {%- set extra_html = caller() if caller -%} - {% call input_block(id or name, label or name, error, classes, extra_html=extra_html, is_required=is_required) - %} - <textarea id="{{ id or name }}" name="{{ name }}" cols="{{ cols }}" rows="{{ rows }}" placeholder="{{ placeholder }}" - {{ attributes(attrs) }}>{{ value | empty_and_escape }}</textarea> - {% endcall %} - {% endmacro %} - - {# - Creates all the markup required for an input element with a prefixed segment. - These are useful for showing url slugs and other fields where the input - information forms only part of the saved data. - - name - The name of the form parameter. - id - The id to use on the input and label. Convention is to prefix with 'field-'. - label - The human readable label. - prepend - The text that will be prepended before the input. - value - The value of the input. - which will use the name key as the value. - placeholder - Some placeholder text. - error - A list of error strings for the field or just true to highlight the field. - classes - An array of classes to apply to the control-group. - is_required - Boolean of whether this input is requred for the form to validate - - Examples: - - {% import 'macros/form.html' as form %} - {{ form.prepend('slug', id='field-slug', prepend='/dataset/', label=_('Slug'), value=data.slug, - error=errors.slug) }} - - #} - {% macro prepend(name, id='', label='', prepend='', value='', placeholder='', type='text', error="", - classes=[], attrs={}, is_required=false) %} - {# We manually append the error here as it needs to be inside the .input-prepend block #} - {% set classes = (classes|list) %} - {% do classes.append('error') if error %} - {%- set extra_html = caller() if caller -%} - {% call input_block(id or name, label or name, error='', classes=classes, extra_html=extra_html, - is_required=is_required) %} - <div class="input-prepend"> - {% if prepend %}<span class="add-on">{{ prepend }}</span>{%- endif -%} - <input id="{{ id or name }}" type="{{ type }}" name="{{ name }}" value="{{ value | empty_and_escape }}" - placeholder="{{ placeholder }}" {{ attributes(attrs) }} /> - </div> - {% endcall %} - {% endmacro %} - - {# - Creates all the markup required for an custom key/value input. These are usually - used to let the user provide custom meta data. Each "field" has three inputs - one for the key, one for the value and a checkbox to remove it. So the arguments - for this macro are nearly all tuples containing values for the - (key, value, delete) fields respectively. - - name - A tuple of names for the three fields. - id - An id string to be used for each input. - label - The human readable label for the main label. - values - A tuple of values for the (key, value, delete) fields. If delete - is truthy the checkbox will be checked. - placeholder - A tuple of placeholder text for the (key, value) fields. - error - A list of error strings for the field or just true to highlight the field. - classes - An array of classes to apply to the control-group. - is_required - Boolean of whether this input is requred for the form to validate - - Examples: - - {% import 'macros/form.html' as form %} - {{ form.custom( - names=('custom_key', 'custom_value', 'custom_deleted'), - id='field-custom', - label=_('Custom Field'), - values=(extra.key, extra.value, extra.deleted), - error='' - ) }} - #} - {% macro custom(names=(), id="", label="", values=(), placeholders=(), error="", classes=[], attrs={}, - is_required=false, key_values=()) %} - {%- set classes = (classes|list) -%} - {%- set label_id = (id or names[0]) ~ "-key" -%} - {%- set extra_html = caller() if caller -%} - {%- do classes.append('control-custom') -%} - - {% call input_block(label_id, label or name, error, classes, control_classes=["editor"], extra_html=extra_html, - is_required=is_required) %} - <div class="input-prepend" {{ attributes(attrs) }}> - <label for="{{ label_id }}" class="add-on">Key</label><input id="{{ id or names[0] }}-key" type="text" name="{{ names[0] }}" - value="{{ values[0] | empty_and_escape }}" placeholder="{{ placeholders[0] }}" /> - <label for="{{ id or names[1] }}-value" class="add-on">Value</label><input id="{{ id or names[1] }}-value" - type="text" name="{{ names[1] }}" value="{{ values[1] | empty_and_escape }}" placeholder="{{ placeholders[1] }}" /> - {% if values[0] or values[1] or error %} - <label class="checkbox" for="{{ id or names[2] }}-remove"> - <input type="checkbox" id="{{ id or names[2] }}-remove" name="{{ names[2] }}" {% if values[2] %} - checked{% endif %} /> <span>{{ _('Remove') }}</span> - </label> - {% endif %} - </div> - {% endcall %} - {% endmacro %} - - {# - A generic input_block for providing the default markup for CKAN form elements. - It is expected to be called using a {% call %} block, the contents of which - will be inserted into the .controls element. - - for - The id for the input that the label should match. - label - A human readable label. - error - A list of error strings for the field or just true. - classes - An array of custom classes for the outer element. - control_classes - An array of custom classes for the .control wrapper. - extra_html - An html string to be inserted after the errors eg. info text. - is_required - Boolean of whether this input is requred for the form to validate - - Example: - - {% import 'macros/form.html' as form %} - {% call form.input_block("field", "My Field") %} - <input id="field" type="text" name="{{ name }}" value="{{ value | empty_and_escape }}" /> - {% endcall %} - - #} - {% macro input_block(for, label, error="", classes=[], control_classes=[], extra_html="", is_required=false) - %} - <div class="control-group{{ " error" if error }}{{ " " ~ classes | join(' ') }}"> - {%if label%} - <label class="control-label" for="{{ for }}">{{ label or _('Custom') }}: {% if is_required %}<span title="{{ _("This field is required") }}" - class="control-required">*</span> {% endif %}</label> - {%endif%} - <div class="controls{{ " " ~ control_classes | join(' ') }}"> - {{ caller() }} - {{ extra_html }} - </div> - </div> - {% endmacro %} - - {# - Builds a list of errors for the current form. - - errors - A dict of field/message pairs. - type - The alert-* class that should be applied (default: "error") - classes - A list of classes to apply to the wrapper (default: []) - - Example: - - {% import 'macros/form.html' as form %} - {{ form.errors(error_summary, type="warning") }} - - #} - {% macro errors(errors={}, type="error", classes=[]) %} - {% if errors %} - <div class="error-explanation alert alert-{{ type }}{{ " " ~ classes | join(' ') }}"> - <ul> - {% for key, error in errors.items() %} - {%if key %} - <li data-field-label="{{ key }}"> - {{_(key+': '+error)}} - </li> - {%else%} - <li data-field-label="{{ key }}"> {{error}} </li> - {%endif%} - {% endfor %} - </ul> - </div> + </div> + <div class="span6 inline-error"> + {% if error is iterable and error is not string %} + {{error|first}} + {% elif error is string %} + {{error}} {% endif %} - {% endmacro %} + </div> +</div> +{% endcall %} +{% endmacro %} - {# - Renders an info box with a description. This will usually be used with in a - call block when creating an input element. - text - The text to include in the box. - inline - If true displays the info box inline with the input. - classes - A list of classes to add to the info box. +{# +Creates a select with an input field for autocomplete #} +{% macro select_autocomplete(name, id='', label='', options='', selected='', error='', classes=[], attrs={}, + is_required=false) %} +{% set classes = (classes|list) %} +{% do classes.append('control-select') %} - Example +{%- set extra_html = caller() if caller -%} +{% call input_block(id or name, label or name, error, classes, extra_html=extra_html, is_required=is_required) +%} - {% import 'macros/form.html' as form %} - {% call form.input('name') %} - {{ form.info(_('My useful help text')) }} - {% endcall %} +<div class="row-fluid"> + <div class="span6"> + <select id="{{ id or name }}" name="{{ name }}" {{ attributes(attrs) }} data-module="autocomplete"> + {% for option in options %} + <option value="{{ option.key }}" {% if option.key==selected %} selected {% endif %}>{{ option.text or + option.value }} </option> + {% endfor %} + </select> + </div> + <div class="span6 inline-error"> + {% if error is iterable and error is not string %} + {{error|first}} + {% elif error is string %} + {{error}} + {% endif %} + </div> +</div> +{% endcall %} +{% endmacro %} - #} - {% macro info(text='', inline=false, classes=[]) %} - {%- if text -%} - <div class="info-block{{ ' info-inline' if inline }}{{ " " ~ classes | join(' ') }}"> - <i class="fa fa-info-circle"></i> - {{ text }} - </div> - {%- endif -%} - {% endmacro %} - {# - Builds a single hidden input. +{# +Creates all the markup required for a Markdown textarea element. Handles +matching labels to inputs, selected item and error messages. - name - name of the hidden input - value - value of the hidden input +name - The name of the form parameter. +id - The id to use on the input and label. Convention is to prefix with 'field-'. +label - The human readable label. +value - The value of the input. +placeholder - Some placeholder text. +error - A list of error strings for the field or just true to highlight the field. +classes - An array of classes to apply to the control-group. +is_required - Boolean of whether this input is requred for the form to validate - Example - {% import 'macros/form.html' as form %} - {{ form.hidden('name', 'value') }} +Examples: - #} - {% macro hidden(name, value) %} - <input type="hidden" name="{{ name }}" value="{{ value }}" /> - {% endmacro %} +{% import 'macros/form.html' as form %} +{{ form.markdown('desc', id='field-description', label=_('Description'), value=data.desc, error=errors.desc) }} - {# - Contructs hidden inputs for each name-value pair. +#} +{% macro markdown(name, id='', label='', value='', placeholder='', error="", classes=[], attrs={}, +is_required=false, cols=20, rows=5) %} +{% set classes = (classes|list) %} +{% do classes.append('control-full') %} +{% set markdown_tooltip = " +<pre><p>__Bold text__ or _italic text_</p><p># title<br>## secondary title<br>### etc</p><p>* list<br>* of<br>* items</p><p>http://auto.link.ed/</p></pre> +<p><b><a href='http://daringfireball.net/projects/markdown/syntax' target='_blank'>Full markdown syntax</a></b></p> +<p class='muted'><b>Please note:</b> HTML tags are stripped out for security reasons</p>" %} - fields - [('name1', 'value1'), ('name2', 'value2'), ...] +{%- set extra_html = caller() if caller -%} +{% call input_block(id or name, label or name, error, classes, control_classes=["editor"], +extra_html=extra_html, is_required=is_required) %} +<div class="row-fluid"> + <div class="span6"> + <textarea id="{{ id or name }}" name="{{ name }}" cols="{{cols}}" rows="{{rows}}" placeholder="{{ placeholder }}" + {{ attributes(attrs) }}>{{ value | empty_and_escape }}</textarea> + </div> + <div class="span6 inline-error"> + {{error}} + </div> +</div> +{% endcall %} +{% endmacro %} - Two parameter for excluding several names or name-value pairs. +{# +Creates all the markup required for a plain textarea element. Handles +matching labels to inputs, selected item and error messages. - except_names - list of names to be excluded - except - list of name-value pairs to be excluded +name - The name of the form parameter. +id - The id to use on the input and label. Convention is to prefix with 'field-'. +label - The human readable label. +value - The value of the input. +placeholder - Some placeholder text. +error - A list of error strings for the field or just true to highlight the field. +classes - An array of classes to apply to the control-group. +is_required - Boolean of whether this input is requred for the form to validate +Examples: - Example: - {% import 'macros/form.html' as form %} - {% form.hidden_from_list(fields=c.fields, except=[('topic', 'xyz')]) %} - {% form.hidden_from_list(fields=c.fields, except_names=['time_min', 'time_max']) %} - #} - {% macro hidden_from_list(fields, except_names=None, except=None) %} - {% set except_names = except_names or [] %} - {% set except = except or [] %} +{% import 'macros/form.html' as form %} +{{ form.textarea('desc', id='field-description', label=_('Description'), value=data.desc, error=errors.desc) }} - {% for name, value in fields %} - {% if name and value and name not in except_names and (name, value) not in except %} - {{ hidden(name, value) }} - {% endif %} +#} +{% macro textarea(name, id='', label='', value='', placeholder='', error="", classes=[], attrs={}, +is_required=false, rows=5, cols=20) %} +{% set classes = (classes|list) %} +{% do classes.append('control-full') %} + +{%- set extra_html = caller() if caller -%} +{% call input_block(id or name, label or name, error, classes, extra_html=extra_html, is_required=is_required) +%} +<textarea id="{{ id or name }}" name="{{ name }}" cols="{{ cols }}" rows="{{ rows }}" placeholder="{{ placeholder }}" + {{ attributes(attrs) }}>{{ value | empty_and_escape }}</textarea> +{% endcall %} +{% endmacro %} + +{# +Creates all the markup required for an input element with a prefixed segment. +These are useful for showing url slugs and other fields where the input +information forms only part of the saved data. + +name - The name of the form parameter. +id - The id to use on the input and label. Convention is to prefix with 'field-'. +label - The human readable label. +prepend - The text that will be prepended before the input. +value - The value of the input. +which will use the name key as the value. +placeholder - Some placeholder text. +error - A list of error strings for the field or just true to highlight the field. +classes - An array of classes to apply to the control-group. +is_required - Boolean of whether this input is requred for the form to validate + +Examples: + +{% import 'macros/form.html' as form %} +{{ form.prepend('slug', id='field-slug', prepend='/dataset/', label=_('Slug'), value=data.slug, +error=errors.slug) }} + +#} +{% macro prepend(name, id='', label='', prepend='', value='', placeholder='', type='text', error="", +classes=[], attrs={}, is_required=false) %} +{# We manually append the error here as it needs to be inside the .input-prepend block #} +{% set classes = (classes|list) %} +{% do classes.append('error') if error %} +{%- set extra_html = caller() if caller -%} +{% call input_block(id or name, label or name, error='', classes=classes, extra_html=extra_html, +is_required=is_required) %} +<div class="input-prepend"> + {% if prepend %}<span class="add-on">{{ prepend }}</span>{%- endif -%} + <input id="{{ id or name }}" type="{{ type }}" name="{{ name }}" value="{{ value | empty_and_escape }}" + placeholder="{{ placeholder }}" {{ attributes(attrs) }} /> +</div> +{% endcall %} +{% endmacro %} + +{# +Creates all the markup required for an custom key/value input. These are usually +used to let the user provide custom meta data. Each "field" has three inputs +one for the key, one for the value and a checkbox to remove it. So the arguments +for this macro are nearly all tuples containing values for the +(key, value, delete) fields respectively. + +name - A tuple of names for the three fields. +id - An id string to be used for each input. +label - The human readable label for the main label. +values - A tuple of values for the (key, value, delete) fields. If delete +is truthy the checkbox will be checked. +placeholder - A tuple of placeholder text for the (key, value) fields. +error - A list of error strings for the field or just true to highlight the field. +classes - An array of classes to apply to the control-group. +is_required - Boolean of whether this input is requred for the form to validate + +Examples: + +{% import 'macros/form.html' as form %} +{{ form.custom( +names=('custom_key', 'custom_value', 'custom_deleted'), +id='field-custom', +label=_('Custom Field'), +values=(extra.key, extra.value, extra.deleted), +error='' +) }} +#} +{% macro custom(names=(), id="", label="", values=(), placeholders=(), error="", classes=[], attrs={}, +is_required=false, key_values=()) %} +{%- set classes = (classes|list) -%} +{%- set label_id = (id or names[0]) ~ "-key" -%} +{%- set extra_html = caller() if caller -%} +{%- do classes.append('control-custom') -%} + +{% call input_block(label_id, label or name, error, classes, control_classes=["editor"], extra_html=extra_html, +is_required=is_required) %} +<div class="input-prepend" {{ attributes(attrs) }}> + <label for="{{ label_id }}" class="add-on">Key</label><input id="{{ id or names[0] }}-key" type="text" name="{{ names[0] }}" + value="{{ values[0] | empty_and_escape }}" placeholder="{{ placeholders[0] }}" /> + <label for="{{ id or names[1] }}-value" class="add-on">Value</label><input id="{{ id or names[1] }}-value" + type="text" name="{{ names[1] }}" value="{{ values[1] | empty_and_escape }}" placeholder="{{ placeholders[1] }}" /> + {% if values[0] or values[1] or error %} + <label class="checkbox" for="{{ id or names[2] }}-remove"> + <input type="checkbox" id="{{ id or names[2] }}-remove" name="{{ names[2] }}" {% if values[2] %} + checked{% endif %} /> <span>{{ _('Remove') }}</span> + </label> + {% endif %} +</div> +{% endcall %} +{% endmacro %} + + + +{% macro input_address(field, label, value='', index='', placeholder='', type='text', attrs={}) %} +{%- set _type = 'text' if type=='date' and not value else type -%} +{%- set onFocus = 'onfocus=(this.type=\'date\')' if type=='date' and not value else '' -%} +<div class="row-fluid"> + <div class="span6"> + <label class="address-label"> {{ label }} </label> + <input id="field-{{field}}-key" type="hidden" name="extras__{{index}}__key" value="{{field}}" /> + <input id="field-{{field}}-value" type="{{_type}}" name="extras__{{index}}__value" value="{{value | empty_and_escape }}" placeholder="{{ placeholder }}" {{ onFocus }} {{ attributes(attrs) }}/> +</div> +</div> +{% endmacro %} + + + +{# +A generic input_block for providing the default markup for CKAN form elements. +It is expected to be called using a {% call %} block, the contents of which +will be inserted into the .controls element. + +for - The id for the input that the label should match. +label - A human readable label. +error - A list of error strings for the field or just true. +classes - An array of custom classes for the outer element. +control_classes - An array of custom classes for the .control wrapper. +extra_html - An html string to be inserted after the errors eg. info text. +is_required - Boolean of whether this input is requred for the form to validate + +Example: + +{% import 'macros/form.html' as form %} +{% call form.input_block("field", "My Field") %} +<input id="field" type="text" name="{{ name }}" value="{{ value | empty_and_escape }}" /> +{% endcall %} + +#} +{% macro input_block(for, label, error="", classes=[], control_classes=[], extra_html="", is_required=false) +%} +<div class="control-group{{ " error" if error }}{{ " " ~ classes | join(' ') }}"> + {%if label%} + <label class="control-label" for="{{ for }}">{{ label or _('Custom') }}: {% if is_required %}<span title="{{ _("This field is required") }}" + class="control-required">*</span> {% endif %}</label> + {%endif%} + <div class="controls{{ " " ~ control_classes | join(' ') }}"> + {{ caller() }} + {{ extra_html }} + </div> +</div> +{% endmacro %} + +{# +Builds a list of errors for the current form. + +errors - A dict of field/message pairs. +type - The alert-* class that should be applied (default: "error") +classes - A list of classes to apply to the wrapper (default: []) + +Example: + +{% import 'macros/form.html' as form %} +{{ form.errors(error_summary, type="warning") }} + +#} +{% macro errors(errors={}, type="error", classes=[]) %} +{% if errors %} +<div class="error-explanation alert alert-{{ type }}{{ " " ~ classes | join(' ') }}"> + <ul> + {% for key, error in errors.items() %} + {%if key %} + <li data-field-label="{{ key }}"> + {{_(key+': '+error)}} + </li> + {%else%} + <li data-field-label="{{ key }}"> {{error}} </li> + {%endif%} {% endfor %} - {% endmacro %} - - {# - Builds a space seperated list of html attributes from a dict of key/value pairs. - Generally only used internally by macros. - - attrs - A dict of attribute/value pairs - - Example - - {% import 'macros/form.html' as form %} - {{ form.attributes({}) }} - - #} - {%- macro attributes(attrs={}) -%} - {%- for key, value in attrs.items() -%} - {{ " " }}{{ key }}{% if value != "" %}="{{ value }}"{% endif %} - {%- endfor -%} - {%- endmacro -%} - - {# - Outputs the "* Required field" message for the bottom of formss - - Example - {% import 'macros/form.html' as form %} - {{ form.required_message() }} - - #} - {% macro required_message() %} - <p class="control-required-message"> - <span class="control-required">*</span> {{ _("Required field") }} - </p> - {% endmacro %} - - {# - Builds a file upload for input - - Example - {% import 'macros/form.html' as form %} - {{ form.image_upload(data, errors, is_upload_enabled=true) }} - - #} - {% macro image_upload(data, errors, field_url='image_url', field_upload='image_upload', - field_clear='clear_upload', - is_url=false, is_upload=false, is_upload_enabled=false, placeholder=false, - url_label='', upload_label='', field_name='image_url') %} - {% set placeholder = placeholder if placeholder else _('http://example.com/my-image.jpg') %} - {% set url_label = url_label or _('Image URL') %} - {% set upload_label = upload_label or _('Image') %} - - {% if is_upload_enabled %} - <div class="image-upload" data-module="odsh_image-upload" data-module-is_url="{{ 'true' if is_url else 'false' }}" - data-module-is_upload="{{ 'true' if is_upload else 'false' }}" data-module-field_url="{{ field_url }}" - data-module-field_upload="{{ field_upload }}" data-module-field_clear="{{ field_clear }}" - data-module-upload_label="{{ upload_label }}" data-module-field_name="{{ field_name }}"> - {% endif %} - - {{ input(field_url, label=url_label, id='field-image-url', placeholder=placeholder, - value=data.get(field_url), error='', classes=['control-full']) }} - - {% if is_upload_enabled %} - {{ input(field_upload, label=upload_label, id='field-image-upload', type='file', placeholder='', value='', - error=errors, classes=['control-full'], is_required=true) }} - {% if is_upload %} - {{ checkbox(field_clear, label=_('Clear Upload'), id='field-clear-upload', value='true', error='', - classes=['control-full']) }} - {% endif %} - {% endif %} - - {% if is_upload_enabled %}</div>{% endif %} - - {% endmacro %} + </ul> +</div> +{% endif %} +{% endmacro %} + +{# +Renders an info box with a description. This will usually be used with in a +call block when creating an input element. + +text - The text to include in the box. +inline - If true displays the info box inline with the input. +classes - A list of classes to add to the info box. + +Example + +{% import 'macros/form.html' as form %} +{% call form.input('name') %} +{{ form.info(_('My useful help text')) }} +{% endcall %} + +#} +{% macro info(text='', inline=false, classes=[]) %} +{%- if text -%} +<div class="info-block{{ ' info-inline' if inline }}{{ " " ~ classes | join(' ') }}"> + <i class="fa fa-info-circle"></i> + {{ text }} +</div> +{%- endif -%} +{% endmacro %} + +{# +Builds a single hidden input. + +name - name of the hidden input +value - value of the hidden input + +Example +{% import 'macros/form.html' as form %} +{{ form.hidden('name', 'value') }} + +#} +{% macro hidden(name, value) %} +<input type="hidden" name="{{ name }}" value="{{ value }}" /> +{% endmacro %} + +{# +Contructs hidden inputs for each name-value pair. + +fields - [('name1', 'value1'), ('name2', 'value2'), ...] + +Two parameter for excluding several names or name-value pairs. + +except_names - list of names to be excluded +except - list of name-value pairs to be excluded + + +Example: +{% import 'macros/form.html' as form %} +{% form.hidden_from_list(fields=c.fields, except=[('topic', 'xyz')]) %} +{% form.hidden_from_list(fields=c.fields, except_names=['time_min', 'time_max']) %} +#} +{% macro hidden_from_list(fields, except_names=None, except=None) %} +{% set except_names = except_names or [] %} +{% set except = except or [] %} + +{% for name, value in fields %} +{% if name and value and name not in except_names and (name, value) not in except %} +{{ hidden(name, value) }} +{% endif %} +{% endfor %} +{% endmacro %} + +{# +Builds a space seperated list of html attributes from a dict of key/value pairs. +Generally only used internally by macros. + +attrs - A dict of attribute/value pairs + +Example + +{% import 'macros/form.html' as form %} +{{ form.attributes({}) }} + +#} +{%- macro attributes(attrs={}) -%} +{%- for key, value in attrs.items() -%} +{{ " " }}{{ key }}{% if value != "" %}="{{ value }}"{% endif %} +{%- endfor -%} +{%- endmacro -%} + +{# +Outputs the "* Required field" message for the bottom of formss + +Example +{% import 'macros/form.html' as form %} +{{ form.required_message() }} + +#} +{% macro required_message() %} +<p class="control-required-message"> + <span class="control-required">*</span> {{ _("Required field") }} +</p> +{% endmacro %} + +{# +Builds a file upload for input + +Example +{% import 'macros/form.html' as form %} +{{ form.image_upload(data, errors, is_upload_enabled=true) }} + +#} +{% macro image_upload(data, errors, field_url='image_url', field_upload='image_upload', +field_clear='clear_upload', +is_url=false, is_upload=false, is_upload_enabled=false, placeholder=false, +url_label='', upload_label='', field_name='image_url') %} +{% set placeholder = placeholder if placeholder else _('http://example.com/my-image.jpg') %} +{% set url_label = url_label or _('Image URL') %} +{% set upload_label = upload_label or _('Image') %} + +{% if is_upload_enabled %} +<div class="image-upload" data-module="odsh_image-upload" data-module-is_url="{{ 'true' if is_url else 'false' }}" + data-module-is_upload="{{ 'true' if is_upload else 'false' }}" data-module-field_url="{{ field_url }}" + data-module-field_upload="{{ field_upload }}" data-module-field_clear="{{ field_clear }}" + data-module-upload_label="{{ upload_label }}" data-module-field_name="{{ field_name }}"> + {% endif %} + + {{ input(field_url, label=url_label, id='field-image-url', placeholder=placeholder, + value=data.get(field_url), error='', classes=['control-full']) }} + + {% if is_upload_enabled %} + {{ input(field_upload, label=upload_label, id='field-image-upload', type='file', placeholder='', value='', + error=errors, classes=['control-full'], is_required=true) }} + {% if is_upload %} + {{ checkbox(field_clear, label=_('Clear Upload'), id='field-clear-upload', value='true', error='', + classes=['control-full']) }} + {% endif %} + {% endif %} + + {% if is_upload_enabled %}</div>{% endif %} + +{% endmacro %} diff --git a/ckanext/odsh/templates/organization/snippets/organization_form.html b/ckanext/odsh/templates/organization/snippets/organization_form.html index 4028d04852a5a92661fad71deaa3ed069504c2fd..364433fb5a1841e363507669c15fecdaf52598dc 100644 --- a/ckanext/odsh/templates/organization/snippets/organization_form.html +++ b/ckanext/odsh/templates/organization/snippets/organization_form.html @@ -25,32 +25,23 @@ {{ form.image_upload(data, errors, is_upload_enabled=h.uploads_enabled(), is_url=is_url, is_upload=is_upload) }} {% endblock %} - - {% block custom_fields %} - {% for extra in data.extras %} - {% set prefix = 'extras__%d__' % loop.index0 %} - {{ form.custom( - names=(prefix ~ 'key', prefix ~ 'value', prefix ~ 'deleted'), - id='field-extras-%d' % loop.index, - label=_('Custom Field'), - values=(extra.key, extra.value, extra.deleted), - error=errors[prefix ~ 'key'] or errors[prefix ~ 'value'] - ) }} - {% endfor %} - - {# Add a max if 3 empty columns #} - {% for extra in range(data.extras|count, 3) %} - {% set index = (loop.index0 + data.extras|count) %} - {% set prefix = 'extras__%d__' % index %} - {{ form.custom( - names=(prefix ~ 'key', prefix ~ 'value', prefix ~ 'deleted'), - id='field-extras-%d' % index, - label=_('Custom Field'), - values=(extra.key, extra.value, extra.deleted), - error=errors[prefix ~ 'key'] or errors[prefix ~ 'value'] - ) }} - {% endfor %} - {% endblock %} + + + + + {% set extras = h.tpsh_get_address_org(data) %} + + {{ form.input_address('person','Ansprechpartner', value= extras.person , index=0, placeholder='', type='text', attrs={}) }} + + {{ form.input_address('street','Straße', value= extras.street, index=1, placeholder='', type='text', attrs={}) }} + + {{ form.input_address('location','Stadt', value=extras.location, index=2, placeholder='', type='text', attrs={}) }} + + {{ form.input_address('telephone','Tel.-Nr.:', value=extras.telephone, index=3, placeholder='', type='text', attrs={}) }} + + {{ form.input_address('mail','e-mail', value=extras.mail, index=4, placeholder='', type='text', attrs={}) }} + + {{ form.input_address('web','Website', value=extras.web, index=5, placeholder='', type='text', attrs={}) }} {{ form.required_message() }} diff --git a/ckanext/odsh/templates/package/read.html b/ckanext/odsh/templates/package/read.html index 097f993db901aef9d5a1aaac8dd66a5a240c4d83..068b60870df00513f3c63b30f848ad3724c0b7f6 100644 --- a/ckanext/odsh/templates/package/read.html +++ b/ckanext/odsh/templates/package/read.html @@ -1,8 +1,10 @@ {% 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 stars = h.odsh_openness_score_dataset_html(pkg) %} {% block breadcrumb_content %} @@ -25,7 +27,7 @@ {% block primary_content_inner %} {{ super() }} {% block package_description %} -<div class="odsh-dataset-heading row-fluid"> +<div class="odsh-dataset-heading"> <h2> {% block page_heading %} {{ h.dataset_display_name(pkg) }} @@ -36,7 +38,7 @@ [{{ _('Deleted') }}] {% endif %} {% endblock %} - <div class="pull-right"> + <div class="odsh-dataset-edit-button"> {% if h.check_access('package_update', {'id':pkg.id }) %} <div> {% link_for _('Manage Dataset'), controller='package', action='edit', id=pkg.name, class_='btn btn-primary btn-add-dataset', @@ -60,16 +62,19 @@ {% endif %} </div> {# {{ pkg.resources }} #} + {% block package_notes %} +<div class="btn btn-primary btn-lg show-filters" role="button"> + <img src="/base/images/icon_info.svg" aria-hidden="true"></img> + {{ _('Detailinformationen') }} +</div> {% if pkg.notes %} <div class="notes embedded-content"> {{ h.render_markdown(h.get_translated(pkg, 'notes')) }} </div> {% endif %} -{% endblock %} -{# FIXME why is this here? seems wrong #} -<span class="insert-comment-thread"></span> -{% endblock %} +{% endblock package_notes %} +{% endblock package_description %} {% block package_resources %} {% snippet "package/snippets/resources_list.html", pkg=pkg, resources=pkg.resources %} @@ -80,6 +85,48 @@ {% endif %} {% endblock %} +{% block collection %} +{% if latest_collection_member %} +<section id="dataset-collection"> + {% if collection_title %} + <p> + Dieser Datensatz ist Bestandteil der Datenreihe "{{ collection_title }}". + Sie können zu älteren und neueren Datensätzen blättern. + </p> + {% endif %} {# collection_title #} + <div class="button-container"> + <a href="{{ latest_collection_member }}" class="btn btn-primary btn-lg" role="button"> + <img src="/base/images/icon_latest.svg" aria-hidden="true"></img> + <span class="hide-on-mobile">{{ _('latest collection member') }}</span> + </a> + {% if predecessor_url %} + <a href="{{ predecessor_url }}" class="btn btn-primary btn-lg" role="button"> + <img src="/base/images/icon_arrow_left.svg" aria-hidden="true"></img> + <span class="hide-on-mobile">{{ _('predecessor') }}</span> + </a> + {% else %} + <a href="#" class="btn btn-primary btn-lg disabled" role="button"> + <img src="/base/images/icon_arrow_left.svg" aria-hidden="true"></img> + <span class="hide-on-mobile">{{ _('predecessor') }}</span> + </a> + {% endif %} + {% if successor_url %} + <a href="{{ successor_url }}" class="btn btn-primary btn-lg" role="button"> + <span class="hide-on-mobile">{{ _('successor') }}</span> + <img src="/base/images/icon_arrow_right.svg" aria-hidden="true"></img> + </a> + {% else %} + <a href="#" class="btn btn-primary btn-lg disabled" role="button"> + <span class="hide-on-mobile">{{ _('successor') }}</span> + <img src="/base/images/icon_arrow_right.svg" aria-hidden="true"></img> + </a> + {% endif %} + </div> +</section> +{% endif %} {# latest_collection_member #} +{% endblock collection %} + + <div class='tag-container'> {% for tag in pkg.tags %} <div class='tag-pill'> @@ -88,4 +135,5 @@ {% endfor %} </div> -{% endblock %} \ No newline at end of file +{% endblock %} + diff --git a/ckanext/odsh/templates/package/read_base.html b/ckanext/odsh/templates/package/read_base.html index 9597da673921613a4a8c9f15e2c7269c33e2943b..99cb65fdda0f56337edb14a2135016b73955fbd5 100644 --- a/ckanext/odsh/templates/package/read_base.html +++ b/ckanext/odsh/templates/package/read_base.html @@ -9,9 +9,10 @@ {% block secondary_content %} {% block package_organization %} +{% snippet "package/snippets/close_mobile_sidebar_button.html" %} {% if pkg.organization %} {% set org = h.get_organization(pkg.organization.name) %} -{% snippet "snippets/organization.html", organization=org, has_context_title=true, hide_description=true %} +{% snippet "snippets/organization.html", package=pkg, organization=org, has_context_title=true, hide_description=true %} {% endif %} {% endblock %} diff --git a/ckanext/odsh/templates/package/search.html b/ckanext/odsh/templates/package/search.html index 75ce5e4e537d2d1edecf4eed5d6987f71f8e2f30..b08b1f55b8059262e0300d2d75153f0b94abb5e4 100644 --- a/ckanext/odsh/templates/package/search.html +++ b/ckanext/odsh/templates/package/search.html @@ -1,12 +1,18 @@ {% ckan_extends %} +{% block breadcrumb_content %} + <li class="active">{{ h.nav_link(_('Documents'), controller='package', action='search', highlight_actions = 'new index') }}</li> +{% endblock %} {% block toolbar %} {{ super() }} <form id='dataset-search-box-form' class="section site-search simple-input" action="{% url_for controller='package', action='search' %}" - method="get" data-module="select-switch"> -{% snippet "snippets/search_box.html"%} +method="get" data-module="select-switch"> + <div class="row filter-search-row"> + <div class='btn show-filters'>Filter</div> + {% snippet "snippets/search_box.html"%} + </div> {% endblock %} {% block primary_content %} @@ -31,22 +37,23 @@ {% endblock %} {% endblock %} - {% block secondary_content %} <div class="filters"> - <div class="container-fluid filter-reset"> - <div class="filter-reset-label"><span>Filter:</span></div> - <div class="filter-reset-box"><a href="{{h.url_for(controller='package', action='search')}}">zurücksetzen</a></button></div> - </div> + {% snippet "package/snippets/close_mobile_sidebar_button.html" %} + <div class="container-fluid filter-reset"> + <div class="filter-reset-label"><span>Filter:</span></div> + <div class="filter-reset-box"> + <a href="{{h.url_for(controller='package', action='search')}}">zurücksetzen</a> + </div> + </div> </div> -{% snippet "snippets/map.html", -default_extent="{ \"type\": \"Polygon\", \"coordinates\": [[[7.6574,53.1632],[11.8322,53.1632],[11.8322,55.1066], -[7.6574,55.1066],[7.6574,53.1632]]] }"%} + <div class="filters"> <div> {% for facet in c.facet_titles %} {{ h.snippet('snippets/facet_list.html', title=c.facet_titles[facet], name=facet) }} {% endfor %} + </div> {% block datereange_search %} {% set has_range_filter = request.params.get('ext_startdate') or request.params.get('ext_enddate') %} @@ -66,27 +73,41 @@ default_extent="{ \"type\": \"Polygon\", \"coordinates\": [[[7.6574,53.1632],[11 {%- set end_error = h.odsh_render_datetime(end, False) == '' and end -%} {%- set typeStart = 'text' if (start_error or not start) else 'date' -%} {%- set typeEnd = 'text' if (end_error or not end) else 'date' -%} + <label for="ext_startdate">{{_('from')|title}}</label> - {% snippet 'snippets/datepicker.html', id='ext_startdate', value=start, class='rangesearch', placeholder=_('Date') %} - {%if start_error %} - <div class="error-reange-search"> - {{_('wrong_start_date_for_search')}} - </div> - {%endif%} + {% + snippet 'snippets/datepicker.html', + aria_label=_('date start'), + id='ext_startdate', + value=start, + class='rangesearch', + placeholder=_('Date') + %} + {%if start_error %} + <div class="error-reange-search"> + {{_('wrong_start_date_for_search')}} + </div> + {%endif%} + <label for="ext_enddate">{{_('to')|title}}</label> - {% snippet 'snippets/datepicker.html', id='ext_enddate', value=end, class='rangesearch', placeholder=_('Date') %} - {%if end_error %} - <div class="error-reange-search"> - {{_('wrong_end_date_for_search')}} - </div> - {%endif%} + {% + snippet 'snippets/datepicker.html', + aria_label=_('date end'), + id='ext_enddate', + value=end, + class='rangesearch', + placeholder=_('Date') + %} + {%if end_error %} + <div class="error-reange-search"> + {{_('wrong_end_date_for_search')}} + </div> + {%endif%} <a class='pull-right' href="javascript:{}" onclick="$('#dataset-search-box-form').submit();" class="action">{{_('submit date search') }}</a> </div> </div> </nav> </section> - </div> - <a class="close no-text hide-filters"><i class="fa fa-times-circle"></i><span class="text">close</span></a> {% endblock %} </div> @@ -94,4 +115,4 @@ default_extent="{ \"type\": \"Polygon\", \"coordinates\": [[[7.6574,53.1632],[11 {% block pre_wrap %} </form> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/ckanext/odsh/templates/package/search.html_neu b/ckanext/odsh/templates/package/search.html_neu new file mode 100644 index 0000000000000000000000000000000000000000..cadf27f07acc15663e13424255d3e6da720fa87b --- /dev/null +++ b/ckanext/odsh/templates/package/search.html_neu @@ -0,0 +1,123 @@ +{% ckan_extends %} + + +{% block breadcrumb_content %} + <li class="active">{{ h.nav_link(_('Documents'), controller='package', action='search', highlight_actions = 'new index') }}</li> +{% endblock %} + +{% block toolbar %} +{{ super() }} +<form id='dataset-search-box-form' class="section site-search simple-input" action="{% url_for controller='package', action='search' %}" +method="get" data-module="select-switch"> + <div class="row filter-search-row"> + <div class='btn show-filters'>Filter</div> + {% snippet "snippets/search_box.html"%} + </div> +{% endblock %} + +{% block primary_content %} + <section class="module"> + <div class="module-content"> + {% block page_primary_action %} + {% endblock %} + {% block form %} + {{ super() }} + {% endblock %} + {% block package_search_results_list %} + {{ h.snippet('snippets/package_list.html', packages=c.page.items) }} + {% endblock %} + </div> + + {% block page_pagination %} + {{ c.page.pager(q=c.q,symbol_next='>') }} + {% endblock %} + </section> + + {% block package_search_results_api %} + {% endblock %} +{% endblock %} + + +{% block secondary_content %} +<div class="filters"> + {% snippet "package/snippets/close_mobile_sidebar_button.html" %} + <div class="container-fluid filter-reset"> + <div class="filter-reset-label"><span>Filter:</span></div> + <div class="filter-reset-box"> + <a href="{{h.url_for(controller='package', action='search')}}">zurücksetzen</a> + </div> + </div> +</div> +{% snippet "snippets/map.html", +default_extent="{ \"type\": \"Polygon\", \"coordinates\": [[[7.6574,53.1632],[11.8322,53.1632],[11.8322,55.1066], +[7.6574,55.1066],[7.6574,53.1632]]] }"%} +<div class="filters"> + <div> + {% for facet in c.facet_titles %} + {{ h.snippet('snippets/facet_list.html', title=c.facet_titles[facet], name=facet) }} + {% endfor %} + </div> + + {% block datereange_search %} + {% set has_range_filter = request.params.get('ext_startdate') or request.params.get('ext_enddate') %} + <section class="module module-narrow module-shallow"> + <nav> + <div class="nav-title">{{_('timerange')}}</div> + <div class="rangesearch-controls"> + {% import 'macros/form.html' as form %} + {% resource 'odsh/moment.js' %} + {% resource 'odsh/datepicker.css' %} + {% resource 'odsh/bootstrap-datepicker.js' %} + {% resource 'odsh/odsh_datepicker.js' %} + <div class='controls rangesearch' > + {%- set start = request.params['ext_startdate'] -%} + {%- set start_error = h.odsh_render_datetime(start, False) == '' and start -%} + {%- set end = request.params['ext_enddate'] -%} + {%- set end_error = h.odsh_render_datetime(end, False) == '' and end -%} + {%- set typeStart = 'text' if (start_error or not start) else 'date' -%} + {%- set typeEnd = 'text' if (end_error or not end) else 'date' -%} + + <label for="ext_startdate">{{_('from')|title}}</label> + {% + snippet 'snippets/datepicker.html', + aria_label=_('date start'), + id='ext_startdate', + value=start, + class='rangesearch', + placeholder=_('Date') + %} + {%if start_error %} + <div class="error-reange-search"> + {{_('wrong_start_date_for_search')}} + </div> + {%endif%} + + <label for="ext_enddate">{{_('to')|title}}</label> + {% + snippet 'snippets/datepicker.html', + aria_label=_('date end'), + id='ext_enddate', + value=end, + class='rangesearch', + placeholder=_('Date') + %} + {%if end_error %} + <div class="error-reange-search"> + {{_('wrong_end_date_for_search')}} + </div> + {%endif%} + <a class='pull-right' href="javascript:{}" onclick="$('#dataset-search-box-form').submit();" class="action">{{_('submit date search') }}</a> + </div> + </div> + </nav> + </section> + </div> + <a class="close no-text hide-filters"><i class="fa fa-times-circle"></i><span class="text">close</span></a> + {% endblock %} + +</div> +{% endblock %} + +{% block pre_wrap %} +</form> +{% endblock %} diff --git a/ckanext/odsh/templates/package/snippets/close_mobile_sidebar_button.html b/ckanext/odsh/templates/package/snippets/close_mobile_sidebar_button.html new file mode 100644 index 0000000000000000000000000000000000000000..5e30ade9262d6db85bd7ed8792b3f92091950b7a --- /dev/null +++ b/ckanext/odsh/templates/package/snippets/close_mobile_sidebar_button.html @@ -0,0 +1,3 @@ +<div class="container-fluid hide-filters-style"> + <a class="no-text hide-filters"><img src="/base/images/icon_close.svg" alt="Filterdialog schließen"><span class="text">close</span></a> +</div> \ No newline at end of file diff --git a/ckanext/odsh/templates/package/snippets/info.html b/ckanext/odsh/templates/package/snippets/info.html index cff6830c16011422f3f1a62ed512c8e0d4773219..167ec74d8efccd2624e126e827508a1d86a0de34 100644 --- a/ckanext/odsh/templates/package/snippets/info.html +++ b/ckanext/odsh/templates/package/snippets/info.html @@ -8,6 +8,11 @@ Example: {% snippet "package/snippets/info.html", pkg=pkg %} #} + +{% set daterange = h.tpsh_get_daterange_prettified(pkg) %} +{% set language_icon = h.get_language_icon(pkg) %} +{% set license_attribution_by_text = h.odsh_extract_value_from_extras(pkg.extras, 'licenseAttributionByText') %} + {% block package_info %} {% if pkg %} <section class="module module-narrow"> @@ -15,40 +20,43 @@ Example: <div class="module-content"> {% block package_info_inner %} {% block heading %} - {% endblock %} + {% endblock heading %} - {% block groups %} - <div class="groups-detail info-detail"> - <div>{{ _('Kategorie') }}:</div> - {{ pkg.groups|map(attribute='display_name')|join('; ') }} + {% block groups %} {# aka categories #} + {% if pkg.groups|length %} + <div class="info-detail groups-detail"> + <div> + {% trans category_count=pkg.groups|length %} + category: + {% pluralize %} + categories: + {% endtrans %} + </div> + {% for category in pkg.groups %} + <span class='category-with-icon'> + <span class='category-icon-container'> + <img src="/base/images/icon_kat_{{category.name}}.svg" /> + </span> + <span class="category-name"> + {{ category.display_name }} + </span> + </span> + {% endfor %} </div> - {% endblock %} - - {% block nums %} - {% endblock %} - + {% endif %} + {% endblock groups %} - {% block license %} - <div class="license-detail info-detail"> - <div>{{ _('License') }}:</div> - {%set lic=pkg.license_title if pkg.license_title else '-'%} - {%set licenseAttributionByText = h.odsh_extract_value_from_extras(pkg.extras,'licenseAttributionByText') %} - {%set name=' (' + licenseAttributionByText +')' if licenseAttributionByText else ''%} - <a href='{{pkg.license_url}}'>{{ lic }}</a>{{ name }} - </div> - {% endblock %} {% block timerange %} + {% if daterange %} <div class="timerange-detail info-detail"> <div>{{ _('timerange') }}:</div> - {% set temporal_start = h.odsh_extract_value_from_extras(pkg.extras,'temporal_start') %} - {% set temporal_end = h.odsh_extract_value_from_extras(pkg.extras,'temporal_end') %} - {%set start=h.odsh_render_datetime(temporal_start) if temporal_start else ''%} - {%set end=h.odsh_render_datetime(temporal_end) if temporal_end else ''%} - <p>{{ start }} - {{ end }}</p> + <p>{{ daterange }}</p> </div> - {% endblock %} + {% endif %} + {% endblock timerange %} + {% block last_change %} <div class="last-change-detail info-detail"> {% set value = h.odsh_extract_value_from_extras(pkg.extras,'issued')%} @@ -61,9 +69,19 @@ Example: <div>{{ _('modified') }}:</div> {{modified}} </div> - {% endblock %} + {% endblock last_change %} + + {% block license %} + <div class="license-detail info-detail"> + <div>{{ _('License') }}:</div> + {%set lic=pkg.license_title if pkg.license_title else '-'%} + {%set licenseAttributionByText = h.odsh_extract_value_from_extras(pkg.extras,'licenseAttributionByText') %} + {%set name=' (' + licenseAttributionByText +')' if licenseAttributionByText else ''%} + <a href='{{pkg.license_url}}'>{{ lic }}</a>{{ name }} + </div> + {% endblock license %} - {% endblock %} + {% endblock package_info_inner %} </div> {% set map_text = h.odsh_get_spatial_text(pkg) %} <div class="spatial-detail info-detail"> @@ -95,15 +113,27 @@ Example: {% set current_url = h.odsh_encodeurl(h.full_current_url()) %} <p>{{ _('share this dataset')}}:</p> <div class="share-links"> - <a href="https://www.linkedin.com/shareArticle?mini=true&url={{current_url}}" target="_blank"><img class='' src="/base/images/Icon-linkedin.png"/></a> - <a href="https://www.xing.com/app/user?op=share&url={{ current_url }}" target="_blank"><img class='' src="/base/images/Icon-xing.png"/></a> - <a href="https://www.facebook.com/sharer.php?href={{ current_url }}" target="_blank"><img class='' src="/base/images/Icon-facebook.png"/></a> - <a href="https://twitter.com/share?url={{ current_url }}" target="_blank"><img class='' src="/base/images/Icon-twitter.png"/></a> - <a href="mailto:?body={{ current_url }}" target="_blank"><img class='' src="/base/images/Icon-mail.png"/></a> + <a href="https://www.linkedin.com/shareArticle?mini=true&url={{current_url}}" target="_blank"> + <img class='' src="/base/images/Icon-linkedin.png" alt="Linked In"/> + </a> + <a href="https://www.xing.com/app/user?op=share&url={{ current_url }}" target="_blank"> + <img class='' src="/base/images/Icon-xing.png" alt="XING"/> + </a> + <a href="https://www.facebook.com/sharer.php?href={{ current_url }}" target="_blank"> + <img class='' src="/base/images/Icon-facebook.png" alt="facebook"/> + </a> + <a href="https://twitter.com/share?url={{ current_url }}" target="_blank"> + <img class='' src="/base/images/Icon-twitter.png" alt="twitter"/> + </a> + <a href="mailto:?body={{ current_url }}" target="_blank"> + <img class='' src="/base/images/Icon-mail.png" alt="{{ _('send an email') }}"/> + </a> </div> </div> - {% endblock %} + {% endblock social_nav %} {% endif %} + + </section> {% endif %} -{% endblock %} \ No newline at end of file +{% endblock package_info %} diff --git a/ckanext/odsh/templates/package/snippets/package_basic_fields.html b/ckanext/odsh/templates/package/snippets/package_basic_fields.html index 5bed804b41f9cc894ef9381a496e24c22c738daa..ada4778f2c438600cabce8295223a229ed2e87b6 100644 --- a/ckanext/odsh/templates/package/snippets/package_basic_fields.html +++ b/ckanext/odsh/templates/package/snippets/package_basic_fields.html @@ -3,315 +3,320 @@ {% resource 'odsh/bootstrap-multiselect.js' %} {% set dataset_is_draft = data.get('state', 'draft').startswith('draft') or data.get('state', 'none') == 'none' %} -<!-- field title --> + +{# field title #} {% block package_basic_fields_title %} {% set error_string = _(_('Title') + ': '+errors.title[0]) if errors.title %} -{{ form.input('title', id='field-title', label=_('Title'), value=data.title, -error=error_string, classes=['control-full'], attrs={'data-module': 'slug-preview-target'}, -is_required=true,placeholder=_('Enter title')) }} -{% endblock %} +{{ + form.input( + 'title', id='field-title', + label=_('Title'), + value=data.title, + error=error_string, + classes=['control-full'], + attrs={'data-module': 'slug-preview-target'}, + is_required=true, + placeholder=_('Enter title') + ) +}} +{% endblock package_basic_fields_title %} - <!-- field notes --> - {% block package_basic_fields_description %} - {% set error_string = _(_('Description') + ': '+errors.notes[0]) if errors.notes %} - {{ form.markdown('notes', id='field-notes', label=_('Description'), value=data.notes, - error=error_string, is_required=true, placeholder=_('Enter description')) }} - {% endblock %} +{# field note #} +{% block package_basic_fields_description %} +{% set error_string = _(_('Description') + ': '+errors.notes[0]) if errors.notes %} +{{ + form.markdown( + 'notes', + id='field-notes', + label=_('Description'), + value=data.notes, + error=error_string, + is_required=false, + placeholder=_('Enter description') + ) +}} +{% endblock package_basic_fields_description %} - {% block package_basic_fields_org %} - {% if data.group_id %} - <input type="hidden" name="groups__0__id" value="{{ data.group_id }}" /> - {% endif %} - {% set dataset_has_organization = data.owner_org or data.group_id %} - {% set organizations_available = h.organizations_available('create_dataset') %} - {% set user_is_sysadmin = h.check_access('sysadmin') %} - {% set show_organizations_selector = organizations_available %} - {% set show_visibility_selector = dataset_has_organization or (organizations_available and (user_is_sysadmin or - dataset_is_draft)) %} - {% set existing_org = data.owner_org or data.group_id %} +{# field Organization #} +{% block package_basic_fields_org %} +{% if data.group_id %} +<input type="hidden" name="groups__0__id" value="{{ data.group_id }}" /> +{% endif %} - <div class="control-group field-organization"> - <label for="field-organizations" class="control-label">{{ _('Organization') }} - <span title="{{ _("This field is required") }}" class="control-required">*</span> - </label> - <div class="controls"> - <div class="row-fluid"> - <div class="span6"> - <select id="field-organizations" name="owner_org" data-module="autocomplete"> - {% if h.check_config_permission('create_unowned_dataset') %} - <option value="" {% if not selected_org and data.id %} selected="selected" {% endif %}>{{ _('No - organization') }}</option> - {% endif %} - {% for organization in organizations_available %} - {# get out first org from users list only if there is not an existing org #} - {% set selected_org = (existing_org and existing_org == organization.id) or (not existing_org and not data.id and organization.id == organizations_available[0].id) %} - <option value="{{ organization.id }}" {% if selected_org %} selected="selected" {% endif %}>{{ - organization.display_name }}</option> - {% endfor %} - </select> - </div> +{% set dataset_has_organization = data.owner_org or data.group_id %} +{% set organizations_available = h.organizations_available('create_dataset') %} +{% set user_is_sysadmin = h.check_access('sysadmin') %} +{% set show_organizations_selector = organizations_available %} +{% set show_visibility_selector = dataset_has_organization or (organizations_available and (user_is_sysadmin or +dataset_is_draft)) %} +{% set existing_org = data.owner_org or data.group_id %} + +<div class="control-group field-organization"> + <label for="field-organizations" class="control-label"> + {{ _('Organization') }} + <span title="{{ _("This field is required") }}" class="control-required">*</span> + </label> + <div class="controls"> + <div class="row-fluid"> + <div class="span6"> + <select id="field-organizations" name="owner_org" data-module="autocomplete"> + {% if h.check_config_permission('create_unowned_dataset') %} + <option value="" {% if not selected_org and data.id %} selected="selected" {% endif %}>{{ _('No + organization') }}</option> + {% endif %} + {% for organization in organizations_available %} + {# get out first org from users list only if there is not an existing org #} + {% set selected_org = (existing_org and existing_org == organization.id) or (not existing_org and not data.id and organization.id == organizations_available[0].id) %} + <option value="{{ organization.id }}" {% if selected_org %} selected="selected" {% endif %}>{{ + organization.display_name }}</option> + {% endfor %} + </select> </div> </div> </div> - {% endblock %} +</div> +{% endblock package_basic_fields_org %} - <!-- field license --> - {% set error_missing_attr = h.odsh_extract_error('licenseAttributionByText', errors) %} - <label class="control-label" for="field-license"> - {{ _("License") }}: - <span title="{{ _("This field is required") }}" class="control-required">*</span> - </label> - <div class='row-fluid'> - <div class='span3'> - {% block package_basic_fields_license %} - {% set error = errors.license_id %} - {% set error_license_id = _(_("License") + ': '+errors.license_id[0] ) if errors.license_id %} - <div class="control-group {{ " error" if error_license_id }}"> - <div class="controls"> - <select id="field-license" name="license_id"> - {% set existing_license_id = data.get('license_id') %} - {% for license_id, license_desc in h.presorted_license_options(existing_license_id) %} - <option value="{{ license_id }}" {% if existing_license_id==license_id %}selected="selected" {% - endif %}>{{ license_desc }}</option> - {% endfor %} - </select> - </div> +{# field license #} +{% block package_basic_fields_license %} + +{% set error_missing_attr = h.odsh_extract_error('licenseAttributionByText', errors) %} +<label class="control-label" for="field-license"> + {{ _("License") }}: + <span title="{{ _("This field is required") }}" class="control-required">*</span> +</label> +<div class='row-fluid'> + <div class='span3'> + {% set error = errors.license_id %} + {% set error_license_id = _(_("License") + ': '+errors.license_id[0] ) if errors.license_id %} + <div class="control-group {{ " error" if error_license_id }}"> + <div class="controls"> + <select id="field-license" name="license_id"> + {% set existing_license_id = data.get('license_id') %} + {% for license_id, license_desc in h.presorted_license_options(existing_license_id) %} + <option value="{{ license_id }}" {% if existing_license_id==license_id %}selected="selected" {% + endif %}>{{ license_desc }}</option> + {% endfor %} + </select> </div> </div> - <div class='span3'> - <!-- field Namensnennung --> - {% set field = 'licenseAttributionByText' %} - {% set value = h.odsh_extract_value_from_extras(data.extras,field) %} - <div class="control-group {{ " error" if error_missing_attr }} control-full"> - <div class="controls"> - {{ form.input_extra(field, value=value, index=h.odsh_create_checksum(field), type='text', attrs={'disabled':true, - 'data-module':"odsh_form", 'data-module-licensetoggle':'true', 'data-module-autofill':'true'}, placeholder=_('enter name')) }} - </div> + </div> + <div class='span3'> + {# field Namensnennung #} + {% set field = 'licenseAttributionByText' %} + {% set value = h.odsh_extract_value_from_extras(data.extras,field) %} + <div class="control-group {{ " error" if error_missing_attr }} control-full"> + <div class="controls"> + {{ form.input_extra(field, value=value, index=h.odsh_create_checksum(field), type='text', attrs={'disabled':true, + 'data-module':"odsh_form", 'data-module-licensetoggle':'true', 'data-module-autofill':'true'}, placeholder=_('enter name')) }} </div> </div> - {% if error_license_id %} - <div class="span6 inline-error"> - {{_(error_license_id)}} - </div> - {% endif %} - {% if error_missing_attr%} - <div class="span6 inline-error"> - {{_(error_missing_attr)}} - </div> - {% endif %} </div> + {% if error_license_id %} + <div class="span6 inline-error"> + {{_(error_license_id)}} + </div> + {% endif %} + {% if error_missing_attr%} + <div class="span6 inline-error"> + {{_(error_missing_attr)}} + </div> + {% endif %} +</div> - {% endblock %} +{% endblock package_basic_fields_license%} - <!-- timerange --> - {% set error_start = h.odsh_extract_error_new('temporal_start', errors) %} - {% set error_end = h.odsh_extract_error_new('temporal_end', errors) %} - {% set error_string = 'odsh_temporal_error_label' if error_start and error_end else (error_start if error_start else (error_end if error_end))%} - <label for="start-end" class="control-label">{{ _('timerange') }}: - <span title="{{ _("This field is required") }}" class="control-required">*</span> - </label> - <div class='row-fluid'> - {% resource 'odsh/moment.js' %} - {% resource 'odsh/datepicker.css' %} - {% resource 'odsh/bootstrap-datepicker.js' %} - {% resource 'odsh/odsh_datepicker.js' %} - <div id='start-end' class='span3'> - <!-- field temporal_start --> - {% set field = 'temporal_start' %} - {% set data_temporal_start = h.odsh_extract_value_from_extras(data.extras,field) %} - {% set value = data_temporal_start.split('T')[0] if data_temporal_start else None %} - {% set index = h.odsh_create_checksum(field) %} - <div class="control-group {{ " error" if error_start }} control-full"> - <div class="controls"> - {{ form.input_extra(field, value=value, index=index, type='hidden') }} - <input pattern="\d{2}\.\d{2}\.\d{4}" autocomplete="off" id="datepicker_start" type="text" - value="{{h.odsh_render_datetime(value, False)}}" placeholder="{{ _('from') }}" - data-module="odsh_datepicker" data-module-target='field-temporal_start-value'/> - </div> + +{# timerange #} +{% set error_start = h.odsh_extract_error_new('temporal_start', errors) %} +{% set error_end = h.odsh_extract_error_new('temporal_end', errors) %} +{% set error_string = 'odsh_temporal_error_label' if error_start and error_end else (error_start if error_start else (error_end if error_end))%} +<label for="start-end" class="control-label">{{ _('timerange') }}: + {# <span title="{{ _("This field is required") }}" class="control-required">*</span> #} +</label> +<div class='row-fluid'> + {% resource 'odsh/moment.js' %} + {% resource 'odsh/datepicker.css' %} + {% resource 'odsh/bootstrap-datepicker.js' %} + {% resource 'odsh/odsh_datepicker.js' %} + <div id='start-end' class='span3'> + {# field temporal_start #} + {% set field = 'temporal_start' %} + {% set data_temporal_start = h.odsh_extract_value_from_extras(data.extras,field) %} + {% set value = data_temporal_start.split('T')[0] if data_temporal_start else None %} + {% set index = h.odsh_create_checksum(field) %} + <div class="control-group {{ " error" if error_start }} control-full"> + <div class="controls"> + {{ form.input_extra(field, value=value, index=index, type='hidden') }} + <input pattern="\d{2}\.\d{2}\.\d{4}" autocomplete="off" id="datepicker_start" type="text" + value="{{h.odsh_render_datetime(value, False)}}" placeholder="{{ _('from') }}" + data-module="odsh_datepicker" data-module-target='field-temporal_start-value'/> </div> </div> - <div class='span3'> - <!-- field temporal_end --> - {% set field = 'temporal_end' %} - {% set data_temporal_end = h.odsh_extract_value_from_extras(data.extras,field) %} - {% set value = data_temporal_end.split('T')[0] if data_temporal_end else None %} - <div class="control-group {{ " error" if error_end }} control-full"> - <div class="controls"> - {{ form.input_extra(field, value=value, index=h.odsh_create_checksum(field), type='hidden') }} - <input pattern="\d{2}\.\d{2}\.\d{4}" autocomplete="off" id="datepicker_end" type="text" - value="{{h.odsh_render_datetime(value, False)}}" placeholder="{{ _('to') }}" - data-module="odsh_datepicker" data-module-target='field-temporal_end-value'/> - </div> + </div> + <div class='span3'> + {# field temporal_end #} + {% set field = 'temporal_end' %} + {% set data_temporal_end = h.odsh_extract_value_from_extras(data.extras,field) %} + {% set value = data_temporal_end.split('T')[0] if data_temporal_end else None %} + <div class="control-group {{ " error" if error_end }} control-full"> + <div class="controls"> + {{ form.input_extra(field, value=value, index=h.odsh_create_checksum(field), type='hidden') }} + <input pattern="\d{2}\.\d{2}\.\d{4}" autocomplete="off" id="datepicker_end" type="text" + value="{{h.odsh_render_datetime(value, False)}}" placeholder="{{ _('to') }}" + data-module="odsh_datepicker" data-module-target='field-temporal_end-value'/> </div> </div> - {% if error_string %} - <div class="span6 inline-error"> - {{_(error_string)}} - </div> - {% endif %} </div> + {% if error_string %} + <div class="span6 inline-error"> + {{_(error_string)}} + </div> + {% endif %} +</div> - <!-- field issued --> - {% set field = 'issued' %} - {% set data_issued = h.odsh_extract_value_from_extras(data.extras,field) %} - {% set error = h.odsh_extract_error_new(field, errors) %} - {% set value = data_issued.split('T')[0] if data_issued else (h.odsh_now() if not error else '')%} - <div class="control-group {{ " error" if error }} control-full"> - <label class="control-label" for="field-{{field}}">Veröffentlichungsdatum: <span title="Dieses Feld ist erforderlich" class="control-required">*</span> </label> - <div class="controls"> - <div class="row-fluid"> - <div class="span6"> - {{ form.input_extra(field, value=value , index=h.odsh_create_checksum(field), type='hidden') }} - <input pattern="\d{2}\.\d{2}\.\d{4}" autocomplete="off" type="text" - value="{{h.odsh_render_datetime(value, False)}}" - data-module="odsh_datepicker" data-module-target='field-issued-value'/> - </div> - <div class="span6 inline-error"> - {% if error %} - {{_(error)}} - {% endif %} - </div> +{# field issued #} +{% set field = 'issued' %} +{% set data_issued = h.odsh_extract_value_from_extras(data.extras,field) %} +{% set error = h.odsh_extract_error_new(field, errors) %} +{% set value = data_issued.split('T')[0] if data_issued else (h.odsh_now() if not error else '')%} +<div class="control-group {{ " error" if error }} control-full"> + <label class="control-label" for="field-{{field}}">Veröffentlichungsdatum: <span title="Dieses Feld ist erforderlich" class="control-required">*</span> </label> + <div class="controls"> + <div class="row-fluid"> + <div class="span6"> + {{ form.input_extra(field, value=value , index=h.odsh_create_checksum(field), type='hidden') }} + <input pattern="\d{2}\.\d{2}\.\d{4}" autocomplete="off" type="text" + value="{{h.odsh_render_datetime(value, False)}}" + data-module="odsh_datepicker" data-module-target='field-issued-value'/> + </div> + <div class="span6 inline-error"> + {% if error %} + {{_(error)}} + {% endif %} </div> </div> </div> +</div> - <!-- field tags --> - {% block package_basic_fields_tags %} - {% set tag_attrs = {'data-module': 'autocomplete', 'data-module-tags': '', 'data-module-source': - '/api/2/util/tag/autocomplete?incomplete=?'} %} - {{ form.input('tag_string', id='field-tags', label=_('Tags'), value=data.tag_string, error=errors.tag_string, - classes=['control-full'], attrs=tag_attrs, - is_required=false, placeholder=_('odsh tags placeholder')) }} - {% endblock %} +{# field tags #} +{% block package_basic_fields_tags %} +{% set tag_attrs = { + 'data-module': 'autocomplete', + 'data-module-tags': '', + 'data-module-source': '/api/2/util/tag/autocomplete?incomplete=?' +} %} +{{ + form.input( + 'tag_string', + id='field-tags', + label=_('Tags'), + value=data.tag_string, + error=errors.tag_string, + classes=['control-full'], + attrs=tag_attrs, + is_required=false, + placeholder=_('odsh tags placeholder') + ) +}} +{% endblock %} - <!-- field spatial_uri --> - {% set field = 'spatial_uri' %} - {% set value = h.odsh_extract_value_from_extras(data.extras,field) %} - {% set error = h.odsh_extract_error(field, errors) %} - <div class="control-group {{ " error" if error }} control-full"> - <label class="control-label" for="field-{{field}}">{{_('Spatial uri')}}: <span title="Dieses Feld ist erforderlich" class="control-required">*</span> </label> - <div class="controls"> - <div class="row-fluid"> - {% if not value and not error and data.state == 'active'%} - <div class="span6"> - <input type='text' value="{{h.truncate(h.odsh_extract_value_from_extras(data.extras,'spatial'),20)}}" readonly /> - </div> - {% else %} - <div class="span6"> - {{ form.input_extra(field, value=value, index=h.odsh_create_checksum(field), type='text', placeholder=_('Enter spatial uri')) }} - </div> - <div class="span6 inline-error"> - {% if error %} - {{_(error)}} - {% endif %} - </div> - {% endif %} - </div> - </div> - </div> +{# field language #} +{% set before_selected = data.get('language') or h.odsh_extract_value_from_extras(data.extras, 'language') or 'http://publications.europa.eu/resource/authority/language/DEU' %} +{{ form.tpsh_select('language', label=_('language'), options=h.get_language_for_selection(), selected=before_selected , error=errors.language, is_required=False, classes=['control-full']) }} - {# - {% set spatial_extends_available = h.odsh_spatial_extends_available() %} - <div class="control-group"> - <label class="control-label" for="field-{{field}}">{{_('Spatial uri')}}: <span title="Dieses Feld ist erforderlich" class="control-required">*</span> </label> - <div class="controls"> - <div class="row-fluid"> - <div class="span6"> - <select id="field-{{field}}" data-module="autocomplete" data-module-items="10"> - {% for extend in spatial_extends_available%} - <option value="{{ extend }}" {% if selected_extend %} selected="selected" {% endif %}>{{extend}}</option> - {% endfor %} - </select> - </div> - </div> - </div> - </div> - #} - <!-- field private --> - <div class="control-group"> - <label for="field-private" class="control-label">{{ _('Visibility') }}: - <span title="{{ _("This field is required") }}" class="control-required">*</span> - </label> - <div class="controls"> - <div class="row-fluid"> - <div class="span6"> - <select id="field-private" name="private"> - {% for option in [('True', _('Private')), ('False', _('Public'))] %} - <option value="{{ option[0] }}" {% if option[0]==data.private|trim %}selected="selected" {% - endif %}>{{ - option[1] }}</option> - {% endfor %} - </select> - </div> +{# field spatial_uri #} +{% set spatial_uri_from_extras = h.odsh_extract_value_from_extras(data.extras, 'spatial_uri') %} +{% set spatial_uri = data.spatial_uri_temp or spatial_uri_from_extras or '' %} +{{ form.select_autocomplete('spatial_uri_temp', label=_('Spatial uri'), selected=spatial_uri, options=h.get_spatial_for_selection(), is_required=False, classes=['control-full', 'field-spatial_uri'])}} + + +{# field private #} +<div class="control-group"> + <label for="field-private" class="control-label">{{ _('Visibility') }}: + <span title="{{ _("This field is required") }}" class="control-required">*</span> + </label> + <div class="controls"> + <div class="row-fluid"> + <div class="span6"> + <select id="field-private" name="private"> + {% for option in [('True', _('Private')), ('False', _('Public'))] %} + <option value="{{ option[0] }}" {% if option[0]==data.private|trim %}selected="selected" {% + endif %}>{{ + option[1] }}</option> + {% endfor %} + </select> </div> </div> </div> +</div> - {% if data.id and h.check_access('package_delete', {'id': data.id}) and data.state != 'active' %} - <div class="control-group"> - <label for="field-state" class="control-label">{{ _('State') }}</label> - <div class="controls"> - <div class="row-fluid"> - <div class="span6"> - <select id="field-state" name="state"> - <option value="active" {% if data.get('state', 'none' )=='active' %} selected="selected" {% - endif %}>{{ - _('Active') }}</option> - <option value="deleted" {% if data.get('state', 'none' )=='deleted' %} selected="selected" {% - endif %}>{{ - _('Deleted') }}</option> - </select> - </div> +{% if data.id and h.check_access('package_delete', {'id': data.id}) and data.state != 'active' %} +<div class="control-group"> + <label for="field-state" class="control-label">{{ _('State') }}</label> + <div class="controls"> + <div class="row-fluid"> + <div class="span6"> + <select id="field-state" name="state"> + <option value="active" {% if data.get('state', 'none' )=='active' %} selected="selected" {% + endif %}>{{ + _('Active') }}</option> + <option value="deleted" {% if data.get('state', 'none' )=='deleted' %} selected="selected" {% + endif %}>{{ + _('Deleted') }}</option> + </select> </div> </div> </div> - {% endif %} +</div> +{% endif %} - <!-- field groups --> - {% set error_groups = h.odsh_extract_error_new('groups', errors) %} - <div class="control-group {{ " error" if error_groups }}"> - {% set groups_label='Kategorien'%} - {% set multiselect_nonSelectedText='keine' %} - {% set multiselect_allSelectedText='alle' %} - {% set multiselect_nSelectedText='gewählt' %} - <label for="field-groups" class="control-label">{{ groups_label }} - <span title="{{ _("This field is required") }}" class="control-required">*</span> - </label> - <div class="controls"> - <div class="row-fluid"> - <div class="span6"> - {% set existing_groups = data.get('groups') %} - {% if existing_groups %} - {% set existing_groups_string = existing_groups|map(attribute='id')|join(',') %} - {% else %} - {% set existing_groups_string = h.odsh_extract_value_from_extras(data.extras,'groups') %} - {% endif %} - <select id='field-groups' multiple="multiple" data-module="odsh_form" data-module-multiselect='true' - data-module-nonSelectedText="{{multiselect_nonSelectedText}}" data-module-allSelectedText="{{multiselect_allSelectedText}}" - data-module-nSelectedText="{{multiselect_nSelectedText}}" data-module-update='{{data.id != None}}'> - {% for option in h.groups_available()%} - <option value={{option.id}} {% if existing_groups_string!=None and option['id'] in existing_groups_string %}selected="selected" - {% endif %}> - {{ option['display_name'] }}</option> - {% endfor %} - </select> - <div id='selected-groups'> - {{ form.input_extra('groups', value=existing_groups_string, index=h.odsh_create_checksum('groups'), type='hidden')}} - </div> - </div> - <div class="span6 inline-error"> - {% if error_groups %} - {{_(error_groups)}} - {% endif %} + +{# field groups #} +{% set error_groups = h.odsh_extract_error_new('groups', errors) %} +<div class="control-group {{ " error" if error_groups }}"> + {% set groups_label='Kategorien'%} + {% set multiselect_nonSelectedText='keine' %} + {% set multiselect_allSelectedText='alle' %} + {% set multiselect_nSelectedText='gewählt' %} + <label for="field-groups" class="control-label"> + {{ groups_label }} + </label> + <div class="controls"> + <div class="row-fluid"> + <div class="span6"> + {% set existing_groups = data.get('groups') %} + {% if existing_groups %} + {% set existing_groups_string = existing_groups|map(attribute='id')|join(',') %} + {% else %} + {% set existing_groups_string = h.odsh_extract_value_from_extras(data.extras,'groups') %} + {% endif %} + <select id='field-groups' multiple="multiple" data-module="odsh_form" data-module-multiselect='true' + data-module-nonSelectedText="{{multiselect_nonSelectedText}}" data-module-allSelectedText="{{multiselect_allSelectedText}}" + data-module-nSelectedText="{{multiselect_nSelectedText}}" data-module-update='{{data.id != None}}'> + {% for option in h.groups_available()%} + <option value={{option.id}} {% if existing_groups_string!=None and option['id'] in existing_groups_string %}selected="selected" + {% endif %}> + {{ option['display_name'] }}</option> + {% endfor %} + </select> + <div id='selected-groups'> + {{ form.input_extra('groups', value=existing_groups_string, index=h.odsh_create_checksum('groups'), type='hidden')}} </div> </div> + <div class="span6 inline-error"> + {% if error_groups %} + {{_(error_groups)}} + {% endif %} + </div> </div> - </div> \ No newline at end of file + </div> +</div> diff --git a/ckanext/odsh/templates/package/snippets/resource_item.html b/ckanext/odsh/templates/package/snippets/resource_item.html index cf63b5a7ba30932a882c494c31c6b0c7c29d556b..457eb4d8fca6dabff1ec8849816cf23a09270e64 100644 --- a/ckanext/odsh/templates/package/snippets/resource_item.html +++ b/ckanext/odsh/templates/package/snippets/resource_item.html @@ -2,44 +2,66 @@ {% set url = h.url_for(controller='package', action=url_action, id=pkg.name, resource_id=res.id) %} {# hack for correcting slave url if resource was uploaded #} -{% set download = h.odsh_public_resource_url(res) or ulr%} +{% set download = h.odsh_public_resource_url(res) or url %} -<li class="resource-item" data-id="{{ res.id }}"> +{% set rtitle=h.resource_display_name(res) if res.name else ' '%} +{% set resource_size = h.tpsh_get_resource_size(res) %} - <div class="container-fluid"> - <div class="row"> - <div class="span8 resource-details-left"> - <div class="row resource-title-container"> - {% set rtitle=h.resource_display_name(res) if res.name else ' '%} - <div class="resource-title" title="{{ rtitle }}"> - {% if res.name %} - <a href="{{ download }}"> - {{ h.resource_display_name(res) }} - </a> - {% endif %} - </div> +<li class="resource-item" data-id="{{ res.id }}"> + <div class="resource-title-container"> + <div class="resource-title" title="{{ rtitle }}"> + {% if res.name %} + <a href="{{ download }}"> + {{ h.resource_display_name(res) }} + </a> + {% endif %} + {% if resource_size %} + <p>Dateigröße: {{ resource_size }}</p> + {% endif %} + {% set number_of_pages = res.get('number_of_pages') %} + {% if number_of_pages%} + <p>Seitenanzahl: {{ number_of_pages }}</p> + {% endif %} </div> + <div class="resource-icons"> + {% block resource_item_explore_links %} + <a href="{{ download }}" aria-label="{{ _('download file') }}"> + <div aria-hidden="true" class="icon icon-download"></div> + </a> + {% if can_edit %} + <a href="{{ h.url_for(controller='package', action='resource_edit', id=pkg.name, resource_id=res.id) }}"> + <div class="icon icon-edit"></div> + </a> + {% endif %} + {% endblock %} + </div> + {% if res.format %} + <a href="{{ download }}" > + <div class="dataformat-label resource-dataformat-label label" style='font-size:{{150/(res.format|length)}}px'>{{res.format}}</div> + </a> + {% endif %} + </div> +</li> + + <!-- </div> <div class="row resource-description-container"> </div> <div class="resource-icons"> - {% block resource_item_explore_links %} - <a href="{{ download }}"> - <div class="icon icon-download"></div> + <a href="{{ download }}" aria-label="{{ _('download file') }}"> + <div aria-hidden="true" class="icon icon-download"></div> </a> {% if can_edit %} <a href="{{ h.url_for(controller='package', action='resource_edit', id=pkg.name, resource_id=res.id) }}"> <div class="icon icon-edit"></div> </a> {% endif %} - {% endblock %} + </div> + <div> + + </div> </div> - {% if(res.format) %} - <a href="{{ download }}"> - <div class="dataformat-label resource-dataformat-label label" style='font-size:{{150/(res.format|length)}}px'>{{res.format}}</div> - </a> - {% endif %} </div> </div> -</li> \ No newline at end of file +</li> --> diff --git a/ckanext/odsh/templates/page.html b/ckanext/odsh/templates/page.html index 3cd01dece9eb8d86bdd41a8a5bb86f31af21909d..134079dd6ced8e52b3e01a7eec0fcef0a3db1a22 100644 --- a/ckanext/odsh/templates/page.html +++ b/ckanext/odsh/templates/page.html @@ -1,11 +1,21 @@ {% ckan_extends %} +{% block skip %} + <a class="skip-link" href="#content"> + Zum Inhalt springen. + </a> +{% endblock skip %} {%- block content %} +<div class="blur-layer"></div> {% block maintag %}<div role="main">{% endblock %} + +{% if h.odsh_use_matomo() %} {% set matomo_url = h.odsh_tracking_url()%} {% set matomo_id = h.odsh_tracking_id()%} <noscript><p><img src="{{matomo_url}}?idsite={{matomo_id}}&rec=1" style="border:0;" alt="" /></p></noscript> +{% endif %} + <div id="content" class="container"> {% if h.odsh_show_testbanner() %} <div id="testsystem"> diff --git a/ckanext/odsh/templates/snippets/datepicker.html b/ckanext/odsh/templates/snippets/datepicker.html index 18ff3039faa64807512896b5e1977099c162b66c..1119e4e4882883ee6760984109351f31eb30e2f2 100644 --- a/ckanext/odsh/templates/snippets/datepicker.html +++ b/ckanext/odsh/templates/snippets/datepicker.html @@ -1,6 +1,17 @@ {% set class = class or '' %} {% set placeholder = placeholder or '' %} +{% set aria_label = aria_label or '' %} <input id="{{id}}" name="{{id}}" type="hidden" value="{{value}}" /> -<input pattern="\d{2}\.\d{2}\.\d{4}" autocomplete="off" id="datepicker_{{id}}" type="text" value="{{h.odsh_render_datetime(value, False)}}" placeholder="{{ placeholder }}" - class='{{class}}' data-module="odsh_datepicker" data-module-target='{{id}}'/> \ No newline at end of file +<input + aria-label="{{ aria_label }}" + pattern="\d{2}\.\d{2}\.\d{4}" + autocomplete="off" + id="datepicker_{{ id }}" + type="text" + value="{{h.odsh_render_datetime(value, False)}}" + placeholder="{{ placeholder }}" + class='{{ class }}' + data-module="odsh_datepicker" + data-module-target='{{ id }}' +/> \ No newline at end of file diff --git a/ckanext/odsh/templates/snippets/facet_list.html b/ckanext/odsh/templates/snippets/facet_list.html index 9741ee167dcdeb210b0c8013c6c8bf5668eabc25..17531a4884925b89173a02aebf0c20109933daba 100644 --- a/ckanext/odsh/templates/snippets/facet_list.html +++ b/ckanext/odsh/templates/snippets/facet_list.html @@ -12,44 +12,66 @@ <nav> {% set title = title or h.get_facet_title(name) %} <div class="nav-title">{{ title }}</div> - <ul class="{{ nav_class or 'unstyled nav nav-simple nav-facet' }}"> - {% for item in items %} - {% set href = h.remove_url_param(name, item.name, extras=extras, alternative_url=alternative_url) if - item.active else h.add_url_param(new_params={name: item.name}, extras=extras, alternative_url=alternative_url) - %} - {% set label = label_function(item) if label_function else item.display_name %} - {% if title=='Open-Data-Eigenschaften'%} - {% set label = _(label)%} - {% endif%} - {% set count = count_label(item['count']) if count_label else ('%d' % item['count']) %} - {% set label_truncated = h.truncate(label, 25) if not label_function else label %} - <li class="{{ nav_item_class or 'nav-item' }}"> - <div class="facet_label"> - {# TODO: checkbox-id vereinheitlichen (code-duplikation) und sicher gegen Titel mit Leerzeichen machen! #} - <input type="checkbox" alt="Checkbox" {% if item.active %} checked {% endif %} class="filter-checkbox" id="check-{{ title.lower() }}-{{ loop.index }}"/> - <label class="mylabel" for="check-{{ title.lower() }}-{{ loop.index }}"></label> - <a href="{{ href }}" title="{{ label if label != label_truncated else '' }}"> - <span>{{ label}}</span> + <ul class="{{ nav_class or 'unstyled nav nav-simple nav-facet' }}"> + {% for item in items %} + {% set href = h.remove_url_param(name, item.name, extras=extras, alternative_url=alternative_url) if + item.active else h.add_url_param(new_params={name: item.name}, extras=extras, alternative_url=alternative_url) + %} + {% set label = label_function(item) if label_function else item.display_name %} + {% if title=='Open-Data-Eigenschaften'%} + {% set label = _(label)%} + {% endif%} + {% set count = count_label(item['count']) if count_label else ('%d' % item['count']) %} + {% set label_truncated = h.truncate(label, 25) if not label_function else label %} + <li class="{{ nav_item_class or 'nav-item' }}"> + <div class="facet_label"> + {# TODO: checkbox-id vereinheitlichen (code-duplikation) und sicher gegen Titel mit Leerzeichen machen! #} + <input type="checkbox" + {% if item.active %} checked {% endif %} + class="filter-checkbox" + id="check-{{ title.lower() }}-{{ loop.index }}" + aria-hidden="true" + /> + <span + role="checkbox" + class="mylabel" + for="check-{{ title.lower() }}-{{ loop.index }}" + aria-labelledby="description-{{ title.lower() }}-{{ loop.index }}" + aria-checked={% if item.active %}"true"{% else %}"false"{% endif %} + > + </span> + {% if name=='groups' %} + <span class="group-icon-container" aria-hidden="true"> + <img src="/base/images/icon_kat_{{item.name}}.svg" alt=""/> + </span> + {% endif %} + + <label class="odsh-visible-label" for="check-{{ title.lower() }}-{{ loop.index }}"> + <a href="{{ href }}" title="{{ label if label != label_truncated else '' }}"> + <span id="description-{{ title.lower() }}-{{ loop.index }}">{{ label }}</span> </a> - </div> - <div class="facet_count"> - <span class="facet_count">{{ count }}</span> - </div> - </li> - {% endfor %} - </ul> + </label> + </div> + <div class="facet_count"> + <span class="facet_count">{{ count }}</span> + </div> + </li> + {% endfor %} + </ul> </nav> <div class="module-footer"> + {% if not name=='groups' %} {% if h.get_param_int('_%s_limit' % name) %} {% if h.odsh_has_more_facets(name) %} <a href="{{ h.remove_url_param('_%s_limit' % name, replace=0, extras=extras, alternative_url=alternative_url) }}" - class="read-more">> {{ _('Show More') }}</a> + class="read-more">> {{ _('Show More') }}</a> {% endif %} {% else %} <a href="{{ h.remove_url_param('_%s_limit' % name, extras=extras, alternative_url=alternative_url) }}" class="read-more">> {{ _('Show Less')}}</a> {% endif %} + {% endif %} </div> {% else %} diff --git a/ckanext/odsh/templates/snippets/home_breadcrumb_item.html b/ckanext/odsh/templates/snippets/home_breadcrumb_item.html index a14dcb74445f0a35d201232203c641343a0d676b..7631991a8f5af79049b34ee308827ade14c6a421 100644 --- a/ckanext/odsh/templates/snippets/home_breadcrumb_item.html +++ b/ckanext/odsh/templates/snippets/home_breadcrumb_item.html @@ -1,9 +1,7 @@ {# Used to insert the home icon into a breadcrumb #} <li class="home"> <a href="{{ h.url_for('home') }}"> - <img class='icon-home' src='/base/images/Icon-Home.png' alt='Ikon Home' /> - <span> - {{_('Home') }} - </span> + <img class='icon-home' src='/base/images/Icon-Home.png' alt='home icon' /> + <span> {{_('Home') }}</span> </a> </li> \ No newline at end of file diff --git a/ckanext/odsh/templates/snippets/organization.html b/ckanext/odsh/templates/snippets/organization.html index bbd506d3b94d35b1cbe17f2c40452801a0a77755..df7bda0041df1b7cc4f988683ee0d9dd98ae4bc7 100644 --- a/ckanext/odsh/templates/snippets/organization.html +++ b/ckanext/odsh/templates/snippets/organization.html @@ -15,7 +15,6 @@ Example: #} {% set truncate = truncate or 0 %} -{% set url = h.url_for(controller='organization', action='read', id=organization.name) %} {% block info %} <div class="module module-narrow module-shallow context-info"> @@ -25,17 +24,61 @@ Example: [{{ _('Deleted') }}] {% endif %} </h1> - {% endblock %} - <section class="module-content"> + {% endblock heading%} + <section id="section-org" class="module-content"> {% block inner %} {% block image %} <div class="image"> - <a href="{{ url }}"> - <img src="{{ organization.image_display_url or h.url_for_static('/base/images/placeholder-organization.png') }}" - width="200" alt="{{ organization.name }}" /> - </a> + <img src="{{ organization.image_display_url or h.url_for_static('/base/images/placeholder-organization.png') }}" + width="200" alt="{{ organization.name }}" /> </div> - {% endblock %} + + <div class="information-organisation"> + {% set address = h.tpsh_get_address_org(organization) %} + {% set street = address.get('street') %} + {% set location = address.get('location') %} + {% set mail = address.get('mail') %} + {% set telephone = address.get('telephone') %} + {% set web = address.get('web') %} + + {% if person %} + <p> + {{ person }} + </p> + {% endif %} {# person #} + + {% if street and location %} + <p>{{ street }}</p> + <p>{{ location }}</p> + {% endif %} {# street and location #} + + {% if mail %} + {% if package %} + {% set package_name = package.get('name') %} + {% set betreff = "Transparenzportal Dokument: "+ package_name%} + {% set body_mail = h.tpsh_get_body_mail(organization, package) %} + {% else %} + {% set betreff = "Transparenzportal" %} + {% endif %} {# package #} + <p> + <a href="mailto:{{ mail }}?subject={{betreff}}&body={{body_mail}}Viele Grüße">{{ mail }}</a> + </p> + {% endif %} {# mail #} + + {% if telephone %} + <p class="phone"> + {{ telephone }} + </p> + {% endif %} {# telephone #} + + {% if web %} + <p> + <a href="{{address.web}}">{{ address.web }}</a> + </p> + {% endif %} {# web #} + </div> + {% endblock image%} + {% if not hide_description %} {% block description %} {% if organization.description %} @@ -46,7 +89,7 @@ Example: {% else %} <p class="empty">{{ _('There is no description for this organization') }}</p> {% endif %} - {% endblock %} + {% endblock description %} {% endif %} {% if show_nums %} {% block nums %} @@ -56,18 +99,18 @@ Example: <dd>{{ h.SI_number_span(organization.num_followers) }}</dd> </dl> <dl> - <dt>{{ _('Datasets') }}</dt> + <dt>{{ _('Documents') }}</dt> <dd>{{ h.SI_number_span(organization.package_count) }}</dd> </dl> </div> - {% endblock %} + {% endblock nums%} {% block follow %} <div class="follow_button"> {{ h.follow_button('group', organization.id) }} </div> - {% endblock %} + {% endblock follow %} {% endif %} - {% endblock %} + {% endblock inner %} </section> </div> {% endblock %} \ No newline at end of file diff --git a/ckanext/odsh/templates/snippets/package_item.html b/ckanext/odsh/templates/snippets/package_item.html index d1a84c3e442076b49baef7108d0a385d6f9e613c..b0918d3e68682688b5e93514aa556d969b320f13 100644 --- a/ckanext/odsh/templates/snippets/package_item.html +++ b/ckanext/odsh/templates/snippets/package_item.html @@ -14,82 +14,104 @@ Example: #} {% set truncate = truncate or 180 %} -{% set truncate_title = truncate_title or 80 %} +{% set truncate_title = truncate_title or 180 %} {% set title = package.title or package.name %} {% set notes = h.markdown_extract(package.notes, extract_length=truncate) %} {% set license_title =package.license_title if package.license_title else '-'%} {% set licenseAttributionByText = h.odsh_extract_value_from_extras(package.extras,'licenseAttributionByText')%} {% set license_name=' ('+licenseAttributionByText +')' if licenseAttributionByText else ''%} {% set license = license_title + license_name %} -{% set temporal_start = h.odsh_extract_value_from_extras(package.extras,'temporal_start') %} -{% set start=h.odsh_render_datetime(temporal_start) if temporal_start else ''%} -{% set temporal_end = h.odsh_extract_value_from_extras(package.extras,'temporal_end') %} -{% set end=h.odsh_render_datetime(temporal_end) if temporal_end else ''%} -{% set timerange = start + '-' + end%} {% set org = package.organization.title or package.organization.name or '-' %} {% set stars = h.odsh_openness_score_dataset_html(package) %} {% set access_count=(package.tracking_summary.total if package.tracking_summary) or '-' %} -{% set timerange_label ='Zeitraum' %} -{% set access_count_label ='Aufrufe' %} {% set issued_extra = h.odsh_extract_value_from_extras(package.extras,'issued') %} -{% set issued = h.odsh_render_datetime(issued_extra) if issued_extra else h.odsh_render_datetime(package.metadata_created)%} -{% set categories = package.groups|map(attribute='display_name')|join(', ') if package.groups else '-' %} +{% set issued = h.odsh_render_datetime(issued_extra) if issued_extra else h.odsh_render_datetime(package.metadata_created)%} +{% set daterange = h.tpsh_get_daterange_prettified(package) %} +{% set language_of_package = h.tpsh_get_language_of_package(package) %} +{% set language_icon = h.get_language_icon(package) %} +{% set thumbnail = package.get('thumbnail') %} {% block package_item %} -<div class="container-fluid odsh-dataset-item"> - {% block content %} - <div class="dataset-content"> - {% block heading %} - <p>{{org}}</p> - <h3 class="dataset-heading"> - {% block heading_private %} - {% if package.is_new %} - <span class='label new-dataset-label'>{{ _('NEW') }}</span> - {% endif %} - {% if package.private %} - <span class="dataset-private label label-inverse"> - <i class="fa fa-lock"></i> - {{ _('Private') }} - </span> - {% endif %} - {% endblock %} - {% block heading_title %} - {{ h.link_to(h.truncate(title, truncate_title), h.url_for(controller='package', action='read', id=package.name),title=_('View {organization_name}').format(organization_name=title)) - }} - {% endblock %} - {% block heading_meta %} - {% if package.get('state', '').startswith('draft') %} - <span class="label label-info">{{ _('Draft') }}</span> - {% elif package.get('state', '').startswith('deleted') %} - <span class="label label-important">{{ _('Deleted') }}</span> - {% endif %} - {{ h.popular('recent views', package.tracking_summary.recent, min=10) if package.tracking_summary }} - </span> - {% endblock %} - </h3> - {% endblock %} - {% block banner %} - {% if banner %} - <span class="banner">{{ _('Popular') }}</span> - {% endif %} - {% endblock %} - {% block notes %} +<div class="odsh-dataset-item"> + {% block content %} + <div class="dataset-content"> + {% block heading %} + {%if daterange %} + <div class='package-info-pill'> + {{ daterange }} + </div> + {% endif %} + {% set spatial_text=h.odsh_get_spatial_text(package)%} + {% if spatial_text %} + <div class='package-info-pill'> + {{ spatial_text }} + </div> + {% endif %} + {% if language_of_package != 'Deutsch' and language_of_package %} + <div class='package-info-pill'> + <img src={{ language_icon }} /> + {{ language_of_package }} + </div> + {% endif %} + <p>{{ org }}</p> + <h3 class="dataset-heading"> + {% block heading_private %} + {% if package.is_new %} + <span class='label new-dataset-label'>{{ _('NEW') }}</span> + {% endif %} + {% if package.private %} + <span class="dataset-private label label-inverse"> + <i class="fa fa-lock"></i> + {{ _('Private') }} + </span> + {% endif %} + {% endblock heading_private %} + {% block heading_title %} + {{ h.link_to(h.truncate(title, truncate_title), h.url_for(controller='package', action='read', id=package.name),title=_('View {organization_name}').format(organization_name=title))}} + {% endblock heading_title %} + {% block heading_meta %} + {% if package.get('state', '').startswith('draft') %} + <span class="label label-info">{{ _('Draft') }}</span> + {% elif package.get('state', '').startswith('deleted') %} + <span class="label label-important">{{ _('Deleted') }}</span> + {% endif %} + {{ h.popular('recent views', package.tracking_summary.recent, min=10) if package.tracking_summary }} + {% endblock heading_meta %} + </h3> + {% endblock heading%} + {% block banner %} + {% if banner %} + <span class="banner">{{ _('Popular') }}</span> + {% endif %} + {% endblock banner%} - {%if start or end %} - <div class='package-info-pill'> - {{timerange}} - </div> - {% endif %} - {% set spatial_text=h.odsh_get_spatial_text(package)%} - {%if spatial_text %} - <div class='package-info-pill'> - {{ spatial_text }} + {% block notes %} + {% if package.groups|length %} + <p class='package-info-category'> + <span class='category-header'> + {% trans category_count=package.groups|length %} + category: + {% pluralize %} + categories: + {% endtrans %} + </span> + {% for category in package.groups %} + <span class='category-with-icon'> + <span class='category-icon-container' aria-hidden='true'> + <img src="/base/images/icon_kat_{{category.name}}.svg" alt=""/> + </span> + <span class="category-name"> + {{ category.display_name }} + </span> + </span> + {% endfor %} + </p> + {% endif %} + {% endblock notes %} </div> - {% endif %} - <p class='package-info-categorie'>{{ _('Kategorie') }}: {{ categories}} </p> - {% endblock %} - </div> - <div class='dataset-spacer'></div> + + <div class='dataset-spacer'></div> + <div class="dataset-meta"> {% block resources %} {% block resources_outer %} @@ -118,6 +140,7 @@ Example: {% endblock %} {% endblock %} </div> - {% endblock %} + + {% endblock content %} </div> -{% endblock %} +{% endblock package_item %} diff --git a/ckanext/odsh/templates/snippets/search_box.html b/ckanext/odsh/templates/snippets/search_box.html index 7599b722123d70f198501106356c5fdb49722c51..e5831c99d0e756541209e5fe61aa331bc961020e 100644 --- a/ckanext/odsh/templates/snippets/search_box.html +++ b/ckanext/odsh/templates/snippets/search_box.html @@ -1,8 +1,16 @@ <div class='search-box-container'> <div class='search-box'> - <div class="field"> - <input id="field-sitewide-search" class="search" type="text" value="{{c.q}}" name="q" placeholder="{{ _('Search dataset') }}" /> - <button class="btn-search" type="submit"> + <div class="field search-field"> + <input + aria-label="{{ _('enter search query for datasets') }}" + id="field-sitewide-search" + class="search" + type="text" + value="{{c.q}}" + name="q" + placeholder="{{ _('Search dataset') }}" + /> + <button class="btn-search" type="submit" aria-label="{{ _('Search dataset') }}"></button> </div> </div> </div> \ No newline at end of file diff --git a/ckanext/odsh/templates/snippets/search_form.html b/ckanext/odsh/templates/snippets/search_form.html index 52b85f3da7aa1625a11f32654bb1b700afe82f93..9e1ea9ed0bb4d3b4941c5f72d83dcbeca6e9521a 100644 --- a/ckanext/odsh/templates/snippets/search_form.html +++ b/ckanext/odsh/templates/snippets/search_form.html @@ -12,7 +12,11 @@ (_('Name Ascending'), 'title_string asc'), (_('Name Descending'), 'title_string desc'), (_('Issued Ascending'), 'extras_issued asc'), - (_('Issued Descending'), 'extras_issued desc'), + (_('Issued Descending'), 'extras_issued desc'), + (_('Start Date Ascending'), 'temporal_start asc'), + (_('Start Date Descending'), 'temporal_start desc'), + (_('End Date Ascending'), 'temporal_end asc'), + (_('End Date Descending'), 'temporal_end desc'), (_('Popular'), 'views_recent desc') if g.tracking_enabled else (false, false) ] %} {% endif %} @@ -31,10 +35,27 @@ {%- endif %} {% endblock %} + {% block search_title %} + {% if not error %} + {% if force_title %} + <h2>{{force_title }}</h2> + {% else %} + <h2>{% snippet 'snippets/search_result_text.html', query=query, count=count, type=type %}</h2> + {% if type == 'dataset' %} + <a aria-label="{{ _('rss feeds') }}" href={{h.remove_url_param([''], controller='feed', action='custom') }}> + <i aria-hidden="true" class="fa fa-medium fa-rss"></i> + </a> + {%- endif -%} + {% endif %} + {% else %} + <h2>Error</h2> + {% endif %} + {% endblock %} + {% block search_sortby %} {% if sorting %} <div class="form-select control-group control-order-by"> - <select id="field-order-by" name="sort"> + <select id="field-order-by" name="sort" aria-label="{{sorting_label_prefix}} …"> {% for label, value in sorting %} {% if label and value %} <option value="{{ value }}" {% if sorting_selected==value %} selected="selected" {% endif %}> @@ -49,18 +70,6 @@ {% endif %} {% endblock %} - {% block search_title %} - {% if not error %} - {% if force_title %} - <h2>{{force_title }}</h2> - {% else %} - <h2>{% snippet 'snippets/search_result_text.html', query=query, count=count, type=type %}</h2> - {% endif %} - {% else %} - <h2>Error</h2> - {% endif %} - {% endblock %} - {% block search_facets %} {% if facets %} <p class="filter-list"> @@ -78,12 +87,19 @@ {#{{_(value)}}#} {%- endif %} {%- endif %} - <a href="{{ facets.remove_field(field, value) }}" class="remove" title="{{ _('Remove') }}"><i class="fa fa-times"></i></a> + <a + href="{{ facets.remove_field(field, value) }}" + class="remove" + aria-label="{{ _('Remove filters') }}" + title="{{ _('Remove filters') }}" + > + <i class="fa fa-times" aria-hidden="true"></i> + </a> </span> {% endfor %} {% endfor %} </p> - <a class="show-filters btn">{{ _('Filter Results') }}</a> + <!-- <a class="show-filters btn">{{ _('Filter Results') }}</a> --> {% else%} {% if always_show_filter_list%} <p class="filter-list"></p> @@ -99,26 +115,54 @@ {% if start_date and not start_error%} <span class="filtered pill"> {{_('daterange')}}: {{_('from')}} {{ start_date}} - <a href="{{ h.remove_url_param('ext_startdate') }}" class="remove" title="{{ _('Remove') }}"><i class="fa fa-times"></i></a> + <a + href="{{ h.remove_url_param('ext_startdate') }}" + class="remove" + aria-label="{{ _('Remove filters') }}" + title="{{ _('Remove filters') }}" + > + <i class="fa fa-times" aria-hidden="true"></i> + </a> </span> {% endif %} {% if start_error %} <span class="filtered pill error"> {{_('daterange')}}: {{_('wrong_start_date_for_search')}} - <a href="{{ h.remove_url_param('ext_startdate') }}" class="remove" title="{{ _('Remove') }}"><i class="fa fa-times"></i></a> + <a + href="{{ h.remove_url_param('ext_startdate') }}" + class="remove" + aria-label="{{ _('Remove filters') }}" + title="{{ _('Remove filters') }}" + > + <i class="fa fa-times" aria-hidden="true"></i> + </a> </span> {% endif %} {% if end_date and not end_error%} <span class="filtered pill"> {{_('daterange')}}: {{_('to')}} {{ end_date}} - <a href="{{ h.remove_url_param('ext_enddate') }}" class="remove" title="{{ _('Remove') }}"><i class="fa fa-times"></i></a> + <a + href="{{ h.remove_url_param('ext_enddate') }}" + class="remove" + aria-label="{{ _('Remove filters') }}" + title="{{ _('Remove filters') }}" + > + <i class="fa fa-times" aria-hidden="true"></i> + </a> </span> {% endif %} {% if end_error %} <span class="filtered pill error"> {{_('daterange')}}: {{_('wrong_end_date_for_search')}} - <a href="{{ h.remove_url_param('ext_enddate') }}" class="remove" title="{{ _('Remove') }}"><i class="fa fa-times"></i></a> + <a + href="{{ h.remove_url_param('ext_enddate') }}" + class="remove" + aria-label="{{ _('Remove filters') }}" + title="{{ _('Remove filters') }}" + > + <i class="fa fa-times" aria-hidden="true"></i> + </a> </span> {% endif %} </p> @@ -132,7 +176,14 @@ <p class="filter-list"> <span class="filtered pill"> {{_('map search active')}} - <a href="{{ h.remove_url_param(['ext_bbox','ext_prev_extent', 'ext_location'])}}" class="remove" title="{{ _('Remove') }}"><i class="fa fa-times"></i></a> + <a + href="{{ h.remove_url_param(['ext_bbox','ext_prev_extent', 'ext_location'])}}" + class="remove" + aria-label="{{ _('Remove filters') }}" + title="{{ _('Remove filters') }}" + > + <i class="fa fa-times" aria-hidden="true"></i> + </a> </span> </p> {% endif %} diff --git a/ckanext/odsh/templates/snippets/search_result_text.html b/ckanext/odsh/templates/snippets/search_result_text.html index 56cf8470d4d6db7b993132f30f3fa32ebdff231d..cd44abe63cd5f3be1caa86928cedc31bbb0dd5e1 100644 --- a/ckanext/odsh/templates/snippets/search_result_text.html +++ b/ckanext/odsh/templates/snippets/search_result_text.html @@ -45,7 +45,4 @@ Example: {%- else -%} {{ text_no_query_none }} {%- endif -%} -{%- endif -%} -{% if type == 'dataset' %} -<a href={{h.remove_url_param([''], controller='feed', action='custom' )}}> <i class="fa fa-medium fa-rss"></i></a> {%- endif -%} \ No newline at end of file diff --git a/ckanext/odsh/templates/user/edit_user_form.html b/ckanext/odsh/templates/user/edit_user_form.html new file mode 100644 index 0000000000000000000000000000000000000000..9ceda7c7afc8e093149537317040f080d943afef --- /dev/null +++ b/ckanext/odsh/templates/user/edit_user_form.html @@ -0,0 +1,59 @@ +{% import 'macros/form.html' as form %} +{% resource 'odsh/tpsh_validate_password.js' %} + + +<form id="user-edit-form" class="dataset-form form-horizontal" method="post" action="{{ action }}"> + {{ form.errors(error_summary) }} + + <fieldset> + <legend>{{ _('Change details') }}</legend> + {{ form.input('name', label=_('Username'), id='field-username', value=data.name, error=errors.name, classes=['control-medium'], is_required=true) }} + + {{ form.input('fullname', label=_('Full name'), id='field-fullname', value=data.fullname, error=errors.fullname, placeholder=_('eg. Joe Bloggs'), classes=['control-medium']) }} + + {{ form.input('email', label=_('Email'), id='field-email', type='email', value=data.email, error=errors.email, placeholder=_('eg. joe@example.com'), classes=['control-medium'], is_required=true) }} + + {{ form.markdown('about', label=_('About'), id='field-about', value=data.about, error=errors.about, placeholder=_('A little information about yourself')) }} + + {% if c.show_email_notifications %} + {% call form.checkbox('activity_streams_email_notifications', label=_('Subscribe to notification emails'), id='field-activity-streams-email-notifications', value=True, checked=c.userobj.activity_streams_email_notifications) %} + {% set helper_text = _("You will receive notification emails from {site_title}, e.g. when you have new activities on your dashboard."|string) %} + {{ form.info(helper_text.format(site_title=g.site_title), classes=['info-help-tight']) }} + {% endcall %} + {% endif %} + + </fieldset> + + <fieldset> + <legend>{{ _('Change password') }}</legend> + {{ form.input('old_password', + type='password', + label=_('Sysadmin Password') if c.is_sysadmin else _('Old Password'), + id='field-password', + value=data.oldpassword, + error=errors.oldpassword, + classes=['control-medium'], + attrs={'autocomplete': 'off'} + ) }} + + {{ form.input( + 'password1', type='password', label=_('Password'), id='field-password', value=data.password1, error=errors.password1, classes=['control-medium'], attrs={'autocomplete': 'off', 'data-module': 'tpsh_validate_password'} ) }} + + {{ form.input('password2', type='password', label=_('Confirm Password'), id='field-password-confirm', value=data.password2, error=errors.password2, classes=['control-medium'], attrs={'autocomplete': 'off'}) }} + </fieldset> + + <div class="form-actions"> + {% block delete_button %} + {% if h.check_access('user_delete', {'id': data.id}) %} + <a class="btn btn-danger pull-left" href="{% url_for controller='user', action='delete', id=data.id %}" data-module="confirm-action" data-module-content="{{ _('Are you sure you want to delete this User?') }}">{% block delete_button_text %}{{ _('Delete') }}{% endblock %}</a> + {% endif %} + {% endblock %} + {% block generate_button %} + {% if h.check_access('user_generate_apikey', {'id': data.id}) %} + <a class="btn btn-warning" href="{% url_for controller='user', action='generate_apikey', id=data.id %}" data-module="confirm-action" data-module-content="{{ _('Are you sure you want to regenerate the API key?') }}">{% block generate_button_text %}{{ _('Regenerate API Key') }}{% endblock %}</a> + {% endif %} + {% endblock %} + {{ form.required_message() }} + <button class="btn btn-primary" type="submit" name="save">{{ _('Update Profile') }}</button> + </div> +</form> \ No newline at end of file diff --git a/ckanext/odsh/tests/test_validation.py b/ckanext/odsh/tests/test_validation.py index d631545ab34a2a0cd10094ab55ff22f7904970f6..b2edc40b15f6f2e6446a709c34706bb63ac3a3a2 100644 --- a/ckanext/odsh/tests/test_validation.py +++ b/ckanext/odsh/tests/test_validation.py @@ -110,3 +110,25 @@ def test_validate_licenseAttributionByText(): ('extras', 0, 'key'): 'licenseAttributionByText', ('extras', 0, 'value'): ''} validate_licenseAttributionByText('key', data, {}, None) + + +def test_convert_subjectID_to_subjectText(): + # arrange + data = {('extras', 0, 'subject'): 'subject', + ('extras', 0, 'subject'): 'Test_id'} + # act + convert_subjectID_to_subjectText('key', data, {}, None): + # assert + assert data[('extras', 1, 'key')] == 'subject_text' + assert data[('extras', 1, 'value')] == 'Test_subject' + + +def test_exception_convert_subjectID_to_subjectText(): + # arrange + data = {('extras', 0, 'subject'): 'subject', + ('extras', 0, 'subject'): 'Nicht_Vorhanden'} + # act + convert_subjectID_to_subjectText('key', data, {}, None): + #assert + assert data[('extras', 1, 'key')] == 'subject_text' + assert data[('extras', 1, 'value')] == '' diff --git a/ckanext/odsh/tests_tpsh/__init__.py b/ckanext/odsh/tests_tpsh/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ckanext/odsh/tests_tpsh/resources/__init__.py b/ckanext/odsh/tests_tpsh/resources/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ckanext/odsh/tests_tpsh/resources/collection1.rdf b/ckanext/odsh/tests_tpsh/resources/collection1.rdf new file mode 100644 index 0000000000000000000000000000000000000000..8ffd258536049afa2bd76a6a10196bf05b992386 --- /dev/null +++ b/ckanext/odsh/tests_tpsh/resources/collection1.rdf @@ -0,0 +1,474 @@ +<?xml version="1.0" encoding="utf-8"?> +<rdf:RDF xmlns:dcatde="http://dcat-ap.de/def/dcatde/1_0/" xmlns:dcat="http://www.w3.org/ns/dcat#" +xmlns:dct="http://purl.org/dc/terms/" xmlns:foaf="http://xmlns.com/foaf/0.1/" +xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:schema="http://schema.org/"> + <dcat:Catalog rdf:about="https://data.some.org/catalog"> + + <dcat:dataset> + <dcat:Dataset rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte"> + <dct:type rdf:resource="http://dcat-ap.de/def/datasetTypes/collection" /> + <dct:title>SER Statistik anerkannte Versorgungsberechtigte</dct:title> + <dct:description>Statistik der Versorgung von rentenberechtigten Kriegsopfern und gleichgestellten Personen nach Personenkreis und MdE innerhalb Schleswig-Holsteins</dct:description> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/SOCI" /> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dcat:keyword>Soziales Entschädigungsrecht</dcat:keyword> + <dcat:keyword>Kriegsopfer</dcat:keyword> + <dcat:keyword>Beschädigte</dcat:keyword> + <dcat:keyword>Witwen</dcat:keyword> + <dcat:keyword>Waisen</dcat:keyword> + <dcat:keyword>BVG</dcat:keyword> + <dcat:keyword>SVG</dcat:keyword> + <dcat:keyword>ZDG</dcat:keyword> + <dcat:keyword>OEG</dcat:keyword> + <dcat:keyword>IfSG</dcat:keyword> + <dcat:keyword>HHG</dcat:keyword> + <dcat:keyword>SER</dcat:keyword> + <dcat:keyword>Statistik</dcat:keyword> + <dcat:keyword>Versorgungsberechtigte</dcat:keyword> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + + <dct:hasVersion rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201704" /> + <dct:hasVersion rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201703" /> + <dct:hasVersion rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201702" /> + <dct:hasVersion rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201701" /> + <dct:hasVersion rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201612" /> + <dct:hasVersion rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_Antrag_Jahr_2017" /> + <dct:hasVersion rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_Antrag_Jahr_2016" /> + <dct:hasVersion rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_Antrag_Jahr_2015" /> + </dcat:Dataset> + </dcat:dataset> + + <dcat:dataset> + <dcat:Dataset rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201704"> + <dct:isVersionOf rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte" /> + <dct:title>SER Statistik anerkannte Versorgungsberechtigte</dct:title> + <dcat:distribution> + <dcat:Distribution rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201704/resource"> + <dcat:accessURL rdf:resource="http://185.223.104.6/data/lasd/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201704.pdf" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/SOCI" /> + <dct:description>Statistik der Versorgung von rentenberechtigten Kriegsopfern und gleichgestellten Personen nach Personenkreis und MdE innerhalb Schleswig-Holsteins</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-03-20</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:publisher> + <foaf:Organization rdf:about="http://opendata.schleswig-holstein.de/lasd" /> + </dct:publisher> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-04-01</schema:startDate> + <schema:endDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-04-30</schema:endDate> + </dct:PeriodOfTime> + </dct:temporal> + <dcat:keyword>Soziales Entschädigungsrecht</dcat:keyword> + <dcat:keyword>Kriegsopfer</dcat:keyword> + <dcat:keyword>Beschädigte</dcat:keyword> + <dcat:keyword>Witwen</dcat:keyword> + <dcat:keyword>Waisen</dcat:keyword> + <dcat:keyword>BVG</dcat:keyword> + <dcat:keyword>SVG</dcat:keyword> + <dcat:keyword>ZDG</dcat:keyword> + <dcat:keyword>OEG</dcat:keyword> + <dcat:keyword>IfSG</dcat:keyword> + <dcat:keyword>HHG</dcat:keyword> + <dcat:keyword>SER</dcat:keyword> + <dcat:keyword>Statistik</dcat:keyword> + <dcat:keyword>Versorgungsberechtigte</dcat:keyword> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201703"> + <dct:isVersionOf rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte" /> + <dct:title>SER Statistik anerkannte Versorgungsberechtigte</dct:title> + <dcat:distribution> + <dcat:Distribution rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201703/resource"> + <dcat:accessURL rdf:resource="http://185.223.104.6/data/lasd/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201703.pdf" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/SOCI" /> + <dct:description>Statistik der Versorgung von rentenberechtigten Kriegsopfern und gleichgestellten Personen nach + Personenkreis und MdE innerhalb Schleswig-Holsteins</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-02-20</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:publisher> + <foaf:Organization rdf:about="http://opendata.schleswig-holstein.de/lasd" /> + </dct:publisher> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-03-01</schema:startDate> + <schema:endDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-03-31</schema:endDate> + </dct:PeriodOfTime> + </dct:temporal> + <dcat:keyword>Soziales Entschädigungsrecht</dcat:keyword> + <dcat:keyword>Kriegsopfer</dcat:keyword> + <dcat:keyword>Beschädigte</dcat:keyword> + <dcat:keyword>Witwen</dcat:keyword> + <dcat:keyword>Waisen</dcat:keyword> + <dcat:keyword>BVG</dcat:keyword> + <dcat:keyword>SVG</dcat:keyword> + <dcat:keyword>ZDG</dcat:keyword> + <dcat:keyword>OEG</dcat:keyword> + <dcat:keyword>IfSG</dcat:keyword> + <dcat:keyword>HHG</dcat:keyword> + <dcat:keyword>SER</dcat:keyword> + <dcat:keyword>Statistik</dcat:keyword> + <dcat:keyword>Versorgungsberechtigte</dcat:keyword> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201702"> + <dct:isVersionOf rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte" /> + <dct:title>SER Statistik anerkannte Versorgungsberechtigte</dct:title> + <dcat:distribution> + <dcat:Distribution rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201702/resource"> + <dcat:accessURL rdf:resource="http://185.223.104.6/data/lasd/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201702.pdf" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/SOCI" /> + <dct:description>Statistik der Versorgung von rentenberechtigten Kriegsopfern und gleichgestellten Personen nach + Personenkreis und MdE innerhalb Schleswig-Holsteins</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-01-16</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:publisher> + <foaf:Organization rdf:about="http://opendata.schleswig-holstein.de/lasd" /> + </dct:publisher> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-02-01</schema:startDate> + <schema:endDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-02-28</schema:endDate> + </dct:PeriodOfTime> + </dct:temporal> + <dcat:keyword>Soziales Entschädigungsrecht</dcat:keyword> + <dcat:keyword>Kriegsopfer</dcat:keyword> + <dcat:keyword>Beschädigte</dcat:keyword> + <dcat:keyword>Witwen</dcat:keyword> + <dcat:keyword>Waisen</dcat:keyword> + <dcat:keyword>BVG</dcat:keyword> + <dcat:keyword>SVG</dcat:keyword> + <dcat:keyword>ZDG</dcat:keyword> + <dcat:keyword>OEG</dcat:keyword> + <dcat:keyword>IfSG</dcat:keyword> + <dcat:keyword>HHG</dcat:keyword> + <dcat:keyword>SER</dcat:keyword> + <dcat:keyword>Statistik</dcat:keyword> + <dcat:keyword>Versorgungsberechtigte</dcat:keyword> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201701"> + <dct:isVersionOf rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte" /> + <dct:title>SER Statistik anerkannte Versorgungsberechtigte</dct:title> + <dcat:distribution> + <dcat:Distribution rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201701/resource"> + <dcat:accessURL rdf:resource="http://185.223.104.6/data/lasd/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201701.pdf" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/SOCI" /> + <dct:description>Statistik der Versorgung von rentenberechtigten Kriegsopfern und gleichgestellten Personen nach + Personenkreis und MdE innerhalb Schleswig-Holsteins</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-12-12</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:publisher> + <foaf:Organization rdf:about="http://opendata.schleswig-holstein.de/lasd" /> + </dct:publisher> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-01-01</schema:startDate> + <schema:endDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-01-31</schema:endDate> + </dct:PeriodOfTime> + </dct:temporal> + <dcat:keyword>Soziales Entschädigungsrecht</dcat:keyword> + <dcat:keyword>Kriegsopfer</dcat:keyword> + <dcat:keyword>Beschädigte</dcat:keyword> + <dcat:keyword>Witwen</dcat:keyword> + <dcat:keyword>Waisen</dcat:keyword> + <dcat:keyword>BVG</dcat:keyword> + <dcat:keyword>SVG</dcat:keyword> + <dcat:keyword>ZDG</dcat:keyword> + <dcat:keyword>OEG</dcat:keyword> + <dcat:keyword>IfSG</dcat:keyword> + <dcat:keyword>HHG</dcat:keyword> + <dcat:keyword>SER</dcat:keyword> + <dcat:keyword>Statistik</dcat:keyword> + <dcat:keyword>Versorgungsberechtigte</dcat:keyword> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201612"> + <dct:isVersionOf rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte" /> + <dct:title>SER Statistik anerkannte Versorgungsberechtigte</dct:title> + <dcat:distribution> + <dcat:Distribution rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201612/resource"> + <dcat:accessURL rdf:resource="http://185.223.104.6/data/lasd/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201612.pdf" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/SOCI" /> + <dct:description>Statistik der Versorgung von rentenberechtigten Kriegsopfern und gleichgestellten Personen nach + Personenkreis und MdE innerhalb Schleswig-Holsteins</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-11-21</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:publisher> + <foaf:Organization rdf:about="http://opendata.schleswig-holstein.de/lasd" /> + </dct:publisher> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-12-01</schema:startDate> + <schema:endDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-12-31</schema:endDate> + </dct:PeriodOfTime> + </dct:temporal> + <dcat:keyword>Soziales Entschädigungsrecht</dcat:keyword> + <dcat:keyword>Kriegsopfer</dcat:keyword> + <dcat:keyword>Beschädigte</dcat:keyword> + <dcat:keyword>Witwen</dcat:keyword> + <dcat:keyword>Waisen</dcat:keyword> + <dcat:keyword>BVG</dcat:keyword> + <dcat:keyword>SVG</dcat:keyword> + <dcat:keyword>ZDG</dcat:keyword> + <dcat:keyword>OEG</dcat:keyword> + <dcat:keyword>IfSG</dcat:keyword> + <dcat:keyword>HHG</dcat:keyword> + <dcat:keyword>SER</dcat:keyword> + <dcat:keyword>Statistik</dcat:keyword> + <dcat:keyword>Versorgungsberechtigte</dcat:keyword> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_Antrag_Jahr_2017"> + <dct:isVersionOf rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte" /> + <dct:title>SER Statistik anerkannte Versorgungsberechtigte</dct:title> + <dcat:distribution> + <dcat:Distribution rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_Antrag_Jahr_2017/resource"> + <dcat:accessURL rdf:resource="http://185.223.104.6/data/lasd/LAsDSH_SER_Statistik_Antrag_Jahr_2017.pdf" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/SOCI" /> + <dct:description>Statistik der Versorgung von rentenberechtigten Kriegsopfern und gleichgestellten Personen nach + Personenkreis und MdE innerhalb Schleswig-Holsteins</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-12-31</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:publisher> + <foaf:Organization rdf:about="http://opendata.schleswig-holstein.de/lasd" /> + </dct:publisher> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-01-01</schema:startDate> + <schema:endDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-12-31</schema:endDate> + </dct:PeriodOfTime> + </dct:temporal> + <dcat:keyword>Soziales Entschädigungsrecht</dcat:keyword> + <dcat:keyword>Kriegsopfer</dcat:keyword> + <dcat:keyword>Beschädigte</dcat:keyword> + <dcat:keyword>Witwen</dcat:keyword> + <dcat:keyword>Waisen</dcat:keyword> + <dcat:keyword>BVG</dcat:keyword> + <dcat:keyword>SVG</dcat:keyword> + <dcat:keyword>ZDG</dcat:keyword> + <dcat:keyword>OEG</dcat:keyword> + <dcat:keyword>IfSG</dcat:keyword> + <dcat:keyword>HHG</dcat:keyword> + <dcat:keyword>SER</dcat:keyword> + <dcat:keyword>Statistik</dcat:keyword> + <dcat:keyword>Versorgungsberechtigte</dcat:keyword> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_Antrag_Jahr_2016"> + <dct:isVersionOf rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte" /> + <dct:title>SER Statistik anerkannte Versorgungsberechtigte</dct:title> + <dcat:distribution> + <dcat:Distribution rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_Antrag_Jahr_2016/resource"> + <dcat:accessURL rdf:resource="http://185.223.104.6/data/lasd/LAsDSH_SER_Statistik_Antrag_Jahr_2016.pdf" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/SOCI" /> + <dct:description>Statistik der Versorgung von rentenberechtigten Kriegsopfern und gleichgestellten Personen nach + Personenkreis und MdE innerhalb Schleswig-Holsteins</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-12-31</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:publisher> + <foaf:Organization rdf:about="http://opendata.schleswig-holstein.de/lasd" /> + </dct:publisher> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-01-01</schema:startDate> + <schema:endDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-12-31</schema:endDate> + </dct:PeriodOfTime> + </dct:temporal> + <dcat:keyword>Soziales Entschädigungsrecht</dcat:keyword> + <dcat:keyword>Kriegsopfer</dcat:keyword> + <dcat:keyword>Beschädigte</dcat:keyword> + <dcat:keyword>Witwen</dcat:keyword> + <dcat:keyword>Waisen</dcat:keyword> + <dcat:keyword>BVG</dcat:keyword> + <dcat:keyword>SVG</dcat:keyword> + <dcat:keyword>ZDG</dcat:keyword> + <dcat:keyword>OEG</dcat:keyword> + <dcat:keyword>IfSG</dcat:keyword> + <dcat:keyword>HHG</dcat:keyword> + <dcat:keyword>SER</dcat:keyword> + <dcat:keyword>Statistik</dcat:keyword> + <dcat:keyword>Versorgungsberechtigte</dcat:keyword> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_Antrag_Jahr_2015"> + <dct:isVersionOf rdf:resource="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte" /> + <dct:title>SER Statistik anerkannte Versorgungsberechtigte</dct:title> + <dcat:distribution> + <dcat:Distribution rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_Antrag_Jahr_2015/resource"> + <dcat:accessURL rdf:resource="http://185.223.104.6/data/lasd/LAsDSH_SER_Statistik_Antrag_Jahr_2015.pdf" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/SOCI" /> + <dct:description>Statistik der Versorgung von rentenberechtigten Kriegsopfern und gleichgestellten Personen nach + Personenkreis und MdE innerhalb Schleswig-Holsteins</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-31</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:publisher> + <foaf:Organization rdf:about="http://opendata.schleswig-holstein.de/lasd" /> + </dct:publisher> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-01-01</schema:startDate> + <schema:endDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-31</schema:endDate> + </dct:PeriodOfTime> + </dct:temporal> + <dcat:keyword>Soziales Entschädigungsrecht</dcat:keyword> + <dcat:keyword>Kriegsopfer</dcat:keyword> + <dcat:keyword>Beschädigte</dcat:keyword> + <dcat:keyword>Witwen</dcat:keyword> + <dcat:keyword>Waisen</dcat:keyword> + <dcat:keyword>BVG</dcat:keyword> + <dcat:keyword>SVG</dcat:keyword> + <dcat:keyword>ZDG</dcat:keyword> + <dcat:keyword>OEG</dcat:keyword> + <dcat:keyword>IfSG</dcat:keyword> + <dcat:keyword>HHG</dcat:keyword> + <dcat:keyword>SER</dcat:keyword> + <dcat:keyword>Statistik</dcat:keyword> + <dcat:keyword>Versorgungsberechtigte</dcat:keyword> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://opendata.schleswig-holstein.de/dataset/Test_nicht_in_Collection"> + <dct:title>SER Statistik anerkannte Versorgungsberechtigte</dct:title> + <dcat:distribution> + <dcat:Distribution rdf:about="http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_Antrag_Jahr_2015/resource"> + <dcat:accessURL rdf:resource="http://185.223.104.6/data/lasd/LAsDSH_SER_Statistik_Antrag_Jahr_2015.pdf" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/SOCI" /> + <dct:description>Statistik der Versorgung von rentenberechtigten Kriegsopfern und gleichgestellten Personen nach + Personenkreis und MdE innerhalb Schleswig-Holsteins</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-31</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/dl-by-de/2.0" /> + <dcatde:licenseAttributionByText>Landesamt für soziale Dienste Schleswig-Holstein</dcatde:licenseAttributionByText> + <dct:publisher> + <foaf:Organization rdf:about="http://opendata.schleswig-holstein.de/lasd" /> + </dct:publisher> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-01-01</schema:startDate> + <schema:endDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-31</schema:endDate> + </dct:PeriodOfTime> + </dct:temporal> + <dcat:keyword>Soziales Entschädigungsrecht</dcat:keyword> + <dcat:keyword>Kriegsopfer</dcat:keyword> + <dcat:keyword>Beschädigte</dcat:keyword> + <dcat:keyword>Witwen</dcat:keyword> + <dcat:keyword>Waisen</dcat:keyword> + <dcat:keyword>BVG</dcat:keyword> + <dcat:keyword>SVG</dcat:keyword> + <dcat:keyword>ZDG</dcat:keyword> + <dcat:keyword>OEG</dcat:keyword> + <dcat:keyword>IfSG</dcat:keyword> + <dcat:keyword>HHG</dcat:keyword> + <dcat:keyword>SER</dcat:keyword> + <dcat:keyword>Statistik</dcat:keyword> + <dcat:keyword>Versorgungsberechtigte</dcat:keyword> + </dcat:Dataset> + </dcat:dataset> + </dcat:Catalog> +</rdf:RDF> diff --git a/ckanext/odsh/tests_tpsh/resources/org_dicts.py b/ckanext/odsh/tests_tpsh/resources/org_dicts.py new file mode 100644 index 0000000000000000000000000000000000000000..5948d7658b0e626459a3cef62392c124f8b3f723 --- /dev/null +++ b/ckanext/odsh/tests_tpsh/resources/org_dicts.py @@ -0,0 +1,87 @@ +# encoding: utf-8 + +organization_with_address = { + 'approval_status': u'approved', + 'created': '2019-07-29T08:11:32.697127', + 'description': u'', + 'display_name': u'Test-Organisation', + 'extras': [ + { + 'group_id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + 'id': u'b31c1b27-8c4e-46d7-b533-30bc4cc93d0e', + 'key': u'location', + 'revision_id': u'5e9677a3-ddab-4306-850f-cfd4fad3d676', + 'state': u'active', + 'value': u'Müllerdorf' + }, + { + 'group_id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + 'id': u'7302c660-871e-44f0-92f4-18bc5b8953ec', + 'key': u'mail', + 'revision_id': u'5e9677a3-ddab-4306-850f-cfd4fad3d676', + 'state': u'active', + 'value': u'mueller@mueller.de' + }, + { + 'group_id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + 'id': u'6d3e02ba-b721-4362-92f3-985ed50f6c42', + 'key': u'person', + 'revision_id': u'5e9677a3-ddab-4306-850f-cfd4fad3d676', + 'state': u'active', + 'value': u'Michael Müller' + }, + { + 'group_id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + 'id': u'baf92ac2-21e2-49f5-96f8-e48c7fd50cf0', + 'key': u'street', + 'revision_id': u'5e9677a3-ddab-4306-850f-cfd4fad3d676', + 'state': u'active', + 'value': u'Müllergasse 10' + }, + { + 'group_id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + 'id': u'd5ce2972-487e-444b-95ba-c6a8db238c67', + 'key': u'telephone', + 'revision_id': u'5e9677a3-ddab-4306-850f-cfd4fad3d676', + 'state': u'active', + 'value': u'040 123456' + }, + { + 'group_id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + 'id': u'00d1d308-6718-4d90-81ae-25ee3f0b76f7', + 'key': u'web', + 'revision_id': u'5e9677a3-ddab-4306-850f-cfd4fad3d676', + 'state': u'active', + 'value': u'mueller.de' + } + ], + 'groups': [], + 'id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b', + 'image_display_url': u'', + 'image_url': u'', + 'is_organization': True, + 'name': u'test-organisation', + 'num_followers': 0L, + 'package_count': 51, + 'revision_id': u'3040af6c-d3f6-462d-b48a-329d63e17a28', + 'state': u'active', + 'tags': [], + 'title': u'Test-Organisation', + 'type': u'organization', + 'users': [{ + 'about': u'', + 'activity_streams_email_notifications': False, + 'capacity': u'admin', + 'created': '2019-06-05T06:19:17.576291', + 'display_name': u'ckanuser', + 'email_hash': 'b28744113bd1ae8dc4f6e17aefe3bd9b', + 'fullname': u'', + 'id': u'ff8d002d-3908-45e5-ba7b-445381860957', + 'name': u'ckanuser', + 'number_created_packages': 13L, + 'number_of_edits': 456L, + 'openid': None, + 'state': u'active', + 'sysadmin': True + }] + } \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/resources/subject_mapping_for_tests.json b/ckanext/odsh/tests_tpsh/resources/subject_mapping_for_tests.json new file mode 100644 index 0000000000000000000000000000000000000000..2e8bd946d9237ed177f8d3759defc4732d2f8ddf --- /dev/null +++ b/ckanext/odsh/tests_tpsh/resources/subject_mapping_for_tests.json @@ -0,0 +1,26 @@ +{ + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Verwaltungsvorschrift" : "Verwaltungsvorschrift", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Organisationsplan" : "Organisationsplan", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Geschaeftsverteilungsplan" : "Geschäftsverteilungsplan", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Aktenplan" : "Aktenplan", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Richtlinie" : "Richtlinie und Runderlass", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Statistik" : "amtliche Statistik", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Taetigkeitsbericht" : "Tätigkeitsbericht", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Broschuere" : "Broschüre", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Gutachten" : "Gutachten", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Studie" : "Studie", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Haushaltsplan" : "Haushaltsplan", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Stellenplan" : "Stellenplan", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Wirtschaftsplan" : "Wirtschaftsplan", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#ZuwendungAnPerson" : "Übersicht über Zuwendungen an juristische Personen des Privatrechts", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#ZuwendungAnLand" : "Übersicht über Zuwendungen an das Land Schleswig-Holstein", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#IZGAntwort" : "IZG/GvgV-Auskunft", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Gerichtsurteil" : "Gerichtsurteil", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#GesetzvorlageLandtag" : "Gesetzesvorlage an den Landtag", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#MitteilungLandtag" : "Mitteilung an den Landtag", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Unternehmensdaten" : "wesentliche Unternehmensdaten von Beteiligungen des Landes", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#VergütungsOG" : "jährliche Vergütungen nach dem VergütungsOG", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Vertrag" : "Vertrag", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#sonstiges" : "sonstiges" +} + \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/resources/test.pdf b/ckanext/odsh/tests_tpsh/resources/test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..10451cf0fba2fea7ac87ae52cc9b350b48c67198 Binary files /dev/null and b/ckanext/odsh/tests_tpsh/resources/test.pdf differ diff --git a/ckanext/odsh/tests_tpsh/resources/transparenz.rdf b/ckanext/odsh/tests_tpsh/resources/transparenz.rdf new file mode 100644 index 0000000000000000000000000000000000000000..ea4fb5e3c229d3e01feb3e4bb562f644cb8017da --- /dev/null +++ b/ckanext/odsh/tests_tpsh/resources/transparenz.rdf @@ -0,0 +1,1145 @@ +<?xml version="1.0" encoding="utf-8"?> +<rdf:RDF xmlns:dcatde="http://dcat-ap.de/def/dcatde/1_0/" xmlns:dcat="http://www.w3.org/ns/dcat#" xmlns:dct="http://purl.org/dc/terms/" +xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:schema="http://schema.org/"> + <dcat:Catalog rdf:about="https://transparenz.schleswig-holstein.de/catalog"> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/9f9ae60bb0d8985e10e9ab8aa6a7ca34"> + <dct:title>Munitionsbelastung der deutschen Meeresgewässer – Entwicklungen und Fortschritt (Jahr 2017)</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5768d5cdbc58abd45c8620e44e017f01" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/UXO/Berichte/PDF/Berichte/af_blano_fortschritt2017.pdf?__blob=publicationFile&v=5"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/UXO/Berichte/PDF/Berichte/af_blano_fortschritt2017.pdf?__blob=publicationFile&v=5" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Munitionsbelastung der deutschen Meeresgewässer – Entwicklungen und Fortschritt (Jahr 2017)</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-05-02</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-05-02</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/ff612d0f165d46f3091f58e1ef56a2ec"> + <dct:title>Munitionsbelastung der deutschen Meeresgewässer – Entwicklungen und Fortschritt (Jahr 2016)</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5768d5cdbc58abd45c8620e44e017f01" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/UXO/Berichte/PDF/Berichte/ae_blano_fortschritt2016.pdf?__blob=publicationFile&v=6"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/UXO/Berichte/PDF/Berichte/ae_blano_fortschritt2016.pdf?__blob=publicationFile&v=6" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Munitionsbelastung der deutschen Meeresgewässer – Entwicklungen und Fortschritt (Jahr 2016)</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-04-10</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-04-10</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/3a0d0674120fbf06b5cb8737124e3fd0"> + <dct:title>Munitionsbelastung der deutschen Meeresgewässer – Entwicklungen und Fortschritt (Jahr 2015)</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5768d5cdbc58abd45c8620e44e017f01" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/UXO/Berichte/PDF/Berichte/ad_blano_fortschritt2015.pdf?__blob=publicationFile&v=8"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/UXO/Berichte/PDF/Berichte/ad_blano_fortschritt2015.pdf?__blob=publicationFile&v=8" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Munitionsbelastung der deutschen Meeresgewässer – Entwicklungen und Fortschritt (Jahr 2015)</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-07-10</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-07-10</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/b5cd3f303f594ecde96e1017e953b688"> + <dct:title>Munitionsbelastung der deutschen Meeresgewässer – Entwicklungen und Fortschritt (Jahr 2014)</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5768d5cdbc58abd45c8620e44e017f01" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/UXO/Berichte/PDF/Berichte/ad_blano_fortschritt2014.pdf?__blob=publicationFile&v=4"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/UXO/Berichte/PDF/Berichte/ad_blano_fortschritt2014.pdf?__blob=publicationFile&v=4" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Munitionsbelastung der deutschen Meeresgewässer – Entwicklungen und Fortschritt (Jahr 2014)</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-07-17</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-07-17</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/cd606b042789723a2b6d61cb31c46c39"> + <dct:title>Munitionsbelastung der deutschen Meeresgewässer – Entwicklungen und Fortschritt (Jahr 2013)</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5768d5cdbc58abd45c8620e44e017f01" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/UXO/Berichte/PDF/Berichte/ac_blano_fortschritt2013.pdf?__blob=publicationFile&v=1"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/UXO/Berichte/PDF/Berichte/ac_blano_fortschritt2013.pdf?__blob=publicationFile&v=1" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Munitionsbelastung der deutschen Meeresgewässer – Entwicklungen und Fortschritt (Jahr 2013)</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2014-07-26</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2014-07-26</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/04640ac19a21450984d2af7c402d6aa0"> + <dct:title>Schuleingangsuntersuchungen in Schleswig-Holstein 2006 - Bericht</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/f1f2513c3899ecf6f15691e0e4412822" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2006.pdf?__blob=publicationFile&v=6"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2006.pdf?__blob=publicationFile&v=6" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Schuleingangsuntersuchungen in Schleswig-Holstein 2006 - Bericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-29</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-29</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/86fe10eb6578e940d7df8e3d6da6520d"> + <dct:title>Schuleingangsuntersuchungen in Schleswig-Holstein 2007 - Bericht</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/f1f2513c3899ecf6f15691e0e4412822" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2007.pdf?__blob=publicationFile&v=6"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2007.pdf?__blob=publicationFile&v=6" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Schuleingangsuntersuchungen in Schleswig-Holstein 2007 - Bericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-29</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-29</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/b0024265f96b9de5a590541f5e0aec91"> + <dct:title>Schuleingangsuntersuchungen in Schleswig-Holstein 2008 - Bericht</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/f1f2513c3899ecf6f15691e0e4412822" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2008.pdf?__blob=publicationFile&v=6"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2008.pdf?__blob=publicationFile&v=6" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Schuleingangsuntersuchungen in Schleswig-Holstein 2008 - Bericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-29</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-29</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/ccb65ee5047e3c9cc67384b513400246"> + <dct:title>Schuleingangsuntersuchungen in Schleswig-Holstein 2009 - Bericht</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/f1f2513c3899ecf6f15691e0e4412822" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2009.pdf?__blob=publicationFile&v=6"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2009.pdf?__blob=publicationFile&v=6" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Schuleingangsuntersuchungen in Schleswig-Holstein 2009 - Bericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-29</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-29</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/ae2a3cffda84388365bc87711ed4af47"> + <dct:title>Schuleingangsuntersuchungen in Schleswig-Holstein 2010 - Bericht</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/f1f2513c3899ecf6f15691e0e4412822" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2010.pdf?__blob=publicationFile&v=6"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2010.pdf?__blob=publicationFile&v=6" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Schuleingangsuntersuchungen in Schleswig-Holstein 2010 - Bericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-29</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-29</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/91a479991245f208dd6f8ba82411a570"> + <dct:title>Schuleingangsuntersuchungen in Schleswig-Holstein 2011 - Bericht</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/f1f2513c3899ecf6f15691e0e4412822" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2011.pdf?__blob=publicationFile&v=6"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2011.pdf?__blob=publicationFile&v=6" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Schuleingangsuntersuchungen in Schleswig-Holstein 2011 - Bericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-28</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-28</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/1ee93d311ae6078dc11655c2ca8865b5"> + <dct:title>Schuleingangsuntersuchungen in Schleswig-Holstein 2012 - Bericht</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/f1f2513c3899ecf6f15691e0e4412822" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2012.pdf?__blob=publicationFile&v=6"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2012.pdf?__blob=publicationFile&v=6" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Schuleingangsuntersuchungen in Schleswig-Holstein 2012 - Bericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-28</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-28</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/f5123967a8d0d80ff135342ce41a09bb"> + <dct:title>Schuleingangsuntersuchungen in Schleswig-Holstein 2013 - Bericht</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/f1f2513c3899ecf6f15691e0e4412822" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2013.pdf?__blob=publicationFile&v=5"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2013.pdf?__blob=publicationFile&v=5" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Schuleingangsuntersuchungen in Schleswig-Holstein 2013 - Bericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-28</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-28</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/05199f38b6234e794f79ad3726957a3e"> + <dct:title>Schuleingangsuntersuchungen in Schleswig-Holstein 2014/15 - Bericht</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/f1f2513c3899ecf6f15691e0e4412822" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2015.pdf?__blob=publicationFile&v=3"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2015.pdf?__blob=publicationFile&v=3" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Schuleingangsuntersuchungen in Schleswig-Holstein 2014/15 - Bericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-07-14</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-07-14</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/ffef477b7ebb1447d869b33c79cac8e2"> + <dct:title>Schuleingangsuntersuchungen in Schleswig-Holstein 2016/17 - Bericht</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/f1f2513c3899ecf6f15691e0e4412822" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2017.pdf?__blob=publicationFile&v=3"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2017.pdf?__blob=publicationFile&v=3" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Schuleingangsuntersuchungen in Schleswig-Holstein 2016/17 - Bericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-08-01</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-08-01</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/a1fea922aa7d9f7a924784615301da83"> + <dct:title>Schuleingangsuntersuchungen in Schleswig-Holstein 2015/16 – Bericht</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/f1f2513c3899ecf6f15691e0e4412822" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2016.pdf?__blob=publicationFile&v=2"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/VIII/Service/Broschueren/Broschueren_VIII/Gesundheit/schuleinguntber2016.pdf?__blob=publicationFile&v=2" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Schuleingangsuntersuchungen in Schleswig-Holstein 2015/16 – Bericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-11-27</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-11-27</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/423e1ea7e98b492aebc0ca5108c7b36d"> + <dct:title>Jahresbericht 2017 zur biologischen Vielfalt - Jagd und Artenschutz</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/7ee94f73beb43faf4099208b4e55734b" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/V/Service/Broschueren/Broschueren_V/Umwelt/pdf/Jahresbericht_Zur_biologischen_Vielfalt_2017.pdf?__blob=publicationFile&v=3"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/V/Service/Broschueren/Broschueren_V/Umwelt/pdf/Jahresbericht_Zur_biologischen_Vielfalt_2017.pdf?__blob=publicationFile&v=3" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Jahresbericht 2017 zur biologischen Vielfalt - Jagd und Artenschutz</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-01-16</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-01-16</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/6ea87d1f4a92efb288ed53e2bf901880"> + <dct:title>Jahresbericht 2018 zur biologischen Vielfalt - Jagd und Artenschutz</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/7ee94f73beb43faf4099208b4e55734b" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/V/Service/Broschueren/Broschueren_V/Umwelt/pdf/Jahresbericht_zur_biologischen_Vielfalt_2018.pdf?__blob=publicationFile&v=2"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/V/Service/Broschueren/Broschueren_V/Umwelt/pdf/Jahresbericht_zur_biologischen_Vielfalt_2018.pdf?__blob=publicationFile&v=2" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Jahresbericht 2018 zur biologischen Vielfalt - Jagd und Artenschutz</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-12-16</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-12-16</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/7f6ef434153bdbfff9ed42c8d9ee1381"> + <dct:title>Jahresbericht 2016 zur biologischen Vielfalt – Jagd- und Artenschutz</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/7ee94f73beb43faf4099208b4e55734b" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Landesregierung/V/Service/Broschueren/Broschueren_V/Umwelt/pdf/biodiversitaetsbericht2016.pdf?__blob=publicationFile&v=3"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Landesregierung/V/Service/Broschueren/Broschueren_V/Umwelt/pdf/biodiversitaetsbericht2016.pdf?__blob=publicationFile&v=3" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Jahresbericht 2016 zur biologischen Vielfalt – Jagd- und Artenschutz</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-12-21</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-12-21</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/f681f52201dcf1b1ea467aeed683c8a2"> + <dct:title>Waldzustandsbericht 2018</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2018.pdf?__blob=publicationFile&v=2"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2018.pdf?__blob=publicationFile&v=2" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldzustandsbericht 2018</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-11-29</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-11-29</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/225ebaffe8e7a30483ac43935e91e1f4"> + <dct:title>Waldzustandsbericht 2017</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht_2017.pdf?__blob=publicationFile&v=1"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht_2017.pdf?__blob=publicationFile&v=1" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldzustandsbericht 2017</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-12-27</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2017-12-27</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/9f48b6c643a139a0b4e7eac2ab4cfbb0"> + <dct:title>Waldzustandsbericht 2016</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2016.pdf?__blob=publicationFile&v=3"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2016.pdf?__blob=publicationFile&v=3" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldzustandsbericht 2016</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-12-15</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2016-12-15</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/904ef47daad01d2979ab20dda18b2ae7"> + <dct:title>Waldzustandsbericht 2015</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2015.pdf?__blob=publicationFile&v=3"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2015.pdf?__blob=publicationFile&v=3" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldzustandsbericht 2015</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-31</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2015-12-31</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/2147dcaa148ff19a89fa99c989dd648e"> + <dct:title>Waldzustandsbericht 2014</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2014.pdf?__blob=publicationFile&v=2"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2014.pdf?__blob=publicationFile&v=2" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldzustandsbericht 2014</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2014-12-31</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2014-12-31</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/f201ecaf859f8e713cd030615bd3f5cb"> + <dct:title>Waldzustandsbericht 2013</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2013.pdf?__blob=publicationFile&v=2"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2013.pdf?__blob=publicationFile&v=2" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldzustandsbericht 2013</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2013-12-31</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2013-12-31</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/9bbeaf7b503dbd2c667786db08c4512d"> + <dct:title>Waldzustandsbericht 2012</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2012.pdf?__blob=publicationFile&v=2"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2012.pdf?__blob=publicationFile&v=2" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldzustandsbericht 2012</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2012-12-31</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2012-12-31</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/f6de9c145fe28effe99fc163b92d657e"> + <dct:title>Waldzustandsbericht 2011</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2011.pdf?__blob=publicationFile&v=1"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2011.pdf?__blob=publicationFile&v=1" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldzustandsbericht 2011</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2011-12-31</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2011-12-31</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/5d794a0f4064ba53b5047dbe36eef3ef"> + <dct:title>Waldzustandsbericht 2010</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2010.pdf?__blob=publicationFile&v=1"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2010.pdf?__blob=publicationFile&v=1" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldzustandsbericht 2010</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2010-12-31</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2010-12-31</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/0ac0c2ceb265a7c0ca18385c95ec6e3a"> + <dct:title>Waldzustandsbericht 2009</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2009.pdf?__blob=publicationFile&v=1"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2009.pdf?__blob=publicationFile&v=1" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldzustandsbericht 2009</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2009-12-31</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2009-12-31</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/2f21507e1dc4e9106e348a65bb9e5cfa"> + <dct:title>Waldzustandsbericht 2008</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2008.pdf?__blob=publicationFile&v=1"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2008.pdf?__blob=publicationFile&v=1" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldzustandsbericht 2008</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2009-01-06</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2009-01-06</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/1e4b51fbab78072693617fcc2458824e"> + <dct:title>Waldzustandsbericht 2006/2007</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2006_2007.pdf?__blob=publicationFile&v=1"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2006_2007.pdf?__blob=publicationFile&v=1" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldzustandsbericht 2006/2007</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2008-02-18</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2008-02-18</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/ac543132dfecddfdaa9ef8385034323f"> + <dct:title>Waldschadensbericht 2005</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2005.pdf?__blob=publicationFile&v=1"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2005.pdf?__blob=publicationFile&v=1" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldschadensbericht 2005</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2006-03-22</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2006-03-22</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + <dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/f9727d9cfb4ad6f923a800e0f116b6e3"> + <dct:title>Waldschadensbericht 2004</dct:title> + + <dct:isVersionOf rdf:resource="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63" /> + <dcat:distribution> + <dcat:Distribution rdf:about="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2004.pdf?__blob=publicationFile&v=1"> + <dct:title></dct:title> + <dcat:accessURL rdf:resource="https://www.schleswig-holstein.de/DE/Fachinhalte/W/wald/Downloads/Waldzustandsbericht2004.pdf?__blob=publicationFile&v=1" /> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/PDF" /> + <dcat:mediaType>application/pdf</dcat:mediaType> + </dcat:Distribution> + </dcat:distribution> + <dct:subject rdf:resource="http://d-nb.info/gnd/4128022-2"/> + <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/GOVE" /> + <dct:description>Waldschadensbericht 2004</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU"/> + <dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2004-11-30</dct:issued> + <dct:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero" /> + <dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-07-15T14:55:00.00000</dct:modified> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + <dct:temporal> + <dct:PeriodOfTime> + <schema:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2004-11-30</schema:startDate> + </dct:PeriodOfTime> + </dct:temporal> + </dcat:Dataset> + </dcat:dataset> + +<dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/5768d5cdbc58abd45c8620e44e017f01"> + <dct:type rdf:resource="http://dcat-ap.de/def/datasetTypes/collection" /> + <dct:title>Munitionsbelastung der deutschen Meeresgewässer – Entwicklungen und Fortschritt</dct:title> + <dct:description>Munitionsbelastung der deutschen Meeresgewässer – Entwicklungen und Fortschritt</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/b5cd3f303f594ecde96e1017e953b688" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/9f9ae60bb0d8985e10e9ab8aa6a7ca34" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/ff612d0f165d46f3091f58e1ef56a2ec" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/3a0d0674120fbf06b5cb8737124e3fd0" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/cd606b042789723a2b6d61cb31c46c39" /> + + </dcat:Dataset> + </dcat:dataset> +<dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/f1f2513c3899ecf6f15691e0e4412822"> + <dct:type rdf:resource="http://dcat-ap.de/def/datasetTypes/collection" /> + <dct:title>Schuleingangsuntersuchungen in Schleswig-Holstein – Bericht</dct:title> + <dct:description>Schuleingangsuntersuchungen in Schleswig-Holstein – Bericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/ae2a3cffda84388365bc87711ed4af47" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/1ee93d311ae6078dc11655c2ca8865b5" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/91a479991245f208dd6f8ba82411a570" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/b0024265f96b9de5a590541f5e0aec91" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/04640ac19a21450984d2af7c402d6aa0" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/ffef477b7ebb1447d869b33c79cac8e2" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/a1fea922aa7d9f7a924784615301da83" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/86fe10eb6578e940d7df8e3d6da6520d" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/ccb65ee5047e3c9cc67384b513400246" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/f5123967a8d0d80ff135342ce41a09bb" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/05199f38b6234e794f79ad3726957a3e" /> + + </dcat:Dataset> + </dcat:dataset> +<dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63"> + <dct:type rdf:resource="http://dcat-ap.de/def/datasetTypes/collection" /> + <dct:title>Waldzustandsbericht</dct:title> + <dct:description>Waldzustandsbericht</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/1e4b51fbab78072693617fcc2458824e" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/0ac0c2ceb265a7c0ca18385c95ec6e3a" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/f9727d9cfb4ad6f923a800e0f116b6e3" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/904ef47daad01d2979ab20dda18b2ae7" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/5d794a0f4064ba53b5047dbe36eef3ef" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/2147dcaa148ff19a89fa99c989dd648e" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/f201ecaf859f8e713cd030615bd3f5cb" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/9bbeaf7b503dbd2c667786db08c4512d" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/ac543132dfecddfdaa9ef8385034323f" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/f6de9c145fe28effe99fc163b92d657e" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/f681f52201dcf1b1ea467aeed683c8a2" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/9f48b6c643a139a0b4e7eac2ab4cfbb0" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/225ebaffe8e7a30483ac43935e91e1f4" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/2f21507e1dc4e9106e348a65bb9e5cfa" /> + + </dcat:Dataset> + </dcat:dataset> +<dcat:dataset> + <dcat:Dataset rdf:about="http://transparenz.schleswig-holstein.de/7ee94f73beb43faf4099208b4e55734b"> + <dct:type rdf:resource="http://dcat-ap.de/def/datasetTypes/collection" /> + <dct:title>Jahresbericht zur biologischen Vielfalt</dct:title> + <dct:description>Jahresbericht zur biologischen Vielfalt</dct:description> + <dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/DEU" /> + <dct:spatial> + <dct:Location rdf:about="http://dcat-ap.de/def/politicalGeocoding/stateKey/01" /> + </dct:spatial> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/423e1ea7e98b492aebc0ca5108c7b36d" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/6ea87d1f4a92efb288ed53e2bf901880" /> + + <dct:hasVersion rdf:resource="http://transparenz.schleswig-holstein.de/7f6ef434153bdbfff9ed42c8d9ee1381" /> + + </dcat:Dataset> + </dcat:dataset> +</dcat:Catalog> +</rdf:RDF> + diff --git a/ckanext/odsh/tests_tpsh/test_checksum.py b/ckanext/odsh/tests_tpsh/test_checksum.py new file mode 100644 index 0000000000000000000000000000000000000000..6bff5d4fe469025e396c8b7e291e0d99106aad36 --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_checksum.py @@ -0,0 +1,59 @@ +from mock import patch, mock_open +import nose.tools as nt +from ckanext.odsh.lib.uploader import _raise_validation_error_if_hash_values_differ, calculate_hash +import ckantoolkit as ct +import ckan.logic as logic +import hashlib + +import os +path = os.path.abspath(__file__) +dir_path = os.path.dirname(path) + +class testHashException(object): + text = 'Transparenz SH' + # hash produced by following command in bash: + # echo -n "Transparenz SH" | md5sum + hash_for_text = '791759e98a3ec4cc9c03141a3293292b' + + def test_without_hash(self): + resource = {'package_id':'Test_id',} + with patch("__builtin__.open", mock_open(read_data=self.text)) as mock_file: + with open('some/file') as f: + assert _raise_validation_error_if_hash_values_differ(f, resource) == None + + def test_with_correct_hash(self): + resource = {'package_id':'Test_id', 'hash':self.hash_for_text} + with patch("__builtin__.open", mock_open(read_data=self.text)) as mock_file: + with open('some/file') as f: + assert _raise_validation_error_if_hash_values_differ(f, resource) == None + + def test_with_wrong_hash(self): + resource = {'package_id':'Test_id', 'hash':'incorrect_hash'} + with patch("__builtin__.open", mock_open(read_data=self.text)) as mock_file: + with open('some/file') as f: + with nt.assert_raises(logic.ValidationError) as e: + _raise_validation_error_if_hash_values_differ(f, resource) + exception_upload = e.exception.error_dict.get('upload') + assert exception_upload[0] == 'Berechneter Hash und mitgelieferter Hash sind unterschiedlich' + + def test_mock_file(self): + with patch("__builtin__.open", mock_open(read_data=self.text)) as mock_file: + with open('some/file') as f: + file_content = f.read() + nt.assert_equal(file_content, self.text) + + def test_hash_of_empty_string(self): + hash_empty_string = 'd41d8cd98f00b204e9800998ecf8427e' + nt.assert_equal(hash_empty_string, hashlib.md5('').hexdigest()) + + def test_pdf(self): + # expected_hash_pdf produced by following command in bash: + # md5sum test.pdf + expected_hash_pdf = '66123edf64fabf1c073fc45478bf4a57' + with open(dir_path + '/resources/test.pdf') as f: + hash = calculate_hash(f) + nt.assert_equal(hash, expected_hash_pdf) + + + + \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/test_date_range.py b/ckanext/odsh/tests_tpsh/test_date_range.py new file mode 100644 index 0000000000000000000000000000000000000000..e9b7121db7b7285b13ae6690a763af9028c901cd --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_date_range.py @@ -0,0 +1,158 @@ + # -*- coding: utf-8 -*- + +import nose.tools as nt + +import datetime + +from ckanext.odsh.pretty_daterange.date_range import DateRange + +class TestDateRange(object): + def test_is_one_year_returns_true(self): + dr = DateRange( + date_start = datetime.date(2000, 1, 1), + date_end = datetime.date(2000, 12, 31) + ) + assert dr.is_one_year() + + def test_is_one_year_returns_false_for_less_than_one_year(self): + dr = DateRange( + date_start = datetime.date(2000, 1, 1), + date_end = datetime.date(2000, 12, 30) + ) + assert dr.is_one_year()==False + + def test_is_one_year_returns_false_for_more_than_one_year(self): + dr = DateRange( + date_start = datetime.date(2000, 1, 1), + date_end = datetime.date(2001, 1, 15) + ) + assert dr.is_one_year()==False + + def test_is_one_year_returns_false_for_two_years(self): + dr = DateRange( + date_start = datetime.date(2000, 1, 1), + date_end = datetime.date(2001, 12, 31) + ) + assert dr.is_one_year()==False + + def test_is_one_half_of_year_returns_true_for_first_half(self): + dr = DateRange( + date_start = datetime.date(2000, 1, 1), + date_end = datetime.date(2000, 6, 30) + ) + assert dr.is_one_half_of_year() + + def test_is_one_half_of_year_returns_true_for_second_half(self): + dr = DateRange( + date_start = datetime.date(2000, 7, 1), + date_end = datetime.date(2000, 12, 31) + ) + assert dr.is_one_half_of_year() + + def test_is_one_half_of_year_returns_false_for_full_year(self): + dr = DateRange( + date_start = datetime.date(2000, 1, 1), + date_end = datetime.date(2000, 12, 31) + ) + assert dr.is_one_half_of_year()==False + + def test_get_half_year_returns_1_for_first_half_of_year(self): + half_year = DateRange.get_half_year(datetime.date(2000, 6, 30)) + nt.assert_equal(half_year, 1) + + def test_get_half_year_returns_2_for_second_half_of_year(self): + half_year = DateRange.get_half_year(datetime.date(2000, 7, 1)) + nt.assert_equal(half_year, 2) + + def test_is_one_quarter_of_year_returns_true_for_first_quarter(self): + dr = DateRange( + date_start = datetime.date(2000, 1, 1), + date_end = datetime.date(2000, 3, 31) + ) + assert dr.is_one_quarter_of_year() + + def test_is_one_quarter_of_year_returns_true_for_second_quarter(self): + dr = DateRange( + date_start = datetime.date(2000, 4, 1), + date_end = datetime.date(2000, 6, 30) + ) + assert dr.is_one_quarter_of_year() + + def test_is_one_quarter_of_year_returns_true_for_third_quarter(self): + dr = DateRange( + date_start = datetime.date(2000, 7, 1), + date_end = datetime.date(2000, 9, 30) + ) + assert dr.is_one_quarter_of_year() + + def test_is_one_quarter_of_year_returns_true_for_fourth_quarter(self): + dr = DateRange( + date_start = datetime.date(2000, 10, 1), + date_end = datetime.date(2000, 12, 31) + ) + assert dr.is_one_quarter_of_year() + + def test_get_quarter_returns_1_for_first_quarter(self): + quarter = DateRange.get_quarter(datetime.date(2000, 2, 1)) + nt.assert_equal(quarter, 1) + + def test_get_quarter_returns_2_for_second_quarter(self): + quarter = DateRange.get_quarter(datetime.date(2000, 5, 1)) + nt.assert_equal(quarter, 2) + + def test_get_quarter_returns_3_for_third_quarter(self): + quarter = DateRange.get_quarter(datetime.date(2000, 8, 1)) + nt.assert_equal(quarter, 3) + + def test_get_quarter_returns_4_for_fourth_quarter(self): + quarter = DateRange.get_quarter(datetime.date(1981, 11, 21)) + nt.assert_equal(quarter, 4) + + def test_is_one_month_returns_true(self): + dr = DateRange( + date_start = datetime.date(2000, 12, 1), + date_end = datetime.date(2000, 12, 31) + ) + assert dr.is_one_month() + + def test_is_one_month_returns_false_for_two_months(self): + dr = DateRange( + date_start = datetime.date(2000, 11, 1), + date_end = datetime.date(2000, 12, 31) + ) + assert dr.is_one_month()==False + + def test_is_range_of_multiple_months_returns_false_for_single_month(self): + dr = DateRange( + date_start = datetime.date(2000, 11, 1), + date_end = datetime.date(2000, 11, 30) + ) + assert dr.is_range_of_multiple_months()==False + + def test_is_range_of_multiple_months_returns_true_for_multiple_months(self): + dr = DateRange( + date_start = datetime.date(2000, 11, 1), + date_end = datetime.date(2021, 12, 31) + ) + assert dr.is_range_of_multiple_months() + + def test_is_range_of_multiple_months_in_same_year_returns_false_for_differing_years(self): + dr = DateRange( + date_start = datetime.date(2000, 11, 1), + date_end = datetime.date(2021, 12, 31) + ) + assert dr.is_range_of_multiple_months_in_same_year()==False + + def test_is_range_of_multiple_months_in_same_year_returns_true_for_same_year(self): + dr = DateRange( + date_start = datetime.date(2000, 11, 1), + date_end = datetime.date(2000, 12, 31) + ) + assert dr.is_range_of_multiple_months_in_same_year() + + def test_is_range_of_multiple_years_returns_false_for_single_year(self): + dr = DateRange( + date_start = datetime.date(2000, 1, 1), + date_end = datetime.date(2000, 12, 31) + ) + assert dr.is_range_of_multiple_years() == False \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/test_date_range_formatter.py b/ckanext/odsh/tests_tpsh/test_date_range_formatter.py new file mode 100644 index 0000000000000000000000000000000000000000..d2c8e77bf148fe8ef67b6a973033ba759caa3e9e --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_date_range_formatter.py @@ -0,0 +1,145 @@ + # -*- coding: utf-8 -*- + +import nose.tools as nt + +import datetime + +from ckanext.odsh.pretty_daterange.date_range_formatter import DateRangeFormatter + +class TestDateRangeFormatter(object): + def test_it_raises_value_error_if_date_end_before_date_start(self): + date_start = datetime.date(2019,1,1) + date_end = datetime.date(2018,1,1) + with nt.assert_raises(ValueError): + drf = DateRangeFormatter(date_start, date_end) + + def test_it_stores_date_start_and_date_end(self): + date_start = datetime.date(2019,1,1) + date_end = datetime.date(2019,2,1) + drf = DateRangeFormatter(date_start, date_end) + assert drf.date_start==date_start and drf.date_end==date_end + + def test_it_returns_date_in_correct_format(self): + self._assert_output_string_equals( + date_start = datetime.date(2019,1,2), + date_end = datetime.date(2020, 2, 4), + expected_str = u'02.01.2019 - 04.02.2020') + + def _assert_output_string_equals(self, date_start, date_end, expected_str): + drf = DateRangeFormatter(date_start, date_end) + formatted_date_range = drf.get_formatted_str() + nt.assert_equal(formatted_date_range, expected_str) + + def test_it_returns_single_date_if_start_equals_end(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 1, 2), + date_end = datetime.date(2019, 1, 2), + expected_str = u'02.01.2019') + + def test_it_returns_only_year_for_full_year(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 1, 1), + date_end = datetime.date(2019, 12, 31), + expected_str = u'2019') + + def test_it_returns_only_year_for_multiple_full_years(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 1, 1), + date_end = datetime.date(2020, 12, 31), + expected_str = u'2019 - 2020') + + def test_it_returns_month_for_full_month(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 2, 1), + date_end = datetime.date(2019, 2, 28), + expected_str = u'Februar 2019') + + def test_it_returns_months_for_range_of_months(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 2, 1), + date_end = datetime.date(2020, 3, 31), + expected_str = u'Februar 2019 - März 2020') + + def test_it_returns_months_for_range_of_months_in_same_year(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 2, 1), + date_end = datetime.date(2019, 3, 31), + expected_str = u'Februar - März 2019') + + def test_it_returns_first_quarter(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 1, 1), + date_end = datetime.date(2019, 3, 31), + expected_str = u'1. Quartal 2019') + + def test_it_returns_second_quarter(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 4, 1), + date_end = datetime.date(2019, 6, 30), + expected_str = u'2. Quartal 2019') + + def test_it_returns_third_quarter(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 7, 1), + date_end = datetime.date(2019, 9, 30), + expected_str = u'3. Quartal 2019') + + def test_it_returns_fourth_quarter(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 10, 1), + date_end = datetime.date(2019, 12, 31), + expected_str = u'4. Quartal 2019') + + def test_it_returns_first_half_year(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 1, 1), + date_end = datetime.date(2019, 6, 30), + expected_str = u'1. Halbjahr 2019') + + def test_it_returns_second_half_year(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 7, 1), + date_end = datetime.date(2019, 12, 31), + expected_str = u'2. Halbjahr 2019') + + def test_it_returns_date_start_if_date_end_is_none(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 7, 1), + date_end = None, + expected_str = u'ab 01.07.2019' + ) + + def test_it_returns_date_end_if_date_start_is_none(self): + self._assert_output_string_equals( + date_start = None, + date_end = datetime.date(2019, 7, 1), + expected_str = u'bis 01.07.2019' + ) + + def test_it_returns_empty_string_if_date_start_and_end_are_none(self): + self._assert_output_string_equals( + date_start = None, + date_end = None, + expected_str = u'' + ) + + def test_it_returns_date_start_if_date_end_is_empty(self): + self._assert_output_string_equals( + date_start = datetime.date(2019, 7, 1), + date_end = '', + expected_str = u'ab 01.07.2019' + ) + + def test_it_returns_date_end_if_date_start_is_empty(self): + self._assert_output_string_equals( + date_start = '', + date_end = datetime.date(2019, 7, 1), + expected_str = u'bis 01.07.2019' + ) + + def test_it_returns_empty_string_if_date_start_and_end_are_empty(self): + self._assert_output_string_equals( + date_start = '', + date_end = '', + expected_str = u'' + ) \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/test_helper_pkg_dict.py b/ckanext/odsh/tests_tpsh/test_helper_pkg_dict.py new file mode 100644 index 0000000000000000000000000000000000000000..2b5af7be0bade0d18299473ba0052fa9118047f0 --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_helper_pkg_dict.py @@ -0,0 +1,200 @@ +import datetime +import nose.tools as nt +from mock import patch, call + +from ckanext.odsh.helper_pkg_dict import HelperPgkDict +import ckanext.odsh.helpers_tpsh as helpers_tpsh +import ckanext.odsh.uri_store as uri_store + +class TestHelperPkgDict(object): + def test_is_collection_returns_true(self): + dataset_dict = { + u'type': u'collection', + } + h = HelperPgkDict(dataset_dict) + is_collection = h.is_collection() + nt.assert_true(is_collection) + + def test_is_collection_returns_false_if_no_type(self): + dataset_dict = {} + h = HelperPgkDict(dataset_dict) + is_collection = h.is_collection() + nt.assert_false(is_collection) + + def test_is_collection_returns_false_if_type_dataset(self): + dataset_dict = {u'type': u'dataset'} + h = HelperPgkDict(dataset_dict) + is_collection = h.is_collection() + nt.assert_false(is_collection) + + def test_shall_be_part_of_collection_returns_true_if_flag_add_to_collection_is_True(self): + dataset_dict = {'add_to_collection': True} + h = HelperPgkDict(dataset_dict) + shall_be_part_of_collection = h.shall_be_part_of_collection() + nt.assert_true(shall_be_part_of_collection) + + def test_shall_be_part_of_collection_returns_false_if_flag_add_to_collection_is_False(self): + dataset_dict = {'add_to_collection': False} + h = HelperPgkDict(dataset_dict) + shall_be_part_of_collection = h.shall_be_part_of_collection() + nt.assert_false(shall_be_part_of_collection) + + def test_shall_be_part_of_collection_returns_false_if_flag_add_to_collection_not_in_dict(self): + dataset_dict = {} + h = HelperPgkDict(dataset_dict) + shall_be_part_of_collection = h.shall_be_part_of_collection() + nt.assert_false(shall_be_part_of_collection) + + def test_update_relations_to_collection_members_leads_to_correct_call_of_add_to_collection(self): + with patch.object(helpers_tpsh, 'add_pkg_to_collection') as patch_add_package_to_collection: + # arange + # taken from debugging _update_relations_to_collection_members: + dataset_dict_collection = { + 'id': u'248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d', + u'extras': [ + {u'key': u'has_version', u'value': u'["http://transparenz.schleswig-holstein.de/9f9ae60bb0d8985e10e9ab8aa6a7ca34", "http://transparenz.schleswig-holstein.de/3a0d0674120fbf06b5cb8737124e3fd0", "http://transparenz.schleswig-holstein.de/b5cd3f303f594ecde96e1017e953b688", "http://transparenz.schleswig-holstein.de/ff612d0f165d46f3091f58e1ef56a2ec", "http://transparenz.schleswig-holstein.de/cd606b042789723a2b6d61cb31c46c39"]'}, + ] + } + id_collection = u'248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d' + uri_collection_member = 'http://transparenz.schleswig-holstein.de/b5cd3f303f594ecde96e1017e953b688' + id_collection_member = 'fake_id_collection_member' + uri_store._set_uri_to_id({uri_collection_member: id_collection_member}) + + # act + h = HelperPgkDict(dataset_dict_collection) + h.update_relations_to_collection_members() + + # assert + calls = [call(id_collection_member,id_collection), ] + patch_add_package_to_collection.assert_has_calls(calls) + + # teardown + uri_store._set_uri_to_id({}) + + def test_update_relation_to_collection_leads_to_correct_call_of_add_to_collection(self): + with patch.object(helpers_tpsh, 'add_pkg_to_collection') as patch_add_package_to_collection: + # arange + # taken from debugging _update_relations_to_collection_members: + dataset_dict_collection_member = { + u'add_to_collection': True, + u'extras': [ + {u'key': u'is_version_of', u'value': u'["http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63"]'}, + ], + 'id': u'248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d', + } + id_collection_member = u'248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d' + uri_collection = 'http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63' + id_collection = 'fake_id_collection' + uri_store._set_uri_to_id({uri_collection: id_collection}) + + # act + h = HelperPgkDict(dataset_dict_collection_member) + h.update_relation_to_collection() + + # assert + calls = [call(id_collection_member,id_collection), ] + patch_add_package_to_collection.assert_has_calls(calls) + + # teardown + uri_store._set_uri_to_id({}) + + def test_get_collection_uri(self): + dataset_dict_collection_member = { + u'add_to_collection': True, + u'extras': [ + {u'key': u'is_version_of', u'value': u'["http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63"]'}, + ], + 'id': u'248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d', + } + h = HelperPgkDict(dataset_dict_collection_member) + collection = h.get_collection_uri() + nt.assert_equal(collection, u'http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63') + + def test_get_collection_uri_if_not_in_extras(self): + dataset_dict_collection_member = { + u'id': u'248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d', + } + h = HelperPgkDict(dataset_dict_collection_member) + collection = h.get_collection_uri() + nt.assert_equal(collection, None) + + def test_get_uris_collection_members(self): + collection_members_as_string = u'["http://transparenz.schleswig-holstein.de/cd606b042789723a2b6d61cb31c46c39", "http://transparenz.schleswig-holstein.de/3a0d0674120fbf06b5cb8737124e3fd0", "http://transparenz.schleswig-holstein.de/b5cd3f303f594ecde96e1017e953b688", "http://transparenz.schleswig-holstein.de/9f9ae60bb0d8985e10e9ab8aa6a7ca34", "http://transparenz.schleswig-holstein.de/ff612d0f165d46f3091f58e1ef56a2ec"]' + dataset_dict = { + u'extras': [ + {u'key': u'has_version', u'value': collection_members_as_string}, + ] + } + h = HelperPgkDict(dataset_dict) + uris = h.get_uris_collection_members() + nt.assert_equal(uris[0], 'http://transparenz.schleswig-holstein.de/cd606b042789723a2b6d61cb31c46c39') + nt.assert_equal(uris[1], 'http://transparenz.schleswig-holstein.de/3a0d0674120fbf06b5cb8737124e3fd0') + nt.assert_equal(uris[-1], 'http://transparenz.schleswig-holstein.de/ff612d0f165d46f3091f58e1ef56a2ec') + + + +class Test_get_date_start_and_end_from_pkg_dict(object): + + def setUp(self): + self.dict_with_start_and_end_date = { + u'extras': [ + {u'key': u'groups', u'value': u''}, + {u'key': u'issued', u'value': u'2019-07-06T00:00:00'}, + {u'key': u'licenseAttributionByText', u'value': u''}, + {u'key': u'subject_text', u'value': u''}, + {u'key': u'temporal_end', u'value': u'2019-08-31T00:00:00'}, + {u'key': u'temporal_start', u'value': u'2019-08-01T00:00:00'} + ], + } + self.dict_with_empty_start_date = { + u'extras': [ + {u'key': u'groups', u'value': u''}, + {u'key': u'issued', u'value': u'2019-07-06T00:00:00'}, + {u'key': u'licenseAttributionByText', u'value': u''}, + {u'key': u'subject_text', u'value': u''}, + {u'key': u'temporal_end', u'value': u'2019-08-31T00:00:00'}, + {u'key': u'temporal_start', u'value': u''} + ], + } + self.dict_with_empty_end_date = { + u'extras': [ + {u'key': u'groups', u'value': u''}, + {u'key': u'issued', u'value': u'2019-07-06T00:00:00'}, + {u'key': u'licenseAttributionByText', u'value': u''}, + {u'key': u'subject_text', u'value': u''}, + {u'key': u'temporal_end', u'value': u''}, + {u'key': u'temporal_start', u'value': u'2019-08-01T00:00:00'} + ], + } + self.date_start_expected = datetime.date(2019, 8, 1) + self.date_end_expected = datetime.date(2019, 8, 31) + + def test_it_returns_correct_start_date(self): + h = HelperPgkDict(self.dict_with_start_and_end_date) + date_start, _ = h._get_date_start_and_end_from_pkg_dict() + nt.assert_equal(date_start, self.date_start_expected) + + def test_it_returns_correct_end_date(self): + h = HelperPgkDict(self.dict_with_start_and_end_date) + _, date_end = h._get_date_start_and_end_from_pkg_dict() + nt.assert_equal(date_end, self.date_end_expected) + + def test_it_return_none_if_date_start_empty(self): + h = HelperPgkDict(self.dict_with_empty_start_date) + date_start, _ = h._get_date_start_and_end_from_pkg_dict() + nt.assert_equal(date_start, None) + + def test_it_return_none_if_date_end_empty(self): + h = HelperPgkDict(self.dict_with_empty_end_date) + _, date_end = h._get_date_start_and_end_from_pkg_dict() + nt.assert_equal(date_end, None) + + def test_it_returns_date_start_if_date_end_empty(self): + h = HelperPgkDict(self.dict_with_empty_end_date) + date_start, _ = h._get_date_start_and_end_from_pkg_dict() + nt.assert_equal(date_start, self.date_start_expected) + + def test_it_returns_date_end_if_date_start_empty(self): + h = HelperPgkDict(self.dict_with_empty_start_date) + _, date_end = h._get_date_start_and_end_from_pkg_dict() + nt.assert_equal(date_end, self.date_end_expected) \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/test_helpers_tpsh.py b/ckanext/odsh/tests_tpsh/test_helpers_tpsh.py new file mode 100644 index 0000000000000000000000000000000000000000..78c106081ea654955449b41e79412f5c7563e33a --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_helpers_tpsh.py @@ -0,0 +1,281 @@ +# encoding: utf-8 + +import os +from collections import namedtuple, OrderedDict +import datetime +import nose.tools as nt +from mock import patch +from ckan.common import config +import ckan.logic.action.create as create +from ckanext.odsh.tests_tpsh.resources import org_dicts + + +from ckanext.odsh.helpers_tpsh import ( + map_dct_type_to_ckan_type, + map_ckan_type_to_dct_type, + add_pkg_to_collection, + correct_missing_relationship, + get_language_of_package, + get_address_org, + load_json_to_ordered_dict, + load_subject_mapping, + get_subject_for_selection +) + + +class TestMatchDctTypeToCkanType(object): + + def test_it_returns_collection(self): + dct_type = 'http://dcat-ap.de/def/datasetTypes/collection' + ckan_type = map_dct_type_to_ckan_type(dct_type) + expected_ckan_type = 'collection' + nt.assert_equal(ckan_type, expected_ckan_type) + + def test_it_returns_none_for_unknown_type(self): + nt.assert_equal( + map_dct_type_to_ckan_type('some unknown type'), + None + ) + + +class TestMatchCkanTypeToDctType(object): + + def test_it_returns_url_for_collection(self): + ckan_type = 'collection' + dct_type = map_ckan_type_to_dct_type(ckan_type) + expected_dct_type = 'http://dcat-ap.de/def/datasetTypes/collection' + nt.assert_equal( + dct_type, expected_dct_type + ) + + +FakePackageRelationship = namedtuple( + 'FakePackageRelationship', + ''' + object_package_id + revision_id + subject_package_id + id + type + ''' +) + +class Test_correct_missing_relationship(object): + def setUp(self): + self.relationships_from_model = [ + FakePackageRelationship( + object_package_id=u'object package id', + revision_id=u'revision id', + subject_package_id=u'subject package id', + id=u'id', + type=u'type' + ) + ] + + def test_it_does_not_modify_pkg_dict_if_no_model_relationships(self): + relationships_from_model = [] + original_pkg_dict = {'id': 'some_id'} + pkg_dict = dict(original_pkg_dict) + correct_missing_relationship( + pkg_dict, relationships_from_model + ) + nt.assert_equal(pkg_dict, original_pkg_dict) + + def test_it_does_not_modify_pkg_dict_if_relationships_already_in_dict(self): + original_pkg_dict = { + u'type': u'dataset', + u'relationships_as_subject': [ + { + u'__extras': { + u'object_package_id': u'original object package id', + u'revision_id': u'original revision id', + u'subject_package_id': u'original subject package id' + }, + u'comment': u'', + u'id': u'original id', + u'type': u'original type' + } + ] + } + pkg_dict = dict(original_pkg_dict) + correct_missing_relationship( + pkg_dict, self.relationships_from_model + ) + nt.assert_equal(pkg_dict, original_pkg_dict) + + def test_it_does_not_modify_pkg_dict_if_type_collection(self): + original_pkg_dict = { + u'type': u'collection', + u'relationships_as_subject': [ + { + u'__extras': { + u'object_package_id': u'original object package id', + u'revision_id': u'original revision id', + u'subject_package_id': u'original subject package id' + }, + u'comment': u'', + u'id': u'original id', + u'type': u'original type' + } + ] + } + pkg_dict = dict(original_pkg_dict) + correct_missing_relationship( + pkg_dict, self.relationships_from_model + ) + nt.assert_equal(pkg_dict, original_pkg_dict) + + def test_it_adds_relationships_if_not_already_in_dict(self): + pkg_dict = { + u'type': u'dataset', + u'relationships_as_subject': [] + } + correct_missing_relationship( + pkg_dict, self.relationships_from_model + ) + expected_pkg_dict = { + u'type': u'dataset', + u'relationships_as_subject': [ + { + u'__extras': { + u'object_package_id': u'object package id', + u'revision_id': u'revision id', + u'subject_package_id': u'subject package id' + }, + u'comment': u'', + u'id': u'id', + u'type': u'type' + } + ] + } + + from_relationships = lambda d, key: d.get('relationships_as_subject')[0].get(key) + from_extras = lambda d, key: d.get('relationships_as_subject')[0].get('__extras').get(key) + + # assert + nt.assert_equal(pkg_dict.get('type'), 'dataset') + + for key in ('id', 'type'): + nt.assert_true(from_relationships(pkg_dict, key) is not None) + nt.assert_equal( + from_relationships(pkg_dict, key), + from_relationships(expected_pkg_dict, key) + ) + + for key in ('object_package_id', 'revision_id', 'subject_package_id'): + nt.assert_true( + pkg_dict is not None) + nt.assert_equal( + from_extras(pkg_dict, key), + from_extras(expected_pkg_dict, key) + ) + + +class Test_get_language_of_package(object): + def setUp(self): + config.update({'ckanext.odsh.language_mapping': '/usr/lib/ckan/default/src/ckanext-odsh/language_mapping.json'}) + + def tearDown(self): + config.clear() + + def test_it_returns_Englisch(self): + test_package = { + 'id': u'language_test', + u'extras': [ + {u'key': u'language', u'value': u'http://publications.europa.eu/resource/authority/language/ENG'}, + ] + } + nt.assert_equal(get_language_of_package(test_package),'Englisch') + + def test_it_returns_None_if_language_id_not_in_dict(self): + test_package = { + 'id': u'language_test', + u'extras': [ + {u'key': u'language', u'value': u'tlhIngan Hol'}, + ] + } + nt.assert_equal(get_language_of_package(test_package), None) + + def test_it_returns_None_if_language_not_in_pkg_dict(self): + test_package = {} + nt.assert_equal(get_language_of_package(test_package), None) + + +class Test_get_address_org(object): + def test_it_returns_address_for_org_with_address(self): + organization = org_dicts.organization_with_address + address = get_address_org(organization) + nt.assert_equal(address.get('location'), u'Müllerdorf') + nt.assert_equal(address.get('person'), u'Michael Müller') + nt.assert_equal(address.get('mail'), u'mueller@mueller.de') + nt.assert_equal(address.get('street'), u'Müllergasse 10') + nt.assert_equal(address.get('telephone'), u'040 123456') + nt.assert_equal(address.get('web'), u'http://mueller.de') + + def test_it_returns_empty_dict_if_called_via_organization_new(self): + organization = dict() + address = get_address_org(organization) + assert type(address) is dict + nt.assert_equal(len(address), 0) + + +def _add_subject_mapping_file_to_config(): + path_current_file = os.path.dirname(os.path.abspath(__file__)) + path_to_subject_mapping_file = path_current_file + '/resources/subject_mapping_for_tests.json' + config.update({'ckanext.odsh.subject_mapping': path_to_subject_mapping_file}) + +class Test_load_json_to_ordered_dict(object): + def setUp(self): + json_str = '{"A": 1, "B": 2, "D": 3, "C":4, "E": 0}' + self.result = load_json_to_ordered_dict(json_str) + + def test_it_does_not_crash(self): + pass + + def test_it_returns_ordered_dict(self): + nt.assert_is(type(self.result), OrderedDict) + + def test_it_preserves_order_of_keys(self): + keys = self.result.keys() + nt.assert_equal(keys, [u'A', u'B', u'D', u'C', u'E']) + + def test_it_preserves_order_of_values(self): + values = self.result.values() + nt.assert_equal(values, [1, 2, 3, 4, 0]) + +class Test_load_subject_mapping(object): + def setUp(self): + _add_subject_mapping_file_to_config() + self.SUBJECT_MAPPING = load_subject_mapping() + + def tearDown(self): + config.clear() + + def test_it_returns_an_ordered_dictionary(self): + nt.assert_is(type(self.SUBJECT_MAPPING), OrderedDict) + + def test_it_preserves_order_of_json_file(self): + keys = self.SUBJECT_MAPPING.keys() + nt.assert_equal(keys[0], u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Verwaltungsvorschrift') + nt.assert_equal(keys[1], u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Organisationsplan') + nt.assert_equal(keys[2], u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Geschaeftsverteilungsplan') + nt.assert_equal(keys[3], u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Aktenplan') + +class Test_get_subject_for_selection(object): + def setUp(self): + _add_subject_mapping_file_to_config() + self.result = get_subject_for_selection() + + def tearDown(self): + config.clear() + + def test_it_returns_a_list(self): + assert type(self.result) is list + + def test_first_element_is_empty(self): + nt.assert_equal(self.result[0], {'key': 'empty', 'value': ' '}) + + def test_it_contains_more_than_one_element(self): + nt.assert_greater(len(self.result), 1) + + diff --git a/ckanext/odsh/tests_tpsh/test_icap.py b/ckanext/odsh/tests_tpsh/test_icap.py new file mode 100644 index 0000000000000000000000000000000000000000..f97bbc74249eadeeb2e36ebdf07aae53191d0d82 --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_icap.py @@ -0,0 +1,67 @@ +import nose.tools as nt +from testfixtures import log_capture + +from ckan.common import config +from ckanext.odsh.lib.odsh_icap_client import ODSHICAPRequest, _read_from_config + +class Test_ODSHICAPRequest(object): + def setUp(self): + config.update({ + 'ckanext.odsh.icap.host': 'some_host', + 'ckanext.odsh.icap.port': '123', + 'ckanext.odsh.icap.clientip': 'some_ip', + }) + + def tearDown(self): + config.clear() + + def test_it_initializes_with_parameters_set_in_config(self): + request = ODSHICAPRequest('some_filename', 'some_filebuffer') + nt.assert_equal(request.HOST, 'some_host') + nt.assert_equal(request.PORT, 123) + nt.assert_equal(request.CLIENTIP, 'some_ip') + + @log_capture() + def test_it_logs_missing_parameter_host_to_error_log(self, capture): + del config['ckanext.odsh.icap.host'] + ODSHICAPRequest('some_filename', 'some_filebuffer') + capture.check(( + 'ckanext.odsh.lib.odsh_icap_client', + 'ERROR', + "'key ckanext.odsh.icap.host is not defined in ckan config file.'" + )) + + @log_capture() + def test_it_logs_missing_parameter_port_to_error_log(self, capture): + del config['ckanext.odsh.icap.port'] + ODSHICAPRequest('some_filename', 'some_filebuffer') + capture.check(( + 'ckanext.odsh.lib.odsh_icap_client', + 'ERROR', + "'key ckanext.odsh.icap.port is not defined in ckan config file.'" + )) + + @log_capture() + def test_it_logs_missing_parameter_clientip_to_error_log(self, capture): + del config['ckanext.odsh.icap.clientip'] + ODSHICAPRequest('some_filename', 'some_filebuffer') + capture.check(( + 'ckanext.odsh.lib.odsh_icap_client', + 'ERROR', + "'key ckanext.odsh.icap.clientip is not defined in ckan config file.'" + )) + + def test_read_from_config_raises_KeyError_if_host_not_defined_in_config(self): + del config['ckanext.odsh.icap.host'] + with nt.assert_raises(KeyError): + _read_from_config('ckanext.odsh.icap.host') + + def test_read_from_config_raises_KeyError_if_port_not_defined_in_config(self): + del config['ckanext.odsh.icap.port'] + with nt.assert_raises(KeyError): + _read_from_config('ckanext.odsh.icap.port') + + def test_read_from_config_raises_KeyError_if_clientip_not_defined_in_config(self): + del config['ckanext.odsh.icap.clientip'] + with nt.assert_raises(KeyError): + _read_from_config('ckanext.odsh.icap.clientip') diff --git a/ckanext/odsh/tests_tpsh/test_matomo.py b/ckanext/odsh/tests_tpsh/test_matomo.py new file mode 100644 index 0000000000000000000000000000000000000000..0c20d6853e746ad866e7c30f25db91a2d6e7c5ab --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_matomo.py @@ -0,0 +1,53 @@ +import nose.tools as nt +from mock import patch +from ckan.common import config + +from ckanext.odsh.plugin import OdshPlugin +import ckanext.odsh.helpers_tpsh as helpers_tpsh +import ckanext.odsh.matomo as matomo + +class Test_helper_odsh_use_matomo(object): + def setUp(self): + self.plugin = OdshPlugin() + + def test_use_matomo_returns_False_if_not_in_config(self): + use_matomo = self.plugin.get_helpers()['odsh_use_matomo']() + nt.assert_false(use_matomo) + + def test_use_matomo_returns_False_if_set_False_in_config(self): + config.update({'ckanext.odsh.use_matomo': 'False'}) + use_matomo = self.plugin.get_helpers()['odsh_use_matomo']() + nt.assert_false(use_matomo) + config.clear() + + def test_use_matomo_returns_True_if_set_True_in_config(self): + config.update({'ckanext.odsh.use_matomo': 'True'}) + use_matomo = self.plugin.get_helpers()['odsh_use_matomo']() + nt.assert_true(use_matomo) + config.clear() + + +class Test_decorator_do_if_use_matomo(object): + def test_it_does_not_if_use_matomo_set_False_in_config(self): + + @matomo.do_if_use_matomo + def set_to_true(_): + return True + + config.update({'ckanext.odsh.use_matomo': 'False'}) + did_run = False + did_run = set_to_true(did_run) + nt.assert_false(did_run) + config.clear() + + def test_it_does_if_use_matomo_set_True_in_config(self): + + @matomo.do_if_use_matomo + def set_to_true(_): + return True + + config.update({'ckanext.odsh.use_matomo': 'True'}) + did_run = False + did_run = set_to_true(did_run) + nt.assert_true(did_run) + config.clear() diff --git a/ckanext/odsh/tests_tpsh/test_odsh_helpers.py b/ckanext/odsh/tests_tpsh/test_odsh_helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..a4c488230d254139ec4b3c7da1df93dd762a41dc --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_odsh_helpers.py @@ -0,0 +1,161 @@ +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 + + 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 diff --git a/ckanext/odsh/tests_tpsh/test_password_validation.py b/ckanext/odsh/tests_tpsh/test_password_validation.py new file mode 100644 index 0000000000000000000000000000000000000000..c999b3974784df07d2da8c3d40ac1d9e11e8026d --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_password_validation.py @@ -0,0 +1,35 @@ +# encoding: utf-8 + +import nose.tools as nt +from ckanext.odsh.logic.action import check_password + +class Test_PasswordValidation(object): + + @staticmethod + def assert_password_invalid(password): + assert not check_password(password) + + @staticmethod + def assert_password_valid(password): + assert check_password(password) + + def test_valid_password(self): + self.assert_password_valid('Passwort1 :) :P :D') + + def test_umlaute(self): + self.assert_password_valid('Pässword') + + def test_no_uppercase(self): + self.assert_password_invalid('passwort1') + + def test_no_lowercase(self): + self.assert_password_invalid('PASSWORT1') + + def test_no_letters(self): + self.assert_password_invalid('37459073245!!?===))/=$§äüöÄÜÖ') + + def test_only_letters(self): + self.assert_password_invalid('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz') + + def test_to_short(self): + self.assert_password_invalid('Pw123') \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/test_plugin.py b/ckanext/odsh/tests_tpsh/test_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..e5084a4518c3639bba1956d4dac29f0606ad3c28 --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_plugin.py @@ -0,0 +1,52 @@ +import datetime + +import ckanext.odsh.plugin as plugin + +class TestMethodBeforeView(object): + date_time_format = '%Y-%m-%dT%H:%M:%S' + + def test_before_view_adds_false_for_old_dataset(self): + plugin_object = plugin.OdshPlugin() + today = datetime.date.today() + hundred_days_ago = today - datetime.timedelta(days=100) + hundred_days_ago_as_ckan_str = self._date_as_ckan_str(hundred_days_ago) + dict_for_template = plugin_object.before_view( + { + u'extras': [ + {u'key': 'issued', u'value': hundred_days_ago_as_ckan_str} + ] + } + ) + assert dict_for_template['is_new']==False + + def _date_as_ckan_str(self, date): + return date.strftime(self.date_time_format) + + def test_before_view_adds_true_for_new_dataset(self): + plugin_object = plugin.OdshPlugin() + today = datetime.date.today() + ten_days_ago = today - datetime.timedelta(days=10) + ten_days_ago_as_ckan_str = self._date_as_ckan_str(ten_days_ago) + dict_for_template = plugin_object.before_view( + { + u'extras': [ + {u'key': 'issued', u'value': ten_days_ago_as_ckan_str} + ] + } + ) + assert dict_for_template['is_new']==True + + def test_before_view_does_not_modify_unconcerned_dict_values(self): + plugin_object = plugin.OdshPlugin() + today = datetime.date.today() + ten_days_ago = today - datetime.timedelta(days=10) + ten_days_ago_as_ckan_str = self._date_as_ckan_str(ten_days_ago) + dict_for_template = plugin_object.before_view( + { + u'extras': [ + {u'key': 'issued', u'value': ten_days_ago_as_ckan_str} + ], + 'some_other_key': 'some_other_value', + } + ) + assert dict_for_template['some_other_key']=='some_other_value' \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/test_profiles.py b/ckanext/odsh/tests_tpsh/test_profiles.py new file mode 100644 index 0000000000000000000000000000000000000000..0d6e859b98e3dcf8ec557ee0df1d2bd0160a9690 --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_profiles.py @@ -0,0 +1,184 @@ +import nose.tools as nt +import rdflib +from rdflib import Graph +from mock import patch + +from ckanext.dcat.profiles import URIRefOrLiteral + +import ckanext.odsh.profiles as profiles +import ckanext.odsh.helper_pkg_dict as helper_pkg_dict + +DCT = rdflib.namespace.Namespace("http://purl.org/dc/terms/") + + +class TestODSHDCATdeProfileParseDatasetWithCollection(object): + ''' + Tests for ODSHDCATdeProfile.parse_dataset with an rdf file + containing a collection + ''' + def setUp(self): + rdf_graph = Graph() + rdf_graph.parse('ckanext/odsh/tests_tpsh/resources/collection1.rdf') + self.profile = profiles.ODSHDCATdeProfile(rdf_graph) + self.dataset_ref_with_collection = URIRefOrLiteral( + 'http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte' + ) + self.dataset_ref_with_collection_member = URIRefOrLiteral( + 'http://opendata.schleswig-holstein.de/dataset/LAsDSH_SER_Statistik_anerkannte_Versorgungsberechtigte_Monat_201704' + ) + self.dataset_ref_with_member_not_in_collection = URIRefOrLiteral( + 'http://opendata.schleswig-holstein.de/dataset/Test_nicht_in_Collection' + ) + + def test_parse_dataset_adds_type_collection_to_dataset_dict(self): + dataset_dict = {} + self.profile.parse_dataset(dataset_dict, self.dataset_ref_with_collection) + nt.assert_equal('collection', dataset_dict['type']) + + def test_parse_dataset_adds_flag_for_collection_member(self): + dataset_dict = {} + self.profile.parse_dataset(dataset_dict, self.dataset_ref_with_collection_member) + nt.assert_equal(True, dataset_dict.get('add_to_collection')) + + def test_parse_type_adds_type_collection_to_dataset_dict(self): + dataset_dict = {} + self.profile._parse_type(dataset_dict, self.dataset_ref_with_collection) + nt.assert_equal('collection', dataset_dict['type']) + + def test_parse_type_does_not_add_collection_to_dataset_dict(self): + dataset_dict = {} + self.profile._parse_type(dataset_dict, self.dataset_ref_with_collection_member) + nt.assert_not_in('type', dataset_dict) + + def test_belongs_to_collection_returns_true(self): + dataset_dict = {} + assert self.profile._belongs_to_collection(dataset_dict, self.dataset_ref_with_collection_member) + + def test_belongs_to_collection_returns_false(self): + dataset_dict = {} + belongs_to_collection = self.profile._belongs_to_collection( + dataset_dict, self.dataset_ref_with_member_not_in_collection) + nt.assert_false(belongs_to_collection) + + +class TestODSHDCATdeProfileParseDatasetWithSubject(object): + ''' + Tests for ODSHDCATdeProfile.parse_dataset with an rdf file + containing datasets with subjects + ''' + def setUp(self): + rdf_graph = Graph() + rdf_graph.parse('ckanext/odsh/tests_tpsh/resources/transparenz.rdf') + self.profile = profiles.ODSHDCATdeProfile(rdf_graph) + self.dataset_ref_with_subject = URIRefOrLiteral( + 'http://transparenz.schleswig-holstein.de/ae2a3cffda84388365bc87711ed4af47' + ) + + def test_parse_subject_returns_subject(self): + dataset_dict = {} + self.profile._parse_subject(dataset_dict, self.dataset_ref_with_subject) + nt.assert_equal('http://d-nb.info/gnd/4128022-2', dataset_dict['subject']) + + def test_parse_dataset_returns_subject(self): + dataset_dict = {} + self.profile.parse_dataset(dataset_dict, self.dataset_ref_with_subject) + nt.assert_equal('http://d-nb.info/gnd/4128022-2', dataset_dict['subject']) + + +class TestODSHDCATdeProfileGraphFromDataset(object): + ''' + Tests for ODSHDCATdeProfile.graph_from_dataset + ''' + def setUp(self): + rdf_graph = Graph() + self.profile = profiles.ODSHDCATdeProfile(rdf_graph) + self.dummy_dataset_ref = URIRefOrLiteral('http://some_ref') + + def get_graph_and_assert_in(self, dataset_dict, dataset_ref, expected_node): + self.profile.graph_from_dataset(dataset_dict, dataset_ref) + graph_serialized = self.profile.g.serialize() + print(self.profile.g.serialize(format='pretty-xml')) + nt.assert_in(expected_node, graph_serialized) + + + patch_collection_member = patch.object( + profiles.ODSHDCATdeProfile, + '_dataset_belongs_to_collection', + return_value=True + ) + + patch_no_collection_member = patch.object( + profiles.ODSHDCATdeProfile, + '_dataset_belongs_to_collection', + return_value=False + ) + + + @patch_no_collection_member + def test_it_adds_dct_subject(self, __): + dataset_dict = { + 'subject': 'http://some_subject', + 'type': 'dataset', + 'groups': [], + } + expected_node = '<dct:subject rdf:resource="http://some_subject"/>' + self.get_graph_and_assert_in(dataset_dict, self.dummy_dataset_ref, expected_node) + + + @patch_no_collection_member + def test_it_adds_dct_type_collection(self, __): + dataset_dict = { + 'groups': [], + 'type': 'collection', + } + expected_node = ( + '<dct:type rdf:resource="http://dcat-ap.de/def/datasetTypes/collection"/>' + ) + with patch.object( + profiles.ODSHDCATdeProfile, + '_get_dataset_refs_belonging_to_collection', + return_value=[] + ): + self.get_graph_and_assert_in(dataset_dict, self.dummy_dataset_ref, expected_node) + + + @patch_no_collection_member + def test_it_adds_members_of_collection(self, __): + ''' + tests if rdf export of a collection contains the members of that collection. + The members are read from the package relationships. + ''' + dataset_dict = { + 'groups': [], + 'type': 'collection', + } + expected_nodes = ( + '<dct:hasVersion rdf:resource="http://id_1"/>', + '<dct:hasVersion rdf:resource="http://id_2"/>', + ) + # mock get_dataset_refs_belonging_to_collection(context, collection_name) + dataset_refs_belonging_to_collection = ['http://id_1', 'http://id_2'] + with patch.object( + profiles.ODSHDCATdeProfile, + '_get_dataset_refs_belonging_to_collection', + return_value=dataset_refs_belonging_to_collection + ): + for expected_node in expected_nodes: + self.get_graph_and_assert_in(dataset_dict, self.dummy_dataset_ref, expected_node) + + def test_remove_predefined_collection_members(self): + rdf_graph = Graph() + self.profile = profiles.ODSHDCATdeProfile(rdf_graph) + dummy_ref = rdflib.URIRef('http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63') + refs = ( + 'http://transparenz.schleswig-holstein.de/9bbeaf7b503dbd2c667786db08c4512d', + 'http://transparenz.schleswig-holstein.de/f6de9c145fe28effe99fc163b92d657e' + ) + for ref in refs: + self.profile.g.add( + (dummy_ref, DCT.hasVersion, rdflib.URIRef(ref)) + ) + self.profile._remove_predefined_collection_members() + graph_serialized = self.profile.g.serialize() + for ref in refs: + nt.assert_not_in(ref, graph_serialized) \ No newline at end of file diff --git a/ckanext/odsh/tests_tpsh/test_search.py b/ckanext/odsh/tests_tpsh/test_search.py new file mode 100644 index 0000000000000000000000000000000000000000..8e5008f9bf5dc88ecf3c0df5f224824319e9118a --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_search.py @@ -0,0 +1,133 @@ +import nose.tools as nt +import ckanext.odsh.search as search + +class Test_before_search(object): + def setUp(self): + self.search_params_before_test = { + 'extras': {}, + 'facet.field': ['organization', 'subject_text', 'groups'], + 'fq': u'organization:"test-organisation" groups:"gove" subject_text:"T\xe4tigkeitsbericht" +dataset_type:dataset', + 'include_private': True, + 'q': u'', + 'rows': 20, + 'sort': u'score desc, metadata_modified desc', + 'start': 0 + } + self.search_params_with_facet_mincount = self.search_params_before_test.copy() + self.search_params_with_facet_mincount.update({'facet.mincount': 0}) + + def test_it_solely_adds_facet_mincount_to_dict_if_no_extras(self): + # arange + search_params = self.search_params_before_test.copy() + # act + search.before_search(search_params) + # assert + search_params_expected = self.search_params_with_facet_mincount.copy() + nt.assert_equal(search_params, search_params_expected) + + def test_it_adds_fq_if_empty_range(self): + # arange + search_params = self.search_params_before_test.copy() + extras = {'ext_enddate': u'2019-08-01', 'ext_startdate': u'2019-08-02'} + search_params.update({'extras': extras}) + search_params_expected = self.search_params_with_facet_mincount.copy() + search_params_expected.update({'extras': extras}) + search_params_expected.update({ + 'fq': ( + u'organization:"test-organisation" groups:"gove" subject_text:"T\xe4tigkeitsbericht" ' + u'+dataset_type:dataset (+extras_temporal_start:[2019-08-02T00:00:00Z TO 2019-08-01T00:00:00Z] ' + u'OR +extras_temporal_end:[2019-08-02T00:00:00Z TO 2019-08-01T00:00:00Z] OR ' + u'((*:* NOT extras_temporal_end:[* TO *]) AND extras_temporal_start:[* TO 2019-08-01T00:00:00Z]))' + ) + }) + # act + search.before_search(search_params) + # assert + nt.assert_equal(search_params, search_params_expected) + + def test_it_solely_adds_facet_mincount_to_dict_if_wrong_date_format_in_extras(self): + # arange + search_params = self.search_params_before_test.copy() + extras = {'ext_enddate': u'some_date', 'ext_startdate': u'some_date'} + search_params.update({'extras': extras}) + search_params_expected = self.search_params_with_facet_mincount.copy() + search_params_expected.update({'extras': extras}) + # act + search.before_search(search_params) + # assert + nt.assert_equal(search_params, search_params_expected) + + def test_it_adds_fq_if_enclosing_range(self): + # arange + search_params = self.search_params_before_test.copy() + extras = {'ext_enddate': u'2019-08-02', 'ext_startdate': u'2019-08-01'} + search_params.update({'extras': extras}) + search_params_expected = self.search_params_with_facet_mincount.copy() + search_params_expected.update({'extras': extras}) + search_params_expected.update({ + 'fq': ( + u'organization:"test-organisation" groups:"gove" ' + u'subject_text:"T\xe4tigkeitsbericht" +dataset_type:dataset ' + u'(+extras_temporal_start:[2019-08-01T00:00:00Z TO 2019-08-02T00:00:00Z] ' + u'OR +extras_temporal_end:[2019-08-01T00:00:00Z TO 2019-08-02T00:00:00Z] ' + u'OR (extras_temporal_start:[* TO 2019-08-01T00:00:00Z] AND ' + u'extras_temporal_end:[2019-08-02T00:00:00Z TO *]) OR ' + u'((*:* NOT extras_temporal_end:[* TO *]) AND ' + u'extras_temporal_start:[* TO 2019-08-02T00:00:00Z]))' + ) + }) + # act + search.before_search(search_params) + # assert + nt.assert_equal(search_params, search_params_expected) + + def test_it_adds_fq_if_start_only(self): + # arange + search_params = self.search_params_before_test.copy() + extras = {'ext_startdate': u'2019-08-01'} + search_params.update({'extras': extras}) + search_params_expected = self.search_params_with_facet_mincount.copy() + search_params_expected.update({'extras': extras}) + search_params_expected.update({ + 'fq': ( + u'organization:"test-organisation" groups:"gove" ' + u'subject_text:"T\xe4tigkeitsbericht" +dataset_type:dataset ' + u'(+extras_temporal_start:[2019-08-01T00:00:00Z TO *] ' + u'OR +extras_temporal_end:[2019-08-01T00:00:00Z TO *] ' + u'OR (*:* NOT extras_temporal_end:[* TO *]))' + ) + }) + # act + search.before_search(search_params) + # assert + nt.assert_equal(search_params, search_params_expected) + + def test_it_adds_fq_if_end_only(self): + # arange + search_params = self.search_params_before_test.copy() + extras = {'ext_enddate': u'2019-08-02'} + search_params.update({'extras': extras}) + search_params_expected = self.search_params_with_facet_mincount.copy() + search_params_expected.update({'extras': extras}) + search_params_expected.update({ + 'fq': ( + u'organization:"test-organisation" groups:"gove" ' + u'subject_text:"T\xe4tigkeitsbericht" +dataset_type:dataset ' + u'(+extras_temporal_start:[* TO 2019-08-02T00:00:00Z] OR ' + u'+extras_temporal_end:[* TO 2019-08-02T00:00:00Z] ' + u'OR ((*:* NOT extras_temporal_end:[* TO *]) ' + u'AND extras_temporal_start:[* TO 2019-08-02T00:00:00Z]))' + ) + }) + # act + search.before_search(search_params) + # assert + nt.assert_equal(search_params, search_params_expected) + + def test_it_returns_search_params(self): + # arange + search_params = self.search_params_before_test.copy() + # act + return_value = search.before_search(search_params) + # assert + nt.assert_equal(return_value, search_params) diff --git a/ckanext/odsh/tests_tpsh/test_uri_store.py b/ckanext/odsh/tests_tpsh/test_uri_store.py new file mode 100644 index 0000000000000000000000000000000000000000..0fc92f09df43419cc3375dcd3188c0697e6d9e69 --- /dev/null +++ b/ckanext/odsh/tests_tpsh/test_uri_store.py @@ -0,0 +1,38 @@ +import nose.tools as nt +from ckanext.odsh.uri_store import add_uri, get_id_from_uri, _set_uri_to_id, _get_uri_to_id + +class Test_uri_store(object): + def test_add_uri_adds_values_to_dict(self): + _set_uri_to_id({u'http://some_uri': u'some_id'}) + dataset_dict = { + 'id': u'some_new_id', + u'extras': [ + {u'key': u'uri', u'value': u'http://some_new_uri'}, + ] + } + add_uri(dataset_dict) + nt.assert_equal( + _get_uri_to_id().get('http://some_uri'), + u'some_id' + ) + nt.assert_equal( + _get_uri_to_id().get('http://some_new_uri'), + u'some_new_id' + ) + + def test_get_id_returns_id(self): + _set_uri_to_id({u'http://some_uri': u'some_id'}) + uri = 'http://some_uri' + id = get_id_from_uri(uri) + id_expected = u'some_id' + nt.assert_equal(id, id_expected) + + def test_get_id_from_uri_returns_None_if_dict_empty(self): + _set_uri_to_id({}) + id = get_id_from_uri('some_uri') + nt.assert_equal(id, None) + + def test_get_id_from_uri_returns_None_if_id_unknown(self): + _set_uri_to_id({'uri_to_id': {u'http://some_uri': u'some_id'}}) + id = get_id_from_uri('some_unknown_id') + nt.assert_equal(id, None) \ No newline at end of file diff --git a/ckanext/odsh/tools.py b/ckanext/odsh/tools.py new file mode 100644 index 0000000000000000000000000000000000000000..ab7c1ba60885d1a4589b1bc533f9d22e4d74d27b --- /dev/null +++ b/ckanext/odsh/tools.py @@ -0,0 +1,45 @@ +import os +from ckanext.odsh.pdf_to_thumbnail.thumbnail import get_filepath_to_resource +from ckanext.odsh.lib.uploader import calculate_hash +import ckan.plugins.toolkit as toolkit +import magic +import pdftotext + +def add_attributes_resources(context, resource): + package_id = resource.get('package_id') + package = toolkit.get_action('package_show')(context, {'id': package_id}) + resources = package.get('resources') + i = 0 + for item in resources: + if item.get('id') == resource.get('id'): + path = get_filepath_to_resource(resource) + if os.path.exists(path): + with open(path, 'rb') as file: + + #size + if not item.get('size'): + resource_size = os.path.getsize(path) + item.update({'size': resource_size}) + + #hash + file.seek(0) + hash = calculate_hash(file) + item.update({'hash':hash}) + + #hash algorithm + item.update({'hash_algorithm': 'http://dcat-ap.de/def/hashAlgorithms/md/5'}) + + + #number of pages + file_type = magic.from_file(path, mime = True) + if file_type == 'application/pdf': + file.seek(0) + pdf = pdftotext.PDF(file) + number_of_pages = len(pdf) + item.update({'number_of_pages':number_of_pages}) + + resources[i] = item + break + i = i + 1 + package.update({'resources':resources}) + toolkit.get_action('package_update')(context, package) diff --git a/ckanext/odsh/uri_store/__init__.py b/ckanext/odsh/uri_store/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8f72f0e2a35f457b004d2a60703ca917a2fb864e --- /dev/null +++ b/ckanext/odsh/uri_store/__init__.py @@ -0,0 +1 @@ +from modifiers import add_uri, get_id_from_uri, _set_uri_to_id, _get_uri_to_id \ No newline at end of file diff --git a/ckanext/odsh/uri_store/modifiers.py b/ckanext/odsh/uri_store/modifiers.py new file mode 100644 index 0000000000000000000000000000000000000000..4fc1175b1bf10d870aca353edb6b5eddc38b0f34 --- /dev/null +++ b/ckanext/odsh/uri_store/modifiers.py @@ -0,0 +1,20 @@ +import store + +import ckanext.odsh.helpers as odsh_helpers + +def add_uri(dataset_dict): + uri = odsh_helpers.odsh_extract_value_from_extras( + dataset_dict.get('extras'), 'uri' + ) + id = dataset_dict.get('id') + store.uri_to_id.update({uri: id}) + +def get_id_from_uri(uri): + id = store.uri_to_id.get(uri) + return id + +def _set_uri_to_id(new_uri_to_id): + store.uri_to_id = new_uri_to_id + +def _get_uri_to_id(): + return store.uri_to_id \ No newline at end of file diff --git a/ckanext/odsh/uri_store/store.py b/ckanext/odsh/uri_store/store.py new file mode 100644 index 0000000000000000000000000000000000000000..1d152d8eee425ee7af34e5f6e618d7a9fb668ea0 --- /dev/null +++ b/ckanext/odsh/uri_store/store.py @@ -0,0 +1 @@ +uri_to_id = {} \ No newline at end of file diff --git a/ckanext/odsh/validation.py b/ckanext/odsh/validation.py index e50cbb4394ddbcc447a7fe87d200e738ba6019e6..a1e5338ec9e15795c7cab51ccf728c4fc38e0246 100644 --- a/ckanext/odsh/validation.py +++ b/ckanext/odsh/validation.py @@ -1,6 +1,6 @@ # This Python file uses the following encoding: utf-8 import logging -import csv +import unicodecsv as csv import re import urllib2 import json @@ -33,12 +33,13 @@ def _extract_value(data, field): def validate_extra_groups(data, requireAtLeastOne, errors): value = _extract_value(data, 'groups') + error_message_no_group = 'at least one group needed' if value != None: # 'value != None' means the extra key 'groups' was found, # so the dataset came from manual editing via the web-frontend. if not value: if requireAtLeastOne: - errors['groups'] = 'at least one group needed' + errors['groups'] = error_message_no_group data[('groups', 0, 'id')] = '' return @@ -49,7 +50,7 @@ def validate_extra_groups(data, requireAtLeastOne, errors): # del data[k] if len(groups) == 0: if requireAtLeastOne: - errors['groups'] = 'at least one group needed' + errors['groups'] = error_message_no_group return for num, group in zip(range(len(groups)), groups): @@ -58,22 +59,59 @@ def validate_extra_groups(data, requireAtLeastOne, errors): # dataset might come from a harvest process if not data.get(('groups', 0, 'id'), False) and \ not data.get(('groups', 0, 'name'), False): - errors['groups'] = 'at least one group needed' + errors['groups'] = error_message_no_group def validate_extras(key, data, errors, context): extra_errors = {} + isStaNord = ('id',) in data and data[('id',)][:7] == 'StaNord' - - validate_extra_groups(data, True, extra_errors) - validate_extra_date_new(key, 'issued', data, isStaNord, extra_errors) - validate_extra_date_new(key, 'temporal_start', - data, isStaNord, extra_errors) - validate_extra_date_new(key, 'temporal_end', data, True, extra_errors) + is_optional_temporal_start = toolkit.asbool( + config.get('ckanext.odsh.is_optional_temporal_start', False) + ) or isStaNord + + require_at_least_one_category = toolkit.asbool( + config.get('ckanext.odsh.require_at_least_one_category', False) + ) + validate_extra_groups( + data=data, + requireAtLeastOne=require_at_least_one_category, + errors=extra_errors + ) + + is_date_start_before_date_end(data, extra_errors) + + validate_extra_date_new( + key=key, + field='issued', + data=data, + optional=isStaNord, + errors=extra_errors + ) + validate_extra_date_new( + key=key, + field='temporal_start', + data=data, + optional=is_optional_temporal_start, + errors=extra_errors + ) + validate_extra_date_new( + key=key, + field='temporal_end', + data=data, + optional=True, + errors=extra_errors + ) if len(extra_errors.values()): raise toolkit.Invalid(extra_errors) +def is_date_start_before_date_end(data, extra_errors): + start_date = _extract_value(data, 'temporal_start') + end_date = _extract_value(data, 'temporal_end') + if start_date and end_date: + if start_date > end_date: + extra_errors['temporal_start'] = extra_errors['temporal_end'] = 'Please enter a valid period of time.' def _set_value(data, field, value): key = None @@ -141,7 +179,13 @@ def validate_licenseAttributionByText(key, data, errors, context): def known_spatial_uri(key, data, errors, context): + if data.get(('__extras',)) and 'spatial_uri_temp' in data.get(('__extras',)): + _copy_spatial_uri_temp_to_extras(data) value = _extract_value(data, 'spatial_uri') + require_spatial_uri = toolkit.asbool( + config.get('ckanext.odsh.require_spatial_uri', False) + ) + error_message_spatial_uri_empty = 'spatial_uri: empty not allowed' if not value: poly = None @@ -157,8 +201,10 @@ def known_spatial_uri(key, data, errors, context): has_old_uri = old_uri != None and len(old_uri) > 0 if not poly: poly = pkg.extras.get('spatial', None) - if not poly or has_old_uri: - raise toolkit.Invalid('spatial_uri: empty not allowed') + if (not poly) and require_spatial_uri: + raise toolkit.Invalid(error_message_spatial_uri_empty) + if has_old_uri and require_spatial_uri: + raise toolkit.Invalid(error_message_spatial_uri_empty) else: if poly: new_index = next_extra_index(data) @@ -175,9 +221,9 @@ def known_spatial_uri(key, data, errors, context): not_found = True spatial_text = str() spatial = str() - cr = csv.reader(mapping_file, delimiter="\t") + cr = csv.reader(mapping_file, delimiter="\t", encoding='utf-8') for row in cr: - if row[0].encode('UTF-8') == value: + if row[0] == value: not_found = False spatial_text = row[1] loaded = json.loads(row[2]) @@ -195,6 +241,21 @@ def known_spatial_uri(key, data, errors, context): data[('extras', new_index+1, 'value')] = spatial +def _copy_spatial_uri_temp_to_extras(data): + ''' + copy the field spatial_uri_temp originating + from the user interface to extras + ''' + spatial_uri = data.get(('__extras',)).get('spatial_uri_temp') + is_spatial_uri_in_extras = _extract_value(data, 'spatial_uri') is not None + if not is_spatial_uri_in_extras: + next_index = next_extra_index(data) + data[('extras', next_index, 'key')] = 'spatial_uri' + data[('extras', next_index, 'value')] = spatial_uri + else: + _set_value(data, 'spatial_uri', spatial_uri) + + def next_extra_index(data): current_indexes = [k[1] for k in data.keys() if len(k) > 1 and k[0] == 'extras'] @@ -232,10 +293,65 @@ def tag_string_convert(key, data, errors, context): tag_name_validator(tag, context) +def _convert_subjectID_to_subjectText(subject_id, flattened_data): + + if not subject_id: + return flattened_data + + default_subject_mapping_file_path = '/usr/lib/ckan/default/src/ckanext-odsh/subject_mapping.json' + subject_mapping_file_path = config.get( + 'ckanext.odsh.subject_mapping_file_path', default_subject_mapping_file_path) + + try: + with open(subject_mapping_file_path) as mapping_json: + subject_mapping = json.loads(mapping_json.read()) + except IOError as err: + log.error( + 'Could not load subject mapping file from {}' + .format(subject_mapping_file_path) + ) + raise + except ValueError as err: + log.error( + 'Could not convert subject mapping file from json. \nSubject mapping file: {}' + .format(subject_mapping_file_path) + ) + raise + + try: + subject_text = subject_mapping[subject_id] + except: + raise toolkit.Invalid(_('Subject must be a known URI.')) + log.warning( + 'Subject_id "{}" not found in subject mapping dictionary.\nSubject mapping file: {}' + .format(subject_id, subject_mapping_file_path) + ) + + + new_index = next_extra_index(flattened_data) + flattened_data[('extras', new_index, 'key')] = 'subject_text' + flattened_data[('extras', new_index, 'value')] = subject_text + return flattened_data + + +def validate_subject(key, flattened_data, errors, context): + subject_id = flattened_data[key] + require_subject = toolkit.asbool( + config.get('ckanext.odsh.require_subject', True) + ) + if not require_subject: + flattened_data = _convert_subjectID_to_subjectText(subject_id, flattened_data) + return + if not subject_id: + raise toolkit.Invalid(_('Subject must not be empty.')) + flattened_data = _convert_subjectID_to_subjectText(subject_id, flattened_data) + + def get_validators(): return { 'known_spatial_uri': known_spatial_uri, 'odsh_tag_name_validator': tag_name_validator, 'odsh_validate_extras': validate_extras, - 'validate_licenseAttributionByText': validate_licenseAttributionByText + 'validate_licenseAttributionByText': validate_licenseAttributionByText, + 'tpsh_validate_subject': validate_subject, } diff --git a/dev-requirements.txt b/dev-requirements.txt index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c574e4eaf013c487e6c63e0048eb9132b71c2627 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -0,0 +1,185 @@ +alabaster==0.7.11 +amqp==1.4.9 +anyjson==0.3.3 +arrow==0.13.1 +asn1crypto==0.24.0 +astroid==1.6.6 +atomicwrites==1.3.0 +attrs==19.3.0 +Babel==2.3.4 +backports.functools-lru-cache==1.5 +Beaker==1.8.1 +beautifulsoup4==4.5.1 +billiard==3.3.0.23 +bleach==1.5.0 +blinker==1.4 +celery==3.1.25 +certifi==2018.8.13 +cffi==1.11.5 +chardet==3.0.4 +click==6.7 +configparser==3.7.4 +contextlib2==0.6.0.post1 +cookies==2.2.1 +coverage==4.5.1 +coveralls==1.3.0 +cryptography==2.3 +decorator==4.0.6 +defusedxml==0.6.0 +dnspython==1.16.0 +docopt==0.6.2 +docutils==0.12 +entrypoints==0.3 +enum34==1.1.6 +eventlet==0.24.1 +extras==1.0.0 +factory-boy==2.1.1 +fanstatic==0.12 +filehash==0.1.dev3 +first==2.0.1 +fixtures==3.0.0 +flake8==3.7.8 +Flask==0.11.1 +Flask-DebugToolbar==0.10.0 +FormEncode==1.3.0 +funcsigs==1.0.2 +functools32==3.2.3.post2 +futures==3.3.0 +GeoAlchemy==0.7.2 +GeoAlchemy2==0.4.2 +geolinks==0.2.0 +geomet==0.2.0.post2 +greenlet==0.4.15 +html5lib==0.9999999 +httpretty==0.8.3 +hupper==1.6.1 +idna==2.7 +imagesize==1.0.0 +importlib-metadata==0.23 +ipaddress==1.0.22 +isodate==0.6.0 +isort==4.3.21 +itsdangerous==0.24 +Jinja2==2.8 +json-table-schema==0.2.1 +kombu==3.0.37 +lazy-object-proxy==1.4.1 +linecache2==1.0.0 +lxml==3.6.2 +Mako==1.0.4 +Markdown==2.6.7 +MarkupSafe==0.23 +mccabe==0.6.1 +messytables==0.15.2 +mock==2.0.0 +monotonic==1.5 +more-itertools==5.0.0 +mox3==0.26.0 +multiline-log-formatter==0.1.8 +nose==1.3.7 +ofs==0.4.2 +ordereddict==1.1 +OWSLib==0.16.0 +packaging==17.1 +Pairtree===0.7.1-T +passlib==1.6.5 +Paste==1.7.5.1 +PasteDeploy==2.0.1 +PasteScript==2.0.2 +pathlib==1.0.1 +pathlib2==2.3.5 +pbr==1.10.0 +pdf2image==1.9.0 +pdftotext==2.1.2 +pika==0.12.0 +Pillow==6.1.0 +pip-tools==1.7.0 +piwikapi==0.3 +pkg-resources==0.0.0 +plaster==1.0 +plaster-pastedeploy==0.7 +pluggy==0.13.0 +polib==1.0.7 +progressbar==2.3 +psycopg2==2.7.3.2 +py==1.8.0 +pyasn1==0.4.8 +pycodestyle==2.5.0 +pycountry==18.5.26 +pycparser==2.18 +pycrypto==2.6.1 +pycryptodome==3.9.0 +pycsw==2.2.0 +pyfakefs==2.9 +pyflakes==2.1.1 +Pygments==2.1.3 +pylint==1.9.5 +Pylons==0.9.7 +pymongo==3.9.0 +pyOpenSSL==18.0.0 +pyparsing==2.2.0 +pyproj==1.9.5.1 +pyramid==1.10.4 +pyramid-exclog==0.1 +-e git+https://github.com/IdentityPython/pysaml2.git@247e2f642b37de90a61c4f5ca49d8403840b6ea2#egg=pysaml2 +pysolr==3.6.0 +pytest==4.6.6 +pytest-cov==2.8.1 +python-dateutil==2.8.0 +python-json-logger==0.1.11 +python-magic==0.4.12 +python-memcached==1.48 +python-mimeparse==1.6.0 +pytz==2016.7 +pyutilib.component.core==4.6.4 +rdflib==4.2.1 +rdflib-jsonld==0.4.0 +redis==2.10.1 +repoze.lru==0.6 +repoze.who==2.3 +repoze.who-friendlyform==1.0.8 +requests==2.11.1 +responses==0.10.6 +rope==0.14.0 +Routes==1.13 +rq==0.6.0 +rq-dashboard==0.4.0 +scandir==1.10.0 +selenium==3.141.0 +Shapely==1.5.17 +simplejson==3.10.0 +singledispatch==3.4.0.3 +six==1.10.0 +snowballstemmer==1.2.1 +SPARQLWrapper==1.8.2 +Sphinx==1.7.1 +sphinx-rtd-theme==0.3.1 +sphinxcontrib-websupport==1.1.0 +SQLAlchemy==0.9.6 +sqlalchemy-migrate==0.10.0 +sqlparse==0.2.2 +Tempita==0.5.2 +testfixtures==6.10.3 +testtools==2.3.0 +traceback2==1.4.0 +translationstring==1.3 +typing==3.6.4 +tzlocal==1.3 +unicodecsv==0.14.1 +unittest2==1.1.0 +urllib3==1.23 +vdm==0.13 +venusian==1.2.0 +vine==1.1.4 +wcwidth==0.1.7 +WebError==0.13.1 +WebHelpers==1.3 +WebOb==1.0.8 +WebTest==1.4.3 +Werkzeug==0.11.10 +wrapt==1.11.2 +xlrd==1.0.0 +xmltodict==0.10.2 +zipp==0.6.0 +zope.deprecation==4.4.0 +zope.interface==4.3.2 diff --git a/language_mapping.json b/language_mapping.json new file mode 100644 index 0000000000000000000000000000000000000000..808122e6d854b0e9ea1bd006e474830017ef1dc2 --- /dev/null +++ b/language_mapping.json @@ -0,0 +1,5 @@ +{ + "http://publications.europa.eu/resource/authority/language/DAN":"Dänisch", + "http://publications.europa.eu/resource/authority/language/DEU":"Deutsch", + "http://publications.europa.eu/resource/authority/language/ENG":"Englisch" +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 83c9faa80e481fef6c586113897a26bc7bc9b949..524de62fd1f8a0134be3fbf616914c1e64692ac9 100755 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,8 @@ ckanext-harvest ckanext-spatial lxml piwikapi -multiline-log-formatter \ No newline at end of file +multiline-log-formatter +filehash==0.1.dev3 +pdf2image==1.9.0 +pathlib +pdftotext diff --git a/setup.py b/setup.py index e1a98ffc15ae06b7b6a0972f7d34a726114984a2..56287f08b92df2409200a83f764ab61a294c50df 100755 --- a/setup.py +++ b/setup.py @@ -81,13 +81,14 @@ setup( entry_points=''' [ckan.plugins] odsh=ckanext.odsh.plugin:OdshPlugin - odsh_icap=ckanext.odsh.plugin:OdshIcapPlugin + odsh_icap=ckanext.odsh.plugin_odsh_icap:OdshIcapPlugin statistikamtnord_harvester=ckanext.odsh.harvesters:StatistikamtNordHarvester kiel_harvester=ckanext.odsh.harvesters:KielHarvester - odsh_autocomplete=ckanext.odsh.plugin:OdshAutocompletePlugin - odsh_harvest=ckanext.odsh.plugin:OdshHarvestPlugin - odsh_dcat_harvest=ckanext.odsh.plugin:OdshDCATHarvestPlugin - + odsh_autocomplete=ckanext.odsh.plugin_odsh_autocomplete:OdshAutocompletePlugin + odsh_harvest=ckanext.odsh.plugin_odsh_harvest:OdshHarvestPlugin + odsh_dcat_harvest=ckanext.odsh.plugin_odsh_dcat_harvest:OdshDCATHarvestPlugin + tpsh_collections=ckanext.odsh.collection.plugin:CollectionsPlugin + thumbnail=ckanext.odsh.pdf_to_thumbnail.plugin:ThumbnailPlugin [paste.paster_command] odsh_initialization = ckanext.odsh.commands.initialization:Initialization diff --git a/subject_mapping.json b/subject_mapping.json new file mode 100644 index 0000000000000000000000000000000000000000..97ebe1718d106fb679478595aff84c4acab1c26d --- /dev/null +++ b/subject_mapping.json @@ -0,0 +1,25 @@ +{ + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Verwaltungsvorschrift": "Verwaltungsvorschrift", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Organisationsplan": "Organisationsplan", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Geschaeftsverteilungsplan": "Geschäftsverteilungsplan", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Aktenplan": "Aktenplan", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Richtlinie": "Richtlinie und Runderlass", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Statistik": "amtliche Statistik", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Taetigkeitsbericht": "Tätigkeitsbericht", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Broschuere": "Broschüre", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Gutachten": "Gutachten", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Studie": "Studie", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Haushaltsplan": "Haushaltsplan", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Stellenplan": "Stellenplan", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Wirtschaftsplan": "Wirtschaftsplan", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#ZuwendungAnPerson": "Übersicht über Zuwendungen an juristische Personen des Privatrechts", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#ZuwendungAnLand": "Übersicht über Zuwendungen an das Land Schleswig-Holstein", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#IZGAntwort": "IZG/GvgV-Auskunft", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Gerichtsurteil": "Gerichtsurteil", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#GesetzvorlageLandtag": "Gesetzesvorlage an den Landtag", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#MitteilungLandtag": "Mitteilung an den Landtag", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Unternehmensdaten": "wesentliche Unternehmensdaten von Beteiligungen des Landes", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#VergütungsOG": "jährliche Vergütungen nach dem VergütungsOG", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#Vertrag": "Vertrag", + "http://transparenz.schleswig-holstein.de/informationsgegenstand#sonstiges": "zusätzliche freiwillige Information" +}