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:22 UTC
[airavata-django-portal] 01/16: AIRAVATA-3557 Refactored experiment summary to use vuex store
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 07655c44fafa8f7d9a859cb57770f80266940dde
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Thu Jan 13 15:38:08 2022 -0500
AIRAVATA-3557 Refactored experiment summary to use vuex store
---
django_airavata/apps/api/serializers.py | 1 +
.../js/components/experiment/ExperimentSummary.vue | 108 +++++-------------
.../js/containers/ViewExperimentContainer.vue | 52 ---------
.../js/entry-view-experiment.js | 71 +++++++-----
.../django_airavata_workspace/js/store/index.js | 14 +++
.../js/store/modules/view-experiment.js | 124 +++++++++++++++++++++
6 files changed, 211 insertions(+), 159 deletions(-)
diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index 6eb8034..4c6297e 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -517,6 +517,7 @@ class FullExperimentSerializer(serializers.Serializer):
lookup_field='experimentId',
lookup_url_kwarg='experiment_id')
experiment = ExperimentSerializer()
+ experimentId = serializers.CharField(read_only=True)
outputDataProducts = DataProductSerializer(many=True, read_only=True)
inputDataProducts = DataProductSerializer(many=True, read_only=True)
applicationModule = ApplicationModuleSerializer(read_only=True)
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 2813e55..c622480 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
@@ -1,5 +1,5 @@
<template>
- <div>
+ <div v-if="localFullExperiment">
<div class="row">
<div class="col-auto mr-auto">
<h1 class="h4 mb-4">
@@ -12,15 +12,15 @@
Edit
<i class="fa fa-edit" aria-hidden="true"></i>
</b-link>
- <b-link v-if="isLaunchable" class="btn btn-primary" @click="launch">
+ <b-link v-if="isLaunchable" class="btn btn-primary" @click="onLaunch">
Launch
<i class="fa fa-running" aria-hidden="true"></i>
</b-link>
- <b-btn v-if="isClonable" variant="primary" @click="clone">
+ <b-btn v-if="isClonable" variant="primary" @click="onClone">
Clone
<i class="fa fa-copy" aria-hidden="true"></i>
</b-btn>
- <b-btn v-if="isCancelable" variant="primary" @click="cancel">
+ <b-btn v-if="isCancelable" variant="primary" @click="onCancel">
Cancel
<i class="fa fa-window-close" aria-hidden="true"></i>
</b-btn>
@@ -293,7 +293,7 @@
</template>
<script>
-import { models, services } from "django-airavata-api";
+import { models } from "django-airavata-api";
import { components, notifications } from "django-airavata-common-ui";
import OutputDisplayContainer from "./output-displays/OutputDisplayContainer";
import urls from "../../utils/urls";
@@ -301,24 +301,10 @@ import urls from "../../utils/urls";
import moment from "moment";
import ExperimentStorageViewContainer from "../storage/ExperimentStorageViewContainer.vue";
import DataProductViewer from "django-airavata-common-ui/js/components/DataProductViewer.vue";
+import { mapActions, mapState } from "vuex";
export default {
name: "experiment-summary",
- props: {
- fullExperiment: {
- type: models.FullExperiment,
- required: true,
- },
- launching: {
- type: Boolean,
- default: false,
- },
- },
- data() {
- return {
- localFullExperiment: this.fullExperiment.clone(),
- };
- },
components: {
"clipboard-copy-link": components.ClipboardCopyLink,
"share-button": components.ShareButton,
@@ -327,6 +313,14 @@ export default {
DataProductViewer,
},
computed: {
+ ...mapState("viewExperiment", [
+ "fullExperiment",
+ "launching",
+ "clonedExperiment",
+ ]),
+ localFullExperiment() {
+ return this.fullExperiment;
+ },
inputDataProducts() {
const result = {};
if (
@@ -410,57 +404,23 @@ export default {
},
},
methods: {
- loadExperiment: function () {
- return services.FullExperimentService.retrieve(
- { lookup: this.localFullExperiment.experiment.experimentId },
- { ignoreErrors: true, showSpinner: false }
- ).then((exp) => (this.localFullExperiment = exp));
- },
- initPollingExperiment: function () {
- var pollExperiment = function () {
- if (
- (this.launching &&
- !this.localFullExperiment.experiment.hasLaunched) ||
- this.localFullExperiment.experiment.isProgressing
- ) {
- this.loadExperiment()
- .then(() => {
- setTimeout(pollExperiment.bind(this), 3000);
- })
- .catch(() => {
- // Wait 30 seconds after an error and then try again
- setTimeout(pollExperiment.bind(this), 30000);
- });
- }
- }.bind(this);
- setTimeout(pollExperiment, 3000);
- },
- clone() {
- services.ExperimentService.clone({
- lookup: this.experiment.experimentId,
- }).then((clonedExperiment) => {
- urls.navigateToEditExperiment(clonedExperiment);
- });
+ ...mapActions("viewExperiment", ["clone", "launch", "cancel"]),
+ async onClone() {
+ await this.clone();
+ urls.navigateToEditExperiment(this.clonedExperiment);
},
- launch() {
- services.ExperimentService.launch({
- lookup: this.experiment.experimentId,
- }).then(() => {
- this.$emit("Launched");
- });
+ onLaunch() {
+ this.launch();
},
- cancel() {
- services.ExperimentService.cancel({
- lookup: this.experiment.experimentId,
- }).then(() => {
- notifications.NotificationList.add(
- new notifications.Notification({
- type: "SUCCESS",
- message: "Cancel-experiment requested",
- duration: 5,
- })
- );
- });
+ async onCancel() {
+ await this.cancel();
+ notifications.NotificationList.add(
+ new notifications.Notification({
+ type: "SUCCESS",
+ message: "Cancel-experiment requested",
+ duration: 5,
+ })
+ );
},
getDataProducts(io, collection) {
if (!io.value || !collection) {
@@ -483,15 +443,5 @@ export default {
: [];
},
},
- watch: {
- launching: function (val) {
- if (val == true) {
- this.initPollingExperiment();
- }
- },
- },
- mounted: function () {
- this.initPollingExperiment();
- },
};
</script>
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/ViewExperimentContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/ViewExperimentContainer.vue
deleted file mode 100644
index 2570701..0000000
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/ViewExperimentContainer.vue
+++ /dev/null
@@ -1,52 +0,0 @@
-<template>
- <experiment-summary
- v-if="fullExperiment"
- :fullExperiment="fullExperiment"
- :launching="launch"
- @Launched="handleLaunchedExperiment"
- :key="componentKey"
- >
- </experiment-summary>
-</template>
-
-<script>
-import { services } from "django-airavata-api";
-import ExperimentSummary from "../components/experiment/ExperimentSummary.vue";
-
-export default {
- name: "view-experiment-container",
- props: {
- initialFullExperimentData: {
- required: true,
- },
- launching: {
- type: Boolean,
- default: false,
- },
- },
- data() {
- return {
- fullExperiment: null,
- componentKey: 0,
- launch: this.launching,
- };
- },
- components: {
- ExperimentSummary,
- },
- methods: {
- handleLaunchedExperiment: function () {
- this.launch = true;
- this.componentKey = 1;
- },
- },
- computed: {},
- beforeMount: function () {
- services.FullExperimentService.retrieve({
- lookup: this.initialFullExperimentData.experimentId,
- initialFullExperimentData: this.initialFullExperimentData,
- }).then((exp) => (this.fullExperiment = exp));
- },
-};
-</script>
-<style></style>
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-view-experiment.js b/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-view-experiment.js
index c6289e9..4a0ab0d 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-view-experiment.js
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-view-experiment.js
@@ -1,29 +1,44 @@
-import { components, entry } from "django-airavata-common-ui";
-import ViewExperimentContainer from "./containers/ViewExperimentContainer.vue";
+import { components, errors } from "django-airavata-common-ui";
+import { mapActions } from "vuex";
+import ExperimentSummary from "./components/experiment/ExperimentSummary.vue";
+import store from "./store";
-entry((Vue) => {
- new Vue({
- render(h) {
- return h(components.MainLayout, [
- h(ViewExperimentContainer, {
- props: {
- initialFullExperimentData: this.fullExperimentData,
- launching: this.launching,
- },
- }),
- ]);
- },
- data() {
- return {
- fullExperimentData: null,
- launching: false,
- };
- },
- beforeMount() {
- this.fullExperimentData = JSON.parse(this.$el.dataset.fullExperimentData);
- if ("launching" in this.$el.dataset) {
- this.launching = JSON.parse(this.$el.dataset.launching);
- }
- },
- }).$mount("#view-experiment");
-});
+import Vue from "vue";
+import BootstrapVue from "bootstrap-vue";
+import AsyncComputed from "vue-async-computed";
+
+errors.GlobalErrorHandler.init();
+
+// This is imported globally on the website (see main.js) so no need to include
+// it again in this view
+// import 'bootstrap/dist/css/bootstrap.css'
+import "bootstrap-vue/dist/bootstrap-vue.css";
+
+// Common Vue configuration
+Vue.use(BootstrapVue);
+Vue.use(AsyncComputed);
+
+// TODO: fix entry so that we can still get common configuration when using Vuex
+// entry((Vue) => {
+new Vue({
+ store,
+ render(h) {
+ return h(components.MainLayout, [h(ExperimentSummary)]);
+ },
+ async beforeMount() {
+ const fullExperimentData = JSON.parse(this.$el.dataset.fullExperimentData);
+ this.setInitialFullExperimentData({ fullExperimentData });
+ if ("launching" in this.$el.dataset) {
+ const launching = JSON.parse(this.$el.dataset.launching);
+ this.setLaunching({ launching });
+ }
+ },
+ methods: {
+ ...mapActions("viewExperiment", [
+ "setInitialFullExperimentData",
+ "setLaunching",
+ ]),
+ },
+}).$mount("#view-experiment");
+
+// });
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/store/index.js b/django_airavata/apps/workspace/static/django_airavata_workspace/js/store/index.js
new file mode 100644
index 0000000..b997094
--- /dev/null
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/store/index.js
@@ -0,0 +1,14 @@
+import Vue from "vue";
+import Vuex from "vuex";
+import viewExperiment from "./modules/view-experiment";
+
+Vue.use(Vuex);
+
+const debug = process.env.NODE_ENV !== "production";
+
+export default new Vuex.Store({
+ modules: {
+ viewExperiment,
+ },
+ strict: debug,
+});
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/store/modules/view-experiment.js b/django_airavata/apps/workspace/static/django_airavata_workspace/js/store/modules/view-experiment.js
new file mode 100644
index 0000000..72e09c8
--- /dev/null
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/store/modules/view-experiment.js
@@ -0,0 +1,124 @@
+import { services } from "django-airavata-api";
+
+// const PROMISES = {
+// workspacePreferences: null,
+// };
+export const mutations = {
+ setFullExperiment(state, { fullExperiment }) {
+ state.fullExperiment = fullExperiment;
+ },
+ setLaunching(state, { launching }) {
+ state.launching = launching;
+ },
+ startPolling(state) {
+ state.polling = true;
+ },
+ stopPolling(state) {
+ state.polling = false;
+ },
+ setClonedExperiment(state, { clonedExperiment }) {
+ state.clonedExperiment = clonedExperiment;
+ },
+};
+export const actions = {
+ async setInitialFullExperimentData(
+ { commit, dispatch },
+ { fullExperimentData }
+ ) {
+ const fullExperiment = await services.FullExperimentService.retrieve({
+ lookup: fullExperimentData.experimentId,
+ initialFullExperimentData: fullExperimentData,
+ });
+ commit("setFullExperiment", { fullExperiment });
+ dispatch("initPollingExperiment");
+ },
+ setFullExperiment({ dispatch, commit }, { fullExperiment }) {
+ commit("setFullExperiment", { fullExperiment });
+ dispatch("initPollingExperiment");
+ },
+ setLaunching({ dispatch, commit }, { launching }) {
+ commit("setLaunching", { launching });
+ if (launching) {
+ dispatch("initPollingExperiment");
+ }
+ },
+ async loadExperiment({ commit }, { experimentId }) {
+ const fullExperiment = await services.FullExperimentService.retrieve(
+ { lookup: experimentId },
+ { ignoreErrors: true, showSpinner: false }
+ );
+ commit("setFullExperiment", { fullExperiment });
+ },
+ async pollExperiment({ commit, dispatch, state }) {
+ if (!state.fullExperiment) {
+ return;
+ }
+ if (
+ (state.launching && !state.fullExperiment.experiment.hasLaunched) ||
+ state.fullExperiment.experiment.isProgressing
+ ) {
+ commit("startPolling");
+ try {
+ await dispatch("loadExperiment", {
+ experimentId: state.fullExperiment.experimentId,
+ });
+ setTimeout(() => {
+ dispatch("pollExperiment");
+ }, 3000);
+ } catch (error) {
+ // Wait 30 seconds after an error and then try again
+ setTimeout(() => {
+ dispatch("pollExperiment");
+ }, 30000);
+ }
+ } else {
+ commit("stopPolling");
+ }
+ },
+ initPollingExperiment({ dispatch, getters }) {
+ // Only start polling if we aren't already polling
+ if (!getters.isPolling) {
+ dispatch("pollExperiment");
+ }
+ },
+ async clone({ commit, getters }) {
+ const clonedExperiment = await services.ExperimentService.clone({
+ lookup: getters.experimentId,
+ });
+ commit("setClonedExperiment", { clonedExperiment });
+ },
+ async launch({ dispatch, getters }) {
+ try {
+ await services.ExperimentService.launch({
+ lookup: getters.experimentId,
+ });
+ dispatch("setLaunching", { launching: true });
+ } catch (error) {
+ // TODO: handle launch error
+ }
+ },
+ async cancel({ dispatch, getters }) {
+ await services.ExperimentService.cancel({
+ lookup: getters.experimentId,
+ });
+ dispatch("loadExperiment", { experimentId: getters.experimentId });
+ },
+};
+
+export const getters = {
+ isPolling: (state) => state.polling,
+ experimentId: (state) => state.fullExperiment.experimentId,
+};
+
+export default {
+ namespaced: true,
+ state: {
+ fullExperiment: null,
+ launching: false,
+ polling: false,
+ clonedExperiment: null,
+ },
+ mutations,
+ actions,
+ getters,
+};