Skip to content

Commit bb8bf24

Browse files
authored
Merge branch 'main' into improve-display-method
2 parents bf24730 + 966ec62 commit bb8bf24

8 files changed

Lines changed: 284 additions & 124 deletions

File tree

examples/noodles_fw.ipynb

Lines changed: 127 additions & 49 deletions
Large diffs are not rendered by default.

fairworkflows/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from ._version import __version__
2+
from .linguistic_system import LinguisticSystem, LINGSYS_ENGLISH, LINGSYS_PYTHON
23
from .fairstep import FairStep, FairVariable, is_fairstep
34
from .fairworkflow import FairWorkflow, is_fairworkflow

fairworkflows/fairstep.py

Lines changed: 8 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
import inspect
23
import typing
34
from copy import deepcopy
@@ -8,7 +9,7 @@
89
import rdflib
910
from rdflib import RDF, RDFS, DCTERMS
1011

11-
from fairworkflows import namespaces
12+
from fairworkflows import namespaces, LinguisticSystem, LINGSYS_ENGLISH, LINGSYS_PYTHON
1213
from fairworkflows.config import DUMMY_FAIRWORKFLOWS_URI, IS_FAIRSTEP_RETURN_VALUE_PARAMETER_NAME
1314
from fairworkflows.rdf_wrapper import RdfWrapper, replace_in_rdf
1415

@@ -85,9 +86,12 @@ class FairStep(RdfWrapper):
8586

8687
def __init__(self, label: str = None, description: str = None, uri=None,
8788
is_pplan_step: bool = True, is_manual_task: bool = None,
88-
is_script_task: bool = None, inputs: List[FairVariable] = None,
89+
is_script_task: bool = None,
90+
language: LinguisticSystem = LINGSYS_ENGLISH,
91+
inputs: List[FairVariable] = None,
8992
outputs: List[FairVariable] = None, derived_from=None):
90-
super().__init__(uri=uri, ref_name='step', derived_from=derived_from)
93+
super().__init__(uri=uri, ref_name='step', derived_from=derived_from, language=language)
94+
9195
if label is not None:
9296
self.label = label
9397
if description is not None:
@@ -287,39 +291,6 @@ def outputs(self, variables: List[FairVariable]):
287291
for variable in variables:
288292
self._add_variable(variable, namespaces.PPLAN.hasOutputVar)
289293

290-
@property
291-
def label(self):
292-
"""Label.
293-
294-
Returns the rdfs:label of this step (or a list, if more than one matching triple is found)
295-
"""
296-
return self.get_attribute(RDFS.label)
297-
298-
@label.setter
299-
def label(self, value):
300-
"""
301-
Adds the given text string as an rdfs:label for this FairStep
302-
object.
303-
"""
304-
self.set_attribute(RDFS.label, rdflib.term.Literal(value))
305-
306-
@property
307-
def description(self):
308-
"""Description.
309-
310-
Returns the dcterms:description of this step (or a list, if more than
311-
one matching triple is found)
312-
"""
313-
return self.get_attribute(DCTERMS.description)
314-
315-
@description.setter
316-
def description(self, value):
317-
"""
318-
Adds the given text string as a dcterms:description for this FairStep
319-
object.
320-
"""
321-
self.set_attribute(DCTERMS.description, rdflib.term.Literal(value))
322-
323294
def validate(self, shacl=False):
324295
"""Validate step.
325296
@@ -454,13 +425,13 @@ def _modify_function(func):
454425
inputs = _extract_inputs_from_function(func, kwargs)
455426
outputs = _extract_outputs_from_function(func, kwargs)
456427

457-
458428
func._fairstep = FairStep(uri='http://www.example.org/unpublished-'+func.__name__,
459429
label=label,
460430
description=description,
461431
is_pplan_step=is_pplan_step,
462432
is_manual_task=is_manual_task,
463433
is_script_task=is_script_task,
434+
language=LINGSYS_PYTHON,
464435
inputs=inputs,
465436
outputs=outputs)
466437

fairworkflows/fairworkflow.py

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from rdflib.tools.rdf2dot import rdf2dot
1717
from requests import HTTPError
1818

19-
from fairworkflows import namespaces
19+
from fairworkflows import namespaces, LinguisticSystem, LINGSYS_ENGLISH, LINGSYS_PYTHON
2020
from fairworkflows.fairstep import FairStep
2121
from fairworkflows.rdf_wrapper import RdfWrapper
2222

@@ -32,8 +32,9 @@ class FairWorkflow(RdfWrapper):
3232
"""
3333

