diff --git a/api/schema.graphqls b/api/schema.graphqls index ca8ab95..f5ca392 100644 --- a/api/schema.graphqls +++ b/api/schema.graphqls @@ -174,6 +174,9 @@ type JobStats { jobId: Int! startTime: Int! duration: Int! + numNodes: Int! + numHWThreads: Int + numAccelerators: Int stats: [NamedStats!]! } diff --git a/internal/graph/generated/generated.go b/internal/graph/generated/generated.go index 1eaf841..d692335 100644 --- a/internal/graph/generated/generated.go +++ b/internal/graph/generated/generated.go @@ -171,10 +171,13 @@ 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 + Duration func(childComplexity int) int + JobID func(childComplexity int) int + NumAccelerators func(childComplexity int) int + NumHWThreads func(childComplexity int) int + NumNodes func(childComplexity int) int + StartTime func(childComplexity int) int + Stats func(childComplexity int) int } JobsStatistics struct { @@ -956,6 +959,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.JobStats.JobID(childComplexity), true + case "JobStats.numAccelerators": + if e.complexity.JobStats.NumAccelerators == nil { + break + } + + return e.complexity.JobStats.NumAccelerators(childComplexity), true + + case "JobStats.numHWThreads": + if e.complexity.JobStats.NumHWThreads == nil { + break + } + + return e.complexity.JobStats.NumHWThreads(childComplexity), true + + case "JobStats.numNodes": + if e.complexity.JobStats.NumNodes == nil { + break + } + + return e.complexity.JobStats.NumNodes(childComplexity), true + case "JobStats.startTime": if e.complexity.JobStats.StartTime == nil { break @@ -2299,6 +2323,9 @@ type JobStats { jobId: Int! startTime: Int! duration: Int! + numNodes: Int! + numHWThreads: Int + numAccelerators: Int stats: [NamedStats!]! } @@ -7506,6 +7533,132 @@ func (ec *executionContext) fieldContext_JobStats_duration(_ context.Context, fi return fc, nil } +func (ec *executionContext) _JobStats_numNodes(ctx context.Context, field graphql.CollectedField, obj *model.JobStats) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_JobStats_numNodes(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.NumNodes, 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_numNodes(_ 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_numHWThreads(ctx context.Context, field graphql.CollectedField, obj *model.JobStats) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_JobStats_numHWThreads(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.NumHWThreads, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*int) + fc.Result = res + return ec.marshalOInt2ᚖint(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_JobStats_numHWThreads(_ 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_numAccelerators(ctx context.Context, field graphql.CollectedField, obj *model.JobStats) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_JobStats_numAccelerators(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.NumAccelerators, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*int) + fc.Result = res + return ec.marshalOInt2ᚖint(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_JobStats_numAccelerators(_ 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 { @@ -11307,6 +11460,12 @@ func (ec *executionContext) fieldContext_Query_jobsMetricStats(ctx context.Conte return ec.fieldContext_JobStats_startTime(ctx, field) case "duration": return ec.fieldContext_JobStats_duration(ctx, field) + case "numNodes": + return ec.fieldContext_JobStats_numNodes(ctx, field) + case "numHWThreads": + return ec.fieldContext_JobStats_numHWThreads(ctx, field) + case "numAccelerators": + return ec.fieldContext_JobStats_numAccelerators(ctx, field) case "stats": return ec.fieldContext_JobStats_stats(ctx, field) } @@ -17647,6 +17806,15 @@ func (ec *executionContext) _JobStats(ctx context.Context, sel ast.SelectionSet, if out.Values[i] == graphql.Null { out.Invalids++ } + case "numNodes": + out.Values[i] = ec._JobStats_numNodes(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "numHWThreads": + out.Values[i] = ec._JobStats_numHWThreads(ctx, field, obj) + case "numAccelerators": + out.Values[i] = ec._JobStats_numAccelerators(ctx, field, obj) case "stats": out.Values[i] = ec._JobStats_stats(ctx, field, obj) if out.Values[i] == graphql.Null { diff --git a/internal/graph/model/models_gen.go b/internal/graph/model/models_gen.go index d4486fc..d493844 100644 --- a/internal/graph/model/models_gen.go +++ b/internal/graph/model/models_gen.go @@ -97,10 +97,13 @@ type JobResultList struct { } type JobStats struct { - JobID int `json:"jobId"` - StartTime int `json:"startTime"` - Duration int `json:"duration"` - Stats []*NamedStats `json:"stats"` + JobID int `json:"jobId"` + StartTime int `json:"startTime"` + Duration int `json:"duration"` + NumNodes int `json:"numNodes"` + NumHWThreads *int `json:"numHWThreads,omitempty"` + NumAccelerators *int `json:"numAccelerators,omitempty"` + Stats []*NamedStats `json:"stats"` } type JobsStatistics struct { diff --git a/internal/graph/schema.resolvers.go b/internal/graph/schema.resolvers.go index 771565b..6176402 100644 --- a/internal/graph/schema.resolvers.go +++ b/internal/graph/schema.resolvers.go @@ -615,11 +615,16 @@ func (r *queryResolver) JobsMetricStats(ctx context.Context, filter []*model.Job }) } + numThreadsInt := int(job.NumHWThreads) + numAccsInt := int(job.NumAcc) res = append(res, &model.JobStats{ - JobID: int(job.JobID), - StartTime: int(job.StartTime.Unix()), - Duration: int(job.Duration), - Stats: sres, + JobID: int(job.JobID), + StartTime: int(job.StartTime.Unix()), + Duration: int(job.Duration), + NumNodes: int(job.NumNodes), + NumHWThreads: &numThreadsInt, + NumAccelerators: &numAccsInt, + Stats: sres, }) } return res, err diff --git a/internal/repository/jobQuery.go b/internal/repository/jobQuery.go index d169b6f..3f75ec6 100644 --- a/internal/repository/jobQuery.go +++ b/internal/repository/jobQuery.go @@ -149,6 +149,14 @@ func BuildWhereClause(filter *model.JobFilter, query sq.SelectBuilder) sq.Select if filter.JobID != nil { query = buildStringCondition("job.job_id", filter.JobID, query) } + if filter.JobIds != nil { + jobIds := make([]string, len(filter.JobIds)) + for i, val := range filter.JobIds { + jobIds[i] = string(val) + } + + query = query.Where(sq.Eq{"job.job_id": jobIds}) + } if filter.ArrayJobID != nil { query = query.Where("job.array_job_id = ?", *filter.ArrayJobID) } diff --git a/web/frontend/src/generic/JobCompare.svelte b/web/frontend/src/generic/JobCompare.svelte index ba40bcc..2ff3337 100644 --- a/web/frontend/src/generic/JobCompare.svelte +++ b/web/frontend/src/generic/JobCompare.svelte @@ -15,6 +15,7 @@