Skip to content
Snippets Groups Projects
Commit 03fc80c2 authored by Vuillaume's avatar Vuillaume
Browse files

Merge branch 'add_codemeta2zenodo_module' into 'master'

add codemeta2zenodo package as a submodule to the eossr package

See merge request !1
parents 8216213f bdabd49a
No related branches found
No related tags found
1 merge request!1add codemeta2zenodo package as a submodule to the eossr package
__version__ = "0.1"
#!/usr/bin/env python
import json
codemeta_creators_fields = ['author', 'creator', 'maintainer', 'contributor']
codemeta_contributors_fields = \
['editor', 'producer', 'publisher', 'provider', 'sponsor']
codemeta_allowed_person_fields = \
codemeta_creators_fields + codemeta_contributors_fields
def parse_person_schema_property(person_property, contributor_field):
"""
Parse the Person Schema property correctly
Parameters:
--------
person_property: dict
dictionary codemeta key with the a list or a single Person property
item.
contributor_field : str
contributor type {'editor', 'producer', 'sponsor'} or publisher,
although the last one can only happen if `upload_type` is publication
(NOT SUPPORTED - contact E. Garcia by email).
Returns:
--------
zenodo_person: dict
dictionary with the correct zenodo syntax for all {author, contributor,
maintainer}.
"""
zenodo_person = {}
name = person_property['familyName']
if 'givenName' in person_property:
name += f', {person_property["givenName"]}'
zenodo_person['name'] = name
if "@id" in person_property:
if 'orcid.org/' in person_property["@id"]:
# reformat "https://orcid.org/0000-0002-5686-2078"
zenodo_person['orcid'] = person_property["@id"] \
.split('orcid.org/')[-1]
else:
zenodo_person['orcid'] = person_property["@id"]
if "affiliation" in person_property:
zenodo_person['affiliation'] = person_property['affiliation']['name']
# Parse correctly the contributors
if contributor_field in codemeta_contributors_fields:
if contributor_field == 'provider' or contributor_field == 'publisher':
zenodo_person['type'] = 'Other'
else:
# First letter of contributor type MUST be capitalized
# (not for two words' contributor !)
zenodo_person['type'] = contributor_field.title()
return zenodo_person
def add_author_metadata(zenodo_file, codemt_person_entry, person_field):
"""
Aux function to parse correctly all the authors, contributors and
maintainers that can be found at the codemeta.json file
zenodo_file: dict
metadata dictionary with the zenodo syntax
codemt_person_entry: list or dict
metadata dictionary key field with the codemeta syntax
person_field: str
codemeta key field specifying creator {author, contributor, maintainer,
creator}, or contributors {editor, sponsor, producer, project
manager...}
"""
full_contacts = {}
# First create the full contact agenda by field
if type(codemt_person_entry) is list:
for person_property in codemt_person_entry:
zenodo_person = parse_person_schema_property(person_property,
person_field)
# 'name' is the only key that MUST be contained in a
# person_property at least
full_contacts[zenodo_person['name']] = zenodo_person
else:
zenodo_person = parse_person_schema_property(codemt_person_entry,
person_field)
full_contacts[zenodo_person['name']] = zenodo_person
# then save each person by field and avoid duplicates
for person in full_contacts:
if person_field in codemeta_creators_fields:
# Contributors and maintainers in the same zenodo key
if 'creators' not in zenodo_file:
zenodo_file['creators'] = []
if full_contacts[person] not in zenodo_file['creators']:
zenodo_file['creators'].append(full_contacts[person])
else:
pass # avoid duplicates
elif person_field in codemeta_contributors_fields:
if 'contributors' not in zenodo_file:
zenodo_file['contributors'] = []
if full_contacts[person] not in zenodo_file['contributors']:
zenodo_file['contributors'].append(full_contacts[person])
else:
pass # avoid duplicates
def find_matching_metadata(codemeta_json):
"""
Please note that the following fields are ASSUMED. If they are not
correct, change them, or contact us otherwise.
* "access_right": "open"
* "language": "eng"
param codemeta_json: dict
already parsed dictionary containing the metadata of the codemeta.json
file
Returns:
--------
metadata_zenodo : dict
dictionary cotaining the metadata information found at the
codemeta.json file but written using the Zenodo syntax.
"""
# All the 'person type' allowed in the CodeMeta schema are listed in the
# 'codemeta_allowed_person_fields' list. However, the Zenodo schema
# does not accept certain codemeta 'person type' properties; like
# publisher and provider, nor all the extended schema.org 'person type'
# (actor, director, member, performer ...).
# The crosswalk will be limited to the 'codemeta_allowed_person_fields'
# list.
metadata_zenodo = {'language': 'eng',
'access_right': 'open'}
if codemeta_json["@type"] == "SoftwareSourceCode":
metadata_zenodo['upload_type'] = 'software'
else:
metadata_zenodo['upload_type'] = ''
print("\nCould not identify the type of schema in the `codemeta.json file`.\n"
"Thus the 'upload_type' at the `.zenodo.json` file was left EMPTY.\n"
"Please fill it up by yourself choosing from the following list - otherwise zenodo will NOT be able "
"to publish your entry:\n"
" * publication: Publication\n"
" * poster: Poster\n"
" * presentation: Presentation\n"
" * dataset: Dataset\n"
" * image: Image\n"
" * video: Video/Audio\n"
" * software: Software\n"
" * lesson: Lesson\n"
" * physicalobject: Physical object\n"
" * other: Other\n")
if 'name' in codemeta_json:
metadata_zenodo['title'] = codemeta_json['name']
if 'description' in codemeta_json:
metadata_zenodo['description'] = codemeta_json['description']
if 'softwareVersion' in codemeta_json and 'version' not in codemeta_json:
metadata_zenodo['version'] = codemeta_json['softwareVersion']
elif 'version' in codemeta_json and 'softwareVersion' not in codemeta_json:
metadata_zenodo['version'] = codemeta_json['version']
else:
metadata_zenodo['version'] = codemeta_json['version']
if 'keywords' in codemeta_json:
if type(codemeta_json['keywords']) == list:
metadata_zenodo['keywords'] = codemeta_json['keywords']
else:
metadata_zenodo['keywords'] = [codemeta_json['keywords']]
if 'releaseNotes' in codemeta_json:
metadata_zenodo['notes'] = "Release Notes: " + \
codemeta_json['releaseNotes']
if 'citation' in codemeta_json:
metadata_zenodo['references'] = codemeta_json['citation']
if 'datePublished' in codemeta_json:
metadata_zenodo['publication_date'] = codemeta_json['datePublished']
for person_type in codemeta_allowed_person_fields:
if person_type in codemeta_json:
add_author_metadata(metadata_zenodo, codemeta_json[person_type],
person_field=person_type)
return metadata_zenodo
class CodeMeta2ZenodoController(object):
"""Control the conversion of a codemeta file to a zenodo file"""
def __init__(self):
self.codemeta_data = {}
self.zenodo_data = {}
def load_codemeta(self, codemeta_filename):
"""Load `codemeta_filename` into the converter"""
with open(codemeta_filename) as infile:
self.codemeta_data = json.load(infile)
def convert_license(self):
license = self.codemeta_data.get('license', None)
if license is None:
print("No license information found.\n"
"This means a proprietary license.\n"
"Please contact us, ESCAPE encourages Open Source Science.\n")
return
if license.startswith('https://spdx.org/licenses/'):
self.zenodo_data['license'] = license.split('/')[-1]
else:
self.zenodo_data['license'] = 'other-open'
def convert(self):
"""Convert data over to zenodo format"""
self.zenodo_data = find_matching_metadata(self.codemeta_data)
self.convert_license()
def add_escape2020_community(self):
"""
Add compulsory information to the .zenodo.json file:
* zenodo community : ESCAPE2020
"""
self.zenodo_data["communities"] = [{"identifier": "escape2020"}]
def add_escape2020_grant(self):
"""
Add compulsory information to the .zenodo.json file:
* ESCAPE grant ID (zenodo syntax)
"""
self.zenodo_data["grants"] = [{"id": "10.13039/501100000780::824064"}]
def write_zenodo(self, zenodo_filename):
"""Write `zenodo_filename` after conversion"""
with open(zenodo_filename, 'w') as outfile:
json.dump(self.zenodo_data, outfile, indent=4, sort_keys=True)
def parse_codemeta_and_write_zenodo_metadata_file(codemeta_filename,
zenodo_outname):
"""
Reads the codemeta.json file and creates a new `.zenodo.json` file. This
file will contain the SAME information that in the codemeta.json file but
*** WITH THE ZENODO SYNTAX. ***
codemeta_filename: str or Path
path to the codemeta.json file
zenodo_outname: str or Path
path and name to the zenodo metada json file
NOT TO BE CHANGED. The file must be named `.zenodo.json` and be stored
in the root directory of the library.
"""
converter = CodeMeta2ZenodoController()
converter.load_codemeta(codemeta_filename)
converter.convert()
converter.add_escape2020_community()
converter.add_escape2020_grant()
converter.write_zenodo(zenodo_outname)
#!/usr/bin/env python
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
def query_yes_no(question, default="yes"):
"""
Ask a yes/no question via raw_input() and return their answer.
:param question: str
question to the user
:param default: str - "yes", "no" or None
resumed answer if the user just hits <Enter>.
"yes" or "no" will set a default answer for the user
None will require a clear answer from the user
:return: bool - True for "yes", False for "no"
"""
valid = {"yes": True, "y": True, "ye": True,
"no": False, "n": False}
if default is None:
prompt = " [y/n] "
elif default == "yes":
prompt = " [Y/n] "
elif default == "no":
prompt = " [y/N] "
else:
raise ValueError("invalid default answer: '%s'" % default)
while True:
sys.stdout.write(question + prompt)
choice = input().lower()
if default is not None and choice == '':
return valid[default]
else:
try:
return bool(strtobool(choice))
except:
sys.stdout.write("Please respond with 'yes' or 'no' "
"(or 'y' or 'n').\n")
def query_continue(question, default="no"):
"""
Ask a question and if the answer is no, exit the program.
Calls `query_yes_no`.
:param question: str
:param default: str
:return answer: bool - answer from query_yes_no
"""
answer = query_yes_no(question, default=default)
if not answer:
sys.exit("Program stopped by user")
else:
return answer
def main():
parser = argparse.ArgumentParser(
description="Converts a metadata descriptive files from the the CodeMeta to the Zenodo schema. "
"Creates a .zenodo.json file from a codemeta.json file."
)
parser.add_argument(
'--input_codemeta_file', '-i', type=str,
dest='codemeta_file',
help='Path to a codemeta.json file',
required=True
)
args = parser.parse_args()
codemeta_file = Path(args.codemeta_file)
# Check if file exists and it is named as it should
if not codemeta_file.exists():
print("\n\tThe input file doest not exists. Exiting.")
sys.exit(-1)
if not codemeta_file.name.startswith('codemeta') or not \
codemeta_file.name.endswith('.json'):
print(f"\n\t{codemeta_file.name} either does not starts with the `codemeta` prefix or "
f"either it does not finishes with a `.json` suffix. Exiting")
sys.exit(-1)
directory_codemeta = codemeta_file.parent.absolute()
zenodo_metadata_file = directory_codemeta / '.zenodo.json'
# Check overwrite zenodo file if exists
if zenodo_metadata_file.exists():
query_continue(
f"\nThe {zenodo_metadata_file.name} file already exists."
f"\nIf you continue you will overwrite the file with the metadata found in the {codemeta_file.name} file. "
f"\n\nAre you sure ?")
# Parse the codemeta.json file and create the .zenodo.json file
parse_codemeta_and_write_zenodo_metadata_file(
codemeta_file,
zenodo_metadata_file
)
print("\nConversion codemeta2zenodo done.\n")
if __name__ == "__main__":
main()
setup.py 0 → 100644
#!/usr/bin/env python
import re
from setuptools import setup, find_packages
entry_points = {'console_scripts': [
'eossr-codemeta2zenodo = eossr.scripts.eossr_codemeta2zenodo:main'
]
}
def get_property(prop, project):
result = re.search(r'{}\s*=\s*[\'"]([^\'"]*)[\'"]'.format(prop),
open(project + '/__init__.py').read())
return result.group(1)
setup(
name='eossr',
version=get_property('__version__', 'eossr'),
description="ESCAPE OSSR library",
# install_requires=[],
packages=find_packages(),
# scripts=[],
# tests_require=['pytest'],
author='Thomas Vuillaume & Enrique Garcia',
author_email='vuillaume<at>lapp.in2p3.fr',
url='https://gitlab.in2p3.fr/escape2020/wp3/eossr',
license='MIT',
entry_points=entry_points
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment