diff --git a/swh/indexer/codemeta.py b/swh/indexer/codemeta.py --- a/swh/indexer/codemeta.py +++ b/swh/indexer/codemeta.py @@ -69,7 +69,6 @@ raise ValueError("empty file") data_sources = set(header) - {"Parent Type", "Property", "Type", "Description"} - assert "codemeta-V1" in data_sources codemeta_translation = {data_source: {} for data_source in data_sources} terms = set() diff --git a/swh/indexer/data/composer.csv b/swh/indexer/data/composer.csv new file mode 100644 --- /dev/null +++ b/swh/indexer/data/composer.csv @@ -0,0 +1,68 @@ +Property,Composer +codeRepository,support.source +programmingLanguage, +runtimePlatform, +targetProduct, +applicationCategory, +applicationSubCategory, +downloadUrl, +fileSize, +installUrl, +memoryRequirements, +operatingSystem, +permissions, +processorRequirements, +releaseNotes, +softwareHelp, +softwareRequirements,require +softwareVersion,version +storageRequirements, +supportingData, +author,authors +citation, +contributor, +copyrightHolder, +copyrightYear, +dateCreated, +dateModified, +datePublished, +editor, +encoding, +fileFormat, +funder, +keywords,keywords +license,license +producer, +provider, +publisher, +sponsor, +version,version +isAccessibleForFree, +isPartOf, +hasPart, +position, +description,description +identifier,name +name,name +sameAs, +url,homepage +relatedLink, +givenName, +familyName, +email,author.email +affiliation, +identifier, +name,author.name +address, +type, +id, +softwareSuggestions,suggest +maintainer, +contIntegration, +buildInstructions, +developmentStatus, +embargoDate, +funding, +issueTracker,support.issues +referencePublication, +readme, \ No newline at end of file diff --git a/swh/indexer/metadata_dictionary/__init__.py b/swh/indexer/metadata_dictionary/__init__.py --- a/swh/indexer/metadata_dictionary/__init__.py +++ b/swh/indexer/metadata_dictionary/__init__.py @@ -2,7 +2,7 @@ import click -from . import cff, codemeta, maven, npm, python, ruby +from . import cff, codemeta, composer, maven, npm, python, ruby MAPPINGS = { "CodemetaMapping": codemeta.CodemetaMapping, @@ -11,6 +11,7 @@ "PythonPkginfoMapping": python.PythonPkginfoMapping, "GemspecMapping": ruby.GemspecMapping, "CffMapping": cff.CffMapping, + "ComposerMapping": composer.ComposerMapping, } diff --git a/swh/indexer/metadata_dictionary/composer.py b/swh/indexer/metadata_dictionary/composer.py new file mode 100644 --- /dev/null +++ b/swh/indexer/metadata_dictionary/composer.py @@ -0,0 +1,58 @@ +# Copyright (C) 2022 The Software Heritage developers +# See the AUTHORS file at the top-level directory of this distribution +# License: GNU General Public License version 3, or any later version +# See top-level LICENSE file for more information + +import os.path + +from swh.indexer.codemeta import _DATA_DIR, SCHEMA_URI, _read_crosstable + +from .base import JsonMapping + +COMPOSER_TABLE_PATH = os.path.join(_DATA_DIR, "composer.csv") + +with open(COMPOSER_TABLE_PATH) as fd: + (CODEMETA_TERMS, COMPOSER_TABLE) = _read_crosstable(fd) + + +class ComposerMapping(JsonMapping): + """Dedicated class for Packagist(composer.json) mapping and translation""" + + name = "composer" + mapping = COMPOSER_TABLE["Composer"] + filename = b"composer.json" + string_fields = [ + "name", + "description", + "version", + "keywords", + "homepage", + "license", + "author", + "authors", + ] + + def normalize_homepage(self, s): + if isinstance(s, str): + return {"@id": s} + + def normalize_license(self, s): + if isinstance(s, str): + return {"@id": "https://spdx.org/licenses/" + s} + + def normalize_authors(self, author_list): + authors = [] + for author in author_list: + author_obj = {"@type": SCHEMA_URI + "Person"} + + if isinstance(author, dict): + if isinstance(author.get("name", None), str): + author_obj[SCHEMA_URI + "name"] = author.get("name", None) + if isinstance(author.get("email", None), str): + author_obj[SCHEMA_URI + "email"] = author.get("email", None) + if isinstance(author.get("role", None), str): + author_obj[SCHEMA_URI + "role"] = author.get("role", None) + + authors.append(author_obj) + + return {"@list": authors} diff --git a/swh/indexer/tests/test_cli.py b/swh/indexer/tests/test_cli.py --- a/swh/indexer/tests/test_cli.py +++ b/swh/indexer/tests/test_cli.py @@ -95,6 +95,7 @@ [ "cff", "codemeta", + "composer", "gemspec", "maven", "npm", diff --git a/swh/indexer/tests/test_metadata.py b/swh/indexer/tests/test_metadata.py --- a/swh/indexer/tests/test_metadata.py +++ b/swh/indexer/tests/test_metadata.py @@ -64,6 +64,7 @@ self.pkginfo_mapping = MAPPINGS["PythonPkginfoMapping"]() self.gemspec_mapping = MAPPINGS["GemspecMapping"]() self.cff_mapping = MAPPINGS["CffMapping"]() + self.composer_mapping = MAPPINGS["ComposerMapping"]() def test_compute_metadata_none(self): """ @@ -616,6 +617,83 @@ # then assert expected_results == results + def test_compute_metadata_composer(self): + raw_content = """{ + "name": "symfony/polyfill-mbstring", + "type": "library", + "description": "Symfony polyfill for the Mbstring extension", + "keywords": [ + "polyfill", + "shim", + "compatibility", + "portable" + ], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} + """.encode( + "utf-8" + ) + + result = self.composer_mapping.translate(raw_content) + + expected = { + "@context": "https://doi.org/10.5063/schema/codemeta-2.0", + "type": "SoftwareSourceCode", + "name": "symfony/polyfill-mbstring", + "keywords": ["polyfill", "shim", "compatibility", "portable"], + "description": "Symfony polyfill for the Mbstring extension", + "url": "https://symfony.com", + "license": "https://spdx.org/licenses/MIT", + "author": [ + { + "type": "Person", + "name": "Nicolas Grekas", + "email": "p@tchwork.com", + }, + { + "type": "Person", + "name": "Symfony Community", + }, + ], + } + + assert result == expected + def test_compute_metadata_valid_codemeta(self): raw_content = b"""{ "@context": "https://doi.org/10.5063/schema/codemeta-2.0",