Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/appendices/ontology.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://schema.software-metadata.pub/software-card/2025-01/#> .

# Class Definition with cardinality restrictions
Expand Down Expand Up @@ -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 <https://schema.software-metadata.pub/software-card/2025-01/#> .
Expand Down Expand Up @@ -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 <https://schema.software-metadata.pub/software-card/2025-01/#> .

Expand Down Expand Up @@ -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
Expand Down
31 changes: 8 additions & 23 deletions docs/specification/parameterization.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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`

Expand Down Expand Up @@ -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++" ) .
```
3 changes: 1 addition & 2 deletions docs/specification/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 6 additions & 17 deletions src/software_card_policies/data_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = (
Expand All @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -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!
Expand Down
Loading