Skip to content

Commit bedbcaa

Browse files
committed
refactor: change code location and color gradiant of repair attempt graph
1 parent c63ba65 commit bedbcaa

File tree

4 files changed

+127
-101
lines changed

4 files changed

+127
-101
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import {RunInfoFromReportServer} from '../../../../../runner/shared-interfaces';
2+
import {BuildResultStatus} from '../../../../../runner/workers/builder/builder-types';
3+
import {ScoreCssVariable} from '../../shared/scoring';
4+
import {StackedBarChartData} from '../../shared/visualization/stacked-bar-chart/stacked-bar-chart';
5+
6+
/**
7+
* Calculates the average number of repair attempts performed in a run.
8+
*/
9+
export function calculateAverageRepairAttempts(report: RunInfoFromReportServer) {
10+
let totalRepairs = 0;
11+
let count = 0;
12+
13+
for (const result of report.results) {
14+
// Only consider successful builds that required repairs.
15+
if (
16+
result.finalAttempt.buildResult.status === BuildResultStatus.SUCCESS &&
17+
result.repairAttempts > 0
18+
) {
19+
totalRepairs += result.repairAttempts;
20+
count++;
21+
}
22+
}
23+
24+
return count > 0 ? totalRepairs / count : null;
25+
}
26+
27+
/**
28+
* Creates graph data for the "repair attempt" graph, from a given run report.
29+
*/
30+
export function createRepairAttemptGraphData(report: RunInfoFromReportServer) {
31+
const repairsToAppCount = new Map<number | 'failed', number>();
32+
33+
// Map repair count to how many applications shared that count.
34+
let maxRepairCount = 0;
35+
for (const result of report.results) {
36+
if (result.finalAttempt.buildResult.status === BuildResultStatus.ERROR) {
37+
repairsToAppCount.set('failed', (repairsToAppCount.get('failed') || 0) + 1);
38+
} else {
39+
const repairs = result.repairAttempts;
40+
// For this graph, we ignore applications that required no repair.
41+
if (repairs > 0) {
42+
repairsToAppCount.set(repairs, (repairsToAppCount.get(repairs) || 0) + 1);
43+
maxRepairCount = Math.max(maxRepairCount, repairs);
44+
}
45+
}
46+
}
47+
48+
const data: StackedBarChartData = [];
49+
50+
// All the numeric keys, sorted by value.
51+
const intermediateRepairKeys = Array.from(repairsToAppCount.keys())
52+
.filter((k): k is number => typeof k === 'number')
53+
.sort((a, b) => a - b);
54+
55+
// This graph might involve a bunch of sections. We want to scale them among all the possible color "grades".
56+
57+
for (let repairCount = 1; repairCount <= maxRepairCount; repairCount++) {
58+
const applicationCount = repairsToAppCount.get(repairCount);
59+
if (!applicationCount) continue;
60+
61+
data.push({
62+
label: labelByRepairCount(repairCount),
63+
color: colorByRepairCount(repairCount),
64+
value: applicationCount,
65+
});
66+
}
67+
68+
// Handle 'Build failed even after all retries' - always maps to the "failure" grade.
69+
const failedCount = repairsToAppCount.get('failed') || 0;
70+
if (failedCount > 0) {
71+
data.push({
72+
label: 'Build failed even after all retries',
73+
color: ScoreCssVariable.poor,
74+
value: failedCount,
75+
});
76+
}
77+
return data;
78+
}
79+
80+
function labelByRepairCount(repairCount: number): string {
81+
switch (repairCount) {
82+
case 1:
83+
return '1 repair';
84+
case 2:
85+
case 3:
86+
case 4:
87+
return `${repairCount} repairs`;
88+
default:
89+
return '5+ repairs';
90+
}
91+
}
92+
93+
function colorByRepairCount(repairCount: number): string {
94+
// We're using mediocre1-5 since these are essentially *all* bad so we don't want green in this
95+
// graph.
96+
switch (repairCount) {
97+
case 1:
98+
return ScoreCssVariable.mediocre1;
99+
case 2:
100+
return ScoreCssVariable.mediocre2;
101+
case 3:
102+
return ScoreCssVariable.mediocre3;
103+
case 4:
104+
return ScoreCssVariable.mediocre4;
105+
default:
106+
return ScoreCssVariable.mediocre5;
107+
}
108+
}

