-
Notifications
You must be signed in to change notification settings - Fork 11
/
scoring.ts
120 lines (109 loc) · 3.32 KB
/
scoring.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import {
AuditGroup,
AuditReport,
CategoryConfig,
PluginReport,
Report,
} from '@code-pushup/models';
type EnrichedAuditReport = AuditReport & { plugin: string };
type EnrichedScoredAuditGroup = AuditGroup & { plugin: string; score: number };
type ScoredCategoryConfig = CategoryConfig & { score: number };
export type ScoredReport = Omit<Report, 'plugins' | 'categories'> & {
plugins: (Omit<PluginReport, 'audits' | 'groups'> & {
audits: EnrichedAuditReport[];
groups: EnrichedScoredAuditGroup[];
})[];
categories: ScoredCategoryConfig[];
};
function groupRefToScore(
audits: AuditReport[],
): (ref: AuditGroup['refs'][0]) => number {
return ref => {
const score = audits.find(audit => audit.slug === ref.slug)?.score;
if (score == null) {
throw new Error(
`Group has invalid ref - audit with slug ${ref.slug} not found`,
);
}
return score;
};
}
function categoryRefToScore(
audits: EnrichedAuditReport[],
groups: EnrichedScoredAuditGroup[],
): (ref: CategoryConfig['refs'][0]) => number {
return (ref: CategoryConfig['refs'][0]): number => {
switch (ref.type) {
case 'audit':
// eslint-disable-next-line no-case-declarations
const audit = audits.find(
a => a.slug === ref.slug && a.plugin === ref.plugin,
);
if (!audit) {
throw new Error(
`Category has invalid ref - audit with slug ${ref.slug} not found in ${ref.plugin} plugin`,
);
}
return audit.score;
case 'group':
// eslint-disable-next-line no-case-declarations
const group = groups.find(
g => g.slug === ref.slug && g.plugin === ref.plugin,
);
if (!group) {
throw new Error(
`Category has invalid ref - group with slug ${ref.slug} not found in ${ref.plugin} plugin`,
);
}
return group.score;
default:
throw new Error(`Type ${ref.type} is unknown`);
}
};
}
export function calculateScore<T extends { weight: number }>(
refs: T[],
scoreFn: (ref: T) => number,
): number {
const numerator = refs.reduce(
(sum, ref) => sum + scoreFn(ref) * ref.weight,
0,
);
const denominator = refs.reduce((sum, ref) => sum + ref.weight, 0);
return numerator / denominator;
}
export function scoreReport(report: Report): ScoredReport {
const scoredPlugins = report.plugins.map(plugin => {
const { groups, audits } = plugin;
const preparedAudits = audits.map(audit => ({
...audit,
plugin: plugin.slug,
}));
const preparedGroups =
groups?.map(group => ({
...group,
score: calculateScore(group.refs, groupRefToScore(preparedAudits)),
plugin: plugin.slug,
})) || [];
return {
...plugin,
audits: preparedAudits,
groups: preparedGroups,
};
});
// @TODO intro dict to avoid multiple find calls in the scoreFn
const allScoredAudits = scoredPlugins.flatMap(({ audits }) => audits);
const allScoredGroups = scoredPlugins.flatMap(({ groups }) => groups);
const scoredCategories = report.categories.map(category => ({
...category,
score: calculateScore(
category.refs,
categoryRefToScore(allScoredAudits, allScoredGroups),
),
}));
return {
...report,
categories: scoredCategories,
plugins: scoredPlugins,
};
}