mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-02-04 15:25:17 +01:00
Merge branch 'dev' into add_detailed_nodelist
This commit is contained in:
commit
571652c314
@ -245,7 +245,7 @@ type Query {
|
|||||||
jobsFootprints(filter: [JobFilter!], metrics: [String!]!): Footprints
|
jobsFootprints(filter: [JobFilter!], metrics: [String!]!): Footprints
|
||||||
|
|
||||||
jobs(filter: [JobFilter!], page: PageRequest, order: OrderByInput): JobResultList!
|
jobs(filter: [JobFilter!], page: PageRequest, order: OrderByInput): JobResultList!
|
||||||
jobsStatistics(filter: [JobFilter!], metrics: [String!], page: PageRequest, sortBy: SortByAggregate, groupBy: Aggregate): [JobsStatistics!]!
|
jobsStatistics(filter: [JobFilter!], metrics: [String!], page: PageRequest, sortBy: SortByAggregate, groupBy: Aggregate, numDurationBins: String, numMetricBins: Int): [JobsStatistics!]!
|
||||||
|
|
||||||
rooflineHeatmap(filter: [JobFilter!]!, rows: Int!, cols: Int!, minX: Float!, minY: Float!, maxX: Float!, maxY: Float!): [[Float!]!]!
|
rooflineHeatmap(filter: [JobFilter!]!, rows: Int!, cols: Int!, minX: Float!, minY: Float!, maxX: Float!, maxY: Float!): [[Float!]!]!
|
||||||
|
|
||||||
|
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
|||||||
module github.com/ClusterCockpit/cc-backend
|
module github.com/ClusterCockpit/cc-backend
|
||||||
|
|
||||||
go 1.23
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/99designs/gqlgen v0.17.57
|
github.com/99designs/gqlgen v0.17.57
|
||||||
|
@ -48,8 +48,7 @@ type ResolverRoot interface {
|
|||||||
SubCluster() SubClusterResolver
|
SubCluster() SubClusterResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
type DirectiveRoot struct {
|
type DirectiveRoot struct{}
|
||||||
}
|
|
||||||
|
|
||||||
type ComplexityRoot struct {
|
type ComplexityRoot struct {
|
||||||
Accelerator struct {
|
Accelerator struct {
|
||||||
@ -266,7 +265,7 @@ type ComplexityRoot struct {
|
|||||||
JobMetrics func(childComplexity int, id string, metrics []string, scopes []schema.MetricScope, resolution *int) int
|
JobMetrics func(childComplexity int, id string, metrics []string, scopes []schema.MetricScope, resolution *int) int
|
||||||
Jobs func(childComplexity int, filter []*model.JobFilter, page *model.PageRequest, order *model.OrderByInput) int
|
Jobs func(childComplexity int, filter []*model.JobFilter, page *model.PageRequest, order *model.OrderByInput) int
|
||||||
JobsFootprints func(childComplexity int, filter []*model.JobFilter, metrics []string) int
|
JobsFootprints func(childComplexity int, filter []*model.JobFilter, metrics []string) int
|
||||||
JobsStatistics func(childComplexity int, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate) int
|
JobsStatistics func(childComplexity int, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate, numDurationBins *string, numMetricBins *int) int
|
||||||
NodeMetrics func(childComplexity int, cluster string, nodes []string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time) int
|
NodeMetrics func(childComplexity int, cluster string, nodes []string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time) int
|
||||||
NodeMetricsList func(childComplexity int, cluster string, subCluster string, nodeFilter string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time, page *model.PageRequest, resolution *int) int
|
NodeMetricsList func(childComplexity int, cluster string, subCluster string, nodeFilter string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time, page *model.PageRequest, resolution *int) int
|
||||||
RooflineHeatmap func(childComplexity int, filter []*model.JobFilter, rows int, cols int, minX float64, minY float64, maxX float64, maxY float64) int
|
RooflineHeatmap func(childComplexity int, filter []*model.JobFilter, rows int, cols int, minX float64, minY float64, maxX float64, maxY float64) int
|
||||||
@ -392,7 +391,7 @@ type QueryResolver interface {
|
|||||||
JobMetrics(ctx context.Context, id string, metrics []string, scopes []schema.MetricScope, resolution *int) ([]*model.JobMetricWithName, error)
|
JobMetrics(ctx context.Context, id string, metrics []string, scopes []schema.MetricScope, resolution *int) ([]*model.JobMetricWithName, error)
|
||||||
JobsFootprints(ctx context.Context, filter []*model.JobFilter, metrics []string) (*model.Footprints, error)
|
JobsFootprints(ctx context.Context, filter []*model.JobFilter, metrics []string) (*model.Footprints, error)
|
||||||
Jobs(ctx context.Context, filter []*model.JobFilter, page *model.PageRequest, order *model.OrderByInput) (*model.JobResultList, error)
|
Jobs(ctx context.Context, filter []*model.JobFilter, page *model.PageRequest, order *model.OrderByInput) (*model.JobResultList, error)
|
||||||
JobsStatistics(ctx context.Context, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate) ([]*model.JobsStatistics, error)
|
JobsStatistics(ctx context.Context, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate, numDurationBins *string, numMetricBins *int) ([]*model.JobsStatistics, error)
|
||||||
RooflineHeatmap(ctx context.Context, filter []*model.JobFilter, rows int, cols int, minX float64, minY float64, maxX float64, maxY float64) ([][]float64, error)
|
RooflineHeatmap(ctx context.Context, filter []*model.JobFilter, rows int, cols int, minX float64, minY float64, maxX float64, maxY float64) ([][]float64, error)
|
||||||
NodeMetrics(ctx context.Context, cluster string, nodes []string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time) ([]*model.NodeMetrics, error)
|
NodeMetrics(ctx context.Context, cluster string, nodes []string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time) ([]*model.NodeMetrics, error)
|
||||||
NodeMetricsList(ctx context.Context, cluster string, subCluster string, nodeFilter string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time, page *model.PageRequest, resolution *int) (*model.NodesResultList, error)
|
NodeMetricsList(ctx context.Context, cluster string, subCluster string, nodeFilter string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time, page *model.PageRequest, resolution *int) (*model.NodesResultList, error)
|
||||||
@ -1425,7 +1424,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.Query.JobsStatistics(childComplexity, args["filter"].([]*model.JobFilter), args["metrics"].([]string), args["page"].(*model.PageRequest), args["sortBy"].(*model.SortByAggregate), args["groupBy"].(*model.Aggregate)), true
|
return e.complexity.Query.JobsStatistics(childComplexity, args["filter"].([]*model.JobFilter), args["metrics"].([]string), args["page"].(*model.PageRequest), args["sortBy"].(*model.SortByAggregate), args["groupBy"].(*model.Aggregate), args["numDurationBins"].(*string), args["numMetricBins"].(*int)), true
|
||||||
|
|
||||||
case "Query.nodeMetrics":
|
case "Query.nodeMetrics":
|
||||||
if e.complexity.Query.NodeMetrics == nil {
|
if e.complexity.Query.NodeMetrics == nil {
|
||||||
@ -2206,7 +2205,7 @@ type Query {
|
|||||||
jobsFootprints(filter: [JobFilter!], metrics: [String!]!): Footprints
|
jobsFootprints(filter: [JobFilter!], metrics: [String!]!): Footprints
|
||||||
|
|
||||||
jobs(filter: [JobFilter!], page: PageRequest, order: OrderByInput): JobResultList!
|
jobs(filter: [JobFilter!], page: PageRequest, order: OrderByInput): JobResultList!
|
||||||
jobsStatistics(filter: [JobFilter!], metrics: [String!], page: PageRequest, sortBy: SortByAggregate, groupBy: Aggregate): [JobsStatistics!]!
|
jobsStatistics(filter: [JobFilter!], metrics: [String!], page: PageRequest, sortBy: SortByAggregate, groupBy: Aggregate, numDurationBins: String, numMetricBins: Int): [JobsStatistics!]!
|
||||||
|
|
||||||
rooflineHeatmap(filter: [JobFilter!]!, rows: Int!, cols: Int!, minX: Float!, minY: Float!, maxX: Float!, maxY: Float!): [[Float!]!]!
|
rooflineHeatmap(filter: [JobFilter!]!, rows: Int!, cols: Int!, minX: Float!, minY: Float!, maxX: Float!, maxY: Float!): [[Float!]!]!
|
||||||
|
|
||||||
@ -2359,6 +2358,7 @@ func (ec *executionContext) field_Mutation_addTagsToJob_args(ctx context.Context
|
|||||||
args["tagIds"] = arg1
|
args["tagIds"] = arg1
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_addTagsToJob_argsJob(
|
func (ec *executionContext) field_Mutation_addTagsToJob_argsJob(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -2423,6 +2423,7 @@ func (ec *executionContext) field_Mutation_createTag_args(ctx context.Context, r
|
|||||||
args["scope"] = arg2
|
args["scope"] = arg2
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_createTag_argsType(
|
func (ec *executionContext) field_Mutation_createTag_argsType(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -2499,6 +2500,7 @@ func (ec *executionContext) field_Mutation_deleteTag_args(ctx context.Context, r
|
|||||||
args["id"] = arg0
|
args["id"] = arg0
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_deleteTag_argsID(
|
func (ec *executionContext) field_Mutation_deleteTag_argsID(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -2536,6 +2538,7 @@ func (ec *executionContext) field_Mutation_removeTagsFromJob_args(ctx context.Co
|
|||||||
args["tagIds"] = arg1
|
args["tagIds"] = arg1
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_removeTagsFromJob_argsJob(
|
func (ec *executionContext) field_Mutation_removeTagsFromJob_argsJob(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -2595,6 +2598,7 @@ func (ec *executionContext) field_Mutation_updateConfiguration_args(ctx context.
|
|||||||
args["value"] = arg1
|
args["value"] = arg1
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_updateConfiguration_argsName(
|
func (ec *executionContext) field_Mutation_updateConfiguration_argsName(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -2649,6 +2653,7 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs
|
|||||||
args["name"] = arg0
|
args["name"] = arg0
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query___type_argsName(
|
func (ec *executionContext) field_Query___type_argsName(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -2681,6 +2686,7 @@ func (ec *executionContext) field_Query_allocatedNodes_args(ctx context.Context,
|
|||||||
args["cluster"] = arg0
|
args["cluster"] = arg0
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query_allocatedNodes_argsCluster(
|
func (ec *executionContext) field_Query_allocatedNodes_argsCluster(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -2728,6 +2734,7 @@ func (ec *executionContext) field_Query_jobMetrics_args(ctx context.Context, raw
|
|||||||
args["resolution"] = arg3
|
args["resolution"] = arg3
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query_jobMetrics_argsID(
|
func (ec *executionContext) field_Query_jobMetrics_argsID(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -2826,6 +2833,7 @@ func (ec *executionContext) field_Query_job_args(ctx context.Context, rawArgs ma
|
|||||||
args["id"] = arg0
|
args["id"] = arg0
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query_job_argsID(
|
func (ec *executionContext) field_Query_job_argsID(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -2863,6 +2871,7 @@ func (ec *executionContext) field_Query_jobsFootprints_args(ctx context.Context,
|
|||||||
args["metrics"] = arg1
|
args["metrics"] = arg1
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query_jobsFootprints_argsFilter(
|
func (ec *executionContext) field_Query_jobsFootprints_argsFilter(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -2935,8 +2944,19 @@ func (ec *executionContext) field_Query_jobsStatistics_args(ctx context.Context,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
args["groupBy"] = arg4
|
args["groupBy"] = arg4
|
||||||
|
arg5, err := ec.field_Query_jobsStatistics_argsNumDurationBins(ctx, rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
args["numDurationBins"] = arg5
|
||||||
|
arg6, err := ec.field_Query_jobsStatistics_argsNumMetricBins(ctx, rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
args["numMetricBins"] = arg6
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query_jobsStatistics_argsFilter(
|
func (ec *executionContext) field_Query_jobsStatistics_argsFilter(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -3047,6 +3067,50 @@ func (ec *executionContext) field_Query_jobsStatistics_argsGroupBy(
|
|||||||
return zeroVal, nil
|
return zeroVal, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) field_Query_jobsStatistics_argsNumDurationBins(
|
||||||
|
ctx context.Context,
|
||||||
|
rawArgs map[string]interface{},
|
||||||
|
) (*string, error) {
|
||||||
|
// We won't call the directive if the argument is null.
|
||||||
|
// Set call_argument_directives_with_null to true to call directives
|
||||||
|
// even if the argument is null.
|
||||||
|
_, ok := rawArgs["numDurationBins"]
|
||||||
|
if !ok {
|
||||||
|
var zeroVal *string
|
||||||
|
return zeroVal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("numDurationBins"))
|
||||||
|
if tmp, ok := rawArgs["numDurationBins"]; ok {
|
||||||
|
return ec.unmarshalOString2ᚖstring(ctx, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
var zeroVal *string
|
||||||
|
return zeroVal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) field_Query_jobsStatistics_argsNumMetricBins(
|
||||||
|
ctx context.Context,
|
||||||
|
rawArgs map[string]interface{},
|
||||||
|
) (*int, error) {
|
||||||
|
// We won't call the directive if the argument is null.
|
||||||
|
// Set call_argument_directives_with_null to true to call directives
|
||||||
|
// even if the argument is null.
|
||||||
|
_, ok := rawArgs["numMetricBins"]
|
||||||
|
if !ok {
|
||||||
|
var zeroVal *int
|
||||||
|
return zeroVal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("numMetricBins"))
|
||||||
|
if tmp, ok := rawArgs["numMetricBins"]; ok {
|
||||||
|
return ec.unmarshalOInt2ᚖint(ctx, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
var zeroVal *int
|
||||||
|
return zeroVal, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query_jobs_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Query_jobs_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
@ -3067,6 +3131,7 @@ func (ec *executionContext) field_Query_jobs_args(ctx context.Context, rawArgs m
|
|||||||
args["order"] = arg2
|
args["order"] = arg2
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query_jobs_argsFilter(
|
func (ec *executionContext) field_Query_jobs_argsFilter(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -3183,6 +3248,7 @@ func (ec *executionContext) field_Query_nodeMetricsList_args(ctx context.Context
|
|||||||
args["resolution"] = arg8
|
args["resolution"] = arg8
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query_nodeMetricsList_argsCluster(
|
func (ec *executionContext) field_Query_nodeMetricsList_argsCluster(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -3416,6 +3482,7 @@ func (ec *executionContext) field_Query_nodeMetrics_args(ctx context.Context, ra
|
|||||||
args["to"] = arg5
|
args["to"] = arg5
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query_nodeMetrics_argsCluster(
|
func (ec *executionContext) field_Query_nodeMetrics_argsCluster(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -3588,6 +3655,7 @@ func (ec *executionContext) field_Query_rooflineHeatmap_args(ctx context.Context
|
|||||||
args["maxY"] = arg6
|
args["maxY"] = arg6
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query_rooflineHeatmap_argsFilter(
|
func (ec *executionContext) field_Query_rooflineHeatmap_argsFilter(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -3752,6 +3820,7 @@ func (ec *executionContext) field_Query_user_args(ctx context.Context, rawArgs m
|
|||||||
args["username"] = arg0
|
args["username"] = arg0
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query_user_argsUsername(
|
func (ec *executionContext) field_Query_user_argsUsername(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -3784,6 +3853,7 @@ func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, ra
|
|||||||
args["includeDeprecated"] = arg0
|
args["includeDeprecated"] = arg0
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field___Type_enumValues_argsIncludeDeprecated(
|
func (ec *executionContext) field___Type_enumValues_argsIncludeDeprecated(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -3816,6 +3886,7 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg
|
|||||||
args["includeDeprecated"] = arg0
|
args["includeDeprecated"] = arg0
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field___Type_fields_argsIncludeDeprecated(
|
func (ec *executionContext) field___Type_fields_argsIncludeDeprecated(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rawArgs map[string]interface{},
|
rawArgs map[string]interface{},
|
||||||
@ -10355,7 +10426,7 @@ func (ec *executionContext) _Query_jobsStatistics(ctx context.Context, field gra
|
|||||||
}()
|
}()
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return ec.resolvers.Query().JobsStatistics(rctx, fc.Args["filter"].([]*model.JobFilter), fc.Args["metrics"].([]string), fc.Args["page"].(*model.PageRequest), fc.Args["sortBy"].(*model.SortByAggregate), fc.Args["groupBy"].(*model.Aggregate))
|
return ec.resolvers.Query().JobsStatistics(rctx, fc.Args["filter"].([]*model.JobFilter), fc.Args["metrics"].([]string), fc.Args["page"].(*model.PageRequest), fc.Args["sortBy"].(*model.SortByAggregate), fc.Args["groupBy"].(*model.Aggregate), fc.Args["numDurationBins"].(*string), fc.Args["numMetricBins"].(*int))
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
@ -19995,7 +20066,7 @@ func (ec *executionContext) unmarshalOAggregate2ᚖgithubᚗcomᚋClusterCockpit
|
|||||||
if v == nil {
|
if v == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
var res = new(model.Aggregate)
|
res := new(model.Aggregate)
|
||||||
err := res.UnmarshalGQL(v)
|
err := res.UnmarshalGQL(v)
|
||||||
return res, graphql.ErrorOnPath(ctx, err)
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
}
|
}
|
||||||
@ -20594,7 +20665,7 @@ func (ec *executionContext) unmarshalOSortByAggregate2ᚖgithubᚗcomᚋClusterC
|
|||||||
if v == nil {
|
if v == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
var res = new(model.SortByAggregate)
|
res := new(model.SortByAggregate)
|
||||||
err := res.UnmarshalGQL(v)
|
err := res.UnmarshalGQL(v)
|
||||||
return res, graphql.ErrorOnPath(ctx, err)
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
}
|
}
|
||||||
|
@ -354,10 +354,14 @@ func (r *queryResolver) Jobs(ctx context.Context, filter []*model.JobFilter, pag
|
|||||||
}
|
}
|
||||||
|
|
||||||
// JobsStatistics is the resolver for the jobsStatistics field.
|
// JobsStatistics is the resolver for the jobsStatistics field.
|
||||||
func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate) ([]*model.JobsStatistics, error) {
|
func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate, numDurationBins *string, numMetricBins *int) ([]*model.JobsStatistics, error) {
|
||||||
var err error
|
var err error
|
||||||
var stats []*model.JobsStatistics
|
var stats []*model.JobsStatistics
|
||||||
|
|
||||||
|
// Top Level Defaults
|
||||||
|
var defaultDurationBins string = "1h"
|
||||||
|
var defaultMetricBins int = 10
|
||||||
|
|
||||||
if requireField(ctx, "totalJobs") || requireField(ctx, "totalWalltime") || requireField(ctx, "totalNodes") || requireField(ctx, "totalCores") ||
|
if requireField(ctx, "totalJobs") || requireField(ctx, "totalWalltime") || requireField(ctx, "totalNodes") || requireField(ctx, "totalCores") ||
|
||||||
requireField(ctx, "totalAccs") || requireField(ctx, "totalNodeHours") || requireField(ctx, "totalCoreHours") || requireField(ctx, "totalAccHours") {
|
requireField(ctx, "totalAccs") || requireField(ctx, "totalNodeHours") || requireField(ctx, "totalCoreHours") || requireField(ctx, "totalAccHours") {
|
||||||
if groupBy == nil {
|
if groupBy == nil {
|
||||||
@ -391,8 +395,13 @@ func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobF
|
|||||||
}
|
}
|
||||||
|
|
||||||
if requireField(ctx, "histDuration") || requireField(ctx, "histNumNodes") || requireField(ctx, "histNumCores") || requireField(ctx, "histNumAccs") {
|
if requireField(ctx, "histDuration") || requireField(ctx, "histNumNodes") || requireField(ctx, "histNumCores") || requireField(ctx, "histNumAccs") {
|
||||||
|
|
||||||
|
if numDurationBins == nil {
|
||||||
|
numDurationBins = &defaultDurationBins
|
||||||
|
}
|
||||||
|
|
||||||
if groupBy == nil {
|
if groupBy == nil {
|
||||||
stats[0], err = r.Repo.AddHistograms(ctx, filter, stats[0])
|
stats[0], err = r.Repo.AddHistograms(ctx, filter, stats[0], numDurationBins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -402,8 +411,13 @@ func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobF
|
|||||||
}
|
}
|
||||||
|
|
||||||
if requireField(ctx, "histMetrics") {
|
if requireField(ctx, "histMetrics") {
|
||||||
|
|
||||||
|
if numMetricBins == nil {
|
||||||
|
numMetricBins = &defaultMetricBins
|
||||||
|
}
|
||||||
|
|
||||||
if groupBy == nil {
|
if groupBy == nil {
|
||||||
stats[0], err = r.Repo.AddMetricHistograms(ctx, filter, metrics, stats[0])
|
stats[0], err = r.Repo.AddMetricHistograms(ctx, filter, metrics, stats[0], numMetricBins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ClusterCockpit/cc-backend/internal/config"
|
"github.com/ClusterCockpit/cc-backend/internal/config"
|
||||||
@ -447,15 +446,40 @@ func (r *JobRepository) AddHistograms(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
filter []*model.JobFilter,
|
filter []*model.JobFilter,
|
||||||
stat *model.JobsStatistics,
|
stat *model.JobsStatistics,
|
||||||
|
durationBins *string,
|
||||||
) (*model.JobsStatistics, error) {
|
) (*model.JobsStatistics, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
var targetBinCount int
|
||||||
|
var targetBinSize int
|
||||||
|
switch {
|
||||||
|
case *durationBins == "1m": // 1 Minute Bins + Max 60 Bins -> Max 60 Minutes
|
||||||
|
targetBinCount = 60
|
||||||
|
targetBinSize = 60
|
||||||
|
case *durationBins == "10m": // 10 Minute Bins + Max 72 Bins -> Max 12 Hours
|
||||||
|
targetBinCount = 72
|
||||||
|
targetBinSize = 600
|
||||||
|
case *durationBins == "1h": // 1 Hour Bins + Max 48 Bins -> Max 48 Hours
|
||||||
|
targetBinCount = 48
|
||||||
|
targetBinSize = 3600
|
||||||
|
case *durationBins == "6h": // 6 Hour Bins + Max 12 Bins -> Max 3 Days
|
||||||
|
targetBinCount = 12
|
||||||
|
targetBinSize = 21600
|
||||||
|
case *durationBins == "12h": // 12 hour Bins + Max 14 Bins -> Max 7 Days
|
||||||
|
targetBinCount = 14
|
||||||
|
targetBinSize = 43200
|
||||||
|
default: // 24h
|
||||||
|
targetBinCount = 24
|
||||||
|
targetBinSize = 3600
|
||||||
|
}
|
||||||
|
|
||||||
castType := r.getCastType()
|
castType := r.getCastType()
|
||||||
var err error
|
var err error
|
||||||
value := fmt.Sprintf(`CAST(ROUND((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END) / 3600) as %s) as value`, time.Now().Unix(), castType)
|
// Return X-Values always as seconds, will be formatted into minutes and hours in frontend
|
||||||
stat.HistDuration, err = r.jobsStatisticsHistogram(ctx, value, filter)
|
value := fmt.Sprintf(`CAST(ROUND(((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END) / %d) + 1) as %s) as value`, time.Now().Unix(), targetBinSize, castType)
|
||||||
|
stat.HistDuration, err = r.jobsDurationStatisticsHistogram(ctx, value, filter, targetBinSize, &targetBinCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Error while loading job statistics histogram: running jobs")
|
log.Warn("Error while loading job statistics histogram: job duration")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,6 +511,7 @@ func (r *JobRepository) AddMetricHistograms(
|
|||||||
filter []*model.JobFilter,
|
filter []*model.JobFilter,
|
||||||
metrics []string,
|
metrics []string,
|
||||||
stat *model.JobsStatistics,
|
stat *model.JobsStatistics,
|
||||||
|
targetBinCount *int,
|
||||||
) (*model.JobsStatistics, error) {
|
) (*model.JobsStatistics, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
@ -494,7 +519,7 @@ func (r *JobRepository) AddMetricHistograms(
|
|||||||
for _, f := range filter {
|
for _, f := range filter {
|
||||||
if f.State != nil {
|
if f.State != nil {
|
||||||
if len(f.State) == 1 && f.State[0] == "running" {
|
if len(f.State) == 1 && f.State[0] == "running" {
|
||||||
stat.HistMetrics = r.runningJobsMetricStatisticsHistogram(ctx, metrics, filter)
|
stat.HistMetrics = r.runningJobsMetricStatisticsHistogram(ctx, metrics, filter, targetBinCount)
|
||||||
log.Debugf("Timer AddMetricHistograms %s", time.Since(start))
|
log.Debugf("Timer AddMetricHistograms %s", time.Since(start))
|
||||||
return stat, nil
|
return stat, nil
|
||||||
}
|
}
|
||||||
@ -503,7 +528,7 @@ func (r *JobRepository) AddMetricHistograms(
|
|||||||
|
|
||||||
// All other cases: Query and make bins in sqlite directly
|
// All other cases: Query and make bins in sqlite directly
|
||||||
for _, m := range metrics {
|
for _, m := range metrics {
|
||||||
metricHisto, err := r.jobsMetricStatisticsHistogram(ctx, m, filter)
|
metricHisto, err := r.jobsMetricStatisticsHistogram(ctx, m, filter, targetBinCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Error while loading job metric statistics histogram: %s", m)
|
log.Warnf("Error while loading job metric statistics histogram: %s", m)
|
||||||
continue
|
continue
|
||||||
@ -540,6 +565,7 @@ func (r *JobRepository) jobsStatisticsHistogram(
|
|||||||
}
|
}
|
||||||
|
|
||||||
points := make([]*model.HistoPoint, 0)
|
points := make([]*model.HistoPoint, 0)
|
||||||
|
// is it possible to introduce zero values here? requires info about bincount
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
point := model.HistoPoint{}
|
point := model.HistoPoint{}
|
||||||
if err := rows.Scan(&point.Value, &point.Count); err != nil {
|
if err := rows.Scan(&point.Value, &point.Count); err != nil {
|
||||||
@ -553,10 +579,66 @@ func (r *JobRepository) jobsStatisticsHistogram(
|
|||||||
return points, nil
|
return points, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *JobRepository) jobsDurationStatisticsHistogram(
|
||||||
|
ctx context.Context,
|
||||||
|
value string,
|
||||||
|
filters []*model.JobFilter,
|
||||||
|
binSizeSeconds int,
|
||||||
|
targetBinCount *int,
|
||||||
|
) ([]*model.HistoPoint, error) {
|
||||||
|
start := time.Now()
|
||||||
|
query, qerr := SecurityCheck(ctx,
|
||||||
|
sq.Select(value, "COUNT(job.id) AS count").From("job"))
|
||||||
|
|
||||||
|
if qerr != nil {
|
||||||
|
return nil, qerr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup Array
|
||||||
|
points := make([]*model.HistoPoint, 0)
|
||||||
|
for i := 1; i <= *targetBinCount; i++ {
|
||||||
|
point := model.HistoPoint{Value: i * binSizeSeconds, Count: 0}
|
||||||
|
points = append(points, &point)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range filters {
|
||||||
|
query = BuildWhereClause(f, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := query.GroupBy("value").RunWith(r.DB).Query()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error while running query")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill Array at matching $Value
|
||||||
|
for rows.Next() {
|
||||||
|
point := model.HistoPoint{}
|
||||||
|
if err := rows.Scan(&point.Value, &point.Count); err != nil {
|
||||||
|
log.Warn("Error while scanning rows")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range points {
|
||||||
|
if e.Value == (point.Value * binSizeSeconds) {
|
||||||
|
// Note:
|
||||||
|
// Matching on unmodified integer value (and multiplying point.Value by binSizeSeconds after match)
|
||||||
|
// causes frontend to loop into highest targetBinCount, due to zoom condition instantly being fullfilled (cause unknown)
|
||||||
|
e.Count = point.Count
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("Timer jobsStatisticsHistogram %s", time.Since(start))
|
||||||
|
return points, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *JobRepository) jobsMetricStatisticsHistogram(
|
func (r *JobRepository) jobsMetricStatisticsHistogram(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
metric string,
|
metric string,
|
||||||
filters []*model.JobFilter,
|
filters []*model.JobFilter,
|
||||||
|
bins *int,
|
||||||
) (*model.MetricHistoPoints, error) {
|
) (*model.MetricHistoPoints, error) {
|
||||||
// Get specific Peak or largest Peak
|
// Get specific Peak or largest Peak
|
||||||
var metricConfig *schema.MetricConfig
|
var metricConfig *schema.MetricConfig
|
||||||
@ -624,16 +706,15 @@ func (r *JobRepository) jobsMetricStatisticsHistogram(
|
|||||||
return nil, sqlerr
|
return nil, sqlerr
|
||||||
}
|
}
|
||||||
|
|
||||||
bins := 10
|
|
||||||
binQuery := fmt.Sprintf(`CAST( (case when %s = value.max
|
binQuery := fmt.Sprintf(`CAST( (case when %s = value.max
|
||||||
then value.max*0.999999999 else %s end - value.min) / (value.max -
|
then value.max*0.999999999 else %s end - value.min) / (value.max -
|
||||||
value.min) * %d as INTEGER )`, jm, jm, bins)
|
value.min) * %v as INTEGER )`, jm, jm, *bins)
|
||||||
|
|
||||||
mainQuery := sq.Select(
|
mainQuery := sq.Select(
|
||||||
fmt.Sprintf(`%s + 1 as bin`, binQuery),
|
fmt.Sprintf(`%s + 1 as bin`, binQuery),
|
||||||
fmt.Sprintf(`count(%s) as count`, jm),
|
fmt.Sprintf(`count(%s) as count`, jm),
|
||||||
fmt.Sprintf(`CAST(((value.max / %d) * (%s )) as INTEGER ) as min`, bins, binQuery),
|
fmt.Sprintf(`CAST(((value.max / %d) * (%v )) as INTEGER ) as min`, *bins, binQuery),
|
||||||
fmt.Sprintf(`CAST(((value.max / %d) * (%s + 1 )) as INTEGER ) as max`, bins, binQuery),
|
fmt.Sprintf(`CAST(((value.max / %d) * (%v + 1 )) as INTEGER ) as max`, *bins, binQuery),
|
||||||
).From("job").CrossJoin(
|
).From("job").CrossJoin(
|
||||||
fmt.Sprintf(`(%s) as value`, crossJoinQuerySql), crossJoinQueryArgs...,
|
fmt.Sprintf(`(%s) as value`, crossJoinQuerySql), crossJoinQueryArgs...,
|
||||||
).Where(fmt.Sprintf(`%s is not null and %s <= %f`, jm, jm, peak))
|
).Where(fmt.Sprintf(`%s is not null and %s <= %f`, jm, jm, peak))
|
||||||
@ -657,7 +738,15 @@ func (r *JobRepository) jobsMetricStatisticsHistogram(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup Array
|
||||||
points := make([]*model.MetricHistoPoint, 0)
|
points := make([]*model.MetricHistoPoint, 0)
|
||||||
|
for i := 1; i <= *bins; i++ {
|
||||||
|
binMax := ((int(peak) / *bins) * i)
|
||||||
|
binMin := ((int(peak) / *bins) * (i - 1))
|
||||||
|
point := model.MetricHistoPoint{Bin: &i, Count: 0, Min: &binMin, Max: &binMax}
|
||||||
|
points = append(points, &point)
|
||||||
|
}
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
point := model.MetricHistoPoint{}
|
point := model.MetricHistoPoint{}
|
||||||
if err := rows.Scan(&point.Bin, &point.Count, &point.Min, &point.Max); err != nil {
|
if err := rows.Scan(&point.Bin, &point.Count, &point.Min, &point.Max); err != nil {
|
||||||
@ -665,7 +754,20 @@ func (r *JobRepository) jobsMetricStatisticsHistogram(
|
|||||||
return nil, err // Totally bricks cc-backend if returned and if all metrics requested?
|
return nil, err // Totally bricks cc-backend if returned and if all metrics requested?
|
||||||
}
|
}
|
||||||
|
|
||||||
points = append(points, &point)
|
for _, e := range points {
|
||||||
|
if e.Bin != nil && point.Bin != nil {
|
||||||
|
if *e.Bin == *point.Bin {
|
||||||
|
e.Count = point.Count
|
||||||
|
if point.Min != nil {
|
||||||
|
e.Min = point.Min
|
||||||
|
}
|
||||||
|
if point.Max != nil {
|
||||||
|
e.Max = point.Max
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := model.MetricHistoPoints{Metric: metric, Unit: unit, Stat: &footprintStat, Data: points}
|
result := model.MetricHistoPoints{Metric: metric, Unit: unit, Stat: &footprintStat, Data: points}
|
||||||
@ -678,7 +780,9 @@ func (r *JobRepository) runningJobsMetricStatisticsHistogram(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
metrics []string,
|
metrics []string,
|
||||||
filters []*model.JobFilter,
|
filters []*model.JobFilter,
|
||||||
|
bins *int,
|
||||||
) []*model.MetricHistoPoints {
|
) []*model.MetricHistoPoints {
|
||||||
|
|
||||||
// Get Jobs
|
// Get Jobs
|
||||||
jobs, err := r.QueryJobs(ctx, filters, &model.PageRequest{Page: 1, ItemsPerPage: 500 + 1}, nil)
|
jobs, err := r.QueryJobs(ctx, filters, &model.PageRequest{Page: 1, ItemsPerPage: 500 + 1}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -720,7 +824,6 @@ func (r *JobRepository) runningJobsMetricStatisticsHistogram(
|
|||||||
metricConfig = archive.GetMetricConfig(*f.Cluster.Eq, metric)
|
metricConfig = archive.GetMetricConfig(*f.Cluster.Eq, metric)
|
||||||
peak = metricConfig.Peak
|
peak = metricConfig.Peak
|
||||||
unit = metricConfig.Unit.Prefix + metricConfig.Unit.Base
|
unit = metricConfig.Unit.Prefix + metricConfig.Unit.Base
|
||||||
log.Debugf("Cluster %s filter found with peak %f for %s", *f.Cluster.Eq, peak, metric)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -740,28 +843,24 @@ func (r *JobRepository) runningJobsMetricStatisticsHistogram(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make and fill bins
|
// Make and fill bins
|
||||||
bins := 10.0
|
peakBin := int(peak) / *bins
|
||||||
peakBin := peak / bins
|
|
||||||
|
|
||||||
points := make([]*model.MetricHistoPoint, 0)
|
points := make([]*model.MetricHistoPoint, 0)
|
||||||
for b := 0; b < 10; b++ {
|
for b := 0; b < *bins; b++ {
|
||||||
count := 0
|
count := 0
|
||||||
bindex := b + 1
|
bindex := b + 1
|
||||||
bmin := math.Round(peakBin * float64(b))
|
bmin := peakBin * b
|
||||||
bmax := math.Round(peakBin * (float64(b) + 1.0))
|
bmax := peakBin * (b + 1)
|
||||||
|
|
||||||
// Iterate AVG values for indexed metric and count for bins
|
// Iterate AVG values for indexed metric and count for bins
|
||||||
for _, val := range avgs[idx] {
|
for _, val := range avgs[idx] {
|
||||||
if float64(val) >= bmin && float64(val) < bmax {
|
if int(val) >= bmin && int(val) < bmax {
|
||||||
count += 1
|
count += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bminint := int(bmin)
|
|
||||||
bmaxint := int(bmax)
|
|
||||||
|
|
||||||
// Append Bin to Metric Result Array
|
// Append Bin to Metric Result Array
|
||||||
point := model.MetricHistoPoint{Bin: &bindex, Count: count, Min: &bminint, Max: &bmaxint}
|
point := model.MetricHistoPoint{Bin: &bindex, Count: count, Min: &bmin, Max: &bmax}
|
||||||
points = append(points, &point)
|
points = append(points, &point)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
web/frontend/package-lock.json
generated
4
web/frontend/package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cc-frontend",
|
"name": "cc-frontend",
|
||||||
"version": "1.0.2",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cc-frontend",
|
"name": "cc-frontend",
|
||||||
"version": "1.0.2",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rollup/plugin-replace": "^5.0.7",
|
"@rollup/plugin-replace": "^5.0.7",
|
||||||
|
@ -174,6 +174,7 @@
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Note: Different footprints than those saved in DB per Job -> Caused by Legacy Naming
|
||||||
$: footprintsQuery = queryStore({
|
$: footprintsQuery = queryStore({
|
||||||
client: client,
|
client: client,
|
||||||
query: gql`
|
query: gql`
|
||||||
@ -470,10 +471,12 @@
|
|||||||
height={300}
|
height={300}
|
||||||
data={convert2uplot($statsQuery.data.stats[0].histDuration)}
|
data={convert2uplot($statsQuery.data.stats[0].histDuration)}
|
||||||
title="Duration Distribution"
|
title="Duration Distribution"
|
||||||
xlabel="Current Runtimes"
|
xlabel="Current Job Runtimes"
|
||||||
xunit="Hours"
|
xunit="Runtime"
|
||||||
ylabel="Number of Jobs"
|
ylabel="Number of Jobs"
|
||||||
yunit="Jobs"
|
yunit="Jobs"
|
||||||
|
usesBins
|
||||||
|
xtime
|
||||||
/>
|
/>
|
||||||
{/key}
|
{/key}
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -463,7 +463,7 @@
|
|||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<!-- Usage Stats as Histograms -->
|
<!-- User and Project Stats as Pie-Charts -->
|
||||||
|
|
||||||
<Row cols={{ lg: 4, md: 2, sm: 1 }}>
|
<Row cols={{ lg: 4, md: 2, sm: 1 }}>
|
||||||
<Col class="p-2">
|
<Col class="p-2">
|
||||||
@ -587,17 +587,23 @@
|
|||||||
{/key}
|
{/key}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<hr class="my-2" />
|
<hr class="my-2" />
|
||||||
|
|
||||||
|
<!-- Static Stats as Histograms : Running Duration && Allocated Hardware Counts-->
|
||||||
|
|
||||||
<Row cols={{ lg: 2, md: 1 }}>
|
<Row cols={{ lg: 2, md: 1 }}>
|
||||||
<Col class="p-2">
|
<Col class="p-2">
|
||||||
{#key $mainQuery.data.stats}
|
{#key $mainQuery.data.stats}
|
||||||
<Histogram
|
<Histogram
|
||||||
data={convert2uplot($mainQuery.data.stats[0].histDuration)}
|
data={convert2uplot($mainQuery.data.stats[0].histDuration)}
|
||||||
title="Duration Distribution"
|
title="Duration Distribution"
|
||||||
xlabel="Current Runtimes"
|
xlabel="Current Job Runtimes"
|
||||||
xunit="Hours"
|
xunit="Runtime"
|
||||||
ylabel="Number of Jobs"
|
ylabel="Number of Jobs"
|
||||||
yunit="Jobs"
|
yunit="Jobs"
|
||||||
|
usesBins
|
||||||
|
xtime
|
||||||
/>
|
/>
|
||||||
{/key}
|
{/key}
|
||||||
</Col>
|
</Col>
|
||||||
@ -640,7 +646,11 @@
|
|||||||
{/key}
|
{/key}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<hr class="my-2" />
|
<hr class="my-2" />
|
||||||
|
|
||||||
|
<!-- Selectable Stats as Histograms : Average Values of Running Jobs -->
|
||||||
|
|
||||||
{#if metricsInHistograms}
|
{#if metricsInHistograms}
|
||||||
{#key $mainQuery.data.stats[0].histMetrics}
|
{#key $mainQuery.data.stats[0].histMetrics}
|
||||||
<PlotGrid
|
<PlotGrid
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
Icon,
|
Icon,
|
||||||
Card,
|
Card,
|
||||||
Spinner,
|
Spinner,
|
||||||
|
Input,
|
||||||
|
InputGroup,
|
||||||
|
InputGroupText
|
||||||
} from "@sveltestrap/sveltestrap";
|
} from "@sveltestrap/sveltestrap";
|
||||||
import {
|
import {
|
||||||
queryStore,
|
queryStore,
|
||||||
@ -59,6 +62,11 @@
|
|||||||
let showFootprint = filterPresets.cluster
|
let showFootprint = filterPresets.cluster
|
||||||
? !!ccconfig[`plot_list_showFootprint:${filterPresets.cluster}`]
|
? !!ccconfig[`plot_list_showFootprint:${filterPresets.cluster}`]
|
||||||
: !!ccconfig.plot_list_showFootprint;
|
: !!ccconfig.plot_list_showFootprint;
|
||||||
|
|
||||||
|
let numDurationBins = "1h";
|
||||||
|
let numMetricBins = 10;
|
||||||
|
let durationBinOptions = ["1m","10m","1h","6h","12h"];
|
||||||
|
let metricBinOptions = [10, 20, 50, 100];
|
||||||
|
|
||||||
$: metricsInHistograms = selectedCluster
|
$: metricsInHistograms = selectedCluster
|
||||||
? ccconfig[`user_view_histogramMetrics:${selectedCluster}`] || []
|
? ccconfig[`user_view_histogramMetrics:${selectedCluster}`] || []
|
||||||
@ -68,8 +76,8 @@
|
|||||||
$: stats = queryStore({
|
$: stats = queryStore({
|
||||||
client: client,
|
client: client,
|
||||||
query: gql`
|
query: gql`
|
||||||
query ($jobFilters: [JobFilter!]!, $metricsInHistograms: [String!]) {
|
query ($jobFilters: [JobFilter!]!, $metricsInHistograms: [String!], $numDurationBins: String, $numMetricBins: Int) {
|
||||||
jobsStatistics(filter: $jobFilters, metrics: $metricsInHistograms) {
|
jobsStatistics(filter: $jobFilters, metrics: $metricsInHistograms, numDurationBins: $numDurationBins , numMetricBins: $numMetricBins ) {
|
||||||
totalJobs
|
totalJobs
|
||||||
shortJobs
|
shortJobs
|
||||||
totalWalltime
|
totalWalltime
|
||||||
@ -96,7 +104,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
variables: { jobFilters, metricsInHistograms },
|
variables: { jobFilters, metricsInHistograms, numDurationBins, numMetricBins },
|
||||||
});
|
});
|
||||||
|
|
||||||
onMount(() => filterComponent.updateFilters());
|
onMount(() => filterComponent.updateFilters());
|
||||||
@ -118,8 +126,8 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- ROW2: Tools-->
|
<!-- ROW2: Tools-->
|
||||||
<Row cols={{ xs: 1, md: 2, lg: 4}} class="mb-3">
|
<Row cols={{ xs: 1, md: 2, lg: 6}} class="mb-3">
|
||||||
<Col lg="2" class="mb-2 mb-lg-0">
|
<Col class="mb-2 mb-lg-0">
|
||||||
<ButtonGroup class="w-100">
|
<ButtonGroup class="w-100">
|
||||||
<Button outline color="primary" on:click={() => (isSortingOpen = true)}>
|
<Button outline color="primary" on:click={() => (isSortingOpen = true)}>
|
||||||
<Icon name="sort-up" /> Sorting
|
<Icon name="sort-up" /> Sorting
|
||||||
@ -133,7 +141,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</Col>
|
</Col>
|
||||||
<Col lg="4" xl="6" class="mb-1 mb-lg-0">
|
<Col lg="4" class="mb-1 mb-lg-0">
|
||||||
<Filters
|
<Filters
|
||||||
{filterPresets}
|
{filterPresets}
|
||||||
{matchedJobs}
|
{matchedJobs}
|
||||||
@ -148,12 +156,27 @@
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col lg="3" xl="2" class="mb-2 mb-lg-0">
|
<Col class="mb-2 mb-lg-0">
|
||||||
|
<InputGroup>
|
||||||
|
<InputGroupText>
|
||||||
|
<Icon name="bar-chart-line-fill" />
|
||||||
|
</InputGroupText>
|
||||||
|
<InputGroupText>
|
||||||
|
Duration Bin Size
|
||||||
|
</InputGroupText>
|
||||||
|
<Input type="select" bind:value={numDurationBins} style="max-width: 120px;">
|
||||||
|
{#each durationBinOptions as dbin}
|
||||||
|
<option value={dbin}>{dbin}</option>
|
||||||
|
{/each}
|
||||||
|
</Input>
|
||||||
|
</InputGroup>
|
||||||
|
</Col>
|
||||||
|
<Col class="mb-2 mb-lg-0">
|
||||||
<TextFilter
|
<TextFilter
|
||||||
on:set-filter={({ detail }) => filterComponent.updateFilters(detail)}
|
on:set-filter={({ detail }) => filterComponent.updateFilters(detail)}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col lg="3" xl="2" class="mb-1 mb-lg-0">
|
<Col class="mb-1 mb-lg-0">
|
||||||
<Refresher on:refresh={() => {
|
<Refresher on:refresh={() => {
|
||||||
jobList.refreshJobs()
|
jobList.refreshJobs()
|
||||||
jobList.refreshAllMetrics()
|
jobList.refreshAllMetrics()
|
||||||
@ -215,10 +238,12 @@
|
|||||||
<Histogram
|
<Histogram
|
||||||
data={convert2uplot($stats.data.jobsStatistics[0].histDuration)}
|
data={convert2uplot($stats.data.jobsStatistics[0].histDuration)}
|
||||||
title="Duration Distribution"
|
title="Duration Distribution"
|
||||||
xlabel="Current Runtimes"
|
xlabel="Job Runtimes"
|
||||||
xunit="Hours"
|
xunit="Runtime"
|
||||||
ylabel="Number of Jobs"
|
ylabel="Number of Jobs"
|
||||||
yunit="Jobs"
|
yunit="Jobs"
|
||||||
|
usesBins
|
||||||
|
xtime
|
||||||
/>
|
/>
|
||||||
{/key}
|
{/key}
|
||||||
</Col>
|
</Col>
|
||||||
@ -238,16 +263,32 @@
|
|||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<!-- ROW4+5: Selectable Histograms -->
|
<!-- ROW4+5: Selectable Histograms -->
|
||||||
<Row cols={{ xs: 1, md: 5}}>
|
<Row>
|
||||||
<Col>
|
<Col xs="12" md="3" lg="2" class="mb-2 mb-md-0">
|
||||||
<Button
|
<Button
|
||||||
outline
|
outline
|
||||||
color="secondary"
|
color="secondary"
|
||||||
|
class="w-100"
|
||||||
on:click={() => (isHistogramSelectionOpen = true)}
|
on:click={() => (isHistogramSelectionOpen = true)}
|
||||||
>
|
>
|
||||||
<Icon name="bar-chart-line" /> Select Histograms
|
<Icon name="bar-chart-line" /> Select Histograms
|
||||||
</Button>
|
</Button>
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col xs="12" md="9" lg="10" class="mb-2 mb-md-0">
|
||||||
|
<InputGroup>
|
||||||
|
<InputGroupText>
|
||||||
|
<Icon name="bar-chart-line-fill" />
|
||||||
|
</InputGroupText>
|
||||||
|
<InputGroupText>
|
||||||
|
Metric Bins
|
||||||
|
</InputGroupText>
|
||||||
|
<Input type="select" bind:value={numMetricBins} style="max-width: 120px;">
|
||||||
|
{#each metricBinOptions as mbin}
|
||||||
|
<option value={mbin}>{mbin}</option>
|
||||||
|
{/each}
|
||||||
|
</Input>
|
||||||
|
</InputGroup>
|
||||||
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{#if metricsInHistograms?.length > 0}
|
{#if metricsInHistograms?.length > 0}
|
||||||
{#if $stats.error}
|
{#if $stats.error}
|
||||||
@ -272,12 +313,12 @@
|
|||||||
>
|
>
|
||||||
<Histogram
|
<Histogram
|
||||||
data={convert2uplot(item.data)}
|
data={convert2uplot(item.data)}
|
||||||
usesBins={true}
|
|
||||||
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"
|
||||||
|
usesBins
|
||||||
/>
|
/>
|
||||||
</PlotGrid>
|
</PlotGrid>
|
||||||
{/key}
|
{/key}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
const ccconfig = getContext("cc-config");
|
const ccconfig = getContext("cc-config");
|
||||||
let message = { msg: "", target: "", color: "#d63384" };
|
let message = { msg: "", target: "", color: "#d63384" };
|
||||||
let displayMessage = false;
|
let displayMessage = false;
|
||||||
|
let cbmode = ccconfig?.plot_general_colorblindMode || false;
|
||||||
|
|
||||||
async function handleSettingSubmit(event) {
|
async function handleSettingSubmit(event) {
|
||||||
const selector = event.detail.selector
|
const selector = event.detail.selector
|
||||||
@ -28,6 +29,9 @@
|
|||||||
const res = await fetch(form.action, { method: "POST", body: formData });
|
const res = await fetch(form.action, { method: "POST", body: formData });
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
let text = await res.text();
|
let text = await res.text();
|
||||||
|
if (formData.get("key") === "plot_general_colorblindMode") {
|
||||||
|
cbmode = JSON.parse(formData.get("value"));
|
||||||
|
}
|
||||||
popMessage(text, target, "#048109");
|
popMessage(text, target, "#048109");
|
||||||
} else {
|
} else {
|
||||||
let text = await res.text();
|
let text = await res.text();
|
||||||
@ -51,4 +55,4 @@
|
|||||||
|
|
||||||
<UserOptions config={ccconfig} {username} {isApi} bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/>
|
<UserOptions config={ccconfig} {username} {isApi} bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/>
|
||||||
<PlotRenderOptions config={ccconfig} bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/>
|
<PlotRenderOptions config={ccconfig} bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/>
|
||||||
<PlotColorScheme config={ccconfig} bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/>
|
<PlotColorScheme config={ccconfig} bind:cbmode bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/>
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
export let config;
|
export let config;
|
||||||
export let message;
|
export let message;
|
||||||
export let displayMessage;
|
export let displayMessage;
|
||||||
|
export let cbmode = false;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
function updateSetting(selector, target) {
|
function updateSetting(selector, target) {
|
||||||
@ -265,6 +266,62 @@
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// https://personal.sron.nl/~pault/
|
||||||
|
// https://tsitsul.in/blog/coloropt/
|
||||||
|
const cvdschemes = {
|
||||||
|
HighContrast: [
|
||||||
|
"rgb(221,170,51)",
|
||||||
|
"rgb(187,85,102)",
|
||||||
|
"rgb(0,68,136)",
|
||||||
|
"rgb(0,0,0)",
|
||||||
|
],
|
||||||
|
Bright: [
|
||||||
|
"rgb(68,119,170)",
|
||||||
|
"rgb(102,204,238)",
|
||||||
|
"rgb(34,136,51)",
|
||||||
|
"rgb(204,187,68)",
|
||||||
|
"rgb(238,102,119)",
|
||||||
|
"rgb(170,51,119)",
|
||||||
|
"rgb(187,187,187)",
|
||||||
|
],
|
||||||
|
Muted: [
|
||||||
|
"rgb(51,34,136)",
|
||||||
|
"rgb(136,204,238)",
|
||||||
|
"rgb(68,170,153)",
|
||||||
|
"rgb(17,119,51)",
|
||||||
|
"rgb(153,153,51)",
|
||||||
|
"rgb(221,204,119)",
|
||||||
|
"rgb(204,102,119)",
|
||||||
|
"rgb(136,34,85)",
|
||||||
|
"rgb(170,68,153)",
|
||||||
|
"rgb(221,221,221)",
|
||||||
|
],
|
||||||
|
NormalSixColor: [
|
||||||
|
"rgb(64,83,211)",
|
||||||
|
"rgb(221,179,16)",
|
||||||
|
"rgb(181,29,20)",
|
||||||
|
"rgb(0,190,255)",
|
||||||
|
"rgb(251,73,176)",
|
||||||
|
"rgb(0,178,93)",
|
||||||
|
"rgb(202,202,202)",
|
||||||
|
],
|
||||||
|
NormalTwelveColor: [
|
||||||
|
"rgb(235,172,35)",
|
||||||
|
"rgb(184,0,88)",
|
||||||
|
"rgb(0,140,249)",
|
||||||
|
"rgb(0,110,0)",
|
||||||
|
"rgb(0,187,173)",
|
||||||
|
"rgb(209,99,230)",
|
||||||
|
"rgb(178,69,2)",
|
||||||
|
"rgb(255,146,135)",
|
||||||
|
"rgb(89,84,214)",
|
||||||
|
"rgb(0,198,248)",
|
||||||
|
"rgb(135,133,0)",
|
||||||
|
"rgb(0,167,108)",
|
||||||
|
"rgb(189,189,189)",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Row cols={1} class="p-2 g-2">
|
<Row cols={1} class="p-2 g-2">
|
||||||
@ -281,7 +338,7 @@
|
|||||||
<CardTitle
|
<CardTitle
|
||||||
style="margin-bottom: 1em; display: flex; align-items: center;"
|
style="margin-bottom: 1em; display: flex; align-items: center;"
|
||||||
>
|
>
|
||||||
<div>Color Scheme for Timeseries Plots</div>
|
<div>Color Scheme for Timeseries Plots {cbmode ? `(Color Blind Friendly Palettes)` : ``}</div>
|
||||||
{#if displayMessage && message.target == "cs"}<div
|
{#if displayMessage && message.target == "cs"}<div
|
||||||
style="margin-left: auto; font-size: 0.9em;"
|
style="margin-left: auto; font-size: 0.9em;"
|
||||||
>
|
>
|
||||||
@ -293,7 +350,7 @@
|
|||||||
<input type="hidden" name="key" value="plot_general_colorscheme" />
|
<input type="hidden" name="key" value="plot_general_colorscheme" />
|
||||||
<Table hover>
|
<Table hover>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each Object.entries(colorschemes) as [name, rgbrow]}
|
{#each Object.entries(cbmode ? cvdschemes : colorschemes) as [name, rgbrow]}
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">{name}</th>
|
<th scope="col">{name}</th>
|
||||||
<td>
|
<td>
|
||||||
@ -333,8 +390,9 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.color-dot {
|
.color-dot {
|
||||||
height: 10px;
|
margin-left: 1px;
|
||||||
width: 10px;
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
@ -129,8 +129,8 @@
|
|||||||
>
|
>
|
||||||
|
|
||||||
<!-- BACKGROUND -->
|
<!-- BACKGROUND -->
|
||||||
<Col
|
<Col class="d-flex justify-content-between"
|
||||||
><Card class="h-100">
|
><Card class="h-100" style="width: 49%;">
|
||||||
<form
|
<form
|
||||||
id="backgrounds-form"
|
id="backgrounds-form"
|
||||||
method="post"
|
method="post"
|
||||||
@ -173,6 +173,50 @@
|
|||||||
</div>
|
</div>
|
||||||
<Button color="primary" type="submit">Submit</Button>
|
<Button color="primary" type="submit">Submit</Button>
|
||||||
</form>
|
</form>
|
||||||
</Card></Col
|
</Card>
|
||||||
>
|
<Card class="h-100" style="width: 49%;">
|
||||||
|
<form
|
||||||
|
id="colorblindmode-form"
|
||||||
|
method="post"
|
||||||
|
action="/frontend/configuration/"
|
||||||
|
class="card-body"
|
||||||
|
on:submit|preventDefault={() =>
|
||||||
|
updateSetting("#colorblindmode-form", "cbm")}
|
||||||
|
>
|
||||||
|
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
|
||||||
|
<CardTitle
|
||||||
|
style="margin-bottom: 1em; display: flex; align-items: center;"
|
||||||
|
>
|
||||||
|
<div>Color Blind Mode</div>
|
||||||
|
{#if displayMessage && message.target == "cbm"}<div
|
||||||
|
style="margin-left: auto; font-size: 0.9em;"
|
||||||
|
>
|
||||||
|
<code style="color: {message.color};" out:fade
|
||||||
|
>Update: {message.msg}</code
|
||||||
|
>
|
||||||
|
</div>{/if}
|
||||||
|
</CardTitle>
|
||||||
|
<input type="hidden" name="key" value="plot_general_colorblindMode" />
|
||||||
|
<div class="mb-3">
|
||||||
|
<div>
|
||||||
|
{#if config?.plot_general_colorblindMode}
|
||||||
|
<input type="radio" id="cbm-true-checked" name="value" value="true" checked />
|
||||||
|
{:else}
|
||||||
|
<input type="radio" id="cbm-true" name="value" value="true" />
|
||||||
|
{/if}
|
||||||
|
<label for="true">Yes</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{#if config?.plot_general_colorblindMode}
|
||||||
|
<input type="radio" id="cbm-false" name="value" value="false" />
|
||||||
|
{:else}
|
||||||
|
<input type="radio" id="cbm-false-checked" name="value" value="false" checked />
|
||||||
|
{/if}
|
||||||
|
<label for="false">No</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button color="primary" type="submit">Submit</Button>
|
||||||
|
</form>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
</Row>
|
</Row>
|
@ -15,8 +15,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import uPlot from "uplot";
|
import uPlot from "uplot";
|
||||||
import { formatNumber } from "../units.js";
|
|
||||||
import { onMount, onDestroy } from "svelte";
|
import { onMount, onDestroy } from "svelte";
|
||||||
|
import { formatNumber } from "../units.js";
|
||||||
import { Card } from "@sveltestrap/sveltestrap";
|
import { Card } from "@sveltestrap/sveltestrap";
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
@ -26,16 +26,31 @@
|
|||||||
export let title = "";
|
export let title = "";
|
||||||
export let xlabel = "";
|
export let xlabel = "";
|
||||||
export let xunit = "";
|
export let xunit = "";
|
||||||
|
export let xtime = false;
|
||||||
export let ylabel = "";
|
export let ylabel = "";
|
||||||
export let yunit = "";
|
export let yunit = "";
|
||||||
|
|
||||||
const { bars } = uPlot.paths;
|
const { bars } = uPlot.paths;
|
||||||
|
|
||||||
const drawStyles = {
|
const drawStyles = {
|
||||||
bars: 1,
|
bars: 1,
|
||||||
points: 2,
|
points: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function paths(u, seriesIdx, idx0, idx1, extendGap, buildClip) {
|
function paths(u, seriesIdx, idx0, idx1, extendGap, buildClip) {
|
||||||
let s = u.series[seriesIdx];
|
let s = u.series[seriesIdx];
|
||||||
let style = s.drawStyle;
|
let style = s.drawStyle;
|
||||||
@ -139,7 +154,7 @@
|
|||||||
label: xlabel,
|
label: xlabel,
|
||||||
labelGap: 10,
|
labelGap: 10,
|
||||||
size: 25,
|
size: 25,
|
||||||
incrs: [1, 2, 5, 6, 10, 12, 50, 100, 500, 1000, 5000, 10000],
|
incrs: xtime ? [60, 120, 300, 600, 1800, 3600, 7200, 14400, 18000, 21600, 43200, 86400] : [1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000],
|
||||||
border: {
|
border: {
|
||||||
show: true,
|
show: true,
|
||||||
stroke: "#000000",
|
stroke: "#000000",
|
||||||
@ -149,7 +164,13 @@
|
|||||||
size: 5 / devicePixelRatio,
|
size: 5 / devicePixelRatio,
|
||||||
stroke: "#000000",
|
stroke: "#000000",
|
||||||
},
|
},
|
||||||
values: (_, t) => t.map((v) => formatNumber(v)),
|
values: (_, t) => t.map((v) => {
|
||||||
|
if (xtime) {
|
||||||
|
return formatTime(v);
|
||||||
|
} else {
|
||||||
|
return formatNumber(v)
|
||||||
|
}
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stroke: "#000000",
|
stroke: "#000000",
|
||||||
@ -166,17 +187,25 @@
|
|||||||
size: 5 / devicePixelRatio,
|
size: 5 / devicePixelRatio,
|
||||||
stroke: "#000000",
|
stroke: "#000000",
|
||||||
},
|
},
|
||||||
values: (_, t) => t.map((v) => formatNumber(v)),
|
values: (_, t) => t.map((v) => {
|
||||||
|
return formatNumber(v)
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
label: xunit !== "" ? xunit : null,
|
label: xunit !== "" ? xunit : null,
|
||||||
value: (u, ts, sidx, didx) => {
|
value: (u, ts, sidx, didx) => {
|
||||||
if (usesBins) {
|
if (usesBins && xtime) {
|
||||||
|
const min = u.data[sidx][didx - 1] ? formatTime(u.data[sidx][didx - 1]) : 0;
|
||||||
|
const max = formatTime(u.data[sidx][didx]);
|
||||||
|
ts = min + " - " + max; // narrow spaces
|
||||||
|
} else if (usesBins) {
|
||||||
const min = u.data[sidx][didx - 1] ? u.data[sidx][didx - 1] : 0;
|
const min = u.data[sidx][didx - 1] ? u.data[sidx][didx - 1] : 0;
|
||||||
const max = u.data[sidx][didx];
|
const max = u.data[sidx][didx];
|
||||||
ts = min + " - " + max; // narrow spaces
|
ts = min + " - " + max; // narrow spaces
|
||||||
|
} else if (xtime) {
|
||||||
|
ts = formatTime(ts);
|
||||||
}
|
}
|
||||||
return ts;
|
return ts;
|
||||||
},
|
},
|
||||||
@ -191,6 +220,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
drawStyle: drawStyles.bars,
|
drawStyle: drawStyles.bars,
|
||||||
|
width: 1, // 1 / lastBinCount,
|
||||||
lineInterpolation: null,
|
lineInterpolation: null,
|
||||||
stroke: "#85abce",
|
stroke: "#85abce",
|
||||||
fill: "#85abce", // + "1A", // Transparent Fill
|
fill: "#85abce", // + "1A", // Transparent Fill
|
||||||
|
@ -152,10 +152,12 @@
|
|||||||
const lineWidth =
|
const lineWidth =
|
||||||
clusterCockpitConfig.plot_general_lineWidth / window.devicePixelRatio;
|
clusterCockpitConfig.plot_general_lineWidth / window.devicePixelRatio;
|
||||||
const lineColors = clusterCockpitConfig.plot_general_colorscheme;
|
const lineColors = clusterCockpitConfig.plot_general_colorscheme;
|
||||||
|
const cbmode = clusterCockpitConfig?.plot_general_colorblindMode || false;
|
||||||
|
|
||||||
const backgroundColors = {
|
const backgroundColors = {
|
||||||
normal: "rgba(255, 255, 255, 1.0)",
|
normal: "rgba(255, 255, 255, 1.0)",
|
||||||
caution: "rgba(255, 128, 0, 0.3)",
|
caution: cbmode ? "rgba(239, 230, 69, 0.3)" : "rgba(255, 128, 0, 0.3)",
|
||||||
alert: "rgba(255, 0, 0, 0.3)",
|
alert: cbmode ? "rgba(225, 86, 44, 0.3)" : "rgba(255, 0, 0, 0.3)",
|
||||||
};
|
};
|
||||||
const thresholds = findJobAggregationThresholds(
|
const thresholds = findJobAggregationThresholds(
|
||||||
subClusterTopology,
|
subClusterTopology,
|
||||||
@ -348,13 +350,13 @@
|
|||||||
label: "min",
|
label: "min",
|
||||||
scale: "y",
|
scale: "y",
|
||||||
width: lineWidth,
|
width: lineWidth,
|
||||||
stroke: "red",
|
stroke: cbmode ? "rgb(0,255,0)" : "red",
|
||||||
});
|
});
|
||||||
plotSeries.push({
|
plotSeries.push({
|
||||||
label: "max",
|
label: "max",
|
||||||
scale: "y",
|
scale: "y",
|
||||||
width: lineWidth,
|
width: lineWidth,
|
||||||
stroke: "green",
|
stroke: cbmode ? "rgb(0,0,255)" : "green",
|
||||||
});
|
});
|
||||||
plotSeries.push({
|
plotSeries.push({
|
||||||
label: usesMeanStatsSeries ? "mean" : "median",
|
label: usesMeanStatsSeries ? "mean" : "median",
|
||||||
@ -364,8 +366,8 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
plotBands = [
|
plotBands = [
|
||||||
{ series: [2, 3], fill: "rgba(0,255,0,0.1)" },
|
{ series: [2, 3], fill: cbmode ? "rgba(0,0,255,0.1)" : "rgba(0,255,0,0.1)" },
|
||||||
{ series: [3, 1], fill: "rgba(255,0,0,0.1)" },
|
{ series: [3, 1], fill: cbmode ? "rgba(0,255,0,0.1)" : "rgba(255,0,0,0.1)" },
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < series.length; i++) {
|
for (let i = 0; i < series.length; i++) {
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
let timeoutId = null;
|
let timeoutId = null;
|
||||||
|
|
||||||
const lineWidth = clusterCockpitConfig.plot_general_lineWidth;
|
const lineWidth = clusterCockpitConfig.plot_general_lineWidth;
|
||||||
|
const cbmode = clusterCockpitConfig?.plot_general_colorblindMode || false;
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
function getGradientR(x) {
|
function getGradientR(x) {
|
||||||
@ -61,7 +62,7 @@
|
|||||||
return Math.floor(x * 255.0);
|
return Math.floor(x * 255.0);
|
||||||
}
|
}
|
||||||
function getRGB(c) {
|
function getRGB(c) {
|
||||||
return `rgb(${getGradientR(c)}, ${getGradientG(c)}, ${getGradientB(c)})`;
|
return `rgb(${cbmode ? '0' : getGradientR(c)}, ${getGradientG(c)}, ${getGradientB(c)})`;
|
||||||
}
|
}
|
||||||
function nearestThousand(num) {
|
function nearestThousand(num) {
|
||||||
return Math.ceil(num / 1000) * 1000;
|
return Math.ceil(num / 1000) * 1000;
|
||||||
|
@ -449,7 +449,7 @@ function getMetricConfigDeep(metric, cluster, subCluster) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convert2uplot(canvasData) {
|
export function convert2uplot(canvasData, secondsToMinutes = false, secondsToHours = false) {
|
||||||
// Prep: Uplot Data Structure
|
// Prep: Uplot Data Structure
|
||||||
let uplotData = [[],[]] // [X, Y1, Y2, ...]
|
let uplotData = [[],[]] // [X, Y1, Y2, ...]
|
||||||
// Iterate if exists
|
// Iterate if exists
|
||||||
@ -457,11 +457,21 @@ export function convert2uplot(canvasData) {
|
|||||||
canvasData.forEach( cd => {
|
canvasData.forEach( cd => {
|
||||||
if (Object.keys(cd).length == 4) { // MetricHisto Datafromat
|
if (Object.keys(cd).length == 4) { // MetricHisto Datafromat
|
||||||
uplotData[0].push(cd?.max ? cd.max : 0)
|
uplotData[0].push(cd?.max ? cd.max : 0)
|
||||||
|
uplotData[1].push(cd?.count ? cd.count : 0)
|
||||||
|
} else { // Default -> Fill Histodata with zero values on unused value placing -> maybe allows zoom trigger as known
|
||||||
|
if (secondsToHours) {
|
||||||
|
let hours = cd.value / 3600
|
||||||
|
console.log("x seconds to y hours", cd.value, hours)
|
||||||
|
uplotData[0].push(hours)
|
||||||
|
} else if (secondsToMinutes) {
|
||||||
|
let minutes = cd.value / 60
|
||||||
|
console.log("x seconds to y minutes", cd.value, minutes)
|
||||||
|
uplotData[0].push(minutes)
|
||||||
|
} else {
|
||||||
|
uplotData[0].push(cd.value)
|
||||||
|
}
|
||||||
uplotData[1].push(cd.count)
|
uplotData[1].push(cd.count)
|
||||||
} else { // Default
|
}
|
||||||
uplotData[0].push(cd.value)
|
|
||||||
uplotData[1].push(cd.count)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return uplotData
|
return uplotData
|
||||||
|
Loading…
Reference in New Issue
Block a user