3434
def __init__(self, description: str = None, label: str = None, uri=None,
35+
language: LinguisticSystem = None,
3536
is_pplan_plan: bool = True, first_step: FairStep = None, derived_from=None):
36-
super().__init__(uri=uri, ref_name='plan', derived_from=derived_from)
37+
super().__init__(uri=uri, ref_name='plan', derived_from=derived_from, language=language)
3738
self._is_published = False
3839
self.is_pplan_plan = is_pplan_plan
3940
if description is not None:
@@ -85,8 +86,10 @@ def from_function(cls, func: Callable):
8586
'use is_fairworkflow decorator to mark it.')
8687

8788
@classmethod
88-
def from_noodles_promise(cls, workflow_level_promise, step_level_promise, description: str = None, label: str =
89-
None, is_pplan_plan: bool = True, derived_from=None):
89+
def from_noodles_promise(cls, workflow_level_promise: PromisedObject,
90+
step_level_promise: PromisedObject,
91+
description: str = None, label: str= None,
92+
is_pplan_plan: bool = True, derived_from=None):
9093
"""
9194
9295
Args:
@@ -96,7 +99,8 @@ def from_noodles_promise(cls, workflow_level_promise, step_level_promise, descri
9699
nodes but does not bind them together. We use this to extract step information from
97100
it.
98101
"""
99-
self = cls(description=description, label=label, is_pplan_plan=is_pplan_plan, derived_from=derived_from)
102+
self = cls(description=description, label=label, is_pplan_plan=is_pplan_plan,
103+
derived_from=derived_from, language=LINGSYS_PYTHON)
100104
self.workflow_level_promise = workflow_level_promise
101105
self.step_level_promise = step_level_promise
102106

@@ -286,35 +290,6 @@ def get_step(self, uri):
286290
"""
287291
return self._steps[uri]
288292

289-
@property
290-
def label(self):
291-
"""Label.
292-
293-
Returns the rdfs:label of this workflow (or a list, if more than one matching triple is found)
294-
"""
295-
return self.get_attribute(RDFS.label)
296-
297-
@label.setter
298-
def label(self, value):
299-
"""
300-
Adds the given text string as an rdfs:label for this FairWorkflow
301-
object.
302-
"""
303-
self.set_attribute(RDFS.label, rdflib.term.Literal(value))
304-
305-
@property
306-
def description(self):
307-
"""
308-
Description of the workflow. This is the dcterms:description found in
309-
the rdf for this workflow (or a list if more than one matching triple
310-
found)
311-
"""
312-
return self.get_attribute(DCTERMS.description)
313-
314-
@description.setter
315-
def description(self, value):
316-
self.set_attribute(DCTERMS.description, rdflib.term.Literal(value))
317-
318293
def validate(self, shacl=False):
319294
"""Validate workflow.
320295

