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
16 changes: 14 additions & 2 deletions src/runcrate/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,27 @@ def cli():
type=click.Path(exists=True, dir_okay=False, readable=True, path_type=Path),
help="path to a README file (should be README.md in Markdown format)",
)
def convert(root, output, license, workflow_name, readme):
@click.option(
"-n",
"--crate-name",
metavar="STRING",
help="crate name",
)
@click.option(
"-d",
"--crate-desc",
metavar="STRING",
help="crate description",
)
def convert(root, output, license, workflow_name, readme, crate_name, crate_desc):
"""\
Convert a CWLProv RO bundle into a Workflow Run RO-Crate.

RO_DIR: top-level directory of the CWLProv RO
"""
if not output:
output = Path(f"{root.name}.crate.zip")
builder = ProvCrateBuilder(root, workflow_name, license, readme)
builder = ProvCrateBuilder(root, workflow_name, license, readme, crate_name, crate_desc)
crate = builder.build()
if output.suffix == ".zip":
crate.write_zip(output)
Expand Down
25 changes: 17 additions & 8 deletions src/runcrate/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@

CWLPROV_NONE = "https://w3id.org/cwl/prov#None"

# https://w3id.org/workflowhub/workflow-ro-crate/1.0
WROC_PROFILE_VERSION = "1.0"
DEFAULT_LICENSE = "notspecified"

DOCKER_IMG_TYPE = "https://w3id.org/ro/terms/workflow-run#DockerImage"

Expand Down Expand Up @@ -197,11 +199,14 @@ def get_workflow(wf_path):

class ProvCrateBuilder:

def __init__(self, root, workflow_name=None, license=None, readme=None):
def __init__(self, root, workflow_name=None, license=None, readme=None,
crate_name=None, crate_desc=None):
self.root = Path(root)
self.workflow_name = workflow_name
self.license = license
self.license = license or DEFAULT_LICENSE
self.readme = Path(readme) if readme else readme
self.crate_name = crate_name
self.crate_desc = crate_desc
self.wf_path = self.root / "workflow" / WORKFLOW_BASENAME
self.cwl_defs = get_workflow(self.wf_path)
self.step_maps = self._get_step_maps(self.cwl_defs)
Expand Down Expand Up @@ -290,9 +295,9 @@ def get_dict(self, entity):
def build(self):
crate = ROCrate(gen_preview=False)
crate.metadata.extra_contexts.append(TERMS_NAMESPACE)
self.add_root_metadata(crate)
self.add_profiles(crate)
self.add_workflow(crate)
self.add_root_metadata(crate)
self.add_engine_run(crate)
self.add_action(crate, self.workflow_run)
self.patch_workflow_input_collection(crate)
Expand All @@ -301,13 +306,16 @@ def build(self):
return crate

def add_root_metadata(self, crate):
if self.license:
crate.root_dataset["license"] = self.license
crate.root_dataset["license"] = self.license
if self.readme:
readme = crate.add_file(self.readme)
readme["about"] = crate.root_dataset
if self.readme.suffix.lower() == ".md":
readme["encodingFormat"] = "text/markdown"
if not self.crate_name:
self.crate_name = f"run of {self.workflow_name}"
crate.root_dataset["name"] = self.crate_name
crate.root_dataset["description"] = self.crate_desc or self.crate_name

def add_profiles(self, crate):
profiles = []
Expand Down Expand Up @@ -338,10 +346,11 @@ def add_workflow(self, crate):
lang_version=lang_version, gen_cwl=False, properties=properties
)
cwl_workflow = self.cwl_defs[workflow.id]
wf_name = self.wf_path.name
if not self.workflow_name:
self.workflow_name = self.wf_path.name
if hasattr(cwl_workflow, "label") and cwl_workflow.label:
wf_name = cwl_workflow.label
workflow["name"] = self.workflow_name or wf_name
self.workflow_name = cwl_workflow.label
workflow["name"] = self.workflow_name
if hasattr(cwl_workflow, "doc") and cwl_workflow.doc:
workflow["description"] = cwl_workflow.doc
# cannot convert "intent" to featureList: workflow is not a SoftwareApplication
Expand Down
34 changes: 31 additions & 3 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_cli_convert(data_dir, tmpdir, monkeypatch):
crate_zip = tmpdir / f"{root.name}.crate.zip"
assert crate_zip.is_file()
crate = ROCrate(crate_zip)
assert not crate.root_dataset.get("license")
assert crate.root_dataset.get("license") == "notspecified"
workflow = crate.mainEntity
assert workflow["name"] == "packed.cwl"

