diff --git a/internal/routerConfig/routes.go b/internal/routerConfig/routes.go index b6137ddb..218384e0 100644 --- a/internal/routerConfig/routes.go +++ b/internal/routerConfig/routes.go @@ -305,30 +305,66 @@ func buildFilterPresets(query url.Values) map[string]any { if query.Get("numNodes") != "" { parts := strings.Split(query.Get("numNodes"), "-") if len(parts) == 2 { - a, e1 := strconv.Atoi(parts[0]) - b, e2 := strconv.Atoi(parts[1]) - if e1 == nil && e2 == nil { - filterPresets["numNodes"] = map[string]int{"from": a, "to": b} + if parts[0] == "lessthan" { + lt, lte := strconv.Atoi(parts[1]) + if lte == nil { + filterPresets["numNodes"] = map[string]int{"from": 1, "to": lt} + } + } else if parts[0] == "morethan" { + mt, mte := strconv.Atoi(parts[1]) + if mte == nil { + filterPresets["numNodes"] = map[string]int{"from": mt, "to": 0} + } + } else { + a, e1 := strconv.Atoi(parts[0]) + b, e2 := strconv.Atoi(parts[1]) + if e1 == nil && e2 == nil { + filterPresets["numNodes"] = map[string]int{"from": a, "to": b} + } } } } if query.Get("numHWThreads") != "" { parts := strings.Split(query.Get("numHWThreads"), "-") if len(parts) == 2 { - a, e1 := strconv.Atoi(parts[0]) - b, e2 := strconv.Atoi(parts[1]) - if e1 == nil && e2 == nil { - filterPresets["numHWThreads"] = map[string]int{"from": a, "to": b} + if parts[0] == "lessthan" { + lt, lte := strconv.Atoi(parts[1]) + if lte == nil { + filterPresets["numHWThreads"] = map[string]int{"from": 1, "to": lt} + } + } else if parts[0] == "morethan" { + mt, mte := strconv.Atoi(parts[1]) + if mte == nil { + filterPresets["numHWThreads"] = map[string]int{"from": mt, "to": 0} + } + } else { + a, e1 := strconv.Atoi(parts[0]) + b, e2 := strconv.Atoi(parts[1]) + if e1 == nil && e2 == nil { + filterPresets["numHWThreads"] = map[string]int{"from": a, "to": b} + } } } } if query.Get("numAccelerators") != "" { parts := strings.Split(query.Get("numAccelerators"), "-") if len(parts) == 2 { - a, e1 := strconv.Atoi(parts[0]) - b, e2 := strconv.Atoi(parts[1]) - if e1 == nil && e2 == nil { - filterPresets["numAccelerators"] = map[string]int{"from": a, "to": b} + if parts[0] == "lessthan" { + lt, lte := strconv.Atoi(parts[1]) + if lte == nil { + filterPresets["numAccelerators"] = map[string]int{"from": 1, "to": lt} + } + } else if parts[0] == "morethan" { + mt, mte := strconv.Atoi(parts[1]) + if mte == nil { + filterPresets["numAccelerators"] = map[string]int{"from": mt, "to": 0} + } + } else { + a, e1 := strconv.Atoi(parts[0]) + b, e2 := strconv.Atoi(parts[1]) + if e1 == nil && e2 == nil { + filterPresets["numAccelerators"] = map[string]int{"from": a, "to": b} + } } } } diff --git a/web/frontend/src/generic/Filters.svelte b/web/frontend/src/generic/Filters.svelte index 4031d0a5..162082c0 100644 --- a/web/frontend/src/generic/Filters.svelte +++ b/web/frontend/src/generic/Filters.svelte @@ -280,12 +280,24 @@ opts.push(`duration=morethan-${filters.duration.moreThan}`); if (filters.tags.length != 0) for (let tag of filters.tags) opts.push(`tag=${tag}`); - if (filters.numNodes.from && filters.numNodes.to) + if (filters.numNodes.from > 1 && filters.numNodes.to > 0) opts.push(`numNodes=${filters.numNodes.from}-${filters.numNodes.to}`); - if (filters.numHWThreads.from && filters.numHWThreads.to) + else if (filters.numNodes.from > 1 && filters.numNodes.to == 0) + opts.push(`numNodes=morethan-${filters.numNodes.from}`); + else if (filters.numNodes.from == 1 && filters.numNodes.to > 0) + opts.push(`numNodes=lessthan-${filters.numNodes.to}`); + if (filters.numHWThreads.from > 1 && filters.numHWThreads.to > 0) opts.push(`numHWThreads=${filters.numHWThreads.from}-${filters.numHWThreads.to}`); + else if (filters.numHWThreads.from > 1 && filters.numHWThreads.to == 0) + opts.push(`numHWThreads=morethan-${filters.numHWThreads.from}`); + else if (filters.numHWThreads.from == 1 && filters.numHWThreads.to > 0) + opts.push(`numHWThreads=lessthan-${filters.numHWThreads.to}`); if (filters.numAccelerators.from && filters.numAccelerators.to) opts.push(`numAccelerators=${filters.numAccelerators.from}-${filters.numAccelerators.to}`); + else if (filters.numAccelerators.from > 1 && filters.numAccelerators.to == 0) + opts.push(`numAccelerators=morethan-${filters.numAccelerators.from}`); + else if (filters.numAccelerators.from == 1 && filters.numAccelerators.to > 0) + opts.push(`numAccelerators=lessthan-${filters.numAccelerators.to}`); if (filters.node) opts.push(`node=${filters.node}`); if (filters.node && filters.nodeMatch != "eq") // "eq" is default-case opts.push(`nodeMatch=${filters.nodeMatch}`); @@ -490,21 +502,45 @@ {/if} - {#if filters.numNodes.from != null || filters.numNodes.to != null} + {#if filters.numNodes.from > 1 && filters.numNodes.to > 0} (isResourcesOpen = true)}> - Nodes: {filters.numNodes.from} - {filters.numNodes.to} + Nodes: {filters.numNodes.from} - {filters.numNodes.to} + + {:else if filters.numNodes.from > 1 && filters.numNodes.to == 0} + (isResourcesOpen = true)}> +  ≥ {filters.numNodes.from} Node(s) + + {:else if filters.numNodes.from == 1 && filters.numNodes.to > 0} + (isResourcesOpen = true)}> +  ≤ {filters.numNodes.to} Node(s) {/if} - {#if filters.numHWThreads.from != null || filters.numHWThreads.to != null} + {#if filters.numHWThreads.from > 1 && filters.numHWThreads.to > 0} (isResourcesOpen = true)}> - HWThreads: {filters.numHWThreads.from} - {filters.numHWThreads.to} + HWThreads: {filters.numHWThreads.from} - {filters.numHWThreads.to} + + {:else if filters.numHWThreads.from > 1 && filters.numHWThreads.to == 0} + (isResourcesOpen = true)}> +  ≥ {filters.numHWThreads.from} HWThread(s) + + {:else if filters.numHWThreads.from == 1 && filters.numHWThreads.to > 0} + (isResourcesOpen = true)}> +  ≤ {filters.numHWThreads.to} HWThread(s) {/if} - {#if filters.numAccelerators.from != null || filters.numAccelerators.to != null} + {#if filters.numAccelerators.from > 1 && filters.numAccelerators.to > 0} (isResourcesOpen = true)}> - Accelerators: {filters.numAccelerators.from} - {filters.numAccelerators.to} + Accelerators: {filters.numAccelerators.from} - {filters.numAccelerators.to} + + {:else if filters.numAccelerators.from > 1 && filters.numAccelerators.to == 0} + (isResourcesOpen = true)}> +  ≥ {filters.numAccelerators.from} Acc(s) + + {:else if filters.numAccelerators.from == 1 && filters.numAccelerators.to > 0} + (isResourcesOpen = true)}> +  ≤ {filters.numAccelerators.to} Acc(s) {/if} diff --git a/web/frontend/src/generic/filters/Resources.svelte b/web/frontend/src/generic/filters/Resources.svelte index d8cdbc65..be55bcc3 100644 --- a/web/frontend/src/generic/filters/Resources.svelte +++ b/web/frontend/src/generic/filters/Resources.svelte @@ -20,7 +20,9 @@ ModalBody, ModalHeader, ModalFooter, - Input + Input, + Tooltip, + Icon } from "@sveltestrap/sveltestrap"; import DoubleRangeSlider from "../select/DoubleRangeSlider.svelte"; @@ -42,6 +44,19 @@ contains: "Contains", } + const findMaxNumNodes = (infos) => + infos.reduce( + (max, cluster) => + Math.max( + max, + cluster.subClusters.reduce( + (max, sc) => Math.max(max, sc.numberOfNodes || 0), + 0, + ), + ), + 0, + ); + const findMaxNumAccels = (infos) => infos.reduce( (max, cluster) => @@ -75,29 +90,46 @@ /* State Init*/ // Counts - let minNumNodes = $state(1); - let maxNumNodes = $state(128); - let maxNumHWThreads = $state(0); - let maxNumAccelerators = $state(0); + let maxNumNodes = $state(1); + let maxNumHWThreads = $state(1); + let maxNumAccelerators = $state(1); /* Derived States */ // Pending - let pendingNumNodes = $derived(presetNumNodes); - let pendingNumHWThreads = $derived(presetNumHWThreads); - let pendingNumAccelerators = $derived(presetNumAccelerators); + let pendingNumNodes = $derived({ + from: presetNumNodes.from, + to: (presetNumNodes.to == 0) ? maxNumNodes : presetNumNodes.to + }); + let pendingNumHWThreads = $derived({ + from: presetNumHWThreads.from, + to: (presetNumHWThreads.to == 0) ? maxNumHWThreads : presetNumHWThreads.to + }); + let pendingNumAccelerators = $derived({ + from: presetNumAccelerators.from, + to: (presetNumAccelerators.to == 0) ? maxNumAccelerators : presetNumAccelerators.to + }); let pendingNamedNode = $derived(presetNamedNode); let pendingNodeMatch = $derived(presetNodeMatch); // Changable States - let nodesState = $derived(presetNumNodes); - let threadState = $derived(presetNumHWThreads); - let accState = $derived(presetNumAccelerators); + let nodesState = $derived({ + from: presetNumNodes.from, + to: (presetNumNodes.to == 0) ? maxNumNodes : presetNumNodes.to + }); + let threadState = $derived({ + from: presetNumHWThreads.from, + to: (presetNumHWThreads.to == 0) ? maxNumHWThreads : presetNumHWThreads.to + }); + let accState = $derived({ + from: presetNumAccelerators.from, + to: (presetNumAccelerators.to == 0) ? maxNumAccelerators : presetNumAccelerators.to + }); const initialized = $derived(getContext("initialized") || false); const clusterInfos = $derived($initialized ? getContext("clusters") : null); // Is Selection Active const nodesActive = $derived(!(JSON.stringify(nodesState) === JSON.stringify({ from: 1, to: maxNumNodes }))); const threadActive = $derived(!(JSON.stringify(threadState) === JSON.stringify({ from: 1, to: maxNumHWThreads }))); - const accActive = $derived(!(JSON.stringify(accState) === JSON.stringify({ from: 0, to: maxNumAccelerators }))); + const accActive = $derived(!(JSON.stringify(accState) === JSON.stringify({ from: 1, to: maxNumAccelerators }))); // Block Apply if null const disableApply = $derived( nodesState.from === null || nodesState.to === null || @@ -110,11 +142,13 @@ if ($initialized) { if (activeCluster != null) { const { subClusters } = clusterInfos.find((c) => c.name == activeCluster); - maxNumAccelerators = findMaxNumAccels([{ subClusters }]); + maxNumNodes = findMaxNumNodes([{ subClusters }]); maxNumHWThreads = findMaxNumHWThreadsPerNode([{ subClusters }]); + maxNumAccelerators = findMaxNumAccels([{ subClusters }]); } else if (clusterInfos.length > 0) { - maxNumAccelerators = findMaxNumAccels(clusterInfos); + maxNumNodes = findMaxNumNodes(clusterInfos); maxNumHWThreads = findMaxNumHWThreadsPerNode(clusterInfos); + maxNumAccelerators = findMaxNumAccels(clusterInfos); } } }); @@ -145,26 +179,35 @@ pendingNumAccelerators.from == null && pendingNumAccelerators.to == null ) { - accState = { from: 0, to: maxNumAccelerators }; + accState = { from: 1, to: maxNumAccelerators }; } }); /* Functions */ function setResources() { if (nodesActive) { - pendingNumNodes = {...nodesState}; + pendingNumNodes = { + from: nodesState.from, + to: (nodesState.to == maxNumNodes) ? 0 : nodesState.to + }; } else { - pendingNumNodes = { from: null, to: null }; + pendingNumNodes = { from: null, to: null}; }; if (threadActive) { - pendingNumHWThreads = {...threadState}; + pendingNumHWThreads = { + from: threadState.from, + to: (threadState.to == maxNumHWThreads) ? 0 : threadState.to + }; } else { - pendingNumHWThreads = { from: null, to: null }; + pendingNumHWThreads = { from: null, to: null}; }; if (accActive) { - pendingNumAccelerators = {...accState}; + pendingNumAccelerators = { + from: accState.from, + to: (accState.to == maxNumAccelerators) ? 0 : accState.to + }; } else { - pendingNumAccelerators = { from: null, to: null }; + pendingNumAccelerators = { from: null, to: null}; }; }; @@ -195,13 +238,18 @@
-
Number of Nodes
+
Number of Nodes + +
+ + Preset maximum is for whole cluster. + { nodesState.from = detail[0]; nodesState.to = detail[1]; }} - sliderMin={minNumNodes} + sliderMin={1} sliderMax={maxNumNodes} fromPreset={nodesState.from} toPreset={nodesState.to} @@ -209,7 +257,13 @@
-
Number of HWThreads (Use for Single-Node Jobs)
+
+ Number of HWThreads + +
+ + Presets for a single node. Can be changed to higher values. + { threadState.from = detail[0]; @@ -223,13 +277,19 @@
{#if maxNumAccelerators != null && maxNumAccelerators > 1}
-
Number of Accelerators
+
+ Number of Accelerators + +
+ + Presets for a single node. Can be changed to higher values. + { accState.from = detail[0]; accState.to = detail[1]; }} - sliderMin={0} + sliderMin={1} sliderMax={maxNumAccelerators} fromPreset={accState.from} toPreset={accState.to} diff --git a/web/frontend/src/generic/select/DoubleRangeSlider.svelte b/web/frontend/src/generic/select/DoubleRangeSlider.svelte index 12ca7449..c655087f 100644 --- a/web/frontend/src/generic/select/DoubleRangeSlider.svelte +++ b/web/frontend/src/generic/select/DoubleRangeSlider.svelte @@ -34,8 +34,8 @@ let pendingValues = $derived([fromPreset, toPreset]); let sliderFrom = $derived(Math.max(((fromPreset == null ? sliderMin : fromPreset) - sliderMin) / (sliderMax - sliderMin), 0.)); let sliderTo = $derived(Math.min(((toPreset == null ? sliderMin : toPreset) - sliderMin) / (sliderMax - sliderMin), 1.)); - let inputFieldFrom = $derived(fromPreset.toString()); - let inputFieldTo = $derived(toPreset.toString()); + let inputFieldFrom = $derived(fromPreset ? fromPreset.toString() : null); + let inputFieldTo = $derived(toPreset ? toPreset.toString() : null); /* Var Init */ let timeoutId = null; @@ -160,12 +160,26 @@
inputChanged(e, 'from')} /> + oninput={(e) => { + inputChanged(e, 'from'); + }} + /> - Full Range: {sliderMin} - {sliderMax} + {#if inputFieldFrom != "1" && inputFieldTo != sliderMax?.toString() } + Selected: Range {inputFieldFrom} - {inputFieldTo} + {:else if inputFieldFrom != "1" && inputFieldTo == sliderMax?.toString() } + Selected: More than {inputFieldFrom} + {:else if inputFieldFrom == "1" && inputFieldTo != sliderMax?.toString() } + Selected: Less than {inputFieldTo} + {:else} + No Selection + {/if} inputChanged(e, 'to')} /> + oninput={(e) => { + inputChanged(e, 'to'); + }} + />