diff --git a/docs/appendices/ontology.md b/docs/appendices/ontology.md index a134185..539b197 100644 --- a/docs/appendices/ontology.md +++ b/docs/appendices/ontology.md @@ -88,7 +88,7 @@ sc:AllowedOuterType a owl:Class ; rdfs:label "Allowed Outer Type"@en ; rdfs:comment """The set of container types that may be used as outer types for parameters. This includes scalar (single value), ordered lists, and unordered bags."""@en ; - owl:oneOf ( sc:Scalar rdf:List rdf:Bag ) ; + owl:oneOf ( sc:Scalar rdf:List ) ; rdfs:isDefinedBy . # Class Definition with cardinality restrictions @@ -143,7 +143,7 @@ sc:parameterOuterType a owl:ObjectProperty ; rdfs:label "parameter outer type"@en ; rdfs:comment """Specifies the container/cardinality type of the parameter value. Determines whether the parameter represents a single value (sc:Scalar), - an ordered collection (rdf:List), or an unordered collection (rdf:Bag)."""@en ; + or a list of values (rdf:List)."""@en ; rdfs:domain sc:Parameter ; rdfs:range sc:AllowedOuterType ; rdfs:isDefinedBy . @@ -171,7 +171,7 @@ sc:parameterDefaultValue a rdf:Property ; parameter becomes required and must be provided in the configuration file. When present, the default value must match the parameter's declared outer and inner types. For scalar parameters, this is a single literal or resource. - For list or bag parameters, this is an rdf:List or rdf:Bag."""@en ; + For list parameters, this is an rdf:List."""@en ; rdfs:domain sc:Parameter ; rdfs:isDefinedBy . @@ -241,8 +241,8 @@ sc:ParameterShape a sh:NodeShape ; sh:description "The container/cardinality type of the parameter."@en ; sh:minCount 1 ; sh:maxCount 1 ; - sh:in ( sc:Scalar rdf:List rdf:Bag ) ; - sh:message "Parameter must have exactly one outer type: sc:Scalar, rdf:List, or rdf:Bag."@en ; + sh:in ( sc:Scalar rdf:List ) ; + sh:message "Parameter must have exactly one outer type: sc:Scalar, or rdf:List."@en ; ] ; # Must have exactly one inner type from allowed set diff --git a/docs/specification/parameterization.md b/docs/specification/parameterization.md index b342906..dc263ec 100644 --- a/docs/specification/parameterization.md +++ b/docs/specification/parameterization.md @@ -97,8 +97,8 @@ We mention dates as an example here but disallow them later. This separation allows the framework to correctly handle configurations like: - A minimum character requirement for descriptions (outer: `sc:Scalar`, inner: `xsd:int`) -- A list of license URIs (outer: `rdf:List`, inner: `xsd:anyURI`) -- A set of organization names (outer: `rdf:Bag`, inner: `xsd:string`) +- A selection of license URIs (outer: `rdf:List`, inner: `xsd:anyURI`) +- A list of organization names (outer: `rdf:List`, inner: `xsd:string`) ## `sc:parameterInnerType` @@ -159,13 +159,13 @@ The outer type MUST be present and MUST specify how values are structured. These types MUST be supported by implementations: -| Outer Type | Meaning | Use When | Example | -| ----------- | ------------------ | -------------------------------------------- | ------------------------------------------ | -| `sc:Scalar` | Single value | Only one value is needed | Minimum description length, a boolean flag | -| `rdf:List` | Ordered sequence | Order matters and multiple values are needed | Priority-ordered list of licenses | -| `rdf:Bag` | Unordered sequence | Order does not matter for multiple values | List of software dependencies | +| Outer Type | Meaning | Use When | Examples | +| ----------- | --------------------------------------------- | -------------------------- | ---------------------------------------------------------------- | +| `sc:Scalar` | Single value | Only one value is needed | Minimum text length, a boolean flag, a single email address, ... | +| `rdf:List` | Ordered and unordered sequences, enumerations | Multiple values are needed | A selection of allowed licenses, a list of persons, ... | -Ohter RDF collection types (like `rdf:Seq`, `rdf:Alt`) MAY be supported by a validation implementation. +RDF container types (`rdf:Alt`, `rdf:Bag`, `rdf:Seq`) MAY be supported by validation implementations. +However, these types are not supported by SHACL which means conversion has to be applied. ## `sc:parameterConfigKey` @@ -323,18 +323,3 @@ scex:requiredKeywords a sc:Parameter ; sc:parameterConfigKey "required_keywords" ; sc:parameterDefaultValue ( "research-software" "open-source" ) . ``` - -Configuration: -```toml -[policies.keywords.parameters] -required_keywords = ["fair-software", "reproducible-research", "scientific-computing"] -``` - -**Bag of Strings (Unordered Collection):** -```turtle -scex:allowedLanguages a sc:Parameter ; - sc:parameterOuterType rdf:Bag ; - sc:parameterInnerType xsd:string ; - sc:parameterConfigKey "allowed_programming_languages" ; - sc:parameterDefaultValue ( "Python" "Java" "C++" ) . -``` diff --git a/docs/specification/validation.md b/docs/specification/validation.md index bd1c238..d2d75ae 100644 --- a/docs/specification/validation.md +++ b/docs/specification/validation.md @@ -90,8 +90,7 @@ Implementations MUST validate that configured parameter values match the declare 1. **Outer Type Validation:** - `sc:Scalar`: parameters MUST receive a single value (not an array/list) - - `rdf:List`: parameters MUST receive an (ordered) array/list of values - - `rdf:Bag`: parameters MUST receive an (unorderd) array/list of values + - `rdf:List`: parameters MUST receive an array/list of values 2. **Inner Type Validation:** - Values MUST be compatible with the declared XSD datatype diff --git a/src/software_card_policies/data_model.py b/src/software_card_policies/data_model.py index 41273f0..1050548 100644 --- a/src/software_card_policies/data_model.py +++ b/src/software_card_policies/data_model.py @@ -7,6 +7,7 @@ from enum import Enum from functools import reduce from pathlib import Path +from types import NoneType from typing import Any, Dict, List, Tuple from pyshacl import validate @@ -146,9 +147,6 @@ def from_graph(cls, reference: URIRef, graph: Graph): _ALLOWED_OUTER_TYPES = ( SC.Scalar, RDF.List, - RDF.Seq, - RDF.Bag, - RDF.Alt, ) _ALLOWED_INNER_TYPES = ( @@ -172,11 +170,10 @@ def read_rdf_resource(source: Path | str) -> Graph: return graph -def _create_rdf_list_parameter( +def _create_list_parameter( parameter: Parameter, graph: Graph, config_parameter: Any ) -> Node: assert parameter.outer_type == RDF.List - # assert (parameter.default_value, RDF.type, RDF.List) in graph assert (parameter.default_value, RDF.first, None) in graph assert (parameter.default_value, RDF.rest, None) in graph @@ -190,12 +187,11 @@ def _create_rdf_list_parameter( ).uri -def _create_sc_scalar_parameter( +def _create_scalar_parameter( parameter: Parameter, graph: Graph, config_parameter: Any ) -> Node: assert parameter.outer_type == SC.Scalar - - assert isinstance(config_parameter, (str, int, float, type(None))) + assert isinstance(config_parameter, (str, int, float, NoneType)) if config_parameter: return Literal(config_parameter) @@ -228,17 +224,10 @@ def parameterize_graph(graph: Graph, config_parameters: Dict[str, Any]) -> Graph ) if parameter.outer_type == SC.Scalar: - o = _create_sc_scalar_parameter(parameter, graph, config_parameter) - - elif parameter.outer_type == RDF.List: - o = _create_rdf_list_parameter(parameter, graph, config_parameter) + o = _create_scalar_parameter(parameter, graph, config_parameter) else: - raise NotImplementedError( - f"Parameter '{parameter.uri}' has outer type " - f"'{parameter.outer_type}', the handling of which " - "is currently not implemented" - ) + o = _create_list_parameter(parameter, graph, config_parameter) # Add replacements for all occurences of the parameter. Again, extract # occurences before modifying the graph!