| <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> |
| <tbody> |
| <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> |
| </tbody> |
| </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 |
| v-if=" |
| fullExperiment.jobDetails && fullExperiment.jobDetails.length > 0 |
| " |
| > |
| <th scope="row">Job Description</th> |
| <td> |
| <b-card |
| v-for="jobDetail in fullExperiment.jobDetails" |
| :key="jobDetail.jobId" |
| :header="jobDetail.jobName" |
| > |
| <pre>{{ jobDetail.jobDescription }}</pre> |
| </b-card> |
| </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 |
| v-if=" |
| experiment.userConfigurationData.computationalResourceScheduling |
| .totalPhysicalMemory |
| " |
| > |
| <th scope="row">Total Physical Memory</th> |
| <td> |
| {{ |
| experiment.userConfigurationData.computationalResourceScheduling.totalPhysicalMemory.toLocaleString() |
| }} |
| MB |
| </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" |
| :key="dp.productUri" |
| /> |
| </li> |
| </ul> |
| </td> |
| </tr> |
| <tr> |
| <th scope="row">Outputs</th> |
| <td> |
| <ul> |
| <li |
| v-for="output in experiment.experimentOutputs" |
| :key="output.name" |
| > |
| {{ output.name }}: |
| <template v-if="output.type.isSimpleValueType"> |
| <span class="text-break">{{ output.value }}</span> |
| </template> |
| <data-product-viewer |
| v-for="dp in outputDataProducts[output.name]" |
| v-else-if="output.type.isFileValueType" |
| :data-product="dp" |
| :key="dp.productUri" |
| /> |
| </li> |
| </ul> |
| </td> |
| </tr> |
| <tr> |
| <th scope="row">Experiment Data Dir</th> |
| <td> |
| <div>{{ experimentDataDir }}</div> |
| <b-alert show variant="warning" v-if="archived" class="mt-2"> |
| This directory was archived in |
| <b>{{ experimentArchive.archive_name }}</b> on |
| {{ experimentArchive.created_date }}. |
| </b-alert> |
| </td> |
| </tr> |
| <tr> |
| <th scope="row">Errors</th> |
| <td> |
| <b-card |
| v-for="error in experiment.errors" |
| :key="error.errorId" |
| header="Error" |
| > |
| <p>{{ error.userFriendlyMessage }}</p> |
| <pre class="pre-scrollable">{{ error.actualErrorMessage }}</pre> |
| </b-card> |
| </td> |
| </tr> |
| <template v-if="failedJobs.length > 0"> |
| <tr v-for="job in failedJobs" :key="job.jobId"> |
| <th scope="row">Job Submission Response</th> |
| <td> |
| <b-card v-if="job.stdOut" :header="job.jobName + ' STDOUT'"> |
| <pre class="pre-scrollable">{{ job.stdOut }}</pre> |
| </b-card> |
| <b-card v-if="job.stdErr" :header="job.jobName + ' STDERR'"> |
| <pre class="pre-scrollable">{{ job.stdErr }}</pre> |
| </b-card> |
| </td> |
| </tr> |
| </template> |
| </tbody> |
| </table> |
| <h2 class="h5 mb-3">Process Details</h2> |
| <b-card |
| v-for="process in experiment.processes" |
| :key="process.processId" |
| :header="process.processId" |
| > |
| <b-card |
| v-for="task in process.sortedTasks" |
| :key="task.taskId" |
| :header="task.taskId" |
| > |
| <table class="table table-sm"> |
| <tbody> |
| <tr> |
| <th scope="row">Task Id</th> |
| <td>{{ task.taskId }}</td> |
| </tr> |
| <tr> |
| <th scope="row">Task Type</th> |
| <td>{{ task.taskType.name }}</td> |
| </tr> |
| <tr> |
| <th scope="row">Task Status</th> |
| <td>{{ task.latestStatus.state.name }}</td> |
| </tr> |
| <tr> |
| <th scope="row">Task Status Time</th> |
| <td> |
| <human-date :date="task.latestStatus.timeOfStateChange" /> |
| </td> |
| </tr> |
| <tr> |
| <th scope="row">Task Status Reason</th> |
| <td>{{ task.latestStatus.reason }}</td> |
| </tr> |
| <template v-if="task.taskErrors && task.taskErrors.length > 0"> |
| <tr> |
| <th scope="row">Task Errors</th> |
| <td> |
| <b-card |
| v-for="error in task.taskErrors" |
| :key="error.errorId" |
| :header="error.errorId" |
| > |
| <p>{{ error.userFriendlyMessage }}</p> |
| <pre class="pre-scrollable">{{ |
| error.actualErrorMessage |
| }}</pre> |
| </b-card> |
| </td> |
| </tr> |
| </template> |
| <template v-if="task.jobs && task.jobs.length > 0"> |
| <tr> |
| <th scope="row">Jobs</th> |
| <td> |
| <b-card |
| v-for="job in task.jobs" |
| :key="job.jobId" |
| :header="job.jobName" |
| > |
| <pre>{{ job.jobDescription }}</pre> |
| </b-card> |
| </td> |
| </tr> |
| </template> |
| </tbody> |
| </table> |
| </b-card> |
| |
| <b-card |
| v-for="error in process.processErrors" |
| :key="error.errorId" |
| :header="'Process Error ' + error.errorId" |
| > |
| <p>{{ error.userFriendlyMessage }}</p> |
| <pre class="pre-scrollable">{{ error.actualErrorMessage }}</pre> |
| </b-card> |
| </b-card> |
| </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, |
| "human-date": components.HumanDate, |
| }, |
| data() { |
| return { |
| fullExperiment: null, |
| experimentArchive: 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() |
| ); |
| }, |
| failedJobs() { |
| if (this.fullExperiment && this.fullExperiment.jobDetails) { |
| return this.fullExperiment.jobDetails.filter( |
| (job) => |
| this.experiment.latestStatus.state === |
| models.ExperimentState.FAILED || |
| (job.latestJobStatus && |
| job.latestJobStatus.jobState === models.JobState.FAILED) |
| ); |
| } else { |
| return []; |
| } |
| }, |
| experimentDataDir() { |
| if (this.experiment && this.experiment.userConfigurationData) { |
| return this.experiment.userConfigurationData.experimentDataDir; |
| } else { |
| return null; |
| } |
| }, |
| archived() { |
| return this.experimentArchive?.archived; |
| }, |
| }, |
| created() { |
| services.FullExperimentService.retrieve({ |
| lookup: this.experiment.experimentId, |
| }).then((fullExperiment) => (this.fullExperiment = fullExperiment)); |
| services.ExperimentArchiveService.get({ |
| experimentId: this.experiment.experimentId, |
| }).then((result) => { |
| this.experimentArchive = result; |
| }); |
| }, |
| 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> |
| |
| <style scoped> |
| .table { |
| table-layout: fixed; |
| } |
| .table th[scope="row"] { |
| width: 20%; |
| } |
| </style> |