You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2022/02/17 00:43:25 UTC
[airavata-django-portal] 04/16: AIRAVATA-3557 Display text preview inline of intermediate and final text files
This is an automated email from the ASF dual-hosted git repository.
machristie pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
commit e46aff105a91f70a9fc8921f1861eb26fa15cac2
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Fri Feb 4 14:28:45 2022 -0500
AIRAVATA-3557 Display text preview inline of intermediate and final text files
---
.../output-displays/DefaultOutputDisplay.vue | 150 +++++++++++++++++++--
.../output-displays/OutputDisplayContainer.vue | 55 ++++++--
2 files changed, 186 insertions(+), 19 deletions(-)
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/DefaultOutputDisplay.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/DefaultOutputDisplay.vue
index 383676c..cf3ed8a 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/DefaultOutputDisplay.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/DefaultOutputDisplay.vue
@@ -1,18 +1,43 @@
<template>
<div>
- <div v-for="dp in dataProducts" :key="dp.productUri">
- <img
- v-if="dp.isImage && dp.downloadURL"
- class="image-preview rounded"
- :src="dp.downloadURL"
+ <!-- Show the final data products if available, otherwise, display intermediate outputs -->
+ <template v-if="dataProducts.length > 0">
+ <pre v-if="finalOutputText">
+ {{ finalOutputText }}
+ </pre>
+ <div v-else v-for="dp in dataProducts" :key="dp.productUri">
+ <img
+ v-if="dp.isImage && dp.downloadURL"
+ class="image-preview rounded"
+ :src="dp.downloadURL"
+ />
+ <data-product-viewer :data-product="dp" :mime-type="fileMimeType" />
+ </div>
+ </template>
+
+ <template v-else-if="intermediateOutputDataProduct">
+ <pre v-if="intermediateOutputText">
+ {{ intermediateOutputText }}
+ </pre>
+ <data-product-viewer
+ v-else
+ :data-product="intermediateOutputDataProduct"
+ :mime-type="fileMimeType"
/>
- <data-product-viewer :data-product="dp" :mime-type="fileMimeType" />
- </div>
+ </template>
+ <template v-else-if="intermediateOutputMultipleDataProducts">
+ <div
+ v-for="dp in intermediateOutputMultipleDataProducts"
+ :key="dp.productUri"
+ >
+ <data-product-viewer :data-product="dp" :mime-type="fileMimeType" />
+ </div>
+ </template>
</div>
</template>
<script>
-import { models } from "django-airavata-api";
+import { models, utils } from "django-airavata-api";
import DataProductViewer from "django-airavata-common-ui/js/components/DataProductViewer.vue";
export default {
@@ -30,6 +55,17 @@ export default {
components: {
DataProductViewer,
},
+ data() {
+ return {
+ intermediateOutputText: null,
+ finalOutputText: null,
+ };
+ },
+ async created() {
+ // Check and load intermediate or final output as text if available and applicable
+ this.loadIntermediateOutputText();
+ this.loadFinalOutputText();
+ },
computed: {
fileMimeType() {
if (this.experimentOutput.fileMetadataMimeType) {
@@ -43,6 +79,92 @@ export default {
return null;
}
},
+ intermediateOutputProcessStatusState() {
+ if (
+ this.experimentOutput &&
+ this.experimentOutput.intermediateOutput &&
+ this.experimentOutput.intermediateOutput.processStatus
+ ) {
+ return this.experimentOutput.intermediateOutput.processStatus.state;
+ } else {
+ return null;
+ }
+ },
+ intermediateOutputDataProduct() {
+ if (
+ this.experimentOutput &&
+ this.experimentOutput.intermediateOutput &&
+ this.experimentOutput.intermediateOutput.dataProducts &&
+ this.experimentOutput.intermediateOutput.dataProducts.length === 1
+ ) {
+ return this.experimentOutput.intermediateOutput.dataProducts[0];
+ } else {
+ return null;
+ }
+ },
+ intermediateOutputMultipleDataProducts() {
+ if (
+ this.experimentOutput &&
+ this.experimentOutput.intermediateOutput &&
+ this.experimentOutput.intermediateOutput.dataProducts &&
+ this.experimentOutput.intermediateOutput.dataProducts.length > 1
+ ) {
+ return this.experimentOutput.intermediateOutput.dataProducts;
+ } else {
+ return null;
+ }
+ },
+ intermediateOutputFileSize() {
+ if (this.intermediateOutputDataProduct) {
+ return this.intermediateOutputDataProduct.filesize;
+ } else {
+ return -1;
+ }
+ },
+ },
+ methods: {
+ async loadIntermediateOutputText() {
+ if (
+ this.intermediateOutputDataProduct &&
+ (this.intermediateOutputDataProduct.isText ||
+ this.fileMimeType === "text/plain") &&
+ this.intermediateOutputDataProduct.downloadURL
+ ) {
+ this.intermediateOutputText = await utils.FetchUtils.get(
+ this.intermediateOutputDataProduct.downloadURL,
+ "",
+ {
+ responseType: "text",
+ }
+ );
+ }
+ },
+ async loadFinalOutputText() {
+ if (
+ this.dataProducts &&
+ this.dataProducts.length === 1 &&
+ (this.dataProducts[0].isText || this.fileMimeType === "text/plain") &&
+ this.dataProducts[0].downloadURL
+ ) {
+ this.finalOutputText = await utils.FetchUtils.get(
+ this.dataProducts[0].downloadURL,
+ "",
+ {
+ responseType: "text",
+ }
+ );
+ }
+ },
+ },
+ watch: {
+ intermediateOutputFileSize() {
+ this.loadIntermediateOutputText();
+ },
+ dataProducts(value, oldValue) {
+ if ((!oldValue || oldValue.length === 0) && value && value.length > 0) {
+ this.loadFinalOutputText();
+ }
+ },
},
};
</script>
@@ -52,4 +174,16 @@ export default {
max-width: 100%;
max-height: 120px;
}
+pre {
+ max-height: 340px;
+ overflow: auto;
+ max-width: 100%;
+ margin-bottom: 0;
+ /* background-color: #efefef; */
+ background-color: var(--light);
+ border-style: solid;
+ border-width: 1px;
+ border-color: var(--gray);
+ border-radius: 3px;
+}
</style>
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/OutputDisplayContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/OutputDisplayContainer.vue
index 4f3231c..2390048 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/OutputDisplayContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/OutputDisplayContainer.vue
@@ -24,21 +24,19 @@
:parameters="viewData.interactive"
@input="parametersUpdated"
/>
- <div slot="footer" v-if="dataProducts.length > 0 || isExecuting">
+ <div slot="footer" v-if="dataProducts.length > 0 || isExecuting" class="d-flex justify-content-end align-items-baseline">
<template v-if="isExecuting">
- <span v-if="experimentOutput.intermediateOutput.processStatus"
- >Status of intermediate output fetch:
- {{
- experimentOutput.intermediateOutput.processStatus.state.name
- }}</span
+ <span class="small text-muted mr-2">
+ {{ fetchIntermediateOutputStatusMessage }}</span
>
- <b-btn size="sm" @click="fetchLatest" :disabled="fetchLatestDisabled"
- >Fetch Latest</b-btn
+ <b-btn size="sm" @click="fetchLatest" :disabled="fetchLatestDisabled">
+ <b-spinner small v-if="fetchLatestDisabled"></b-spinner>
+ Fetch Latest</b-btn
>
</template>
<template v-else>
<!-- TODO: support downloading URI_COLLECTIONs as well -->
- <b-btn size="sm" :href="dataProducts[0].downloadURL">Download</b-btn>
+ <b-btn size="sm" :href="dataProducts[0].downloadURL + '&download'">Download</b-btn>
</template>
</div>
</b-card>
@@ -55,6 +53,7 @@ import NotebookOutputDisplay from "./NotebookOutputDisplay";
import InteractiveParametersPanel from "./interactive-parameters/InteractiveParametersPanel";
import OutputViewDataLoader from "./OutputViewDataLoader";
import { mapActions, mapGetters, mapState } from "vuex";
+import ProcessState from "django-airavata-api/static/django_airavata_api/js/models/ProcessState";
export default {
name: "output-viewer-container",
@@ -168,13 +167,47 @@ export default {
canFetchIntermediateOutput() {
return (
this.isExecuting &&
- (!this.experimentOutput.intermediateOutput.processStatus ||
- this.experimentOutput.intermediateOutput.processStatus.isFinished)
+ (!this.experimentOutput.intermediateOutput ||
+ !this.experimentOutput.intermediateOutput.processStatus ||
+ (this.experimentOutput.intermediateOutput.processStatus &&
+ this.experimentOutput.intermediateOutput.processStatus.isFinished))
);
},
fetchLatestDisabled() {
return !this.canFetchIntermediateOutput;
},
+ fetchIntermediateOutputStatusMessage() {
+ let msg = "";
+ if (
+ this.experimentOutput.intermediateOutput &&
+ this.experimentOutput.intermediateOutput.dataProducts &&
+ this.experimentOutput.intermediateOutput.dataProducts.length > 0
+ ) {
+ const timestamp = this.experimentOutput.intermediateOutput
+ .dataProducts[0].lastModifiedTime;
+ msg +=
+ "Latest output fetched on " +
+ timestamp.toLocaleString([], {
+ dateStyle: "medium",
+ timeStyle: "short",
+ }) +
+ ". ";
+ }
+ if (
+ this.experimentOutput.intermediateOutput &&
+ this.experimentOutput.intermediateOutput.processStatus
+ ) {
+ if (
+ this.experimentOutput.intermediateOutput.processStatus.state ===
+ ProcessState.FAILED
+ ) {
+ msg += "Last fetch failed, please try again.";
+ }
+ }
+ return msg;
+ // TODO: show timestamp for data products
+ // TODO: show failure message if failed
+ },
},
methods: {
...mapActions("viewExperiment", ["submitFetchIntermediateOutputs"]),