mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-03-04 05:55:56 +01:00
simplify polar plot data code, add scaling for shared jobs to polar
This commit is contained in:
parent
140b3c371d
commit
8af92b1557
@ -303,6 +303,7 @@ func (r *queryResolver) JobMetrics(ctx context.Context, id string, metrics []str
|
|||||||
|
|
||||||
// JobsFootprints is the resolver for the jobsFootprints field.
|
// JobsFootprints is the resolver for the jobsFootprints field.
|
||||||
func (r *queryResolver) JobsFootprints(ctx context.Context, filter []*model.JobFilter, metrics []string) (*model.Footprints, error) {
|
func (r *queryResolver) JobsFootprints(ctx context.Context, filter []*model.JobFilter, metrics []string) (*model.Footprints, error) {
|
||||||
|
// NOTE: Legacy Naming! This resolver is for normalized histograms in analysis view only - *Not* related to DB "footprint" column!
|
||||||
return r.jobsFootprints(ctx, filter, metrics)
|
return r.jobsFootprints(ctx, filter, metrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,16 +2,12 @@
|
|||||||
@component Polar Plot based on chartJS Radar
|
@component Polar Plot based on chartJS Radar
|
||||||
|
|
||||||
Properties:
|
Properties:
|
||||||
- `footprintData [Object]?`: job.footprint content, evaluated in regards to peak config in jobSummary.svelte [Default: null]
|
- `polarMetrics [Object]?`: Metric names and scaled peak values for rendering polar plot [Default: [] ]
|
||||||
- `metrics [String]?`: Metric names to display as polar plot [Default: null]
|
|
||||||
- `cluster GraphQL.Cluster?`: Cluster Object of the parent job [Default: null]
|
|
||||||
- `subCluster GraphQL.SubCluster?`: SubCluster Object of the parent job [Default: null]
|
|
||||||
- `jobMetrics [GraphQL.JobMetricWithName]?`: Metric data [Default: null]
|
- `jobMetrics [GraphQL.JobMetricWithName]?`: Metric data [Default: null]
|
||||||
- `height Number?`: Plot height [Default: 365]
|
- `height Number?`: Plot height [Default: 365]
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from 'svelte'
|
|
||||||
import { Radar } from 'svelte-chartjs';
|
import { Radar } from 'svelte-chartjs';
|
||||||
import {
|
import {
|
||||||
Chart as ChartJS,
|
Chart as ChartJS,
|
||||||
@ -34,54 +30,37 @@
|
|||||||
LineElement
|
LineElement
|
||||||
);
|
);
|
||||||
|
|
||||||
export let footprintData = null;
|
export let polarMetrics = [];
|
||||||
export let metrics = null;
|
|
||||||
export let cluster = null;
|
|
||||||
export let subCluster = null;
|
|
||||||
export let jobMetrics = null;
|
export let jobMetrics = null;
|
||||||
export let height = 350;
|
export let height = 350;
|
||||||
|
|
||||||
function getLabels() {
|
const labels = polarMetrics
|
||||||
if (footprintData) {
|
.filter((m) => (m.peak != null))
|
||||||
return footprintData.filter(fpd => {
|
.map(pm => pm.name)
|
||||||
if (!jobMetrics.find(m => m.name == fpd.name && m.scope == "node" || fpd.impact == 4)) {
|
.sort(function (a, b) {return ((a > b) ? 1 : ((b > a) ? -1 : 0))});
|
||||||
console.warn(`PolarPlot: No metric data for '${fpd.name}'`)
|
|
||||||
return false
|
function loadData(type) {
|
||||||
}
|
if (!labels) {
|
||||||
return true
|
console.warn("Empty 'metrics' array prop! Cannot render Polar.")
|
||||||
})
|
return []
|
||||||
.map(filtered => filtered.name)
|
|
||||||
.sort(function (a, b) {
|
|
||||||
return ((a > b) ? 1 : ((b > a) ? -1 : 0));
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
return metrics.filter(name => {
|
if (type === 'avg') {
|
||||||
if (!jobMetrics.find(m => m.name == name && m.scope == "node")) {
|
return getValues(getAvg)
|
||||||
console.warn(`PolarPlot: No metric data for '${name}'`)
|
} else if (type === 'max') {
|
||||||
return false
|
return getValues(getMax)
|
||||||
|
} else if (type === 'min') {
|
||||||
|
return getValues(getMin)
|
||||||
}
|
}
|
||||||
return true
|
console.log('Unknown Type For Polar Data')
|
||||||
})
|
return []
|
||||||
.sort(function (a, b) {
|
|
||||||
return ((a > b) ? 1 : ((b > a) ? -1 : 0));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const labels = getLabels();
|
// Helpers
|
||||||
const getMetricConfig = getContext("getMetricConfig");
|
|
||||||
|
|
||||||
const getValuesForStatGeneric = (getStat) => labels.map(name => {
|
const getValues = (getStat) => labels.map(name => {
|
||||||
// TODO: Requires Scaling if Shared Job
|
// Peak is adapted and scaled for job shared state
|
||||||
const peak = getMetricConfig(cluster, subCluster, name).peak
|
const peak = polarMetrics.find(m => m.name == name).peak
|
||||||
const metric = jobMetrics.find(m => m.name == name && m.scope == "node")
|
|
||||||
const value = getStat(metric.metric) / peak
|
|
||||||
return value <= 1. ? value : 1.
|
|
||||||
})
|
|
||||||
|
|
||||||
const getValuesForStatFootprint = (getStat) => labels.map(name => {
|
|
||||||
// FootprintData 'Peak' is pre-scaled for Shared Jobs in JobSummary Component
|
|
||||||
const peak = footprintData.find(fpd => fpd.name === name).peak
|
|
||||||
const metric = jobMetrics.find(m => m.name == name && m.scope == "node")
|
const metric = jobMetrics.find(m => m.name == name && m.scope == "node")
|
||||||
const value = getStat(metric.metric) / peak
|
const value = getStat(metric.metric) / peak
|
||||||
return value <= 1. ? value : 1.
|
return value <= 1. ? value : 1.
|
||||||
@ -108,36 +87,14 @@
|
|||||||
return avg / metric.series.length
|
return avg / metric.series.length
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadDataGeneric(type) {
|
// Chart JS Objects
|
||||||
if (type === 'avg') {
|
|
||||||
return getValuesForStatGeneric(getAvg)
|
|
||||||
} else if (type === 'max') {
|
|
||||||
return getValuesForStatGeneric(getMax)
|
|
||||||
} else if (type === 'min') {
|
|
||||||
return getValuesForStatGeneric(getMin)
|
|
||||||
}
|
|
||||||
console.log('Unknown Type For Polar Data')
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadDataForFootprint(type) {
|
|
||||||
if (type === 'avg') {
|
|
||||||
return getValuesForStatFootprint(getAvg)
|
|
||||||
} else if (type === 'max') {
|
|
||||||
return getValuesForStatFootprint(getMax)
|
|
||||||
} else if (type === 'min') {
|
|
||||||
return getValuesForStatFootprint(getMin)
|
|
||||||
}
|
|
||||||
console.log('Unknown Type For Polar Data')
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
labels: labels,
|
labels: labels,
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: 'Max',
|
label: 'Max',
|
||||||
data: footprintData ? loadDataForFootprint('max') : loadDataGeneric('max'), // Node Scope Only
|
data: loadData('max'), // Node Scope Only
|
||||||
fill: 1,
|
fill: 1,
|
||||||
backgroundColor: 'rgba(0, 0, 255, 0.25)',
|
backgroundColor: 'rgba(0, 0, 255, 0.25)',
|
||||||
borderColor: 'rgb(0, 0, 255)',
|
borderColor: 'rgb(0, 0, 255)',
|
||||||
@ -148,7 +105,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Avg',
|
label: 'Avg',
|
||||||
data: footprintData ? loadDataForFootprint('avg') : loadDataGeneric('avg'), // Node Scope Only
|
data: loadData('avg'), // Node Scope Only
|
||||||
fill: 2,
|
fill: 2,
|
||||||
backgroundColor: 'rgba(255, 210, 0, 0.25)',
|
backgroundColor: 'rgba(255, 210, 0, 0.25)',
|
||||||
borderColor: 'rgb(255, 210, 0)',
|
borderColor: 'rgb(255, 210, 0)',
|
||||||
@ -159,7 +116,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Min',
|
label: 'Min',
|
||||||
data: footprintData ? loadDataForFootprint('min') : loadDataGeneric('min'), // Node Scope Only
|
data: loadData('min'), // Node Scope Only
|
||||||
fill: true,
|
fill: true,
|
||||||
backgroundColor: 'rgba(255, 0, 0, 0.25)',
|
backgroundColor: 'rgba(255, 0, 0, 0.25)',
|
||||||
borderColor: 'rgb(255, 0, 0)',
|
borderColor: 'rgb(255, 0, 0)',
|
||||||
|
@ -30,9 +30,18 @@
|
|||||||
export let height = "400px";
|
export let height = "400px";
|
||||||
|
|
||||||
const ccconfig = getContext("cc-config")
|
const ccconfig = getContext("cc-config")
|
||||||
const showFootprint = !!ccconfig[`job_view_showFootprint`];
|
const showFootprintTab = !!ccconfig[`job_view_showFootprint`];
|
||||||
|
|
||||||
const footprintData = job?.footprint?.map((jf) => {
|
const polarMetrics = job?.footprint?.map((jf) => {
|
||||||
|
const fmt = findJobFootprintThresholds(job, jf.stat, getContext("getMetricConfig")(job.cluster, job.subCluster, jf.name));
|
||||||
|
// If no matching metric config: Metric will be omitted in polar
|
||||||
|
return {
|
||||||
|
name: jf.name,
|
||||||
|
peak: fmt ? fmt.peak : null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const footprintData = !showFootprintTab ? null : job?.footprint?.map((jf) => {
|
||||||
const fmc = getContext("getMetricConfig")(job.cluster, job.subCluster, jf.name);
|
const fmc = getContext("getMetricConfig")(job.cluster, job.subCluster, jf.name);
|
||||||
if (fmc) {
|
if (fmc) {
|
||||||
// Unit
|
// Unit
|
||||||
@ -193,7 +202,7 @@
|
|||||||
|
|
||||||
<Card class="overflow-auto" style="width: {width}; height: {height}">
|
<Card class="overflow-auto" style="width: {width}; height: {height}">
|
||||||
<TabContent> <!-- on:tab={(e) => (status = e.detail)} -->
|
<TabContent> <!-- on:tab={(e) => (status = e.detail)} -->
|
||||||
{#if showFootprint}
|
{#if showFootprintTab}
|
||||||
<TabPane tabId="foot" tab="Footprint" active>
|
<TabPane tabId="foot" tab="Footprint" active>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
{#each footprintData as fpd, index}
|
{#each footprintData as fpd, index}
|
||||||
@ -279,10 +288,10 @@
|
|||||||
</CardBody>
|
</CardBody>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
{/if}
|
{/if}
|
||||||
<TabPane tabId="polar" tab="Polar" active={!showFootprint}>
|
<TabPane tabId="polar" tab="Polar" active={!showFootprintTab}>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<Polar
|
<Polar
|
||||||
{footprintData}
|
{polarMetrics}
|
||||||
{jobMetrics}
|
{jobMetrics}
|
||||||
/>
|
/>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user