You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ya...@apache.org on 2026/04/25 05:16:49 UTC
(airavata-portals) 32/34: refactor(launcher): delete old experiment-editor surface
This is an automated email from the ASF dual-hosted git repository.
yasithdev pushed a commit to branch feat/generic-experiment-launcher
in repository https://gitbox.apache.org/repos/asf/airavata-portals.git
commit 9d90b2d58f8c03575e21076cd30080575a0cfeae
Author: yasithdev <ya...@icloud.com>
AuthorDate: Fri Apr 24 23:26:06 2026 -0400
refactor(launcher): delete old experiment-editor surface
Remove CreateExperimentContainer, EditExperimentContainer, ExperimentEditor,
ComputationalResourceSchedulingEditor, QueueSettingsEditor, GroupResourceProfileSelector,
entry-create-experiment.js, and entry-edit-experiment.js. Drop the matching view
functions (create_experiment, edit_experiment, applications, get_custom_template) and
orphaned imports from workspace/views.py. Replace the still-routed edit_experiment URL
with a 301 redirect to /workspace/launch (missed by Task 5). Drop create-experiment and
edit-experiment entries from vite.config.js.
---
.../ComputationalResourceSchedulingEditor.vue | 296 -----------
.../js/components/experiment/ExperimentEditor.vue | 423 ----------------
.../experiment/GroupResourceProfileSelector.vue | 114 -----
.../components/experiment/QueueSettingsEditor.vue | 544 ---------------------
.../js/containers/CreateExperimentContainer.vue | 99 ----
.../js/containers/EditExperimentContainer.vue | 78 ---
.../js/entry-create-experiment.js | 32 --
.../js/entry-edit-experiment.js | 25 -
.../django_airavata/apps/workspace/urls.py | 4 +-
.../django_airavata/apps/workspace/views.py | 120 -----
.../django_airavata/apps/workspace/vite.config.js | 2 -
11 files changed, 2 insertions(+), 1735 deletions(-)
diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ComputationalResourceSchedulingEditor.vue b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ComputationalResourceSchedulingEditor.vue
deleted file mode 100644
index 6e7c50454..000000000
--- a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ComputationalResourceSchedulingEditor.vue
+++ /dev/null
@@ -1,296 +0,0 @@
-<template>
- <div>
- <div class="row">
- <div class="col">
- <form-group
- label="Compute Resource"
- label-for="compute-resource"
- :feedback="getValidationFeedback('resource_host_id')"
- :state="getValidationState('resource_host_id')"
- >
- <select
- id="compute-resource"
- v-model="resource_host_id"
- class="form-select"
- required
- :state="getValidationState('resource_host_id')"
- :disabled="!computeResourceOptions || computeResourceOptions.length === 0"
- @change="computeResourceChanged(($event.target as HTMLSelectElement).value)"
- >
- <option :value="null" disabled>Select a Compute Resource</option>
- <option v-for="opt in computeResourceOptions" :key="opt.value" :value="opt.value">
- {{ opt.text }}
- </option>
- </select>
- </form-group>
- </div>
- </div>
- <div class="row">
- <div class="col">
- <QueueSettingsEditor
- v-if="appDeploymentId"
- v-model="data"
- :app-module-id="appModuleId"
- :app-deployment-id="appDeploymentId"
- :compute-resource-policy="selectedComputeResourcePolicy"
- :batch-queue-resource-policies="batchQueueResourcePolicies"
- @input="queueSettingsChanged"
- @valid="queueSettingsValidityChanged(true)"
- @invalid="queueSettingsValidityChanged(false)"
- >
- </QueueSettingsEditor>
- </div>
- </div>
- </div>
-</template>
-
-<script setup lang="ts">
-import { ref, computed, watch, onMounted } from "vue";
-import QueueSettingsEditor from "./QueueSettingsEditor.vue";
-import { errors, models, services, utils as apiUtils } from "django-airavata-api";
-import { utils } from "django-airavata-common-ui";
-
-type ComputationalResourceSchedulingModel = InstanceType<typeof models.ComputationalResourceSchedulingModel>;
-
-interface ComputeResourceOption {
- value: string;
- text: string;
-}
-
-interface WorkspacePreferences {
- most_recent_compute_resource_id?: string | null;
-}
-
-interface GroupResourceProfileData {
- group_resource_profile_id: string;
- compute_resource_policies: Array<{ compute_resource_id: string; [key: string]: unknown }>;
- batch_queue_resource_policies: Array<{ compute_resource_id: string; [key: string]: unknown }>;
-}
-
-interface ApplicationDeployment {
- compute_host_id: string;
- app_deployment_id: string;
-}
-
-const props = defineProps<{
- modelValue: ComputationalResourceSchedulingModel;
- appModuleId: string;
- groupResourceProfileId: string;
-}>();
-
-const emit = defineEmits<{
- "update:modelValue": [value: ComputationalResourceSchedulingModel];
- input: [value: ComputationalResourceSchedulingModel];
- valid: [];
- invalid: [];
-}>();
-
-// VModelMixin inline
-function copyValue(value: ComputationalResourceSchedulingModel) {
- return value instanceof models.BaseModel
- ? (value as unknown as { clone: () => ComputationalResourceSchedulingModel }).clone()
- : value;
-}
-
-const data = ref<ComputationalResourceSchedulingModel>(copyValue(props.modelValue));
-
-watch(
- () => props.modelValue,
- (newValue) => {
- data.value = copyValue(newValue);
- },
- { deep: true },
-);
-
-watch(
- data,
- (newValue, oldValue) => {
- if (typeof props.modelValue === "object" && newValue === oldValue) {
- emit("update:modelValue", newValue);
- emit("input", newValue);
- } else if (
- (props.modelValue === null || typeof props.modelValue !== "object") &&
- newValue !== oldValue
- ) {
- emit("update:modelValue", newValue);
- emit("input", newValue);
- }
- },
- { deep: true },
-);
-
-const computeResources = ref<Record<string, string>>({});
-const applicationDeployments = ref<ApplicationDeployment[]>([]);
-const selectedGroupResourceProfileData = ref<GroupResourceProfileData | null>(null);
-const resource_host_id = ref<string | null>(props.modelValue.resource_host_id ?? null);
-const invalidQueueSettings = ref(false);
-const workspacePreferences = ref<WorkspacePreferences | null>(null);
-
-const localComputationalResourceScheduling = computed(() => data.value);
-
-const computeResourceOptions = computed<ComputeResourceOption[]>(() => {
- const options = applicationDeployments.value.map((dep) => ({
- value: dep.compute_host_id,
- text: dep.compute_host_id in computeResources.value ? computeResources.value[dep.compute_host_id] : "",
- }));
- options.sort((a, b) => a.text.localeCompare(b.text));
- return options;
-});
-
-const selectedComputeResourcePolicy = computed(() => {
- if (selectedGroupResourceProfileData.value === null) return null;
- return selectedGroupResourceProfileData.value.compute_resource_policies.find(
- (crp) => crp.compute_resource_id === localComputationalResourceScheduling.value.resource_host_id,
- ) ?? null;
-});
-
-interface BatchQueueResourcePolicyLike {
- compute_resource_id: string;
- queuename: string;
- maxAllowedCores: number;
- maxAllowedNodes: number;
- maxAllowedWalltime: number;
- resourcePolicyId?: string;
- [key: string]: unknown;
-}
-
-const batchQueueResourcePolicies = computed<BatchQueueResourcePolicyLike[] | null>(() => {
- if (selectedGroupResourceProfileData.value === null) return null;
- return selectedGroupResourceProfileData.value.batch_queue_resource_policies.filter(
- (bqrp) => bqrp.compute_resource_id === localComputationalResourceScheduling.value.resource_host_id,
- ) as BatchQueueResourcePolicyLike[];
-});
-
-const appDeploymentId = computed<string | null>(() => {
- if (!resource_host_id.value || applicationDeployments.value.length === 0) return null;
- const selectedDep = applicationDeployments.value.find(
- (dep) => dep.compute_host_id === resource_host_id.value,
- );
- if (!selectedDep) {
- throw new Error("Failed to find application deployment!");
- }
- return selectedDep.app_deployment_id;
-});
-
-const validation = computed(() => {
- const queueInfo = {};
- return localComputationalResourceScheduling.value.validate(queueInfo);
-});
-
-const valid = computed(() => !invalidQueueSettings.value && Object.keys(validation.value).length === 0);
-
-watch(computeResourceOptions, (newOptions) => {
- if (resource_host_id.value !== null && !newOptions.find((opt) => opt.value === resource_host_id.value)) {
- resource_host_id.value = null;
- }
- if (
- resource_host_id.value === null &&
- workspacePreferences.value?.most_recent_compute_resource_id &&
- newOptions.find((opt) => opt.value === workspacePreferences.value!.most_recent_compute_resource_id)
- ) {
- resource_host_id.value = workspacePreferences.value.most_recent_compute_resource_id;
- }
- if (resource_host_id.value === null && newOptions.length > 0) {
- resource_host_id.value = newOptions[0].value;
- }
- computeResourceChanged(resource_host_id.value);
-});
-
-watch(
- () => props.groupResourceProfileId,
- (newGroupResourceProfileId) => {
- loadApplicationDeployments(props.appModuleId, newGroupResourceProfileId);
- if (
- selectedGroupResourceProfileData.value &&
- selectedGroupResourceProfileData.value.group_resource_profile_id !== newGroupResourceProfileId
- ) {
- loadGroupResourceProfile();
- }
- },
-);
-
-onMounted(() => {
- loadWorkspacePreferences().then(() => {
- loadApplicationDeployments(props.appModuleId, props.groupResourceProfileId);
- });
- loadComputeResourceNames();
- loadGroupResourceProfile();
- validate();
-});
-
-function computeResourceChanged(selectedComputeResourceId: string | null) {
- data.value.resource_host_id = selectedComputeResourceId;
-}
-
-function loadApplicationDeployments(appModuleId: string, groupResourceProfileId: string) {
- services.ApplicationDeploymentService.list(
- { app_module_id: appModuleId, group_resource_profile_id: groupResourceProfileId },
- { ignoreErrors: true },
- )
- .then((deps: unknown) => {
- applicationDeployments.value = deps as ApplicationDeployment[];
- })
- .catch((error: unknown) => {
- if (!errors.ErrorUtils.isUnauthorizedError(error)) {
- return Promise.reject(error);
- }
- })
- .catch(apiUtils.FetchUtils.reportError);
-}
-
-function loadGroupResourceProfile() {
- services.ProjectResourceProfileService.retrieve(
- { lookup: props.groupResourceProfileId },
- { ignoreErrors: true },
- )
- .then((grp: unknown) => {
- selectedGroupResourceProfileData.value = grp as GroupResourceProfileData;
- })
- .catch((error: unknown) => {
- if (!errors.ErrorUtils.isUnauthorizedError(error)) {
- return Promise.reject(error);
- }
- })
- .catch(apiUtils.FetchUtils.reportError);
-}
-
-function loadComputeResourceNames() {
- services.ComputeResourceService.names().then(
- (names: unknown) => (computeResources.value = names as Record<string, string>),
- );
-}
-
-function loadWorkspacePreferences(): Promise<void> {
- return services.WorkspacePreferencesService.get().then(
- (prefs: unknown) => (workspacePreferences.value = prefs as WorkspacePreferences),
- );
-}
-
-function queueSettingsChanged() {
- localComputationalResourceScheduling.value.resource_host_id = resource_host_id.value;
- emit("input", data.value);
-}
-
-function queueSettingsValidityChanged(validValue: boolean) {
- invalidQueueSettings.value = !validValue;
- validate();
-}
-
-function validate() {
- if (!valid.value) {
- emit("invalid");
- } else {
- emit("valid");
- }
-}
-
-function getValidationFeedback(properties: string): unknown {
- return utils.getProperty(validation.value, properties);
-}
-
-function getValidationState(properties: string): boolean | null {
- return getValidationFeedback(properties) ? false : null;
-}
-</script>
-
-<style></style>
diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ExperimentEditor.vue b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ExperimentEditor.vue
deleted file mode 100644
index be28bbc44..000000000
--- a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ExperimentEditor.vue
+++ /dev/null
@@ -1,423 +0,0 @@
-<template>
- <div>
- <UnsavedChangesGuard :dirty="dirty" />
- <div class="row">
- <div class="col-auto me-auto">
- <h1 class="h4 mb-4">
- <div v-if="appModule" class="application-name text-muted text-uppercase">
- <i class="fa fa-code" aria-hidden="true"></i>
- {{ appModule.app_module_name }}
- </div>
- <slot name="title">Experiment Editor</slot>
- </h1>
- </div>
- <div class="col-auto">
- <ShareButton
- ref="shareButton"
- :entity-id="localExperiment.experiment_id"
- :entity-label="'Experiment'"
- :parent-entity-id="localExperiment.project_id"
- :parent-entity-label="'Project'"
- :auto-add-default-gateway-users-group="false"
- />
- </div>
- </div>
- <form novalidate>
- <div class="row">
- <div class="col">
- <form-group
- label="Experiment Name"
- label-for="experiment-name"
- :feedback="getValidationFeedback('experiment_name')"
- :state="getValidationState('experiment_name')"
- >
- <input
- id="experiment-name"
- v-model="localExperiment.experiment_name"
- class="form-control"
- type="text"
- required
- placeholder="Experiment name"
- :state="getValidationState('experiment_name')"
- />
- </form-group>
- <ExperimentDescriptionEditor v-model="localExperiment.description" />
- </div>
- </div>
- <div class="row">
- <div class="col">
- <form-group
- label="Project"
- label-for="project"
- :feedback="getValidationFeedback('project_id')"
- :state="getValidationState('project_id')"
- >
- <select
- id="project"
- v-model="localExperiment.project_id"
- class="form-select"
- required
- :state="getValidationState('project_id')"
- >
- <option :value="null" disabled>Select a Project</option>
- <optgroup label="My Projects">
- <option
- v-for="project in myProjectOptions"
- :key="project.value"
- :value="project.value"
- >
- {{ project.text }}
- </option>
- </optgroup>
- <optgroup label="Projects Shared With Me">
- <option
- v-for="project in sharedProjectOptions"
- :key="project.value"
- :value="project.value"
- >
- {{ project.text }}
- </option>
- </optgroup>
- </select>
- </form-group>
- </div>
- </div>
- <div class="row">
- <div class="col">
- <WorkspaceNoticesManagementContainer
- v-if="appInterface && (appInterface as Record<string, unknown>).application_description"
- class="mt-2"
- :data="[{ notificationMessage: (appInterface as Record<string, unknown>).application_description as string | undefined }]"
- />
- </div>
- </div>
- <div class="row">
- <div class="col">
- <h1 class="h4 mt-2 mb-4">Application Configuration</h1>
- </div>
- </div>
- <div class="row">
- <div class="col">
- <div class="card border-default">
- <div class="card-body">
- <h2 class="h6 mb-3">Application Inputs</h2>
- <transition-group name="fade">
- <InputEditorContainer
- v-for="experimentInput in localExperiment.experiment_inputs"
- v-show="experimentInput.show"
- :key="experimentInput.name"
- v-model="experimentInput.value"
- :experiment-input="experimentInput"
- :experiment="localExperiment"
- @invalid="recordInvalidInputEditorValue(experimentInput.name)"
- @valid="recordValidInputEditorValue(experimentInput.name)"
- @input="inputValueChanged"
- @uploadstart="uploadStart(experimentInput.name)"
- @uploadend="uploadEnd(experimentInput.name)"
- />
- </transition-group>
- </div>
- </div>
- </div>
- </div>
- <GroupResourceProfileSelector
- v-model="localExperiment.user_configuration_data.group_resource_profile_id"
- @invalid="invalidGroupResourceProfileSelector = true"
- @valid="invalidGroupResourceProfileSelector = false"
- >
- </GroupResourceProfileSelector>
- <div class="row">
- <div class="col">
- <ComputationalResourceSchedulingEditor
- v-if="localExperiment.user_configuration_data.group_resource_profile_id"
- v-model="localExperiment.user_configuration_data.computational_resource_scheduling"
- :app-module-id="appModule.app_module_id"
- :group-resource-profile-id="
- localExperiment.user_configuration_data.group_resource_profile_id
- "
- @invalid="invalidComputationalResourceSchedulingEditor = true"
- @valid="invalidComputationalResourceSchedulingEditor = false"
- >
- </ComputationalResourceSchedulingEditor>
- </div>
- </div>
- <div class="row">
- <div class="col">
- <div class="mb-3" label="Email Settings">
- <div class="form-check">
- <input
- v-model="localExperiment.enable_email_notification"
- class="form-check-input"
- type="checkbox"
- />
- Receive email notification of experiment status
- </div>
- </div>
- </div>
- </div>
- <div class="row">
- <div id="col-exp-buttons" class="col">
- <button
- class="btn btn-success btn-sm"
- :disabled="isSaveDisabled"
- @click="saveAndLaunchExperiment"
- >
- Save and Launch
- </button>
- <button class="btn btn-primary btn-sm" :disabled="isSaveDisabled" @click="saveExperiment">
- Save
- </button>
- </div>
- </div>
- </form>
- </div>
-</template>
-
-<script setup lang="ts">
-import { ref, computed, watch, onMounted } from "vue";
-import ComputationalResourceSchedulingEditor from "./ComputationalResourceSchedulingEditor.vue";
-import ExperimentDescriptionEditor from "./ExperimentDescriptionEditor.vue";
-import GroupResourceProfileSelector from "./GroupResourceProfileSelector.vue";
-import InputEditorContainer from "./input-editors/InputEditorContainer.vue";
-import { models, services } from "django-airavata-api";
-import { components, utils } from "django-airavata-common-ui";
-import WorkspaceNoticesManagementContainer from "../notices/WorkspaceNoticesManagementContainer.vue";
-
-const ShareButton = components.ShareButton;
-const UnsavedChangesGuard = components.UnsavedChangesGuard;
-
-type Experiment = InstanceType<typeof models.Experiment>;
-type ApplicationModule = InstanceType<typeof models.ApplicationModule>;
-type ApplicationInterfaceDefinition = InstanceType<typeof models.ApplicationInterfaceDefinition>;
-
-interface ProjectOption {
- value: string;
- text: string;
-}
-
-const props = defineProps<{
- experiment: Experiment;
- appModule: ApplicationModule;
- appInterface: ApplicationInterfaceDefinition;
-}>();
-
-const emit = defineEmits<{
- saved: [experiment: Experiment];
- savedAndLaunched: [experiment: Experiment];
-}>();
-
-const shareButton = ref<{ mergeAndSave: (_id: string) => Promise<unknown> } | null>(null);
-
-const projects = ref<unknown[]>([]);
-const localExperiment = ref<Experiment>(props.experiment.clone() as Experiment);
-const invalidInputs = ref<string[]>([]);
-const invalidComputationalResourceSchedulingEditor = ref(false);
-const invalidGroupResourceProfileSelector = ref(false);
-const edited = ref(false);
-const saved_ = ref(false);
-const uploadingInputs = ref<string[]>([]);
-
-const sharedProjectOptions = computed<ProjectOption[]>(() =>
- (projects.value as Array<Record<string, unknown>>)
- .filter((p) => !p.is_owner)
- .map((project) => ({
- value: project.project_id as string,
- text: project.name + (!project.is_owner ? " (owned by " + project.owner + ")" : ""),
- })),
-);
-
-const myProjectOptions = computed<ProjectOption[]>(() =>
- (projects.value as Array<Record<string, unknown>>)
- .filter((p) => p.is_owner)
- .map((project) => ({
- value: project.project_id as string,
- text: project.name as string,
- })),
-);
-
-const valid = computed(() => {
- const validation = localExperiment.value.validate();
- return (
- Object.keys(validation).length === 0 &&
- invalidInputs.value.length === 0 &&
- !invalidComputationalResourceSchedulingEditor.value &&
- !invalidGroupResourceProfileSelector.value
- );
-});
-
-const isSaveDisabled = computed(() => !valid.value || hasUploadingInputs.value);
-
-const dirty = computed(() => edited.value && !saved_.value);
-
-const hasUploadingInputs = computed(() => uploadingInputs.value.length > 0);
-
-watch(
- () => props.experiment,
- (newValue) => {
- localExperiment.value = newValue.clone() as Experiment;
- },
-);
-
-watch(
- localExperiment,
- () => {
- edited.value = true;
- },
- { deep: true },
-);
-
-watch(
- () => (props.experiment as unknown as Record<string, unknown>).experiment_inputs,
- () => {
- experimentInputsChanged();
- },
- { deep: true },
-);
-
-watch(
- () => {
- const ucd = (props.experiment as unknown as {
- user_configuration_data?: {
- computational_resource_scheduling?: { resource_host_id?: string };
- };
- }).user_configuration_data;
- return ucd?.computational_resource_scheduling?.resource_host_id;
- },
- () => {
- resourceHostIdChanged();
- },
-);
-
-onMounted(() => {
- services.ProjectService.listAll().then((projs: unknown) => {
- projects.value = projs as unknown[];
- if (!localExperiment.value.project_id) {
- services.WorkspacePreferencesService.get().then((workspacePreferences: unknown) => {
- const prefs = workspacePreferences as Record<string, unknown>;
- if (!localExperiment.value.project_id) {
- localExperiment.value.project_id = prefs.most_recent_project_id as string;
- }
- });
- }
- });
-});
-
-function saveExperiment() {
- return saveOrUpdateExperiment().then((experiment: Experiment) => {
- localExperiment.value = experiment;
- emit("saved", experiment);
- });
-}
-
-function saveAndLaunchExperiment() {
- return saveOrUpdateExperiment().then((experiment: Experiment) => {
- localExperiment.value = experiment;
- return services.ExperimentService.launch({
- lookup: experiment.experiment_id,
- }).then(() => {
- emit("savedAndLaunched", experiment);
- });
- });
-}
-
-function saveOrUpdateExperiment(): Promise<Experiment> {
- if (localExperiment.value.experiment_id) {
- return services.ExperimentService.update({
- lookup: localExperiment.value.experiment_id,
- data: localExperiment.value,
- }).then((experiment: unknown) => {
- saved_.value = true;
- return experiment as Experiment;
- });
- } else {
- return services.ExperimentService.create({
- data: localExperiment.value,
- }).then((experiment: unknown) => {
- const exp = experiment as Experiment;
- saved_.value = true;
- return shareButton.value!
- .mergeAndSave(exp.experiment_id)
- .then(() => exp);
- });
- }
-}
-
-function getValidationFeedback(properties: string): unknown {
- return utils.getProperty(localExperiment.value.validate(), properties);
-}
-
-function getValidationState(properties: string): boolean | null {
- return getValidationFeedback(properties) ? false : null;
-}
-
-function recordInvalidInputEditorValue(experimentInputName: string) {
- if (!invalidInputs.value.includes(experimentInputName)) {
- invalidInputs.value.push(experimentInputName);
- }
-}
-
-function recordValidInputEditorValue(experimentInputName: string) {
- if (invalidInputs.value.includes(experimentInputName)) {
- const index = invalidInputs.value.indexOf(experimentInputName);
- invalidInputs.value.splice(index, 1);
- }
-}
-
-function uploadStart(experimentInputName: string) {
- if (!uploadingInputs.value.includes(experimentInputName)) {
- uploadingInputs.value.push(experimentInputName);
- }
-}
-
-function uploadEnd(experimentInputName: string) {
- if (uploadingInputs.value.includes(experimentInputName)) {
- const index = uploadingInputs.value.indexOf(experimentInputName);
- uploadingInputs.value.splice(index, 1);
- }
-}
-
-function inputValueChanged() {
- (localExperiment.value as unknown as { evaluateInputDependencies: () => void }).evaluateInputDependencies();
-}
-
-// Inline debounce wrapper for calculateQueueSettings
-let calcTimer: ReturnType<typeof setTimeout> | undefined;
-function calculateQueueSettings() {
- clearTimeout(calcTimer);
- calcTimer = setTimeout(async () => {
- const queueSettingsUpdate = await services.QueueSettingsCalculatorService.calculate(
- {
- lookup: (props.appInterface as unknown as Record<string, unknown>).queue_settings_calculator_id,
- data: localExperiment.value,
- },
- { showSpinner: false },
- );
- Object.assign(
- (localExperiment.value as unknown as Record<string, unknown> & { user_configuration_data: Record<string, unknown> }).user_configuration_data.computationalResourceScheduling as object,
- queueSettingsUpdate,
- );
- }, 500);
-}
-
-function experimentInputsChanged() {
- if ((props.appInterface as unknown as Record<string, unknown>).queue_settings_calculator_id) {
- calculateQueueSettings();
- }
-}
-
-function resourceHostIdChanged() {
- if ((props.appInterface as unknown as Record<string, unknown>).queue_settings_calculator_id) {
- calculateQueueSettings();
- }
-}
-</script>
-
-<style>
-.application-name {
- font-size: 12px;
-}
-
-#col-exp-buttons {
- text-align: right;
-}
-</style>
diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/GroupResourceProfileSelector.vue b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/GroupResourceProfileSelector.vue
deleted file mode 100644
index ca3a591d4..000000000
--- a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/GroupResourceProfileSelector.vue
+++ /dev/null
@@ -1,114 +0,0 @@
-<template>
- <div class="row">
- <div class="col">
- <div class="mb-3" label="Allocation" label-for="group-resource-profile">
- <select
- id="group-resource-profile"
- v-model="groupResourceProfileId"
- class="form-select"
- required
- @change="emitValueChanged"
- >
- <option :value="null" disabled>Select an allocation</option>
- <option
- v-for="option in groupResourceProfileOptions"
- :key="option.value"
- :value="option.value"
- >
- {{ option.text }}
- </option>
- </select>
- </div>
- </div>
- </div>
-</template>
-
-<script setup lang="ts">
-import { ref, computed, onMounted } from "vue";
-import { services } from "django-airavata-api";
-
-interface GroupResourceProfile {
- group_resource_profile_id: string;
- group_resource_profile_name: string;
-}
-
-interface WorkspacePreferences {
- most_recent_project_resource_profile_id?: string;
-}
-
-const props = defineProps<{
- modelValue?: string | null;
-}>();
-
-const emit = defineEmits<{
- "update:modelValue": [value: string | null];
- valid: [];
- invalid: [];
-}>();
-
-const groupResourceProfileId = ref<string | null>(props.modelValue ?? null);
-const groupResourceProfiles = ref<GroupResourceProfile[]>([]);
-const workspacePreferences = ref<WorkspacePreferences | null>(null);
-
-const groupResourceProfileOptions = computed(() => {
- if (groupResourceProfiles.value.length > 0) {
- const options = groupResourceProfiles.value.map((grp) => ({
- value: grp.group_resource_profile_id,
- text: grp.group_resource_profile_name,
- }));
- options.sort((a, b) => a.text.localeCompare(b.text));
- return options;
- }
- return [];
-});
-
-const valid = computed(() => !!groupResourceProfileId.value);
-
-function emitValueChanged() {
- validate();
- emit("update:modelValue", groupResourceProfileId.value);
-}
-
-function validate() {
- if (!valid.value) {
- emit("invalid");
- } else {
- emit("valid");
- }
-}
-
-function selectedValueInGroupResourceProfileList(profiles: GroupResourceProfile[]) {
- return profiles.map((grp) => grp.group_resource_profile_id).indexOf(props.modelValue ?? "") >= 0;
-}
-
-function loadGroupResourceProfiles() {
- return services.ProjectResourceProfileService.list().then(
- (profiles: GroupResourceProfile[]) => {
- groupResourceProfiles.value = profiles;
- if (
- (!props.modelValue || !selectedValueInGroupResourceProfileList(profiles)) &&
- groupResourceProfiles.value.length > 0
- ) {
- // automatically select the last one user selected
- groupResourceProfileId.value =
- workspacePreferences.value?.most_recent_project_resource_profile_id ?? null;
- emitValueChanged();
- }
- },
- );
-}
-
-function loadWorkspacePreferences() {
- return services.WorkspacePreferencesService.get().then(
- (prefs: WorkspacePreferences) => (workspacePreferences.value = prefs),
- );
-}
-
-onMounted(async () => {
- await loadWorkspacePreferences();
- await loadGroupResourceProfiles();
- validate();
-});
-</script>
-
-<style></style>
diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/QueueSettingsEditor.vue b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/QueueSettingsEditor.vue
deleted file mode 100644
index d5255364d..000000000
--- a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/QueueSettingsEditor.vue
+++ /dev/null
@@ -1,544 +0,0 @@
-<template>
- <div v-if="showQueueSettings">
- <div class="row">
- <div class="col">
- <div :class="['card border-default', { 'border-danger': !valid, 'is-disabled': disabled }]">
- <a
- class="card-link text-dark"
- :disabled="disabled"
- @click="showConfiguration = !showConfiguration"
- >
- <div class="card-body">
- <h5 class="card-title mb-4">Settings for queue {{ data.queue_name }}</h5>
- <div class="row">
- <div class="col">
- <h3 class="h5 mb-0">
- {{ data.node_count }}
- </h3>
- <span class="text-muted text-uppercase">NODE COUNT</span>
- </div>
- <div class="col">
- <h3 class="h5 mb-0">
- {{ data.total_cpu_count }}
- </h3>
- <span class="text-muted text-uppercase">CORE COUNT</span>
- </div>
- <div class="col">
- <h3 class="h5 mb-0">{{ data.wall_time_limit }} minutes</h3>
- <span class="text-muted text-uppercase">TIME LIMIT</span>
- </div>
- <div v-if="maxPhysicalMemory > 0" class="col">
- <h3 class="h5 mb-0">{{ data.total_physical_memory }} MB</h3>
- <span class="text-muted text-uppercase">PHYSICAL MEMORY</span>
- </div>
- </div>
- </div>
- </a>
- </div>
- </div>
- </div>
- <div v-if="showConfiguration">
- <div class="row">
- <div class="col">
- <form-group
- label="Select a Queue"
- label-for="queue"
- :invalid-feedback="getValidationFeedback('queue_name')"
- :state="getValidationState('queue_name')"
- >
- <select
- id="queue"
- v-model="data.queue_name"
- class="form-select"
- required
- @change="queueChanged"
- >
- <option v-for="opt in queueOptions" :key="opt.value" :value="opt.value">
- {{ opt.text }}
- </option>
- </select>
- <small class="form-text text-muted">{{ queueDescription }}</small>
- </form-group>
- </div>
- <div class="d-flex flex-row">
- <div class="flex-fill">
- <form-group
- label="Node Count"
- label-for="node-count"
- :invalid-feedback="getValidationFeedback('nodeCount')"
- :state="getValidationState('nodeCount', true)"
- >
- <input
- id="node-count"
- v-model="data.node_count"
- class="form-control"
- type="number"
- min="1"
- :max="maxNodes"
- required
- @input="nodeCountChanged"
- />
- <small class="form-text text-muted">
- <i class="fa fa-info-circle" aria-hidden="true"></i>
- Max Allowed Nodes = {{ maxNodes }}
- </small>
- </form-group>
- </div>
- <div class="flex-fill">
- <form-group
- label="Total Core Count"
- label-for="core-count"
- :invalid-feedback="getValidationFeedback('totalCPUCount')"
- :state="getValidationState('totalCPUCount', true)"
- >
- <input
- id="core-count"
- v-model="data.total_cpu_count"
- class="form-control"
- type="number"
- min="1"
- :max="maxCPUCount"
- required
- @input="cpuCountChanged"
- />
- <small class="form-text text-muted">
- <i class="fa fa-info-circle" aria-hidden="true"></i>
- Max Allowed Cores = {{ maxCPUCount
- }}<template v-if="selectedQueueDefault && selectedQueueDefault.cpu_per_node > 0"
- >. There are {{ selectedQueueDefault.cpu_per_node }} cores per node.
- </template>
- </small>
- </form-group>
- </div>
- </div>
- <div
- v-if="selectedQueueDefault && selectedQueueDefault.cpu_per_node > 0"
- class="d-flex flex-column"
- >
- <div
- class="flex-fill"
- style="
- border: 1px solid #6c757d;
- border-top-right-radius: 10px;
- margin-top: 51px;
- border-left-width: 0px;
- border-bottom-width: 0px;
- margin-right: 15px;
- "
- ></div>
- <button
- class="btn btn-sm btn-outline-secondary rounded-pill"
- @click="enableNodeCountToCpuCheck = !enableNodeCountToCpuCheck"
- >
- <i v-if="enableNodeCountToCpuCheck" class="fa fa-lock" aria-hidden="true"></i>
- <i v-else class="fa fa-unlock" aria-hidden="true"></i>
- </button>
- <div
- class="flex-fill"
- style="
- border: 1px solid #6c757d;
- border-bottom-right-radius: 10px;
- margin-bottom: 57px;
- border-left-width: 0px;
- border-top-width: 0px;
- margin-right: 15px;
- "
- ></div>
- </div>
- </div>
- <form-group
- label="Wall Time Limit"
- label-for="walltime-limit"
- :invalid-feedback="getValidationFeedback('wallTimeLimit')"
- :state="getValidationState('wallTimeLimit', true)"
- >
- <div class="input-group">
- <input
- id="walltime-limit"
- v-model="data.wall_time_limit"
- class="form-control"
- type="number"
- min="1"
- :max="maxWalltime"
- required
- />
- <span class="input-group-text">minutes</span>
- </div>
- <small class="form-text text-muted">
- <i class="fa fa-info-circle" aria-hidden="true"></i>
- Max Allowed Wall Time = {{ maxWalltime }} minutes
- </small>
- </form-group>
- <form-group
- v-if="maxPhysicalMemory > 0"
- label="Total Physical Memory"
- label-for="total-physical-memory"
- :invalid-feedback="getValidationFeedback('totalPhysicalMemory')"
- :state="getValidationState('totalPhysicalMemory', true)"
- >
- <div class="input-group">
- <input
- id="total-physical-memory"
- v-model="data.total_physical_memory"
- class="form-control"
- type="number"
- min="0"
- :max="maxPhysicalMemory"
- />
- <span class="input-group-text">MB</span>
- </div>
- <small class="form-text text-muted">
- <i class="fa fa-info-circle" aria-hidden="true"></i>
- Max Physical Memory = {{ maxPhysicalMemory }} MB
- </small>
- </form-group>
- <div>
- <a class="text-secondary action-link" href="#" @click.prevent="showConfiguration = false">
- <i class="fa fa-times text-secondary" aria-hidden="true"></i>
- Hide Settings</a
- >
- </div>
- </div>
- </div>
-</template>
-
-<script setup lang="ts">
-import { ref, computed, watch, onMounted } from "vue";
-import { models, services } from "django-airavata-api";
-import { utils } from "django-airavata-common-ui";
-
-type ComputationalResourceSchedulingModel = InstanceType<typeof models.ComputationalResourceSchedulingModel>;
-type ComputeResourcePolicy = InstanceType<typeof models.ComputeResourcePolicy>;
-
-interface QueueDefault {
- queue_name: string;
- queue_description?: string;
- max_processors: number;
- max_nodes: number;
- max_run_time: number;
- max_memory: number;
- default_cpu_count: number;
- default_node_count: number;
- default_walltime: number;
- is_default_queue: boolean;
- cpu_per_node: number;
-}
-
-interface BatchQueueResourcePolicy {
- queuename: string;
- maxAllowedCores: number;
- maxAllowedNodes: number;
- maxAllowedWalltime: number;
- resourcePolicyId?: string;
-}
-
-interface ApplicationInterface {
- show_queue_settings: boolean;
- queue_settings_calculator_id?: string;
-}
-
-const props = defineProps<{
- modelValue: ComputationalResourceSchedulingModel;
- appDeploymentId: string;
- appModuleId: string;
- computeResourcePolicy?: ComputeResourcePolicy | null;
- batchQueueResourcePolicies?: BatchQueueResourcePolicy[] | null;
-}>();
-
-const emit = defineEmits<{
- "update:modelValue": [value: ComputationalResourceSchedulingModel];
- valid: [];
- invalid: [];
- input: [value: ComputationalResourceSchedulingModel];
-}>();
-
-// VModelMixin inline
-function copyValue(value: ComputationalResourceSchedulingModel) {
- return value instanceof models.BaseModel ? (value as unknown as { clone: () => ComputationalResourceSchedulingModel }).clone() : value;
-}
-
-const data = ref<ComputationalResourceSchedulingModel>(copyValue(props.modelValue));
-
-watch(
- () => props.modelValue,
- (newValue) => {
- data.value = copyValue(newValue);
- },
- { deep: true },
-);
-
-watch(
- data,
- (newValue, oldValue) => {
- if (typeof props.modelValue === "object" && newValue === oldValue) {
- emit("update:modelValue", newValue);
- emit("input", newValue);
- } else if (
- (props.modelValue === null || typeof props.modelValue !== "object") &&
- newValue !== oldValue
- ) {
- emit("update:modelValue", newValue);
- emit("input", newValue);
- }
- },
- { deep: true },
-);
-
-const showConfiguration = ref(false);
-const appDeploymentQueues = ref<QueueDefault[] | null>(null);
-const enableNodeCountToCpuCheck = ref(true);
-const applicationInterface = ref<ApplicationInterface | null>(null);
-
-const queueOptions = computed(() =>
- queueDefaults.value.map((queueDefault) => ({
- value: queueDefault.queue_name,
- text: queueDefault.queue_name,
- })),
-);
-
-const selectedQueueDefault = computed<QueueDefault | undefined>(() =>
- queueDefaults.value.find((queue) => queue.queue_name === data.value.queue_name),
-);
-
-const batchQueueResourcePolicyComputed = computed<BatchQueueResourcePolicy | null>(() => {
- if (!selectedQueueDefault.value) return null;
- return getBatchQueueResourcePolicy(selectedQueueDefault.value.queue_name);
-});
-
-const maxCPUCount = computed<number>(() => {
- if (!selectedQueueDefault.value) return 0;
- const bqrp = batchQueueResourcePolicyComputed.value;
- if (bqrp) {
- return Math.min(bqrp.maxAllowedCores, selectedQueueDefault.value.max_processors);
- }
- return selectedQueueDefault.value.max_processors;
-});
-
-const maxNodes = computed<number>(() => {
- if (!selectedQueueDefault.value) return 0;
- const bqrp = batchQueueResourcePolicyComputed.value;
- if (bqrp) {
- return Math.min(bqrp.maxAllowedNodes, selectedQueueDefault.value.max_nodes);
- }
- return selectedQueueDefault.value.max_nodes;
-});
-
-const maxWalltime = computed<number>(() => {
- if (!selectedQueueDefault.value) return 0;
- const bqrp = batchQueueResourcePolicyComputed.value;
- if (bqrp) {
- return Math.min(bqrp.maxAllowedWalltime, selectedQueueDefault.value.max_run_time);
- }
- return selectedQueueDefault.value.max_run_time;
-});
-
-const maxPhysicalMemory = computed<number>(() =>
- selectedQueueDefault.value ? selectedQueueDefault.value.max_memory : 0,
-);
-
-const queueDefaults = computed<QueueDefault[]>(() => {
- if (!appDeploymentQueues.value) return [];
- return appDeploymentQueues.value
- .filter((q) => isQueueInComputeResourcePolicy(q.queue_name))
- .sort((a, b) => {
- if (a.is_default_queue) return -1;
- if (b.is_default_queue) return 1;
- return a.queue_name.localeCompare(b.queue_name);
- });
-});
-
-const validation = computed(() => {
- if (!selectedQueueDefault.value) {
- return data.value.validate();
- }
- return data.value.validate(selectedQueueDefault.value, batchQueueResourcePolicyComputed.value);
-});
-
-const valid = computed(() => Object.keys(validation.value).length === 0);
-
-const showQueueSettings = computed<boolean>(() =>
- applicationInterface.value ? applicationInterface.value.show_queue_settings : false,
-);
-
-const disabled = computed<boolean>(
- () => !!(applicationInterface.value && applicationInterface.value.queue_settings_calculator_id),
-);
-
-const queueDescription = computed<string | null>(() =>
- selectedQueueDefault.value ? (selectedQueueDefault.value.queue_description ?? null) : null,
-);
-
-watch(enableNodeCountToCpuCheck, () => {
- if (enableNodeCountToCpuCheck.value) {
- nodeCountChanged();
- }
-});
-
-watch(
- () => props.appDeploymentId,
- () => {
- loadAppDeploymentQueues().then(() => setDefaultQueue());
- },
-);
-
-watch(batchQueueResourcePolicyComputed, (value, oldValue) => {
- if (value && (!oldValue || value.resourcePolicyId !== oldValue.resourcePolicyId)) {
- applyBatchQueueResourcePolicy();
- }
-});
-
-watch(
- () => props.computeResourcePolicy,
- () => {
- if (!isQueueInComputeResourcePolicy(data.value.queue_name)) {
- setDefaultQueue();
- }
- },
-);
-
-watch(
- () => props.modelValue,
- () => {
- validate();
- },
- { deep: true },
-);
-
-onMounted(() => {
- loadAppDeploymentQueues().then(() => {
- if (!props.modelValue.queue_name) {
- setDefaultQueue();
- }
- });
- validate();
- loadApplicationInterface();
-});
-
-function queueChanged(event: Event) {
- const queueName = (event.target as HTMLSelectElement).value;
- const queueDefault = queueDefaults.value.find((queue) => queue.queue_name === queueName);
- if (queueDefault) {
- data.value.total_cpu_count = getDefaultCPUCount(queueDefault);
- data.value.node_count = getDefaultNodeCount(queueDefault);
- data.value.wall_time_limit = getDefaultWalltime(queueDefault);
- if (maxPhysicalMemory.value === 0) {
- data.value.total_physical_memory = 0;
- }
- }
-}
-
-function validate() {
- if (!valid.value) {
- emit("invalid");
- } else {
- emit("valid");
- }
-}
-
-function loadAppDeploymentQueues(): Promise<void> {
- return services.ApplicationDeploymentService.getQueues({
- lookup: props.appDeploymentId,
- }).then((queueDefaults: unknown) => {
- appDeploymentQueues.value = queueDefaults as QueueDefault[];
- });
-}
-
-function setDefaultQueue() {
- if (queueDefaults.value.length === 0) {
- data.value.queue_name = null;
- return;
- }
- const defaultQueue = queueDefaults.value[0];
- data.value.queue_name = defaultQueue.queue_name;
- data.value.total_cpu_count = getDefaultCPUCount(defaultQueue);
- data.value.node_count = getDefaultNodeCount(defaultQueue);
- data.value.wall_time_limit = getDefaultWalltime(defaultQueue);
- if (maxPhysicalMemory.value === 0) {
- data.value.total_physical_memory = 0;
- }
-}
-
-function isQueueInComputeResourcePolicy(queue_name: string): boolean {
- if (!props.computeResourcePolicy) return true;
- return (props.computeResourcePolicy as unknown as { allowedBatchQueues: string[] }).allowedBatchQueues.includes(queue_name);
-}
-
-function getBatchQueueResourcePolicy(queueName: string): BatchQueueResourcePolicy | null {
- if (!props.batchQueueResourcePolicies || props.batchQueueResourcePolicies.length === 0) {
- return null;
- }
- return props.batchQueueResourcePolicies.find((bqrp) => bqrp.queuename === queueName) ?? null;
-}
-
-function getDefaultCPUCount(queueDefault: QueueDefault): number {
- const bqrp = batchQueueResourcePolicyComputed.value;
- if (bqrp) {
- return Math.min(bqrp.maxAllowedCores, queueDefault.default_cpu_count);
- }
- return queueDefault.default_cpu_count;
-}
-
-function getDefaultNodeCount(queueDefault: QueueDefault): number {
- const bqrp = batchQueueResourcePolicyComputed.value;
- if (bqrp) {
- return Math.min(bqrp.maxAllowedNodes, queueDefault.default_node_count);
- }
- return queueDefault.default_node_count;
-}
-
-function getDefaultWalltime(queueDefault: QueueDefault): number {
- const bqrp = batchQueueResourcePolicyComputed.value;
- if (bqrp) {
- return Math.min(bqrp.maxAllowedWalltime, queueDefault.default_walltime);
- }
- return queueDefault.default_walltime;
-}
-
-function getValidationFeedback(properties: string): unknown {
- return utils.getProperty(validation.value, properties);
-}
-
-function getValidationState(properties: string, showValidState?: boolean): boolean | null {
- return getValidationFeedback(properties) ? false : showValidState ? true : null;
-}
-
-function applyBatchQueueResourcePolicy() {
- if (selectedQueueDefault.value) {
- data.value.total_cpu_count = Math.min(data.value.total_cpu_count, maxCPUCount.value);
- data.value.node_count = Math.min(data.value.node_count, maxNodes.value);
- data.value.wall_time_limit = Math.min(data.value.wall_time_limit, maxWalltime.value);
- }
-}
-
-function nodeCountChanged() {
- if (enableNodeCountToCpuCheck.value && selectedQueueDefault.value && selectedQueueDefault.value.cpu_per_node > 0) {
- const nodeCount = parseInt(String(data.value.node_count));
- data.value.total_cpu_count = Math.min(
- nodeCount * selectedQueueDefault.value.cpu_per_node,
- maxCPUCount.value,
- );
- }
-}
-
-function cpuCountChanged() {
- if (enableNodeCountToCpuCheck.value && selectedQueueDefault.value && selectedQueueDefault.value.cpu_per_node > 0) {
- const cpuCount = parseInt(String(data.value.total_cpu_count));
- if (cpuCount > 0) {
- data.value.node_count = Math.min(
- Math.ceil(cpuCount / selectedQueueDefault.value.cpu_per_node),
- maxNodes.value,
- );
- }
- }
-}
-
-function loadApplicationInterface() {
- services.ApplicationModuleService.getApplicationInterface({
- lookup: props.appModuleId,
- }).then((iface: unknown) => {
- applicationInterface.value = iface as ApplicationInterface;
- });
-}
-</script>
-
-<style></style>
diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/CreateExperimentContainer.vue b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/CreateExperimentContainer.vue
deleted file mode 100644
index 7a0bfcaac..000000000
--- a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/CreateExperimentContainer.vue
+++ /dev/null
@@ -1,99 +0,0 @@
-<template>
- <experiment-editor
- v-if="experiment"
- :experiment="experiment"
- :app-module="appModule"
- :app-interface="appInterface"
- @saved="handleSavedExperiment"
- @saved-and-launched="handleSavedAndLaunchedExperiment"
- >
- <template #title>
- <span>Create a New Experiment</span>
- </template>
- </experiment-editor>
-</template>
-
-<script setup lang="ts">
-import { ref, onMounted } from "vue";
-import { services } from "django-airavata-api";
-import { notifications } from "django-airavata-common-ui";
-import ExperimentEditor from "../components/experiment/ExperimentEditor.vue";
-import urls from "../utils/urls";
-
-import { formatShort } from "django-airavata-common-ui/js/utils/dates.js";
-
-const props = defineProps<{
- appModuleId?: string;
- userInputValues?: Record<string, unknown> | null;
- experimentDataDir?: string | null;
-}>();
-
-// Convenience accessors
-const appModuleId = () => props.appModuleId;
-const userInputValues = () => props.userInputValues;
-const experimentDataDir = () => props.experimentDataDir;
-
-const experiment = ref<unknown>(null);
-const appModule = ref<unknown>(null);
-const appInterface = ref<unknown>(null);
-
-onMounted(() => {
- const modId = appModuleId();
- if (!modId) return;
- const loadAppModule = services.ApplicationModuleService.retrieve(
- { lookup: modId },
- { ignoreErrors: true },
- );
- const loadAppInterface = services.ApplicationModuleService.getApplicationInterface(
- { lookup: modId },
- { ignoreErrors: true },
- );
- Promise.all([loadAppModule, loadAppInterface])
- .then(([mod, iface]: [unknown, unknown]) => {
- const typedMod = mod as { app_module_name: string };
- const typedIface = iface as {
- createExperiment(): unknown;
- };
- const exp = typedIface.createExperiment() as {
- experiment_name: string;
- experiment_inputs: Array<{ name: string; value: unknown }>;
- user_configuration_data: { experiment_data_dir: string };
- };
- exp.experiment_name = typedMod.app_module_name + " on " + formatShort(new Date());
- appModule.value = mod;
- appInterface.value = iface;
- const inputValues = userInputValues();
- if (inputValues) {
- Object.keys(inputValues).forEach((k) => {
- const experimentInput = exp.experiment_inputs.find((inp) => inp.name === k);
- if (experimentInput) {
- experimentInput.value = inputValues[k];
- }
- });
- }
- const dataDir = experimentDataDir();
- if (dataDir) {
- exp.user_configuration_data.experiment_data_dir = dataDir;
- }
- experiment.value = exp;
- })
- .catch((error: unknown) => {
- notifications.NotificationList.addError(error);
- });
-});
-
-function handleSavedExperiment() {
- urls.navigateToExperimentsList("");
-}
-
-function handleSavedAndLaunchedExperiment(exp: unknown) {
- urls.navigateToViewExperiment("", exp as { experiment_id: string }, { launching: true });
-}
-</script>
-
-<style>
-/* style the containing div, in base.html template */
-.main-content-wrapper {
- background-color: #ffffff;
-}
-</style>
diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/EditExperimentContainer.vue b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/EditExperimentContainer.vue
deleted file mode 100644
index a5566322c..000000000
--- a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/EditExperimentContainer.vue
+++ /dev/null
@@ -1,78 +0,0 @@
-<template>
- <experiment-editor
- v-if="appModule"
- :experiment="experiment"
- :app-module="appModule"
- :app-interface="appInterface"
- @saved="handleSavedExperiment"
- @saved-and-launched="handleSavedAndLaunchedExperiment"
- >
- <template #title>
- <span>Edit Experiment</span>
- </template>
- </experiment-editor>
-</template>
-
-<script setup lang="ts">
-import { ref, onMounted } from "vue";
-import { errors, services } from "django-airavata-api";
-import { notifications } from "django-airavata-common-ui";
-import ExperimentEditor from "../components/experiment/ExperimentEditor.vue";
-import urls from "../utils/urls";
-
-const props = defineProps<{
- experimentId: string;
-}>();
-
-const experiment = ref<unknown>(null);
-const appModule = ref<unknown>(null);
-const appInterface = ref<unknown>(null);
-
-onMounted(() => {
- services.ExperimentService.retrieve({ lookup: props.experimentId })
- .then((exp: unknown) => {
- experiment.value = exp;
- const appInterfaceId = (exp as { execution_id: string }).execution_id;
- return services.ApplicationInterfaceService.retrieve(
- { lookup: appInterfaceId },
- { ignoreErrors: true },
- );
- })
- .then((iface: unknown) => {
- appInterface.value = iface;
- const appModuleId = (iface as { application_modules: string[] }).application_modules[0];
- return services.ApplicationModuleService.retrieve({ lookup: appModuleId });
- })
- .then((mod: unknown) => {
- appModule.value = mod;
- })
- .catch((error: unknown) => {
- const exp = experiment.value as { execution_id: string } | null;
- const message = errors.ErrorUtils.isNotFoundError(error)
- ? `Application interface (${exp?.execution_id}) was not found.
- If it has been deleted then you won't be able to edit this experiment.`
- : `Unable to load application interface (${exp?.execution_id}) or module`;
- notifications.NotificationList.add(
- new notifications.Notification({
- type: "ERROR",
- message,
- }),
- );
- });
-});
-
-function handleSavedExperiment() {
- urls.navigateToExperimentsList("");
-}
-
-function handleSavedAndLaunchedExperiment(exp: unknown) {
- urls.navigateToViewExperiment("", exp as { experiment_id: string }, { launching: true });
-}
-</script>
-
-<style>
-/* style the containing div, in base.html template */
-.main-content-wrapper {
- background-color: #ffffff;
-}
-</style>
diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-create-experiment.js b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-create-experiment.js
deleted file mode 100644
index 9d0d999b4..000000000
--- a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-create-experiment.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { h } from "vue";
-import { components, entry } from "django-airavata-common-ui";
-import CreateExperimentContainer from "./containers/CreateExperimentContainer.vue";
-import "../../scss/styles.scss";
-
-entry(({ createApp }) => {
- const el = document.getElementById("create-experiment");
- const appModuleId = el ? el.dataset.appModuleId || null : null;
- const userInputValues =
- el && el.dataset.userInputValues ? JSON.parse(el.dataset.userInputValues) : null;
- const experimentDataDir = el ? el.dataset.experimentDataDir || null : null;
- const app = createApp({
- data() {
- return {
- appModuleId,
- userInputValues,
- experimentDataDir,
- };
- },
- render() {
- return h(components.MainLayout, null, {
- default: () =>
- h(CreateExperimentContainer, {
- appModuleId: this.appModuleId,
- userInputValues: this.userInputValues,
- experimentDataDir: this.experimentDataDir,
- }),
- });
- },
- });
- app.mount("#create-experiment");
-});
diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-edit-experiment.js b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-edit-experiment.js
deleted file mode 100644
index 4332b258e..000000000
--- a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-edit-experiment.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { h } from "vue";
-import { components, entry } from "django-airavata-common-ui";
-import EditExperimentContainer from "./containers/EditExperimentContainer.vue";
-import "../../scss/styles.scss";
-
-entry(({ createApp }) => {
- const el = document.getElementById("edit-experiment");
- const experimentId = el ? el.dataset.experimentId || null : null;
- const app = createApp({
- data() {
- return {
- experimentId,
- };
- },
- render() {
- return h(components.MainLayout, null, {
- default: () =>
- h(EditExperimentContainer, {
- experimentId: this.experimentId,
- }),
- });
- },
- });
- app.mount("#edit-experiment");
-});
diff --git a/airavata-django-portal/django_airavata/apps/workspace/urls.py b/airavata-django-portal/django_airavata/apps/workspace/urls.py
index d493334b5..5e1218575 100644
--- a/airavata-django-portal/django_airavata/apps/workspace/urls.py
+++ b/airavata-django-portal/django_airavata/apps/workspace/urls.py
@@ -23,8 +23,8 @@ urlpatterns = [
),
re_path(
r"^projects/(?P<project_id>[^/]+)/experiments/(?P<experiment_id>[^/]+)/edit$",
- views.edit_experiment,
- name="edit_experiment",
+ RedirectView.as_view(url="/workspace/launch", permanent=True),
+ name="edit_experiment_redirect",
),
re_path(
r"^projects/(?P<project_id>[^/]+)/experiments/(?P<experiment_id>[^/]+)/$",
diff --git a/airavata-django-portal/django_airavata/apps/workspace/views.py b/airavata-django-portal/django_airavata/apps/workspace/views.py
index cfdf3010d..54ecdaa1a 100644
--- a/airavata-django-portal/django_airavata/apps/workspace/views.py
+++ b/airavata-django-portal/django_airavata/apps/workspace/views.py
@@ -1,22 +1,16 @@
import json
import logging
-from urllib.parse import urlparse
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
-from django.utils.module_loading import import_string
from rest_framework.renderers import JSONRenderer
-from django_airavata.apps.api import models
-from django_airavata.apps.api import user_storage as user_storage_sdk
from django_airavata.apps.api.views import (
- ApplicationModuleViewSet,
ExperimentSearchViewSet,
FullExperimentViewSet,
ProjectViewSet,
)
-from django_airavata.proto_compat import DataType
logger = logging.getLogger(__name__)
@@ -27,8 +21,6 @@ ENTRY_POINTS = {
"project-list": "static/django_airavata_workspace/js/entry-project-list.js",
"project-overview": "static/django_airavata_workspace/js/entry-project-overview.js",
"edit-project": "static/django_airavata_workspace/js/entry-edit-project.js",
- "create-experiment": "static/django_airavata_workspace/js/entry-create-experiment.js",
- "edit-experiment": "static/django_airavata_workspace/js/entry-edit-experiment.js",
"view-experiment": "static/django_airavata_workspace/js/entry-view-experiment.js",
"user-storage": "static/django_airavata_workspace/js/entry-user-storage.js",
"compute": "static/django_airavata_workspace/js/entry-compute.js",
@@ -110,19 +102,6 @@ def _create_default_project(request):
return project_id
-@login_required
-def applications(request):
- request.active_nav_item = "applications"
- return render(
- request,
- "django_airavata_workspace/base.html",
- {
- "bundle_name": "applications",
- "entry_point": ENTRY_POINTS["applications"],
- },
- )
-
-
@login_required
def new_application(request):
request.active_nav_item = "applications"
@@ -185,105 +164,6 @@ def project_overview(request, project_id):
)
-@login_required
-def create_experiment(request, app_module_id):
- request.active_nav_item = "applications"
-
- # User input files can be passed as query parameters
- # <input name>=<path/to/user_file>
- # and also as data product URIs
- # <input name>=<data product URI>
- app_interface = ApplicationModuleViewSet.as_view({"get": "application_interface"})(
- request, app_module_id=app_module_id
- )
- if app_interface.status_code != 200:
- raise Exception("Failed to load application module data: {}".format(app_interface.data["detail"]))
- user_input_values = {}
- for app_input in app_interface.data.get("application_inputs", []):
- if app_input["type"] == DataType.URI and app_input["name"] in request.GET:
- user_file_value = request.GET[app_input["name"]]
- try:
- user_file_url = urlparse(user_file_value)
- if user_file_url.scheme == "airavata-dp":
- dp_uri = user_file_value
- try:
- data_product = request.airavata_client.research.get_data_product(dp_uri)
- if user_storage_sdk.exists(request, data_product):
- user_input_values[app_input["name"]] = dp_uri
- except Exception:
- logger.exception(f"Failed checking data product uri: {dp_uri}", extra={"request": request})
- except ValueError:
- logger.exception(f"Invalid user file value: {user_file_value}", extra={"request": request})
- elif app_input["type"] == DataType.STRING and app_input["name"] in request.GET:
- name = app_input["name"]
- user_input_values[name] = request.GET[name]
- context = {
- "bundle_name": "create-experiment",
- "entry_point": ENTRY_POINTS["create-experiment"],
- "app_module_id": app_module_id,
- "user_input_values": json.dumps(user_input_values),
- }
- if "experiment-data-dir" in request.GET:
- context["experiment_data_dir"] = request.GET["experiment-data-dir"]
-
- template_path = "django_airavata_workspace/create_experiment.html"
- # Apply a custom application template if it exists
- custom_template_path, custom_context = get_custom_template(request, app_module_id)
- if custom_template_path is not None:
- logger.debug(f"Applying custom application template {custom_template_path}")
- template_path = custom_template_path
- context.update(custom_context)
-
- return render(request, template_path, context)
-
-
-@login_required
-def edit_experiment(request, project_id, experiment_id):
- request.active_nav_item = "projects"
-
- project = request.airavata_client.research.get_project(project_id)
- experiment = request.airavata_client.research.get_experiment(experiment_id)
- applicationInterface = request.airavata_client.research.get_application_interface(experiment.execution_id)
- app_module_id = applicationInterface.application_modules[0]
-
- breadcrumbs = [
- {"label": "Projects", "url": "/workspace/projects"},
- {"label": project.name, "url": f"/workspace/projects/{project_id}/"},
- {"label": "Experiments", "url": f"/workspace/projects/{project_id}/experiments"},
- {"label": "Edit Experiment", "url": None},
- ]
-
- context = {
- "bundle_name": "edit-experiment",
- "entry_point": ENTRY_POINTS["edit-experiment"],
- "experiment_id": experiment_id,
- "app_module_id": app_module_id,
- "project_id": project_id,
- "breadcrumbs_json": json.dumps(breadcrumbs),
- }
- template_path = "django_airavata_workspace/edit_experiment.html"
- custom_template_path, custom_context = get_custom_template(request, app_module_id)
- if custom_template_path is not None:
- logger.debug(f"Applying custom application template {custom_template_path}")
- template_path = custom_template_path
- context.update(custom_context)
-
- return render(request, template_path, context)
-
-
-def get_custom_template(request, app_module_id):
- template_path = None
- context = {}
- query = models.ApplicationTemplate.objects.filter(application_module_id=app_module_id)
- if query.exists():
- application_template = query.get()
- template_path = application_template.template_path
- for context_processor in application_template.context_processors.all():
- context_processor = import_string(context_processor.callable_path)
- context.update(context_processor(request))
- return template_path, context
-
-
@login_required
def view_experiment(request, project_id, experiment_id):
request.active_nav_item = "projects"
diff --git a/airavata-django-portal/django_airavata/apps/workspace/vite.config.js b/airavata-django-portal/django_airavata/apps/workspace/vite.config.js
index a73d02e6a..2d23675da 100644
--- a/airavata-django-portal/django_airavata/apps/workspace/vite.config.js
+++ b/airavata-django-portal/django_airavata/apps/workspace/vite.config.js
@@ -10,10 +10,8 @@ export default defineAppConfig({
dashboard: resolve(srcDir, "entry-dashboard.js"),
"project-list": resolve(srcDir, "entry-project-list.js"),
applications: resolve(srcDir, "entry-applications.js"),
- "create-experiment": resolve(srcDir, "entry-create-experiment.js"),
"view-experiment": resolve(srcDir, "entry-view-experiment.js"),
"experiment-list": resolve(srcDir, "entry-experiment-list.js"),
- "edit-experiment": resolve(srcDir, "entry-edit-experiment.js"),
"edit-project": resolve(srcDir, "entry-edit-project.js"),
"user-storage": resolve(srcDir, "entry-user-storage.js"),
compute: resolve(srcDir, "entry-compute.js"),