From 236f107c8308adb294703483150b5a47e7469acb Mon Sep 17 00:00:00 2001 From: "Kacper Kowalik (Xarthisius)" Date: Tue, 29 Oct 2024 13:57:33 -0500 Subject: [PATCH 1/4] Handle perfomances being a single dict --- tro_utils/tro_utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tro_utils/tro_utils.py b/tro_utils/tro_utils.py index 2b87461..8871c0f 100644 --- a/tro_utils/tro_utils.py +++ b/tro_utils/tro_utils.py @@ -429,6 +429,8 @@ def generate_report(self, template, report): dot.attr("node", shape="box3d", style="filled, rounded", fillcolor="#D6FDD0") + if isinstance(graph["trov:hasPerformance"], dict): + graph["trov:hasPerformance"] = [graph["trov:hasPerformance"]] for trp in graph["trov:hasPerformance"]: description = trp["rdfs:comment"] accessed = arrangements[trp["trov:accessedArrangement"]["@id"]]["name"] From 09b4dce6ccf6e76e09d56ecf8eef0a838d2af7d3 Mon Sep 17 00:00:00 2001 From: Craig Willis Date: Tue, 29 Oct 2024 13:58:07 -0500 Subject: [PATCH 2/4] Add default template --- tro_utils/default.jinja2 | 69 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tro_utils/default.jinja2 diff --git a/tro_utils/default.jinja2 b/tro_utils/default.jinja2 new file mode 100644 index 0000000..faf54c0 --- /dev/null +++ b/tro_utils/default.jinja2 @@ -0,0 +1,69 @@ +# TRO Report + + +## TRO Information +| Property | Value | +| -------- | ----- | +| Name | {{ tro["name"] }} | +| Description | {{ tro["description"] }} | +| Created by | {{ tro["createdBy"] }} | +| Created date | {{ tro["createdDate"] }} | + + +## TRACE System Information + +This TRO was generated by the following TRACE System: + +| Property | Value | +| -------- | ----- | +| Name | {{ tro["trs"]["name"] }} | +| Description | {{ tro["trs"]["description"] }} | +| Owner | {{ tro["trs"]["owner"] }} | +| Contact | {{ tro["trs"]["contact"] }} | +| URL | {{ tro["trs"]["url"] }} | + +
+Show Public Key +{{ tro["trs"]["publicKey"] }} +
+ +### Capabilities +| Capability | Description | +| ----------- | ------------ | +{%- for capability in tro["trs"]["capabilities"] %} +| {{ capability["name"] }} | {{ capability["description"] }} | +{%- endfor %} + +## Trusted Research Performances + +A Trusted Research Performance (TRP) captures the execution of a process in the context of a TRACE system. Typically, a TRP would take as input one or more sets of files (input arrangements) and produce another set of files (output arrangements). + + + +| Description | Accessed | Contributed | +| ----------- | ------------ | ------------ | +{%- for trp in tro["trps"] %} +| {{ trp["description"] }} | {{ trp["accessed"] }} | {{ trp["contributed"] }} | +{%- endfor %} + + +## Arrangements + +Arrangements define how artifacts, typically files, are organized before and after each TRP. Artifacts are defined by their location and a checksum of their contents. Artifacts may be local or remote, defined by an URI. They may be included or excluded from the associated archive. + +{%- for arrangement in tro["arrangements"].keys() %} +### {{ tro["arrangements"][arrangement]["name"] }} +| Artifact | SHA-256 | Status | +| -------- | -------- | ------ | +{%- for location in tro["arrangements"][arrangement]["artifacts"] %} + {%- if tro["arrangements"][arrangement]["artifacts"][location]["excluded"] != "None" %} +| ~~{{ location }}~~ | {{ tro["arrangements"][arrangement]["artifacts"][location]["sha256"] | truncate(32) }} | Excluded due to {{ tro["arrangements"][arrangement]["artifacts"][location]["excluded"] }} | + {%- else %} +| {{ location }} | {{ tro["arrangements"][arrangement]["artifacts"][location]["sha256"] | truncate(32) }} | {{ tro["arrangements"][arrangement]["artifacts"][location]["status"] }} | + + {%- endif %} +{%- endfor %} +{%- endfor %} + + + From dd042c5896283c18e09326271551ca17eac20ef3 Mon Sep 17 00:00:00 2001 From: "Kacper Kowalik (Xarthisius)" Date: Tue, 29 Oct 2024 13:58:33 -0500 Subject: [PATCH 3/4] Allow template to be a name or a path. Fixes #2 --- pyproject.toml | 3 +++ tro_utils/cli.py | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b8e7332..ddbef7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,3 +57,6 @@ dev = [ "Sphinx>=1.8.5", "twine>=1.14.0", ] + +[tool.setuptools.package-data] +tro_utils = ["*.jinja2"] diff --git a/tro_utils/cli.py b/tro_utils/cli.py index 628c8d2..301c73a 100644 --- a/tro_utils/cli.py +++ b/tro_utils/cli.py @@ -1,4 +1,5 @@ """Console script for tro_utils.""" +import os import sys import click @@ -6,6 +7,37 @@ from . import TRPAttribute from .tro_utils import TRO +_TEMPLATES = { + "default": { + "description": "Default pretty template by Craig Willis", + "filename": "default.jinja2", + }, +} + + +class StringOrPath(click.ParamType): + """Custom parameter type to accept either a valid string or a file path.""" + + name = "string_or_path" + + def __init__(self, templates=None): + self.valid_strings = templates.keys() + + def convert(self, value, param, ctx): + # Check if the value is in the allowed string set + if value in self.valid_strings: + return value + # Check if the value is a valid path + elif os.path.exists(value) and os.path.isfile(value): + return value + else: + self.fail( + f"'{value}' is neither a valid option ({', '.join(self.valid_strings)}) " + f"nor a valid file path.", + param, + ctx, + ) + @click.group() @click.option( @@ -189,12 +221,20 @@ def sign(ctx): @cli.command(help="Generate a report of the TRO", name="report") @click.option( - "--template", "-t", type=click.Path(), required=True, help="Template file" + "--template", + "-t", + type=StringOrPath(_TEMPLATES), + required=True, + help=f"Template file or one of the following: {', '.join(_TEMPLATES.keys())}", ) @click.option("--output", "-o", type=click.Path(), required=True, help="Output file") @click.pass_context def generate_report(ctx, template, output): declaration = ctx.parent.params.get("declaration") + if template in _TEMPLATES: + template = os.path.join( + os.path.dirname(__file__), _TEMPLATES[template]["filename"] + ) tro = TRO( filepath=declaration, ) From cca2f65dcbc2f233c983998b97b85ed5f98788ac Mon Sep 17 00:00:00 2001 From: "Kacper Kowalik (Xarthisius)" Date: Tue, 27 Jan 2026 13:20:12 -0600 Subject: [PATCH 4/4] Use more vanilla attributes in the default template. Inline image --- tro_utils/default.jinja2 | 54 ++++++++++++++++++++-------------------- tro_utils/tro_utils.py | 27 ++++---------------- 2 files changed, 32 insertions(+), 49 deletions(-) diff --git a/tro_utils/default.jinja2 b/tro_utils/default.jinja2 index faf54c0..1ff41a4 100644 --- a/tro_utils/default.jinja2 +++ b/tro_utils/default.jinja2 @@ -2,48 +2,49 @@ ## TRO Information -| Property | Value | -| -------- | ----- | -| Name | {{ tro["name"] }} | -| Description | {{ tro["description"] }} | -| Created by | {{ tro["createdBy"] }} | -| Created date | {{ tro["createdDate"] }} | +| Property | Value | +| -------- | ----- | +| Name | {{ tro["schema:name"] }} | +| Description | {{ tro["schema:description"] }} | +| Created by | {{ tro["schema:creator"] }} | +| Created date | {{ tro["schema:dateCreated"] }} | ## TRACE System Information This TRO was generated by the following TRACE System: -| Property | Value | -| -------- | ----- | -| Name | {{ tro["trs"]["name"] }} | -| Description | {{ tro["trs"]["description"] }} | -| Owner | {{ tro["trs"]["owner"] }} | -| Contact | {{ tro["trs"]["contact"] }} | -| URL | {{ tro["trs"]["url"] }} | +| Property | Value | +| -------- | ----- | +| Name | {{ tro["trov:wasAssembledBy"]["trov:name"] }} | +| Description | {{ tro["trov:wasAssembledBy"]["trov:description"] }} | +| Owner | {{ tro["trov:wasAssembledBy"]["trov:owner"] }} | +| Contact | {{ tro["trov:wasAssembledBy"]["trov:contact"] }} | +| URL | {{ tro["trov:wasAssembledBy"]["trov:url"] }} |
Show Public Key -{{ tro["trs"]["publicKey"] }} +{{ tro["trov:wasAssembledBy"]["trov:publicKey"] }}
### Capabilities -| Capability | Description | + +| Capability | Description | | ----------- | ------------ | -{%- for capability in tro["trs"]["capabilities"] %} -| {{ capability["name"] }} | {{ capability["description"] }} | +{%- for capability in tro["trov:wasAssembledBy"].get("trov:hasCapability", []) %} +| {{ capability.get("@type", "") }} | {{ capability.get("trov:description", "") }} | {%- endfor %} ## Trusted Research Performances A Trusted Research Performance (TRP) captures the execution of a process in the context of a TRACE system. Typically, a TRP would take as input one or more sets of files (input arrangements) and produce another set of files (output arrangements). - + -| Description | Accessed | Contributed | +| Description | Accessed | Contributed | | ----------- | ------------ | ------------ | {%- for trp in tro["trps"] %} -| {{ trp["description"] }} | {{ trp["accessed"] }} | {{ trp["contributed"] }} | +| {{ trp["description"] }} | {{ trp["accessed"] }} | {{ trp["contributed"] }} | {%- endfor %} @@ -52,18 +53,17 @@ A Trusted Research Performance (TRP) captures the execution of a process in the Arrangements define how artifacts, typically files, are organized before and after each TRP. Artifacts are defined by their location and a checksum of their contents. Artifacts may be local or remote, defined by an URI. They may be included or excluded from the associated archive. {%- for arrangement in tro["arrangements"].keys() %} + ### {{ tro["arrangements"][arrangement]["name"] }} + | Artifact | SHA-256 | Status | -| -------- | -------- | ------ | +| -------- | -------- | ------ | {%- for location in tro["arrangements"][arrangement]["artifacts"] %} - {%- if tro["arrangements"][arrangement]["artifacts"][location]["excluded"] != "None" %} -| ~~{{ location }}~~ | {{ tro["arrangements"][arrangement]["artifacts"][location]["sha256"] | truncate(32) }} | Excluded due to {{ tro["arrangements"][arrangement]["artifacts"][location]["excluded"] }} | + {%- if tro["arrangements"][arrangement]["artifacts"][location].get("excluded") %} +| ~~`{{ location }}`~~ | {{ tro["arrangements"][arrangement]["artifacts"][location]["sha256"] | truncate(32) }} | Excluded due to {{ tro["arrangements"][arrangement]["artifacts"][location]["excluded"] }} | {%- else %} -| {{ location }} | {{ tro["arrangements"][arrangement]["artifacts"][location]["sha256"] | truncate(32) }} | {{ tro["arrangements"][arrangement]["artifacts"][location]["status"] }} | +| `{{ location }}` | {{ tro["arrangements"][arrangement]["artifacts"][location]["sha256"] | truncate(32) }} | {{ tro["arrangements"][arrangement]["artifacts"][location]["status"] }} | {%- endif %} {%- endfor %} {%- endfor %} - - - diff --git a/tro_utils/tro_utils.py b/tro_utils/tro_utils.py index 8871c0f..55eaba6 100644 --- a/tro_utils/tro_utils.py +++ b/tro_utils/tro_utils.py @@ -1,4 +1,5 @@ """Main module.""" +import base64 import hashlib import json import os @@ -393,7 +394,6 @@ def add_performance( def generate_report(self, template, report): graph = self.data["@graph"][0] - trs = graph["trov:wasAssembledBy"] composition = { obj["@id"]: obj for obj in graph["trov:hasComposition"]["trov:hasArtifact"] } @@ -439,7 +439,8 @@ def generate_report(self, template, report): dot.edge(accessed, description) dot.edge(description, contributed) - dot.render("workflow", directory=".", cleanup=True, format="png") + png_bytes = dot.pipe(format="png") + png_base64 = base64.b64encode(png_bytes).decode("utf-8") # Detect changes between arrangements # Which files were added? Which files changed? @@ -460,26 +461,8 @@ def generate_report(self, template, report): arrangements[keys[n]]["artifacts"][location]["status"] = "Created" data = { - "name": graph.get("schema:name", "No name provided"), - "description": graph.get("schema:description", "No Description provided"), - "creator": graph.get("schema:creator", "No creator provided"), - "dateCreated": graph.get("schema:dateCreated", "No date provided"), - "trs": { - "publicKey": trs.get("trov:publicKey"), - "name": trs.get("schema:name", ""), - "comment": trs["rdfs:comment"], - "publisher": trs.get("schema:publisher", ""), - "description": trs.get("schema:description", ""), - "email": trs.get("schema:email", ""), - "url": trs.get("schema:url", ""), - "capabilities": [ - { - "name": _.get("trov:name", _["@type"]), - "description": _.get("trov:description", ""), - } - for _ in trs["trov:hasCapability"] - ], - }, + **graph, + "workflow_diagram": png_base64, "trps": [ { "id": trp["@id"],