add not configured info cards, show short job filter options if one active filter

This commit is contained in:
Christoph Kluge
2026-02-10 13:49:23 +01:00
parent 0dff9fa07f
commit 49a1748641
9 changed files with 105 additions and 63 deletions

2
go.sum
View File

@@ -4,8 +4,6 @@ github.com/99designs/gqlgen v0.17.85 h1:EkGx3U2FDcxQm8YDLQSpXIAVmpDyZ3IcBMOJi2nH
github.com/99designs/gqlgen v0.17.85/go.mod h1:yvs8s0bkQlRfqg03YXr3eR4OQUowVhODT/tHzCXnbOU=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/ClusterCockpit/cc-lib/v2 v2.2.1 h1:iCVas+Jc61zFH5S2VG3H1sc7tsn+U4lOJwUYjYZEims=
github.com/ClusterCockpit/cc-lib/v2 v2.2.1/go.mod h1:JuxMAuEOaLLNEnnL9U3ejha8kMvsSatLdKPZEgJw6iw=
github.com/ClusterCockpit/cc-lib/v2 v2.2.2 h1:ye4RY57I19c2cXr3XWZBS/QYYgQVeGFvsiu5HkyKq9E=
github.com/ClusterCockpit/cc-lib/v2 v2.2.2/go.mod h1:JuxMAuEOaLLNEnnL9U3ejha8kMvsSatLdKPZEgJw6iw=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=

View File

