mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-26 05:19:05 +01:00
replace plotTable with new bootstrap plotGrid component
- helps with narrow window sizes - plotTable kept for now
This commit is contained in:
parent
0ff5c4bedd
commit
7243dbe763
@ -28,7 +28,7 @@
|
|||||||
} from "./generic/utils.js";
|
} from "./generic/utils.js";
|
||||||
import PlotSelection from "./analysis/PlotSelection.svelte";
|
import PlotSelection from "./analysis/PlotSelection.svelte";
|
||||||
import Filters from "./generic/Filters.svelte";
|
import Filters from "./generic/Filters.svelte";
|
||||||
import PlotTable from "./generic/PlotTable.svelte";
|
import PlotGrid from "./generic/PlotGrid.svelte";
|
||||||
import Histogram from "./generic/plots/Histogram.svelte";
|
import Histogram from "./generic/plots/Histogram.svelte";
|
||||||
import Pie, { colors } from "./generic/plots/Pie.svelte";
|
import Pie, { colors } from "./generic/plots/Pie.svelte";
|
||||||
import ScatterPlot from "./generic/plots/Scatter.svelte";
|
import ScatterPlot from "./generic/plots/Scatter.svelte";
|
||||||
@ -70,6 +70,8 @@
|
|||||||
...new Set([...metricsInHistograms, ...metricsInScatterplots.flat()]),
|
...new Set([...metricsInHistograms, ...metricsInScatterplots.flat()]),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$: console.log(">>> CLUSTER", cluster)
|
||||||
|
|
||||||
const sortOptions = [
|
const sortOptions = [
|
||||||
{ key: "totalWalltime", label: "Walltime" },
|
{ key: "totalWalltime", label: "Walltime" },
|
||||||
{ key: "totalNodeHours", label: "Node Hours" },
|
{ key: "totalNodeHours", label: "Node Hours" },
|
||||||
@ -523,7 +525,7 @@
|
|||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col>
|
||||||
<PlotTable
|
<PlotGrid
|
||||||
let:item
|
let:item
|
||||||
let:width
|
let:width
|
||||||
renderFor="analysis"
|
renderFor="analysis"
|
||||||
@ -551,7 +553,7 @@
|
|||||||
ylabel="Normalized Hours"
|
ylabel="Normalized Hours"
|
||||||
yunit="Hours"
|
yunit="Hours"
|
||||||
/>
|
/>
|
||||||
</PlotTable>
|
</PlotGrid>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<br />
|
<br />
|
||||||
@ -569,7 +571,7 @@
|
|||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col>
|
||||||
<PlotTable
|
<PlotGrid
|
||||||
let:item
|
let:item
|
||||||
let:width
|
let:width
|
||||||
renderFor="analysis"
|
renderFor="analysis"
|
||||||
@ -595,7 +597,7 @@
|
|||||||
Y={item.f2}
|
Y={item.f2}
|
||||||
S={$footprintsQuery.data.footprints.timeWeights.nodeHours}
|
S={$footprintsQuery.data.footprints.timeWeights.nodeHours}
|
||||||
/>
|
/>
|
||||||
</PlotTable>
|
</PlotGrid>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
import JobSummary from "./job/JobSummary.svelte";
|
import JobSummary from "./job/JobSummary.svelte";
|
||||||
import EnergySummary from "./job/EnergySummary.svelte";
|
import EnergySummary from "./job/EnergySummary.svelte";
|
||||||
import ConcurrentJobs from "./generic/helper/ConcurrentJobs.svelte";
|
import ConcurrentJobs from "./generic/helper/ConcurrentJobs.svelte";
|
||||||
import PlotTable from "./generic/PlotTable.svelte";
|
import PlotGrid from "./generic/PlotGrid.svelte";
|
||||||
import Roofline from "./generic/plots/Roofline.svelte";
|
import Roofline from "./generic/plots/Roofline.svelte";
|
||||||
import JobInfo from "./generic/joblist/JobInfo.svelte";
|
import JobInfo from "./generic/joblist/JobInfo.svelte";
|
||||||
import MetricSelection from "./generic/select/MetricSelection.svelte";
|
import MetricSelection from "./generic/select/MetricSelection.svelte";
|
||||||
@ -330,50 +330,55 @@
|
|||||||
</Col>
|
</Col>
|
||||||
{/if}
|
{/if}
|
||||||
</Row>
|
</Row>
|
||||||
<hr/>
|
<hr class="mb-2"/>
|
||||||
<Row>
|
|
||||||
<Col>
|
{#if $jobMetrics.error}
|
||||||
{#if $jobMetrics.error}
|
<Row class="mt-2">
|
||||||
|
<Col>
|
||||||
{#if $initq.data.job.monitoringStatus == 0 || $initq.data.job.monitoringStatus == 2}
|
{#if $initq.data.job.monitoringStatus == 0 || $initq.data.job.monitoringStatus == 2}
|
||||||
<Card body color="warning">Not monitored or archiving failed</Card>
|
<Card body color="warning">Not monitored or archiving failed</Card>
|
||||||
<br />
|
<br />
|
||||||
{/if}
|
{/if}
|
||||||
<Card body color="danger">{$jobMetrics.error.message}</Card>
|
<Card body color="danger">{$jobMetrics.error.message}</Card>
|
||||||
{:else if $jobMetrics.fetching}
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{:else if $jobMetrics.fetching}
|
||||||
|
<Row class="mt-2">
|
||||||
|
<Col>
|
||||||
<Spinner secondary />
|
<Spinner secondary />
|
||||||
{:else if $initq?.data && $jobMetrics?.data?.jobMetrics}
|
</Col>
|
||||||
<PlotTable
|
</Row>
|
||||||
let:item
|
{:else if $initq?.data && $jobMetrics?.data?.jobMetrics}
|
||||||
let:width
|
<PlotGrid
|
||||||
renderFor="job"
|
let:item
|
||||||
items={orderAndMap(
|
let:width
|
||||||
groupByScope($jobMetrics.data.jobMetrics),
|
renderFor="job"
|
||||||
selectedMetrics,
|
items={orderAndMap(
|
||||||
)}
|
groupByScope($jobMetrics.data.jobMetrics),
|
||||||
itemsPerRow={ccconfig.plot_view_plotsPerRow}
|
selectedMetrics,
|
||||||
|
)}
|
||||||
|
itemsPerRow={ccconfig.plot_view_plotsPerRow}
|
||||||
|
>
|
||||||
|
{#if item.data}
|
||||||
|
<Metric
|
||||||
|
bind:this={plots[item.metric]}
|
||||||
|
on:more-loaded={({ detail }) => statsTable.moreLoaded(detail)}
|
||||||
|
job={$initq.data.job}
|
||||||
|
metricName={item.metric}
|
||||||
|
metricUnit={$initq.data.globalMetrics.find((gm) => gm.name == item.metric)?.unit}
|
||||||
|
nativeScope={$initq.data.globalMetrics.find((gm) => gm.name == item.metric)?.scope}
|
||||||
|
rawData={item.data.map((x) => x.metric)}
|
||||||
|
scopes={item.data.map((x) => x.scope)}
|
||||||
|
{width}
|
||||||
|
isShared={$initq.data.job.exclusive != 1}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<Card body color="warning" class="mt-2"
|
||||||
|
>No dataset returned for <code>{item.metric}</code></Card
|
||||||
>
|
>
|
||||||
{#if item.data}
|
|
||||||
<Metric
|
|
||||||
bind:this={plots[item.metric]}
|
|
||||||
on:more-loaded={({ detail }) => statsTable.moreLoaded(detail)}
|
|
||||||
job={$initq.data.job}
|
|
||||||
metricName={item.metric}
|
|
||||||
metricUnit={$initq.data.globalMetrics.find((gm) => gm.name == item.metric)?.unit}
|
|
||||||
nativeScope={$initq.data.globalMetrics.find((gm) => gm.name == item.metric)?.scope}
|
|
||||||
rawData={item.data.map((x) => x.metric)}
|
|
||||||
scopes={item.data.map((x) => x.scope)}
|
|
||||||
{width}
|
|
||||||
isShared={$initq.data.job.exclusive != 1}
|
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<Card body color="warning"
|
|
||||||
>No dataset returned for <code>{item.metric}</code></Card
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
</PlotTable>
|
|
||||||
{/if}
|
{/if}
|
||||||
</Col>
|
</PlotGrid>
|
||||||
</Row>
|
{/if}
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
init,
|
init,
|
||||||
checkMetricDisabled,
|
checkMetricDisabled,
|
||||||
} from "./generic/utils.js";
|
} from "./generic/utils.js";
|
||||||
import PlotTable from "./generic/PlotTable.svelte";
|
import PlotGrid from "./generic/PlotGrid.svelte";
|
||||||
import MetricPlot from "./generic/plots/MetricPlot.svelte";
|
import MetricPlot from "./generic/plots/MetricPlot.svelte";
|
||||||
import TimeSelection from "./generic/select/TimeSelection.svelte";
|
import TimeSelection from "./generic/select/TimeSelection.svelte";
|
||||||
import Refresher from "./generic/helper/Refresher.svelte";
|
import Refresher from "./generic/helper/Refresher.svelte";
|
||||||
@ -187,7 +187,7 @@
|
|||||||
{:else if $nodeMetricsData.fetching || $initq.fetching}
|
{:else if $nodeMetricsData.fetching || $initq.fetching}
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{:else}
|
{:else}
|
||||||
<PlotTable
|
<PlotGrid
|
||||||
let:item
|
let:item
|
||||||
let:width
|
let:width
|
||||||
renderFor="node"
|
renderFor="node"
|
||||||
@ -233,7 +233,7 @@
|
|||||||
>No dataset returned for <code>{item.name}</code></Card
|
>No dataset returned for <code>{item.name}</code></Card
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
</PlotTable>
|
</PlotGrid>
|
||||||
{/if}
|
{/if}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
transformPerNodeDataForRoofline,
|
transformPerNodeDataForRoofline,
|
||||||
} from "./generic/utils.js";
|
} from "./generic/utils.js";
|
||||||
import { scaleNumbers } from "./generic/units.js";
|
import { scaleNumbers } from "./generic/units.js";
|
||||||
import PlotTable from "./generic/PlotTable.svelte";
|
import PlotGrid from "./generic/PlotGrid.svelte";
|
||||||
import Roofline from "./generic/plots/Roofline.svelte";
|
import Roofline from "./generic/plots/Roofline.svelte";
|
||||||
import Pie, { colors } from "./generic/plots/Pie.svelte";
|
import Pie, { colors } from "./generic/plots/Pie.svelte";
|
||||||
import Histogram from "./generic/plots/Histogram.svelte";
|
import Histogram from "./generic/plots/Histogram.svelte";
|
||||||
@ -651,31 +651,27 @@
|
|||||||
</Row>
|
</Row>
|
||||||
<hr class="my-2" />
|
<hr class="my-2" />
|
||||||
{#if metricsInHistograms}
|
{#if metricsInHistograms}
|
||||||
<Row cols={1}>
|
{#key $mainQuery.data.stats[0].histMetrics}
|
||||||
<Col>
|
<PlotGrid
|
||||||
{#key $mainQuery.data.stats[0].histMetrics}
|
let:item
|
||||||
<PlotTable
|
let:width
|
||||||
let:item
|
renderFor="user"
|
||||||
let:width
|
items={$mainQuery.data.stats[0].histMetrics}
|
||||||
renderFor="user"
|
itemsPerRow={2}
|
||||||
items={$mainQuery.data.stats[0].histMetrics}
|
>
|
||||||
itemsPerRow={2}
|
<Histogram
|
||||||
>
|
data={convert2uplot(item.data)}
|
||||||
<Histogram
|
usesBins={true}
|
||||||
data={convert2uplot(item.data)}
|
{width}
|
||||||
usesBins={true}
|
height={250}
|
||||||
{width}
|
title="Distribution of '{item.metric}' averages"
|
||||||
height={250}
|
xlabel={`${item.metric} bin maximum ${item?.unit ? `[${item.unit}]` : ``}`}
|
||||||
title="Distribution of '{item.metric}' averages"
|
xunit={item.unit}
|
||||||
xlabel={`${item.metric} bin maximum ${item?.unit ? `[${item.unit}]` : ``}`}
|
ylabel="Number of Jobs"
|
||||||
xunit={item.unit}
|
yunit="Jobs"
|
||||||
ylabel="Number of Jobs"
|
/>
|
||||||
yunit="Jobs"
|
</PlotGrid>
|
||||||
/>
|
{/key}
|
||||||
</PlotTable>
|
|
||||||
{/key}
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
init,
|
init,
|
||||||
checkMetricDisabled,
|
checkMetricDisabled,
|
||||||
} from "./generic/utils.js";
|
} from "./generic/utils.js";
|
||||||
import PlotTable from "./generic/PlotTable.svelte";
|
import PlotGrid from "./generic/PlotGrid.svelte";
|
||||||
import MetricPlot from "./generic/plots/MetricPlot.svelte";
|
import MetricPlot from "./generic/plots/MetricPlot.svelte";
|
||||||
import TimeSelection from "./generic/select/TimeSelection.svelte";
|
import TimeSelection from "./generic/select/TimeSelection.svelte";
|
||||||
import Refresher from "./generic/helper/Refresher.svelte";
|
import Refresher from "./generic/helper/Refresher.svelte";
|
||||||
@ -160,73 +160,77 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</Row>
|
</Row>
|
||||||
<br />
|
<br />
|
||||||
<Row>
|
{#if $nodesQuery.error}
|
||||||
<Col>
|
<Row>
|
||||||
{#if $nodesQuery.error}
|
<Col>
|
||||||
<Card body color="danger">{$nodesQuery.error.message}</Card>
|
<Card body color="danger">{$nodesQuery.error.message}</Card>
|
||||||
{:else if $nodesQuery.fetching || $initq.fetching}
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{:else if $nodesQuery.fetching || $initq.fetching}
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{:else}
|
</Col>
|
||||||
<PlotTable
|
</Row>
|
||||||
let:item
|
{:else}
|
||||||
let:width
|
<PlotGrid
|
||||||
renderFor="systems"
|
let:item
|
||||||
itemsPerRow={ccconfig.plot_view_plotsPerRow}
|
let:width
|
||||||
items={$nodesQuery.data.nodeMetrics
|
renderFor="systems"
|
||||||
.filter(
|
itemsPerRow={ccconfig.plot_view_plotsPerRow}
|
||||||
(h) =>
|
items={$nodesQuery.data.nodeMetrics
|
||||||
h.host.includes(hostnameFilter) &&
|
.filter(
|
||||||
h.metrics.some(
|
(h) =>
|
||||||
(m) => m.name == selectedMetric && m.scope == "node",
|
h.host.includes(hostnameFilter) &&
|
||||||
),
|
h.metrics.some(
|
||||||
)
|
(m) => m.name == selectedMetric && m.scope == "node",
|
||||||
.map((h) => ({
|
),
|
||||||
host: h.host,
|
)
|
||||||
subCluster: h.subCluster,
|
.map((h) => ({
|
||||||
data: h.metrics.find(
|
host: h.host,
|
||||||
(m) => m.name == selectedMetric && m.scope == "node",
|
subCluster: h.subCluster,
|
||||||
),
|
data: h.metrics.find(
|
||||||
disabled: checkMetricDisabled(
|
(m) => m.name == selectedMetric && m.scope == "node",
|
||||||
selectedMetric,
|
),
|
||||||
cluster,
|
disabled: checkMetricDisabled(
|
||||||
h.subCluster,
|
selectedMetric,
|
||||||
),
|
cluster,
|
||||||
}))
|
h.subCluster,
|
||||||
.sort((a, b) => a.host.localeCompare(b.host))}
|
),
|
||||||
|
}))
|
||||||
|
.sort((a, b) => a.host.localeCompare(b.host))}
|
||||||
|
>
|
||||||
|
<h4 style="width: 100%; text-align: center;">
|
||||||
|
<a
|
||||||
|
style="display: block;padding-top: 15px;"
|
||||||
|
href="/monitoring/node/{cluster}/{item.host}"
|
||||||
|
>{item.host} ({item.subCluster})</a
|
||||||
|
>
|
||||||
|
</h4>
|
||||||
|
{#if item.disabled === false && item.data}
|
||||||
|
<MetricPlot
|
||||||
|
{width}
|
||||||
|
height={plotHeight}
|
||||||
|
timestep={item.data.metric.timestep}
|
||||||
|
series={item.data.metric.series}
|
||||||
|
metric={item.data.name}
|
||||||
|
cluster={clusters.find((c) => c.name == cluster)}
|
||||||
|
subCluster={item.subCluster}
|
||||||
|
forNode={true}
|
||||||
|
/>
|
||||||
|
{:else if item.disabled === true && item.data}
|
||||||
|
<Card style="margin-left: 2rem;margin-right: 2rem;" body color="info"
|
||||||
|
>Metric disabled for subcluster <code
|
||||||
|
>{selectedMetric}:{item.subCluster}</code
|
||||||
|
></Card
|
||||||
|
>
|
||||||
|
{:else}
|
||||||
|
<Card
|
||||||
|
style="margin-left: 2rem;margin-right: 2rem;"
|
||||||
|
body
|
||||||
|
color="warning"
|
||||||
|
>No dataset returned for <code>{selectedMetric}</code></Card
|
||||||
>
|
>
|
||||||
<h4 style="width: 100%; text-align: center;">
|
|
||||||
<a
|
|
||||||
style="display: block;padding-top: 15px;"
|
|
||||||
href="/monitoring/node/{cluster}/{item.host}"
|
|
||||||
>{item.host} ({item.subCluster})</a
|
|
||||||
>
|
|
||||||
</h4>
|
|
||||||
{#if item.disabled === false && item.data}
|
|
||||||
<MetricPlot
|
|
||||||
{width}
|
|
||||||
height={plotHeight}
|
|
||||||
timestep={item.data.metric.timestep}
|
|
||||||
series={item.data.metric.series}
|
|
||||||
metric={item.data.name}
|
|
||||||
cluster={clusters.find((c) => c.name == cluster)}
|
|
||||||
subCluster={item.subCluster}
|
|
||||||
forNode={true}
|
|
||||||
/>
|
|
||||||
{:else if item.disabled === true && item.data}
|
|
||||||
<Card style="margin-left: 2rem;margin-right: 2rem;" body color="info"
|
|
||||||
>Metric disabled for subcluster <code
|
|
||||||
>{selectedMetric}:{item.subCluster}</code
|
|
||||||
></Card
|
|
||||||
>
|
|
||||||
{:else}
|
|
||||||
<Card
|
|
||||||
style="margin-left: 2rem;margin-right: 2rem;"
|
|
||||||
body
|
|
||||||
color="warning"
|
|
||||||
>No dataset returned for <code>{selectedMetric}</code></Card
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
</PlotTable>
|
|
||||||
{/if}
|
{/if}
|
||||||
</Col>
|
</PlotGrid>
|
||||||
</Row>
|
{/if}
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
} from "./generic/utils.js";
|
} from "./generic/utils.js";
|
||||||
import JobList from "./generic/JobList.svelte";
|
import JobList from "./generic/JobList.svelte";
|
||||||
import Filters from "./generic/Filters.svelte";
|
import Filters from "./generic/Filters.svelte";
|
||||||
import PlotTable from "./generic/PlotTable.svelte";
|
import PlotGrid from "./generic/PlotGrid.svelte";
|
||||||
import Histogram from "./generic/plots/Histogram.svelte";
|
import Histogram from "./generic/plots/Histogram.svelte";
|
||||||
import MetricSelection from "./generic/select/MetricSelection.svelte";
|
import MetricSelection from "./generic/select/MetricSelection.svelte";
|
||||||
import HistogramSelection from "./generic/select/HistogramSelection.svelte";
|
import HistogramSelection from "./generic/select/HistogramSelection.svelte";
|
||||||
@ -162,7 +162,7 @@
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<br />
|
<br />
|
||||||
<Row>
|
<Row cols={{ xs: 1, md: 3}}>
|
||||||
{#if $stats.error}
|
{#if $stats.error}
|
||||||
<Col>
|
<Col>
|
||||||
<Card body color="danger">{$stats.error.message}</Card>
|
<Card body color="danger">{$stats.error.message}</Card>
|
||||||
@ -172,7 +172,7 @@
|
|||||||
<Spinner secondary />
|
<Spinner secondary />
|
||||||
</Col>
|
</Col>
|
||||||
{:else}
|
{:else}
|
||||||
<Col xs="4">
|
<Col>
|
||||||
<Table>
|
<Table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@ -210,72 +210,77 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Col>
|
</Col>
|
||||||
<div class="col-4 text-center" bind:clientWidth={w1}>
|
<Col class="text-center">
|
||||||
{#key $stats.data.jobsStatistics[0].histDuration}
|
<div bind:clientWidth={w1}>
|
||||||
<Histogram
|
{#key $stats.data.jobsStatistics[0].histDuration}
|
||||||
data={convert2uplot($stats.data.jobsStatistics[0].histDuration)}
|
<Histogram
|
||||||
width={w1 - 25}
|
data={convert2uplot($stats.data.jobsStatistics[0].histDuration)}
|
||||||
height={histogramHeight}
|
width={w1 - 25}
|
||||||
title="Duration Distribution"
|
height={histogramHeight}
|
||||||
xlabel="Current Runtimes"
|
title="Duration Distribution"
|
||||||
xunit="Hours"
|
xlabel="Current Runtimes"
|
||||||
ylabel="Number of Jobs"
|
xunit="Hours"
|
||||||
yunit="Jobs"
|
ylabel="Number of Jobs"
|
||||||
/>
|
yunit="Jobs"
|
||||||
{/key}
|
/>
|
||||||
</div>
|
{/key}
|
||||||
<div class="col-4 text-center" bind:clientWidth={w2}>
|
</div>
|
||||||
{#key $stats.data.jobsStatistics[0].histNumNodes}
|
</Col>
|
||||||
<Histogram
|
<Col class="text-center">
|
||||||
data={convert2uplot($stats.data.jobsStatistics[0].histNumNodes)}
|
<div bind:clientWidth={w2}>
|
||||||
width={w2 - 25}
|
{#key $stats.data.jobsStatistics[0].histNumNodes}
|
||||||
height={histogramHeight}
|
<Histogram
|
||||||
title="Number of Nodes Distribution"
|
data={convert2uplot($stats.data.jobsStatistics[0].histNumNodes)}
|
||||||
xlabel="Allocated Nodes"
|
width={w2 - 25}
|
||||||
xunit="Nodes"
|
height={histogramHeight}
|
||||||
ylabel="Number of Jobs"
|
title="Number of Nodes Distribution"
|
||||||
yunit="Jobs"
|
xlabel="Allocated Nodes"
|
||||||
/>
|
xunit="Nodes"
|
||||||
{/key}
|
ylabel="Number of Jobs"
|
||||||
</div>
|
yunit="Jobs"
|
||||||
|
/>
|
||||||
|
{/key}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
{/if}
|
{/if}
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
{#if metricsInHistograms}
|
{#if metricsInHistograms}
|
||||||
<Row>
|
{#if $stats.error}
|
||||||
{#if $stats.error}
|
<Row>
|
||||||
<Col>
|
<Col>
|
||||||
<Card body color="danger">{$stats.error.message}</Card>
|
<Card body color="danger">{$stats.error.message}</Card>
|
||||||
</Col>
|
</Col>
|
||||||
{:else if !$stats.data}
|
</Row>
|
||||||
|
{:else if !$stats.data}
|
||||||
|
<Row>
|
||||||
<Col>
|
<Col>
|
||||||
<Spinner secondary />
|
<Spinner secondary />
|
||||||
</Col>
|
</Col>
|
||||||
{:else}
|
</Row>
|
||||||
<Col>
|
{:else}
|
||||||
{#key $stats.data.jobsStatistics[0].histMetrics}
|
{#key $stats.data.jobsStatistics[0].histMetrics}
|
||||||
<PlotTable
|
<PlotGrid
|
||||||
let:item
|
let:item
|
||||||
let:width
|
let:width
|
||||||
renderFor="user"
|
renderFor="user"
|
||||||
items={$stats.data.jobsStatistics[0].histMetrics}
|
items={$stats.data.jobsStatistics[0].histMetrics}
|
||||||
itemsPerRow={3}
|
itemsPerRow={3}
|
||||||
>
|
>
|
||||||
<Histogram
|
<Histogram
|
||||||
data={convert2uplot(item.data)}
|
data={convert2uplot(item.data)}
|
||||||
usesBins={true}
|
usesBins={true}
|
||||||
{width}
|
{width}
|
||||||
height={250}
|
height={250}
|
||||||
title="Distribution of '{item.metric} ({item.stat})' footprints"
|
title="Distribution of '{item.metric} ({item.stat})' footprints"
|
||||||
xlabel={`${item.metric} bin maximum ${item?.unit ? `[${item.unit}]` : ``}`}
|
xlabel={`${item.metric} bin maximum ${item?.unit ? `[${item.unit}]` : ``}`}
|
||||||
xunit={item.unit}
|
xunit={item.unit}
|
||||||
ylabel="Number of Jobs"
|
ylabel="Number of Jobs"
|
||||||
yunit="Jobs"
|
yunit="Jobs"
|
||||||
/>
|
/>
|
||||||
</PlotTable>
|
</PlotGrid>
|
||||||
{/key}
|
{/key}
|
||||||
</Col>
|
{/if}
|
||||||
{/if}
|
|
||||||
</Row>
|
|
||||||
{/if}
|
{/if}
|
||||||
<br />
|
<br />
|
||||||
<Row>
|
<Row>
|
||||||
|
@ -6,7 +6,8 @@ filterPresets.cluster = cluster
|
|||||||
new Analysis({
|
new Analysis({
|
||||||
target: document.getElementById('svelte-app'),
|
target: document.getElementById('svelte-app'),
|
||||||
props: {
|
props: {
|
||||||
filterPresets: filterPresets
|
filterPresets: filterPresets,
|
||||||
|
cluster: cluster
|
||||||
},
|
},
|
||||||
context: new Map([
|
context: new Map([
|
||||||
['cc-config', clusterCockpitConfig]
|
['cc-config', clusterCockpitConfig]
|
||||||
|
60
web/frontend/src/generic/PlotGrid.svelte
Normal file
60
web/frontend/src/generic/PlotGrid.svelte
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<!--
|
||||||
|
@component Organized display of plots as bootstrap (sveltestrap) grid
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
- `itemsPerRow Number`: Elements to render per row
|
||||||
|
- `items [Any]`: List of plot components to render
|
||||||
|
- `renderFor String`: If 'job', filter disabled metrics
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
|
} from "@sveltestrap/sveltestrap";
|
||||||
|
|
||||||
|
export let itemsPerRow
|
||||||
|
export let items
|
||||||
|
export let renderFor
|
||||||
|
|
||||||
|
let rows = []
|
||||||
|
let colWidth;
|
||||||
|
const isPlaceholder = x => x._is_placeholder === true
|
||||||
|
|
||||||
|
function tile(items, itemsPerRow) {
|
||||||
|
const rows = []
|
||||||
|
for (let ri = 0; ri < items.length; ri += itemsPerRow) {
|
||||||
|
const row = []
|
||||||
|
for (let ci = 0; ci < itemsPerRow; ci += 1) {
|
||||||
|
if (ri + ci < items.length)
|
||||||
|
row.push(items[ri + ci])
|
||||||
|
else
|
||||||
|
row.push({ _is_placeholder: true, ri, ci })
|
||||||
|
}
|
||||||
|
rows.push(row)
|
||||||
|
}
|
||||||
|
return rows
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (renderFor === 'job') {
|
||||||
|
rows = tile(items.filter(item => item.disabled === false), itemsPerRow)
|
||||||
|
} else {
|
||||||
|
rows = tile(items, itemsPerRow)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each rows as row}
|
||||||
|
<Row cols={{ xs: 1, sm: 1, md: 2, lg: itemsPerRow}}>
|
||||||
|
{#each row as item (item)}
|
||||||
|
<Col class="px-1">
|
||||||
|
<div bind:clientWidth={colWidth}>
|
||||||
|
{#if !isPlaceholder(item)}
|
||||||
|
<slot item={item} width={colWidth}/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
{/each}
|
||||||
|
</Row>
|
||||||
|
{/each}
|
||||||
|
|
@ -549,6 +549,10 @@
|
|||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (series[0].data.length > 0) {
|
if (series[0].data.length > 0) {
|
||||||
|
if (forNode) {
|
||||||
|
plotWrapper.style.paddingTop = "0.5rem"
|
||||||
|
plotWrapper.style.paddingBottom = "0.5rem"
|
||||||
|
}
|
||||||
plotWrapper.style.backgroundColor = backgroundColor();
|
plotWrapper.style.backgroundColor = backgroundColor();
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
@ -562,7 +566,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if series[0].data.length > 0}
|
{#if series[0].data.length > 0}
|
||||||
<div bind:this={plotWrapper} class="cc-plot"></div>
|
<div bind:this={plotWrapper} class="cc-plot"/>
|
||||||
{:else}
|
{:else}
|
||||||
<Card class="mx-4" body color="warning"
|
<Card class="mx-4" body color="warning"
|
||||||
>Cannot render plot: No series data returned for <code>{metric}</code></Card
|
>Cannot render plot: No series data returned for <code>{metric}</code></Card
|
||||||
|
@ -46,8 +46,8 @@
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const [minX, minY] = [0., 0.];
|
const [minX, minY] = [0., 0.];
|
||||||
let maxX = X.reduce((maxX, x) => Math.max(maxX, x), minX);
|
let maxX = X ? X.reduce((maxX, x) => Math.max(maxX, x), minX) : 1.0;
|
||||||
let maxY = Y.reduce((maxY, y) => Math.max(maxY, y), minY);
|
let maxY = Y ? Y.reduce((maxY, y) => Math.max(maxY, y), minY) : 1.0;
|
||||||
const w = width - paddingLeft - paddingRight;
|
const w = width - paddingLeft - paddingRight;
|
||||||
const h = height - paddingTop - paddingBottom;
|
const h = height - paddingTop - paddingBottom;
|
||||||
|
|
||||||
@ -68,24 +68,26 @@
|
|||||||
|
|
||||||
// Draw Data
|
// Draw Data
|
||||||
let size = 3
|
let size = 3
|
||||||
if (S) {
|
if (S && X && Y) {
|
||||||
let max = S.reduce((max, s, i) => (X[i] == null || Y[i] == null || Number.isNaN(X[i]) || Number.isNaN(Y[i])) ? max : Math.max(max, s), 0)
|
let max = S.reduce((max, s, i) => (X[i] == null || Y[i] == null || Number.isNaN(X[i]) || Number.isNaN(Y[i])) ? max : Math.max(max, s), 0)
|
||||||
size = (w / 15) / max
|
size = (w / 15) / max
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.fillStyle = color;
|
ctx.fillStyle = color;
|
||||||
for (let i = 0; i < X.length; i++) {
|
if (X?.length > 0) {
|
||||||
let x = X[i], y = Y[i];
|
for (let i = 0; i < X.length; i++) {
|
||||||
if (x == null || y == null || Number.isNaN(x) || Number.isNaN(y))
|
let x = X[i], y = Y[i];
|
||||||
continue;
|
if (x == null || y == null || Number.isNaN(x) || Number.isNaN(y))
|
||||||
|
continue;
|
||||||
|
|
||||||
const s = S ? S[i] * size : size;
|
const s = S ? S[i] * size : size;
|
||||||
const px = getCanvasX(x);
|
const px = getCanvasX(x);
|
||||||
const py = getCanvasY(y);
|
const py = getCanvasY(y);
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(px, py, s, 0, Math.PI * 2, false);
|
ctx.arc(px, py, s, 0, Math.PI * 2, false);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Axes
|
// Axes
|
||||||
|
@ -397,16 +397,18 @@ function getMetricConfigDeep(metric, cluster, subCluster) {
|
|||||||
export function convert2uplot(canvasData) {
|
export function convert2uplot(canvasData) {
|
||||||
// Prep: Uplot Data Structure
|
// Prep: Uplot Data Structure
|
||||||
let uplotData = [[],[]] // [X, Y1, Y2, ...]
|
let uplotData = [[],[]] // [X, Y1, Y2, ...]
|
||||||
// Iterate
|
// Iterate if exists
|
||||||
canvasData.forEach( cd => {
|
if (canvasData) {
|
||||||
if (Object.keys(cd).length == 4) { // MetricHisto Datafromat
|
canvasData.forEach( cd => {
|
||||||
uplotData[0].push(cd?.max ? cd.max : 0)
|
if (Object.keys(cd).length == 4) { // MetricHisto Datafromat
|
||||||
uplotData[1].push(cd.count)
|
uplotData[0].push(cd?.max ? cd.max : 0)
|
||||||
} else { // Default
|
uplotData[1].push(cd.count)
|
||||||
uplotData[0].push(cd.value)
|
} else { // Default
|
||||||
uplotData[1].push(cd.count)
|
uplotData[0].push(cd.value)
|
||||||
}
|
uplotData[1].push(cd.count)
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
return uplotData
|
return uplotData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@
|
|||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<InputGroup>
|
<InputGroup class="mt-2">
|
||||||
<InputGroupText style="min-width: 150px;">
|
<InputGroupText style="min-width: 150px;">
|
||||||
{metricName} ({unit})
|
{metricName} ({unit})
|
||||||
</InputGroupText>
|
</InputGroupText>
|
||||||
|
Loading…
Reference in New Issue
Block a user