github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/cmd/deck/static/prow/histogram.ts (about)

     1  import {ProwJobState} from "../api/prow";
     2  
     3  export class JobHistogram {
     4    public start: number;
     5    public end: number;
     6    private data: JobSample[];
     7  
     8    constructor() {
     9      this.data = [];
    10      this.start = Number.MAX_SAFE_INTEGER;
    11      this.end = 0;
    12    }
    13    // add adds a sample to the histogram, filtering states that didn't result in success or clear
    14    // failure, and updating the range of the histogram data.
    15    public add(sample: JobSample) {
    16      if (!(sample.state === "success" || sample.state === "failure" || sample.state === "error")) {
    17        return;
    18      }
    19      if (sample.start < this.start) {
    20        this.start = sample.start;
    21      }
    22      if (sample.start > this.end) {
    23        this.end = sample.start;
    24      }
    25      this.data.push(sample);
    26    }
    27    // buckets assigns all samples between start and end into cols buckets, sorted by
    28    // start timestamp, while the buckets themselves are sorted by duration.
    29    public buckets(start: number, end: number, cols: number): JobBuckets {
    30      this.data.sort((s1, s2) => s1.start - s2.start);
    31  
    32      const buckets: JobSample[][] = [[]];
    33      const stride = (end - start) / cols;
    34      let next = start + stride;
    35      let max = 0;
    36      this.data.forEach((sample) => {
    37        if (sample.start < start || sample.start > end) {
    38          return;
    39        }
    40        if (sample.duration > max) {
    41          max = sample.duration;
    42        }
    43        if (sample.start < next || sample.start === end) {
    44          buckets[buckets.length - 1].push(sample);
    45          return;
    46        }
    47  
    48        const bucket = buckets[buckets.length - 1];
    49        bucket.sort((s1, s2) => s1.duration - s2.duration);
    50  
    51        next = next + stride;
    52        while (next < sample.start) {
    53          buckets.push([]);
    54          next = next + stride;
    55        }
    56        buckets.push([sample]);
    57      });
    58      if (buckets.length > 0) {
    59        const lastBucket = buckets[buckets.length - 1];
    60        lastBucket.sort((s1, s2) => s1.duration - s2.duration);
    61      }
    62      while (buckets.length < cols) {
    63        buckets.push([]);
    64      }
    65      return new JobBuckets(buckets, start, end, max);
    66    }
    67    // length returns the number of samples in the histogram.
    68    public get length(): number {
    69      return this.data.length;
    70    }
    71  }
    72  
    73  export class JobSample {
    74    constructor(public start: number,
    75      public duration: number,
    76      public state: ProwJobState,
    77      public row: number) {}
    78  }
    79  
    80  export class JobBuckets {
    81    constructor(public data: JobSample[][],
    82      public start: number,
    83      public end: number,
    84      public max: number) { }
    85  
    86    public limitMaximum(maximum: number) {
    87      if (this.max > maximum) {
    88        this.max = maximum;
    89      }
    90    }
    91  
    92    public linearChunks(bucket: JobSample[], rows: number): JobSample[][] {
    93      const stride = Math.ceil((this.max) / rows);
    94      const chunks: JobSample[][] = [];
    95      chunks[0] = [];
    96      let next = stride;
    97      for (const sample of bucket) {
    98        if (sample.duration <= next) {
    99          chunks[chunks.length - 1].push(sample);
   100          continue;
   101        }
   102        next = next + stride;
   103        while (next < sample.duration) {
   104          if (chunks.length > (rows - 1)) {
   105            break;
   106          }
   107          chunks.push([]);
   108          next = next + stride;
   109        }
   110        if (chunks.length > (rows - 1)) {
   111          chunks[chunks.length - 1].push(sample);
   112        } else {
   113          chunks.push([sample]);
   114        }
   115      }
   116      if (chunks.length > rows) {
   117        throw new Error("invalid rows");
   118      }
   119      return chunks;
   120    }
   121  }