Skip to content

Commit

Permalink
Tool markdown reports.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmchilton committed Oct 24, 2024
1 parent e6d1b30 commit c4c2777
Show file tree
Hide file tree
Showing 28 changed files with 555 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ 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") {
return `${getAppRoot()}dataset/display?dataset_id=${props.historyDatasetId}`;
}
return pathDestination.value?.fileLink;
});
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 @@ -189,8 +189,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
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,23 @@ interface HistoryDatasetAsTableProps {
showColumnHeaders: boolean;
title?: string;
footer?: 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.historyDatasetId}/get_content_as_text`;
if (props.path) {
return `/api/datasets/${props.historyDatasetId}/get_content_as_text?filename=${props.path}`;
} else {
return `/api/datasets/${props.historyDatasetId}/get_content_as_text`;
}
});
const metaUrl = computed(() => {
Expand Down
17 changes: 12 additions & 5 deletions client/src/components/Markdown/Markdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@
Edit
<FontAwesomeIcon icon="edit" />
</b-button>
<h1 class="float-right align-middle mr-2 mt-1 h-md">Galaxy {{ markdownConfig.model_class }}</h1>
<h1 v-if="title" class="float-right align-middle mr-2 mt-1 h-md">
Galaxy {{ markdownConfig.model_class }}
</h1>
<span class="float-left font-weight-light">
<h1 class="text-break align-middle">
Title: {{ markdownConfig.title || markdownConfig.model_class }}
</h1>
<h1 v-if="title" class="text-break align-middle">Title: {{ title }}</h1>
<h2 v-if="workflowVersions" class="text-break align-middle">
Workflow Checkpoint: {{ workflowVersions.version }}
</h2>
</span>
</div>
<b-badge variant="info" class="w-100 rounded mb-3 white-space-normal">
<div class="float-left m-1 text-break">Generated with Galaxy {{ version }} on {{ time }}</div>
<div class="float-right m-1">Identifier: {{ markdownConfig.id }}</div>
<div v-if="showIdentifier" class="float-right m-1">Identifier: {{ markdownConfig.id }}</div>
</b-badge>
<div>
<b-alert v-if="markdownErrors.length > 0" variant="warning" show>
Expand Down Expand Up @@ -119,6 +119,10 @@ export default {
type: String,
default: null,
},
showIdentifier: {
type: Boolean,
default: true,
},
},
data() {
return {
Expand All @@ -137,6 +141,9 @@ export default {
effectiveExportLink() {
return this.enable_beta_markdown_export ? this.exportLink : null;
},
title() {
return this.markdownConfig.title || this.markdownConfig.model_class;
},
time() {
let generateTime = this.markdownConfig.generate_time;
if (generateTime) {
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/Markdown/MarkdownContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ function argToBoolean(args, name, booleanDefault) {
:compact="argToBoolean(args, 'compact', false)"
:show-column-headers="argToBoolean(args, 'show_column_headers', true)"
:title="args.title"
:footer="args.footer" />
:footer="args.footer"
:path="args.path" />
<HistoryDatasetLink v-else-if="name == 'history_dataset_link'" :args="args" />
<HistoryDatasetIndex v-else-if="name == 'history_dataset_index'" :args="args" />
<InvocationTime v-else-if="name == 'invocation_time'" :args="args" :invocations="invocations" />
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 @@ -21,6 +21,7 @@ import ToolLanding from "components/Landing/ToolLanding";
import WorkflowLanding from "components/Landing/WorkflowLanding";
import PageDisplay from "components/PageDisplay/PageDisplay";
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 @@ -239,6 +240,11 @@ export function getRouter(Galaxy) {
src: `/datasets/${route.params.datasetId}/display/?preview=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 @@ -575,6 +575,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" display_in_upload="true"/>
<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 _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("data_manager_json") # for downloading bundles if bundled.
composite_extensions.append("tool_markdown")

if data.extension in composite_extensions:
return self._archive_composite_dataset(trans, data, headers, do_action=kwd.get("do_action", "zip"))
Expand Down
10 changes: 6 additions & 4 deletions lib/galaxy/managers/hdas.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,13 +302,10 @@ def data_conversion_status(self, hda):

# .... data
# TODO: to data provider or Text datatype directly
def text_data(self, hda, preview=True):
def text_data(self, hda, preview=True, filename: Optional[str] = None):
"""
Get data from text file, truncating if necessary.
"""
# 1 MB
MAX_PEEK_SIZE = 1000000

truncated = False
hda_data = None
# For now, cannot get data from non-text datasets.
Expand All @@ -318,6 +315,11 @@ def text_data(self, hda, preview=True):
if not os.path.exists(file_path):
return truncated, hda_data

return self.text_data_truncated(file_path, preview=preview)

def text_data_truncated(self, file_path, preview=True):
# 1 MB
MAX_PEEK_SIZE = 1000000
truncated = preview and os.stat(file_path).st_size > MAX_PEEK_SIZE
with get_fileobj(file_path) as fh:
try:
Expand Down
Loading

0 comments on commit c4c2777

Please sign in to comment.