Repository: ResearchObject/ro-crate-py
Pull request #31 (2020-11-06 11:55:46)

Update workflow RO-Crate generation
Fixes a problem that prevented the abstract CWL from being added to the output crate. Adds the `Workflow` entity and simplifies workflow RO-Crate generation.


Patch:
diff --git a/rocrate/model/workflow.py b/rocrate/model/workflow.py
new file mode 100644
index 0000000..a0a4ccd
--- /dev/null
+++ b/rocrate/model/workflow.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+# Copyright 2020 The University of Manchester, UK
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+from .file import File
+
+
+class Workflow(File):
+    """\
+    A scientific workflow that was used (or can be used) to analyze or
+    generate files in the RO-Crate.
+    """
+    TYPES = ["File", "SoftwareSourceCode", "ComputationalWorkflow"]
+
+    def _empty(self):
+        return {
+            "@id": self.id,
+            "@type": self.TYPES,
+            "name": os.path.splitext(self.id)[0],
+        }
+
+
+class LegacyWorkflow(Workflow):
+
+    TYPES = ["File", "SoftwareSourceCode", "Workflow"]
diff --git a/rocrate/rocrate_api.py b/rocrate/rocrate_api.py
index e4f9bca..3d1b2d9 100644
--- a/rocrate/rocrate_api.py
+++ b/rocrate/rocrate_api.py
@@ -14,6 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import atexit
 import os
 import tempfile
 from contextlib import redirect_stdout
@@ -21,6 +22,7 @@ from pathlib import Path
 
 import rocrate.rocrate as roc
 from rocrate.model import entity
+from rocrate.model.workflow import Workflow
 from galaxy2cwl import get_cwl_interface
 
 
@@ -60,12 +62,11 @@ def make_workflow_rocrate(workflow_path, wf_type, include_files=[],
     # diagram: an image/graphical workflow representation.
     # If a CWL/CWLAbstract file is provided, this is generated using cwltool
 
-    # abs_path = os.path.abspath(workflow_path)
     wf_crate = roc.ROCrate()
-    # add main workflow file
-    file_name = os.path.basename(workflow_path)
+    wf_path = Path(workflow_path)
     # should this be added in a special path within the crate?
-    wf_file = wf_crate.add_file(workflow_path, file_name)
+    wf_file = Workflow(wf_crate, str(wf_path), wf_path.name)
+    wf_crate._add_data_entity(wf_file)
     wf_crate.set_main_entity(wf_file)
     if wf_type == 'CWL':
         programming_language_entity = entity.Entity(
@@ -81,50 +82,24 @@ def make_workflow_rocrate(workflow_path, wf_type, include_files=[],
     if wf_type == 'Galaxy':
         if not cwl:
             # create cwl_abstract
-            try:
-                with tempfile.NamedTemporaryFile(
-                        mode='w', delete=False, suffix=".cwl"
-                ) as cwl_abstract_out:
-                    with redirect_stdout(cwl_abstract_out):
-                        get_cwl_interface.main(['1', workflow_path])
-                abstract_wf_file = wf_crate.add_file(
-                    cwl_abstract_out.name,
-                    'abstract_wf.cwl',
-                    properties={
-                        "@type": ["File", "SoftwareSourceCode", "ComputationalWorkflow"]
-                    }
-                )
-            finally:
-                Path(cwl_abstract_out.name).unlink()
+            with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".cwl") as f:
+                with redirect_stdout(f):
+                    get_cwl_interface.main(['1', workflow_path])
+            atexit.register(os.unlink, f.name)
+            abstract_wf_id = wf_path.with_suffix(".cwl").name
+            abstract_wf_file = Workflow(wf_crate, f.name, abstract_wf_id)
+            wf_crate._add_data_entity(abstract_wf_file)
             wf_file["subjectOf"] = abstract_wf_file
         programming_language_entity = entity.Entity(
             wf_crate, 'https://galaxyproject.org/'
         )
-
-    # SET PROPERTIES
-
-    # A contextual entity representing a SoftwareApplication or
-    # ComputerLanguage MUST have a name, url and version, which should
-    # indicate a known version the workflow/script was developed or tested
-    # with
     if programming_language_entity:
         wf_file['programmingLanguage'] = programming_language_entity
 
-    # based on ro-crate specification. For workflows: @type is an array
-    # with at least File and Workflow as values.
-    wf_type = wf_file['@type']
-    if not isinstance(wf_type, list):
-        wf_type = [wf_type]
-    if 'ComputationalWorkflow' not in wf_type:
-        wf_type.append('ComputationalWorkflow')
-    if 'SoftwareSourceCode' not in wf_type:
-        wf_type.append('SoftwareSourceCode')
-    wf_file['@type'] = wf_type
-
     # if the source is a remote URL then add https://schema.org/codeRepository
     # property to it this can be checked by checking if the source is a URL
     # instead of a local path
-    if 'url' in wf_file.properties().keys():
+    if 'url' in wf_file.properties():
         wf_file['codeRepository'] = wf_file['url']
 
     # add extra files


Test patch:
diff --git a/test/test_api.py b/test/test_api.py
index 3f30d05..7188b57 100644
--- a/test/test_api.py
+++ b/test/test_api.py
@@ -22,8 +22,8 @@ def test_galaxy_wf_crate(test_data_dir, tmpdir, helpers):
     with open(wf_path) as f1, open(wf_out_path) as f2:
         assert f1.read() == f2.read()
 
-    # abstract_wf_out_path = out_path / abstract_wf_id
-    # assert abstract_wf_out_path.exists()
+    abstract_wf_out_path = out_path / abstract_wf_id
+    assert abstract_wf_out_path.exists()
 
 
 def test_cwl_wf_crate(test_data_dir, tmpdir, helpers):