Merge pull request #12387 from guerler/add_tables_to_pages

Display Tabular datasets as sortable tables, and PDF and HTML in embedded frames in Pages
This commit is contained in:
Dannon
2021-08-31 15:45:57 -04:00
committed by GitHub
12 changed files with 251 additions and 184 deletions
@@ -1,54 +1,52 @@
<template>
<div class="w-100 p-2">
<b-card body-class="p-0">
<b-card-header>
<span class="float-right">
<b-button
:href="downloadUrl"
variant="link"
size="sm"
role="button"
title="Download Collection"
type="button"
class="py-0 px-1"
v-b-tooltip.hover
>
<span class="fa fa-download" />
</b-button>
<CurrentUser v-slot="{ user }">
<UserHistories v-if="user" :user="user" v-slot="{ currentHistoryId }">
<b-button
v-if="currentHistoryId"
@click="onCopyCollection(currentHistoryId)"
href="#"
role="button"
variant="link"
title="Import Collection"
type="button"
class="py-0 px-1"
v-b-tooltip.hover
>
<span class="fa fa-file-import" />
</b-button>
</UserHistories>
</CurrentUser>
</span>
<span>
<span>Dataset Collection:</span>
<span class="font-weight-light">{{ collectionName }}</span>
</span>
</b-card-header>
<b-card-body>
<LoadingSpan v-if="loading" message="Loading Collection" />
<div v-else class="content-height">
<b-alert v-if="!!messageText" :variant="messageVariant" show>
{{ messageText }}
</b-alert>
<CollectionTree :node="itemContent" :skip-head="true" />
</div>
</b-card-body>
</b-card>
</div>
<b-card body-class="p-0">
<b-card-header>
<span class="float-right">
<b-button
:href="downloadUrl"
variant="link"
size="sm"
role="button"
title="Download Collection"
type="button"
class="py-0 px-1"
v-b-tooltip.hover
>
<span class="fa fa-download" />
</b-button>
<CurrentUser v-slot="{ user }">
<UserHistories v-if="user" :user="user" v-slot="{ currentHistoryId }">
<b-button
v-if="currentHistoryId"
@click="onCopyCollection(currentHistoryId)"
href="#"
role="button"
variant="link"
title="Import Collection"
type="button"
class="py-0 px-1"
v-b-tooltip.hover
>
<span class="fa fa-file-import" />
</b-button>
</UserHistories>
</CurrentUser>
</span>
<span>
<span>Dataset Collection:</span>
<span class="font-weight-light">{{ collectionName }}</span>
</span>
</b-card-header>
<b-card-body>
<LoadingSpan v-if="loading" message="Loading Collection" />
<div v-else class="content-height">
<b-alert v-if="!!messageText" :variant="messageVariant" show>
{{ messageText }}
</b-alert>
<CollectionTree :node="itemContent" :skip-head="true" />
</div>
</b-card-body>
</b-card>
</template>
<script>
@@ -1,58 +1,91 @@
<template>
<div class="w-100 p-2">
<b-card body-class="p-0">
<b-card-header v-if="!embedded">
<span class="float-right">
<b-button
:href="downloadUrl"
variant="link"
size="sm"
role="button"
title="Download Dataset"
type="button"
class="py-0 px-1"
v-b-tooltip.hover
>
<span class="fa fa-download" />
</b-button>
<b-button
:href="importUrl"
role="button"
variant="link"
title="Import Dataset"
type="button"
class="py-0 px-1"
v-b-tooltip.hover
>
<span class="fa fa-file-import" />
</b-button>
</span>
<span>
<span>Dataset:</span>
<span class="font-weight-light">{{ datasetName }}</span>
</span>
</b-card-header>
<b-card-body>
<b-card body-class="p-0">
<b-card-header v-if="!embedded">
<span class="float-right">
<b-button
:href="downloadUrl"
variant="link"
size="sm"
role="button"
title="Download Dataset"
type="button"
class="py-0 px-1"
v-b-tooltip.hover
>
<span class="fa fa-download" />
</b-button>
<b-button
:href="importUrl"
role="button"
variant="link"
title="Import Dataset"
type="button"
class="py-0 px-1"
v-b-tooltip.hover
>
<span class="fa fa-file-import" />
</b-button>
</span>
<span>
<span>Dataset:</span>
<span class="font-weight-light">{{ datasetName }}</span>
</span>
</b-card-header>
<b-card-body>
<UrlDataProvider :url="itemUrl" v-slot="{ result: itemContent, loading, error }">
<LoadingSpan v-if="loading" message="Loading Dataset" />
<div v-else-if="error">{{ error }}</div>
<div v-else class="embedded-dataset content-height">
<pre v-if="itemContent.item_data">
<code class="text-normalwrap">{{ itemContent.item_data }}</code>
</pre>
<div v-else>No content found.</div>
<b-link v-if="itemContent.truncated" :href="itemContent.item_url"> Show More... </b-link>
<UrlDataProvider :url="datatypesUrl" v-slot="{ result: datatypesModel, loading: datatypesLoading }">
<LoadingSpan v-if="datatypesLoading" message="Loading Datatypes" />
<div v-else>
<b-embed
v-if="isSubTypeOfAny(datasetType, ['pdf', 'html'], datatypesModel)"
type="iframe"
aspect="16by9"
:src="displayUrl"
/>
<div v-else-if="itemContent.item_data">
<div v-if="isSubTypeOfAny(datasetType, ['tabular'], datatypesModel)">
<UrlDataProvider
:url="metaUrl"
v-slot="{ result: metaData, loading: metaLoading, error: metaError }"
>
<LoadingSpan v-if="metaLoading" message="Loading Metadata" />
<div v-else-if="metaError">{{ metaError }}</div>
<b-table
v-else
striped
hover
:fields="getFields(metaData)"
:items="getItems(itemContent.item_data, metaData)"
/>
</UrlDataProvider>
</div>
<pre v-else>
<code class="text-normalwrap">{{ itemContent.item_data }}</code>
</pre>
</div>
<div v-else>No content found.</div>
<b-link v-if="itemContent.truncated" :href="itemContent.item_url"> Show More... </b-link>
</div>
</UrlDataProvider>
</div>
</b-card-body>
</b-card>
</div>
</UrlDataProvider>
</b-card-body>
</b-card>
</template>
<script>
import { getAppRoot } from "onload/loadConfig";
import LoadingSpan from "components/LoadingSpan";
import axios from "axios";
import { UrlDataProvider } from "components/providers/UrlDataProvider";
import { DatatypesMapperModel } from "components/Datatypes/model";
export default {
components: {
LoadingSpan,
UrlDataProvider,
},
props: {
args: {
@@ -68,47 +101,81 @@ export default {
default: false,
},
},
data() {
return {
itemContent: null,
loading: true,
};
},
created() {
this.getContent().then((data) => {
this.itemContent = data;
this.loading = false;
});
},
computed: {
datasetType() {
const dataset = this.datasets[this.args.history_dataset_id];
return dataset.ext;
},
datasetName() {
const dataset = this.datasets[this.args.history_dataset_id];
return dataset && dataset.name;
},
datatypesUrl() {
return "api/datatypes/types_and_mapping";
},
downloadUrl() {
return `${getAppRoot()}dataset/display?dataset_id=${this.args.history_dataset_id}`;
},
displayUrl() {
return `${getAppRoot()}datasets/${this.args.history_dataset_id}/display`;
},
importUrl() {
return `${getAppRoot()}dataset/imp?dataset_id=${this.args.history_dataset_id}`;
},
itemUrl() {
return `${getAppRoot()}api/datasets/${this.args.history_dataset_id}/get_content_as_text`;
return `api/datasets/${this.args.history_dataset_id}/get_content_as_text`;
},
metaUrl() {
return `api/datasets/${this.args.history_dataset_id}`;
},
},
methods: {
async getContent() {
try {
const response = await axios.get(this.itemUrl);
return response.data;
} catch (e) {
return `Failed to retrieve content. ${e}`;
isSubTypeOfAny(child, parents, datatypesModel) {
const datatypesMapper = new DatatypesMapperModel(datatypesModel);
return datatypesMapper.isSubTypeOfAny(child, parents);
},
getFields(metaData) {
const fields = [];
const columnNames = metaData.metadata_column_names || [];
const columnCount = metaData.metadata_columns;
for (let i = 0; i < columnCount; i++) {
fields.push({
key: `${i}`,
label: columnNames[i] || i,
sortable: true,
});
}
return fields;
},
getItems(textData, metaData) {
const tableData = [];
const delimiter = metaData.metadata_delimiter || "\t";
const comments = metaData.metadata_comment_lines || 0;
const lines = textData.split("\n");
lines.forEach((line, i) => {
if (i >= comments) {
const tabs = line.split(delimiter);
const rowData = {};
let hasData = false;
tabs.forEach((cellData, j) => {
const cellDataTrimmed = cellData.trim();
if (cellDataTrimmed) {
hasData = true;
}
rowData[j] = cellDataTrimmed;
});
if (hasData) {
tableData.push(rowData);
}
}
});
return tableData;
},
},
};
</script>
<style scoped>
.content-height {
max-height: 15rem;
max-height: 20rem;
}
</style>
@@ -1,9 +1,7 @@
<template>
<div class="w-100 p-2">
<b-card nobody>
<JobMetrics class="job-metrics" :job-id="args.job_id" />
</b-card>
</div>
<b-card nobody>
<JobMetrics class="job-metrics" :job-id="args.job_id" />
</b-card>
</template>
<script>
@@ -1,9 +1,7 @@
<template>
<div class="w-100 p-2">
<b-card nobody>
<JobParameters class="job-parameters" :job-id="args.job_id" :param="args.param" :include-title="false" />
</b-card>
</div>
<b-card nobody>
<JobParameters class="job-parameters" :job-id="args.job_id" :param="args.param" :include-title="false" />
</b-card>
</template>
<script>
@@ -1,11 +1,9 @@
<template>
<div class="w-100 p-2">
<b-card nobody class="content-height">
<div :class="name" :job_id="args.job_id">
<pre><code class="text-normalwrap">{{ jobContent }}</code></pre>
</div>
</b-card>
</div>
<b-card nobody class="content-height">
<div :class="name" :job_id="args.job_id">
<pre><code class="text-normalwrap">{{ jobContent }}</code></pre>
</div>
</b-card>
</template>
<script>
@@ -1,11 +1,9 @@
<template>
<div class="w-100 p-2">
<b-card body-class="embed-responsive embed-responsive-4by3">
<LoadingSpan v-if="loading" class="m-2" message="Loading Visualization" />
<div v-else-if="error" class="m-2">{{ error }}</div>
<iframe v-else class="embed-responsive-item" :src="visualizationUrl" />
</b-card>
</div>
<b-card body-class="embed-responsive embed-responsive-4by3">
<LoadingSpan v-if="loading" class="m-2" message="Loading Visualization" />
<div v-else-if="error" class="m-2">{{ error }}</div>
<iframe v-else class="embed-responsive-item" :src="visualizationUrl" />
</b-card>
</template>
<script>
@@ -1,48 +1,46 @@
<template>
<div class="w-100 p-2">
<b-card body-class="p-0">
<b-card-header v-if="!embedded">
<span class="float-right">
<b-button
:href="downloadUrl"
variant="link"
size="sm"
role="button"
title="Download Workflow"
type="button"
class="py-0 px-1"
v-b-tooltip.hover
>
<span class="fa fa-download" />
</b-button>
<b-button
:href="importUrl"
role="button"
variant="link"
title="Import Workflow"
type="button"
class="py-0 px-1"
v-b-tooltip.hover
>
<span class="fa fa-file-import" />
</b-button>
</span>
<span>
<span>Workflow:</span>
<span class="font-weight-light">{{ workflowName }}</span>
</span>
</b-card-header>
<b-card-body>
<LoadingSpan v-if="loading" message="Loading Workflow" />
<div v-else class="content-height">
<div v-for="step in itemContent.steps" :key="step.order_index" class="mb-2">
<div>Step {{ step.order_index + 1 }}: {{ step.label }}</div>
<WorkflowTree :input="step" :skip-head="true" />
</div>
<b-card body-class="p-0">
<b-card-header v-if="!embedded">
<span class="float-right">
<b-button
:href="downloadUrl"
variant="link"
size="sm"
role="button"
title="Download Workflow"
type="button"
class="py-0 px-1"
v-b-tooltip.hover
>
<span class="fa fa-download" />
</b-button>
<b-button
:href="importUrl"
role="button"
variant="link"
title="Import Workflow"
type="button"
class="py-0 px-1"
v-b-tooltip.hover
>
<span class="fa fa-file-import" />
</b-button>
</span>
<span>
<span>Workflow:</span>
<span class="font-weight-light">{{ workflowName }}</span>
</span>
</b-card-header>
<b-card-body>
<LoadingSpan v-if="loading" message="Loading Workflow" />
<div v-else class="content-height">
<div v-for="step in itemContent.steps" :key="step.order_index" class="mb-2">
<div>Step {{ step.order_index + 1 }}: {{ step.label }}</div>
<WorkflowTree :input="step" :skip-head="true" />
</div>
</b-card-body>
</b-card>
</div>
</div>
</b-card-body>
</b-card>
</template>
<script>
+1 -1
View File
@@ -44,7 +44,7 @@
</div>
</b-alert>
</div>
<div v-for="(obj, index) in markdownObjects" :key="index">
<div v-for="(obj, index) in markdownObjects" :key="index" class="markdown-components">
<p v-if="obj.name == 'default'" v-html="obj.content" class="text-justify m-2" />
<div v-else-if="obj.name == 'generate_galaxy_version'" class="galaxy-version">
<pre><code>{{ getVersion }}</code></pre>
@@ -21,6 +21,7 @@ export const SingleQueryProvider = (lookup) => {
data() {
return {
result: undefined,
error: undefined,
};
},
computed: {
@@ -47,6 +48,8 @@ export const SingleQueryProvider = (lookup) => {
this.result = result;
},
(err) => {
this.result = {};
this.error = err;
this.$emit("error", err);
}
);
@@ -55,6 +58,7 @@ export const SingleQueryProvider = (lookup) => {
return this.$scopedSlots.default({
loading: this.loading,
result: this.result,
error: this.error,
});
},
};
+7
View File
@@ -12,3 +12,10 @@
flex: 1;
}
}
.markdown-wrapper {
@extend .px-4;
.markdown-components {
@extend .py-2;
}
}
+3 -2
View File
@@ -270,10 +270,11 @@ class ReadyForExportMarkdownDirectiveHandler(GalaxyInternalMarkdownDirectiveHand
self.ensure_rendering_data_for("history_datasets", obj)[key] = val or default_val
def handle_dataset_display(self, line, hda):
self.extend_history_dataset_rendering_data(hda, "name", hda.name, "")
self.handle_dataset_name(line, hda)
self.handle_dataset_type(line, hda)
def handle_dataset_embedded(self, line, hda):
self.extend_history_dataset_rendering_data(hda, "name", hda.name, "")
self.handle_dataset_name(line, hda)
def handle_dataset_peek(self, line, hda):
self.extend_history_dataset_rendering_data(hda, "peek", hda.peek, "*No Dataset Peek Available*")
@@ -100,7 +100,7 @@
<div class="unified-panel-body">
<div style="overflow: auto; height: 100%;">
<div class="page-body">
<div class="page-body p-3">
<div style="float: right">
${self.render_item_links( item )}
</div>