diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..07a5061cd7a4ff3f280c7b267ffdbfd3e9f746e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Compiled files +__pycache__ +.pytest_cache +.coverage + +# Packages/installer info +*.egg +*.eggs +*.egg-info +dist +build + +# Mac OSX +.DS_Store +default.profraw + +# Pycharm editor project files +.idea \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..abc50b24075ff92f18832462441b969f54908035 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,37 @@ +stages: + - install + - test + +.junit_template: &junit_definition + artifacts: + reports: + junit: "junit*.xml" + + +install_py37: + stage: install + image: python:3.7-buster + script: + - apt-get -y update + - python setup.py install + - eossr-codemeta2zenodo --help + - eossr-codemeta2zenodo --input_codemeta_file ./codemeta.json + - cat .zenodo.json + only: + - branches + +test_py37: + stage: test + image: python:3.7-buster + script: + - apt-get -y update + - pip install pytest pytest-cov + - pip install -e . + - pytest eossr/ + --junitxml=junit_py37.xml + --color=yes + --verbose + --cov=eossr + --cov-report=xml + --cov-report=term + <<: *junit_definition diff --git a/README.md b/README.md index 11f775b7a4f17be76ffd9e5031df28a4100e6306..e6ab968198bdddc9baa47b9753b3bb0347bacb14 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ # eossr +[]( +https://gitlab.in2p3.fr/escape2020/wp3/eossr/-/commits/master) +[](https://opensource.org/licenses/MIT) +[]( +https://gitlab.in2p3.fr/escape2020/wp3/eossr/-/commits/master) + + ESCAPE OSSR library \ No newline at end of file diff --git a/codemeta.json b/codemeta.json index 67249aa0420229987911746b00f6ad4fd402828e..478f5e9a8fb117629764f888721badf574629f37 100644 --- a/codemeta.json +++ b/codemeta.json @@ -39,13 +39,13 @@ } ], "keywords": [], - "identifier": "", "runtimePlatform": "", "downloadUrl": "", "installUrl": "", "releaseNotes": "", "datePublished": "", "dateModified": "", + "operatingSystem": "", "maintainer": { "@type": "Person", "@id": "https://orcid.org/0000-0003-2224-4594", diff --git a/eossr/metadata/tests/samples/codemeta_contributors_sample.json b/eossr/metadata/tests/samples/codemeta_contributors_sample.json new file mode 100644 index 0000000000000000000000000000000000000000..f294c29b72126675cea8676dbe066a613b4692b6 --- /dev/null +++ b/eossr/metadata/tests/samples/codemeta_contributors_sample.json @@ -0,0 +1,55 @@ +{ + "@context": "https://doi.org/10.5063/schema/codemeta-2.0", + "@type": "SoftwareSourceCode", + "name": "Codemeta sample with all king of accepted Zenodo schema person properties.", + "version": "0.1", + "author": [ + { + "@type": "Person", + "givenName": "Author-and-Creator-same-person-name", + "familyName": "Author-and-Creator-same-person-surname" + } + ], + "creator": { + "@type": "Person", + "givenName": "Author-and-Creator-same-person-name", + "familyName": "Author-and-Creator-same-person-surname" + }, + "maintainer": { + "@type": "Person", + "givenName": "Maintainer-name", + "familyName": "Maintainer-surname" + }, + "contributor": [ + { + "@type": "Person", + "givenName": "Contributor-name", + "familyName": "Contributor-surname" + } + ], + "producer": { + "@type": "Person", + "givenName": "Producer-name", + "familyName": "Producer-surname" + }, + "editor": { + "@type": "Person", + "givenName": "Editor-name", + "familyName": "Editor-surname" + }, + "publisher": { + "@type": "Person", + "givenName": "Publisher-name", + "familyName": "Publisher-surname" + }, + "provider": { + "@type": "Person", + "givenName": "Provider-name", + "familyName": "Provider-surname" + }, + "sponsor": { + "@type": "Person", + "givenName": "Sponsor-name", + "familyName": "Sponsor-surname" + } +} \ No newline at end of file diff --git a/eossr/metadata/tests/samples/codemeta_sample1.json b/eossr/metadata/tests/samples/codemeta_sample1.json new file mode 100644 index 0000000000000000000000000000000000000000..bfa4061db4ae33807ff13b02996b31cc78fa983b --- /dev/null +++ b/eossr/metadata/tests/samples/codemeta_sample1.json @@ -0,0 +1,169 @@ +{ + "@context": "https://doi.org/10.5063/schema/codemeta-2.0", + "@type": "SoftwareSourceCode", + "identifier": "CodeMeta", + "description": "CodeMeta is a concept vocabulary that can be used to standardize the exchange of software metadata across repositories and organizations.", + "name": "CodeMeta: Minimal metadata schemas for science software and code, in JSON-LD", + "codeRepository": "https://github.com/codemeta/codemeta", + "issueTracker": "https://github.com/codemeta/codemeta/issues", + "license": "https://spdx.org/licenses/Apache-2.0", + "version": "2.0", + "author": [ + { + "@type": "Person", + "givenName": "Carl", + "familyName": "Boettiger", + "email": "cboettig@gmail.com", + "@id": "http://orcid.org/0000-0002-1642-628X" + }, + { + "@type": "Person", + "givenName": "Matthew B.", + "familyName": "Jones", + "email": "jones@nceas.ucsb.edu", + "@id": "http://orcid.org/0000-0003-0077-4738" + } + ], + "contributor": [ + { + "@type": "Person", + "givenName": "Abby Cabunoc", + "familyName": "Mayes", + "email": "abbycabs@gmail.com" + }, + { + "@type": "Person", + "givenName": "Arfon", + "familyName": "Smith", + "email": "arfon.smith@gmail.com", + "@id": "http://orcid.org/0000-0002-3957-2474" + }, + { + "@type": "Person", + "givenName": "Peter", + "familyName": "Slaughter", + "email": "slaughter@nceas.ucsb.edu", + "@id": "http://orcid.org/0000-0002-2192-403X" + }, + { + "@type": "Person", + "givenName": "Kyle", + "familyName": "Niemeyer", + "email": "Kyle.Niemeyer@oregonstate.edu", + "@id": "http://orcid.org/0000-0003-4425-7097" + }, + { + "@type": "Person", + "givenName": "Yolanda", + "familyName": "Gil", + "email": "GIL@ISI.EDU", + "@id": "http://orcid.org/0000-0001-8465-8341" + }, + { + "@type": "Person", + "givenName": "Krzysztof", + "familyName": "Nowak" + }, + { + "@type": "Person", + "givenName": "Martin", + "familyName": "Fenner", + "@id": "http://orcid.org/0000-0003-1419-2405" + }, + { + "@type": "Person", + "givenName": "Mark", + "familyName": "Hahnel", + "@id": "http://orcid.org/0000-0003-4741-0309" + }, + { + "@type": "Person", + "givenName": "Luke", + "familyName": "Coy", + "email": "luke.coy@rit.edu" + }, + { + "@type": "Person", + "givenName": "Alice", + "familyName": "Allen", + "email": "aallen@ascl.net", + "@id": "http://orcid.org/0000-0003-3477-2845" + }, + { + "@type": "Person", + "givenName": "Mercè", + "familyName": "Crosas", + "@id": "http://orcid.org/0000-0003-1304-1939" + }, + { + "@type": "Person", + "givenName": "Ashley", + "familyName": "Sands", + "@id": "http://orcid.org/0000-0001-5636-0433" + }, + { + "@type": "Person", + "givenName": "Neil", + "familyName": "Chue Hong", + "email": "n.chuehong@epcc.ed.ac.uk", + "@id": "http://orcid.org/0000-0002-8876-7606" + }, + { + "@type": "Person", + "givenName": "Patricia", + "familyName": "Cruse", + "@id": "http://orcid.org/0000-0002-9300-5278" + }, + { + "@type": "Person", + "givenName": "Dan", + "familyName": "Katz", + "email": "dskatz@illinois.edu", + "@id": "http://orcid.org/0000-0003-2720-0339" + }, + { + "@type": "Person", + "givenName": "Carole", + "familyName": "Goble", + "email": "carole.goble@manchester.ac.uk", + "@id": "http://orcid.org/0000-0003-1219-2137" + }, + { + "@type": "Person", + "givenName": "Carl", + "familyName": "Boettiger", + "email": "cboettig@gmail.com", + "@id": "http://orcid.org/0000-0002-1642-628X" + }, + { + "@type": "Person", + "givenName": "Stephan", + "familyName": "Druskat", + "email": "mail@sdruskat.net", + "@id": "http://orcid.org/0000-0003-4925-7248" + } + ], + "maintainer": { + "@type": "Person", + "givenName": "Carl", + "familyName": "Boettiger", + "email": "cboettig@gmail.com", + "@id": "http://orcid.org/0000-0002-1642-628X" + }, + "contIntegration": "https://travis-ci.org/codemeta/codemeta", + "developmentStatus": "active", + "downloadUrl": "https://github.com/codemeta/codemeta/archive/2.0.zip", + "funder": { + "@id": "https://doi.org/10.13039/100000001", + "@type": "Organization", + "name": "National Science Foundation" + }, + "funding":"1549758; Codemeta: A Rosetta Stone for Metadata in Scientific Software", + "keywords": [ + "metadata", + "software" + ], + "dateCreated":"2017-06-05", + "datePublished":"2017-06-05", + "programmingLanguage": "JSON-LD" +} diff --git a/eossr/metadata/tests/test_codemeta2zenodo.py b/eossr/metadata/tests/test_codemeta2zenodo.py new file mode 100644 index 0000000000000000000000000000000000000000..779360d50915c27ed6f72109f7c47270d586766e --- /dev/null +++ b/eossr/metadata/tests/test_codemeta2zenodo.py @@ -0,0 +1,167 @@ +import json +import unittest +import tempfile +from os.path import dirname, realpath, join + +SAMPLES_DIR = join(dirname(realpath(__file__)), "samples") +ROOT_DIR = dirname(realpath("codemeta.json")) + + +codemeta_entries = [ + "@context", + "@type", + "name", + "description", + "license", + "softwareVersion", + "developmentStatus", + "codeRepository", + "dateCreated", + "isAccessibleForFree", + "isPartOf", + "contIntegration", + "issueTracker", + "readme", + "buildInstructions", + "operatingSystem", + "author", + "contributor", + "maintainer", + "funder", + "funding", + "programmingLanguage", + "softwareRequirements", + "keywords", + "downloadUrl", + "installUrl", + "dateModified", + "datePublished", + "runtimePlatform", + "releaseNotes" +] + +zenodo_entries = [ + "upload_type", + "title", + "description", + "language", + "access_right", + "version", + "keywords", + "notes", + "license", + "publication_date", + "creators", + # #"communities", + # #"grants", + # #"contributors", + # #"references", +] + + +def test_Codemeta2ZenodoController(): + from eossr.metadata.codemeta2zenodo import CodeMeta2ZenodoController + + converter = CodeMeta2ZenodoController() + assert converter.codemeta_data == {} + assert converter.zenodo_data == {} + + codemeta_file = join(ROOT_DIR, "codemeta.json") + converter.load_codemeta(codemeta_file) + assert converter.codemeta_data != {} + assert all(key in converter.codemeta_data.keys() for key in codemeta_entries) + + converter.convert() + assert converter.zenodo_data != {} + assert converter.zenodo_data["language"] == "eng" + assert converter.zenodo_data["access_right"] == "open" + assert all(key in converter.zenodo_data.keys() for key in zenodo_entries) + + converter.add_escape2020_community() + assert converter.zenodo_data["communities"] == [{"identifier": "escape2020"}] + converter.add_escape2020_grant() + assert converter.zenodo_data["grants"] == [{"id": "10.13039/501100000780::824064"}] + + +def test_add_author_metadata(): + from eossr.metadata.codemeta2zenodo import add_author_metadata, codemeta_allowed_person_fields + + with open(join(SAMPLES_DIR, "codemeta_contributors_sample.json")) as f: + codemeta_metadata = json.load(f) + zenodo_metadata = {} + + assert all(person in codemeta_metadata.keys() for person in codemeta_allowed_person_fields) + + for person in codemeta_allowed_person_fields: + add_author_metadata(zenodo_metadata, + codemeta_metadata[person], + person + ) + + assert 'creators' in zenodo_metadata.keys() + # 4 'creators' one repeated, should not be duplicated. + # Maintainer and Contributor. Author and Creator are the same person + assert len(zenodo_metadata['creators']) == 3 + + assert 'contributors' in zenodo_metadata.keys() + # Editor, Producer, Publisher, Provider and Sponsor + assert len(zenodo_metadata['contributors']) == 5 + + +def test_parse_person_schema_property(): + from eossr.metadata.codemeta2zenodo import \ + parse_person_schema_property, \ + codemeta_contributors_fields + + with open(join(SAMPLES_DIR, "codemeta_contributors_sample.json")) as f: + codemeta_metadata = json.load(f) + + for person in codemeta_contributors_fields: + zenodo_metadata = parse_person_schema_property(codemeta_metadata[person], + person) + if person == 'editor': + assert zenodo_metadata['type'] == 'Editor' + elif person == 'producer': + assert zenodo_metadata['type'] == 'Producer' + elif person == 'sponsor': + assert zenodo_metadata['type'] == 'Sponsor' + else: + assert zenodo_metadata['type'] == 'Other' + + +class TestConverting(unittest.TestCase): + def test_sample_file_conversion(self): + from eossr.metadata.codemeta2zenodo import parse_codemeta_and_write_zenodo_metadata_file + + outfile = tempfile.NamedTemporaryFile(delete=True) + parse_codemeta_and_write_zenodo_metadata_file( + join(SAMPLES_DIR, "codemeta_sample1.json"), outfile.name + ) + + json.load(outfile) + + def test_root_codemeta_conversion(self): + from eossr.metadata.codemeta2zenodo import parse_codemeta_and_write_zenodo_metadata_file + + outfile = tempfile.NamedTemporaryFile(delete=True) + parse_codemeta_and_write_zenodo_metadata_file( + join(ROOT_DIR, "codemeta.json"), outfile.name + ) + json.load(outfile) + + +class TestLicense(unittest.TestCase): + def test_license1(self): + from eossr.metadata.codemeta2zenodo import CodeMeta2ZenodoController + + converter = CodeMeta2ZenodoController() + converter.convert_license() + assert 'license' not in converter.zenodo_data + + converter.codemeta_data['license'] = 'https://spdx.org/licenses/MIT' + converter.convert_license() + assert converter.zenodo_data['license'] == 'MIT' + + converter.codemeta_data['license'] = './COPYING' + converter.convert_license() + assert converter.zenodo_data['license'] == 'other-open' diff --git a/eossr/scripts/eossr_codemeta2zenodo.py b/eossr/scripts/eossr_codemeta2zenodo.py index 1a50f91fcbd67e0ea7fcf29a9aa524523dae6a63..1970b2c815e6bb04d4066888dd3cf6dfaa1e52f8 100644 --- a/eossr/scripts/eossr_codemeta2zenodo.py +++ b/eossr/scripts/eossr_codemeta2zenodo.py @@ -4,7 +4,7 @@ import sys import argparse from pathlib import Path from distutils.util import strtobool -from codemeta2zenodo.crosswalk.codemeta2zenodo_crosswalk import parse_codemeta_and_write_zenodo_metadata_file +from eossr.metadata.codemeta2zenodo import parse_codemeta_and_write_zenodo_metadata_file def query_yes_no(question, default="yes"): diff --git a/eossr/scripts/tests/test_eossr_codemeta2zenodo.py b/eossr/scripts/tests/test_eossr_codemeta2zenodo.py new file mode 100644 index 0000000000000000000000000000000000000000..316b254cbbc7b28e7c75244c75f9c3b231146208 --- /dev/null +++ b/eossr/scripts/tests/test_eossr_codemeta2zenodo.py @@ -0,0 +1,18 @@ +import subprocess +from os.path import dirname, realpath, join + +ROOT_DIR = dirname(realpath("codemeta.json")) + + +def run_script(*args): + result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8') + + if result.returncode != 0: + raise ValueError( + f"Running {args[0]} failed with return code {result.returncode}" + f", output: \n {result.stdout}" + ) + + +def test_codemeta2zenodo(): + run_script("eossr-codemeta2zenodo", "-i", join(ROOT_DIR, "codemeta.json"))