diff --git a/api/schema.graphqls b/api/schema.graphqls index db19626..268a579 100644 --- a/api/schema.graphqls +++ b/api/schema.graphqls @@ -171,7 +171,8 @@ type ScopedStats { } type JobStats { - jobId: Int! + id: Int! + jobId: String! startTime: Int! duration: Int! cluster: String! diff --git a/internal/graph/generated/generated.go b/internal/graph/generated/generated.go index 615b3f8..e73bcf1 100644 --- a/internal/graph/generated/generated.go +++ b/internal/graph/generated/generated.go @@ -173,6 +173,7 @@ type ComplexityRoot struct { JobStats struct { Cluster func(childComplexity int) int Duration func(childComplexity int) int + ID func(childComplexity int) int JobID func(childComplexity int) int NumAccelerators func(childComplexity int) int NumHWThreads func(childComplexity int) int @@ -961,6 +962,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.JobStats.Duration(childComplexity), true + case "JobStats.id": + if e.complexity.JobStats.ID == nil { + break + } + + return e.complexity.JobStats.ID(childComplexity), true + case "JobStats.jobId": if e.complexity.JobStats.JobID == nil { break @@ -2336,7 +2344,8 @@ type ScopedStats { } type JobStats { - jobId: Int! + id: Int! + jobId: String! startTime: Int! duration: Int! cluster: String! @@ -7419,6 +7428,50 @@ func (ec *executionContext) fieldContext_JobResultList_hasNextPage(_ context.Con return fc, nil } +func (ec *executionContext) _JobStats_id(ctx context.Context, field graphql.CollectedField, obj *model.JobStats) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_JobStats_id(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.ID, 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_id(_ 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_jobId(ctx context.Context, field graphql.CollectedField, obj *model.JobStats) (ret graphql.Marshaler) { fc, err := ec.fieldContext_JobStats_jobId(ctx, field) if err != nil { @@ -7445,9 +7498,9 @@ func (ec *executionContext) _JobStats_jobId(ctx context.Context, field graphql.C } return graphql.Null } - res := resTmp.(int) + res := resTmp.(string) fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_JobStats_jobId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -7457,7 +7510,7 @@ func (ec *executionContext) fieldContext_JobStats_jobId(_ context.Context, 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 nil, errors.New("field of type String does not have child fields") }, } return fc, nil @@ -11560,6 +11613,8 @@ func (ec *executionContext) fieldContext_Query_jobsMetricStats(ctx context.Conte IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { + case "id": + return ec.fieldContext_JobStats_id(ctx, field) case "jobId": return ec.fieldContext_JobStats_jobId(ctx, field) case "startTime": @@ -17901,6 +17956,11 @@ func (ec *executionContext) _JobStats(ctx context.Context, sel ast.SelectionSet, switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("JobStats") + case "id": + out.Values[i] = ec._JobStats_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "jobId": out.Values[i] = ec._JobStats_jobId(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 d88fdc5..5c50ff9 100644 --- a/internal/graph/model/models_gen.go +++ b/internal/graph/model/models_gen.go @@ -97,7 +97,8 @@ type JobResultList struct { } type JobStats struct { - JobID int `json:"jobId"` + ID int `json:"id"` + JobID string `json:"jobId"` StartTime int `json:"startTime"` Duration int `json:"duration"` Cluster string `json:"cluster"` diff --git a/internal/graph/schema.resolvers.go b/internal/graph/schema.resolvers.go index 64e63db..f3fc389 100644 --- a/internal/graph/schema.resolvers.go +++ b/internal/graph/schema.resolvers.go @@ -618,7 +618,8 @@ 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), + ID: int(job.ID), + JobID: strconv.Itoa(int(job.JobID)), StartTime: int(job.StartTime.Unix()), Duration: int(job.Duration), Cluster: job.Cluster, diff --git a/web/frontend/src/generic/JobCompare.svelte b/web/frontend/src/generic/JobCompare.svelte index 13d271f..9c3316b 100644 --- a/web/frontend/src/generic/JobCompare.svelte +++ b/web/frontend/src/generic/JobCompare.svelte @@ -22,7 +22,7 @@ getContextClient, // mutationStore, } from "@urql/svelte"; - import { Row, Col, Card, Spinner } from "@sveltestrap/sveltestrap"; + import { Row, Col, Card, Spinner, Table, Input, InputGroup, InputGroupText, Icon } from "@sveltestrap/sveltestrap"; import { formatTime } from "./units.js"; import Comparogram from "./plots/Comparogram.svelte"; @@ -42,6 +42,7 @@ let comparePlotData = {}; let jobIds = []; let jobClusters = []; + let tableJobIDFilter = ""; /*uPlot*/ let plotSync = uPlot.sync("compareJobsView"); @@ -52,6 +53,7 @@ const compareQuery = gql` query ($filter: [JobFilter!]!, $metrics: [String!]!) { jobsMetricStats(filter: $filter, metrics: $metrics) { + id jobId startTime duration @@ -234,21 +236,67 @@ {/each} {/key} -

- {#each $compareData.data.jobsMetricStats as job, jindex (job.jobId)} - - {jindex}: {job.jobId} - {new Date(job.startTime * 1000).toISOString()} - {formatTime(job.duration)} - {#each job.stats as stat (stat.name)} - {stat.name} - Min {stat.data.min} - Avg {stat.data.avg} - Max {stat.data.max} - {/each} - -
- {:else} -
No jobs found
- {/each} +
+ + + + + + + + + + + + {#each metrics as metric} + + {/each} + + + + + + {/each} + {#each metrics as metric} + {#each ["min", "avg", "max"] as stat} + + {/each} + {/each} + + + + {#each $compareData.data.jobsMetricStats.filter((j) => j.jobId.includes(tableJobIDFilter)) as job, jindex (job.jobId)} + + + + + + + + + + {#each metrics as metric} + + + + {/each} + + {:else} + No jobs found + {/each} + +
IndexJobIDClusterStartTimeDurationResources{metric}
+ + + + + + + + + + + {#each ["Nodes", "Threads", "Accs"] as res} + {res}{stat}
{jindex}{job.jobId}{job.cluster} ({job.subCluster}){new Date(job.startTime * 1000).toISOString()}{formatTime(job.duration)}{job.numNodes}{job.numHWThreads}{job.numAccelerators}{job.stats.find((s) => s.name == metric).data.min}{job.stats.find((s) => s.name == metric).data.avg}{job.stats.find((s) => s.name == metric).data.max}
+
{/if} \ No newline at end of file