scalar Time
scalar Any

scalar NullableFloat
scalar MetricScope
scalar JobState
scalar SchedulerState
scalar MonitoringState

type Node {
  id: ID!
  hostname: String!
  cluster: String!
  subCluster: String!
  jobsRunning: Int!
  cpusAllocated: Int
  memoryAllocated: Int
  gpusAllocated: Int
  schedulerState: SchedulerState!
  healthState: MonitoringState!
  metaData: Any
}

type NodeStates {
  state: String!
  count: Int!
}

type NodeStatesTimed {
  state: String!
  counts: [Int!]!
  times: [Int!]!
}

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!
  energy: Float!
  SMT: Int!
  shared: String!
  partition: String!
  arrayJobId: Int!
  monitoringStatus: Int!
  state: JobState!
  tags: [Tag!]!
  resources: [Resource!]!
  concurrentJobs: JobLinkResultList
  footprint: [FootprintValue]
  energyFootprint: [EnergyFootprintValue]
  metaData: Any
  userData: User
}

type JobLink {
  id: ID!
  jobId: Int!
}

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

type SubCluster {
  name: String!
  nodes: String!
  numberOfNodes: Int!
  processorType: String!
  socketsPerNode: Int!
  coresPerSocket: Int!
  threadsPerCore: Int!
  flopRateScalar: MetricValue!
  flopRateSimd: MetricValue!
  memoryBandwidth: MetricValue!
  topology: Topology!
  metricConfig: [MetricConfig!]!
  footprint: [String!]!
}

type FootprintValue {
  name: String!
  stat: String!
  value: Float!
}

type EnergyFootprintValue {
  hardware: String!
  metric: String!
  value: Float!
}

type MetricValue {
  name: String
  unit: Unit!
  value: Float!
}

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
  remove: Boolean
}

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

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

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

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

type ClusterMetricWithName {
  name: String!
  unit: Unit
  timestep: Int!
  data: [NullableFloat!]!
}

type JobMetric {
  unit: Unit
  timestep: Int!
  series: [Series!]
  statisticsSeries: StatsSeries
}

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

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

type NamedStatsWithScope {
  name: String!
  scope: MetricScope!
  stats: [ScopedStats!]!
}

type ScopedStats {
  hostname: String!
  id: String
  data: MetricStatistics!
}

type JobStats {
  id: Int!
  jobId: String!
  startTime: Int!
  duration: Int!
  cluster: String!
  subCluster: String!
  numNodes: Int!
  numHWThreads: Int
  numAccelerators: Int
  stats: [NamedStats!]!
}

type NamedStats {
  name: String!
  data: MetricStatistics!
}

type Unit {
  base: String!
  prefix: String
}

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

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

type Footprints {
  timeWeights: TimeWeights!
  metrics: [MetricFootprints!]!
}

type TimeWeights {
  nodeHours: [NullableFloat!]!
  accHours: [NullableFloat!]!
  coreHours: [NullableFloat!]!
}

enum Aggregate {
  USER
  PROJECT
  CLUSTER
  SUBCLUSTER
}
enum SortByAggregate {
  TOTALWALLTIME
  TOTALJOBS
  TOTALUSERS
  TOTALNODES
  TOTALNODEHOURS
  TOTALCORES
  TOTALCOREHOURS
  TOTALACCS
  TOTALACCHOURS
}

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

type ClusterMetrics {
  nodeCount: Int!
  metrics: [ClusterMetricWithName!]!
}

type NodesResultList {
  items: [NodeMetrics!]!
  offset: Int
  limit: Int
  count: Int
  totalNodes: Int
  hasNextPage: Boolean
}

type ClusterSupport {
  cluster: String!
  subClusters: [String!]!
}

type GlobalMetricListItem {
  name: String!
  unit: Unit!
  scope: MetricScope!
  footprint: String
  availability: [ClusterSupport!]!
}

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

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

