diff --git a/.travis.yml b/.travis.yml
index ed6ad35..48cbfc7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,8 +6,11 @@ python:
os:
- linux
before_install:
+ - sudo apt-get update
+ - sudo apt-get install graphviz
- python --version
install:
+ - pip install -r requirements.txt
- pip install -r test-requirements.txt
- python setup.py install
# command to run tests
@@ -23,6 +26,7 @@ matrix:
osx_image: xcode9.4 # Python 3.7 running on macOS 10.13
language: shell # 'language: python' is an error on Travis CI macOS
before_install:
+ - brew install graphviz
- python3 --version
- pip3 install virtualenv
- virtualenv -p python3 venv
diff --git a/cwlkernel/kernel_magics.py b/cwlkernel/kernel_magics.py
index c3cd68e..88eaf40 100644
--- a/cwlkernel/kernel_magics.py
+++ b/cwlkernel/kernel_magics.py
@@ -6,6 +6,9 @@
from pathlib import Path
from typing import List
+import pydot
+from cwltool.cwlviewer import CWLViewer
+from cwltool.main import main as cwltool_main
from ruamel.yaml import YAML
from .CWLKernel import CONF as CWLKernel_CONF
@@ -342,6 +345,33 @@ def magics(kernel: CWLKernel, arg: str):
)
+@CWLKernel.register_magic('view')
+def visualize_graph(kernel: CWLKernel, tool_id: str):
+ """Visualize a Workflow"""
+ tool_id = tool_id.strip()
+ path = kernel.workflow_repository.get_tools_path_by_id(tool_id)
+ rdf_stream = StringIO()
+ import logging
+ handler = logging.StreamHandler()
+ cwltool_main(['--print-rdf', os.path.abspath(path)], stdout=rdf_stream, logger_handler=handler)
+ cwl_viewer = CWLViewer(rdf_stream.getvalue())
+ (dot_object,) = pydot.graph_from_dot_data(cwl_viewer.dot())
+ image = dot_object.create('dot', 'svg')
+
+ kernel.send_response(
+ kernel.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ "image/svg+xml": image.decode(),
+ "text/plain": image.decode()
+ },
+ 'metadata': {},
+ },
+
+ )
+
+
# import user's magic commands
if CWLKernel_CONF.CWLKERNEL_MAGIC_COMMANDS_DIRECTORY is not None:
diff --git a/examples/Compose.ipynb b/examples/Compose.ipynb
index c030814..19560b5 100644
--- a/examples/Compose.ipynb
+++ b/examples/Compose.ipynb
@@ -96,7 +96,13 @@
"type": "File"
}
],
- "outputs": [],
+ "outputs": [
+ {
+ "id": "tailoutput",
+ "outputSource": "tailstepid/tailoutput",
+ "type": "File"
+ }
+ ],
"requirements": {},
"steps": {
"headstepid": {
@@ -112,13 +118,15 @@
"in": {
"tailinput": "headstepid/headoutput"
},
- "out": [],
+ "out": [
+ "tailoutput"
+ ],
"run": "tail.cwl"
}
}
},
"text/plain": [
- "{\"cwlVersion\": \"v1.0\", \"class\": \"Workflow\", \"id\": \"main\", \"inputs\": [{\"id\": \"inputfile\", \"type\": \"File\"}], \"outputs\": [], \"steps\": {\"tailstepid\": {\"run\": \"tail.cwl\", \"in\": {\"tailinput\": \"headstepid/headoutput\"}, \"out\": []}, \"headstepid\": {\"run\": \"head.cwl\", \"in\": {\"headinput\": \"inputfile\"}, \"out\": [\"headoutput\"]}}, \"requirements\": {}}"
+ "{\"cwlVersion\": \"v1.0\", \"class\": \"Workflow\", \"id\": \"main\", \"inputs\": [{\"id\": \"inputfile\", \"type\": \"File\"}], \"outputs\": [{\"id\": \"tailoutput\", \"type\": \"File\", \"outputSource\": \"tailstepid/tailoutput\"}], \"steps\": {\"tailstepid\": {\"run\": \"tail.cwl\", \"in\": {\"tailinput\": \"headstepid/headoutput\"}, \"out\": [\"tailoutput\"]}, \"headstepid\": {\"run\": \"head.cwl\", \"in\": {\"headinput\": \"inputfile\"}, \"out\": [\"headoutput\"]}}, \"requirements\": {}}"
]
},
"metadata": {
@@ -139,6 +147,7 @@
"type: File\n",
"% newWorkflowAddStepIn tailstepid headstepid headoutput\n",
"tailinput: headstepid/headoutput\n",
+ "% newWorkflowAddOutputSource tailstepid/tailoutput File\n",
"% newWorkflowBuild"
]
},
@@ -156,9 +165,22 @@
},
{
"data": {
- "application/json": {},
+ "application/json": {
+ "tailoutput": {
+ "basename": "tail.out",
+ "checksum": "sha1$e186f07099395040cf9d83ff1eb0a5dad4801937",
+ "class": "File",
+ "http://commonwl.org/cwltool#generation": 0,
+ "id": "tailoutput",
+ "location": "file:///private/tmp/CWLKERNEL_DATA/d8f78c63-0b6a-413b-b60e-03d8f96165c8/runtime_data/tail.out",
+ "nameext": ".out",
+ "nameroot": "tail",
+ "result_counter": 0,
+ "size": 688
+ }
+ },
"text/plain": [
- "{}"
+ "{\"tailoutput\": {\"location\": \"file:///private/tmp/CWLKERNEL_DATA/d8f78c63-0b6a-413b-b60e-03d8f96165c8/runtime_data/tail.out\", \"basename\": \"tail.out\", \"nameroot\": \"tail\", \"nameext\": \".out\", \"class\": \"File\", \"checksum\": \"sha1$e186f07099395040cf9d83ff1eb0a5dad4801937\", \"size\": 688, \"http://commonwl.org/cwltool#generation\": 0, \"id\": \"tailoutput\", \"result_counter\": 0}}"
]
},
"metadata": {
@@ -174,7 +196,157 @@
"% execute main\n",
"inputfile: \n",
" class: File\n",
- " location: /Users/dks/Desktop/data.csv"
+ " location: /Users/dks/Workspaces/CWLKernel/tests/input_data/data.csv"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "% view main"
]
}
],
diff --git a/examples/multipleSteps.ipynb b/examples/multipleSteps.ipynb
index ac5457b..5c948ae 100644
--- a/examples/multipleSteps.ipynb
+++ b/examples/multipleSteps.ipynb
@@ -276,6 +276,15 @@
"source": [
"% displayData tailoutput"
]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "% view main"
+ ]
}
],
"metadata": {
diff --git a/requirements.txt b/requirements.txt
index 17ed56b..50908a8 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-cwltool>=3.0.20200706173533
+git+https://github.com/giannisdoukas/cwltool.git@cwl-view#egg=cwltool
jsonschema>=3.2.0
jupyter-client>=5.3.4
jupyter-core>=4.6.3
@@ -8,4 +8,5 @@ PyYAML>=5.3.1
pandas>=1.0.4
notebook>=6.0.3
requests>=2.23.0
-pygtrie>=2.3.3
\ No newline at end of file
+pygtrie>=2.3.3
+pydot>=1.4.1
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 7398c7d..df21f28 100644
--- a/setup.py
+++ b/setup.py
@@ -59,16 +59,6 @@ def get_version(rel_path):
with open(os.sep.join([os.path.abspath(os.path.dirname(__file__)), "README.md"]), "r") as fh:
long_description = fh.read()
-with open('requirements.txt') as f:
- req = f.readlines()
-
-for i, r in enumerate(req):
- r = r.rstrip()
- if r.startswith('git+https://'):
- egg_position = r.rfind("#egg=")
- dependency_name = r[egg_position + 5:]
- req[i] = f"{dependency_name} @ {r[:egg_position]}"
-
setup(
name=name,
version=get_version(f"{name}/CWLKernel.py"),
@@ -91,9 +81,4 @@ def get_version(rel_path):
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
],
- # cmdclass={
- # 'develop': PostDevelopCommand,
- # 'install': PostInstallCommand,
- # },
- install_requires=req
)
diff --git a/tests/test_CWLKernel.py b/tests/test_CWLKernel.py
index 9bb6193..9af0677 100644
--- a/tests/test_CWLKernel.py
+++ b/tests/test_CWLKernel.py
@@ -844,7 +844,6 @@ def test_magics_magic_command(self):
commands = [f'\t- {cmd}' for cmd in kernel._magic_commands.keys()]
commands.sort()
-
self.assertDictEqual(
{'status': 'ok', 'execution_count': 0, 'payload': [], 'user_expressions': {}},
kernel.do_execute(f"""% magics""")
@@ -867,6 +866,73 @@ def test_magics_magic_command(self):
self.assertIn(' '.join('Display all the data which are registered in the kernel session.'.split()),
' '.join(responses[-1][0][2]['text'].split()))
+ def test_view_magic_command(self):
+ kernel = CWLKernel()
+ # cancel send_response
+ responses = []
+ kernel.send_response = lambda *args, **kwargs: responses.append((args, kwargs))
+ with open(os.sep.join([self.cwl_directory, 'head.cwl'])) as f:
+ head = f.read()
+ with open(os.sep.join([self.cwl_directory, 'tail.cwl'])) as f:
+ tail = f.read()
+ self.assertDictEqual(
+ {'status': 'ok', 'execution_count': 0, 'payload': [], 'user_expressions': {}},
+ kernel.do_execute(head)
+ )
+ self.assertDictEqual(
+ {'status': 'ok', 'execution_count': 0, 'payload': [], 'user_expressions': {}},
+ kernel.do_execute(tail)
+ )
+
+ self.assertDictEqual(
+ {'status': 'ok', 'execution_count': 0, 'payload': [], 'user_expressions': {}},
+ kernel.do_execute("""% newWorkflow main
+% newWorkflowAddStep tail tailstepid
+% newWorkflowAddStep head headstepid
+% newWorkflowAddInput headstepid headinput
+id: inputfile
+type: File
+% newWorkflowAddStepIn tailstepid headstepid headoutput
+tailinput: headstepid/headoutput
+% newWorkflowAddOutputSource tailstepid/tailoutput File
+% newWorkflowBuild""")
+ )
+
+ self.assertDictEqual(
+ {
+ "cwlVersion": "v1.0",
+ "class": "Workflow",
+ "id": "main",
+ "inputs": [{'id': 'inputfile', 'type': 'File'}],
+ "outputs": [{'id': 'tailoutput', 'type': 'File', 'outputSource': "tailstepid/tailoutput"}],
+ "steps": {
+ "headstepid":
+ {
+ "run": "head.cwl",
+ "in": {"headinput": "inputfile"},
+ "out": ['headoutput']
+ },
+ "tailstepid":
+ {
+ "run": "tail.cwl",
+ "in": {"tailinput": "headstepid/headoutput"},
+ "out": ['tailoutput']
+ },
+ },
+ 'requirements': {}
+ },
+ yaml.load(kernel._workflow_repository.get_by_id("main").to_yaml(), yaml.Loader),
+ )
+
+ self.assertDictEqual(
+ {'status': 'ok', 'execution_count': 0, 'payload': [], 'user_expressions': {}},
+ kernel.do_execute('% view main')
+ )
+
+ self.assertIn(
+ 'image/svg+xml',
+ responses[-1][0][2]['data'])
+
if __name__ == '__main__':
unittest.main()