spdx_tools.spdx3.writer.json_ld.owl_to_context

  1# SPDX-FileCopyrightText: 2023 spdx contributors
  2#
  3# SPDX-License-Identifier: Apache-2.0
  4import json
  5import os.path
  6
  7# current workflow: markdown files + spec_parser -> model.ttl -> convert to json_ld: SPDX_OWL.json ->
  8# use the function below to generate context.json
  9# properties with Enum range should look like this (probably), so that their values are automatically appended
 10# with the Enum URI:
 11# "annotationType": {
 12#     "@id": "core:annotationType",
 13#     "@type": "@vocab",
 14#     "@context": {
 15#         "@vocab": "core:AnnotationType/"
 16#     }
 17# },
 18
 19PROPERTIES_WITH_ENUM_RANGE = [
 20    "safetyRiskAssessment",
 21    "sensitivePersonalInformation",
 22    "annotationType",
 23    "externalIdentifierType",
 24    "externalReferenceType",
 25    "algorithm",
 26    "scope",
 27    "profile",
 28    "completeness",
 29    "relationshipType",
 30    "confidentialityLevel",
 31    "datasetAvailability",
 32    "decisionType",
 33    "justificationType",
 34    "catalogType",
 35    "conditionality",
 36    "sbomType",
 37    "softwareLinkage",
 38    "purpose",
 39]
 40
 41REFERENCE_PROPERTY_TYPES = [
 42    "core:Element",
 43    "core:Agent",
 44]
 45
 46
 47def convert_spdx_owl_to_jsonld_context(spdx_owl: str = "SPDX_OWL.json"):
 48    with open(spdx_owl, "r") as infile:
 49        owl_dict = json.load(infile)
 50
 51    context_dict = owl_dict["@context"]
 52
 53    for node in owl_dict["@graph"]:
 54        # print(node)
 55        node_type = node.get("@type")
 56        if not node_type:
 57            # print(node)
 58            continue
 59
 60        if "owl:NamedIndividual" in node_type:
 61            continue
 62        elif node_type in ["owl:DatatypeProperty", "owl:ObjectProperty"]:
 63            name = node["@id"].split(":")[-1]
 64            type_id = node["rdfs:range"]["@id"]
 65
 66            if name in context_dict and context_dict[name]["@id"].startswith("core"):
 67                # if in doubt, prioritize core properties
 68                continue
 69
 70            if name in PROPERTIES_WITH_ENUM_RANGE:
 71                if name == "profile":
 72                    # FIXME: since the allowed values for the profile enum collide with
 73                    # our namespaces, we need to explicitly remap their meaning in the context
 74                    context_dict[name] = {
 75                        "@id": node["@id"],
 76                        "@type": "@vocab",
 77                        "@context": {
 78                            "core": "https://spdx.org/rdf/Core/ProfileIdentifierType/core",
 79                            "software": "https://spdx.org/rdf/Core/ProfileIdentifierType/software",
 80                            "licensing": "https://spdx.org/rdf/Core/ProfileIdentifierType/licensing",
 81                            "security": "https://spdx.org/rdf/Core/ProfileIdentifierType/security",
 82                            "build": "https://spdx.org/rdf/Core/ProfileIdentifierType/build",
 83                            "ai": "https://spdx.org/rdf/Core/ProfileIdentifierType/ai",
 84                            "dataset": "https://spdx.org/rdf/Core/ProfileIdentifierType/dataset",
 85                            "usage": "https://spdx.org/rdf/Core/ProfileIdentifierType/usage",
 86                            "extension": "https://spdx.org/rdf/Core/ProfileIdentifierType/extension",
 87                        },
 88                    }
 89                else:
 90                    context_dict[name] = {
 91                        "@id": node["@id"],
 92                        "@type": "@vocab",
 93                        "@context": {"@vocab": type_id + "/"},
 94                    }
 95            elif node_type == "owl:ObjectProperty" and type_id in REFERENCE_PROPERTY_TYPES:
 96                context_dict[name] = {"@id": node["@id"], "@type": "@id"}
 97            else:
 98                context_dict[name] = {"@id": node["@id"], "@type": type_id}
 99