input MetricStatItem {
  metricName: String!
  range: FloatRange!
}

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

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

  ## Node Queries New
  node(id: ID!): Node
  nodes(filter: [NodeFilter!], order: OrderByInput): NodeStateResultList!
  nodeStates(filter: [NodeFilter!]): [NodeStates!]!
  nodeStatesTimed(filter: [NodeFilter!], type: String!): [NodeStatesTimed!]!

  job(id: ID!): Job
  jobMetrics(
    id: ID!
    metrics: [String!]
    scopes: [MetricScope!]
    resolution: Int
  ): [JobMetricWithName!]!

  jobStats(id: ID!, metrics: [String!]): [NamedStats!]!

  scopedJobStats(
    id: ID!
    metrics: [String!]
    scopes: [MetricScope!]
  ): [NamedStatsWithScope!]!

  jobs(
    filter: [JobFilter!]
    page: PageRequest
    order: OrderByInput
  ): JobResultList!

  jobsStatistics(
    filter: [JobFilter!]
    metrics: [String!]
    page: PageRequest
    sortBy: SortByAggregate
    groupBy: Aggregate
    numDurationBins: String
    numMetricBins: Int
  ): [JobsStatistics!]!

  jobsMetricStats(filter: [JobFilter!], metrics: [String!]): [JobStats!]!
  jobsFootprints(filter: [JobFilter!], metrics: [String!]!): Footprints

  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!]!

  nodeMetricsList(
    cluster: String!
    subCluster: String!
    stateFilter: String!
    nodeFilter: String!
    scopes: [MetricScope!]
    metrics: [String!]
    from: Time!
    to: Time!
    page: PageRequest
    resolution: Int
  ): NodesResultList!

  clusterMetrics(
    cluster: String!
    metrics: [String!]
    from: Time!
    to: Time!
  ): ClusterMetrics!
}

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

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

type IntRangeOutput {
  from: Int!
  to: Int!
}
type TimeRangeOutput {
  range: String
  from: Time!
  to: Time!
}

input NodeFilter {
  hostname: StringInput
  cluster: StringInput
  subCluster: StringInput
  schedulerState: SchedulerState
  healthState: MonitoringState
  timeStart: Int
}

input JobFilter {
  tags: [ID!]
  dbId: [ID!]
  jobId: StringInput
  arrayJobId: Int
  user: StringInput
  project: StringInput
  jobName: StringInput
  cluster: StringInput
  subCluster: StringInput
  partition: StringInput
  duration: IntRange
  energy: FloatRange

  minRunningFor: Int

  numNodes: IntRange
  numAccelerators: IntRange
  numHWThreads: IntRange

  startTime: TimeRange
  state: [JobState!]
  metricStats: [MetricStatItem!]
  shared: String
  schedule: String
  node: StringInput
}

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

enum SortDirectionEnum {
  DESC
  ASC
}

input StringInput {
  eq: String
  neq: String
  contains: String
  startsWith: String
  endsWith: String
  in: [String!]
}

input IntRange {
  from: Int!
  to: Int!
}
input TimeRange {
  range: String
  from: Time
  to: Time
}

input FloatRange {
  from: Float!
  to: Float!
}

type NodeStateResultList {
  items: [Node!]!
  count: Int
}

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

type JobLinkResultList {
  listQuery: String
  items: [JobLink!]!
  count: Int
}

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

type MetricHistoPoints {
  metric: String!
  unit: String!
  stat: String
  data: [MetricHistoPoint!]
}

type MetricHistoPoint {
  bin: Int
  count: Int!
  min: Int
  max: Int
}

type JobsStatistics {
  id: ID! # If `groupBy` was used, ID of the user/project/cluster/subcluster
  name: String! # if User-Statistics: Given Name of Account (ID) Owner
  totalUsers: Int! # if *not* User-Statistics: Number of active users (based on running jobs)
  totalJobs: Int! # Number of jobs
  runningJobs: Int! # Number of running jobs
  shortJobs: Int! # Number of jobs with a duration of less than config'd ShortRunningJobsDuration
  totalWalltime: Int! # Sum of the duration of all matched jobs in hours
  totalNodes: Int! # Sum of the nodes of all matched jobs
  totalNodeHours: Int! # Sum of the node hours of all matched jobs
  totalCores: Int! # Sum of the cores of all matched jobs
  totalCoreHours: Int! # Sum of the core hours of all matched jobs
  totalAccs: Int! # Sum of the accs of all matched jobs
  totalAccHours: Int! # Sum of the gpu 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
  histNumCores: [HistoPoint!]! # value: number of cores, count: number of jobs with that number of cores
  histNumAccs: [HistoPoint!]! # value: number of accs, count: number of jobs with that number of accs
  histMetrics: [MetricHistoPoints!]! # metric: metricname, data array of histopoints: value: metric average bin, count: number of jobs with that metric average
}

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