fairworkflows/linguistic_system.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import sys
2+
import warnings
3+
from typing import List
4+
import rdflib
5+
from rdflib import DC, RDF, RDFS, OWL
6+
from .namespaces import SCHEMAORG
7+
8+
class LinguisticSystem:
9+
def __init__(self, lstype: rdflib.URIRef = None, label: str = None, see_also: rdflib.URIRef = None, version_info: str = None):
10+
self.lstype = rdflib.URIRef(lstype)
11+
self.label = rdflib.Literal(label)
12+
self.see_also = rdflib.URIRef(see_also)
13+
self.version_info = rdflib.Literal(version_info)
14+
15+
@classmethod
16+
def from_rdf(cls, rdf):
17+
lstype = _check_unique(list(rdf.objects(None, RDF.type)))
18+
label = _check_unique(list(rdf.objects(None, RDFS.label)))
19+
see_also = _check_unique(list(rdf.objects(None, RDFS.seeAlso)))
20+
version_info = _check_unique(list(rdf.objects(None, OWL.versionInfo)))
21+
return cls(lstype = lstype,
22+
label=label,
23+
see_also=see_also,
24+
version_info=version_info)
25+
26+
def generate_rdf(self, ref: rdflib.BNode):
27+
rdf = rdflib.Graph()
28+
if self.lstype:
29+
rdf.add( (ref, RDF.type, self.lstype) )
30+
if self.label:
31+
rdf.add( (ref, RDFS.label, self.label) )
32+
if self.see_also:
33+
rdf.add( (ref, RDFS.seeAlso, self.see_also) )
34+
if self.version_info:
35+
rdf.add( (ref, OWL.versionInfo, self.version_info) )
36+
return rdf
37+
38+
def __str__(self):
39+
return (f'LinguisticSystem with type={self.lstype}, label={self.label}, '
40+
f'seeAlso={self.see_also}, versionInfo={self.version_info}')
41+
42+
43+
def _check_unique(l: List):
44+
if len(l) == 0:
45+
return None
46+
if len(l) > 1:
47+
warnings.warn(f'Only one triple expected, but found {len(l)}: {l}')
48+
return l[0]
49+
50+
51+
52+
LINGSYS_ENGLISH = LinguisticSystem(lstype=DC.LinguisticSystem,
53+
label='en',
54+
see_also="http://www.datypic.com/sc/xsd/t-xsd_language.html")
55+
56+
LINGSYS_PYTHON = LinguisticSystem(lstype=SCHEMAORG.ComputerLanguage,
57+
label='python',
58+
version_info='.'.join([str(v) for v in sys.version_info]),
59+
see_also="https://en.wikipedia.org/wiki/Python_(programming_language)")
60+

fairworkflows/namespaces.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
DUL = rdflib.Namespace("http://www.ontologydesignpatterns.org/ont/dul/DUL.owl#")
66
BPMN = rdflib.Namespace("http://dkm.fbk.eu/index.php/BPMN2_Ontology#")
77
PWO = rdflib.Namespace("http://purl.org/spar/pwo/")
8+
SCHEMAORG = rdflib.Namespace("https://schema.org/")
89

910
"""
1011
Namespace for

fairworkflows/rdf_wrapper.py

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,35 @@
66

77
import pyshacl
88
import rdflib
9+
from rdflib import RDF, RDFS, DCTERMS, OWL
910
from nanopub import Publication, NanopubClient
1011
from rdflib.tools.rdf2dot import rdf2dot
1112

12-
from fairworkflows import namespaces
13+
from fairworkflows import namespaces, LinguisticSystem
1314
from fairworkflows.config import PACKAGE_DIR
1415

1516
PLEX_SHAPES_SHACL_FILEPATH = str(PACKAGE_DIR / 'resources' / 'plex-shapes.ttl')
1617

17-
1818
class RdfWrapper:
19-
def __init__(self, uri, ref_name='fairobject', derived_from: List[str] = None):
19+
def __init__(self, uri, ref_name='fairobject', derived_from: List[str] = None,
20+
language: LinguisticSystem = None ):
2021
self._rdf = rdflib.Graph()
2122
self._uri = str(uri)
2223
self.self_ref = rdflib.term.BNode(ref_name)
2324
self._is_modified = False
2425
self._is_published = False
2526
self.derived_from = derived_from
27+
2628
self._bind_namespaces()
2729

30+
# A blank node to which triples about the linguistic
31+
# system for this FAIR object can be added
32+
self.lingsys_ref = rdflib.BNode('LinguisticSystem')
33+
self._rdf.add((self.self_ref, DCTERMS.language, self.lingsys_ref))
34+
35+
if language is not None:
36+
self.language = language
37+
2838
def _bind_namespaces(self):
2939
"""Bind namespaces used often in fair step and fair workflow.
3040
@@ -35,6 +45,9 @@ def _bind_namespaces(self):
3545
self.rdf.bind("dul", namespaces.DUL)
3646
self.rdf.bind("bpmn", namespaces.BPMN)
3747
self.rdf.bind("pwo", namespaces.PWO)
48+
self.rdf.bind("schema", namespaces.SCHEMAORG)
49+
self.rdf.bind("dc", DCTERMS)
50+
self.rdf.bind("owl", OWL)
3851