report-app/src/app/pages/report-viewer/report-viewer.ts

Lines changed: 6 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
11
import {Clipboard} from '@angular/cdk/clipboard';
22
import {DatePipe, DecimalPipe} from '@angular/common';
33
import {HttpClient} from '@angular/common/http';
4-
import {
5-
afterNextRender,
6-
Component,
7-
computed,
8-
ElementRef,
9-
inject,
10-
input,
11-
resource,
12-
signal,
13-
viewChild,
14-
} from '@angular/core';
4+
import {afterNextRender, Component, computed, inject, input, resource, signal} from '@angular/core';
155
import {NgxJsonViewerModule} from 'ngx-json-viewer';
166
import {
177
BuildErrorType,
@@ -48,7 +38,10 @@ import {ProviderLabel} from '../../shared/provider-label';
4838
import {AiAssistant} from '../../shared/ai-assistant/ai-assistant';
4939
import {LighthouseCategory} from './lighthouse-category';
5040
import {MultiSelect} from '../../shared/multi-select/multi-select';
41+
<<<<<<< HEAD
5142
import {FileCodeViewer} from '../../shared/file-code-viewer/file-code-viewer';
43+
=======
44+
>>>>>>> 7c6ff1f (refactor: change code location and color gradiant of repair attempt graph)
5245
import {
5346
calculateAverageRepairAttempts,
5447
createRepairAttemptGraphData,
@@ -311,21 +304,7 @@ export class ReportViewer {
311304
return null;
312305
}
313306

314-
let totalRepairs = 0;
315-
let count = 0;
316-
317-
for (const result of report.results) {
318-
// Only consider successful builds that required repairs.
319-
if (
320-
result.finalAttempt.buildResult.status === BuildResultStatus.SUCCESS &&
321-
result.repairAttempts > 0
322-
) {
323-
totalRepairs += result.repairAttempts;
324-
count++;
325-
}
326-
}
327-
328-
return count > 0 ? totalRepairs / count : null;
307+
return calculateAverageRepairAttempts(report);
329308
});
330309

331310
protected repairAttemptsAsGraphData = computed<StackedBarChartData>(() => {
@@ -334,70 +313,7 @@ export class ReportViewer {
334313
return [];
335314
}
336315

337-
const repairsToAppCount = new Map<number | 'failed', number>();
338-
339-
// Map repair count to how many applications shared that count.
340-
let maxRepairCount = 0;
341-
for (const result of report.results) {
342-
if (result.finalAttempt.buildResult.status === BuildResultStatus.ERROR) {
343-
repairsToAppCount.set('failed', (repairsToAppCount.get('failed') || 0) + 1);
344-
} else {
345-
const repairs = result.repairAttempts;
346-
// For this graph, we ignore applications that required no repair.
347-
if (repairs > 0) {
348-
repairsToAppCount.set(repairs, (repairsToAppCount.get(repairs) || 0) + 1);
349-
maxRepairCount = Math.max(maxRepairCount, repairs);
350-
}
351-
}
352-
}
353-
354-
const data: StackedBarChartData = [];
355-
356-
// All the numeric keys, sorted by value.
357-
const intermediateRepairKeys = Array.from(repairsToAppCount.keys())
358-
.filter((k): k is number => typeof k === 'number')
359-
.sort((a, b) => a - b);
360-
361-
// This graph might involve a bunch of sections. We want to scale them among all the possible color "grades".
362-
363-
const minGrade = 1;
364-
const maxGrade = 8;
365-
const failureGrade = 9;
366-
367-
for (let repairCount = 1; repairCount <= maxRepairCount; repairCount++) {
368-
const applicationCount = repairsToAppCount.get(repairCount);
369-
if (!applicationCount) continue;
370-
const label = `${repairCount} repair${repairCount > 1 ? 's' : ''}`;
371-
372-
// Normalize the repair count to the range [0, 1].
373-
const normalizedRepairCount = (repairCount - 1) / (maxRepairCount - 1);
374-
375-
let gradeIndex: number;
376-
if (intermediateRepairKeys.length === 1) {
377-
// If there's only one intermediate repair count, map it to a middle grade (e.g., --chart-grade-5)
378-
gradeIndex = Math.floor(maxGrade / 2) + minGrade;
379-
} else {
380-
// Distribute multiple intermediate repair counts evenly across available grades
381-
gradeIndex = minGrade + Math.round(normalizedRepairCount * (maxGrade - minGrade));
382-
}
383-
384-
data.push({
385-
label,
386-
color: `var(--chart-grade-${gradeIndex})`,
387-
value: applicationCount,
388-
});
389-
}
390-
391-
// Handle 'Build failed even after all retries' - always maps to the "failure" grade.
392-
const failedCount = repairsToAppCount.get('failed') || 0;
393-
if (failedCount > 0) {
394-
data.push({
395-
label: 'Build failed even after all retries',
396-
color: `var(--chart-grade-${failureGrade})`,
397-
value: failedCount,
398-
});
399-
}
400-
return data;
316+
return createRepairAttemptGraphData(report);
401317
});
402318

