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:39 UTC
(airavata-portals) 22/34: feat(launcher): RuntimeInputs (profile-driven cr/partition/walltime)
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 67680b5da7ea056c9f1deb8abe8e9f692a4470ed
Author: yasithdev <ya...@icloud.com>
AuthorDate: Fri Apr 24 22:53:08 2026 -0400
feat(launcher): RuntimeInputs (profile-driven cr/partition/walltime)
---
.../js/components/launch/runtime/RuntimeInputs.vue | 80 ++++++++++++++++++++++
.../launch/runtime/RuntimeInputs.spec.ts | 43 ++++++++++++
2 files changed, 123 insertions(+)
diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/runtime/RuntimeInputs.vue b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/runtime/RuntimeInputs.vue
new file mode 100644
index 000000000..df1880417
--- /dev/null
+++ b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/runtime/RuntimeInputs.vue
@@ -0,0 +1,80 @@
+<template>
+ <div class="row g-2">
+ <div class="col-md-3">
+ <label class="form-label small">Compute resource</label>
+ <select
+ data-test="cr"
+ class="form-select form-select-sm"
+ :value="modelValue.compute_resource_id ?? ''"
+ @change="onCR(($event.target as HTMLSelectElement).value)"
+ >
+ <option value="" disabled>Choose…</option>
+ <option v-for="cr in profile.compute_resources" :key="cr.compute_resource_id" :value="cr.compute_resource_id">
+ {{ cr.name }}
+ </option>
+ </select>
+ </div>
+ <div class="col-md-3">
+ <label class="form-label small">Partition</label>
+ <select
+ data-test="partition"
+ class="form-select form-select-sm"
+ :disabled="!partitions.length"
+ :value="modelValue.partition ?? ''"
+ @change="emit('update:modelValue', { ...modelValue, partition: ($event.target as HTMLSelectElement).value })"
+ >
+ <option value="" disabled>Choose…</option>
+ <option v-for="p in partitions" :key="p.name" :value="p.name">{{ p.name }}</option>
+ </select>
+ </div>
+ <div class="col-md-2">
+ <label class="form-label small">Walltime</label>
+ <input
+ data-test="walltime"
+ class="form-control form-control-sm"
+ :value="modelValue.walltime"
+ @input="emit('update:modelValue', { ...modelValue, walltime: ($event.target as HTMLInputElement).value })"
+ />
+ </div>
+ <div class="col-md-2">
+ <label class="form-label small">Nodes</label>
+ <input
+ type="number" min="1"
+ data-test="nodes"
+ class="form-control form-control-sm"
+ :value="modelValue.nodes"
+ @input="emit('update:modelValue', { ...modelValue, nodes: Number(($event.target as HTMLInputElement).value) })"
+ />
+ </div>
+ <div class="col-md-2">
+ <label class="form-label small">CPUs / node</label>
+ <input
+ type="number" min="1"
+ data-test="cpus"
+ class="form-control form-control-sm"
+ :value="modelValue.cpus_per_node"
+ @input="emit('update:modelValue', { ...modelValue, cpus_per_node: Number(($event.target as HTMLInputElement).value) })"
+ />
+ </div>
+ </div>
+</template>
+
+<script setup lang="ts">
+import { computed } from "vue";
+import type { ResourceProfile, RuntimeChoice } from "django-airavata-common-ui/js/stores/launch-types";
+
+const props = defineProps<{ profile: ResourceProfile; modelValue: RuntimeChoice }>();
+const emit = defineEmits<{ "update:modelValue": [v: RuntimeChoice] }>();
+
+const partitions = computed(() => {
+ if (!props.modelValue.compute_resource_id) return [];
+ const cr = props.profile.compute_resources.find(
+ (c) => c.compute_resource_id === props.modelValue.compute_resource_id,
+ );
+ return cr?.partitions ?? [];
+});
+
+function onCR(id: string) {
+ emit("update:modelValue", { ...props.modelValue, compute_resource_id: id, partition: null });
+}
+</script>
diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/runtime/RuntimeInputs.spec.ts b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/runtime/RuntimeInputs.spec.ts
new file mode 100644
index 000000000..e38534a4f
--- /dev/null
+++ b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/runtime/RuntimeInputs.spec.ts
@@ -0,0 +1,43 @@
+import { mount } from "@vue/test-utils";
+import { describe, expect, it } from "vitest";
+import RuntimeInputs from "../../../../../js/components/launch/runtime/RuntimeInputs.vue";
+
+const PROFILE = {
+ project_id: "p1", allocation_id: "NSF-1",
+ compute_resources: [{
+ compute_resource_id: "bridges-2", name: "Bridges-2",
+ mapped_storage: { storage_id: "scratch", scratch_path: "/scratch/p1" },
+ partitions: [{ name: "RM", max_walltime: "48:00:00", max_nodes: 64, cpus_per_node: 128 }],
+ }],
+};
+
+describe("RuntimeInputs", () => {
+ it("populates compute resource dropdown from profile", () => {
+ const w = mount(RuntimeInputs, {
+ props: { profile: PROFILE, modelValue: { compute_resource_id: null, partition: null,
+ walltime: "01:00:00", nodes: 1, cpus_per_node: 1 } },
+ });
+ expect(w.find("select[data-test='cr']").findAll("option").map((o) => o.text())).toContain("Bridges-2");
+ });
+
+ it("emits update:modelValue when the resource changes", async () => {
+ const w = mount(RuntimeInputs, {
+ props: { profile: PROFILE, modelValue: { compute_resource_id: null, partition: null,
+ walltime: "01:00:00", nodes: 1, cpus_per_node: 1 } },
+ });
+ await w.find("select[data-test='cr']").setValue("bridges-2");
+ expect(w.emitted("update:modelValue")?.at(-1)).toEqual([
+ expect.objectContaining({ compute_resource_id: "bridges-2", partition: null }),
+ ]);
+ });
+
+ it("limits partition options to the chosen resource", async () => {
+ const w = mount(RuntimeInputs, {
+ props: { profile: PROFILE, modelValue: { compute_resource_id: "bridges-2", partition: null,
+ walltime: "01:00:00", nodes: 1, cpus_per_node: 1 } },
+ });
+ const opts = w.find("select[data-test='partition']").findAll("option").map((o) => o.text());
+ expect(opts).toContain("RM");
+ expect(opts).not.toContain("nonexistent");
+ });
+});