Expand All @@ -47,7 +47,7 @@ def test_cli_convert_output(data_dir, tmpdir):
assert result.exit_code == 0, result.exception
assert crate_zip.is_file()
crate = ROCrate(crate_zip)
assert not crate.root_dataset.get("license")
assert crate.root_dataset.get("license") == "notspecified"
workflow = crate.mainEntity
assert workflow["name"] == "packed.cwl"
crate_zip.unlink()
Expand All @@ -59,7 +59,7 @@ def test_cli_convert_output(data_dir, tmpdir):
assert result.exit_code == 0, result.exception
assert crate_dir.is_dir()
crate = ROCrate(crate_dir)
assert not crate.root_dataset.get("license")
assert crate.root_dataset.get("license") == "notspecified"
workflow = crate.mainEntity
assert workflow["name"] == "packed.cwl"

Expand Down Expand Up @@ -102,6 +102,34 @@ def test_cli_convert_workflow_name(data_dir, tmpdir):
assert workflow["name"] == workflow_name


def test_cli_convert_crate_name(data_dir, tmpdir):
root = data_dir / "revsort-run-1"
crate_dir = tmpdir / "revsort-run-1-crate"
workflow_name = "RevSort"
crate_name = "name of the crate"
runner = CliRunner()
args = ["convert", str(root), "-o", str(crate_dir), "-w", workflow_name, "-n", crate_name]
result = runner.invoke(cli, args)
assert result.exit_code == 0, result.exception
crate = ROCrate(crate_dir)
assert crate.root_dataset["name"] == crate_name
assert crate.root_dataset["description"] == crate_name


def test_cli_convert_crate_desc(data_dir, tmpdir):
root = data_dir / "revsort-run-1"
crate_dir = tmpdir / "revsort-run-1-crate"
workflow_name = "RevSort"
crate_desc = "description of the crate"
runner = CliRunner()
args = ["convert", str(root), "-o", str(crate_dir), "-w", workflow_name, "-d", crate_desc]
result = runner.invoke(cli, args)
assert result.exit_code == 0, result.exception
crate = ROCrate(crate_dir)
assert crate.root_dataset["name"] == f"run of {workflow_name}"
assert crate.root_dataset["description"] == crate_desc


def test_cli_convert_readme(data_dir, tmpdir):
root = data_dir / "revsort-run-1"
crate_dir = tmpdir / "revsort-run-1-crate"
Expand Down
25 changes: 24 additions & 1 deletion tests/test_cwlprov_crate_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ def test_revsort(data_dir, tmpdir):
assert readme_f["about"] is crate.root_dataset
workflow = crate.mainEntity
assert workflow.id == "packed.cwl"
assert workflow["name"] == "RevSort"
assert workflow["name"] == workflow_name
assert crate.root_dataset["name"] == f"run of {workflow_name}"
assert crate.root_dataset["description"] == f"run of {workflow_name}"
tools = workflow["hasPart"]
assert len(tools) == 2
for entity in tools:
Expand Down Expand Up @@ -1191,3 +1193,24 @@ def test_agent_no_name(data_dir, tmpdir):
assert agent.type == "Person"
assert agent.id == "https://orcid.org/0000-0001-9447-460X"
assert "name" not in agent


def test_no_license(data_dir):
root = data_dir / "revsort-run-1"
builder = ProvCrateBuilder(root)
crate = builder.build()
assert crate.root_dataset["license"] == "notspecified"


def test_name_desc(data_dir):
root = data_dir / "revsort-run-1"
workflow_name = "RevSort"
crate_name = "name of the crate"
builder = ProvCrateBuilder(root, workflow_name=workflow_name, crate_name=crate_name)
crate = builder.build()
assert crate.root_dataset["name"] == crate_name
assert crate.root_dataset["description"] == crate_name
crate_desc = "description of the crate"
builder = ProvCrateBuilder(root, workflow_name=workflow_name, crate_name=crate_name, crate_desc=crate_desc)
crate = builder.build()
assert crate.root_dataset["description"] == crate_desc