403319
protected testsAsGraphData(tests: RunSummaryTests): StackedBarChartData {

report-app/src/app/shared/scoring.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ export enum ScoreCssVariable {
88
good = 'var(--status-fill-good)',
99
poor = 'var(--status-fill-poor)',
1010
neutral = 'var(--status-fill-neutral)',
11+
// When we need more refined gradiant between "good" and "poor".
12+
mediocre1 = 'var(--status-fill-mediocre-1)',
13+
mediocre2 = 'var(--status-fill-mediocre-2)',
14+
mediocre3 = 'var(--status-fill-mediocre-3)',
15+
mediocre4 = 'var(--status-fill-mediocre-4)',
16+
mediocre5 = 'var(--status-fill-mediocre-5)',
1117
}
1218

1319
const CACHED_COLORS = {

report-app/src/styles.scss

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,19 @@
3232
--status-fill-poor: #ef4444;
3333
--status-fill-neutral: #aaa;
3434

35+
/* When we need a more gradiant spread of "meh". */
36+
--status-fill-mediocre-1: #fbbc04; /* Yellow 500 */
37+
--status-fill-mediocre-2: #f9ab00; /* Yellow 600 */
38+
--status-fill-mediocre-3: #f29900; /* Yellow 700 */
39+
--status-fill-mediocre-4: #ea8600; /* Yellow 800 */
40+
--status-fill-mediocre-5: #e37400; /* Yellow 900 */
41+
3542
--status-text-excellent: #0c855d;
3643
--status-text-great: #0c855d; // TODO: do we want to differentiate from `excellent`?
3744
--status-text-good: #c57f08;
3845
--status-text-poor: #eb1515;
3946
--status-text-neutral: #64748b;
4047

41-
/* 10-step Green-to-Red Quality Gradient */
42-
--chart-grade-1: #10b981; /* Emerald 500 (Excellent) */
43-
--chart-grade-2: #22c55e; /* Green 500 */
44-
--chart-grade-3: #4ade80; /* Green 400 */
45-
--chart-grade-4: #84cc16; /* Lime 500 (Great) */
46-
--chart-grade-5: #a3e635; /* Lime 400 */
47-
--chart-grade-6: #facc15; /* Yellow 400 */
48-
--chart-grade-7: #f59e0b; /* Amber 500 (Good) */
49-
--chart-grade-8: #f97316; /* Orange 500 */
50-
--chart-grade-9: #ef4444; /* Red 500 (Poor) */
51-
5248
--tooltip-background-color: light-dark(#111827, #f1f4f9);
5349
--tooltip-text-color: light-dark(#f9fafb, #1e293b);
5450

0 commit comments

Comments
 (0)