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 2019/06/17 14:23:23 UTC
[airavata-django-portal] 10/11: AIRAVATA-2990 Experiment details
tab view
This is an automated email from the ASF dual-hosted git repository.
machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
commit d447ed96710d80838f19cafb2113861d268baa17
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Thu Jun 13 09:56:47 2019 -0400
AIRAVATA-2990 Experiment details tab view
---
.../statistics/ExperimentDetailsView.vue | 258 +++++++++++++++++++++
.../statistics/ExperimentStatisticsContainer.vue | 35 ++-
.../js/components/experiment/ExperimentSummary.vue | 3 +-
.../experiment/input-editors/FileInputEditor.vue | 3 +-
.../output-displays/DownloadOutputDisplay.vue | 3 +-
.../output-displays/OutputDisplayContainer.vue | 3 +-
.../common/js/components}/DataProductViewer.vue | 0
django_airavata/static/common/js/index.js | 2 +
8 files changed, 296 insertions(+), 11 deletions(-)
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentDetailsView.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentDetailsView.vue
new file mode 100644
index 0000000..6cf4ad6
--- /dev/null
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentDetailsView.vue
@@ -0,0 +1,258 @@
+<template>
+ <div>
+ <table
+ class="table"
+ v-if="fullExperiment"
+ >
+ <tbody>
+ <tr>
+ <th scope="row">Name</th>
+ <td>
+ <div :title="experiment.experimentId">{{ experiment.experimentName }}</div>
+ <small class="text-muted">
+ ID: {{ experiment.experimentId }}
+ (<clipboard-copy-link
+ :text="experiment.experimentId"
+ :link-classes="['text-reset']"
+ >
+ copy
+ <span slot="icon"></span>
+ <span slot="tooltip">Copied ID!</span>
+ </clipboard-copy-link>)
+ </small>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Description</th>
+ <td>{{ experiment.description }}</td>
+ </tr>
+ <tr>
+ <th scope="row">Project</th>
+ <td v-if="fullExperiment.project">{{ fullExperiment.projectName }}</td>
+ <td v-else>
+ <em>You don't have access to this project.</em>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Owner</th>
+ <td>{{ experiment.userName }}</td>
+ </tr>
+ <tr>
+ <th scope="row">Application</th>
+ <td v-if="fullExperiment.applicationName">{{ fullExperiment.applicationName }}</td>
+ <td
+ v-else
+ class="font-italic text-muted"
+ >Unable to load interface {{ fullExperiment.experiment.executionId }}</td>
+ </tr>
+ <tr>
+ <th scope="row">Compute Resource</th>
+ <td v-if="fullExperiment.computeHostName">{{ fullExperiment.computeHostName }}</td>
+ <td
+ v-else
+ class="font-italic text-muted"
+ >Unable to load compute resource {{ fullExperiment.resourceHostId }}</td>
+ </tr>
+ <tr>
+ <th scope="row">Experiment Status</th>
+ <td>
+ <template v-if="fullExperiment.experiment.isProgressing">
+ <i class="fa fa-sync-alt fa-spin"></i>
+ <span class="sr-only">Progressing...</span>
+ </template>
+ {{ fullExperiment.experimentStatusName }}
+ </td>
+ </tr>
+ <tr v-if="fullExperiment.jobDetails && fullExperiment.jobDetails.length > 0">
+ <th scope="row">Job</th>
+ <td>
+ <table class="table">
+ <thead>
+ <th>Name</th>
+ <th>ID</th>
+ <th>Status</th>
+ <th>Creation Time</th>
+ </thead>
+ <tr
+ v-for="(jobDetail, index) in fullExperiment.jobDetails"
+ :key="jobDetail.jobId"
+ >
+ <td>{{ jobDetail.jobName }}</td>
+ <td>{{ jobDetail.jobId }}</td>
+ <td>{{ jobDetail.jobStatusStateName }}</td>
+ <td>
+ <span :title="jobDetail.creationTime.toString()">{{ jobCreationTimes[index] }}</span>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Notification List</th>
+ <td>{{ experiment.emailAddresses
+ ? experiment.emailAddresses.join(", ")
+ : '' }}</td>
+ </tr>
+ <tr v-if="fullExperiment.jobDetails && fullExperiment.jobDetails.length > 0">
+ <th scope="row">Working Dir</th>
+ <td>
+ <div
+ v-for="jobDetail in fullExperiment.jobDetails"
+ :key="jobDetail.jobId"
+ >
+ {{ jobDetail.jobName }}: {{ jobDetail.workingDir }}
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Creation Time</th>
+ <td>
+ <span :title="experiment.creationTime.toString()">{{ creationTime }}</span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Last Modified Time</th>
+ <td>
+ <span :title="fullExperiment.experimentStatus.timeOfStateChange.toString()">{{ lastModifiedTime }}</span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Wall Time Limit</th>
+ <td>{{ experiment.userConfigurationData.computationalResourceScheduling.wallTimeLimit }} minutes</td>
+ </tr>
+ <tr>
+ <th scope="row">CPU Count</th>
+ <td>{{ experiment.userConfigurationData.computationalResourceScheduling.totalCPUCount }}</td>
+ </tr>
+ <tr>
+ <th scope="row">Node Count</th>
+ <td>{{ experiment.userConfigurationData.computationalResourceScheduling.nodeCount }}</td>
+ </tr>
+ <tr>
+ <th scope="row">Queue</th>
+ <td>{{ experiment.userConfigurationData.computationalResourceScheduling.queueName }}</td>
+ </tr>
+ <tr>
+ <th scope="row">Inputs</th>
+ <td>
+ <ul>
+ <li
+ v-for="input in experiment.experimentInputs"
+ :key="input.name"
+ >
+ {{ input.name }}:
+ <template v-if="input.type.isSimpleValueType">
+ <span class="text-break">{{ input.value }}</span>
+ </template>
+ <data-product-viewer
+ v-for="dp in inputDataProducts[input.name]"
+ v-else-if="input.type.isFileValueType"
+ :data-product="dp"
+ :input-file="true"
+ class="data-product"
+ :key="dp.productUri"
+ />
+ </li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <!-- TODO -->
+ <th scope="row">Errors</th>
+ <td></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</template>
+
+<script>
+import { models, services } from "django-airavata-api";
+import { components } from "django-airavata-common-ui";
+
+import moment from "moment";
+
+export default {
+ name: "experiment-details-view",
+ props: {
+ experiment: {
+ type: models.Experiment,
+ required: true
+ }
+ },
+ components: {
+ "clipboard-copy-link": components.ClipboardCopyLink,
+ "data-product-viewer": components.DataProductViewer
+ },
+ data() {
+ return {
+ fullExperiment: null
+ };
+ },
+ computed: {
+ inputDataProducts() {
+ const result = {};
+ if (this.fullExperiment && this.fullExperiment.inputDataProducts) {
+ this.fullExperiment.experiment.experimentInputs.forEach(input => {
+ result[input.name] = this.getDataProducts(
+ input,
+ this.fullExperiment.inputDataProducts
+ );
+ });
+ }
+ return result;
+ },
+ outputDataProducts() {
+ const result = {};
+ if (this.fullExperiment && this.fullExperiment.outputDataProducts) {
+ this.fullExperiment.experiment.experimentOutputs.forEach(output => {
+ result[output.name] = this.getDataProducts(
+ output,
+ this.fullExperiment.outputDataProducts
+ );
+ });
+ }
+ return result;
+ },
+ creationTime: function() {
+ return moment(this.fullExperiment.experiment.creationTime).fromNow();
+ },
+ lastModifiedTime: function() {
+ return moment(
+ this.fullExperiment.experimentStatus.timeOfStateChange
+ ).fromNow();
+ },
+ jobCreationTimes: function() {
+ return this.fullExperiment.jobDetails.map(jobDetail =>
+ moment(jobDetail.creationTime).fromNow()
+ );
+ }
+ },
+ created() {
+ services.FullExperimentService.retrieve({
+ lookup: this.experiment.experimentId
+ }).then(fullExperiment => (this.fullExperiment = fullExperiment));
+ },
+ methods: {
+ getDataProducts(io, collection) {
+ if (!io.value || !collection) {
+ return [];
+ }
+ let dataProducts = null;
+ if (io.type === models.DataType.URI_COLLECTION) {
+ const dataProductURIs = io.value.split(",");
+ dataProducts = dataProductURIs.map(uri =>
+ collection.find(dp => dp.productUri === uri)
+ );
+ } else {
+ const dataProductURI = io.value;
+ dataProducts = collection.filter(
+ dp => dp.productUri === dataProductURI
+ );
+ }
+ return dataProducts ? dataProducts.filter(dp => (dp ? true : false)) : [];
+ }
+ }
+};
+</script>
+
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
index cf784ae..1030d49 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
@@ -228,8 +228,23 @@
>
<experiment-status-badge :status-name="data.value.name" />
</template>
+ <template
+ slot="actions"
+ slot-scope="data"
+ >
+ <b-link @click="showExperimentDetails(data.item.experimentId)">
+ View Details
+ <i
+ class="far fa-chart-bar"
+ aria-hidden="true"
+ ></i>
+ </b-link>
+ </template>
</b-table>
</b-tab>
+ <b-tab v-for="experimentDetail in experimentDetails" :key="experimentDetail.experimentId" :title="experimentDetail.experimentName">
+ <experiment-details-view :experiment="experimentDetail"/>
+ </b-tab>
</b-tabs>
</b-card>
</div>
@@ -240,6 +255,7 @@
import { models, services, utils } from "django-airavata-api";
import { components } from "django-airavata-common-ui";
import ExperimentStatisticsCard from "./ExperimentStatisticsCard";
+import ExperimentDetailsView from "./ExperimentDetailsView";
import moment from "moment";
@@ -267,7 +283,8 @@ export default {
hostnameFilterEnabled: false,
hostnameFilter: null,
appInterfaces: null,
- computeResourceNames: null
+ computeResourceNames: null,
+ experimentDetails: []
};
},
created() {
@@ -276,6 +293,7 @@ export default {
this.loadComputeResources();
},
components: {
+ ExperimentDetailsView,
ExperimentStatisticsCard,
"application-name": components.ApplicationName,
"compute-resource-name": components.ComputeResourceName,
@@ -395,9 +413,13 @@ export default {
return "Created Experiments";
} else if (this.selectedExperimentSummariesKey === "runningExperiments") {
return "Running Experiments";
- } else if (this.selectedExperimentSummariesKey === "completedExperiments") {
+ } else if (
+ this.selectedExperimentSummariesKey === "completedExperiments"
+ ) {
return "Completed Experiments";
- } else if (this.selectedExperimentSummariesKey === "cancelledExperiments") {
+ } else if (
+ this.selectedExperimentSummariesKey === "cancelledExperiments"
+ ) {
return "Cancelled Experiments";
} else if (this.selectedExperimentSummariesKey === "failedExperiments") {
return "Failed Experiments";
@@ -476,6 +498,13 @@ export default {
this.hostnameFilter = null;
this.hostnameFilterEnabled = false;
this.loadStatistics();
+ },
+ showExperimentDetails(experimentId) {
+ // TODO: if experiment details already loaded, select its tab
+ // TODO: maybe don't need to load the experiment first since ExperimentDetailsView will load FullExperiment?
+ services.ExperimentService.retrieve({
+ lookup: experimentId
+ }).then(exp => this.experimentDetails.push(exp));
}
}
};
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ExperimentSummary.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ExperimentSummary.vue
index 0339172..97b1336 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ExperimentSummary.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ExperimentSummary.vue
@@ -222,7 +222,6 @@
<script>
import { models, services } from "django-airavata-api";
import { components } from "django-airavata-common-ui";
-import DataProductViewer from "./DataProductViewer.vue";
import OutputDisplayContainer from "./output-displays/OutputDisplayContainer";
import urls from "../../utils/urls";
@@ -246,7 +245,7 @@ export default {
};
},
components: {
- DataProductViewer,
+ "data-product-viewer": components.DataProductViewer,
"clipboard-copy-link": components.ClipboardCopyLink,
"share-button": components.ShareButton,
OutputDisplayContainer
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/FileInputEditor.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/FileInputEditor.vue
index 2c65b9f..c1daef4 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/FileInputEditor.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/FileInputEditor.vue
@@ -56,7 +56,6 @@
<script>
import { models, services, utils } from "django-airavata-api";
import { InputEditorMixin } from "django-airavata-workspace-plugin-api";
-import DataProductViewer from "../DataProductViewer.vue";
import { components } from "django-airavata-common-ui";
import UserStorageFileSelectionContainer from "../../storage/UserStorageFileSelectionContainer";
@@ -64,7 +63,7 @@ export default {
name: "file-input-editor",
mixins: [InputEditorMixin],
components: {
- DataProductViewer,
+ "data-product-viewer": components.DataProductViewer,
"delete-link": components.DeleteLink,
UserStorageFileSelectionContainer
},
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/DownloadOutputDisplay.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/DownloadOutputDisplay.vue
index 51657c5..603b35a 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/DownloadOutputDisplay.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/DownloadOutputDisplay.vue
@@ -7,7 +7,6 @@
<script>
import { models } from "django-airavata-api"
-import DataProductViewer from "../DataProductViewer.vue";
export default {
name: "download-output-viewer",
@@ -25,7 +24,7 @@ export default {
}
},
components: {
- DataProductViewer
+ "data-product-viewer": components.DataProductViewer
}
}
</script>
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 0b2d400..ed8bbe0 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,7 +24,6 @@
import { models } from "django-airavata-api";
import DownloadOutputDisplay from "./DownloadOutputDisplay";
import LinkDisplay from "./LinkDisplay";
-import DataProductViewer from "../DataProductViewer";
export default {
name: "output-viewer-container",
@@ -44,7 +43,7 @@ export default {
}
},
components: {
- DataProductViewer,
+ "data-product-viewer": components.DataProductViewer,
DownloadOutputDisplay,
LinkDisplay
},
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/DataProductViewer.vue b/django_airavata/static/common/js/components/DataProductViewer.vue
similarity index 100%
rename from django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/DataProductViewer.vue
rename to django_airavata/static/common/js/components/DataProductViewer.vue
diff --git a/django_airavata/static/common/js/index.js b/django_airavata/static/common/js/index.js
index e7eda5d..48a040c 100644
--- a/django_airavata/static/common/js/index.js
+++ b/django_airavata/static/common/js/index.js
@@ -5,6 +5,7 @@ import ClipboardCopyButton from "./components/ClipboardCopyButton.vue";
import ClipboardCopyLink from "./components/ClipboardCopyLink.vue";
import ComputeResourceName from "./components/ComputeResourceName";
import ConfirmationDialog from "./components/ConfirmationDialog.vue";
+import DataProductViewer from "./components/DataProductViewer";
import DeleteButton from "./components/DeleteButton.vue";
import DeleteLink from "./components/DeleteLink.vue";
import ExperimentStatusBadge from "./components/ExperimentStatusBadge";
@@ -40,6 +41,7 @@ const components = {
ClipboardCopyLink,
ComputeResourceName,
ConfirmationDialog,
+ DataProductViewer,
DeleteButton,
DeleteLink,
ExperimentStatusBadge,