metripy 0.2.7__py3-none-any.whl → 0.3.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of metripy might be problematic. Click here for more details.
- metripy/Application/Analyzer.py +23 -3
- metripy/Application/Application.py +16 -2
- metripy/Application/Config/Config.py +34 -0
- metripy/Application/Config/File/ConfigFileReaderFactory.py +6 -5
- metripy/Application/Config/File/ConfigFileReaderInterface.py +70 -3
- metripy/Application/Config/File/JsonConfigFileReader.py +5 -70
- metripy/Application/Config/File/YamlConfigFileReader.py +17 -0
- metripy/Application/Config/Parser.py +24 -11
- metripy/Application/Config/ProjectConfig.py +64 -0
- metripy/Application/Info.py +61 -0
- metripy/Dependency/Dependency.py +17 -1
- metripy/Dependency/Pip/Pip.py +21 -31
- metripy/Dependency/Pip/PyPi.py +1 -0
- metripy/Git/GitAnalyzer.py +0 -3
- metripy/Import/Json/JsonImporter.py +17 -0
- metripy/LangAnalyzer/AbstractLangAnalyzer.py +4 -3
- metripy/LangAnalyzer/Php/PhpAnalyzer.py +2 -1
- metripy/LangAnalyzer/Python/PythonAnalyzer.py +31 -9
- metripy/LangAnalyzer/Python/PythonHalSteadAnalyzer.py +55 -0
- metripy/LangAnalyzer/Typescript/TypescriptAnalyzer.py +12 -9
- metripy/LangAnalyzer/Typescript/TypescriptAstParser.py +1 -1
- metripy/Metric/Code/AggregatedMetrics.py +12 -5
- metripy/Metric/Code/FileMetrics.py +32 -1
- metripy/Metric/Code/ModuleMetrics.py +5 -5
- metripy/Metric/Code/SegmentedMetrics.py +72 -36
- metripy/Metric/Code/Segmentor.py +44 -0
- metripy/Metric/FileTree/FileTreeParser.py +0 -4
- metripy/Metric/Git/GitMetrics.py +1 -1
- metripy/Metric/ProjectMetrics.py +29 -0
- metripy/Metric/Trend/AggregatedTrendMetric.py +101 -0
- metripy/Metric/Trend/ClassTrendMetric.py +20 -0
- metripy/Metric/Trend/FileTrendMetric.py +46 -0
- metripy/Metric/Trend/FunctionTrendMetric.py +28 -0
- metripy/Metric/Trend/SegmentedTrendMetric.py +29 -0
- metripy/Report/Html/DependencyPageRenderer.py +21 -0
- metripy/Report/Html/FilesPageRenderer.py +28 -0
- metripy/Report/Html/GitAnalysisPageRenderer.py +55 -0
- metripy/Report/Html/IndexPageRenderer.py +47 -0
- metripy/Report/Html/PageRenderer.py +43 -0
- metripy/Report/Html/PageRendererFactory.py +37 -0
- metripy/Report/Html/Reporter.py +78 -137
- metripy/Report/Html/TopOffendersPageRenderer.py +84 -0
- metripy/Report/Html/TrendsPageRenderer.py +137 -0
- metripy/Report/Json/GitJsonReporter.py +3 -0
- metripy/Report/Json/JsonReporter.py +6 -2
- metripy/Report/ReporterFactory.py +6 -3
- metripy/Tree/ClassNode.py +21 -0
- metripy/Tree/FunctionNode.py +66 -1
- metripy/Trend/TrendAnalyzer.py +150 -0
- metripy/templates/html_report/css/styles.css +1386 -0
- metripy/templates/html_report/dependencies.html +411 -0
- metripy/templates/html_report/files.html +1080 -0
- metripy/templates/html_report/git_analysis.html +325 -0
- metripy/templates/html_report/images/logo.svg +31 -0
- metripy/templates/html_report/index.html +374 -0
- metripy/templates/html_report/js/charts.js +313 -0
- metripy/templates/html_report/js/dashboard.js +546 -0
- metripy/templates/html_report/js/git_analysis.js +383 -0
- metripy/templates/html_report/top_offenders.html +267 -0
- metripy/templates/html_report/trends.html +468 -0
- {metripy-0.2.7.dist-info → metripy-0.3.6.dist-info}/METADATA +27 -9
- metripy-0.3.6.dist-info/RECORD +96 -0
- {metripy-0.2.7.dist-info → metripy-0.3.6.dist-info}/licenses/LICENSE +1 -1
- metripy-0.2.7.dist-info/RECORD +0 -66
- {metripy-0.2.7.dist-info → metripy-0.3.6.dist-info}/WHEEL +0 -0
- {metripy-0.2.7.dist-info → metripy-0.3.6.dist-info}/entry_points.txt +0 -0
- {metripy-0.2.7.dist-info → metripy-0.3.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard JavaScript
|
|
3
|
+
* Handles main dashboard functionality, navigation, and data management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class Dashboard {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.data = {};
|
|
9
|
+
this.currentSection = 'overview';
|
|
10
|
+
this.init();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
init() {
|
|
14
|
+
this.setupEventListeners();
|
|
15
|
+
this.loadData();
|
|
16
|
+
this.updateMetrics();
|
|
17
|
+
//this.setupNavigation();
|
|
18
|
+
this.setupResponsiveMenu();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setupEventListeners() {
|
|
22
|
+
// Navigation links
|
|
23
|
+
/*document.querySelectorAll('.nav-link').forEach(link => {
|
|
24
|
+
link.addEventListener('click', (e) => {
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
this.handleNavigation(e.target.closest('.nav-link'));
|
|
27
|
+
});
|
|
28
|
+
});*/
|
|
29
|
+
|
|
30
|
+
// Header action buttons removed
|
|
31
|
+
|
|
32
|
+
// Chart period controls removed - showing all data
|
|
33
|
+
|
|
34
|
+
// Window resize handler
|
|
35
|
+
window.addEventListener('resize', () => {
|
|
36
|
+
this.handleResize();
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setupNavigation() {
|
|
41
|
+
const navItems = document.querySelectorAll('.nav-item');
|
|
42
|
+
|
|
43
|
+
navItems.forEach(item => {
|
|
44
|
+
const link = item.querySelector('.nav-link');
|
|
45
|
+
link.addEventListener('click', (e) => {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
|
|
48
|
+
// Remove active class from all items
|
|
49
|
+
navItems.forEach(navItem => navItem.classList.remove('active'));
|
|
50
|
+
|
|
51
|
+
// Add active class to clicked item
|
|
52
|
+
item.classList.add('active');
|
|
53
|
+
|
|
54
|
+
// Update current section
|
|
55
|
+
const href = link.getAttribute('href');
|
|
56
|
+
this.currentSection = href.replace('#', '');
|
|
57
|
+
|
|
58
|
+
// Smooth scroll or section switching logic can go here
|
|
59
|
+
this.switchSection(this.currentSection);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setupResponsiveMenu() {
|
|
65
|
+
// Mobile menu toggle logic
|
|
66
|
+
const createMenuToggle = () => {
|
|
67
|
+
if (window.innerWidth <= 1024 && !document.querySelector('.menu-toggle')) {
|
|
68
|
+
const toggle = document.createElement('button');
|
|
69
|
+
toggle.className = 'menu-toggle';
|
|
70
|
+
toggle.innerHTML = '<i class="fas fa-bars"></i>';
|
|
71
|
+
toggle.addEventListener('click', () => {
|
|
72
|
+
document.querySelector('.sidebar').classList.toggle('open');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
document.querySelector('.page-header').prepend(toggle);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
createMenuToggle();
|
|
80
|
+
window.addEventListener('resize', createMenuToggle);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
handleNavigation(link) {
|
|
84
|
+
const href = link.getAttribute('href');
|
|
85
|
+
const section = href.replace('#', '');
|
|
86
|
+
|
|
87
|
+
// Update active navigation
|
|
88
|
+
document.querySelectorAll('.nav-item').forEach(item => {
|
|
89
|
+
item.classList.remove('active');
|
|
90
|
+
});
|
|
91
|
+
link.closest('.nav-item').classList.add('active');
|
|
92
|
+
|
|
93
|
+
// Switch to section
|
|
94
|
+
this.switchSection(section);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
switchSection(section) {
|
|
98
|
+
console.log(`Switching to section: ${section}`);
|
|
99
|
+
|
|
100
|
+
// Here you can implement section switching logic
|
|
101
|
+
// For now, we'll just update the page title
|
|
102
|
+
const titles = {
|
|
103
|
+
overview: 'Code Metrics Overview',
|
|
104
|
+
files: 'File Analysis',
|
|
105
|
+
complexity: 'Complexity Analysis',
|
|
106
|
+
maintainability: 'Maintainability Report',
|
|
107
|
+
'git-analysis': 'Git Analysis',
|
|
108
|
+
trends: 'Trend Analysis'
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const titleElement = document.querySelector('.page-header h1');
|
|
112
|
+
if (titleElement && titles[section]) {
|
|
113
|
+
titleElement.textContent = titles[section];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.currentSection = section;
|
|
117
|
+
this.loadSectionData(section);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
loadSectionData(section) {
|
|
121
|
+
// Load section-specific data
|
|
122
|
+
switch (section) {
|
|
123
|
+
case 'overview':
|
|
124
|
+
this.updateMetrics();
|
|
125
|
+
break;
|
|
126
|
+
case 'files':
|
|
127
|
+
this.loadFilesList();
|
|
128
|
+
break;
|
|
129
|
+
case 'complexity':
|
|
130
|
+
this.loadComplexityData();
|
|
131
|
+
break;
|
|
132
|
+
case 'maintainability':
|
|
133
|
+
this.loadMaintainabilityData();
|
|
134
|
+
break;
|
|
135
|
+
case 'git-analysis':
|
|
136
|
+
this.loadGitAnalysis();
|
|
137
|
+
break;
|
|
138
|
+
case 'trends':
|
|
139
|
+
this.loadTrendsData();
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async loadData() {
|
|
145
|
+
try {
|
|
146
|
+
// Load git statistics from embedded data
|
|
147
|
+
await this.loadGitStatistics();
|
|
148
|
+
|
|
149
|
+
// Load metrics data from embedded template data
|
|
150
|
+
if (window.METRICS_DATA) {
|
|
151
|
+
this.data = {
|
|
152
|
+
...this.data,
|
|
153
|
+
...window.METRICS_DATA
|
|
154
|
+
};
|
|
155
|
+
console.log('Loaded metrics data from template');
|
|
156
|
+
|
|
157
|
+
// Update the UI with the loaded data
|
|
158
|
+
this.updateMetrics();
|
|
159
|
+
} else {
|
|
160
|
+
console.warn('METRICS_DATA not found in template, using fallback');
|
|
161
|
+
await this.simulateDataLoading(); // Fallback for development
|
|
162
|
+
this.updateMetrics();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.error('Error loading data:', error);
|
|
167
|
+
this.showError('Failed to load dashboard data');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async loadGitStatistics() {
|
|
172
|
+
try {
|
|
173
|
+
// Check if git stats data is embedded in the page
|
|
174
|
+
if (window.GIT_STATS_DATA) {
|
|
175
|
+
console.log('Loading git statistics from embedded data');
|
|
176
|
+
this.data.gitCommits = this.formatGitCommitsData(window.GIT_STATS_DATA);
|
|
177
|
+
} else {
|
|
178
|
+
throw new Error('No git statistics data found. Please ensure git_stats.json data is embedded in the HTML template.');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.error('Failed to load git statistics:', error);
|
|
183
|
+
this.showError('Failed to load git statistics: ' + error.message);
|
|
184
|
+
// Set empty data instead of fallback
|
|
185
|
+
this.data.gitCommits = [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
formatGitCommitsData(gitData) {
|
|
190
|
+
// Convert git statistics data to chart format
|
|
191
|
+
// Expected format: { "2024-01": 42, "2024-02": 38, ... }
|
|
192
|
+
if (gitData && typeof gitData === 'object' && !Array.isArray(gitData)) {
|
|
193
|
+
return Object.entries(gitData).map(([date, count]) => ({
|
|
194
|
+
month: this.formatMonthName(date),
|
|
195
|
+
commits: count,
|
|
196
|
+
date: new Date(date + '-01').toISOString()
|
|
197
|
+
})).sort((a, b) => new Date(a.date) - new Date(b.date));
|
|
198
|
+
} else {
|
|
199
|
+
// No valid data format
|
|
200
|
+
console.error('Git data format not recognized');
|
|
201
|
+
throw new Error('Invalid git statistics data format. Expected: {"2024-01": 42, "2024-02": 38, ...}');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
formatMonthName(monthString) {
|
|
206
|
+
try {
|
|
207
|
+
// Handle different date formats
|
|
208
|
+
let date;
|
|
209
|
+
if (monthString.includes('-')) {
|
|
210
|
+
// Format: "2024-10" or "2024-10-01"
|
|
211
|
+
date = new Date(monthString + (monthString.split('-').length === 2 ? '-01' : ''));
|
|
212
|
+
} else {
|
|
213
|
+
date = new Date(monthString);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return date.toLocaleDateString('en-US', {
|
|
217
|
+
month: 'short',
|
|
218
|
+
year: 'numeric'
|
|
219
|
+
});
|
|
220
|
+
} catch (error) {
|
|
221
|
+
return monthString; // Return original if parsing fails
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
simulateDataLoading() {
|
|
226
|
+
return new Promise(resolve => {
|
|
227
|
+
setTimeout(() => {
|
|
228
|
+
// Preserve gitCommits data if already loaded from JSON
|
|
229
|
+
const existingGitCommits = this.data.gitCommits;
|
|
230
|
+
|
|
231
|
+
// Load data from external metrics file if available
|
|
232
|
+
if (window.METRICS_DATA) {
|
|
233
|
+
this.data = {
|
|
234
|
+
...this.data, // Preserve existing data
|
|
235
|
+
...window.METRICS_DATA, // Load metrics from external file
|
|
236
|
+
gitCommits: existingGitCommits || [] // Preserve git data
|
|
237
|
+
};
|
|
238
|
+
} else {
|
|
239
|
+
// Fallback to sample data if external data not available
|
|
240
|
+
console.warn('METRICS_DATA not found, using fallback sample data');
|
|
241
|
+
this.data = {
|
|
242
|
+
...this.data,
|
|
243
|
+
totalLoc: 15420,
|
|
244
|
+
avgComplexity: 2.8,
|
|
245
|
+
maintainabilityIndex: 78.5,
|
|
246
|
+
avgMethodSize: 12.3,
|
|
247
|
+
gitCommits: existingGitCommits || [],
|
|
248
|
+
segmentation: {
|
|
249
|
+
loc: { good: 45, ok: 32, warning: 18, critical: 5 },
|
|
250
|
+
complexity: { good: 52, ok: 28, warning: 15, critical: 5 },
|
|
251
|
+
maintainability: { good: 35, ok: 42, warning: 18, critical: 5 },
|
|
252
|
+
methodSize: { good: 48, ok: 35, warning: 12, critical: 5 }
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
resolve();
|
|
257
|
+
}, 500);
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
updateMetrics() {
|
|
263
|
+
if (!this.data.totalLoc) return;
|
|
264
|
+
|
|
265
|
+
// Update metric values with animation
|
|
266
|
+
this.animateValue('total-loc', this.data.totalLoc);
|
|
267
|
+
this.animateValue('avg-complexity', this.data.avgComplexity, 1);
|
|
268
|
+
this.animateValue('maintainability-index', this.data.maintainabilityIndex, 1);
|
|
269
|
+
this.animateValue('avg-method-size', this.data.avgMethodSize, 1);
|
|
270
|
+
|
|
271
|
+
// Complex files and recent analysis sections removed
|
|
272
|
+
|
|
273
|
+
// Update segmentations
|
|
274
|
+
this.updateSegmentations();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
animateValue(elementId, targetValue, decimals = 0) {
|
|
278
|
+
const element = document.getElementById(elementId);
|
|
279
|
+
if (!element) return;
|
|
280
|
+
|
|
281
|
+
const startValue = 0;
|
|
282
|
+
const duration = 2000;
|
|
283
|
+
const startTime = performance.now();
|
|
284
|
+
|
|
285
|
+
const animate = (currentTime) => {
|
|
286
|
+
const elapsed = currentTime - startTime;
|
|
287
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
288
|
+
|
|
289
|
+
// Easing function
|
|
290
|
+
const easeOutQuart = 1 - Math.pow(1 - progress, 4);
|
|
291
|
+
|
|
292
|
+
const currentValue = startValue + (targetValue - startValue) * easeOutQuart;
|
|
293
|
+
element.textContent = currentValue.toFixed(decimals);
|
|
294
|
+
|
|
295
|
+
if (progress < 1) {
|
|
296
|
+
requestAnimationFrame(animate);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
requestAnimationFrame(animate);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
updateSegmentations() {
|
|
305
|
+
if (!this.data.segmentation) {
|
|
306
|
+
console.warn('No segmentation data found');
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Update LOC segmentation
|
|
311
|
+
this.updateSegmentation('loc', this.data.segmentation.loc);
|
|
312
|
+
|
|
313
|
+
// Update Complexity segmentation
|
|
314
|
+
this.updateSegmentation('complexity', this.data.segmentation.complexity);
|
|
315
|
+
|
|
316
|
+
// Update Maintainability segmentation
|
|
317
|
+
this.updateSegmentation('maintainability', this.data.segmentation.maintainability);
|
|
318
|
+
|
|
319
|
+
// Update Method Size segmentation
|
|
320
|
+
this.updateSegmentation('methodSize', this.data.segmentation.methodSize);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
updateSegmentation(metricType, segmentData) {
|
|
324
|
+
// Calculate total files to determine percentages
|
|
325
|
+
const total = Object.values(segmentData).reduce((sum, count) => sum + count, 0);
|
|
326
|
+
|
|
327
|
+
if (total === 0) return; // No data to display
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
// Get display mapping for this metric type
|
|
331
|
+
const displayMapping = this.getDisplayMapping(metricType);
|
|
332
|
+
const segmentOrder = this.getSegmentOrder(metricType);
|
|
333
|
+
|
|
334
|
+
// Calculate all percentages first to ensure they add up to 100%
|
|
335
|
+
let percentages = [];
|
|
336
|
+
let totalPercentage = 0;
|
|
337
|
+
|
|
338
|
+
segmentOrder.forEach(dataCategory => {
|
|
339
|
+
const count = segmentData[dataCategory] || 0;
|
|
340
|
+
const percentage = (count / total) * 100;
|
|
341
|
+
percentages.push(percentage);
|
|
342
|
+
totalPercentage += percentage;
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Adjust for any rounding errors to ensure total is exactly 100%
|
|
346
|
+
if (totalPercentage > 0 && totalPercentage !== 100) {
|
|
347
|
+
const adjustment = 100 / totalPercentage;
|
|
348
|
+
percentages = percentages.map(p => p * adjustment);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
segmentOrder.forEach((dataCategory, index) => {
|
|
352
|
+
const count = segmentData[dataCategory] || 0;
|
|
353
|
+
const percentage = percentages[index];
|
|
354
|
+
|
|
355
|
+
// Get the display name for DOM queries
|
|
356
|
+
const displayName = displayMapping[dataCategory] || dataCategory;
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
// Update segment bar width
|
|
360
|
+
const segmentElement = document.querySelector(`[data-segment="${displayName}"]`);
|
|
361
|
+
|
|
362
|
+
if (segmentElement) {
|
|
363
|
+
if (count === 0) {
|
|
364
|
+
// Hide segments with no data
|
|
365
|
+
segmentElement.style.width = '0%';
|
|
366
|
+
segmentElement.style.minWidth = '0px';
|
|
367
|
+
segmentElement.removeAttribute('title');
|
|
368
|
+
} else {
|
|
369
|
+
segmentElement.style.width = `${percentage.toFixed(2)}%`;
|
|
370
|
+
// Only apply min-width for very small but non-zero segments
|
|
371
|
+
segmentElement.style.minWidth = percentage < 0.5 ? '1px' : '0px';
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Update count display and add tooltip to the label
|
|
376
|
+
const countElement = document.querySelector(`[data-count="${displayName}"]`);
|
|
377
|
+
if (countElement) {
|
|
378
|
+
countElement.textContent = count;
|
|
379
|
+
|
|
380
|
+
// Add tooltip to the parent segment-label for better hover area
|
|
381
|
+
const segmentLabel = countElement.closest('.segment-label');
|
|
382
|
+
if (segmentLabel) {
|
|
383
|
+
const tooltipText = this.getTooltipText(metricType, dataCategory, count);
|
|
384
|
+
if (tooltipText) {
|
|
385
|
+
segmentLabel.setAttribute('title', tooltipText);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
getSegmentOrder(metricType) {
|
|
393
|
+
// Use consistent naming across all metrics: good, ok, warning, critical
|
|
394
|
+
return ['good', 'ok', 'warning', 'critical'];
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Map data categories to display names for each metric type
|
|
398
|
+
getDisplayMapping(metricType) {
|
|
399
|
+
const displayMap = {
|
|
400
|
+
loc: {
|
|
401
|
+
'good': 'small',
|
|
402
|
+
'ok': 'medium',
|
|
403
|
+
'warning': 'large',
|
|
404
|
+
'critical': 'very-large'
|
|
405
|
+
},
|
|
406
|
+
complexity: {
|
|
407
|
+
'good': 'simple',
|
|
408
|
+
'ok': 'moderate',
|
|
409
|
+
'warning': 'complex',
|
|
410
|
+
'critical': 'very-complex'
|
|
411
|
+
},
|
|
412
|
+
maintainability: {
|
|
413
|
+
'good': 'excellent',
|
|
414
|
+
'ok': 'good',
|
|
415
|
+
'warning': 'fair',
|
|
416
|
+
'critical': 'poor'
|
|
417
|
+
},
|
|
418
|
+
methodSize: {
|
|
419
|
+
'good': 'concise',
|
|
420
|
+
'ok': 'optimal',
|
|
421
|
+
'warning': 'large',
|
|
422
|
+
'critical': 'too-large'
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
return displayMap[metricType] || {};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
getTooltipText(metricType, dataCategory, count) {
|
|
429
|
+
const tooltipMap = {
|
|
430
|
+
loc: {
|
|
431
|
+
'good': `${count} files with 1-200 lines of code (easy to understand and maintain)`,
|
|
432
|
+
'ok': `${count} files with 201-500 lines of code (reasonable size, manageable)`,
|
|
433
|
+
'warning': `${count} files with 501-1000 lines of code (consider splitting into modules)`,
|
|
434
|
+
'critical': `${count} files with 1000+ lines of code (urgent refactoring needed)`
|
|
435
|
+
},
|
|
436
|
+
complexity: {
|
|
437
|
+
'good': `${count} files with 1-5 average cyclomatic complexity (easy to test and maintain)`,
|
|
438
|
+
'ok': `${count} files with 6-10 average cyclomatic complexity (acceptable complexity)`,
|
|
439
|
+
'warning': `${count} files with 11-20 average cyclomatic complexity (should consider refactoring)`,
|
|
440
|
+
'critical': `${count} files with 21+ average cyclomatic complexity (high risk, urgent attention needed)`
|
|
441
|
+
},
|
|
442
|
+
maintainability: {
|
|
443
|
+
'good': `${count} files with 80-100 maintainability score (highly maintainable code)`,
|
|
444
|
+
'ok': `${count} files with 60-79 maintainability score (well-maintained, minor improvements)`,
|
|
445
|
+
'warning': `${count} files with 40-59 maintainability score (needs attention and cleanup)`,
|
|
446
|
+
'critical': `${count} files with 0-39 maintainability score (critical refactoring required)`
|
|
447
|
+
},
|
|
448
|
+
methodSize: {
|
|
449
|
+
'good': `${count} files with 1-15 average lines per method (well-focused methods)`,
|
|
450
|
+
'ok': `${count} files with 16-30 average lines per method (good balance of functionality)`,
|
|
451
|
+
'warning': `${count} files with 31-50 average lines per method (consider breaking down)`,
|
|
452
|
+
'critical': `${count} files with 51+ average lines per method (should be split immediately)`
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
return tooltipMap[metricType]?.[dataCategory] || '';
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
loadComplexityData() {
|
|
461
|
+
console.log('Loading complexity data...');
|
|
462
|
+
// Implement complexity data loading
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
loadMaintainabilityData() {
|
|
466
|
+
console.log('Loading maintainability data...');
|
|
467
|
+
// Implement maintainability data loading
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
loadGitAnalysis() {
|
|
471
|
+
console.log('Loading git analysis...');
|
|
472
|
+
// Implement git analysis loading
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
loadTrendsData() {
|
|
476
|
+
console.log('Loading trends data...');
|
|
477
|
+
// Implement trends data loading
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
handleResize() {
|
|
481
|
+
// Handle responsive behavior
|
|
482
|
+
if (window.innerWidth > 1024) {
|
|
483
|
+
document.querySelector('.sidebar')?.classList.remove('open');
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
showSuccess(message) {
|
|
488
|
+
this.showNotification(message, 'success');
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
showError(message) {
|
|
492
|
+
this.showNotification(message, 'error');
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
showNotification(message, type = 'info') {
|
|
496
|
+
// Create notification element
|
|
497
|
+
const notification = document.createElement('div');
|
|
498
|
+
notification.className = `notification notification-${type}`;
|
|
499
|
+
notification.style.cssText = `
|
|
500
|
+
position: fixed;
|
|
501
|
+
top: 20px;
|
|
502
|
+
right: 20px;
|
|
503
|
+
padding: 1rem 1.5rem;
|
|
504
|
+
border-radius: 0.5rem;
|
|
505
|
+
color: white;
|
|
506
|
+
font-weight: 500;
|
|
507
|
+
z-index: 1000;
|
|
508
|
+
transform: translateX(100%);
|
|
509
|
+
transition: transform 0.3s ease;
|
|
510
|
+
`;
|
|
511
|
+
|
|
512
|
+
// Set background color based on type
|
|
513
|
+
const colors = {
|
|
514
|
+
success: '#10b981',
|
|
515
|
+
error: '#ef4444',
|
|
516
|
+
warning: '#f59e0b',
|
|
517
|
+
info: '#3b82f6'
|
|
518
|
+
};
|
|
519
|
+
notification.style.backgroundColor = colors[type] || colors.info;
|
|
520
|
+
notification.textContent = message;
|
|
521
|
+
|
|
522
|
+
// Add to DOM
|
|
523
|
+
document.body.appendChild(notification);
|
|
524
|
+
|
|
525
|
+
// Animate in
|
|
526
|
+
setTimeout(() => {
|
|
527
|
+
notification.style.transform = 'translateX(0)';
|
|
528
|
+
}, 100);
|
|
529
|
+
|
|
530
|
+
// Remove after delay
|
|
531
|
+
setTimeout(() => {
|
|
532
|
+
notification.style.transform = 'translateX(100%)';
|
|
533
|
+
setTimeout(() => {
|
|
534
|
+
document.body.removeChild(notification);
|
|
535
|
+
}, 300);
|
|
536
|
+
}, 3000);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Initialize dashboard when DOM is loaded
|
|
541
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
542
|
+
window.dashboard = new Dashboard();
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// Export for global access
|
|
546
|
+
window.Dashboard = Dashboard;
|