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 2021/10/05 19:47:14 UTC
[airavata-django-portal] 03/24: AIRAVATA-3477 Finish converting to
Vuex
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 38417879f9ff3c85b8350c24be829d80602d2ce3
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Fri Aug 27 14:14:51 2021 -0400
AIRAVATA-3477 Finish converting to Vuex
---
.../js/web-components/ComputeResourceSelector.vue | 45 +-
.../ExperimentComputeResourceSelector.vue | 61 +++
.../js/web-components/ExperimentEditor.vue | 100 ++++-
.../GroupResourceProfileSelector.vue | 37 +-
.../js/web-components/ProjectSelector.vue | 9 +-
.../js/web-components/QueueSettingsEditor.vue | 142 +++---
.../js/web-components/ResourceSelectionEditor.vue | 2 +-
.../js/web-components/store.js | 1 +
.../js/web-components/vuestore.js | 477 ++++++++++++++++++++-
9 files changed, 716 insertions(+), 158 deletions(-)
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ComputeResourceSelector.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ComputeResourceSelector.vue
index 3b28d83..88189f8 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ComputeResourceSelector.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ComputeResourceSelector.vue
@@ -15,50 +15,57 @@
</template>
<script>
-import { getComputeResourceNames } from "./store";
+import vuestore from "./vuestore";
+import { mapGetters } from "vuex";
+
export default {
name: "compute-resource-selector",
props: {
value: {
// compute resource host id
type: String,
- default: null,
+ required: true,
},
- computeResources: {
- type: Array, // of compute resource host ids
- default: () => [],
+ includedComputeResources: {
+ type: Array,
+ default: null,
},
},
+ store: vuestore,
data() {
return {
resourceHostId: this.value,
- computeResourceNames: {},
};
},
created() {
- this.loadComputeResourceNames();
+ this.$store.dispatch("loadComputeResourceNames");
},
computed: {
computeResourceOptions: function () {
- const computeResourceOptions = this.computeResources.map(
- (computeHostId) => {
- return {
- value: computeHostId,
- text:
- computeHostId in this.computeResourceNames
- ? this.computeResourceNames[computeHostId]
- : "",
- };
+ const computeResourceIds = Object.keys(this.computeResourceNames).filter(
+ (crid) => {
+ if (this.includedComputeResources) {
+ return this.includedComputeResources.includes(crid);
+ } else {
+ return true;
+ }
}
);
+ const computeResourceOptions = computeResourceIds.map((computeHostId) => {
+ return {
+ value: computeHostId,
+ text:
+ computeHostId in this.computeResourceNames
+ ? this.computeResourceNames[computeHostId]
+ : "",
+ };
+ });
computeResourceOptions.sort((a, b) => a.text.localeCompare(b.text));
return computeResourceOptions;
},
+ ...mapGetters(["computeResourceNames"]),
},
methods: {
- async loadComputeResourceNames() {
- this.computeResourceNames = await getComputeResourceNames();
- },
computeResourceChanged() {
this.emitValueChanged();
},
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentComputeResourceSelector.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentComputeResourceSelector.vue
new file mode 100644
index 0000000..d209d57
--- /dev/null
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentComputeResourceSelector.vue
@@ -0,0 +1,61 @@
+<template>
+ <compute-resource-selector
+ :value="value"
+ :includedComputeResources="computeResources"
+ @input.stop="computeResourceChanged"
+ />
+</template>
+
+<script>
+import vuestore from "./vuestore";
+import { mapGetters } from "vuex";
+import ComputeResourceSelector from "./ComputeResourceSelector.vue";
+
+export default {
+ name: "experiment-compute-resource-selector",
+ props: {
+ value: {
+ // compute resource host id
+ type: String,
+ required: true,
+ },
+ },
+ store: vuestore,
+ components: {
+ ComputeResourceSelector,
+ },
+ data() {
+ return {
+ resourceHostId: this.value,
+ };
+ },
+ computed: {
+ // compute resources for the current set of application deployments
+ ...mapGetters(["computeResources"]),
+ },
+ methods: {
+ computeResourceChanged(event) {
+ const [computeResourceId] = event.detail;
+ this.resourceHostId = computeResourceId;
+ this.emitValueChanged();
+ },
+ emitValueChanged: function () {
+ const inputEvent = new CustomEvent("input", {
+ detail: [this.resourceHostId],
+ composed: true,
+ bubbles: true,
+ });
+ this.$el.dispatchEvent(inputEvent);
+ },
+ },
+ watch: {
+ value() {
+ this.resourceHostId = this.value;
+ },
+ },
+};
+</script>
+
+<style>
+@import "./styles.css";
+</style>
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentEditor.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentEditor.vue
index 5c551b8..12f7287 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentEditor.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentEditor.vue
@@ -17,9 +17,54 @@
<!-- programmatically define slots as native slots (not Vue slots), see #mounted() -->
</div>
</template>
- <div ref="resourceSelectionEditor" @input="updateUserConfigurationData">
- <!-- programmatically define slot for experiment-resource-selection as
- native slot (not Vue slots), see #mounted() -->
+ <!-- TODO: programmatically define slot for adpf-group-resource-profile-selector -->
+ <div @input.stop="updateGroupResourceProfileId">
+ <adpf-group-resource-profile-selector
+ :value="experiment.userConfigurationData.groupResourceProfileId"
+ />
+ </div>
+ <!-- TODO: programmatically define slot for adpf-experiment-compute-resource-selector -->
+ <div @input.stop="updateComputeResourceHostId">
+ <adpf-experiment-compute-resource-selector
+ ref="computeResourceSelector"
+ :value="
+ experiment.userConfigurationData.computationalResourceScheduling
+ .resourceHostId
+ "
+ />
+ </div>
+ <!-- TODO: programmatically define slot for adpf-queue-settings-editor -->
+ <div
+ @queue-name-changed="updateQueueName"
+ @node-count-changed="updateNodeCount"
+ @total-cpu-count-change="updateTotalCPUCount"
+ @walltime-limit-changed="updateWallTimeLimit"
+ @total-physical-memory-changed="updateTotalPhysicalMemory"
+ >
+ <adpf-queue-settings-editor
+ ref="queueSettingsEditor"
+ :queue-name="
+ experiment.userConfigurationData.computationalResourceScheduling
+ .queueName
+ "
+ :total-cpu-count="
+ experiment.userConfigurationData.computationalResourceScheduling
+ .totalCPUCount
+ "
+ :node-count="
+ experiment.userConfigurationData.computationalResourceScheduling
+ .nodeCount
+ "
+ :wall-time-limit="
+ experiment.userConfigurationData.computationalResourceScheduling
+ .wallTimeLimit
+ "
+ :total-physical-memory="
+ experiment.userConfigurationData.computationalResourceScheduling
+ .totalPhysicalMemory
+ "
+ />
+
</div>
<div ref="experimentButtons">
<!-- programmatically define slot for experiment-buttons as
@@ -125,19 +170,6 @@ export default {
this.createSlot("experiment-project", projectSelectorEl)
);
- const resourceSelectionEditor = document.createElement(
- "adpf-resource-selection-editor"
- );
- this.$refs.resourceSelectionEditor.append(
- this.createSlot(
- "experiment-resource-selection",
- resourceSelectionEditor
- )
- );
- // Can't set objects via attributes, must set as prop
- resourceSelectionEditor.value = this.experiment.userConfigurationData;
- resourceSelectionEditor.applicationModuleId = this.applicationId;
-
/*
* Experiment (save/launch) Buttons native slot
*/
@@ -202,10 +234,38 @@ export default {
const [projectId] = event.detail;
this.$store.dispatch("updateProjectId", { projectId });
},
- updateUserConfigurationData(event) {
- const [userConfigurationData] = event.detail;
- this.$store.dispatch("updateUserConfigurationData", {
- userConfigurationData,
+ updateGroupResourceProfileId(event) {
+ const [groupResourceProfileId] = event.detail;
+ this.$store.dispatch("updateGroupResourceProfileId", {
+ groupResourceProfileId,
+ });
+ },
+ updateComputeResourceHostId(event) {
+ const [resourceHostId] = event.detail;
+ this.$store.dispatch("updateComputeResourceHostId", {
+ resourceHostId,
+ });
+ },
+ updateQueueName(event) {
+ const [queueName] = event.detail;
+ this.$store.dispatch("updateQueueName", { queueName });
+ },
+ updateTotalCPUCount(event) {
+ const [totalCPUCount] = event.detail;
+ this.$store.dispatch("updateTotalCPUCount", { totalCPUCount });
+ },
+ updateNodeCount(event) {
+ const [nodeCount] = event.detail;
+ this.$store.dispatch("updateNodeCount", { nodeCount });
+ },
+ updateWallTimeLimit(event) {
+ const [wallTimeLimit] = event.detail;
+ this.$store.dispatch("updateWallTimeLimit", { wallTimeLimit });
+ },
+ updateTotalPhysicalMemory(event) {
+ const [totalPhysicalMemory] = event.detail;
+ this.$store.dispatch("updateTotalPhysicalMemory", {
+ totalPhysicalMemory,
});
},
async onSubmit(event) {
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/GroupResourceProfileSelector.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/GroupResourceProfileSelector.vue
index 9fd33c0..7be711f 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/GroupResourceProfileSelector.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/GroupResourceProfileSelector.vue
@@ -17,12 +17,10 @@
</template>
<script>
-import {
- getDefaultGroupResourceProfileId,
- getGroupResourceProfiles,
-} from "./store";
import Vue from "vue";
import { BootstrapVue } from "bootstrap-vue";
+import vuestore from "./vuestore";
+import { mapGetters } from "vuex";
Vue.use(BootstrapVue);
export default {
@@ -30,23 +28,21 @@ export default {
props: {
value: {
type: String,
+ required: true,
},
label: {
type: String,
default: "Allocation",
},
},
+ store: vuestore,
data() {
return {
groupResourceProfileId: this.value,
- groupResourceProfiles: [],
- defaultGroupResourceProfileId: null,
};
},
async mounted() {
- this.defaultGroupResourceProfileId = await getDefaultGroupResourceProfileId();
- this.groupResourceProfiles = await getGroupResourceProfiles();
- this.init();
+ await this.$store.dispatch("loadGroupResourceProfiles");
},
computed: {
groupResourceProfileOptions: function () {
@@ -67,23 +63,9 @@ export default {
return [];
}
},
+ ...mapGetters(["groupResourceProfiles"]),
},
methods: {
- init() {
- // Default the selected group resource profile
- if (
- (!this.value ||
- !this.selectedValueInGroupResourceProfileList(
- this.groupResourceProfiles
- )) &&
- this.groupResourceProfiles &&
- this.groupResourceProfiles.length > 0
- ) {
- // automatically select the last one user selected
- this.groupResourceProfileId = this.defaultGroupResourceProfileId;
- this.emitValueChanged();
- }
- },
groupResourceProfileChanged: function (groupResourceProfileId) {
this.groupResourceProfileId = groupResourceProfileId;
this.emitValueChanged();
@@ -96,13 +78,6 @@ export default {
});
this.$el.dispatchEvent(inputEvent);
},
- selectedValueInGroupResourceProfileList(groupResourceProfiles) {
- return (
- groupResourceProfiles
- .map((grp) => grp.groupResourceProfileId)
- .indexOf(this.value) >= 0
- );
- },
},
watch: {
value() {
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ProjectSelector.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ProjectSelector.vue
index 224da0f..dc4bddf 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ProjectSelector.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ProjectSelector.vue
@@ -47,15 +47,10 @@ export default {
};
},
async mounted() {
- await this.$store.dispatch('loadProjects');
- await this.$store.dispatch('loadDefaultProjectId');
- if (!this.projectId) {
- this.projectId = this.defaultProjectId;
- this.$emit("input", this.projectId);
- }
+ await this.$store.dispatch("loadProjects");
},
computed: {
- ...mapGetters(["projects", "defaultProjectId"]),
+ ...mapGetters(["projects"]),
sharedProjectOptions: function () {
return this.projects
? this.projects
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/QueueSettingsEditor.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/QueueSettingsEditor.vue
index 27c9381..a4692f4 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/QueueSettingsEditor.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/QueueSettingsEditor.vue
@@ -7,31 +7,27 @@
>
<div class="card-body">
<h5 class="card-title mb-4">
- Settings for queue {{ computationalResourceScheduling.queueName }}
+ Settings for queue {{ localQueueName }}
</h5>
<div class="row">
<div class="col">
<h3 class="h5 mb-0">
- {{ computationalResourceScheduling.nodeCount }}
+ {{ localNodeCount }}
</h3>
<span class="text-muted text-uppercase">NODE COUNT</span>
</div>
<div class="col">
<h3 class="h5 mb-0">
- {{ computationalResourceScheduling.totalCPUCount }}
+ {{ localTotalCPUCount }}
</h3>
<span class="text-muted text-uppercase">CORE COUNT</span>
</div>
<div class="col">
- <h3 class="h5 mb-0">
- {{ computationalResourceScheduling.wallTimeLimit }} minutes
- </h3>
+ <h3 class="h5 mb-0">{{ localWallTimeLimit }} minutes</h3>
<span class="text-muted text-uppercase">TIME LIMIT</span>
</div>
<div class="col" v-if="maxMemory > 0">
- <h3 class="h5 mb-0">
- {{ computationalResourceScheduling.totalPhysicalMemory }} MB
- </h3>
+ <h3 class="h5 mb-0">{{ localTotalPhysicalMemory }} MB</h3>
<span class="text-muted text-uppercase">PHYSICAL MEMORY</span>
</div>
</div>
@@ -42,7 +38,7 @@
<b-form-group label="Select a Queue" label-for="queue">
<b-form-select
id="queue"
- v-model="computationalResourceScheduling.queueName"
+ v-model="localQueueName"
:options="queueOptions"
required
@change="queueChanged"
@@ -56,9 +52,11 @@
type="number"
min="1"
:max="maxAllowedNodes"
- v-model="computationalResourceScheduling.nodeCount"
+ v-model="localNodeCount"
required
- @input.native.stop="emitValueChanged"
+ @input.native.stop="
+ emitValueChanged('node-count-changed', localNodeCount)
+ "
>
</b-form-input>
<div slot="description">
@@ -72,9 +70,11 @@
type="number"
min="1"
:max="maxAllowedCores"
- v-model="computationalResourceScheduling.totalCPUCount"
+ v-model="localTotalCPUCount"
required
- @input.native.stop="emitValueChanged"
+ @input.native.stop="
+ emitValueChanged('total-cpu-count-changed', localTotalCPUCount)
+ "
>
</b-form-input>
<div slot="description">
@@ -89,9 +89,11 @@
type="number"
min="1"
:max="maxAllowedWalltime"
- v-model="computationalResourceScheduling.wallTimeLimit"
+ v-model="localWallTimeLimit"
required
- @input.native.stop="emitValueChanged"
+ @input.native.stop="
+ emitValueChanged('walltime-limit-changed', localWallTimeLimit)
+ "
>
</b-form-input>
</b-input-group>
@@ -111,8 +113,13 @@
type="number"
min="0"
:max="maxMemory"
- v-model="computationalResourceScheduling.totalPhysicalMemory"
- @input.native.stop="emitValueChanged"
+ v-model="localTotalPhysicalMemory"
+ @input.native.stop="
+ emitValueChanged(
+ 'total-physical-memory-changed',
+ localTotalPhysicalMemory
+ )
+ "
>
</b-form-input>
</b-input-group>
@@ -132,8 +139,10 @@
</template>
<script>
-import { models, utils } from "django-airavata-api";
+import { utils } from "django-airavata-api";
import Vue from "vue";
+import vuestore from "./vuestore";
+import { mapGetters } from "vuex";
import { BootstrapVue } from "bootstrap-vue";
Vue.use(BootstrapVue);
@@ -146,34 +155,32 @@ config.autoAddCss = false;
export default {
props: {
- value: {
- type: models.ComputationalResourceSchedulingModel,
- // required: true,
- },
- queues: {
- type: Array, // of BatchQueue
- // required: true,
+ queueName: {
+ type: String,
+ required: true,
},
- maxAllowedNodes: {
+ totalCpuCount: {
type: Number,
required: true,
},
- maxAllowedCores: {
+ nodeCount: {
type: Number,
required: true,
},
- maxAllowedWalltime: {
+ wallTimeLimit: {
type: Number,
required: true,
},
- maxMemory: {
+ totalPhysicalMemory: {
type: Number,
- required: true,
+ default: 0,
+ // required: true,
},
},
components: {
FontAwesomeIcon,
},
+ store: vuestore,
mounted() {
// Add font awesome styles
// https://github.com/FortAwesome/vue-fontawesome#web-components-with-vue-web-component-wrapper
@@ -188,12 +195,25 @@ export default {
}
},
data() {
+ console.log("QueueSettingsEditor.vue: queueName", this.queueName);
return {
- computationalResourceScheduling: this.cloneValue(),
+ localQueueName: this.queueName,
+ localTotalCPUCount: this.totalCpuCount,
+ localNodeCount: this.nodeCount,
+ localWallTimeLimit: this.wallTimeLimit,
+ localTotalPhysicalMemory: this.totalPhysicalMemory,
showConfiguration: false,
};
},
computed: {
+ ...mapGetters([
+ "queue",
+ "queues",
+ "maxAllowedCores",
+ "maxAllowedNodes",
+ "maxAllowedWalltime",
+ "maxMemory",
+ ]),
queueOptions() {
if (!this.queues) {
return [];
@@ -207,50 +227,52 @@ export default {
utils.StringUtils.sortIgnoreCase(queueOptions, (q) => q.text);
return queueOptions;
},
- queue() {
- return this.queues
- ? this.queues.find((q) => q.queueName === this.queueName)
- : null;
- },
- queueName() {
- return this.computationalResourceScheduling
- ? this.computationalResourceScheduling.queueName
- : null;
- },
- queueDescription() {
- return this.queue ? this.queue.queueDescription : null;
- },
closeIcon() {
return faTimes;
},
infoIcon() {
return faInfoCircle;
- }
+ },
},
methods: {
- cloneValue() {
- return this.value
- ? this.value.clone()
- : new models.ComputationalResourceSchedulingModel();
- },
- emitValueChanged() {
- const inputEvent = new CustomEvent("input", {
- detail: [this.computationalResourceScheduling.clone()],
+ emitValueChanged(eventName, value) {
+ console.log(
+ "QueueSettingsEditor: emitValueChanged(",
+ eventName,
+ value,
+ ")"
+ );
+ const inputEvent = new CustomEvent(eventName, {
+ detail: [value],
composed: true,
bubbles: true,
});
this.$el.dispatchEvent(inputEvent);
},
queueChanged() {
- this.emitValueChanged();
+ this.emitValueChanged("queue-name-changed", this.localQueueName);
},
},
watch: {
- value: {
- handler() {
- this.computationalResourceScheduling = this.cloneValue();
- },
- deep: true,
+ queueName(value) {
+ console.log("QueueSettingsEditor: watch(queueName)", value);
+ this.localQueueName = value;
+ },
+ // nodes(value, oldValue) {
+ // console.log("QueueSettingsEditor: watch(nodes)", value, oldValue);
+ // this.localNodeCount = value;
+ // },
+ nodeCount(value) {
+ this.localNodeCount = value;
+ },
+ totalCpuCount(value) {
+ this.localTotalCPUCount = value;
+ },
+ wallTimeLimit(value) {
+ this.localWallTimeLimit = value;
+ },
+ totalPhysicalMemory(value) {
+ this.localTotalPhysicalMemory = value;
},
},
};
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ResourceSelectionEditor.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ResourceSelectionEditor.vue
index 8791b32..9557db9 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ResourceSelectionEditor.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ResourceSelectionEditor.vue
@@ -1,4 +1,5 @@
<template>
+<!-- TODO: this is no longer needed -->
<div v-if="userConfigurationData">
<div @input.stop="updateGroupResourceProfileId">
<adpf-group-resource-profile-selector
@@ -395,7 +396,6 @@ export default {
},
bindWebComponentProps() {
this.$nextTick(() => {
- this.$refs.computeResourceSelector.computeResources = this.computeResources;
this.$refs.computeResourceSelector.value = this.resourceHostId;
this.$refs.queueSettingsEditor.value = this.userConfigurationData.computationalResourceScheduling;
this.$refs.queueSettingsEditor.queues = this.queues;
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/store.js b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/store.js
index 321a5a0..1f54748 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/store.js
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/store.js
@@ -1,3 +1,4 @@
+// TODO: this is no longer needed
import { errors, services, utils } from "django-airavata-api";
const CACHE = {
APPLICATION_MODULES: {},
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/vuestore.js b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/vuestore.js
index 0681867..bb51992 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/vuestore.js
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/vuestore.js
@@ -1,15 +1,21 @@
-import { services } from "django-airavata-api";
+import { errors, services, utils } from "django-airavata-api";
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
+const PROMISES = {
+ workspacePreferences: null,
+};
export default new Vuex.Store({
state: {
experiment: null,
projects: null,
- defaultProjectId: null,
- computeResourceNames: null,
+ computeResourceNames: {},
+ applicationDeployments: [],
+ groupResourceProfiles: null,
+ applicationModuleId: null,
+ appDeploymentQueues: [],
},
mutations: {
setExperiment(state, { experiment }) {
@@ -27,21 +33,51 @@ export default new Vuex.Store({
updateProjectId(state, { projectId }) {
state.experiment.projectId = projectId;
},
- updateUserConfigurationData(state, { userConfigurationData }) {
- state.experiment.userConfigurationData = userConfigurationData;
+ updateGroupResourceProfileId(state, { groupResourceProfileId }) {
+ state.experiment.userConfigurationData.groupResourceProfileId = groupResourceProfileId;
+ },
+ updateResourceHostId(state, { resourceHostId }) {
+ state.experiment.userConfigurationData.computationalResourceScheduling.resourceHostId = resourceHostId;
+ },
+ updateQueueName(state, { queueName }) {
+ state.experiment.userConfigurationData.computationalResourceScheduling.queueName = queueName;
+ },
+ updateTotalCPUCount(state, { totalCPUCount }) {
+ state.experiment.userConfigurationData.computationalResourceScheduling.totalCPUCount = totalCPUCount;
+ },
+ updateNodeCount(state, { nodeCount }) {
+ state.experiment.userConfigurationData.computationalResourceScheduling.nodeCount = nodeCount;
+ },
+ updateWallTimeLimit(state, { wallTimeLimit }) {
+ state.experiment.userConfigurationData.computationalResourceScheduling.wallTimeLimit = wallTimeLimit;
+ },
+ updateTotalPhysicalMemory(state, { totalPhysicalMemory }) {
+ state.experiment.userConfigurationData.computationalResourceScheduling.totalPhysicalMemory = totalPhysicalMemory;
},
setProjects(state, { projects }) {
state.projects = projects;
},
- setDefaultProjectId(state, { defaultProjectId }) {
- state.defaultProjectId = defaultProjectId;
- },
setComputeResourceNames(state, { computeResourceNames }) {
state.computeResourceNames = computeResourceNames;
},
+ setGroupResourceProfiles(state, { groupResourceProfiles }) {
+ state.groupResourceProfiles = groupResourceProfiles;
+ },
+ setWorkspacePreferences(state, { workspacePreferences }) {
+ state.workspacePreferences = workspacePreferences;
+ },
+ setApplicationModuleId(state, { applicationModuleId }) {
+ state.applicationModuleId = applicationModuleId;
+ },
+ setApplicationDeployments(state, { applicationDeployments }) {
+ state.applicationDeployments = applicationDeployments;
+ },
+ setAppDeploymentQueues(state, { appDeploymentQueues }) {
+ state.appDeploymentQueues = appDeploymentQueues;
+ },
},
actions: {
- async loadNewExperiment({ commit }, { applicationId }) {
+ async loadNewExperiment({ commit, dispatch }, { applicationId }) {
const applicationModule = await services.ApplicationModuleService.retrieve(
{
lookup: applicationId,
@@ -58,13 +94,50 @@ export default new Vuex.Store({
timeStyle: "short",
});
experiment.experimentName = `${applicationModule.appModuleName} on ${currentDate}`;
- commit("setExperiment", { experiment });
+ commit("setApplicationModuleId", { applicationModuleId: applicationId });
+ await dispatch("setExperiment", { experiment });
},
- async loadExperiment({ commit }, { experimentId }) {
+ async loadExperiment({ commit, dispatch }, { experimentId }) {
const experiment = await services.ExperimentService.retrieve({
lookup: experimentId,
});
+ const appInterface = await services.ApplicationInterfaceService.retrieve({
+ lookup: experiment.executionId,
+ });
+ commit("setApplicationModuleId", {
+ applicationModuleId: appInterface.applicationModuleId,
+ });
+ await dispatch("setExperiment", { experiment });
+ },
+ async setExperiment({ commit, dispatch }, { experiment }) {
commit("setExperiment", { experiment });
+ await dispatch("loadExperimentData");
+ },
+ async loadExperimentData({ commit, dispatch, getters }) {
+ await Promise.all([
+ dispatch("loadProjects"),
+ dispatch("loadWorkspacePreferences"),
+ dispatch("loadGroupResourceProfiles"),
+ ]);
+
+ if (!getters.experiment.projectId) {
+ commit("updateProjectId", { projectId: getters.defaultProjectId });
+ }
+ // If there is no groupResourceProfileId set on the experiment, or there
+ // is one set but it is no longer in the list of accessible
+ // groupResourceProfiles, set to the default one
+ if (!getters.groupResourceProfileId || !getters.groupResourceProfile) {
+ commit("updateGroupResourceProfileId", {
+ groupResourceProfileId: getters.defaultGroupResourceProfileId,
+ });
+ }
+ // If experiment has a group resource profile and user has access to it,
+ // load additional necessary data and re-apply group resource profile
+ if (getters.groupResourceProfile) {
+ await dispatch("loadApplicationDeployments");
+ await dispatch("loadAppDeploymentQueues");
+ await dispatch("applyGroupResourceProfile");
+ }
},
updateExperimentName({ commit }, { name }) {
commit("updateExperimentName", { name });
@@ -75,8 +148,181 @@ export default new Vuex.Store({
updateProjectId({ commit }, { projectId }) {
commit("updateProjectId", { projectId });
},
- updateUserConfigurationData({ commit }, { userConfigurationData }) {
- commit("updateUserConfigurationData", { userConfigurationData });
+ async updateGroupResourceProfileId(
+ { commit, dispatch },
+ { groupResourceProfileId }
+ ) {
+ commit("updateGroupResourceProfileId", { groupResourceProfileId });
+ await dispatch("loadApplicationDeployments");
+ await dispatch("applyGroupResourceProfile");
+ },
+ async updateComputeResourceHostId(
+ { commit, dispatch, getters },
+ { resourceHostId }
+ ) {
+ if (getters.resourceHostId !== resourceHostId) {
+ commit("updateResourceHostId", { resourceHostId });
+ await dispatch("loadAppDeploymentQueues");
+ await dispatch("setDefaultQueue");
+ }
+ },
+ updateQueueName({ commit, dispatch }, { queueName }) {
+ commit("updateQueueName", { queueName });
+ dispatch("initializeQueue");
+ },
+ updateTotalCPUCount({ commit }, { totalCPUCount }) {
+ commit("updateTotalCPUCount", { totalCPUCount });
+ },
+ updateNodeCount({ commit }, { nodeCount }) {
+ commit("updateNodeCount", { nodeCount });
+ },
+ updateWallTimeLimit({ commit }, { wallTimeLimit }) {
+ commit("updateWallTimeLimit", { wallTimeLimit });
+ },
+ updateTotalPhysicalMemory({ commit }, { totalPhysicalMemory }) {
+ commit("updateTotalPhysicalMemory", { totalPhysicalMemory });
+ },
+ async loadApplicationDeployments({ commit, getters, state }) {
+ const applicationDeployments = await services.ApplicationDeploymentService.list(
+ {
+ appModuleId: state.applicationModuleId,
+ groupResourceProfileId: getters.groupResourceProfileId,
+ },
+ { ignoreErrors: true }
+ )
+ .catch((error) => {
+ // Ignore unauthorized errors, force user to pick another GroupResourceProfile
+ if (!errors.ErrorUtils.isUnauthorizedError(error)) {
+ return Promise.reject(error);
+ } else {
+ return Promise.resolve([]);
+ }
+ })
+ // Report all other error types
+ .catch(utils.FetchUtils.reportError);
+ commit("setApplicationDeployments", { applicationDeployments });
+ },
+ async applyGroupResourceProfile({ dispatch, getters }) {
+ // Make sure that resource host id is in the list of app deployments
+ const computeResourceChanged = await dispatch("initializeResourceHostId");
+ if (computeResourceChanged) {
+ await dispatch("loadAppDeploymentQueues");
+ await dispatch("setDefaultQueue");
+ } else if (!getters.queue) {
+ // allowed queues may have changed. If selected queue isn't in the list
+ // of allowed queues, reset to the default
+ await dispatch("setDefaultQueue");
+ } else {
+ // reapply batchQueueResourcePolicy maximums since they may have changed
+ dispatch("applyBatchQueueResourcePolicy");
+ }
+ },
+ async initializeResourceHostId({ commit, dispatch, getters }) {
+ // if there isn't a selected compute resource or there is but it isn't in
+ // the list of app deployments, set a default one
+ // Returns true if the resourceHostId changed
+ if (
+ !getters.resourceHostId ||
+ !getters.computeResources.find(
+ (crid) => crid === getters.resourceHostId
+ )
+ ) {
+ const defaultResourceHostId = await dispatch(
+ "getDefaultResourceHostId"
+ );
+ commit("updateResourceHostId", {
+ resourceHostId: defaultResourceHostId,
+ });
+ return true;
+ }
+ return false;
+ },
+ async getDefaultResourceHostId({ dispatch, getters }) {
+ await dispatch("loadDefaultComputeResourceId");
+ if (
+ getters.defaultComputeResourceId &&
+ getters.computeResources.find(
+ (crid) => crid === getters.defaultComputeResourceId
+ )
+ ) {
+ return getters.defaultComputeResourceId;
+ } else if (getters.computeResources.length > 0) {
+ // Just pick the first one
+ return getters.computeResources[0];
+ } else {
+ return null;
+ }
+ },
+ async loadDefaultComputeResourceId({ dispatch }) {
+ await dispatch("loadWorkspacePreferences");
+ },
+ async loadAppDeploymentQueues({ commit, getters }) {
+ const applicationDeployment = getters.applicationDeployment;
+ if (applicationDeployment) {
+ const appDeploymentQueues = await services.ApplicationDeploymentService.getQueues(
+ {
+ lookup: applicationDeployment.appDeploymentId,
+ }
+ );
+ commit("setAppDeploymentQueues", { appDeploymentQueues });
+ } else {
+ commit("setAppDeploymentQueues", { appDeploymentQueues: [] });
+ }
+ },
+ async setDefaultQueue({ commit, dispatch, getters }) {
+ // set to the default queue or the first one
+ const defaultQueue = getters.defaultQueue;
+ if (defaultQueue) {
+ commit("updateQueueName", { queueName: defaultQueue.queueName });
+ } else {
+ commit("updateQueueName", { queueName: null });
+ }
+ dispatch("initializeQueue");
+ },
+ initializeQueue({ commit, getters }) {
+ const queue = getters.queue;
+ if (queue) {
+ commit("updateTotalCPUCount", {
+ totalCPUCount: getters.getDefaultCPUCount(queue),
+ });
+ commit("updateNodeCount", {
+ nodeCount: getters.getDefaultNodeCount(queue),
+ });
+ commit("updateWallTimeLimit", {
+ wallTimeLimit: getters.getDefaultWalltime(queue),
+ });
+ commit("updateTotalPhysicalMemory", { totalPhysicalMemory: 0 });
+ } else {
+ commit("updateTotalCPUCount", { totalCPUCount: 0 });
+ commit("updateNodeCount", { nodeCount: 0 });
+ commit("updateWallTimeLimit", { wallTimeLimit: 0 });
+ commit("updateTotalPhysicalMemory", { totalPhysicalMemory: 0 });
+ }
+ },
+ applyBatchQueueResourcePolicy({ commit, getters }) {
+ if (getters.batchQueueResourcePolicy) {
+ const crs =
+ getters.experiment.userConfigurationData
+ .computationalResourceScheduling;
+ commit("updateTotalCPUCount", {
+ totalCPUCount: Math.min(
+ crs.totalCPUCount,
+ getters.batchQueueResourcePolicy.maxAllowedCores
+ ),
+ });
+ commit("updateNodeCount", {
+ nodeCount: Math.min(
+ crs.nodeCount,
+ getters.batchQueueResourcePolicy.maxAllowedNodes
+ ),
+ });
+ commit("updateWallTimeLimit", {
+ wallTimeLimit: Math.min(
+ crs.wallTimeLimit,
+ getters.batchQueueResourcePolicy.maxAllowedWalltime
+ ),
+ });
+ }
},
async saveExperiment({ commit, getters }) {
if (getters.experiment.experimentId) {
@@ -98,19 +344,36 @@ export default new Vuex.Store({
});
},
async loadProjects({ commit }) {
- const projects = await services.ProjectService.listAll();
+ if (!PROMISES.projects) {
+ PROMISES.projects = services.ProjectService.listAll();
+ }
+ const projects = await PROMISES.projects;
commit("setProjects", { projects });
},
- async loadDefaultProjectId({ commit }) {
- // TODO: cache the workspace preferences so they aren't loaded more than once
- const prefs = await services.WorkspacePreferencesService.get();
- const defaultProjectId = prefs.most_recent_project_id;
- commit("setDefaultProjectId", { defaultProjectId });
+ async loadWorkspacePreferences({ commit }) {
+ if (!PROMISES.workspacePreferences) {
+ PROMISES.workspacePreferences = services.WorkspacePreferencesService.get();
+ }
+ const workspacePreferences = await PROMISES.workspacePreferences;
+ commit("setWorkspacePreferences", { workspacePreferences });
+ },
+ async loadDefaultProjectId({ dispatch }) {
+ await dispatch("loadWorkspacePreferences");
},
async loadComputeResourceNames({ commit }) {
const computeResourceNames = await services.ComputeResourceService.names();
commit("setComputeResourceNames", { computeResourceNames });
},
+ async loadDefaultGroupResourceProfileId({ dispatch }) {
+ await dispatch("loadWorkspacePreferences");
+ },
+ async loadGroupResourceProfiles({ commit }) {
+ if (!PROMISES.groupResourceProfiles) {
+ PROMISES.groupResourceProfiles = services.GroupResourceProfileService.list();
+ }
+ const groupResourceProfiles = await PROMISES.groupResourceProfiles;
+ commit("setGroupResourceProfiles", { groupResourceProfiles });
+ },
},
getters: {
getExperimentInputByName: (state) => (name) => {
@@ -129,7 +392,181 @@ export default new Vuex.Store({
},
experiment: (state) => state.experiment,
projects: (state) => state.projects,
- defaultProjectId: (state) => state.defaultProjectId,
+ defaultProjectId: (state) =>
+ state.workspacePreferences
+ ? state.workspacePreferences.most_recent_project_id
+ : null,
+ defaultGroupResourceProfileId: (state) =>
+ state.workspacePreferences
+ ? state.workspacePreferences.most_recent_group_resource_profile_id
+ : null,
+ defaultComputeResourceId: (state) =>
+ state.workspacePreferences
+ ? state.workspacePreferences.most_recent_compute_resource_id
+ : null,
computeResourceNames: (state) => state.computeResourceNames,
+ groupResourceProfiles: (state) => state.groupResourceProfiles,
+ groupResourceProfileId: (state) =>
+ state.experiment
+ ? state.experiment.userConfigurationData.groupResourceProfileId
+ : null,
+ groupResourceProfile: (state, getters) =>
+ state.groupResourceProfiles
+ ? state.groupResourceProfiles.find(
+ (g) => g.groupResourceProfileId === getters.groupResourceProfileId
+ )
+ : null,
+ resourceHostId: (state) =>
+ state.experiment &&
+ state.experiment.userConfigurationData &&
+ state.experiment.userConfigurationData.computationalResourceScheduling
+ ? state.experiment.userConfigurationData.computationalResourceScheduling
+ .resourceHostId
+ : null,
+ computeResources: (state) =>
+ state.applicationDeployments.map((dep) => dep.computeHostId),
+ applicationDeployment: (state, getters) => {
+ if (state.applicationDeployments && getters.resourceHostId) {
+ return state.applicationDeployments.find(
+ (ad) => ad.computeHostId === getters.resourceHostId
+ );
+ } else {
+ return null;
+ }
+ },
+ isQueueInComputeResourcePolicy: (state, getters) => (queueName) => {
+ if (!getters.computeResourcePolicy) {
+ return true;
+ }
+ return getters.computeResourcePolicy.allowedBatchQueues.includes(
+ queueName
+ );
+ },
+ queues: (state, getters) => {
+ return state.appDeploymentQueues
+ ? state.appDeploymentQueues.filter((q) =>
+ getters.isQueueInComputeResourcePolicy(q.queueName)
+ )
+ : [];
+ },
+ defaultQueue: (state, getters) => {
+ const defaultQueue = getters.queues.find((q) => q.isDefaultQueue);
+ if (defaultQueue) {
+ return defaultQueue;
+ } else if (getters.queues.length > 0) {
+ return getters.queues[0];
+ } else {
+ return null;
+ }
+ },
+ queueName: (state) => {
+ return state.experiment &&
+ state.experiment.userConfigurationData &&
+ state.experiment.userConfigurationData.computationalResourceScheduling
+ ? state.experiment.userConfigurationData.computationalResourceScheduling
+ .queueName
+ : null;
+ },
+ queue: (state, getters) => {
+ return getters.queues && getters.queueName
+ ? getters.queues.find((q) => q.queueName === getters.queueName)
+ : null;
+ },
+ getDefaultCPUCount: (state, getters) => (queue) => {
+ const batchQueueResourcePolicy = getters.batchQueueResourcePolicy;
+ if (batchQueueResourcePolicy) {
+ return Math.min(
+ batchQueueResourcePolicy.maxAllowedCores,
+ queue.defaultCPUCount
+ );
+ }
+ return queue.defaultCPUCount;
+ },
+ getDefaultNodeCount: (state, getters) => (queue) => {
+ const batchQueueResourcePolicy = getters.batchQueueResourcePolicy;
+ if (batchQueueResourcePolicy) {
+ return Math.min(
+ batchQueueResourcePolicy.maxAllowedNodes,
+ queue.defaultNodeCount
+ );
+ }
+ return queue.defaultNodeCount;
+ },
+ getDefaultWalltime: (state, getters) => (queue) => {
+ const batchQueueResourcePolicy = getters.batchQueueResourcePolicy;
+ if (batchQueueResourcePolicy) {
+ return Math.min(
+ batchQueueResourcePolicy.maxAllowedWalltime,
+ queue.defaultWalltime
+ );
+ }
+ return queue.defaultWalltime;
+ },
+ computeResourcePolicy: (state, getters) => {
+ if (!getters.groupResourceProfile || !getters.resourceHostId) {
+ return null;
+ }
+ return getters.groupResourceProfile.computeResourcePolicies.find(
+ (crp) => crp.computeResourceId === getters.resourceHostId
+ );
+ },
+ batchQueueResourcePolicies: (state, getters) => {
+ if (!getters.groupResourceProfile || !getters.resourceHostId) {
+ return null;
+ }
+ return getters.groupResourceProfile.batchQueueResourcePolicies.filter(
+ (bqrp) => bqrp.computeResourceId === getters.resourceHostId
+ );
+ },
+ batchQueueResourcePolicy: (state, getters) => {
+ if (!getters.batchQueueResourcePolicies || !getters.queueName) {
+ return null;
+ }
+ return getters.batchQueueResourcePolicies.find(
+ (bqrp) => bqrp.queuename === getters.queueName
+ );
+ },
+ maxAllowedCores: (state, getters) => {
+ if (!getters.queue) {
+ return 0;
+ }
+ const batchQueueResourcePolicy = getters.batchQueueResourcePolicy;
+ if (batchQueueResourcePolicy) {
+ return Math.min(
+ batchQueueResourcePolicy.maxAllowedCores,
+ getters.queue.maxProcessors
+ );
+ }
+ return getters.queue.maxProcessors;
+ },
+ maxAllowedNodes: (state, getters) => {
+ if (!getters.queue) {
+ return 0;
+ }
+ const batchQueueResourcePolicy = getters.batchQueueResourcePolicy;
+ if (batchQueueResourcePolicy) {
+ return Math.min(
+ batchQueueResourcePolicy.maxAllowedNodes,
+ getters.queue.maxNodes
+ );
+ }
+ return getters.queue.maxNodes;
+ },
+ maxAllowedWalltime: (state, getters) => {
+ if (!getters.queue) {
+ return 0;
+ }
+ const batchQueueResourcePolicy = getters.batchQueueResourcePolicy;
+ if (batchQueueResourcePolicy) {
+ return Math.min(
+ batchQueueResourcePolicy.maxAllowedWalltime,
+ getters.queue.maxRunTime
+ );
+ }
+ return getters.queue.maxRunTime;
+ },
+ maxMemory: (state, getters) => {
+ return getters.queue ? getters.queue.maxMemory : 0;
+ },
},
});