Skip to content

Commit d84ed93

Browse files
committed
Update class popups for updated metrics format from code service
1 parent 28c26ac commit d84ed93

File tree

2 files changed

+92
-156
lines changed

2 files changed

+92
-156
lines changed
Lines changed: 89 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
import React from 'react';
2-
31
import { useApplicationRepositoryStore } from 'explorviz-frontend/src/stores/repos/application-repository';
42
import { useEvolutionDataRepositoryStore } from 'explorviz-frontend/src/stores/repos/evolution-data-repository';
5-
import { useCommitTreeStateStore } from 'explorviz-frontend/src/stores/commit-tree-state';
63
import {
74
ApplicationMetricsCode,
85
ClassMetricCode,
@@ -11,6 +8,7 @@ import PopupData from 'explorviz-frontend/src/components/visualization/rendering
118
import { Class } from 'explorviz-frontend/src/utils/landscape-schemes/structure-data';
129
import { calculateFqn } from 'explorviz-frontend/src/utils/landscape-structure-helpers';
1310
import HelpTooltip from 'explorviz-frontend/src/components/help-tooltip.tsx';
11+
import { useCommitTreeStateStore } from 'explorviz-frontend/src/stores/commit-tree-state';
1412

1513
interface ClazzPopupCodeProps {
1614
popupData: PopupData;
@@ -22,12 +20,11 @@ export default function ClazzPopupCode({ popupData }: ClazzPopupCodeProps) {
2220
useEvolutionDataRepositoryStore(
2321
(state) => state._appNameToCommitIdToApplicationMetricsCodeMap
2422
);
23+
2524
const getSelectedCommits = useCommitTreeStateStore(
2625
(state) => state.getSelectedCommits
2726
);
2827

29-
const fileName = (popupData.entity as Class).name;
30-
3128
const appName: string | undefined = (() => {
3229
const { applicationId: appId } = popupData;
3330

@@ -60,162 +57,104 @@ export default function ClazzPopupCode({ popupData }: ClazzPopupCodeProps) {
6057
return selectedCommitToApplicationMetricsCodeMap;
6158
})();
6259

63-
const getNumOfCurrentSelectedCommits = (() => {
64-
if (!appName) {
65-
return [];
66-
}
67-
68-
const selectedCommits = getSelectedCommits().get(appName) ?? [];
69-
70-
return selectedCommits.length;
71-
})();
72-
73-
const classnameToCommitAndClassMetricsArray = (() => {
74-
const classnameToCommitAndClassMetricsArray: Map<
75-
string,
76-
{ commitId: string; classMetric: ClassMetricCode }[]
77-
> = new Map();
60+
const commitIdToClassMetrics = (() => {
61+
const commitIdToClassMetric: Map<string, ClassMetricCode> = new Map();
7862

79-
const commitIdToClassMetricsMap: Map<
80-
string,
81-
Map<string, ClassMetricCode>
82-
> = new Map();
83-
84-
const fqnForClass = calculateFqn(popupData.entity as Class, '/');
63+
const fqnForClass = calculateFqn(popupData.entity as Class, '.');
8564

8665
for (const [commitId, appMetricsCode] of commitToAppMetricsCodeMap) {
87-
const indexOfFileName = appMetricsCode.files.findIndex((file) =>
88-
file.includes(fqnForClass)
89-
);
90-
91-
if (indexOfFileName === -1) {
92-
//return commitIdToClassMetricsMap;
66+
const classMetric = appMetricsCode.classMetrics[fqnForClass];
67+
if (!classMetric) {
9368
continue;
9469
}
95-
96-
const classMetric = appMetricsCode.classMetrics[indexOfFileName];
97-
98-
for (const [classFqn, metricsData] of Object.entries(classMetric)) {
99-
const fqnDelimited = classFqn.split('.');
100-
const simpleClassName = fqnDelimited[fqnDelimited.length - 1];
101-
102-
if (classnameToCommitAndClassMetricsArray.has(simpleClassName)) {
103-
classnameToCommitAndClassMetricsArray
104-
.get(simpleClassName)
105-
?.push({ commitId, classMetric: metricsData });
106-
} else {
107-
classnameToCommitAndClassMetricsArray.set(simpleClassName, [
108-
{ commitId, classMetric: metricsData },
109-
]);
110-
}
111-
112-
commitIdToClassMetricsMap.set(
113-
commitId,
114-
new Map([[simpleClassName, metricsData]])
115-
);
116-
}
70+
commitIdToClassMetric.set(commitId, classMetric);
11771
}
11872

119-
return classnameToCommitAndClassMetricsArray;
73+
return commitIdToClassMetric;
12074
})();
12175

122-
const keyValuePairs = Array.from(
123-
classnameToCommitAndClassMetricsArray.entries()
76+
const keyValuePairs = Array.from(commitIdToClassMetrics.entries());
77+
78+
// Extract all unique metric keys to use as row headers
79+
const allMetricKeys = Array.from(
80+
new Set(
81+
keyValuePairs.flatMap(([, classMetric]) => Object.keys(classMetric))
82+
)
12483
);
12584

85+
if (keyValuePairs.length === 0) {
86+
return (
87+
<div className="card mb-4">
88+
<div className="card-body">
89+
<p>No class metrics found for this entity across commits.</p>
90+
</div>
91+
</div>
92+
);
93+
}
94+
95+
const isFirstCommitId = (commitId: string) => {
96+
if (!appName) {
97+
return false;
98+
}
99+
const firstCommit = getSelectedCommits().get(appName);
100+
if (!firstCommit || firstCommit.length === 0) {
101+
return false;
102+
}
103+
return firstCommit[0].commitId === commitId;
104+
};
105+
106+
const getMetricDiff = (metricKey: string) => {
107+
if (keyValuePairs.length < 2) {
108+
return '';
109+
}
110+
const firstValue = keyValuePairs[0][1][metricKey as keyof ClassMetricCode];
111+
const secondValue = keyValuePairs[1][1][metricKey as keyof ClassMetricCode];
112+
if (firstValue === undefined || secondValue === undefined) {
113+
return '';
114+
}
115+
const diff = Number.parseInt(secondValue) - Number.parseInt(firstValue);
116+
return diff !== 0 ? ` (${diff > 0 ? '+' : ''}${diff})` : '';
117+
};
118+
126119
return (
127-
<>
128-
{keyValuePairs.map(([classname, commitAndClassMetricsArray]) => {
129-
return (
130-
<div className="card mb-4" key={classname}>
131-
<div className="card-header">
132-
<strong>Class Name:</strong>
133-
{classname}
134-
</div>
135-
<div className="card-body">
136-
<table className="table table-sm">
137-
<thead>
138-
<tr>
139-
<th>Commit:</th>
140-
{commitAndClassMetricsArray.map(
141-
(commitAndClassMetricsObject, index) => {
142-
return (
143-
<th className="text-left" key={index}>
144-
{shortCommitIdentifierForTable(index)}
145-
<HelpTooltip
146-
title={commitAndClassMetricsObject.commitId}
147-
/>
148-
</th>
149-
);
150-
}
151-
)}
152-
</tr>
153-
</thead>
154-
<tbody>
155-
<tr>
156-
<th>LOC:</th>
157-
{commitAndClassMetricsArray.map(
158-
(commitAndClassMetricsObject) => {
159-
return (
160-
<th className="text-left" key={commitAndClassMetricsObject.commitId}>
161-
{commitAndClassMetricsObject.classMetric.loc}
162-
</th>
163-
);
164-
}
165-
)}
166-
</tr>
167-
<tr>
168-
<th>LCOM4:</th>
169-
{commitAndClassMetricsArray.map(
170-
(commitAndClassMetricsObject) => {
171-
return (
172-
<th className="text-left" key={commitAndClassMetricsObject.commitId}>
173-
{commitAndClassMetricsObject.classMetric.LCOM4}
174-
</th>
175-
);
176-
}
177-
)}
178-
</tr>
179-
<tr>
180-
<th>Cyclomatic complexity:</th>
181-
{commitAndClassMetricsArray.map(
182-
(commitAndClassMetricsObject) => {
183-
return (
184-
<th className="text-left" key={commitAndClassMetricsObject.commitId}>
185-
{
186-
commitAndClassMetricsObject.classMetric
187-
.cyclomatic_complexity
188-
}
189-
</th>
190-
);
191-
}
192-
)}
193-
</tr>
194-
<tr>
195-
<th>Cyclomatic complexity (weighted):</th>
196-
{commitAndClassMetricsArray.map(
197-
(commitAndClassMetricsObject) => {
198-
return (
199-
<th className="text-left" key={commitAndClassMetricsObject.commitId}>
200-
{
201-
commitAndClassMetricsObject.classMetric
202-
.cyclomatic_complexity_weighted
203-
}
204-
</th>
205-
);
206-
}
207-
)}
208-
</tr>
209-
</tbody>
210-
</table>
211-
</div>
212-
</div>
213-
);
214-
})}
215-
</>
120+
<div className="card mb-4">
121+
<div className="card-body">
122+
<table className="table table-sm">
123+
<thead>
124+
<tr>
125+
{/* First header cell for the metric names */}
126+
<th>Metric</th>
127+
{/* Headers for each commit */}
128+
{keyValuePairs.map(([commitId, _]) => (
129+
<th className="text-left" key={commitId + '-tooltip'}>
130+
{'C-' + (isFirstCommitId(commitId) ? '1' : '2')}
131+
<HelpTooltip title={commitId} />
132+
</th>
133+
))}
134+
</tr>
135+
</thead>
136+
<tbody>
137+
{/* Rows for each metric */}
138+
{allMetricKeys.map((metricKey) => (
139+
<tr key={metricKey}>
140+
<th>{metricKey}:</th>
141+
{/* Data cells for each commit's metric value */}
142+
{keyValuePairs.map(([commitId, classMetric], index) => (
143+
<td key={commitId + '-' + metricKey} className="text-left">
144+
{/* Check if the metric exists for this commit, otherwise display N/A*/}
145+
{classMetric[metricKey as keyof ClassMetricCode] !==
146+
undefined
147+
? String(
148+
classMetric[metricKey as keyof ClassMetricCode]
149+
) + (index == 1 ? getMetricDiff(metricKey) : '')
150+
: 'N/A'}
151+
</td>
152+
))}
153+
</tr>
154+
))}
155+
</tbody>
156+
</table>
157+
</div>
158+
</div>
216159
);
217160
}
218-
219-
function shortCommitIdentifierForTable(tableIndex: number) {
220-
return `C-${tableIndex + 1}`;
221-
}

src/utils/metric-schemes/metric-data.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,13 @@ export type FileMetricCode = {
3333
};
3434

3535
export type ApplicationMetricsCode = {
36-
// fileMetrics, classMetrics and methodMetrics should have
37-
// the size of the files list to provide a mapping logic
38-
files: string[];
39-
fileMetrics: FileMetricCode[];
36+
fileMetrics: { [filePath: string]: FileMetricCode };
4037
classMetrics: {
4138
[fullQualifiedClassName: string]: ClassMetricCode;
42-
}[];
39+
};
4340
methodMetrics: {
4441
[fullQualifiedClassName: string]: MethodMetricCode;
45-
}[];
42+
};
4643
};
4744

4845
export type CommitComparisonMetric = {

0 commit comments

Comments
 (0)