From 797ec89e78137b4ad0e5af88c0b551e12bdfb33e Mon Sep 17 00:00:00 2001 From: jose-luis-rs Date: Sun, 8 Dec 2024 00:22:00 +0100 Subject: [PATCH] Added affiliations for zenodo framework --- .github/workflows/check_metadata.yaml | 4 +- .zenodo.json | 108 ++++++++++++++------- AUTHORS | 14 +-- CONTRIBUTORS | 4 +- COPYRIGHT | 9 ++ meta_update.py | 133 +++++++++++--------------- 6 files changed, 146 insertions(+), 126 deletions(-) create mode 100644 COPYRIGHT diff --git a/.github/workflows/check_metadata.yaml b/.github/workflows/check_metadata.yaml index 024d4cc8..a8bf0827 100644 --- a/.github/workflows/check_metadata.yaml +++ b/.github/workflows/check_metadata.yaml @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: 2024 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH, Darmstadt, Germany # -# SPDX-License-Identifier: CC0-1.0 +# SPDX-License-Identifier: LGPL-3.0-or-later name: Check AUTHORS and CONTRIBUTORS in metadata @@ -9,13 +9,11 @@ on: paths: - AUTHORS - CONTRIBUTORS - - codemeta.json - .zenodo.json pull_request: paths: - AUTHORS - CONTRIBUTORS - - codemeta.json - .zenodo.json jobs: diff --git a/.zenodo.json b/.zenodo.json index de8c53ba..c9c17fef 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,65 +1,99 @@ { + "description": "The SOFIA module within the R3BRoot framework encapsulates the configuration and functionality of the SOFIA detectors, specifically designed for fission experiments conducted with the GLAD spectrometer. R3BRoot, built on the FairRoot framework, serves as a software environment for performing detailed Monte Carlo simulations and processing experimental data from R3B (Reactions with Relativistic Radioactive Beams) experiments. These capabilities include precise modeling of detector geometry, particle tracking, event reconstruction, and physics analysis, supporting the investigation of fission dynamics and nuclear structure in high-energy heavy-ion collision scenarios at the GSI-FAIR facility.", + "related_identifiers": [ + { + "identifier": "https://github.com/R3BRootGroup/sofia/", + "relation": "isSupplementTo", + "resource_type": "software", + "scheme": "url" + } + ], + "maintainers": [ + { + "name": "Rodriguez-Sánchez, Jose Luis", + "orcid": "0000-0002-4702-5294", + "email": "j.l.rodriguez.sanchez@udc.es", + "affiliation": "CITENI, Industrial Campus of Ferrol, University of Coruña, 15403 Ferrol, Spain" + } + ], + "title": "SOFIA Software", + "upload_type": "software", + "softwareRequirements": [ + "FairSoft, FairRoot, R3BRoot" + ], + "programmingLanguage": [ + "C", + "C++" + ], + "runtimePlatform": [ + "ROOT" + ], + "operatingSystem": [ + "Linux", + "macOS" + ], + "keywords": [ + "Geant4", + "c-plus-plus", + "cmake", + "vmc", + "modular", + "Track reconstruction", + "Simulation", + "Data analysis", + "Structure and dynamics of nuclei", + "Nuclear reactions", + "Nuclear fission", + "SOFIA/R3B experiments", + "GSI/FAIR facility" + ], "creators": [ { - "name": "Alvarez Pol, Hector", - "orcid": "0000-0001-9643-6252" + "name": "Rodríguez-Sánchez, Jose Luis", + "orcid": "0000-0002-4702-5294", + "email": "j.l.rodriguez.sanchez@udc.es", + "affiliation": "CITENI, Industrial Campus of Ferrol, University of Coruña, 15403 Ferrol, Spain" }, { - "name": "Chatillon, Audrey" + "name": "Alvarez Pol, Hector", + "orcid": "0000-0001-9643-6252", + "affiliation": "IGFAE, University of Santiago de Compostela, 15782 Santiago de Compostela, Spain" }, { - "name": "Garcia Jimenez, Gabriel" + "name": "Chatillon, Audrey", + "affiliation": "CEA, DAM, DIF, 91297 Arpajon, France" }, { - "name": "Gra\u00f1a Gonzalez, Antia", - "orcid": "0000-0002-0842-4110" + "name": "García-Jiménez, Gabriel", + "affiliation": "IGFAE, University of Santiago de Compostela, 15782 Santiago de Compostela, Spain" }, { - "name": "Morfouace, Pierre", - "orcid": "0000-0002-2131-2199" + "name": "Graña-González, Antia", + "orcid": "0000-0002-0842-4110", + "affiliation": "CITENI, Industrial Campus of Ferrol, University of Coruña, 15403 Ferrol, Spain" }, { - "name": "Rodriguez Sanchez, Jose Luis", - "orcid": "0000-0002-4702-5294" + "name": "Morfouace, Pierre", + "orcid": "0000-0002-2131-2199", + "affiliation": "CEA, DAM, DIF, 91297 Arpajon, France" }, { "name": "Taniuchi, Ryo", - "orcid": "0000-0002-8057-7074" + "orcid": "0000-0002-8057-7074", + "affiliation": "School of Physics, Engineering and Technology, University of York, YO10 5DD York, UK" } ], "contributors": [ { "type": "Other", - "name": "Mayer, Jan" + "name": "Mayer, Jan", + "affiliation": "Technical University of Berlin, 10587 Berlin, Germany" }, { "type": "Other", "name": "Wang, Yanzhao", - "orcid": "0000-0002-7006-7986" - } - ], - "description": "The SOFIA module within the R3BRoot framework encapsulates the configuration and functionality of the SOFIA detectors, specifically designed for fission experiments conducted with the GLAD spectrometer. R3BRoot, built on the FairRoot framework, serves as a software environment for performing detailed Monte Carlo simulations and processing experimental data from R3B (Reactions with Relativistic Radioactive Beams) experiments. These capabilities include precise modeling of detector geometry, particle tracking, event reconstruction, and physics analysis, supporting the investigation of fission dynamics and nuclear structure in high-energy heavy-ion collision scenarios at the GSI-FAIR facility.", - "related_identifiers": [ - { - "identifier": "https://github.com/R3BRootGroup/sofia/", - "relation": "isSupplementTo", - "resource_type": "software", - "scheme": "url" + "orcid": "0000-0002-7006-7986", + "affiliation": "Universität zu Köln, 50923 Köln, Germany" } - ], - "title": "SOFIA software", - "upload_type": "software", - "keywords": [ - "Geant4", - "c-plus-plus", - "cmake", - "vmc", - "modular", - "Event reconstruction", - "Simulation", - "Data analysis", - "Structure and dynamics of nuclei", - "Nuclear reactions", - "Nuclear fission" ] } diff --git a/AUTHORS b/AUTHORS index d33add12..fc16f83e 100755 --- a/AUTHORS +++ b/AUTHORS @@ -1,7 +1,7 @@ -Alvarez Pol, Hector [https://orcid.org/0000-0001-9643-6252] -Chatillon, Audrey -Garcia Jimenez, Gabriel -Graña Gonzalez, Antia [https://orcid.org/0000-0002-0842-4110] -Morfouace, Pierre [https://orcid.org/0000-0002-2131-2199] -Rodriguez Sanchez, Jose Luis [https://orcid.org/0000-0002-4702-5294] -Taniuchi, Ryo [https://orcid.org/0000-0002-8057-7074] +Rodríguez-Sánchez, Jose Luis [j.l.rodriguez.sanchez@udc.es] [https://orcid.org/0000-0002-4702-5294] [CITENI, Industrial Campus of Ferrol, University of Coruña, 15403 Ferrol, Spain] +Alvarez Pol, Hector [https://orcid.org/0000-0001-9643-6252] [IGFAE, University of Santiago de Compostela, 15782 Santiago de Compostela, Spain] +Chatillon, Audrey [CEA, DAM, DIF, 91297 Arpajon, France] +García-Jiménez, Gabriel [IGFAE, University of Santiago de Compostela, 15782 Santiago de Compostela, Spain] +Graña-González, Antia [https://orcid.org/0000-0002-0842-4110] [CITENI, Industrial Campus of Ferrol, University of Coruña, 15403 Ferrol, Spain] +Morfouace, Pierre [https://orcid.org/0000-0002-2131-2199] [CEA, DAM, DIF, 91297 Arpajon, France] +Taniuchi, Ryo [https://orcid.org/0000-0002-8057-7074] [School of Physics, Engineering and Technology, University of York, YO10 5DD York, UK] diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 853c7d72..482aeb10 100755 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1,2 +1,2 @@ -Mayer, Jan -Wang, Yanzhao [https://orcid.org/0000-0002-7006-7986] +Mayer, Jan [Technical University of Berlin, 10587 Berlin, Germany] +Wang, Yanzhao [https://orcid.org/0000-0002-7006-7986] [Universität zu Köln, 50923 Köln, Germany] diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 00000000..bad46448 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,9 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: sofia +Upstream-Contact: Jose Luis Rodríguez-Sánchez +Source: https://github.com/R3BRootGroup/sofia + +Files: * +Copyright: 2017-2024, GSI Helmholtzzentrum fuer Schwerionenforschung GmbH +Copyright: 2017-2024, [see AUTHORS file] +Copyright: 2017-2024, [see CONTRIBUTORS file] diff --git a/meta_update.py b/meta_update.py index 0f93aae7..b55cd05f 100755 --- a/meta_update.py +++ b/meta_update.py @@ -1,10 +1,13 @@ #! /usr/bin/env python3 +# Copyright (C) 2019-2024 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH +# +# SPDX-License-Identifier: LGPL-3.0-or-later -from argparse import ArgumentParser -import json import re +import json from collections import OrderedDict - +from argparse import ArgumentParser +import os class Manipulator(object): def __str__(self): @@ -13,15 +16,24 @@ def __str__(self): def load(self, filename=None): if filename is None: filename = self.default_filename - with open(filename, 'rb') as fp: - self.data = json.load(fp, object_pairs_hook=OrderedDict) + try: + with open(filename, 'r', encoding='utf8') as fp: + self.data = json.load(fp, object_pairs_hook=OrderedDict) + except FileNotFoundError as e: + print(f'Error: The file {filename} was not found. {e}') + except json.JSONDecodeError as e: + print(f'Error in JSON file: {e}') + raise # Raise the error and stop execution def save(self, filename=None, indent=2): if filename is None: filename = self.default_filename - with open(filename, 'w', encoding='utf8') as fp: - json.dump(self.data, fp, indent=indent) - fp.write('\n') + try: + with open(filename, 'w', encoding='utf8') as fp: + json.dump(self.data, fp, indent=indent, ensure_ascii=False) + fp.write('\n') + except IOError as e: + print(f'Error saving the file {filename}: {e}') @staticmethod def _dict_entry_cmp(dict1, dict2, field1, field2=None): @@ -29,79 +41,47 @@ def _dict_entry_cmp(dict1, dict2, field1, field2=None): field2 = field1 if (field1 in dict1) and (field2 in dict2): return dict1[field1] == dict2[field2] - else: - return False + return False def _handle_person_list_file(self, filename, field_name, **kwargs): - fp = open(filename, 'r', encoding='utf8') - person_list = self.data.setdefault(field_name, []) - for i, line in enumerate(fp, start=0): - line = line.strip() - m = self.findregex.match(line) - if m is None: - raise RuntimeError("Could not analyze line %r" % line) - found_entry = self._find_person_entry(person_list, m.groupdict()) - entry = self.update_person_entry(found_entry, m.groupdict(), - **kwargs) - if found_entry is None: - person_list.insert(i, entry) - - -class CodeMetaManipulator(Manipulator): - default_filename = 'codemeta.json' - findregex = re.compile(r'^(?P[-\w\s]*[-\w]),\s*' - r'(?P[-\w\s]*[-\w])\s*' - r'(?:<(?P\S+@\S+)>)?\s*' - r'(\[(?P\S+)\])?$') - - @classmethod - def _find_person_entry(cls, person_list, matchdict): - # orcid is unique - for entry in person_list: - if cls._dict_entry_cmp(entry, matchdict, '@id', 'orcid'): - return entry - for entry in person_list: - if cls._dict_entry_cmp(entry, matchdict, 'email'): - return entry - if cls._dict_entry_cmp(entry, matchdict, 'familyName') \ - and cls._dict_entry_cmp(entry, matchdict, 'givenName'): - return entry - return None - - @staticmethod - def update_person_entry(entry, matchdict): - if entry is None: - entry = OrderedDict() - entry['@type'] = 'Person' - for field in ('orcid', 'givenName', 'familyName', 'email'): - val = matchdict.get(field, None) - if val is not None: - if field == 'orcid': - entry['@id'] = val - else: - entry[field] = val - return entry - - def update_authors(self): - self._handle_person_list_file('AUTHORS', 'author') - self._handle_person_list_file('CONTRIBUTORS', 'contributor') - - def version(self, new_version): - self.data['softwareVersion'] = new_version + # Check if the file exists before trying to open it + if not os.path.exists(filename): + print(f'File {filename} not found, skipping.') + return + try: + with open(filename, 'r', encoding='utf8') as fp: + person_list = self.data.setdefault(field_name, []) + for i, line in enumerate(fp, start=0): + line = line.strip() + m = self.findregex.match(line) + if m is None: + print(f"Could not analyze line: {line}") + continue # Skip malformed lines + found_entry = self._find_person_entry(person_list, m.groupdict()) + entry = self.update_person_entry(found_entry, m.groupdict(), **kwargs) + if found_entry is None: + person_list.insert(i, entry) + except FileNotFoundError as e: + print(f'Error: The file {filename} was not found. {e}') + except RuntimeError as e: + print(e) class ZenodoManipulator(Manipulator): default_filename = '.zenodo.json' - findregex = re.compile(r'^(?P[-\w\s,]*[-\w])\s*' - r'(?:<(?P\S+@\S+)>)?\s*' - r'(\[https://orcid\.org/(?P\S+)\])?$') + # Improved regular expression to handle different author formats + findregex = re.compile(r'^(?P[-\w\s,]*[-\w])\s*' # Name + r'(?:\[(?P\S+@\S+)\])?\s*' # Optional email + r'(\[https://orcid\.org/(?P\S+)\])?\s*' # Optional ORCID + r'(\[(?P[^\]]+)\])?$') # Optional affiliation @classmethod def _find_person_entry(cls, person_list, matchdict): - # Match on orcid first + # First attempt to match using ORCID for entry in person_list: if cls._dict_entry_cmp(entry, matchdict, 'orcid'): return entry + # If not, attempt to match by name for entry in person_list: if cls._dict_entry_cmp(entry, matchdict, 'name'): return entry @@ -113,16 +93,16 @@ def update_person_entry(entry, matchdict, contributor_type=None): entry = OrderedDict() if contributor_type: entry['type'] = contributor_type - for field in ('name', 'orcid'): + for field in ('name', 'orcid', 'email', 'affiliation'): val = matchdict.get(field, None) if val is not None: entry[field] = val return entry def update_authors(self): + # Update the authors and contributors if the files exist self._handle_person_list_file('AUTHORS', 'creators') - self._handle_person_list_file('CONTRIBUTORS', 'contributors', - contributor_type='Other') + self._handle_person_list_file('CONTRIBUTORS', 'contributors', contributor_type='Other') def save(self, filename=None): super().save(filename, 4) @@ -132,16 +112,15 @@ def version(self, new_version): def main(): - parser = ArgumentParser(description='Update codemeta.json and ' - '.zenodo.json') + parser = ArgumentParser(description='Update .zenodo.json') parser.add_argument('--set-version', dest='newversion') args = parser.parse_args() - for manipulator in (CodeMetaManipulator(), ZenodoManipulator()): + for manipulator in (ZenodoManipulator(),): try: manipulator.load() - except FileNotFoundError as e: - print('*** Skipping {}: {}'.format(manipulator, e)) + except Exception as e: + print(f'*** Error loading {manipulator}: {e}') continue if args.newversion is not None: manipulator.version(args.newversion)