Skip to content

Commit

Permalink
Merge branch 'hotspots'
Browse files Browse the repository at this point in the history
  • Loading branch information
manfredsteyer committed Oct 15, 2024
2 parents 92467fa + 0292893 commit feac40e
Show file tree
Hide file tree
Showing 18 changed files with 656 additions and 185 deletions.
27 changes: 25 additions & 2 deletions .detective/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,32 @@
"apps/backend/src/options",
"apps/backend/src/services",
"apps/backend/src/utils",
"apps/backend/src/infrastructure"
"apps/backend/src/infrastructure",
"apps/frontend/src/app/features/coupling",
"apps/frontend/src/app/features/hotspot",
"apps/frontend/src/app/features/team-alignment",
"apps/frontend/src/app/shell/about",
"apps/frontend/src/app/shell/filter-tree",
"apps/frontend/src/app/shell/nav",
"apps/frontend/src/app/model",
"apps/frontend/src/app/ui/doughnut",
"apps/frontend/src/app/ui/graph",
"apps/frontend/src/app/ui/limits",
"apps/frontend/src/app/ui/loading",
"apps/frontend/src/app/ui/resizer",
"apps/frontend/src/app/ui/treemap"
],
"groups": [
"apps/backend/src",
"apps/backend",
"apps/frontend/src/app/features",
"apps/frontend/src/app/shell",
"apps/frontend/src/app/ui",
"apps/frontend/src/app",
"apps/frontend/src",
"apps/frontend",
"apps"
],
"groups": ["apps/backend/src", "apps/backend", "apps"],
"entries": [],
"filter": {
"files": [],
Expand Down
2 changes: 1 addition & 1 deletion .detective/hash
Original file line number Diff line number Diff line change
@@ -1 +1 @@
979780ac55f4f8c3680055a838a645cdc8e591a0, v1.1.2
503c63bcf7fab325fc9687a40ad048b2b16ca636, v1.1.6
44 changes: 41 additions & 3 deletions .detective/log
Original file line number Diff line number Diff line change
@@ -1,9 +1,47 @@
"Manfred Steyer <[email protected]>,Sat Sep 28 23:22:37 2024 +0200 947a6f7933d93eeae42e53bbffd8d56f69486095,feat: add resizer for tree"
"Manfred Steyer <[email protected]>,Tue Oct 8 17:44:47 2024 +0200 6ed670acc998f83279713bc9c2a16ef7a79f66a1,feat: filter hotspots using slider and percent"
1 1 .detective/hash
33 3 .detective/log
70 11 apps/backend/src/services/hotspot.ts
8 10 apps/frontend/src/app/features/hotspot/hotspot.component.html
2 0 apps/frontend/src/app/features/hotspot/hotspot.component.ts
1 1 apps/frontend/src/app/features/hotspot/hotspot.store.ts

"Manfred Steyer <[email protected]>,Sun Sep 29 12:41:39 2024 +0200 4ee102065cca1a50063a871b078ec87680116efb,chore(release): publish 1.1.6"
10 0 CHANGELOG.md
1 1 apps/backend/package.json

"Manfred Steyer <[email protected]>,Sun Sep 29 12:41:19 2024 +0200 0130a5533f0979c406e469c74305d8e214b4e0ee,fix: prevent word wrap in tree"
5 0 apps/frontend/src/app/shell/filter-tree/filter-tree.component.css

"Manfred Steyer <[email protected]>,Sun Sep 29 12:29:48 2024 +0200 0f519048ce8d0d999ea0a9fc7a1c8953513eccbf,chore(release): publish 1.1.5"
10 0 CHANGELOG.md
1 1 apps/backend/package.json

"Manfred Steyer <[email protected]>,Sun Sep 29 12:29:29 2024 +0200 527ed589150ac81ee5bc251526644cfdc6eeb11e,fix: don't wrap tree entries"
4 0 apps/frontend/src/app/shell/nav/nav.component.css

"Manfred Steyer <[email protected]>,Sun Sep 29 12:12:29 2024 +0200 7a18a2d6fb97ea4d0d2bea59bd99b6b376862772,chore(release): publish 1.1.4"
10 0 CHANGELOG.md
1 1 apps/backend/package.json

"Manfred Steyer <[email protected]>,Sun Sep 29 12:12:15 2024 +0200 be5836b768b8fc6d307496ace2d037cc7063c56d,fix: always show resize cursor during resizing"
2 0 apps/frontend/src/app/ui/resizer/resizer.component.ts
4 0 apps/frontend/src/styles.scss

"Manfred Steyer <[email protected]>,Sun Sep 29 00:19:03 2024 +0200 567e19278e6204fd4b29ca9490043fe1fc8d7431,chore(release): publish 1.1.3"
14 0 CHANGELOG.md
1 1 apps/backend/package.json

"Manfred Steyer <[email protected]>,Sat Sep 28 23:22:37 2024 +0200 7ef551cfee65bf6a037923eb6310ddf72a2a1e52,feat: add resizer for tree"
1 1 .detective/hash
16 0 .detective/log
25 0 apps/frontend/src/app/shell/nav/nav.component.css
16 2 apps/frontend/src/app/shell/nav/nav.component.css
10 6 apps/frontend/src/app/shell/nav/nav.component.html
46 8 apps/frontend/src/app/shell/nav/nav.component.ts
15 8 apps/frontend/src/app/shell/nav/nav.component.ts
10 0 apps/frontend/src/app/ui/resizer/resizer.component.css
1 0 apps/frontend/src/app/ui/resizer/resizer.component.html
22 0 apps/frontend/src/app/ui/resizer/resizer.component.spec.ts
42 0 apps/frontend/src/app/ui/resizer/resizer.component.ts

"Manfred Steyer <[email protected]>,Sat Sep 28 19:32:42 2024 +0200 06c8dfeb5e988f17bcf3699e8d3c1748981c7231,refactor: streamline dataflow for hotspot analysis"
1 1 .detective/hash
Expand Down
7 changes: 6 additions & 1 deletion apps/backend/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
"options": {
"buildTarget": "backend:build",
"runBuildTargetDependencies": false,
"args": ["--path", ".", "--open", "false"]
"args": [
"--path",
"/Users/manfredsteyer/projects/public/standalone-example-cli",
"--open",
"false"
]
},
"configurations": {
"development": {
Expand Down
107 changes: 96 additions & 11 deletions apps/backend/src/services/hotspot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,26 @@ export type HotspotCriteria = {
};

export type AggregatedHotspot = {
parent: string;
module: string;
count: number;
countWarning: number;
countHotspot: number;
countOk: number;
};

export type AggregatedHotspotsResult = {
aggregated: AggregatedHotspot[];
minScore: number;
maxScore: number;
warningBoundary: number;
hotspotBoundary: number;
};

type Stats = {
maxScore: number;
scores: Map<string, number[]>;
minScore: number;
};

export async function findHotspotFiles(
Expand Down Expand Up @@ -80,28 +94,99 @@ export async function aggregateHotspots(
limits: Limits,
options: Options
): Promise<AggregatedHotspotsResult> {
const hotspotResult = await findHotspotFiles(criteria, limits, options);
const phase1Criteria = {
...criteria,
minScore: 0,
};

const hotspotResult = await findHotspotFiles(phase1Criteria, limits, options);
const hotspots = hotspotResult.hotspots;

const config = loadConfig(options);
const modules = config.scopes.map((m) => normalizeFolder(m));

const stats = collectStats(modules, hotspots);

const warningBoundary = stats.maxScore * (criteria.minScore / 100);
const hotspotBoundary =
warningBoundary + (stats.maxScore - warningBoundary) / 2;

const result = aggregateStats(
modules,
stats,
warningBoundary,
hotspotBoundary
);

result.sort((a, b) => b.count - a.count);

return {
aggregated: result,
maxScore: stats.maxScore,
minScore: stats.minScore,
hotspotBoundary,
warningBoundary,
};
}

function aggregateStats(
modules: string[],
stats: Stats,
warningBoundary: number,
hotspotBoundary: number
): AggregatedHotspot[] {
const result: AggregatedHotspot[] = [];
for (const module of modules) {
let count = 0;
for (const hotspot of hotspots) {
if (
hotspot.fileName.startsWith(module)
//&& hotspot.score >= criteria.minScore
) {
count++;
const moduleStats = stats.scores.get(module);

let countWarning = 0;
let countHotspot = 0;
let countOk = 0;

for (const stat of moduleStats) {
if (stat > hotspotBoundary) {
countHotspot++;
} else if (stat < warningBoundary) {
countOk++;
} else {
countWarning++;
}
}
result.push({ module: toDisplayFolder(module), count });

// const countBelow = moduleStats.length - count;

const displayFolder = toDisplayFolder(module);
const parent = path.dirname(displayFolder);

result.push({
parent,
module: displayFolder,
count: countOk,
countOk,
countWarning,
countHotspot,
});
}
return result;
}

result.sort((a, b) => b.count - a.count);
return { aggregated: result };
function collectStats(modules: string[], hotspots: FlatHotspot[]) {
let minScore = Number.MAX_VALUE;
let maxScore = 0;
const scores = new Map<string, number[]>();

for (const module of modules) {
const moduleScores = [];
for (const hotspot of hotspots) {
if (hotspot.fileName.startsWith(module)) {
minScore = Math.min(minScore, hotspot.score);
maxScore = Math.max(maxScore, hotspot.score);
moduleScores.push(hotspot.score);
}
}
scores.set(module, moduleScores);
}
return { maxScore, scores, minScore };
}

async function analyzeLogs(
Expand Down
101 changes: 101 additions & 0 deletions apps/frontend/src/app/features/hotspot/hotspot-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import 'chartjs-chart-treemap';

import { ChartEvent, InteractionItem } from 'chart.js';

import { AggregatedHotspot } from '../../model/hotspot-result';
import { TreeMapChartConfig } from '../../ui/treemap/treemap.component';

export type ScoreType = 'hotspot' | 'warning' | 'fine';
export type AggregatedHotspotWithType = AggregatedHotspot & {
type: ScoreType;
};

export function toTreeMapConfig(
aggregated: AggregatedHotspot[]
): TreeMapChartConfig {
const values = aggregated.flatMap((v) => [
{ ...v, count: v.countHotspot, type: 'hotspot' },
{ ...v, count: v.countWarning, type: 'warning' },
{ ...v, count: v.countOk, type: 'fine' },
]);

const options = {
onHover: (event: ChartEvent, elements: InteractionItem[]) => {
const chartElement = event.native?.target as HTMLCanvasElement;
if (elements.length >= 2) {
chartElement.style.cursor = 'pointer';
} else {
chartElement.style.cursor = 'default';
}
},
plugins: {
title: {
display: true,
text: 'Hotspots',
},
legend: {
display: false,
},
tooltip: {
callbacks: {
title() {
return 'File Count';
},
},
},
},
};

const config: TreeMapChartConfig = {
type: 'treemap',
data: {
datasets: [
{
data: values,
key: 'count',
groups: ['parent', 'module', 'type'],
spacing: 1,
borderWidth: 0.5,
borderColor: '#EFEFEF',
backgroundColor: (ctx) => {
if (typeof ctx.raw?.l !== 'undefined' && ctx.raw?.l < 2) {
return '#EFEFEF';
}
return getScoreTypeColor(ctx.raw?.g as ScoreType);
},
captions: {
align: 'center',
display: true,
color: 'black',
font: {
size: 14,
},
hoverFont: {
size: 16,
weight: 'bold',
},
padding: 5,
},
labels: {
display: false,
overflow: 'hidden',
},
},
],
},
options: options,
};

return config;
}

export function getScoreTypeColor(scoreType: ScoreType) {
switch (scoreType) {
case 'hotspot':
return '#E74C3C';
case 'warning':
return '#F1C40F';
case 'fine':
return '#2ECC71';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.title {
margin-left: 15px;
margin-top: 10px;
}
Loading

0 comments on commit feac40e

Please sign in to comment.