Migrate metricSelection

This commit is contained in:
Christoph Kluge 2025-06-03 13:32:14 +02:00
parent 42c4926c47
commit 927e25c72c
7 changed files with 117 additions and 94 deletions

View File

@ -56,8 +56,8 @@
selectedScopes = [], selectedScopes = [],
plots = {}; plots = {};
let availableMetrics = new Set(), let totalMetrics = 0;
missingMetrics = [], let missingMetrics = [],
missingHosts = [], missingHosts = [],
somethingMissing = false; somethingMissing = false;
@ -294,7 +294,7 @@
{#if $initq?.data} {#if $initq?.data}
<Col xs="auto"> <Col xs="auto">
<Button outline on:click={() => (isMetricsSelectionOpen = true)} color="primary"> <Button outline on:click={() => (isMetricsSelectionOpen = true)} color="primary">
Select Metrics (Selected {selectedMetrics.length} of {availableMetrics.size} available) Select Metrics (Selected {selectedMetrics.length} of {totalMetrics} available)
</Button> </Button>
</Col> </Col>
{/if} {/if}
@ -428,12 +428,16 @@
{#if $initq?.data} {#if $initq?.data}
<MetricSelection <MetricSelection
bind:isOpen={isMetricsSelectionOpen}
bind:totalMetrics
presetMetrics={selectedMetrics}
cluster={$initq.data.job.cluster} cluster={$initq.data.job.cluster}
subCluster={$initq.data.job.subCluster} subCluster={$initq.data.job.subCluster}
configName="job_view_selectedMetrics" configName="job_view_selectedMetrics"
bind:metrics={selectedMetrics} preInitialized
bind:isOpen={isMetricsSelectionOpen} applyMetrics={(newMetrics) =>
bind:allMetrics={availableMetrics} selectedMetrics = [...newMetrics]
}
/> />
{/if} {/if}

View File

@ -197,10 +197,13 @@
<Sorting bind:sorting bind:isOpen={isSortingOpen}/> <Sorting bind:sorting bind:isOpen={isSortingOpen}/>
<MetricSelection <MetricSelection
bind:cluster={selectedCluster} bind:isOpen={isMetricsSelectionOpen}
configName="plot_list_selectedMetrics" bind:showFootprint
bind:metrics presetMetrics={metrics}
bind:isOpen={isMetricsSelectionOpen} cluster={selectedCluster}
bind:showFootprint configName="plot_list_selectedMetrics"
footprintSelect footprintSelect
applyMetrics={(newMetrics) =>
metrics = [...newMetrics]
}
/> />

View File

@ -193,12 +193,12 @@
{/if} {/if}
<MetricSelection <MetricSelection
bind:isOpen={isMetricsSelectionOpen}
presetMetrics={selectedMetrics}
{cluster} {cluster}
{subCluster} {subCluster}
configName="node_list_selectedMetrics" configName="node_list_selectedMetrics"
metrics={selectedMetrics} applyMetrics={(newMetrics) =>
bind:isOpen={isMetricsSelectionOpen} selectedMetrics = [...newMetrics]
on:update-metrics={({ detail }) => { }
selectedMetrics = [...detail]
}}
/> />

View File

@ -366,12 +366,15 @@
<Sorting bind:sorting bind:isOpen={isSortingOpen} /> <Sorting bind:sorting bind:isOpen={isSortingOpen} />
<MetricSelection <MetricSelection
bind:cluster={selectedCluster} bind:isOpen={isMetricsSelectionOpen}
configName="plot_list_selectedMetrics" bind:showFootprint
bind:metrics presetMetrics={metrics}
bind:isOpen={isMetricsSelectionOpen} cluster={selectedCluster}
bind:showFootprint configName="plot_list_selectedMetrics"
footprintSelect applyMetrics={(newMetrics) =>
metrics = [...newMetrics]
}
footprintSelect
/> />
<HistogramSelection <HistogramSelection

View File

@ -12,7 +12,7 @@
--> -->
<script> <script>
import { getContext, createEventDispatcher } from "svelte"; import { getContext } from "svelte";
import { import {
Modal, Modal,
ModalBody, ModalBody,
@ -23,57 +23,23 @@
} from "@sveltestrap/sveltestrap"; } from "@sveltestrap/sveltestrap";
import { gql, getContextClient, mutationStore } from "@urql/svelte"; import { gql, getContextClient, mutationStore } from "@urql/svelte";
export let metrics; /* Svelte 5 Props */
export let isOpen; let {
export let configName; isOpen = $bindable(false),
export let allMetrics = null; showFootprint = $bindable(false),
export let cluster = null; totalMetrics = $bindable(0),
export let subCluster = null; presetMetrics = [],
export let showFootprint = false; cluster = null,
export let footprintSelect = false; subCluster = null,
footprintSelect = false,
const onInit = getContext("on-init") preInitialized = false, // Job View is Pre-Init'd: $initialized "alone" store returns false
const globalMetrics = getContext("globalMetrics") configName,
const dispatch = createEventDispatcher(); applyMetrics
} = $props();
let newMetricsOrder = [];
let unorderedMetrics = [...metrics];
let pendingShowFootprint = !!showFootprint;
onInit(() => {
if (allMetrics == null) allMetrics = new Set();
for (let metric of globalMetrics) allMetrics.add(metric.name);
});
$: {
if (allMetrics != null) {
if (!cluster) {
for (let metric of globalMetrics) allMetrics.add(metric.name);
} else {
allMetrics.clear();
for (let gm of globalMetrics) {
if (!subCluster) {
if (gm.availability.find((av) => av.cluster === cluster)) allMetrics.add(gm.name);
} else {
if (gm.availability.find((av) => av.cluster === cluster && av.subClusters.includes(subCluster))) allMetrics.add(gm.name);
}
}
}
newMetricsOrder = [...allMetrics].filter((m) => !metrics.includes(m));
newMetricsOrder.unshift(...metrics.filter((m) => allMetrics.has(m)));
unorderedMetrics = unorderedMetrics.filter((m) => allMetrics.has(m));
}
}
function printAvailability(metric, cluster) {
const avail = globalMetrics.find((gm) => gm.name === metric)?.availability
if (!cluster) {
return avail.map((av) => av.cluster).join(',')
} else {
return avail.find((av) => av.cluster === cluster).subClusters.join(',')
}
}
/* Const Init */
const globalMetrics = getContext("globalMetrics");
const initialized = getContext("initialized");
const client = getContextClient(); const client = getContextClient();
const updateConfigurationMutation = ({ name, value }) => { const updateConfigurationMutation = ({ name, value }) => {
return mutationStore({ return mutationStore({
@ -87,7 +53,51 @@
}); });
}; };
let columnHovering = null; /* State Init */
let pendingMetrics = $state(presetMetrics);
let pendingShowFootprint = $state(!!showFootprint);
let listedMetrics = $state([]);
let columnHovering = $state(null);
/* Derives States */
const allMetrics = $derived(loadAvailable(preInitialized || $initialized));
/* Reactive Effects */
$effect(() => {
totalMetrics = allMetrics.size;
});
$effect(() => {
listedMetrics = [...presetMetrics, ...allMetrics.difference(new Set(presetMetrics))]; // List (preset) active metrics first, then list inactives
});
/* Functions */
function loadAvailable(init) {
const availableMetrics = new Set();
if (init) {
for (let gm of globalMetrics) {
if (!cluster) {
availableMetrics.add(gm.name)
} else {
if (!subCluster) {
if (gm.availability.find((av) => av.cluster === cluster)) availableMetrics.add(gm.name);
} else {
if (gm.availability.find((av) => av.cluster === cluster && av.subClusters.includes(subCluster))) availableMetrics.add(gm.name);
}
}
}
}
return availableMetrics
}
function printAvailability(metric, cluster) {
const avail = globalMetrics.find((gm) => gm.name === metric)?.availability
if (!cluster) {
return avail.map((av) => av.cluster).join(',')
} else {
return avail.find((av) => av.cluster === cluster).subClusters.join(',')
}
}
function columnsDragStart(event, i) { function columnsDragStart(event, i) {
event.dataTransfer.effectAllowed = "move"; event.dataTransfer.effectAllowed = "move";
@ -99,20 +109,20 @@
event.dataTransfer.dropEffect = "move"; event.dataTransfer.dropEffect = "move";
const start = Number.parseInt(event.dataTransfer.getData("text/plain")); const start = Number.parseInt(event.dataTransfer.getData("text/plain"));
let pendingMetricsOrder = [...newMetricsOrder]; let pendingMetricsOrder = [...listedMetrics];
if (start < target) { if (start < target) {
pendingMetricsOrder.splice(target + 1, 0, newMetricsOrder[start]); pendingMetricsOrder.splice(target + 1, 0, listedMetrics[start]);
pendingMetricsOrder.splice(start, 1); pendingMetricsOrder.splice(start, 1);
} else { } else {
pendingMetricsOrder.splice(target, 0, newMetricsOrder[start]); pendingMetricsOrder.splice(target, 0, listedMetrics[start]);
pendingMetricsOrder.splice(start + 1, 1); pendingMetricsOrder.splice(start + 1, 1);
} }
newMetricsOrder = [...pendingMetricsOrder]; listedMetrics = [...pendingMetricsOrder];
columnHovering = null; columnHovering = null;
} }
function closeAndApply() { function closeAndApply() {
metrics = newMetricsOrder.filter((m) => unorderedMetrics.includes(m)); pendingMetrics = listedMetrics.filter((m) => pendingMetrics.includes(m));
isOpen = false; isOpen = false;
let configKey; let configKey;
@ -126,7 +136,7 @@
updateConfigurationMutation({ updateConfigurationMutation({
name: configKey, name: configKey,
value: JSON.stringify(metrics), value: JSON.stringify(pendingMetrics),
}).subscribe((res) => { }).subscribe((res) => {
if (res.fetching === false && res.error) { if (res.fetching === false && res.error) {
throw res.error; throw res.error;
@ -148,7 +158,7 @@
}); });
}; };
dispatch('update-metrics', metrics); applyMetrics(pendingMetrics);
} }
</script> </script>
@ -162,7 +172,7 @@
</li> </li>
<hr /> <hr />
{/if} {/if}
{#each newMetricsOrder as metric, index (metric)} {#each listedMetrics as metric, index (metric)}
<li <li
class="cc-config-column list-group-item" class="cc-config-column list-group-item"
draggable={true} draggable={true}
@ -180,17 +190,17 @@
ondragenter={() => (columnHovering = index)} ondragenter={() => (columnHovering = index)}
class:is-active={columnHovering === index} class:is-active={columnHovering === index}
> >
{#if unorderedMetrics.includes(metric)} {#if pendingMetrics.includes(metric)}
<input <input
type="checkbox" type="checkbox"
bind:group={unorderedMetrics} bind:group={pendingMetrics}
value={metric} value={metric}
checked checked
/> />
{:else} {:else}
<input <input
type="checkbox" type="checkbox"
bind:group={unorderedMetrics} bind:group={pendingMetrics}
value={metric} value={metric}
/> />
{/if} {/if}
@ -203,8 +213,8 @@
</ListGroup> </ListGroup>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button color="primary" on:click={() => closeAndApply()}>Close & Apply</Button> <Button color="primary" onclick={() => closeAndApply()}>Close & Apply</Button>
<Button color="secondary" on:click={() => (isOpen = !isOpen)}>Cancel</Button> <Button color="secondary" onclick={() => (isOpen = !isOpen)}>Cancel</Button>
</ModalFooter> </ModalFooter>
</Modal> </Modal>

View File

@ -33,7 +33,7 @@
let loadScopes = false; let loadScopes = false;
let selectedScopes = []; let selectedScopes = [];
let selectedMetrics = []; let selectedMetrics = [];
let availableMetrics = new Set(); // For Info Only, filled by MetricSelection Component let totalMetrics = 0; // For Info Only, filled by MetricSelection Component
let isMetricSelectionOpen = false; let isMetricSelectionOpen = false;
const client = getContextClient(); const client = getContextClient();
@ -99,7 +99,7 @@
<Row> <Row>
<Col class="m-2"> <Col class="m-2">
<Button outline on:click={() => (isMetricSelectionOpen = true)} class="px-2" color="primary" style="margin-right:0.5rem"> <Button outline on:click={() => (isMetricSelectionOpen = true)} class="px-2" color="primary" style="margin-right:0.5rem">
Select Metrics (Selected {selectedMetrics.length} of {availableMetrics.size} available) Select Metrics (Selected {selectedMetrics.length} of {totalMetrics} available)
</Button> </Button>
{#if job.numNodes > 1} {#if job.numNodes > 1}
<Button class="px-2 ml-auto" color="success" outline on:click={() => (loadScopes = !loadScopes)} disabled={loadScopes}> <Button class="px-2 ml-auto" color="success" outline on:click={() => (loadScopes = !loadScopes)} disabled={loadScopes}>
@ -136,10 +136,13 @@
</TabPane> </TabPane>
<MetricSelection <MetricSelection
bind:isOpen={isMetricSelectionOpen}
bind:totalMetrics
presetMetrics={selectedMetrics}
cluster={job.cluster} cluster={job.cluster}
subCluster={job.subCluster} subCluster={job.subCluster}
configName="job_view_nodestats_selectedMetrics" configName="job_view_nodestats_selectedMetrics"
bind:allMetrics={availableMetrics} applyMetrics={(newMetrics) =>
bind:metrics={selectedMetrics} selectedMetrics = [...newMetrics]
bind:isOpen={isMetricSelectionOpen} }
/> />

View File

@ -81,7 +81,7 @@
<thead> <thead>
<!-- Header Row 1: Selectors --> <!-- Header Row 1: Selectors -->
<tr> <tr>
<th/> <th></th>
{#each selectedMetrics as metric} {#each selectedMetrics as metric}
<!-- To Match Row-2 Header Field Count--> <!-- To Match Row-2 Header Field Count-->
<th colspan={selectedScopes[metric] == "node" ? 3 : 4}> <th colspan={selectedScopes[metric] == "node" ? 3 : 4}>