diff --git a/ckanext/nhm/lib/helpers.py b/ckanext/nhm/lib/helpers.py index 0188c10f..d434e6cc 100644 --- a/ckanext/nhm/lib/helpers.py +++ b/ckanext/nhm/lib/helpers.py @@ -1599,3 +1599,31 @@ def get_record_iiif_manifest_url(resource_id: str, record_id: int) -> str: {'builder_id': 'record', 'resource_id': resource_id, 'record_id': record_id}, ) return toolkit.url_for('iiif.resource', identifier=manifest_id, _external=True) + + +def get_status_indicator(): + """ + Check if we need to display a status indicator, and if so what type. + + :return: 'red', 'amber', or None (if no alerts) + """ + # is there a status message? + status_message = toolkit.config.get('ckanext.status.message', None) + if status_message: + return 'red' + + try: + status_reports = toolkit.get_action('status_list')({}, {}).get('reports', []) + except KeyError: + # if the action doesn't exist + status_reports = [] + + # are there any 'bad' items? + red_status = [r for r in status_reports if r['state'] == 'bad'] + if len(red_status) > 0: + return 'red' + + # are there any reports with small issues? + amber_status = [r for r in status_reports if r['state'] == 'ok'] + if len(amber_status) > 0: + return 'amber' diff --git a/ckanext/nhm/lib/mail.py b/ckanext/nhm/lib/mail.py index e9b5f668..398b8333 100644 --- a/ckanext/nhm/lib/mail.py +++ b/ckanext/nhm/lib/mail.py @@ -29,7 +29,7 @@ def create_department_email(mail_dict: dict, department: str): mail_dict['recipient_email'] = COLLECTION_CONTACTS[department] except KeyError: # Other/unknown etc., - so don't set recipient email - mail_dict['body'] += f'\nDepartment: {department}\n' + mail_dict['body'] += f'\nDepartment or team: {department}\n' else: mail_dict['recipient_name'] = department mail_dict['body'] += ( @@ -74,6 +74,9 @@ def get_package_owners(package: dict) -> List[Recipient]: :param package: the package dict """ + maintainer_name = package.get('maintainer', 'Maintainer') + maintainer_email = package.get('maintainer_email') + collaborators = toolkit.get_action('package_collaborator_list')( # ignore auth to ensure we can access the list of collaborators {'ignore_auth': True}, @@ -81,14 +84,19 @@ def get_package_owners(package: dict) -> List[Recipient]: {'id': package['id'], 'capacity': 'admin'}, ) - recipient_ids = [] - if collaborators: - recipient_ids.extend(collaborator['user_id'] for collaborator in collaborators) + recipients = [] + if maintainer_email: + recipients.append(Recipient(maintainer_name, maintainer_email)) + elif collaborators: + recipients = [ + Recipient.from_user_id(collaborator['user_id']) + for collaborator in collaborators + ] else: - # if there aren't any collaborators, use the creator - recipient_ids.append(package['creator_user_id']) + # if there's no maintainer and there aren't any collaborators, use the creator + recipients.append(Recipient.from_user_id(package['creator_user_id'])) - return list(map(Recipient.from_user_id, recipient_ids)) + return recipients def create_package_email(mail_dict: dict, package: dict): diff --git a/ckanext/nhm/lib/utils.py b/ckanext/nhm/lib/utils.py new file mode 100644 index 00000000..490a05c4 --- /dev/null +++ b/ckanext/nhm/lib/utils.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# encoding: utf-8 +# +# This file is part of ckanext-nhm +# Created by the Natural History Museum in London, UK + +from ckan.plugins import toolkit +import requests + + +def get_iiif_status(): + health = {} + + url = toolkit.config.get('ckanext.iiif.image_server_url') + r = requests.get(url + '/status') + if r.ok: + health['ping'] = True + response_json = r.json() + else: + response_json = {} + + health['status'] = response_json.get('status') + mss = response_json.get('profiles', {}).get('mss', {}) + health['specimens'] = mss.get('mss_status', {}).get('status', ':(') + health['es'] = mss.get('es', {'status': 'red', 'response_time': None}) + + return health diff --git a/ckanext/nhm/plugin.py b/ckanext/nhm/plugin.py index 4466db24..b1eb9b89 100644 --- a/ckanext/nhm/plugin.py +++ b/ckanext/nhm/plugin.py @@ -31,6 +31,14 @@ IVersionedDatastore, IVersionedDatastoreDownloads, ) +from ckanext.nhm.lib.utils import get_iiif_status + +try: + from ckanext.status.interfaces import IStatus + + status_available = True +except ImportError: + status_available = False log = logging.getLogger(__name__) @@ -63,6 +71,8 @@ class NHMPlugin(SingletonPlugin, toolkit.DefaultDatasetForm): implements(interfaces.IClick) implements(interfaces.IConfigurable) implements(IVersionedDatastoreDownloads, inherit=True) + if status_available: + implements(IStatus) ## IConfigurable def configure(self, config): @@ -710,3 +720,52 @@ def download_modify_eml(self, eml_dict, query): ) eml_dict['creator'] = creators return eml_dict + + ## IStatus + def modify_status_reports(self, status_reports): + iiif_health = get_iiif_status() + + # overall image server status + if iiif_health['ping'] and iiif_health['status'] == ':)': + status_text = toolkit._('available') + status_type = 'good' + elif iiif_health['ping'] and iiif_health['status'] != ':)': + status_text = toolkit._('available (issues)') + status_type = 'ok' + else: + status_text = toolkit._('unavailable') + status_type = 'bad' + + status_reports.append( + { + 'label': toolkit._('Image server'), + 'value': status_text, + 'group': toolkit._('Images'), + 'help': toolkit._( + 'The IIIF server provides most of the images in datasets (some are externally hosted)' + ), + 'state': status_type, + } + ) + + # specimen images + if iiif_health['ping'] and iiif_health['specimens'] == ':)': + status_text = toolkit._('available') + status_type = 'good' + else: + status_text = toolkit._('unavailable') + status_type = 'bad' + + status_reports.append( + { + 'label': toolkit._('Specimen images'), + 'value': status_text, + 'group': toolkit._('Images'), + 'help': toolkit._( + 'Specimen images are a specific subset of images used primarily in the Collection specimens and Index lots datasets' + ), + 'state': status_type, + } + ) + + return status_reports diff --git a/ckanext/nhm/settings.py b/ckanext/nhm/settings.py index d51d86f2..87a8ba28 100644 --- a/ckanext/nhm/settings.py +++ b/ckanext/nhm/settings.py @@ -17,6 +17,7 @@ ('Library & Archives', 'library@nhm.ac.uk'), ('Mineral & Planetary Sciences', 'm.rumsey@nhm.ac.uk'), ('Vertebrates', 'simon.loader@nhm.ac.uk'), + ('Biodiversity Intactness Index', 'biodiversityfuturesexplorer@nhm.ac.uk'), ('Data Portal / Other', 'data@nhm.ac.uk'), ] ) diff --git a/ckanext/nhm/theme/assets/less/nhm.less b/ckanext/nhm/theme/assets/less/nhm.less index ef4c14eb..c913c8a7 100644 --- a/ckanext/nhm/theme/assets/less/nhm.less +++ b/ckanext/nhm/theme/assets/less/nhm.less @@ -236,6 +236,23 @@ body { & .notifications { padding-right: 10px; } + + & .status-indicator { + font-size: @font-size-body-s; + position: absolute; + top: 0; + margin-left: 1.3em; + padding: 0 2px; + border-radius: @rounding; + + &.status-indicator-red { + background: @warning2; + } + + &.status-indicator-amber { + background: orange; + } + } } .icon-pad() { @@ -1370,6 +1387,7 @@ iframe { margin: 5px 0; } +.info-block, .form-group .info-block { color: @grey4; font-size: @font-size-body-s; @@ -1379,6 +1397,11 @@ iframe { } } +.form-group + .info-block { + margin-top: -25px; // the margin-bottom for .form-group is 30px + margin-bottom: 30px; +} + input[type='radio'], input[type='checkbox'] { position: relative; diff --git a/ckanext/nhm/theme/templates/contact/snippets/form.html b/ckanext/nhm/theme/templates/contact/snippets/form.html index deca40e2..1e2858ea 100644 --- a/ckanext/nhm/theme/templates/contact/snippets/form.html +++ b/ckanext/nhm/theme/templates/contact/snippets/form.html @@ -27,7 +27,7 @@ {# If this is the collections dataset, still add the department select #} {% if package['name'] == 'collection-specimens' %} - {{ form.select('department', label=_('Department'), options=h.get_contact_form_department_options(), selected=data.department, error=errors.department, is_required=true) }} + {{ form.select('department', label=_('Department or team'), options=h.get_contact_form_department_options(), selected=data.department, error=errors.department, is_required=true) }} {% endif %}
@@ -43,7 +43,7 @@ {% else %} {# Add department selection (for non dataset forms) #} - {{ form.select('department', label=_('Department'), options=h.get_contact_form_department_options(), selected=data.department, error=errors.department, is_required=true) }} + {{ form.select('department', label=_('Department or team'), options=h.get_contact_form_department_options(), selected=data.department, error=errors.department, is_required=true) }} {% endif %} diff --git a/ckanext/nhm/theme/templates/footer.html b/ckanext/nhm/theme/templates/footer.html index 396f9848..5fc96749 100644 --- a/ckanext/nhm/theme/templates/footer.html +++ b/ckanext/nhm/theme/templates/footer.html @@ -58,28 +58,31 @@
  • Credits
  • +
  • + + Help + +
  • Contact
  • -
  • {{ h.api_doc_link() }}
  • Privacy notice
  • + href="{{ h.url_for('legal.privacy') }}">Privacy
  • T&C
  • - Website accessibility statement + Accessibility
  • - Acknowledgement of harmful content + Harmful content
  • {% if g.userobj %}
  • - {{ _('My - account') }} + {{ _('Account') }}
  • {{ _('Log out') }} diff --git a/ckanext/nhm/theme/templates/header.html b/ckanext/nhm/theme/templates/header.html index 29853d0b..8fad5f5e 100755 --- a/ckanext/nhm/theme/templates/header.html +++ b/ckanext/nhm/theme/templates/header.html @@ -7,6 +7,24 @@ {# We do not want a search #}{% block header_site_search %}{% endblock %} +{% set status_indicator = h.get_status_indicator() %} +{% macro status_icon() %} +
    + + {{ _('System status') }} + {% if status_indicator %} + + {{ _('Status alert') }} + + + {% endif %} + + +
    +{% endmacro %} + + {% block header_wrapper %}
    {% endif %} + {# Status page #} + {{ status_icon() }} + {# Logout #} {% block header_account_log_out_link %} @@ -85,6 +106,9 @@ {# headway widget #}
    + {# Status page #} + {{ status_icon() }} + {% block header_account_notlogged %}
    diff --git a/ckanext/nhm/theme/templates/package/snippets/package_metadata_fields.html b/ckanext/nhm/theme/templates/package/snippets/package_metadata_fields.html index bacadfda..bd4b58bf 100644 --- a/ckanext/nhm/theme/templates/package/snippets/package_metadata_fields.html +++ b/ckanext/nhm/theme/templates/package/snippets/package_metadata_fields.html @@ -13,6 +13,11 @@ {% endblock %} {% block package_metadata_fields_maintainer %} +{{ form.input('maintainer_email', label=_('Contact email'), id='field-maintainer-email', placeholder=_('e.g. shared-inbox@nhm.ac.uk'), value=data.maintainer_email, error=errors.maintainer_email, classes=['control-medium']) }} +{{ form.info('Enquiries about this dataset will be sent to this address. If not set, messages will go to the collaborators or creator of the dataset.', inline=False) }} + +{{ form.input('maintainer', label=_('Contact name'), id='field-maintainer', placeholder=_('e.g Project Y'), value=data.maintainer, error=errors.maintainer, classes=['control-medium']) }} +{{ form.info('Name of the person or group receiving dataset enquiries (see above). Optional.', inline=False) }} {% endblock %} {% block custom_fields %} @@ -21,8 +26,8 @@ {{ super() }} {% endblock %} - {{ form.info("What time period does this dataset cover? If not applicable, leave blank.", inline=True) }} {{ form.input('temporal_extent', label=_('Temporal extent'), id='field-temporal-extent', placeholder=_('1970 - 1985'), value=data.temporal_extent, error=errors.temporal_extent, classes=['control-medium']) }} + {{ form.info("What time period does this dataset cover? If not applicable, leave blank.", inline=True) }} {{ form.select('update_frequency', label=_('Update frequency'), id='field-update-frequency', options=h.form_select_update_frequency_options(), selected=data.update_frequency, error=errors.update_frequency, classes=['control-medium']) }} {# TODO: Add map to pick spatial extent - hidden for non sysadmin until then #} diff --git a/pyproject.toml b/pyproject.toml index 9bf0b63f..4f7f2df4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,8 +33,9 @@ dependencies = [ "ckanext-ldap>=3.2.0", "ckanext-query-dois>=4.0.0", "ckanext-statistics>=3.1.0", - "ckanext-versioned-datastore>=5.1.0" + "ckanext-versioned-datastore>=5.1.0", # this also depends on ckanext-dcat==1.3.0 (see readme) + "requests" ] [project.optional-dependencies]