0
# History and Models
1
2
Data models for tracking code quality metrics over time and maintaining historical analysis data. These models provide the foundation for trend analysis and longitudinal code quality reporting.
3
4
## Capabilities
5
6
### Base History Model
7
8
Foundation class for tracking analysis data over time with basic operations for adding and retrieving historical entries.
9
10
```javascript { .api }
11
/**
12
* Base history tracking model for analysis data over time
13
* @param {Array} data - Initial history data (optional)
14
*/
15
class History {
16
constructor(data);
17
18
/** Number of history entries */
19
length: number;
20
21
/**
22
* Add entry to history
23
* @param {Object} obj - Object to add to history
24
*/
25
push(obj): void;
26
27
/**
28
* Convert history to JSON array
29
* @returns {Array} JSON representation of history
30
*/
31
toJSON(): Array;
32
}
33
```
34
35
**Constructor Parameters:**
36
37
- `data` (Array, optional): Initial history data to populate the instance
38
39
**Properties:**
40
41
- `length` (Number): Number of entries currently stored in history
42
43
**Methods:**
44
45
- `push(obj)`: Add a new entry to the history
46
- `toJSON()`: Convert the entire history to a JSON-serializable array
47
48
**Usage Examples:**
49
50
```javascript
51
const History = require('plato/lib/models/History');
52
53
// Create new history
54
const history = new History();
55
56
// Add entries
57
history.push({ date: '2024-01-01', complexity: 5.2 });
58
history.push({ date: '2024-01-02', complexity: 4.8 });
59
60
console.log(history.length); // 2
61
62
// Convert to JSON
63
const jsonHistory = history.toJSON();
64
console.log(jsonHistory); // [{ date: '2024-01-01', complexity: 5.2 }, ...]
65
66
// Initialize with existing data
67
const existingData = [
68
{ date: '2024-01-01', value: 100 },
69
{ date: '2024-01-02', value: 95 }
70
];
71
const populatedHistory = new History(existingData);
72
console.log(populatedHistory.length); // 2
73
```
74
75
### Overview History Model
76
77
Specialized history tracking for project-wide overview reports with aggregated metrics across all analyzed files.
78
79
```javascript { .api }
80
/**
81
* History tracking for overview reports (extends History)
82
* @param {Array} data - Initial history data (optional)
83
*/
84
class OverviewHistory extends History {
85
constructor(data);
86
87
/**
88
* Add overview report to history with timestamp
89
* @param {Object} report - Overview report object
90
* @param {String} date - Report date (optional, defaults to current date)
91
*/
92
addReport(report, date): void;
93
}
94
```
95
96
**Constructor Parameters:**
97
98
- `data` (Array, optional): Initial overview history data
99
100
**Methods:**
101
102
- `addReport(report, date)`: Add an overview report with automatic timestamping
103
104
**Usage Examples:**
105
106
```javascript
107
const OverviewHistory = require('plato/lib/models/OverviewHistory');
108
109
// Create overview history
110
const overviewHistory = new OverviewHistory();
111
112
// Add overview reports
113
const overviewReport = {
114
summary: {
115
total: { sloc: 1000, maintainability: 85.5 },
116
average: { sloc: 50, maintainability: 85.5 }
117
},
118
reports: []
119
};
120
121
overviewHistory.addReport(overviewReport, '2024-01-01');
122
overviewHistory.addReport(overviewReport, '2024-01-02');
123
124
// Track trends over time
125
const history = overviewHistory.toJSON();
126
history.forEach(entry => {
127
console.log(`${entry.date}: ${entry.summary.average.maintainability} maintainability`);
128
});
129
130
// Initialize with existing overview data
131
const existingOverviews = [
132
{ date: '2024-01-01', summary: { average: { maintainability: 85 } } }
133
];
134
const preloadedHistory = new OverviewHistory(existingOverviews);
135
```
136
137
### File History Model
138
139
Specialized history tracking for individual file analysis reports with file-specific metrics over time.
140
141
```javascript { .api }
142
/**
143
* History tracking for individual file reports (extends History)
144
* @param {Array} data - Initial history data (optional)
145
*/
146
class FileHistory extends History {
147
constructor(data);
148
149
/**
150
* Add file analysis report to history with timestamp
151
* @param {Object} report - File analysis report object
152
* @param {String} date - Report date (optional, defaults to current date)
153
*/
154
addReport(report, date): void;
155
}
156
```
157
158
**Constructor Parameters:**
159
160
- `data` (Array, optional): Initial file history data
161
162
**Methods:**
163
164
- `addReport(report, date)`: Add a file analysis report with automatic timestamping
165
166
**Usage Examples:**
167
168
```javascript
169
const FileHistory = require('plato/lib/models/FileHistory');
170
171
// Create file history
172
const fileHistory = new FileHistory();
173
174
// Add file reports
175
const fileReport = {
176
info: { file: 'src/app.js' },
177
complexity: {
178
cyclomatic: 8,
179
maintainability: 82.5,
180
sloc: { physical: 150, logical: 120 }
181
}
182
};
183
184
fileHistory.addReport(fileReport, '2024-01-01');
185
fileHistory.addReport(fileReport, '2024-01-02');
186
187
// Analyze file trends
188
const fileHistoryData = fileHistory.toJSON();
189
fileHistoryData.forEach(entry => {
190
console.log(`${entry.date}: Complexity ${entry.complexity.cyclomatic}`);
191
});
192
193
// Load existing file history
194
const existingFileData = [
195
{ date: '2024-01-01', complexity: { cyclomatic: 5 } }
196
];
197
const existingFileHistory = new FileHistory(existingFileData);
198
```
199
200
## Integration with Analysis Workflow
201
202
These models integrate with Plato's analysis workflow to provide historical tracking:
203
204
### Overview Tracking
205
206
```javascript
207
const plato = require('plato');
208
const OverviewHistory = require('plato/lib/models/OverviewHistory');
209
210
// Load existing overview history
211
const overviewHistory = new OverviewHistory();
212
213
// Perform analysis
214
plato.inspect(['src/**/*.js'], 'reports', {}, function(reports) {
215
// Generate overview
216
const overview = plato.getOverviewReport(reports);
217
218
// Add to history
219
const today = new Date().toISOString().split('T')[0];
220
overviewHistory.addReport(overview, today);
221
222
// Save history for future runs
223
const historyData = overviewHistory.toJSON();
224
require('fs').writeFileSync('overview-history.json', JSON.stringify(historyData));
225
});
226
```
227
228
### File-Level Tracking
229
230
```javascript
231
const plato = require('plato');
232
const FileHistory = require('plato/lib/models/FileHistory');
233
234
// Track individual files over time
235
const fileHistories = new Map();
236
237
plato.inspect(['src/**/*.js'], 'reports', {}, function(reports) {
238
const today = new Date().toISOString().split('T')[0];
239
240
reports.forEach(report => {
241
const fileName = report.info.file;
242
243
// Get or create history for this file
244
if (!fileHistories.has(fileName)) {
245
fileHistories.set(fileName, new FileHistory());
246
}
247
248
const fileHistory = fileHistories.get(fileName);
249
fileHistory.addReport(report, today);
250
});
251
252
// Identify files with increasing complexity
253
fileHistories.forEach((history, fileName) => {
254
const entries = history.toJSON();
255
if (entries.length >= 2) {
256
const latest = entries[entries.length - 1];
257
const previous = entries[entries.length - 2];
258
259
if (latest.complexity.cyclomatic > previous.complexity.cyclomatic) {
260
console.log(`${fileName}: Complexity increased from ${previous.complexity.cyclomatic} to ${latest.complexity.cyclomatic}`);
261
}
262
}
263
});
264
});
265
```
266
267
## Data Persistence
268
269
The models are designed to work with persistent storage for long-term trend analysis:
270
271
### JSON Serialization
272
273
```javascript
274
const OverviewHistory = require('plato/lib/models/OverviewHistory');
275
const fs = require('fs');
276
277
// Save history
278
function saveHistory(history, filename) {
279
const data = history.toJSON();
280
fs.writeFileSync(filename, JSON.stringify(data, null, 2));
281
}
282
283
// Load history
284
function loadHistory(filename, HistoryClass) {
285
if (fs.existsSync(filename)) {
286
const data = JSON.parse(fs.readFileSync(filename, 'utf8'));
287
return new HistoryClass(data);
288
}
289
return new HistoryClass();
290
}
291
292
// Usage
293
const overviewHistory = loadHistory('overview.json', OverviewHistory);
294
// ... perform analysis and add reports ...
295
saveHistory(overviewHistory, 'overview.json');
296
```
297
298
### Database Integration
299
300
```javascript
301
// Example database integration
302
class DatabaseHistory extends History {
303
constructor(tableName) {
304
super();
305
this.tableName = tableName;
306
this.loadFromDatabase();
307
}
308
309
async loadFromDatabase() {
310
// Load history from database
311
const rows = await db.query(`SELECT * FROM ${this.tableName} ORDER BY date`);
312
rows.forEach(row => this.push(row));
313
}
314
315
async push(obj) {
316
super.push(obj);
317
// Also save to database
318
await db.query(`INSERT INTO ${this.tableName} VALUES (?, ?)`, [obj.date, JSON.stringify(obj)]);
319
}
320
}
321
```
322
323
## Historical Analysis Patterns
324
325
Common patterns for analyzing historical data:
326
327
### Trend Analysis
328
329
```javascript
330
function analyzeTrends(history) {
331
const data = history.toJSON();
332
333
if (data.length < 2) return null;
334
335
const latest = data[data.length - 1];
336
const previous = data[data.length - 2];
337
338
return {
339
maintainabilityTrend: latest.complexity.maintainability - previous.complexity.maintainability,
340
complexityTrend: latest.complexity.cyclomatic - previous.complexity.cyclomatic,
341
slocTrend: latest.complexity.sloc.logical - previous.complexity.sloc.logical
342
};
343
}
344
```
345
346
### Quality Gates
347
348
```javascript
349
function checkQualityGates(overviewHistory) {
350
const data = overviewHistory.toJSON();
351
const latest = data[data.length - 1];
352
353
const alerts = [];
354
355
if (latest.summary.average.maintainability < 70) {
356
alerts.push('Low maintainability detected');
357
}
358
359
if (data.length >= 2) {
360
const previous = data[data.length - 2];
361
const maintainabilityChange = latest.summary.average.maintainability - previous.summary.average.maintainability;
362
363
if (maintainabilityChange < -5) {
364
alerts.push('Significant maintainability decrease');
365
}
366
}
367
368
return alerts;
369
}
370
```