scalar Time
scalar Any

scalar NullableFloat
scalar MetricScope
scalar JobState

type Job {
  id:               ID!
  jobId:            Int!
  user:             String!
  project:          String!
  cluster:          String!
  subCluster:       String!
  startTime:        Time!
  duration:         Int!
  walltime:         Int!
  numNodes:         Int!
  numHWThreads:     Int!
  numAcc:           Int!
  SMT:              Int!
  exclusive:        Int!
  partition:        String!
  arrayJobId:       Int!
  monitoringStatus: Int!
  state:            JobState!
  tags:             [Tag!]!
  resources:        [Resource!]!

  metaData:         Any
  userData:         User
}

type Cluster {
  name:         String!
  partitions:   [String!]!        # Slurm partitions
  metricConfig: [MetricConfig!]!
  subClusters:  [SubCluster!]!    # Hardware partitions/subclusters
}

type SubCluster {
  name:            String!
  nodes:           String!
  numberOfNodes:   Int!
  processorType:   String!
  socketsPerNode:  Int!
  coresPerSocket:  Int!
  threadsPerCore:  Int!
  flopRateScalar:  Int!
  flopRateSimd:    Int!
  memoryBandwidth: Int!
  topology:        Topology!
}

type Topology {
  node:         [Int!]
  socket:       [[Int!]!]
  memoryDomain: [[Int!]!]
  die:          [[Int!]!]
  core:         [[Int!]!]
  accelerators: [Accelerator!]
}

type Accelerator {
  id:    String!
  type:  String!
  model: String!
}

type SubClusterConfig {
  name:    String!
  peak:    Float!
  normal:  Float!
  caution: Float!
  alert:   Float!
}

type MetricConfig {
  name:        String!
  unit:        String!
  scope:       MetricScope!
  aggregation: String
  timestep:    Int!
  peak:        Float
  normal:      Float
  caution:     Float
  alert:       Float
  subClusters: [SubClusterConfig]
}

type Tag {
  id:   ID!
  type: String!
  name: String!
}

type Resource {
  hostname:      String!
  hwthreads:     [Int!]
  accelerators:  [String!]
  configuration: String
}

type JobMetricWithName {
  name:   String!
  metric: JobMetric!
}

type JobMetric {
  unit:             String!
  scope:            MetricScope!
  timestep:         Int!
  series:           [Series!]
  statisticsSeries: StatsSeries
}

type Series {
  hostname:   String!
  id:         Int
  statistics: MetricStatistics
  data:       [NullableFloat!]!
}

type MetricStatistics {
  avg: Float!
  min: Float!
  max: Float!
}

type StatsSeries {
  mean: [NullableFloat!]!
  min:  [NullableFloat!]!
  max:  [NullableFloat!]!
}

type MetricFootprints {
  metric: String!
  data:   [NullableFloat!]!
}

type Footprints {
  nodehours: [NullableFloat!]!
  metrics:   [MetricFootprints!]!
}

enum Aggregate { USER, PROJECT, CLUSTER }
enum Weights { NODE_COUNT, NODE_HOURS }

type NodeMetrics {
  host:       String!
  subCluster: String!
  metrics:    [JobMetricWithName!]!
}

type Count {
  name:  String!
  count: Int!
}

type User {
  username: String!
  name:     String!
  email:    String!
}

type Query {
  clusters:     [Cluster!]!   # List of all clusters
  tags:         [Tag!]!       # List of all tags

  user(username: String!): User
  allocatedNodes(cluster: String!): [Count!]!

  job(id: ID!): Job
  jobMetrics(id: ID!, metrics: [String!], scopes: [MetricScope!]): [JobMetricWithName!]!
  jobsFootprints(filter: [JobFilter!], metrics: [String!]!): Footprints

  jobs(filter: [JobFilter!], page: PageRequest, order: OrderByInput): JobResultList!
  jobsStatistics(filter: [JobFilter!], groupBy: Aggregate): [JobsStatistics!]!
  jobsCount(filter: [JobFilter]!, groupBy: Aggregate!, weight: Weights, limit: Int): [Count!]!

  rooflineHeatmap(filter: [JobFilter!]!, rows: Int!, cols: Int!, minX: Float!, minY: Float!, maxX: Float!, maxY: Float!): [[Float!]!]!

  nodeMetrics(cluster: String!, nodes: [String!], scopes: [MetricScope!], metrics: [String!], from: Time!, to: Time!): [NodeMetrics!]!
}

type Mutation {
  createTag(type: String!, name: String!): Tag!
  deleteTag(id: ID!): ID!
  addTagsToJob(job: ID!, tagIds: [ID!]!): [Tag!]!
  removeTagsFromJob(job: ID!, tagIds: [ID!]!): [Tag!]!

  updateConfiguration(name: String!, value: String!): String
}

type IntRangeOutput { from: Int!, to: Int! }
type TimeRangeOutput { from: Time!, to: Time! }

input JobFilter {
  tags:        [ID!]
  jobId:       StringInput
  arrayJobId:  Int
  user:        StringInput
  project:     StringInput
  cluster:     StringInput
  partition:   StringInput
  duration:    IntRange

  minRunningFor: Int

  numNodes:        IntRange
  numAccelerators: IntRange
  numHWThreads:    IntRange

  startTime:   TimeRange
  state:       [JobState!]
  flopsAnyAvg: FloatRange
  memBwAvg:    FloatRange
  loadAvg:     FloatRange
  memUsedMax:  FloatRange
}

input OrderByInput {
  field: String!
  order: SortDirectionEnum! = ASC
}

enum SortDirectionEnum {
  DESC
  ASC
}

input StringInput {
  eq:         String
  contains:   String
  startsWith: String
  endsWith:   String
}

input IntRange   { from: Int!,   to: Int! }
input FloatRange { from: Float!, to: Float! }
input TimeRange  { from: Time,   to: Time }

type JobResultList {
  items:  [Job!]!
  offset: Int
  limit:  Int
  count:  Int
}

type HistoPoint {
  count: Int!
  value: Int!
}

type JobsStatistics  {
  id:             ID!            # If `groupBy` was used, ID of the user/project/cluster
  totalJobs:      Int!           # Number of jobs that matched
  shortJobs:      Int!           # Number of jobs with a duration of less than 2 minutes
  totalWalltime:  Int!           # Sum of the duration of all matched jobs in hours
  totalCoreHours: Int!           # Sum of the core hours of all matched jobs
  histDuration:   [HistoPoint!]! # value: hour, count: number of jobs with a rounded duration of value
  histNumNodes:   [HistoPoint!]! # value: number of nodes, count: number of jobs with that number of nodes
}

input PageRequest {
  itemsPerPage: Int!
  page:         Int!
}