100        elif node_type == "owl:Class":
101            name = node["@id"].split(":")[-1]
102            context_dict[name] = node["@id"]
103
104        elif isinstance(node_type, list):
105            name = node["@id"].split(":")[-1]
106            context_dict[name] = node["@id"]
107
108        else:
109            print(f"unknown node_type: {node_type}")
110
111    with open(os.path.join(os.path.dirname(__file__), "context.json"), "w") as infile:
112        json.dump(context_dict, infile)
113
114
115if __name__ == "__main__":
116    convert_spdx_owl_to_jsonld_context("SPDX_OWL.json")
PROPERTIES_WITH_ENUM_RANGE = ['safetyRiskAssessment', 'sensitivePersonalInformation', 'annotationType', 'externalIdentifierType', 'externalReferenceType', 'algorithm', 'scope', 'profile', 'completeness', 'relationshipType', 'confidentialityLevel', 'datasetAvailability', 'decisionType', 'justificationType', 'catalogType', 'conditionality', 'sbomType', 'softwareLinkage', 'purpose']
REFERENCE_PROPERTY_TYPES = ['core:Element', 'core:Agent']
def convert_spdx_owl_to_jsonld_context(spdx_owl: str = 'SPDX_OWL.json'):
 48def convert_spdx_owl_to_jsonld_context(spdx_owl: str = "SPDX_OWL.json"):
 49    with open(spdx_owl, "r") as infile:
 50        owl_dict = json.load(infile)
 51
 52    context_dict = owl_dict["@context"]
 53
 54    for node in owl_dict["@graph"]:
 55        # print(node)
 56        node_type = node.get("@type")
 57        if not node_type:
 58            # print(node)
 59            continue
 60
 61        if "owl:NamedIndividual" in node_type:
 62            continue
 63        elif node_type in ["owl:DatatypeProperty", "owl:ObjectProperty"]:
 64            name = node["@id"].split(":")[-1]
 65            type_id = node["rdfs:range"]["@id"]
 66
 67            if name in context_dict and context_dict[name]["@id"].startswith("core"):
 68                # if in doubt, prioritize core properties
 69                continue
 70
 71            if name in PROPERTIES_WITH_ENUM_RANGE:
 72                if name == "profile":
 73                    # FIXME: since the allowed values for the profile enum collide with
 74                    # our namespaces, we need to explicitly remap their meaning in the context
 75                    context_dict[name] = {
 76                        "@id": node["@id"],
 77                        "@type": "@vocab",
 78                        "@context": {
 79                            "core": "https://spdx.org/rdf/Core/ProfileIdentifierType/core",
 80                            "software": "https://spdx.org/rdf/Core/ProfileIdentifierType/software",
 81                            "licensing": "https://spdx.org/rdf/Core/ProfileIdentifierType/licensing",
 82                            "security": "https://spdx.org/rdf/Core/ProfileIdentifierType/security",
 83                            "build": "https://spdx.org/rdf/Core/ProfileIdentifierType/build",
 84                            "ai": "https://spdx.org/rdf/Core/ProfileIdentifierType/ai",
 85                            "dataset": "https://spdx.org/rdf/Core/ProfileIdentifierType/dataset",
 86                            "usage": "https://spdx.org/rdf/Core/ProfileIdentifierType/usage",
 87                            "extension": "https://spdx.org/rdf/Core/ProfileIdentifierType/extension",
 88                        },
 89                    }
 90                else:
 91                    context_dict[name] = {
 92                        "@id": node["@id"],
 93                        "@type": "@vocab",
 94                        "@context": {"@vocab": type_id + "/"},
 95                    }
 96            elif node_type == "owl:ObjectProperty" and type_id in REFERENCE_PROPERTY_TYPES:
 97                context_dict[name] = {"@id": node["@id"], "@type": "@id"}
 98            else:
 99                context_dict[name] = {"@id": node["@id"], "@type": type_id}
100
101        elif node_type == "owl:Class":
102            name = node["@id"].split(":")[-1]
103            context_dict[name] = node["@id"]
104
105        elif isinstance(node_type, list):
106            name = node["@id"].split(":")[-1]
107            context_dict[name] = node["@id"]
108
109        else:
110            print(f"unknown node_type: {node_type}")
111
112    with open(os.path.join(os.path.dirname(__file__), "context.json"), "w") as infile:
113        json.dump(context_dict, infile)