@@ -30,7 +30,7 @@
import {
init,
groupByScope,
checkMetricDisabled,
checkMetricAvailability,
} from "./generic/utils.js";
import Metric from "./job/Metric.svelte";
import MetricSelection from "./generic/select/MetricSelection.svelte";
@@ -151,17 +151,17 @@
}
return names;
}, []);
//
return metricNames.filter(
(metric) =>
!metrics.some((jm) => jm.name == metric) &&
selectedMetrics.includes(metric) &&
!checkMetricDisabled(
(checkMetricAvailability(
globalMetrics,
metric,
thisJob.cluster,
thisJob.subCluster,
),
) == "configured")
);
} else {
return []
@@ -212,7 +212,7 @@
inputMetrics.map((metric) => ({
metric: metric,
data: grouped.find((group) => group[0].name == metric),
disabled: checkMetricDisabled(
availability: checkMetricAvailability(
globalMetrics,
metric,
thisJob.cluster,
@@ -333,7 +333,17 @@
{:else if thisJob && $jobMetrics?.data?.scopedJobStats}
<!-- Note: Ignore '#snippet' Error in IDE -->
{#snippet gridContent(item)}
{#if item?.disabled}
{#if item.availability == "none"}
<Card color="light" class="mt-2">
<CardHeader class="mb-0">
<b>Metric not configured</b>
</CardHeader>
<CardBody>
<p>No datasets returned for <b>{item.metric}</b>.</p>
<p class="mb-1">Metric is not configured for cluster <b>{thisJob.cluster}</b>.</p>
</CardBody>
</Card>
{:else if item.availability == "disabled"}
<Card color="info" class="mt-2">
<CardHeader class="mb-0">
<b>Disabled Metric</b>

View File

@@ -142,7 +142,8 @@
<Filters
bind:this={filterComponent}
{filterPresets}
shortJobQuickSelect
startTimeQuickSelect
shortJobQuickSelect={(filterBuffer.length > 0)}
shortJobCutoff={ccconfig?.jobList_hideShortRunningJobs}
showFilter={!showCompare}
matchedJobs={showCompare? matchedCompareJobs: matchedListJobs}

View File

@@ -32,7 +32,7 @@
} from "@urql/svelte";
import {
init,
checkMetricDisabled,
checkMetricAvailability,
} from "./generic/utils.js";
import PlotGrid from "./generic/PlotGrid.svelte";
import MetricPlot from "./generic/plots/MetricPlot.svelte";
@@ -242,17 +242,17 @@
{item.name}
{systemUnits[item.name] ? "(" + systemUnits[item.name] + ")" : ""}
</h4>
{#if item.disabled === false && item.metric}
<MetricPlot
metric={item.name}
timestep={item.metric.timestep}
cluster={clusterInfos.find((c) => c.name == cluster)}
subCluster={$nodeMetricsData.data.nodeMetrics[0].subCluster}
series={item.metric.series}
enableFlip
forNode
/>
{:else if item.disabled === true && item.metric}
{#if item.availability == "none"}
<Card color="light" class="mx-2">
<CardHeader class="mb-0">
<b>Metric not configured</b>
</CardHeader>
<CardBody>
<p>No datasets returned for <b>{item.name}</b>.</p>
<p class="mb-1">Metric is not configured for cluster <b>{cluster}</b>.</p>
</CardBody>
</Card>
{:else if item.availability == "disabled"}
<Card color="info" class="mx-2">
<CardHeader class="mb-0">
<b>Disabled Metric</b>
@@ -262,6 +262,16 @@
<p class="mb-1">Metric has been disabled for subcluster <b>{$nodeMetricsData.data.nodeMetrics[0].subCluster}</b>.</p>
</CardBody>
</Card>
{:else if item?.metric}
<MetricPlot
metric={item.name}
timestep={item.metric.timestep}
cluster={clusterInfos.find((c) => c.name == cluster)}
subCluster={$nodeMetricsData.data.nodeMetrics[0].subCluster}
series={item.metric.series}
enableFlip
forNode
/>
{:else}
<Card color="warning" class="mx-2">
<CardHeader class="mb-0">
@@ -279,7 +289,7 @@
items={$nodeMetricsData.data.nodeMetrics[0].metrics
.map((m) => ({
...m,
disabled: checkMetricDisabled(
availability: checkMetricAvailability(
globalMetrics,
m.name,
cluster,

View File

@@ -219,7 +219,8 @@
<Filters
bind:this={filterComponent}
{filterPresets}
shortJobQuickSelect
startTimeQuickSelect
shortJobQuickSelect={(filterBuffer.length > 0)}
shortJobCutoff={ccconfig?.jobList_hideShortRunningJobs}
showFilter={!showCompare}
matchedJobs={showCompare? matchedCompareJobs: matchedListJobs}

View File

@@ -19,7 +19,7 @@
<script>
import { queryStore, gql, getContextClient } from "@urql/svelte";
import { Card, Spinner } from "@sveltestrap/sveltestrap";
import { maxScope, checkMetricDisabled } from "../utils.js";
import { maxScope, checkMetricAvailability } from "../utils.js";
import JobInfo from "./JobInfo.svelte";
import MetricPlot from "../plots/MetricPlot.svelte";
import JobFootprint from "../helper/JobFootprint.svelte";
@@ -145,7 +145,7 @@
metricList.forEach((metricName) => {
const pendingMetric = {
name: metricName,
disabled: checkMetricDisabled(
availability: checkMetricAvailability(
globalMetrics,
metricName,
job.cluster,
@@ -207,7 +207,12 @@
{/if}
{#each refinedData as metric, i (metric?.name || i)}
<td>
{#if metric?.disabled}
{#if metric?.availability == "none"}
<Card body class="mx-2" color="light">
<p>No dataset(s) returned for <b>{metrics[i]}</b></p>
<p class="mb-1">Metric is not configured for cluster <b>{job.cluster}</b>.</p>
</Card>
{:else if metric?.availability == "disabled"}
<Card body class="mx-2" color="info">
<p>No dataset(s) returned for <b>{metrics[i]}</b></p>
<p class="mb-1">Metric has been disabled for subcluster <b>{job.subCluster}</b>.</p>

View File

@@ -302,20 +302,36 @@ export function stickyHeader(datatableHeaderSelector, updatePading) {
onDestroy(() => document.removeEventListener("scroll", onscroll));
}
export function checkMetricDisabled(gm, m, c, s) { // [g]lobal[m]etrics, [m]etric, [c]luster, [s]ubcluster
const available = gm?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s)
// Return inverse logic
return !available
export function checkMetricAvailability(gms, m, c, s = "") { // [g]lobal[m]etrics, [m]etric, [c]luster, [s]ubcluster
let pendingAvailability = "none"
const configured = gms?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)
if (configured) {
pendingAvailability = "configured"
if (s != "") {
const enabled = configured.subClusters?.includes(s)
// Test inverse logic
if (!enabled) {
pendingAvailability = "disabled"
}
}
}
return pendingAvailability;
}
export function checkMetricsDisabled(gm, ma, c, s) { // [g]lobal[m]etrics, [m]etric[a]rray, [c]luster, [s]ubcluster
let result = {};
ma.forEach((m) => {
// Return named inverse logic: !available
result[m] = !(gm?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s))
});
return result
}
// export function checkMetricDisabled(gm, m, c, s) { // [g]lobal[m]etrics, [m]etric, [c]luster, [s]ubcluster
// const available = gm?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s)
// // Return inverse logic
// return !available
// }
// export function checkMetricsDisabled(gm, ma, c, s) { // [g]lobal[m]etrics, [m]etric[a]rray, [c]luster, [s]ubcluster
// let aresult = {};
// ma.forEach((m) => {
// // Return named inverse logic: !available
// aresult[m] = !(gm?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s))
// });
// return aresult
// }
export function getStatsItems(presetStats = []) {
// console.time('stats')

View File

@@ -15,7 +15,7 @@
<script>
import { queryStore, gql, getContextClient } from "@urql/svelte";
import { Row, Col, Card, CardHeader, CardBody, Spinner, Badge } from "@sveltestrap/sveltestrap";
import { checkMetricDisabled } from "../generic/utils.js";
import { checkMetricAvailability } from "../generic/utils.js";
import MetricPlot from "../generic/plots/MetricPlot.svelte";
/* Svelte 5 Props */
@@ -87,6 +87,7 @@
},
}));
const notConfigured = $derived(checkMetricAvailability(globalMetrics, selectedMetric, cluster) == "none");
const mappedData = $derived(handleQueryData($nodesQuery?.data));
const filteredData = $derived(mappedData.filter((h) => {
if (hostnameFilter) {
@@ -120,7 +121,7 @@
data: h.metrics.filter(
(m) => m?.name == selectedMetric && m.scope == "node",
),
disabled: checkMetricDisabled(globalMetrics, selectedMetric, cluster, h.subCluster),
availability: checkMetricAvailability(globalMetrics, selectedMetric, cluster, h.subCluster),
}))
.sort((a, b) => a.host.localeCompare(b.host))
}
@@ -161,7 +162,7 @@
</Badge>
</span>
</div>
{#if item?.disabled}
{#if item?.availability == "disabled"}
<Card color="info">
<CardHeader class="mb-0">
<b>Disabled Metric</b>
@@ -213,6 +214,18 @@
</CardBody>
</Card>
</Row>
{:else if notConfigured}
<Row class="mx-1">
<Card class="px-0" color="light">
<CardHeader>
<b>Metric not configured</b>
</CardHeader>
<CardBody>
<p>No datasets returned for <b>{selectedMetric}</b>.</p>
<p class="mb-1">Metric is not configured for cluster <b>{cluster}</b>.</p>
</CardBody>
</Card>
</Row>
{:else}
<Row class="mx-1">
<Card class="px-0" color="warning">

View File

@@ -16,7 +16,7 @@
} from "@urql/svelte";
import uPlot from "uplot";
import { Card, CardBody, Spinner } from "@sveltestrap/sveltestrap";
import { maxScope, checkMetricDisabled, scramble, scrambleNames } from "../../generic/utils.js";
import { maxScope, checkMetricAvailability, scramble, scrambleNames } from "../../generic/utils.js";
import MetricPlot from "../../generic/plots/MetricPlot.svelte";
import NodeInfo from "./NodeInfo.svelte";
@@ -73,7 +73,7 @@
const extendedLegendData = $derived($nodeJobsData?.data ? buildExtendedLegend() : null);
const refinedData = $derived(nodeData?.metrics ? sortAndSelectScope(selectedMetrics, nodeData.metrics) : []);
const dataHealth = $derived(refinedData.filter((rd) => rd.disabled === false).map((enabled) => (enabled?.data?.metric?.series?.length > 0)));
const dataHealth = $derived(refinedData.filter((rd) => rd.availability == "configured").map((enabled) => (enabled?.data?.metric?.series?.length > 0)));
/* Functions */
function sortAndSelectScope(metricList = [], nodeMetrics = []) {
@@ -81,7 +81,7 @@
metricList.forEach((metricName) => {
const pendingMetric = {
name: metricName,
disabled: checkMetricDisabled(
availability: checkMetricAvailability(
globalMetrics,
metricName,
cluster,
@@ -130,23 +130,6 @@
return pendingExtendedLegendData;
}
/* Inspect */
// $inspect(selectedMetrics).with((type, selectedMetrics) => {
// console.log(type, 'selectedMetrics', selectedMetrics)
// });
// $inspect(nodeData).with((type, nodeData) => {
// console.log(type, 'nodeData', nodeData)
// });
// $inspect(refinedData).with((type, refinedData) => {
// console.log(type, 'refinedData', refinedData)
// });
// $inspect(dataHealth).with((type, dataHealth) => {
// console.log(type, 'dataHealth', dataHealth)
// });
</script>
<tr>
@@ -169,7 +152,12 @@
</td>
{#each refinedData as metricData, i (metricData?.data?.name || i)}
<td>
{#if metricData?.disabled}
{#if metricData?.availability == "none"}
<Card body class="mx-2" color="light">
<p>No dataset(s) returned for <b>{selectedMetrics[i]}</b></p>
<p class="mb-1">Metric is not configured for cluster <b>{cluster}</b>.</p>
</Card>
{:else if metricData?.availability == "disabled"}
<Card body class="mx-2" color="info">
<p>No dataset(s) returned for <b>{selectedMetrics[i]}</b></p>
<p class="mb-1">Metric has been disabled for subcluster <b>{nodeData.subCluster}</b>.</p>
@@ -177,7 +165,7 @@
{:else if !metricData?.data}
<Card body class="mx-2" color="warning">
<p>No dataset(s) returned for <b>{selectedMetrics[i]}</b></p>
<p class="mb-1">Metric was not found in metric store for cluster <b>{cluster}</b>.</p>
<p class="mb-1">Metric or host was not found in metric store for cluster <b>{cluster}</b>.</p>
</Card>
{:else if !!metricData.data?.metric.statisticsSeries}
<!-- "No Data"-Warning included in MetricPlot-Component -->