Skip to content

Commit

Permalink
Merge pull request #82 from NaturalHistoryMuseum/dev
Browse files Browse the repository at this point in the history
Weekly release 2023-09-25
  • Loading branch information
jrdh committed Sep 25, 2023
2 parents cec490c + ad36eb4 commit a11cecc
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 44 deletions.
21 changes: 18 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ on:
workflow_dispatch:

jobs:
test:
test_latest:
name: Run tests against ckantest latest
runs-on: ubuntu-latest

steps:
Expand All @@ -15,7 +16,21 @@ jobs:
- name: Build images
run: docker-compose build

- name: Run tests
- name: Run tests against latest
env:
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
run: docker-compose run -e COVERALLS_REPO_TOKEN ckan bash /opt/scripts/run-tests.sh -c ckanext.doi
run: docker-compose run -e COVERALLS_REPO_TOKEN latest bash /opt/scripts/run-tests.sh -c ckanext.doi

test_next:
name: Run tests against ckantest next
runs-on: ubuntu-latest

steps:
- name: Checkout source code
uses: actions/checkout@v3

- name: Build images
run: docker-compose build

- name: Run tests against next
run: docker-compose run -e COVERALLS_REPO_TOKEN next bash /opt/scripts/run-tests.sh -c ckanext.doi
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[![Tests](https://img.shields.io/github/actions/workflow/status/NaturalHistoryMuseum/ckanext-doi/main.yml?style=flat-square)](https://github.com/NaturalHistoryMuseum/ckanext-doi/actions/workflows/main.yml)
[![Coveralls](https://img.shields.io/coveralls/github/NaturalHistoryMuseum/ckanext-doi/main?style=flat-square)](https://coveralls.io/github/NaturalHistoryMuseum/ckanext-doi)
[![CKAN](https://img.shields.io/badge/ckan-2.9.7-orange.svg?style=flat-square)](https://github.com/ckan/ckan)
[![CKAN](https://img.shields.io/badge/ckan-2.9.9%20%7C%202.10.1-orange.svg?style=flat-square)](https://github.com/ckan/ckan)
[![Python](https://img.shields.io/badge/python-3.6%20%7C%203.7%20%7C%203.8-blue.svg?style=flat-square)](https://www.python.org/)
[![Docs](https://img.shields.io/readthedocs/ckanext-doi?style=flat-square)](https://ckanext-doi.readthedocs.io)

Expand Down Expand Up @@ -229,7 +229,7 @@ first, then the xml dict is passed through all the implementations of this metho
<!--testing-start-->
There is a Docker compose configuration available in this repository to make it easier to run tests. The ckan image uses the Dockerfile in the `docker/` folder.

To run the tests against ckan 2.9.x on Python3:
To run the tests can be run against ckan 2.9.x and 2.10.x on Python3:

1. Build the required images:
```shell
Expand All @@ -241,7 +241,11 @@ To run the tests against ckan 2.9.x on Python3:
configuration, so you should only need to rebuild the ckan image if you change the extension's
dependencies.
```shell
docker-compose run ckan
# run tests against ckan 2.9.x
docker-compose run latest

# run tests against ckan 2.10.x
docker-compose run next
```

Note that the tests mock the DataCite API and therefore don't require an internet connection nor
Expand Down
64 changes: 41 additions & 23 deletions ckanext/doi/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,38 +37,38 @@ def update_config(self, config):
"""
Adds templates.
"""
toolkit.add_template_directory(config, 'theme/templates')
toolkit.add_template_directory(config, "theme/templates")

## IPackageController
def after_create(self, context, pkg_dict):
def after_dataset_create(self, context, pkg_dict):
"""
A new dataset has been created, so we need to create a new DOI.
NB: This is called after creation of a dataset, before resources have been
added, so state = draft.
"""
DOIQuery.read_package(pkg_dict['id'], create_if_none=True)
DOIQuery.read_package(pkg_dict["id"], create_if_none=True)

## IPackageController
def after_update(self, context, pkg_dict):
def after_dataset_update(self, context, pkg_dict):
"""
Dataset has been created/updated.
Check status of the dataset to determine if we should publish DOI to datacite
network.
"""
# Is this active and public? If so we need to make sure we have an active DOI
if pkg_dict.get('state', 'active') == 'active' and not pkg_dict.get(
'private', False
if pkg_dict.get("state", "active") == "active" and not pkg_dict.get(
"private", False
):
package_id = pkg_dict['id']
package_id = pkg_dict["id"]

# remove user-defined update schemas first (if needed)
context.pop('schema', None)
context.pop("schema", None)

# Load the package_show version of the dict
pkg_show_dict = toolkit.get_action('package_show')(
context, {'id': package_id}
pkg_show_dict = toolkit.get_action("package_show")(
context, {"id": package_id}
)

# Load or create the local DOI (package may not have a DOI if extension was loaded
Expand All @@ -84,35 +84,53 @@ def after_update(self, context, pkg_dict):
# metadata gets created before minting
client.set_metadata(doi.identifier, xml_dict)
client.mint_doi(doi.identifier, package_id)
toolkit.h.flash_success('DataCite DOI created')
toolkit.h.flash_success("DataCite DOI created")
else:
same = client.check_for_update(doi.identifier, xml_dict)
if not same:
# Not the same, so we want to update the metadata
client.set_metadata(doi.identifier, xml_dict)
toolkit.h.flash_success('DataCite DOI metadata updated')
toolkit.h.flash_success("DataCite DOI metadata updated")

return pkg_dict

# IPackageController
def after_show(self, context, pkg_dict):
def after_dataset_show(self, context, pkg_dict):
"""
Add the DOI details to the pkg_dict so it can be displayed.
"""
doi = DOIQuery.read_package(pkg_dict['id'])
doi = DOIQuery.read_package(pkg_dict["id"])
if doi:
pkg_dict['doi'] = doi.identifier
pkg_dict['doi_status'] = True if doi.published else False
pkg_dict['domain'] = get_site_url().replace('http://', '')
pkg_dict['doi_date_published'] = (
datetime.strftime(doi.published, '%Y-%m-%d') if doi.published else None
pkg_dict["doi"] = doi.identifier
pkg_dict["doi_status"] = True if doi.published else False
pkg_dict["domain"] = get_site_url().replace("http://", "")
pkg_dict["doi_date_published"] = (
datetime.strftime(doi.published, "%Y-%m-%d") if doi.published else None
)
pkg_dict['doi_publisher'] = toolkit.config.get('ckanext.doi.publisher')
pkg_dict["doi_publisher"] = toolkit.config.get("ckanext.doi.publisher")

def after_create(self, *args, **kwargs):
"""
CKAN 2.9 compat version of after_dataset_create.
"""
return self.after_dataset_create(*args, **kwargs)

def after_update(self, *args, **kwargs):
"""
CKAN 2.9 compat version of after_dataset_update.
"""
return self.after_dataset_update(*args, **kwargs)

def after_show(self, *args, **kwargs):
"""
CKAN 2.9 compat version of after_dataset_show.
"""
return self.after_dataset_show(*args, **kwargs)

# ITemplateHelpers
def get_helpers(self):
return {
'package_get_year': package_get_year,
'now': datetime.now,
'get_site_title': get_site_title,
"package_get_year": package_get_year,
"now": datetime.now,
"get_site_title": get_site_title,
}
19 changes: 17 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
version: "3"

services:
ckan:
latest:
build:
context: .
dockerfile: docker/Dockerfile
dockerfile: docker/Dockerfile_latest
environment:
PYTHONUNBUFFERED: 1
PYTHONDONTWRITEBYTECODE: 1
depends_on:
- db
- solr
- redis
volumes:
- ./ckanext:/base/src/ckanext-doi/ckanext
- ./tests:/base/src/ckanext-doi/tests

next:
build:
context: .
dockerfile: docker/Dockerfile_next
environment:
PYTHONUNBUFFERED: 1
PYTHONDONTWRITEBYTECODE: 1
Expand Down
File renamed without changes.
16 changes: 16 additions & 0 deletions docker/Dockerfile_next
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM naturalhistorymuseum/ckantest:next

WORKDIR /base/src/ckanext-doi

# copy over the source
COPY . .

# install the base + test dependencies
RUN pip install -e .[test]

# this entrypoint ensures our service dependencies (postgresql, solr and redis) are running before
# running the cmd
ENTRYPOINT ["/bin/bash", "/opt/waits/basic.sh"]

# run the tests with coverage output
CMD ["bash", "/opt/scripts/run-tests.sh", "ckanext.doi"]
12 changes: 12 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import pytest

from ckanext.doi.model.doi import doi_table


@pytest.fixture
def with_doi_table(reset_db):
"""
Simple fixture which resets the database and creates the doi table.
"""
reset_db()
doi_table.create(checkfirst=True)
18 changes: 5 additions & 13 deletions tests/test_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,14 @@
#
# This file is part of ckanext-doi
# Created by the Natural History Museum in London, UK
import pytest
from ckan.model import Session
from ckan.tests import factories
from datacite.errors import DataCiteNotFoundError
from unittest.mock import patch, MagicMock

from ckanext.doi.model.doi import DOI, doi_table

import pytest
from datacite.errors import DataCiteNotFoundError

@pytest.fixture
def with_doi_table(reset_db):
"""
Simple fixture which resets the database and creates the doi table.
"""
reset_db()
doi_table.create(checkfirst=True)
from ckan.model import Session
from ckan.tests import factories
from ckanext.doi.model.doi import DOI


@pytest.mark.filterwarnings('ignore::sqlalchemy.exc.SADeprecationWarning')
Expand Down
85 changes: 85 additions & 0 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from unittest.mock import patch, MagicMock

import pytest
from datacite.errors import DataCiteNotFoundError

from ckan.tests import factories
from ckan.tests.helpers import call_action
from ckanext.doi.model.crud import DOIQuery


@pytest.mark.filterwarnings("ignore::sqlalchemy.exc.SADeprecationWarning")
@pytest.mark.ckan_config("ckan.plugins", "doi")
@pytest.mark.ckan_config("ckanext.doi.prefix", "testing")
@pytest.mark.usefixtures("with_doi_table", "with_plugins")
class TestDOIPlugin:
def test_after_dataset_create(self):
# as well as testing the after_dataset_create functionality works on some level,
# this test is also here to confirm that after_dataset_create is called
# correctly whether you are on CKAN 2.9 or CKAN 2.10.

with patch("ckanext.doi.lib.api.DataCiteMDSClient") as mock_client_class:
# mock the datacite API to make it look like the DOI generated is new
mock_client = MagicMock(
metadata_get=MagicMock(side_effect=DataCiteNotFoundError())
)
mock_client_class.return_value = mock_client

# create a new dataset
dataset = factories.Dataset()

# check that a DOI is created in the database for this new dataset
assert DOIQuery.read_package(dataset["id"], create_if_none=False)

@pytest.mark.ckan_config("ckanext.doi.publisher", "argh!")
def test_after_dataset_update(self):
# as well as testing the after_dataset_update functionality works on some level,
# this test is also here to confirm that after_dataset_update is called
# correctly whether you are on CKAN 2.9 or CKAN 2.10.

# the udpate function has flashes in it which we don't care about
with patch("ckan.plugins.toolkit.h.flash_success"):
with patch("ckanext.doi.lib.api.DataCiteMDSClient") as mock_client_class:
# mock the datacite API to make it look like the DOI generated is new
mock_client = MagicMock(
metadata_get=MagicMock(side_effect=DataCiteNotFoundError())
)
mock_client_class.return_value = mock_client

# create a new dataset
dataset = factories.Dataset(title="test", author="Author, Test")

# reset our mock
mock_client.reset()

# update the dataset's title, this should trigger after_dataset_update
call_action("package_patch", id=dataset["id"], title="different")

# check that attempts have been made to mint the DOI
assert mock_client.metadata_post.called
assert mock_client.doi_post.called

@pytest.mark.ckan_config("ckanext.doi.publisher", "argh!")
@pytest.mark.ckan_config("ckanext.doi.site_url", "http://dois.are.great.org")
def test_after_dataset_show(self):
# as well as testing the after_dataset_show functionality works on some level,
# this test is also here to confirm that after_dataset_show is called correctly
# whether you are on CKAN 2.9 or CKAN 2.10.

with patch("ckanext.doi.lib.api.DataCiteMDSClient") as mock_client_class:
# mock the datacite API to make it look like the DOI generated is new
mock_client = MagicMock(
metadata_get=MagicMock(side_effect=DataCiteNotFoundError())
)
mock_client_class.return_value = mock_client

# create a new dataset
dataset = factories.Dataset()

doi = DOIQuery.read_package(dataset["id"], create_if_none=False)
package = call_action("package_show", id=dataset["id"])
assert package["doi"] == doi.identifier
assert package["doi_status"] is False
assert package["domain"] == "dois.are.great.org"
assert package["doi_date_published"] is None
assert package["doi_publisher"] == "argh!"

0 comments on commit a11cecc

Please sign in to comment.