mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-05-14 05:01:41 +02:00
add job duration, add starttime and duration to legend
This commit is contained in:
parent
fd52fdd35b
commit
33ecfe88ef
@ -173,6 +173,7 @@ type ScopedStats {
|
||||
type JobStats {
|
||||
jobId: Int!
|
||||
startTime: Int!
|
||||
duration: Int!
|
||||
stats: [NamedStats!]!
|
||||
}
|
||||
|
||||
|
@ -171,6 +171,7 @@ type ComplexityRoot struct {
|
||||
}
|
||||
|
||||
JobStats struct {
|
||||
Duration func(childComplexity int) int
|
||||
JobID func(childComplexity int) int
|
||||
StartTime func(childComplexity int) int
|
||||
Stats func(childComplexity int) int
|
||||
@ -941,6 +942,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.JobResultList.Offset(childComplexity), true
|
||||
|
||||
case "JobStats.duration":
|
||||
if e.complexity.JobStats.Duration == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.JobStats.Duration(childComplexity), true
|
||||
|
||||
case "JobStats.jobId":
|
||||
if e.complexity.JobStats.JobID == nil {
|
||||
break
|
||||
@ -2290,6 +2298,7 @@ type ScopedStats {
|
||||
type JobStats {
|
||||
jobId: Int!
|
||||
startTime: Int!
|
||||
duration: Int!
|
||||
stats: [NamedStats!]!
|
||||
}
|
||||
|
||||
@ -7453,6 +7462,50 @@ func (ec *executionContext) fieldContext_JobStats_startTime(_ context.Context, f
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _JobStats_duration(ctx context.Context, field graphql.CollectedField, obj *model.JobStats) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_JobStats_duration(ctx, field)
|
||||
if err != nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Duration, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(int)
|
||||
fc.Result = res
|
||||
return ec.marshalNInt2int(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_JobStats_duration(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "JobStats",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type Int does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _JobStats_stats(ctx context.Context, field graphql.CollectedField, obj *model.JobStats) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_JobStats_stats(ctx, field)
|
||||
if err != nil {
|
||||
@ -11252,6 +11305,8 @@ func (ec *executionContext) fieldContext_Query_jobsMetricStats(ctx context.Conte
|
||||
return ec.fieldContext_JobStats_jobId(ctx, field)
|
||||
case "startTime":
|
||||
return ec.fieldContext_JobStats_startTime(ctx, field)
|
||||
case "duration":
|
||||
return ec.fieldContext_JobStats_duration(ctx, field)
|
||||
case "stats":
|
||||
return ec.fieldContext_JobStats_stats(ctx, field)
|
||||
}
|
||||
@ -17587,6 +17642,11 @@ func (ec *executionContext) _JobStats(ctx context.Context, sel ast.SelectionSet,
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
case "duration":
|
||||
out.Values[i] = ec._JobStats_duration(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
case "stats":
|
||||
out.Values[i] = ec._JobStats_stats(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
|
@ -99,6 +99,7 @@ type JobResultList struct {
|
||||
type JobStats struct {
|
||||
JobID int `json:"jobId"`
|
||||
StartTime int `json:"startTime"`
|
||||
Duration int `json:"duration"`
|
||||
Stats []*NamedStats `json:"stats"`
|
||||
}
|
||||
|
||||
|
@ -618,6 +618,7 @@ func (r *queryResolver) JobsMetricStats(ctx context.Context, filter []*model.Job
|
||||
res = append(res, &model.JobStats{
|
||||
JobID: int(job.JobID),
|
||||
StartTime: int(job.StartTime.Unix()),
|
||||
Duration: int(job.Duration),
|
||||
Stats: sres,
|
||||
})
|
||||
}
|
||||
|
@ -39,7 +39,6 @@
|
||||
let filter = [...filterBuffer];
|
||||
let comparePlotData = {};
|
||||
let jobIds = [];
|
||||
let jobStarts = [];
|
||||
const sorting = { field: "startTime", type: "col", order: "DESC" };
|
||||
|
||||
/* GQL */
|
||||
@ -51,6 +50,7 @@
|
||||
jobsMetricStats(filter: $filter, metrics: $metrics) {
|
||||
jobId
|
||||
startTime
|
||||
duration
|
||||
stats {
|
||||
name
|
||||
data {
|
||||
@ -74,7 +74,6 @@
|
||||
$: matchedCompareJobs = $compareData.data != null ? $compareData.data.jobsMetricStats.length : -1;
|
||||
$: if ($compareData.data != null) {
|
||||
jobIds = [];
|
||||
jobStarts = [];
|
||||
comparePlotData = {}
|
||||
jobs2uplot($compareData.data.jobsMetricStats, metrics)
|
||||
}
|
||||
@ -116,7 +115,7 @@
|
||||
const rawUnit = globalMetrics.find((gm) => gm.name == m)?.unit
|
||||
const metricUnit = (rawUnit?.prefix ? rawUnit.prefix : "") + (rawUnit?.base ? rawUnit.base : "")
|
||||
// Init
|
||||
comparePlotData[m] = {unit: metricUnit, data: [[],[],[],[]]} // data: [X, Y1, Y2, Y3]
|
||||
comparePlotData[m] = {unit: metricUnit, data: [[],[],[],[],[],[]]} // data: [X, XST, XRT, YMIN, YAVG, YMAX]
|
||||
}
|
||||
|
||||
// Iterate jobs if exists
|
||||
@ -124,13 +123,13 @@
|
||||
let plotIndex = 0
|
||||
jobs.forEach((j) => {
|
||||
jobIds.push(j.jobId)
|
||||
jobStarts.push(j.startTime)
|
||||
for (let s of j.stats) {
|
||||
// comparePlotData[s.name].data[0].push(j.startTime)
|
||||
comparePlotData[s.name].data[0].push(plotIndex)
|
||||
comparePlotData[s.name].data[1].push(s.data.min)
|
||||
comparePlotData[s.name].data[2].push(s.data.avg)
|
||||
comparePlotData[s.name].data[3].push(s.data.max)
|
||||
comparePlotData[s.name].data[1].push(j.startTime)
|
||||
comparePlotData[s.name].data[2].push(j.duration)
|
||||
comparePlotData[s.name].data[3].push(s.data.min)
|
||||
comparePlotData[s.name].data[4].push(s.data.avg)
|
||||
comparePlotData[s.name].data[5].push(s.data.max)
|
||||
}
|
||||
plotIndex++
|
||||
})
|
||||
@ -186,7 +185,6 @@
|
||||
title={'Compare '+ m}
|
||||
xlabel="JobIds"
|
||||
xticks={jobIds}
|
||||
xtimes={jobStarts}
|
||||
ylabel={m}
|
||||
metric={m}
|
||||
yunit={comparePlotData[m].unit}
|
||||
|
@ -24,15 +24,15 @@
|
||||
export let data;
|
||||
export let xlabel;
|
||||
export let xticks;
|
||||
export let xtimes;
|
||||
export let ylabel;
|
||||
export let yunit;
|
||||
export let title;
|
||||
// export let cluster = "";
|
||||
// export let subCluster = "";
|
||||
|
||||
// $: console.log('LABEL:', metric, yunit)
|
||||
// $: console.log('DATA:', data)
|
||||
$: console.log('LABEL:', metric, yunit)
|
||||
$: console.log('DATA:', data)
|
||||
$: console.log('XTICKS:', xticks)
|
||||
|
||||
const metricConfig = null // DEBUG FILLER
|
||||
// const metricConfig = getContext("getMetricConfig")(cluster, subCluster, metric); // Args woher
|
||||
@ -40,6 +40,22 @@
|
||||
const lineWidth = clusterCockpitConfig.plot_general_lineWidth / window.devicePixelRatio;
|
||||
const cbmode = clusterCockpitConfig?.plot_general_colorblindMode || false;
|
||||
|
||||
// Format Seconds to hh:mm
|
||||
function formatTime(t) {
|
||||
if (t !== null) {
|
||||
if (isNaN(t)) {
|
||||
return t;
|
||||
} else {
|
||||
const tAbs = Math.abs(t);
|
||||
const h = Math.floor(tAbs / 3600);
|
||||
const m = Math.floor((tAbs % 3600) / 60);
|
||||
if (h == 0) return `${m}m`;
|
||||
else if (m == 0) return `${h}h`;
|
||||
else return `${h}:${m}h`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UPLOT PLUGIN // converts the legend into a simple tooltip
|
||||
function legendAsTooltipPlugin({
|
||||
className,
|
||||
@ -120,34 +136,48 @@
|
||||
const plotSeries = [
|
||||
{
|
||||
label: "JobID",
|
||||
scale: "x",
|
||||
value: (u, ts, sidx, didx) => {
|
||||
return xticks[didx] + ' (' + new Date(xtimes[didx] * 1000).toLocaleString() + ')';
|
||||
return xticks[didx];
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Starttime",
|
||||
scale: "xst",
|
||||
value: (u, ts, sidx, didx) => {
|
||||
return new Date(ts * 1000).toLocaleString();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Duration",
|
||||
scale: "xrt",
|
||||
value: (u, ts, sidx, didx) => {
|
||||
return formatTime(ts);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Min",
|
||||
scale: "y",
|
||||
width: lineWidth,
|
||||
stroke: cbmode ? "rgb(0,255,0)" : "red",
|
||||
},
|
||||
{
|
||||
label: "Avg",
|
||||
scale: "y",
|
||||
width: lineWidth,
|
||||
stroke: "black",
|
||||
},
|
||||
{
|
||||
label: "Max",
|
||||
scale: "y",
|
||||
width: lineWidth,
|
||||
stroke: cbmode ? "rgb(0,0,255)" : "green",
|
||||
}
|
||||
];
|
||||
|
||||
plotSeries.push({
|
||||
label: "min",
|
||||
scale: "y",
|
||||
width: lineWidth,
|
||||
stroke: cbmode ? "rgb(0,255,0)" : "red",
|
||||
});
|
||||
plotSeries.push({
|
||||
label: "avg",
|
||||
scale: "y",
|
||||
width: lineWidth,
|
||||
stroke: "black",
|
||||
});
|
||||
plotSeries.push({
|
||||
label: "max",
|
||||
scale: "y",
|
||||
width: lineWidth,
|
||||
stroke: cbmode ? "rgb(0,0,255)" : "green",
|
||||
});
|
||||
|
||||
const plotBands = [
|
||||
{ series: [3, 2], fill: cbmode ? "rgba(0,0,255,0.1)" : "rgba(0,255,0,0.1)" },
|
||||
{ series: [2, 1], fill: cbmode ? "rgba(0,255,0,0.1)" : "rgba(255,0,0,0.1)" },
|
||||
{ series: [5, 4], fill: cbmode ? "rgba(0,0,255,0.1)" : "rgba(0,255,0,0.1)" },
|
||||
{ series: [4, 3], fill: cbmode ? "rgba(0,255,0,0.1)" : "rgba(255,0,0,0.1)" },
|
||||
];
|
||||
|
||||
const opts = {
|
||||
@ -167,6 +197,14 @@
|
||||
return splits.map(s => xticks[s]);
|
||||
}
|
||||
},
|
||||
{
|
||||
scale: "xst",
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
scale: "xrt",
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
scale: "y",
|
||||
grid: { show: true },
|
||||
@ -180,8 +218,8 @@
|
||||
draw: [
|
||||
(u) => {
|
||||
// Draw plot type label:
|
||||
let textl = "Jobs min/avg/max";
|
||||
let textr = "";
|
||||
let textl = "Metric Min/Avg/Max in Duration";
|
||||
let textr = "Earlier <- StartTime -> Later";
|
||||
u.ctx.save();
|
||||
u.ctx.textAlign = "start"; // 'end'
|
||||
u.ctx.fillStyle = "black";
|
||||
@ -216,6 +254,8 @@
|
||||
},
|
||||
scales: {
|
||||
x: { time: false },
|
||||
xst: { time: false },
|
||||
xrt: { time: false },
|
||||
y: maxY ? { min: 0, max: (maxY * 1.1) } : {auto: true}, // Add some space to upper render limit
|
||||
},
|
||||
legend: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user