3952
@property
4053
def rdf(self) -> rdflib.Graph:
@@ -114,6 +127,57 @@ def remove_attribute(self, predicate, object=None):
114127
"""
115128
self._rdf.remove((self.self_ref, predicate, object))
116129

130+
@property
131+
def label(self):
132+
"""Label.
133+
134+
Returns the rdfs:label of this Fair object (or a list, if more than one matching triple is found)
135+
"""
136+
return self.get_attribute(RDFS.label)
137+
138+
@label.setter
139+
def label(self, value):
140+
"""
141+
Adds the given text string as an rdfs:label for this Fair object.
142+
"""
143+
self.set_attribute(RDFS.label, rdflib.term.Literal(value))
144+
145+
@property
146+
def description(self):
147+
"""Description.
148+
149+
Returns the dcterms:description of this Fair object (or a list, if more than
150+
one matching triple is found)
151+
"""
152+
return self.get_attribute(DCTERMS.description)
153+
154+
@description.setter
155+
def description(self, value):
156+
"""
157+
Adds the given text string as a dcterms:description for this Fair object.
158+
"""
159+
self.set_attribute(DCTERMS.description, rdflib.term.Literal(value))
160+
161+
@property
162+
def language(self):
163+
"""Returns the language for this fair objects's description (could be code).
164+
Returns a LinguisticSystem object.
165+
"""
166+
lingsys_rdf = rdflib.Graph()
167+
for t in self._rdf.triples((self.lingsys_ref, None, None)):
168+
lingsys_rdf.add(t)
169+
return LinguisticSystem.from_rdf(lingsys_rdf)
170+
171+
@language.setter
172+
def language(self, value: LinguisticSystem):
173+
"""Sets the language for this fair object's code (takes a LinguisticSystem).
174+
Removes the existing linguistic system triples from the RDF decription
175+
and replaces them with the new linguistic system."""
176+
lingsys_triples = list(self._rdf.triples( (self.lingsys_ref, None, None) ))
177+
if len(lingsys_triples) > 0:
178+
self._rdf.remove(lingsys_triples)
179+
self._rdf += value.generate_rdf(self.lingsys_ref)
180+
117181
def shacl_validate(self):
118182
sg = rdflib.Graph()
119183
sg.parse(PLEX_SHAPES_SHACL_FILEPATH, format='ttl')

tests/test_fairstep.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1+
import sys
12
from typing import Tuple
23
from unittest.mock import patch
34

45
import pytest
56
import rdflib
7+
from rdflib import DCTERMS, OWL, RDF
68
from nanopub import Publication
79

810
from conftest import skip_if_nanopub_server_unavailable, read_rdf_test_resource
911
from fairworkflows import FairStep, namespaces, FairVariable, is_fairworkflow
1012
from fairworkflows.fairstep import _extract_outputs_from_function, is_fairstep
1113
from fairworkflows.rdf_wrapper import replace_in_rdf
12-
14+
from fairworkflows import LinguisticSystem
1315

1416
def test_construct_fair_variable_get_name_from_uri():
1517
variable = FairVariable(name=None, uri='http:example.org#input1', computational_type='int')
@@ -273,6 +275,14 @@ def test_step(a:float, b:float) -> float:
273275
else:
274276
raise
275277

278+
assert test_step._fairstep.language is not None
279+
assert isinstance(test_step._fairstep.language, LinguisticSystem)
280+
assert (None, RDF.type, namespaces.SCHEMAORG.ComputerLanguage) in test_step._fairstep._rdf
281+
assert (None, DCTERMS.language, None) in test_step._fairstep._rdf
282+
assert (None, OWL.versionInfo, None) in test_step._fairstep._rdf
283+
assert 'python' in test_step._fairstep.language.label
284+
285+
276286
def test_decorator_semantic_types_multiple_outputs():
277287
output_tuple = ('http://www.example.org/walrus', 'http://www.example.org/krill')
278288

0 commit comments

Comments
 (0)