mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-06-08 00:23:48 +02:00
Migrate metricSelection
This commit is contained in:
parent
42c4926c47
commit
927e25c72c
@ -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}
|
||||||
|
|
||||||
|
@ -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]
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -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]
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -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}>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user