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 }