Skip to content

Commit 71d839b

Browse files
Merge pull request #153 from fair-workflows/independent-step-publications
Independent step publications
2 parents 01aa4f6 + f386aec commit 71d839b

6 files changed

Lines changed: 63 additions & 147 deletions

File tree

fairworkflows/fairstep.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,12 @@
44
from typing import Callable, get_type_hints, List, Union
55
from urllib.parse import urldefrag
66

7+
import noodles
78
import rdflib
89
from rdflib import RDF, RDFS, DCTERMS
910

10-
import noodles
11-
1211
from fairworkflows import namespaces
13-
from fairworkflows.rdf_wrapper import RdfWrapper
14-
15-
FAIRSTEP_PREDICATES = [RDF.type, namespaces.PPLAN.hasInputVar,
16-
namespaces.PPLAN.hasOutputVar, DCTERMS.description, RDFS.label]
12+
from fairworkflows.rdf_wrapper import RdfWrapper, replace_in_rdf
1713

1814

1915
class FairVariable:
@@ -98,6 +94,7 @@ def __init__(self, label: str = None, description: str = None, uri=None,
9894
if outputs is not None:
9995
self.outputs = outputs
10096
self._is_modified = False
97+
self._workflows = set()
10198

10299
@classmethod
103100
def from_rdf(cls, rdf, uri, fetch_references: bool = False, force: bool = False,
@@ -338,6 +335,17 @@ def validate(self, shacl=False):
338335
if shacl:
339336
self.shacl_validate()
340337

338+
def register_workflow(self, workflow):
339+
"""Register workflow that this step is part of."""
340+
self._workflows.add(workflow)
341+
342+
def _update_registered_workflows(self):
343+
"""Update the workflows that this step is part of.
344+
NB: it could be that a step was deleted from a workflow
345+
"""
346+
self._workflows = {workflow for workflow in self._workflows
347+
if self in workflow}
348+
341349
def publish_as_nanopub(self, use_test_server=False, **kwargs):
342350
"""
343351
Publish this rdf as a nanopublication.
@@ -352,7 +360,23 @@ def publish_as_nanopub(self, use_test_server=False, **kwargs):
352360
Returns:
353361
a dictionary with publication info, including 'nanopub_uri', and 'concept_uri'
354362
"""
355-
return self._publish_as_nanopub(use_test_server=use_test_server, **kwargs)
363+
self._update_registered_workflows()
364+
old_uri = self.uri
365+
self._publish_as_nanopub(use_test_server=use_test_server, **kwargs)
366+
var_names = [var.name for var in (self.inputs + self.outputs)]
367+
for workflow in self._workflows:
368+
replace_in_rdf(workflow.rdf, oldvalue=rdflib.URIRef(old_uri),
369+
newvalue=rdflib.URIRef(self.uri))
370+
371+
# Similarly replace old URIs for variable name bindings
372+
published_step_uri_defrag, _ = urldefrag(self.uri)
373+
for var_name in var_names:
374+
old_var_uri = old_uri + '#' + var_name
375+
new_var_uri = published_step_uri_defrag + '#' + var_name
376+
replace_in_rdf(self.rdf, oldvalue=rdflib.URIRef(old_var_uri),
377+
newvalue=rdflib.URIRef(new_var_uri))
378+
del workflow._steps[old_uri]
379+
workflow._steps[self.uri] = self
356380

357381
def __str__(self):
358382
"""

fairworkflows/fairworkflow.py

Lines changed: 14 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1+
import inspect
12
import warnings
23
from copy import deepcopy
34
from pathlib import Path
45
from tempfile import TemporaryDirectory
56
from typing import Iterator, Optional
6-
import inspect
7-
from urllib.parse import urldefrag
87

98
import networkx as nx
109
import rdflib
1110
from rdflib import RDF, RDFS, DCTERMS
1211
from rdflib.tools.rdf2dot import rdf2dot
1312
from requests import HTTPError
1413

15-
1614
from fairworkflows import namespaces
1715
from fairworkflows.fairstep import FairStep
18-
from fairworkflows.rdf_wrapper import RdfWrapper, replace_in_rdf
16+
from fairworkflows.rdf_wrapper import RdfWrapper
1917
import nanopub
2018

2119
class FairWorkflow(RdfWrapper):
@@ -54,8 +52,7 @@ def from_rdf(cls, rdf: rdflib.Graph, uri: str,
5452
possibly it's associated steps. Should use plex ontology.
5553
uri: URI of the workflow
5654
fetch_references: toggles fetching steps. I.e. if we encounter steps
57-
that are part of the workflow, but are not specified in the
58-
RDF we try fetching them from nanopub
55+
that are part of the workflow we try fetching them from nanopub
5956
force: Toggle forcing creation of object even if url is not in any of the subjects of
6057
the passed RDF
6158
remove_irrelevant_triples: Toggle removing irrelevant triples for this FairWorkflow.
@@ -106,19 +103,18 @@ def from_noodles_promise(cls, noodles_promise, description: str = None, label: s
106103

107104
return self
108105

109-
def _extract_steps(self, rdf, uri, fetch_steps=False):
106+
def _extract_steps(self, rdf, uri, fetch_steps=True):
110107
"""Extract FairStep objects from rdf.
111108
112-
Create FairStep objects for all steps in the passed RDF. Removes
113-
triples describing steps from rdf, those will be represented in
114-
the separate step RDF. Optionally try to fetch steps from nanopub.
109+
Create FairStep objects for all steps in the passed RDF.
110+
Optionally try to fetch steps from nanopub.
115111
"""
116112
step_refs = rdf.subjects(predicate=namespaces.PPLAN.isStepOfPlan,
117113
object=rdflib.URIRef(uri))
118114
for step_ref in step_refs:
119115
step_uri = str(step_ref)
120-
step = self._extract_step_from_rdf(step_uri, rdf)
121-
if step is None and fetch_steps:
116+
step = None
117+
if fetch_steps:
122118
step = self._fetch_step(uri=step_uri)
123119
if step is None:
124120
warnings.warn(f'Could not get detailed information for '
@@ -153,19 +149,6 @@ def _get_relevant_triples(uri, rdf):
153149
g.add(triple)
154150
return g
155151

156-
@staticmethod
157-
def _extract_step_from_rdf(uri, rdf: rdflib.Graph()) -> Optional[FairStep]:
158-
relevant_triples = FairStep._get_relevant_triples(uri, rdf)
159-
step_rdf = rdflib.Graph()
160-
for triple in relevant_triples:
161-
step_rdf.add(triple)
162-
rdf.remove(triple)
163-
164-
if len(step_rdf) > 0:
165-
return FairStep.from_rdf(step_rdf, uri=uri, remove_irrelevant_triples=False)
166-
else:
167-
return None
168-
169152
@staticmethod
170153
def _fetch_step(uri: str) -> Optional[FairStep]:
171154
try:
@@ -207,6 +190,7 @@ def _add_step(self, step: FairStep):
207190
self._rdf.add((rdflib.URIRef(step.uri), namespaces.PPLAN.isStepOfPlan,
208191
self.self_ref))
209192
self._last_step_added = step
193+
step.register_workflow(self)
210194

211195
def add(self, step: FairStep, follows: FairStep = None):
212196
"""Add a step.
@@ -472,8 +456,10 @@ def draw(self, filepath):
472456
def publish_as_nanopub(self, use_test_server=False, **kwargs):
473457
"""Publish to nanopub server.
474458
475-
First publish the steps, use the URIs of the published steps in the workflow. Then
476-
publish the workflow.
459+
Publish the workflow as nanopublication to the nanopub server.
460+
461+
Raises:
462+
RuntimeError: If one of the steps of the workflow was not published yet.
477463
478464
Args:
479465
use_test_server (bool): Toggle using the test nanopub server.
@@ -489,25 +475,8 @@ def publish_as_nanopub(self, use_test_server=False, **kwargs):
489475
for step in self:
490476
if step.is_modified or not step._is_published:
491477
self._is_modified = True # If one of the steps is modified the workflow is too.
492-
old_uri = step.uri
493-
var_names = [var.name for var in (step.inputs + step.outputs)]
494-
495-
step.publish_as_nanopub(use_test_server=use_test_server, **kwargs)
496-
published_step_uri = step.uri
497-
498-
replace_in_rdf(self.rdf, oldvalue=rdflib.URIRef(old_uri),
499-
newvalue=rdflib.URIRef(published_step_uri))
500-
501-
# Similarly replace old URIs for variable name bindings
502-
published_step_uri_defrag, _ = urldefrag(published_step_uri)
503-
for var_name in var_names:
504-
old_var_uri = old_uri + '#' + var_name
505-
new_var_uri = published_step_uri_defrag + '#' + var_name
506-
replace_in_rdf(self.rdf, oldvalue=rdflib.URIRef(old_var_uri),
507-
newvalue=rdflib.URIRef(new_var_uri))
478+
raise RuntimeError(f'{step} was not published yet, please publish steps first')
508479

509-
del self._steps[old_uri]
510-
self._steps[published_step_uri] = step
511480
return self._publish_as_nanopub(use_test_server=use_test_server, **kwargs)
512481

513482
def __str__(self):

fairworkflows/rdf_wrapper.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import warnings
2-
32
from typing import List
43
from urllib.parse import urldefrag
54

@@ -8,8 +7,8 @@
87
from nanopub import Publication, NanopubClient
98

109
from fairworkflows import namespaces
11-
1210
from fairworkflows.config import PACKAGE_DIR
11+
1312
PLEX_SHAPES_SHACL_FILEPATH = str(PACKAGE_DIR / 'resources' / 'plex-shapes.ttl')
1413

1514

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
@prefix this: <http://www.example.org/workflow1> .
2-
@prefix sub: <http://www.example.org/workflow1#> .
32
@prefix pplan: <http://purl.org/net/p-plan#> .
43
@prefix terms: <http://purl.org/dc/terms/> .
54
@prefix pwo: <http://purl.org/spar/pwo/> .
5+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
66

77
{
88
this: a pplan:Plan ;
99
terms:description "This is a test workflow." ;
10-
pwo:hasFirstStep sub:step1 .
10+
pwo:hasFirstStep <http://www.example.org/step1> ;
11+
rdfs:label 'Test workflow' .
1112

12-
sub:step1 pplan:isStepOfPlan this: .
13+
<http://www.example.org/step1> pplan:isStepOfPlan this: .
1314
}

tests/resources/test_workflow_including_steps.trig

Lines changed: 0 additions & 43 deletions
This file was deleted.

0 commit comments

Comments
 (0)