Skip to content

Commit

Permalink
Fix workflow report to target right workflow version.
Browse files Browse the repository at this point in the history
There was a past jmchilton todo in the code base about it. Directives that might target particular versions of a workflow (so workflow_display and workflow_image) now can take a workflow_version attribute and when these things are auto-populated for workflow invocations - they are populated with the right version for the invocation.
  • Loading branch information
jmchilton committed Nov 17, 2023
1 parent 257c2bb commit 7e515a6
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import LoadingSpan from "@/components/LoadingSpan.vue";
interface WorkflowDisplayProps {
workflowId: string;
workflowVersion?: string;
embedded?: boolean;
expanded?: boolean;
}
const props = withDefaults(defineProps<WorkflowDisplayProps>(), {
workflowVersion: null,
embedded: false,
expanded: false,
});
Expand All @@ -31,7 +33,13 @@ const loading = ref(true);
const workflowName = computed(() => (itemContent.value ? itemContent.value.name : "..."));
const downloadUrl = computed(() => withPrefix(`/api/workflows/${props.workflowId}/download?format=json-download`));
const importUrl = computed(() => withPrefix(`/workflow/imp?id=${props.workflowId}`));
const itemUrl = computed(() => withPrefix(`/api/workflows/${props.workflowId}/download?style=preview`));
const itemUrl = computed(() => {
let extra = "";
if (props.workflowVersion) {
extra = `&version=${props.workflowVersion}`;
}
return withPrefix(`/api/workflows/${props.workflowId}/download?style=preview${extra}`);
});
onMounted(async () => {
axios
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@ import { getAppRoot } from "@/onload/loadConfig";
interface WorkflowImageProps {
workflowId: string;
workflowVersion?: string;
size?: string;
}
const props = withDefaults(defineProps<WorkflowImageProps>(), {
size: "lg",
workflowVersion: null,
});
const src = computed(() => {
return `${getAppRoot()}workflow/gen_image?id=${props.workflowId}&embed=true`;
let extraArgs = "";
if (props.workflowVersion) {
extraArgs = `&version=${props.workflowVersion}`;
}
return `${getAppRoot()}workflow/gen_image?id=${props.workflowId}&embed=true${extraArgs}`;
});
const width = computed(() => {
const size = props.size;
Expand Down
6 changes: 5 additions & 1 deletion client/src/components/Markdown/MarkdownContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ const isVisible = computed(() => !isCollapsible.value || toggle.value);
<pre><code>{{ time }}</code></pre>
</div>
<div v-else-if="name == 'workflow_image'" class="workflow-image" style="text-align: center">
<WorkflowImage :workflow-id="args.workflow_id" :size="args.size || 'lg'" />
<WorkflowImage
:workflow-id="args.workflow_id"
:size="args.size || 'lg'"
:workflow-version="args.workflow_checkpoint || undefined" />
</div>
<div v-else-if="name == 'workflow_license'" class="workflow-license">
<WorkflowLicense :license-id="workflows[args.workflow_id]['license']" />
Expand Down Expand Up @@ -131,6 +134,7 @@ const isVisible = computed(() => !isCollapsible.value || toggle.value);
<WorkflowDisplay
v-else-if="name == 'workflow_display'"
:workflow-id="args.workflow_id"
:workflow-version="args.workflow_checkpoint"
:workflows="workflows" />
<Visualization v-else-if="name == 'visualization'" :args="args" />
<HistoryDatasetCollectionDisplay
Expand Down
4 changes: 2 additions & 2 deletions lib/galaxy/managers/markdown_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ class DynamicArguments:
"history_dataset_name": ["input", "output", "history_dataset_id"],
"history_dataset_type": ["input", "output", "history_dataset_id"],
"history_dataset_collection_display": ["input", "output", "history_dataset_collection_id"],
"workflow_display": ["workflow_id"],
"workflow_display": ["workflow_id", "workflow_checkpoint"],
"workflow_license": ["workflow_id"],
"workflow_image": ["workflow_id", "size"],
"workflow_image": ["workflow_id", "size", "workflow_checkpoint"],
"job_metrics": ["step", "job_id"],
"job_parameters": ["step", "job_id"],
"tool_stderr": ["step", "job_id"],
Expand Down
44 changes: 31 additions & 13 deletions lib/galaxy/managers/markdown_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,14 @@ def _remap(container, line):
rval = self.handle_dataset_name(line, hda)
elif container == "workflow_display":
stored_workflow = workflow_manager.get_stored_accessible_workflow(trans, encoded_id)
rval = self.handle_workflow_display(line, stored_workflow)
workflow_version_str = _parse_directive_argument_value("workflow_checkpoint", line)
workflow_version = None if not workflow_version_str else int(workflow_version_str)
rval = self.handle_workflow_display(line, stored_workflow, workflow_version)
elif container == "workflow_image":
stored_workflow = workflow_manager.get_stored_accessible_workflow(trans, encoded_id)
rval = self.handle_workflow_image(line, stored_workflow)
workflow_version_str = _parse_directive_argument_value("workflow_checkpoint", line)
workflow_version = None if not workflow_version_str else int(workflow_version_str)
rval = self.handle_workflow_image(line, stored_workflow, workflow_version)
elif container == "workflow_license":
stored_workflow = workflow_manager.get_stored_accessible_workflow(trans, encoded_id)
rval = self.handle_workflow_license(line, stored_workflow)
Expand Down Expand Up @@ -270,11 +274,11 @@ def handle_dataset_type(self, line, hda):
pass

@abc.abstractmethod
def handle_workflow_display(self, line, stored_workflow):
def handle_workflow_display(self, line, stored_workflow, workflow_version: Optional[int]):
pass

@abc.abstractmethod
def handle_workflow_image(self, line, stored_workflow):
def handle_workflow_image(self, line, stored_workflow, workflow_version: Optional[int]):
pass

@abc.abstractmethod
Expand Down Expand Up @@ -377,10 +381,10 @@ def handle_dataset_peek(self, line, hda):
def handle_dataset_info(self, line, hda):
self.extend_history_dataset_rendering_data(hda, "info", hda.info, "*No Dataset Info Available*")

def handle_workflow_display(self, line, stored_workflow):
def handle_workflow_display(self, line, stored_workflow, workflow_version: Optional[int]):
self.ensure_rendering_data_for("workflows", stored_workflow)["name"] = stored_workflow.name

def handle_workflow_image(self, line, stored_workflow):
def handle_workflow_image(self, line, stored_workflow, workflow_version: Optional[int]):
pass

def handle_workflow_license(self, line, stored_workflow):
Expand Down Expand Up @@ -558,7 +562,7 @@ def handle_dataset_info(self, line, hda):
content = "*No Dataset Info Available*"
return (content, True)

def handle_workflow_display(self, line, stored_workflow):
def handle_workflow_display(self, line, stored_workflow, workflow_version: Optional[int]):
# simple markdown
markdown = "---\n"
markdown += f"**Workflow:** {stored_workflow.name}\n\n"
Expand All @@ -567,7 +571,8 @@ def handle_workflow_display(self, line, stored_workflow):
markdown += "|----|----------|\n"
# Pass two should add tool information, labels, etc.. but
# it requires module_injector and such.
for order_index, step in enumerate(stored_workflow.latest_workflow.steps):
workflow = stored_workflow.get_internal_version(workflow_version)
for order_index, step in enumerate(workflow.steps):
annotation = get_item_annotation_str(self.trans.sa_session, self.trans.user, step) or ""
markdown += "|{}|{}|\n".format(step.label or "Step %d" % (order_index + 1), annotation)
markdown += "\n---\n"
Expand All @@ -586,9 +591,10 @@ def handle_workflow_license(self, line, stored_workflow):
markdown = f"Unknown license ({license_id})"
return (f"\n\n{markdown}\n\n", True)

def handle_workflow_image(self, line, stored_workflow):
def handle_workflow_image(self, line, stored_workflow, workflow_version: Optional[int]):
workflow_manager = self.trans.app.workflow_manager
image_data = workflow_manager.get_workflow_svg(self.trans, stored_workflow.latest_workflow, for_embed=True)
workflow = stored_workflow.get_internal_version(workflow_version)
image_data = workflow_manager.get_workflow_svg(self.trans, workflow, for_embed=True)
rval = (self._embed_image("Workflow", "svg+xml", image_data), True)
return rval

Expand Down Expand Up @@ -868,9 +874,12 @@ def _section_remap(container, line):

def _remap(container, line):
if container == "workflow_display":
# TODO: this really should be workflow id not stored workflow id but the API
# it consumes wants the stored id.
return (f"workflow_display(workflow_id={invocation.workflow.stored_workflow.id})\n", False)
stored_workflow_id = invocation.workflow.stored_workflow.id
workflow_version = invocation.workflow.version
return (
f"workflow_display(workflow_id={stored_workflow_id},workflow_checkpoint={workflow_version})\n",
False,
)
if container == "history_link":
return (f"history_link(history_id={invocation.history.id})\n", False)
if container == "invocation_time":
Expand Down Expand Up @@ -948,6 +957,15 @@ def _remap_galaxy_markdown_containers(func, markdown):
return new_markdown


def _parse_directive_argument_value(arg_name: str, line: str) -> Optional[str]:
arg_pattern = re.compile(rf"{arg_name}=\s*{ARG_VAL_CAPTURED_REGEX}\s*")
match = re.search(arg_pattern, line)
if not match:
return None
value = match.group(1)
return value


def _remap_galaxy_markdown_calls(func, markdown):
def _remap_container(container):
matching_line = None
Expand Down
5 changes: 3 additions & 2 deletions lib/galaxy/managers/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,9 +361,10 @@ def check_security(self, trans, has_workflow, check_ownership=True, check_access

return True

def get_workflow_svg_from_id(self, trans, id, for_embed=False) -> bytes:
def get_workflow_svg_from_id(self, trans, id, version=None, for_embed=False) -> bytes:
stored = self.get_stored_accessible_workflow(trans, id)
return self.get_workflow_svg(trans, stored.latest_workflow, for_embed=for_embed)
workflow = stored.get_internal_version(version)
return self.get_workflow_svg(trans, workflow, for_embed=for_embed)

def get_workflow_svg(self, trans, workflow, for_embed=False) -> bytes:
try:
Expand Down
10 changes: 10 additions & 0 deletions lib/galaxy/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7358,6 +7358,12 @@ def get_internal_version(self, version):
raise Exception("Version does not exist")
return list(reversed(self.workflows))[version]

def version_of(self, workflow):
for version, workflow_instance in enumerate(reversed(self.workflows)):
if workflow_instance.id == workflow.id:
return version
raise KeyError("Failed to find a version of target workflow instance in stored workflow.")

def show_in_tool_panel(self, user_id):
sa_session = object_session(self)
stmt = (
Expand Down Expand Up @@ -7552,6 +7558,10 @@ def copy(self, user=None):
copied_workflow.steps = copied_steps
return copied_workflow

@property
def version(self):
return self.stored_workflow.version_of(self)

def log_str(self):
extra = ""
if self.stored_workflow:
Expand Down
10 changes: 8 additions & 2 deletions lib/galaxy/webapps/galaxy/controllers/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,10 +326,16 @@ def annotate_async(self, trans, id, new_annotation=None, **kwargs):

@web.expose
@web.require_login("use Galaxy workflows")
def gen_image(self, trans, id, embed="false", **kwargs):
def gen_image(self, trans, id, embed="false", version="", **kwargs):
embed = util.asbool(embed)
if version:
version_int_or_none = int(version)
else:
version_int_or_none = None
try:
s = trans.app.workflow_manager.get_workflow_svg_from_id(trans, id, for_embed=embed)
s = trans.app.workflow_manager.get_workflow_svg_from_id(
trans, id, version=version_int_or_none, for_embed=embed
)
trans.response.set_content_type("image/svg+xml")
return s
except Exception as e:
Expand Down
2 changes: 1 addition & 1 deletion test/unit/workflows/test_workflow_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def test_workflow_section_expansion():
"""
galaxy_markdown = resolved_markdown(workflow_markdown)
assert "## Workflow\n" in galaxy_markdown
assert "```galaxy\nworkflow_display(workflow_id=342)\n```\n" in galaxy_markdown
assert "```galaxy\nworkflow_display(workflow_id=342,workflow_checkpoint=0)\n```\n" in galaxy_markdown


def test_inputs_section_expansion():
Expand Down

0 comments on commit 7e515a6

Please sign in to comment.