Skip to content

Implement tool markdown reports. #19054

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
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
87 changes: 86 additions & 1 deletion client/src/api/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,23 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/datasets/{dataset_id}/report": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** Return JSON content Galaxy will use to render Markdown reports */
get: operations["report_api_datasets__dataset_id__report_get"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/datasets/{dataset_id}/storage": {
parameters: {
query?: never;
Expand Down Expand Up @@ -17047,6 +17064,27 @@ export interface components {
*/
values: string;
};
/** ToolReportForDataset */
ToolReportForDataset: {
/**
* Content
* @description Raw text contents of the last page revision (type dependent on content_format).
* @default
*/
content: string | null;
/**
* Galaxy Version
* @description The version of Galaxy this object was generated with.
*/
generate_time?: string | null;
/**
* Galaxy Version
* @description The version of Galaxy this object was generated with.
*/
generate_version?: string | null;
} & {
[key: string]: unknown;
};
/** ToolStep */
ToolStep: {
/**
Expand Down Expand Up @@ -20320,7 +20358,10 @@ export interface operations {
};
get_content_as_text_api_datasets__dataset_id__get_content_as_text_get: {
parameters: {
query?: never;
query?: {
/** @description If non-null, get the specified filename from the extra files for this dataset. */
filename?: string | null;
};
header?: {
/** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */
"run-as"?: string | null;
Expand Down Expand Up @@ -20653,6 +20694,50 @@ export interface operations {
};
};
};
report_api_datasets__dataset_id__report_get: {
parameters: {
query?: never;
header?: {
/** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */
"run-as"?: string | null;
};
path: {
/** @description The ID of the History Dataset. */
dataset_id: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ToolReportForDataset"];
};
};
/** @description Request Error */
"4XX": {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["MessageExceptionModel"];
};
};
/** @description Server Error */
"5XX": {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["MessageExceptionModel"];
};
};
};
};
show_storage_api_datasets__dataset_id__storage_get: {
parameters: {
query?: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ const { datasetPathDestination } = useDatasetPathDestination();

const props = defineProps<Props>();

const pathDestination = computed<PathDestination | null>(() =>
datasetPathDestination.value(props.historyDatasetId, props.path)
);
const pathDestination = computedAsync<PathDestination | null>(async () => {
return await datasetPathDestination.value(props.historyDatasetId, props.path);
}, null);

const imageUrl = computed(() => {
if (props.path === undefined || props.path === "undefined") {
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/Dataset/DatasetIndex/DatasetIndex.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { computedAsync } from "@vueuse/core";
import { computed } from "vue";

import type { DatasetExtraFiles } from "@/api/datasets";
Expand All @@ -13,7 +14,7 @@ const { datasetPathDestination } = useDatasetPathDestination();

const props = defineProps<Props>();

const pathDestination = computed<PathDestination | null>(() =>
const pathDestination = computedAsync<PathDestination | null>(() =>
datasetPathDestination.value(props.historyDatasetId, props.path)
);

Expand Down
3 changes: 2 additions & 1 deletion client/src/components/Dataset/DatasetLink/DatasetLink.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { computedAsync } from "@vueuse/core";
import { computed } from "vue";

import { hasDetails } from "@/api";
Expand All @@ -16,7 +17,7 @@ const { getDataset } = useDatasetStore();

const props = defineProps<Props>();

const pathDestination = computed<PathDestination | null>(() =>
const pathDestination = computedAsync<PathDestination | null>(() =>
datasetPathDestination.value(props.historyDatasetId, props.path)
);

Expand Down
6 changes: 5 additions & 1 deletion client/src/components/History/Content/ContentItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,12 @@ const itemUrls = computed<ItemUrls>(() => {
: null,
};
}
let display = `/datasets/${id}/preview`;
if (props.item.extension == "tool_markdown") {
display = `/datasets/${id}/report`;
}
return {
display: `/datasets/${id}/preview`,
display: display,
edit: `/datasets/${id}/edit`,
showDetails: `/datasets/${id}/details`,
reportError: `/datasets/${id}/error`,
Expand Down
7 changes: 6 additions & 1 deletion client/src/components/Markdown/Markdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const props = defineProps<{
downloadEndpoint: string;
readOnly?: boolean;
exportLink?: string;
showIdentifier?: boolean;
}>();

// Refs and data
Expand Down Expand Up @@ -58,6 +59,10 @@ const updateTime = computed(() => {
return "";
});

const pageTitle = computed(() => {
return props.markdownConfig.title || props.markdownConfig.model_class;
});

// Methods
function initConfig() {
const config = props.markdownConfig;
Expand Down Expand Up @@ -85,7 +90,7 @@ onMounted(() => {
<div class="d-flex flex-column sticky-top bg-white">
<div class="d-flex">
<Heading v-localize h1 separator inline size="md" class="flex-grow-1">
{{ markdownConfig.title || markdownConfig.model_class }}
{{ pageTitle }}
</Heading>
<div>
<StsDownloadButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,23 @@ interface HistoryDatasetAsTableProps {
footer?: string;
showColumnHeaders: boolean;
title?: string;
path?: string;
}

const props = withDefaults(defineProps<HistoryDatasetAsTableProps>(), {
compact: false,
showColumnHeaders: true,
title: undefined,
footer: undefined,
path: undefined,
});

const itemUrl = computed(() => {
return `/api/datasets/${props.datasetId}/get_content_as_text`;
if (props.path) {
return `/api/datasets/${props.datasetId}/get_content_as_text?filename=${props.path}`;
} else {
return `/api/datasets/${props.datasetId}/get_content_as_text`;
}
});

const metaUrl = computed(() => {
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/Markdown/Sections/MarkdownGalaxy.vue
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ watch(
:dataset-id="args.history_dataset_id"
:footer="args.footer"
:show-column-headers="argToBoolean(args, 'show_column_headers', true)"
:title="args.title" />
:title="args.title"
:path="path" />
<HistoryDatasetCollectionDisplay
v-else-if="name == 'history_dataset_collection_display'"
:collection-id="args.history_dataset_collection_id" />
Expand Down
39 changes: 39 additions & 0 deletions client/src/components/Tool/ToolReport.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script setup lang="ts">
import { computed, ref } from "vue";

import { useConfig } from "@/composables/config";
import { urlData } from "@/utils/url";

import Markdown from "@/components/Markdown/Markdown.vue";

interface Props {
datasetId: string;
}

const props = defineProps<Props>();

const dataUrl = computed(() => {
return `/api/datasets/${props.datasetId}/report`;
});

const dataRef = ref<unknown>(null);

const { config, isConfigLoaded } = useConfig(true);

urlData({ url: dataUrl.value }).then((data) => {
dataRef.value = data;
});
</script>

<template>
<div>
<Markdown
v-if="isConfigLoaded && dataRef"
:markdown-config="dataRef"
:enable-beta-markdown-export="config.enable_beta_markdown_export"
download-endpoint="TODO"
:show-identifier="false"
:read-only="true" />
<div v-else>Loading....</div>
</div>
</template>
9 changes: 3 additions & 6 deletions client/src/composables/datasetPathDestination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ export function useDatasetPathDestination() {
const cache = ref<{ [key: string]: PathDestinationMap }>({});

const datasetPathDestination = computed(() => {
return (dataset_id: string, path?: string) => {
return async (dataset_id: string, path?: string) => {
const targetPath = path ?? "undefined";
const pathDestination = cache.value[dataset_id]?.[targetPath];
let pathDestination = cache.value[dataset_id]?.[targetPath];
if (!pathDestination) {
getPathDestination(dataset_id, path);
pathDestination = (await getPathDestination(dataset_id, path)) ?? undefined;
}
return pathDestination ?? null;
};
Expand All @@ -36,7 +36,6 @@ export function useDatasetPathDestination() {
await datasetExtraFilesStore.fetchDatasetExtFilesByDatasetId({ id: dataset_id });
datasetExtraFiles = datasetExtraFilesStore.getDatasetExtraFiles(dataset_id);
}

if (datasetExtraFiles === null) {
return null;
}
Expand Down Expand Up @@ -66,9 +65,7 @@ export function useDatasetPathDestination() {
}
pathDestination.fileLink = getCompositeDatasetLink(dataset_id, datasetEntry.path);
}

set(cache.value, dataset_id, { [path]: pathDestination });

return pathDestination;
}

Expand Down
6 changes: 6 additions & 0 deletions client/src/entry/analysis/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import WorkflowLanding from "components/Landing/WorkflowLanding";
import PageDisplay from "components/PageDisplay/PageDisplay";
import PageForm from "components/PageDisplay/PageForm";
import PageEditor from "components/PageEditor/PageEditor";
import ToolReport from "components/Tool/ToolReport";
import ToolSuccess from "components/Tool/ToolSuccess";
import ToolsList from "components/ToolsList/ToolsList";
import ToolsJson from "components/ToolsView/ToolsSchemaJson/ToolsJson";
Expand Down Expand Up @@ -236,6 +237,11 @@ export function getRouter(Galaxy) {
isPreview: true,
}),
},
{
path: "datasets/:datasetId/report",
component: ToolReport,
props: true,
},
{
// legacy route, potentially used by 3rd parties
path: "datasets/:datasetId/show_params",
Expand Down
28 changes: 28 additions & 0 deletions doc/parse_gx_xsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
from io import StringIO

from lxml import etree
from yaml import safe_load

with open(sys.argv[2]) as f:
xmlschema_doc = etree.parse(f)

markdown_buffer = StringIO()

DIRECTIVES_PATH = "../client/src/components/Markdown/directives.yml"
DIRECTIVES = safe_load(open(DIRECTIVES_PATH))


def main():
"""Entry point for the function that builds Markdown help for the Galaxy XSD."""
Expand Down Expand Up @@ -74,6 +78,7 @@ def _build_tag(tag, hide_attributes):
annotation_el = tag_el.find("{http://www.w3.org/2001/XMLSchema}annotation")
text = annotation_el.find("{http://www.w3.org/2001/XMLSchema}documentation").text
text = _replace_attribute_list(tag, text, attributes)
text = _expand_directives(text)
for line in text.splitlines():
if line.startswith("$assertions"):
assertions_tag = xmlschema_doc.find(
Expand Down Expand Up @@ -127,6 +132,29 @@ def _replace_attribute_list(tag, text, attributes):
return text


def _build_directive_table(line: str) -> str:
_, directives_str = line.split(":", 1)
directives = directives_str.split(",")
attribute_table = StringIO()
attribute_table.write("\n\n")
for directive in directives:
header_level = 3
header_prefix = "#" * header_level
attribute_table.write(f"\n{header_prefix} {directive}\n\n")
directive_info = DIRECTIVES[directive]
if "help" in directive_info:
attribute_table.write(DIRECTIVES[directive]["help"])
return attribute_table.getvalue()


def _expand_directives(text):
for line in text.splitlines():
if not line.startswith("$directive_list:"):
continue
text = text.replace(line, _build_directive_table(line))
return text


def _get_bp_link(annotation_el):
anchor = annotation_el.attrib.get("{http://galaxyproject.org/xml/1.0}best_practices", None)
link = None
Expand Down
1 change: 1 addition & 0 deletions lib/galaxy/config/sample/datatypes_conf.xml.sample
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@
<!-- End RGenetics Datatypes -->
<datatype extension="ipynb" type="galaxy.datatypes.text:Ipynb" display_in_upload="true"/>
<datatype extension="json" type="galaxy.datatypes.text:Json" display_in_upload="true"/>
<datatype extension="tool_markdown" type="galaxy.datatypes.text:Text"/>
<datatype extension="expression.json" type="galaxy.datatypes.text:ExpressionJson" display_in_upload="true"/>
<!-- graph datatypes -->
<datatype extension="xgmml" type="galaxy.datatypes.graph:Xgmml" display_in_upload="true"/>
Expand Down
1 change: 1 addition & 0 deletions lib/galaxy/datatypes/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ def to_archive(self, dataset: DatasetProtocol, name: str = "") -> Iterable:
def _serve_file_download(self, headers, data, trans, to_ext, file_size, **kwd):
composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
composite_extensions.append("html") # for archiving composite datatypes
composite_extensions.append("tool_markdown") # basically should act as an HTML datatype in this capacity
composite_extensions.append("data_manager_json") # for downloading bundles if bundled.
composite_extensions.append("directory") # for downloading directories.
composite_extensions.append("zarr") # for downloading zarr directories.
Expand Down
Loading
Loading