@iservu-inc/adf-cli 0.9.1 → 0.11.0
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.
- package/.claude/settings.local.json +18 -0
- package/.project/PROJECT-SETTINGS.md +68 -0
- package/.project/chats/current/2025-10-05_UX-IMPROVEMENTS-AND-AI-ANALYSIS-CONFIG.md +389 -0
- package/.project/chats/current/SESSION-STATUS.md +205 -228
- package/.project/docs/DOCUMENTATION-UPDATE-CHECKLIST.md +196 -0
- package/.project/docs/ROADMAP.md +142 -44
- package/.project/docs/designs/LEARNING-ANALYTICS-DASHBOARD.md +1383 -0
- package/.project/docs/designs/PATTERN-DECAY-ALGORITHM.md +526 -0
- package/CHANGELOG.md +683 -0
- package/README.md +119 -24
- package/lib/learning/analytics-exporter.js +241 -0
- package/lib/learning/analytics-view.js +508 -0
- package/lib/learning/analytics.js +681 -0
- package/lib/learning/decay-manager.js +336 -0
- package/lib/learning/learning-manager.js +19 -6
- package/lib/learning/pattern-detector.js +285 -2
- package/lib/learning/storage.js +49 -1
- package/lib/utils/pre-publish-check.js +74 -0
- package/package.json +3 -2
- package/scripts/generate-test-data.js +557 -0
- package/tests/analytics-exporter.test.js +477 -0
- package/tests/analytics-view.test.js +466 -0
- package/tests/analytics.test.js +712 -0
- package/tests/decay-manager.test.js +394 -0
- package/tests/pattern-decay.test.js +339 -0
- /package/.project/chats/{current → complete}/2025-10-05_INTELLIGENT-ANSWER-ANALYSIS.md +0 -0
- /package/.project/chats/{current → complete}/2025-10-05_MULTI-IDE-IMPROVEMENTS.md +0 -0
|
@@ -0,0 +1,1383 @@
|
|
|
1
|
+
# Learning Analytics Dashboard - Design Specification
|
|
2
|
+
|
|
3
|
+
**Version:** 1.0
|
|
4
|
+
**Date:** 2025-10-27
|
|
5
|
+
**Phase:** Phase 6 - Advanced Learning Features
|
|
6
|
+
**Status:** Design Complete
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
1. [Overview](#overview)
|
|
13
|
+
2. [Goals](#goals)
|
|
14
|
+
3. [Data Sources](#data-sources)
|
|
15
|
+
4. [Analytics Metrics](#analytics-metrics)
|
|
16
|
+
5. [Dashboard Interface](#dashboard-interface)
|
|
17
|
+
6. [Calculations & Algorithms](#calculations--algorithms)
|
|
18
|
+
7. [Export Formats](#export-formats)
|
|
19
|
+
8. [Implementation Plan](#implementation-plan)
|
|
20
|
+
9. [Testing Strategy](#testing-strategy)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Overview
|
|
25
|
+
|
|
26
|
+
The Learning Analytics Dashboard provides comprehensive insights into the ADF CLI learning system's behavior, effectiveness, and user patterns. It enables users to understand how the learning system is performing and make data-driven decisions about their learned preferences.
|
|
27
|
+
|
|
28
|
+
**Key Features:**
|
|
29
|
+
- 10+ meaningful analytics metrics
|
|
30
|
+
- Time-series trend visualization (ASCII charts)
|
|
31
|
+
- Pattern confidence distribution analysis
|
|
32
|
+
- Learning effectiveness calculations
|
|
33
|
+
- Export to JSON/CSV for external analysis
|
|
34
|
+
- Performance optimized for large datasets
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Goals
|
|
39
|
+
|
|
40
|
+
### Primary Goals
|
|
41
|
+
1. **Transparency** - Show users what the learning system has learned
|
|
42
|
+
2. **Insights** - Provide actionable insights about skip behavior and patterns
|
|
43
|
+
3. **Effectiveness** - Measure time savings and filtering accuracy
|
|
44
|
+
4. **Confidence** - Display pattern confidence distribution and decay status
|
|
45
|
+
5. **Export** - Enable data export for team sharing and external tools
|
|
46
|
+
|
|
47
|
+
### Success Criteria
|
|
48
|
+
- Dashboard shows 10+ meaningful insights
|
|
49
|
+
- Performance acceptable with 100+ sessions (< 2 seconds load time)
|
|
50
|
+
- Export works in both JSON and CSV formats
|
|
51
|
+
- All metrics have clear explanations
|
|
52
|
+
- Visual charts render correctly in CLI
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Data Sources
|
|
57
|
+
|
|
58
|
+
### 1. Skip History (`skip-history.json`)
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"version": "1.0",
|
|
62
|
+
"sessions": [
|
|
63
|
+
{
|
|
64
|
+
"sessionId": "uuid",
|
|
65
|
+
"timestamp": "2025-10-27T...",
|
|
66
|
+
"projectType": "cli-tool",
|
|
67
|
+
"frameworks": ["commander"],
|
|
68
|
+
"languages": ["javascript"],
|
|
69
|
+
"skips": [
|
|
70
|
+
{
|
|
71
|
+
"questionId": "q_deployment_strategy",
|
|
72
|
+
"text": "What deployment strategy?",
|
|
73
|
+
"category": "deployment",
|
|
74
|
+
"phase": "planning",
|
|
75
|
+
"action": "skipped",
|
|
76
|
+
"reason": "manual", // or "filtered"
|
|
77
|
+
"timeViewed": 2.5,
|
|
78
|
+
"relevanceScore": 30,
|
|
79
|
+
"skipReason": "Not relevant for CLI"
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
"answers": []
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 2. Answer History (`answer-history.json`)
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"version": "1.0",
|
|
92
|
+
"sessions": [
|
|
93
|
+
{
|
|
94
|
+
"sessionId": "uuid",
|
|
95
|
+
"timestamp": "2025-10-27T...",
|
|
96
|
+
"projectType": "cli-tool",
|
|
97
|
+
"frameworks": ["commander"],
|
|
98
|
+
"answers": [
|
|
99
|
+
{
|
|
100
|
+
"questionId": "q_core_purpose",
|
|
101
|
+
"text": "What is the core purpose?",
|
|
102
|
+
"category": "overview",
|
|
103
|
+
"phase": "discovery",
|
|
104
|
+
"action": "answered",
|
|
105
|
+
"answerLength": 250,
|
|
106
|
+
"wordCount": 45,
|
|
107
|
+
"timeSpent": 45.2,
|
|
108
|
+
"qualityScore": 85,
|
|
109
|
+
"richness": 0.72,
|
|
110
|
+
"relevanceScore": 100
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 3. Patterns (`patterns.json`)
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"version": "1.0",
|
|
122
|
+
"lastUpdated": "2025-10-27T...",
|
|
123
|
+
"lastDecayCheck": "2025-10-27T...",
|
|
124
|
+
"patterns": [
|
|
125
|
+
{
|
|
126
|
+
"id": "pattern_skip_q_deployment_strategy",
|
|
127
|
+
"type": "consistent_skip",
|
|
128
|
+
"questionId": "q_deployment_strategy",
|
|
129
|
+
"questionText": "What deployment strategy?",
|
|
130
|
+
"category": "deployment",
|
|
131
|
+
"confidence": 100,
|
|
132
|
+
"sessionsAnalyzed": 5,
|
|
133
|
+
"skipCount": 5,
|
|
134
|
+
"recommendation": "Auto-filter this question",
|
|
135
|
+
"status": "active",
|
|
136
|
+
"userApproved": true,
|
|
137
|
+
"createdAt": "2025-10-15T...",
|
|
138
|
+
"lastSeen": "2025-10-27T...",
|
|
139
|
+
"timesRenewed": 2,
|
|
140
|
+
"lastDecayCalculation": "2025-10-27T..."
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 4. Learned Rules (`learned-rules.json`)
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"version": "1.0",
|
|
150
|
+
"rules": [
|
|
151
|
+
{
|
|
152
|
+
"id": "rule_skip_q_deployment_strategy",
|
|
153
|
+
"patternId": "pattern_skip_q_deployment_strategy",
|
|
154
|
+
"type": "question_filter",
|
|
155
|
+
"enabled": true,
|
|
156
|
+
"createdAt": "2025-10-15T...",
|
|
157
|
+
"appliedCount": 12,
|
|
158
|
+
"lastApplied": "2025-10-27T..."
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 5. Statistics (`stats.json`)
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"version": "1.0",
|
|
168
|
+
"totalSessions": 15,
|
|
169
|
+
"totalSkips": 45,
|
|
170
|
+
"totalAnswers": 120,
|
|
171
|
+
"patternsDetected": 8,
|
|
172
|
+
"rulesApplied": 3,
|
|
173
|
+
"lastUpdated": "2025-10-27T..."
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Analytics Metrics
|
|
180
|
+
|
|
181
|
+
### 1. Overview Statistics
|
|
182
|
+
**Purpose:** High-level summary of learning system activity
|
|
183
|
+
|
|
184
|
+
**Metrics:**
|
|
185
|
+
- Total sessions conducted
|
|
186
|
+
- Total questions skipped (manual + filtered)
|
|
187
|
+
- Total questions answered
|
|
188
|
+
- Active patterns count
|
|
189
|
+
- Active learned rules count
|
|
190
|
+
- Learning system age (days since first session)
|
|
191
|
+
- Data size (MB)
|
|
192
|
+
|
|
193
|
+
**Calculation:**
|
|
194
|
+
```javascript
|
|
195
|
+
{
|
|
196
|
+
totalSessions: stats.totalSessions,
|
|
197
|
+
totalSkips: stats.totalSkips,
|
|
198
|
+
totalAnswers: stats.totalAnswers,
|
|
199
|
+
activePatterns: patterns.patterns.filter(p => p.status === 'active').length,
|
|
200
|
+
activeRules: rules.rules.filter(r => r.enabled).length,
|
|
201
|
+
learningAge: daysSince(firstSession.timestamp),
|
|
202
|
+
dataSize: await storage.getLearningDataSize() / 1024 / 1024 // MB
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
### 2. Skip Trends Over Time
|
|
209
|
+
**Purpose:** Visualize skip behavior evolution
|
|
210
|
+
|
|
211
|
+
**Metrics:**
|
|
212
|
+
- Skip count by week (last 12 weeks)
|
|
213
|
+
- Manual skips vs filtered skips over time
|
|
214
|
+
- Skip rate trend (% of questions skipped)
|
|
215
|
+
|
|
216
|
+
**Calculation:**
|
|
217
|
+
```javascript
|
|
218
|
+
function calculateSkipTrends(skipHistory) {
|
|
219
|
+
const weeks = getLast12Weeks();
|
|
220
|
+
const trendData = weeks.map(week => {
|
|
221
|
+
const weekSessions = filterSessionsByWeek(skipHistory.sessions, week);
|
|
222
|
+
const manualSkips = countSkipsByReason(weekSessions, 'manual');
|
|
223
|
+
const filteredSkips = countSkipsByReason(weekSessions, 'filtered');
|
|
224
|
+
const totalQuestions = manualSkips + filteredSkips + countAnswers(weekSessions);
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
week: week.label,
|
|
228
|
+
manualSkips,
|
|
229
|
+
filteredSkips,
|
|
230
|
+
totalSkips: manualSkips + filteredSkips,
|
|
231
|
+
skipRate: Math.round((manualSkips + filteredSkips) / totalQuestions * 100)
|
|
232
|
+
};
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
return trendData;
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Visualization:** ASCII bar chart
|
|
240
|
+
```
|
|
241
|
+
Skip Trends (Last 12 Weeks)
|
|
242
|
+
Week 40: ████████░░ 15 skips (8 manual, 7 filtered)
|
|
243
|
+
Week 41: ██████████ 20 skips (12 manual, 8 filtered)
|
|
244
|
+
Week 42: ████░░░░░░ 8 skips (5 manual, 3 filtered)
|
|
245
|
+
...
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
### 3. Category Preference Heatmap
|
|
251
|
+
**Purpose:** Show skip rates by question category
|
|
252
|
+
|
|
253
|
+
**Metrics:**
|
|
254
|
+
- Skip rate per category (%)
|
|
255
|
+
- Most skipped categories (top 5)
|
|
256
|
+
- Least skipped categories (bottom 5)
|
|
257
|
+
- Category skip trend (increasing/decreasing)
|
|
258
|
+
|
|
259
|
+
**Calculation:**
|
|
260
|
+
```javascript
|
|
261
|
+
function calculateCategoryPreferences(skipHistory, answerHistory) {
|
|
262
|
+
const categories = {};
|
|
263
|
+
|
|
264
|
+
// Count skips and answers by category
|
|
265
|
+
for (const session of skipHistory.sessions) {
|
|
266
|
+
for (const skip of session.skips) {
|
|
267
|
+
if (!skip.category) continue;
|
|
268
|
+
if (!categories[skip.category]) {
|
|
269
|
+
categories[skip.category] = { skips: 0, answers: 0 };
|
|
270
|
+
}
|
|
271
|
+
categories[skip.category].skips++;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (const session of answerHistory.sessions) {
|
|
276
|
+
for (const answer of session.answers) {
|
|
277
|
+
if (!answer.category) continue;
|
|
278
|
+
if (!categories[answer.category]) {
|
|
279
|
+
categories[answer.category] = { skips: 0, answers: 0 };
|
|
280
|
+
}
|
|
281
|
+
categories[answer.category].answers++;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Calculate skip rates
|
|
286
|
+
return Object.entries(categories).map(([category, data]) => {
|
|
287
|
+
const total = data.skips + data.answers;
|
|
288
|
+
const skipRate = Math.round((data.skips / total) * 100);
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
category,
|
|
292
|
+
skips: data.skips,
|
|
293
|
+
answers: data.answers,
|
|
294
|
+
total,
|
|
295
|
+
skipRate,
|
|
296
|
+
level: getSkipLevel(skipRate) // "low", "medium", "high"
|
|
297
|
+
};
|
|
298
|
+
}).sort((a, b) => b.skipRate - a.skipRate);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function getSkipLevel(skipRate) {
|
|
302
|
+
if (skipRate >= 70) return 'high';
|
|
303
|
+
if (skipRate >= 40) return 'medium';
|
|
304
|
+
return 'low';
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**Visualization:** ASCII heatmap
|
|
309
|
+
```
|
|
310
|
+
Category Preferences (Skip Rate)
|
|
311
|
+
deployment ████████████████████ 95% (19/20) HIGH
|
|
312
|
+
UI/UX ██████████████░░░░░░ 70% (14/20) HIGH
|
|
313
|
+
documentation ████████░░░░░░░░░░░░ 40% (8/20) MEDIUM
|
|
314
|
+
architecture ███░░░░░░░░░░░░░░░░░ 15% (3/20) LOW
|
|
315
|
+
core-features ██░░░░░░░░░░░░░░░░░░ 10% (2/20) LOW
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
### 4. Pattern Confidence Distribution
|
|
321
|
+
**Purpose:** Show distribution of pattern confidence scores
|
|
322
|
+
|
|
323
|
+
**Metrics:**
|
|
324
|
+
- Patterns by confidence level (high/medium/low)
|
|
325
|
+
- Average pattern confidence
|
|
326
|
+
- Pattern confidence trend (improving/declining)
|
|
327
|
+
- Patterns approaching removal threshold
|
|
328
|
+
|
|
329
|
+
**Calculation:**
|
|
330
|
+
```javascript
|
|
331
|
+
function calculatePatternDistribution(patterns) {
|
|
332
|
+
const distribution = {
|
|
333
|
+
high: patterns.filter(p => p.confidence >= 90).length, // 90%+
|
|
334
|
+
medium: patterns.filter(p => p.confidence >= 75 && p.confidence < 90).length, // 75-89%
|
|
335
|
+
low: patterns.filter(p => p.confidence < 75 && p.confidence >= 50).length, // 50-74%
|
|
336
|
+
veryLow: patterns.filter(p => p.confidence < 50).length // <50%
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const avgConfidence = patterns.reduce((sum, p) => sum + p.confidence, 0) / patterns.length;
|
|
340
|
+
|
|
341
|
+
const atRisk = patterns.filter(p => p.confidence < 50 || isInactive(p, 5)); // <50 or 5+ months inactive
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
distribution,
|
|
345
|
+
avgConfidence: Math.round(avgConfidence),
|
|
346
|
+
totalPatterns: patterns.length,
|
|
347
|
+
atRiskCount: atRisk.length,
|
|
348
|
+
atRiskPatterns: atRisk
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Visualization:** ASCII pie chart representation
|
|
354
|
+
```
|
|
355
|
+
Pattern Confidence Distribution
|
|
356
|
+
High (90%+): ████████░░ 8 patterns (53%)
|
|
357
|
+
Medium (75-89%): ████░░░░░░ 4 patterns (27%)
|
|
358
|
+
Low (50-74%): ██░░░░░░░░ 2 patterns (13%)
|
|
359
|
+
Very Low (<50%): █░░░░░░░░░ 1 pattern (7%)
|
|
360
|
+
|
|
361
|
+
Average Confidence: 78%
|
|
362
|
+
At Risk (decay): 1 pattern
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
### 5. Learning Effectiveness
|
|
368
|
+
**Purpose:** Measure learning system's impact and accuracy
|
|
369
|
+
|
|
370
|
+
**Metrics:**
|
|
371
|
+
- Time saved (estimated minutes)
|
|
372
|
+
- Questions auto-filtered successfully
|
|
373
|
+
- False positive rate (filtered but should answer)
|
|
374
|
+
- Pattern accuracy (approved patterns / total patterns)
|
|
375
|
+
- Rule application rate (how often rules are triggered)
|
|
376
|
+
|
|
377
|
+
**Calculation:**
|
|
378
|
+
```javascript
|
|
379
|
+
function calculateEffectiveness(skipHistory, patterns, rules) {
|
|
380
|
+
// Time saved: filtered questions × avg time per question
|
|
381
|
+
const avgTimePerQuestion = 2.5; // minutes (from answer data)
|
|
382
|
+
const questionsFiltered = countFilteredQuestions(skipHistory);
|
|
383
|
+
const timeSavedMinutes = Math.round(questionsFiltered * avgTimePerQuestion);
|
|
384
|
+
|
|
385
|
+
// False positives: filtered questions that user later answered manually
|
|
386
|
+
// (This would require tracking if a filtered question was later answered)
|
|
387
|
+
const falsePositives = 0; // TODO: Implement tracking in Phase 6.1
|
|
388
|
+
|
|
389
|
+
// Pattern accuracy: user-approved patterns / total patterns
|
|
390
|
+
const approvedPatterns = patterns.filter(p => p.userApproved).length;
|
|
391
|
+
const patternAccuracy = Math.round((approvedPatterns / patterns.length) * 100);
|
|
392
|
+
|
|
393
|
+
// Rule application rate: how often rules actually filter questions
|
|
394
|
+
const totalRuleApplications = rules.reduce((sum, r) => sum + (r.appliedCount || 0), 0);
|
|
395
|
+
const avgApplicationsPerRule = Math.round(totalRuleApplications / rules.length);
|
|
396
|
+
|
|
397
|
+
return {
|
|
398
|
+
timeSavedMinutes,
|
|
399
|
+
questionsFiltered,
|
|
400
|
+
falsePositives,
|
|
401
|
+
falsePositiveRate: 0, // TODO
|
|
402
|
+
patternAccuracy,
|
|
403
|
+
totalRuleApplications,
|
|
404
|
+
avgApplicationsPerRule,
|
|
405
|
+
effectiveness: calculateOverallEffectiveness(patternAccuracy, falsePositives)
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function calculateOverallEffectiveness(accuracy, falsePositives) {
|
|
410
|
+
// Simple formula: accuracy - (falsePositives * 5)
|
|
411
|
+
// Each false positive reduces effectiveness by 5%
|
|
412
|
+
const effectiveness = accuracy - (falsePositives * 5);
|
|
413
|
+
return Math.max(0, Math.min(100, effectiveness));
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
**Visualization:**
|
|
418
|
+
```
|
|
419
|
+
Learning Effectiveness
|
|
420
|
+
|
|
421
|
+
Time Saved: 145 minutes (2.4 hours)
|
|
422
|
+
Questions Filtered: 58 questions
|
|
423
|
+
Pattern Accuracy: 87% (13/15 patterns approved)
|
|
424
|
+
Rule Applications: 42 total (3.5 avg per rule)
|
|
425
|
+
|
|
426
|
+
Overall Effectiveness: 87%
|
|
427
|
+
Status: EXCELLENT ✓
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
### 6. Pattern Decay Status
|
|
433
|
+
**Purpose:** Show pattern health and decay trends
|
|
434
|
+
|
|
435
|
+
**Metrics:**
|
|
436
|
+
- Patterns by decay status (healthy/warning/critical)
|
|
437
|
+
- Patterns renewed in last 30 days
|
|
438
|
+
- Patterns approaching removal threshold
|
|
439
|
+
- Average pattern age (days)
|
|
440
|
+
- Decay rate distribution
|
|
441
|
+
|
|
442
|
+
**Calculation:**
|
|
443
|
+
```javascript
|
|
444
|
+
function calculateDecayStatus(patterns, decayConfig) {
|
|
445
|
+
const now = new Date();
|
|
446
|
+
const decayStatus = {
|
|
447
|
+
healthy: [],
|
|
448
|
+
warning: [],
|
|
449
|
+
critical: []
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
for (const pattern of patterns) {
|
|
453
|
+
const daysSinceLastSeen = daysBetween(new Date(pattern.lastSeen), now);
|
|
454
|
+
const monthsInactive = daysSinceLastSeen / 30;
|
|
455
|
+
|
|
456
|
+
// Categorize by confidence and inactivity
|
|
457
|
+
if (pattern.confidence >= 75 && monthsInactive < 3) {
|
|
458
|
+
decayStatus.healthy.push(pattern);
|
|
459
|
+
} else if (pattern.confidence >= 50 && monthsInactive < 5) {
|
|
460
|
+
decayStatus.warning.push(pattern);
|
|
461
|
+
} else {
|
|
462
|
+
decayStatus.critical.push(pattern);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const recentlyRenewed = patterns.filter(p => {
|
|
467
|
+
const renewalDate = new Date(pattern.lastSeen);
|
|
468
|
+
return daysBetween(renewalDate, now) <= 30;
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const avgAge = patterns.reduce((sum, p) => {
|
|
472
|
+
return sum + daysBetween(new Date(p.createdAt), now);
|
|
473
|
+
}, 0) / patterns.length;
|
|
474
|
+
|
|
475
|
+
return {
|
|
476
|
+
healthy: decayStatus.healthy.length,
|
|
477
|
+
warning: decayStatus.warning.length,
|
|
478
|
+
critical: decayStatus.critical.length,
|
|
479
|
+
recentlyRenewed: recentlyRenewed.length,
|
|
480
|
+
avgAge: Math.round(avgAge),
|
|
481
|
+
needsAttention: decayStatus.critical
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**Visualization:**
|
|
487
|
+
```
|
|
488
|
+
Pattern Decay Status
|
|
489
|
+
|
|
490
|
+
Healthy (75%+, <3mo): ████████░░ 8 patterns (53%)
|
|
491
|
+
Warning (50-74%, 3-5mo): ████░░░░░░ 4 patterns (27%)
|
|
492
|
+
Critical (<50%, >5mo): ██░░░░░░░░ 2 patterns (13%)
|
|
493
|
+
Recently Renewed: 1 pattern (last 30 days)
|
|
494
|
+
|
|
495
|
+
Average Pattern Age: 45 days
|
|
496
|
+
Patterns Needing Attention: 2 patterns
|
|
497
|
+
|
|
498
|
+
⚠ Warning: 2 patterns approaching removal threshold
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
### 7. Session Timeline
|
|
504
|
+
**Purpose:** Show session history and frequency
|
|
505
|
+
|
|
506
|
+
**Metrics:**
|
|
507
|
+
- Session frequency (sessions per week)
|
|
508
|
+
- Session count by project type
|
|
509
|
+
- Most active time period
|
|
510
|
+
- Sessions with most skips/answers
|
|
511
|
+
|
|
512
|
+
**Calculation:**
|
|
513
|
+
```javascript
|
|
514
|
+
function calculateSessionTimeline(skipHistory, answerHistory) {
|
|
515
|
+
const sessions = skipHistory.sessions;
|
|
516
|
+
const timeline = [];
|
|
517
|
+
|
|
518
|
+
for (const session of sessions) {
|
|
519
|
+
const skipCount = session.skips.length;
|
|
520
|
+
const matchingAnswerSession = answerHistory.sessions.find(
|
|
521
|
+
s => s.sessionId === session.sessionId
|
|
522
|
+
);
|
|
523
|
+
const answerCount = matchingAnswerSession ? matchingAnswerSession.answers.length : 0;
|
|
524
|
+
|
|
525
|
+
timeline.push({
|
|
526
|
+
sessionId: session.sessionId,
|
|
527
|
+
timestamp: session.timestamp,
|
|
528
|
+
projectType: session.projectType,
|
|
529
|
+
frameworks: session.frameworks,
|
|
530
|
+
skipCount,
|
|
531
|
+
answerCount,
|
|
532
|
+
totalQuestions: skipCount + answerCount
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Calculate frequency
|
|
537
|
+
const firstSession = new Date(sessions[0].timestamp);
|
|
538
|
+
const lastSession = new Date(sessions[sessions.length - 1].timestamp);
|
|
539
|
+
const weeksBetween = Math.ceil(daysBetween(firstSession, lastSession) / 7);
|
|
540
|
+
const sessionsPerWeek = sessions.length / weeksBetween;
|
|
541
|
+
|
|
542
|
+
return {
|
|
543
|
+
timeline: timeline.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)),
|
|
544
|
+
sessionsPerWeek: sessionsPerWeek.toFixed(1),
|
|
545
|
+
totalSessions: sessions.length,
|
|
546
|
+
firstSession: firstSession.toISOString().split('T')[0],
|
|
547
|
+
lastSession: lastSession.toISOString().split('T')[0]
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
### 8. Most Impactful Patterns
|
|
555
|
+
**Purpose:** Highlight patterns with biggest impact
|
|
556
|
+
|
|
557
|
+
**Metrics:**
|
|
558
|
+
- Top 5 patterns by time saved
|
|
559
|
+
- Top 5 patterns by application count
|
|
560
|
+
- Newest high-confidence patterns
|
|
561
|
+
- Most consistent patterns (100% confidence)
|
|
562
|
+
|
|
563
|
+
**Calculation:**
|
|
564
|
+
```javascript
|
|
565
|
+
function calculateImpactfulPatterns(patterns, rules, skipHistory) {
|
|
566
|
+
const impactData = [];
|
|
567
|
+
|
|
568
|
+
for (const pattern of patterns) {
|
|
569
|
+
const matchingRule = rules.find(r => r.patternId === pattern.id);
|
|
570
|
+
const appliedCount = matchingRule ? matchingRule.appliedCount : 0;
|
|
571
|
+
const timeSaved = appliedCount * 2.5; // 2.5 min per question
|
|
572
|
+
|
|
573
|
+
impactData.push({
|
|
574
|
+
pattern,
|
|
575
|
+
appliedCount,
|
|
576
|
+
timeSaved,
|
|
577
|
+
impact: calculateImpact(appliedCount, pattern.confidence)
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return {
|
|
582
|
+
topByTimeSaved: impactData.sort((a, b) => b.timeSaved - a.timeSaved).slice(0, 5),
|
|
583
|
+
topByApplications: impactData.sort((a, b) => b.appliedCount - a.appliedCount).slice(0, 5),
|
|
584
|
+
perfectConfidence: patterns.filter(p => p.confidence === 100),
|
|
585
|
+
newestHighConfidence: patterns
|
|
586
|
+
.filter(p => p.confidence >= 90)
|
|
587
|
+
.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
|
|
588
|
+
.slice(0, 5)
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function calculateImpact(applications, confidence) {
|
|
593
|
+
// Impact = applications × confidence × 0.01
|
|
594
|
+
return Math.round(applications * confidence * 0.01);
|
|
595
|
+
}
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
### 9. Question Statistics
|
|
601
|
+
**Purpose:** Detailed question-level analytics
|
|
602
|
+
|
|
603
|
+
**Metrics:**
|
|
604
|
+
- Most answered questions (top 10)
|
|
605
|
+
- Most skipped questions (top 10)
|
|
606
|
+
- Questions with lowest skip rate
|
|
607
|
+
- Average time per question by category
|
|
608
|
+
|
|
609
|
+
**Calculation:**
|
|
610
|
+
```javascript
|
|
611
|
+
function calculateQuestionStats(skipHistory, answerHistory) {
|
|
612
|
+
const questionData = {};
|
|
613
|
+
|
|
614
|
+
// Aggregate skip data
|
|
615
|
+
for (const session of skipHistory.sessions) {
|
|
616
|
+
for (const skip of session.skips) {
|
|
617
|
+
if (!questionData[skip.questionId]) {
|
|
618
|
+
questionData[skip.questionId] = {
|
|
619
|
+
questionId: skip.questionId,
|
|
620
|
+
text: skip.text,
|
|
621
|
+
category: skip.category,
|
|
622
|
+
skips: 0,
|
|
623
|
+
answers: 0,
|
|
624
|
+
totalTimeSpent: 0,
|
|
625
|
+
avgTimeSpent: 0
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
questionData[skip.questionId].skips++;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Aggregate answer data
|
|
633
|
+
for (const session of answerHistory.sessions) {
|
|
634
|
+
for (const answer of session.answers) {
|
|
635
|
+
if (!questionData[answer.questionId]) {
|
|
636
|
+
questionData[answer.questionId] = {
|
|
637
|
+
questionId: answer.questionId,
|
|
638
|
+
text: answer.text,
|
|
639
|
+
category: answer.category,
|
|
640
|
+
skips: 0,
|
|
641
|
+
answers: 0,
|
|
642
|
+
totalTimeSpent: 0,
|
|
643
|
+
avgTimeSpent: 0
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
questionData[answer.questionId].answers++;
|
|
647
|
+
questionData[answer.questionId].totalTimeSpent += answer.timeSpent || 0;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Calculate averages and rates
|
|
652
|
+
const stats = Object.values(questionData).map(q => {
|
|
653
|
+
const total = q.skips + q.answers;
|
|
654
|
+
q.skipRate = Math.round((q.skips / total) * 100);
|
|
655
|
+
q.avgTimeSpent = q.answers > 0 ? Math.round(q.totalTimeSpent / q.answers) : 0;
|
|
656
|
+
return q;
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
return {
|
|
660
|
+
mostAnswered: stats.sort((a, b) => b.answers - a.answers).slice(0, 10),
|
|
661
|
+
mostSkipped: stats.sort((a, b) => b.skips - a.skips).slice(0, 10),
|
|
662
|
+
lowestSkipRate: stats.sort((a, b) => a.skipRate - b.skipRate).slice(0, 10),
|
|
663
|
+
avgTimeByCategory: calculateAvgTimeByCategory(stats)
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
---
|
|
669
|
+
|
|
670
|
+
### 10. Export Summary
|
|
671
|
+
**Purpose:** Provide metadata for exported analytics
|
|
672
|
+
|
|
673
|
+
**Metrics:**
|
|
674
|
+
- Export timestamp
|
|
675
|
+
- Data range (first session to last session)
|
|
676
|
+
- Export version
|
|
677
|
+
- Data completeness indicators
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
681
|
+
## Dashboard Interface
|
|
682
|
+
|
|
683
|
+
### CLI Navigation Structure
|
|
684
|
+
|
|
685
|
+
```
|
|
686
|
+
Learning System
|
|
687
|
+
├── View Skip History
|
|
688
|
+
├── Review Patterns
|
|
689
|
+
├── Manage Rules
|
|
690
|
+
├── Analytics Dashboard ← NEW
|
|
691
|
+
│ ├── Overview
|
|
692
|
+
│ ├── Skip Trends
|
|
693
|
+
│ ├── Category Preferences
|
|
694
|
+
│ ├── Pattern Health
|
|
695
|
+
│ ├── Effectiveness
|
|
696
|
+
│ ├── Question Stats
|
|
697
|
+
│ └── Export Analytics
|
|
698
|
+
├── Settings
|
|
699
|
+
└── Clear Data
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
### Dashboard Layout
|
|
703
|
+
|
|
704
|
+
```
|
|
705
|
+
═══════════════════════════════════════════════════════════════
|
|
706
|
+
LEARNING ANALYTICS DASHBOARD
|
|
707
|
+
═══════════════════════════════════════════════════════════════
|
|
708
|
+
|
|
709
|
+
📊 OVERVIEW
|
|
710
|
+
|
|
711
|
+
Total Sessions: 15 sessions
|
|
712
|
+
Total Skips: 45 questions (30 manual, 15 filtered)
|
|
713
|
+
Total Answers: 120 questions
|
|
714
|
+
Active Patterns: 8 patterns
|
|
715
|
+
Active Rules: 3 rules
|
|
716
|
+
Learning System Age: 32 days
|
|
717
|
+
Data Size: 0.15 MB
|
|
718
|
+
|
|
719
|
+
───────────────────────────────────────────────────────────────
|
|
720
|
+
|
|
721
|
+
📈 SKIP TRENDS (Last 12 Weeks)
|
|
722
|
+
|
|
723
|
+
Week 40: ████████░░ 15 skips (10 manual, 5 filtered)
|
|
724
|
+
Week 41: ██████████ 20 skips (13 manual, 7 filtered)
|
|
725
|
+
Week 42: ████░░░░░░ 8 skips (5 manual, 3 filtered)
|
|
726
|
+
Week 43: ░░░░░░░░░░ 0 skips (no sessions)
|
|
727
|
+
|
|
728
|
+
Trend: Decreasing (↓ 40% from peak)
|
|
729
|
+
|
|
730
|
+
───────────────────────────────────────────────────────────────
|
|
731
|
+
|
|
732
|
+
🎯 CATEGORY PREFERENCES
|
|
733
|
+
|
|
734
|
+
deployment ████████████████████ 95% (19/20) HIGH
|
|
735
|
+
UI/UX ██████████████░░░░░░ 70% (14/20) HIGH
|
|
736
|
+
documentation ████████░░░░░░░░░░░░ 40% (8/20) MEDIUM
|
|
737
|
+
architecture ███░░░░░░░░░░░░░░░░░ 15% (3/20) LOW
|
|
738
|
+
core-features ██░░░░░░░░░░░░░░░░░░ 10% (2/20) LOW
|
|
739
|
+
|
|
740
|
+
───────────────────────────────────────────────────────────────
|
|
741
|
+
|
|
742
|
+
🧬 PATTERN HEALTH
|
|
743
|
+
|
|
744
|
+
Healthy (75%+): ████████░░ 8 patterns (53%)
|
|
745
|
+
Warning (50-74%): ████░░░░░░ 4 patterns (27%)
|
|
746
|
+
Critical (<50%): ██░░░░░░░░ 2 patterns (13%)
|
|
747
|
+
|
|
748
|
+
Average Confidence: 78%
|
|
749
|
+
Recently Renewed: 1 pattern (last 30 days)
|
|
750
|
+
|
|
751
|
+
⚠ Warning: 2 patterns approaching removal threshold
|
|
752
|
+
|
|
753
|
+
───────────────────────────────────────────────────────────────
|
|
754
|
+
|
|
755
|
+
✨ EFFECTIVENESS
|
|
756
|
+
|
|
757
|
+
Time Saved: 145 minutes (2.4 hours)
|
|
758
|
+
Questions Filtered: 58 questions
|
|
759
|
+
Pattern Accuracy: 87% (13/15 patterns approved)
|
|
760
|
+
Rule Applications: 42 total (3.5 avg per rule)
|
|
761
|
+
|
|
762
|
+
Overall Effectiveness: 87% ✓ EXCELLENT
|
|
763
|
+
|
|
764
|
+
───────────────────────────────────────────────────────────────
|
|
765
|
+
|
|
766
|
+
? What would you like to do?
|
|
767
|
+
|
|
768
|
+
View Detailed Skip Trends
|
|
769
|
+
View Category Breakdown
|
|
770
|
+
View Pattern Details
|
|
771
|
+
View Question Statistics
|
|
772
|
+
❯ Export Analytics (JSON/CSV)
|
|
773
|
+
Back to Learning System
|
|
774
|
+
|
|
775
|
+
───────────────────────────────────────────────────────────────
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
### Interactive Features
|
|
779
|
+
|
|
780
|
+
1. **Navigation**
|
|
781
|
+
- Arrow keys to navigate sections
|
|
782
|
+
- Enter to select/drill down
|
|
783
|
+
- 'q' or ESC to go back
|
|
784
|
+
- 'e' to export from any view
|
|
785
|
+
|
|
786
|
+
2. **Drill-Down Views**
|
|
787
|
+
- Click on any metric to see detailed breakdown
|
|
788
|
+
- Example: Click "Category Preferences" → See detailed category analysis with all questions
|
|
789
|
+
|
|
790
|
+
3. **Filtering**
|
|
791
|
+
- Filter by date range
|
|
792
|
+
- Filter by project type
|
|
793
|
+
- Filter by confidence level
|
|
794
|
+
|
|
795
|
+
4. **Refresh**
|
|
796
|
+
- Auto-refresh option (every N seconds)
|
|
797
|
+
- Manual refresh with 'r' key
|
|
798
|
+
|
|
799
|
+
---
|
|
800
|
+
|
|
801
|
+
## Calculations & Algorithms
|
|
802
|
+
|
|
803
|
+
### Helper Functions
|
|
804
|
+
|
|
805
|
+
```javascript
|
|
806
|
+
// Date utilities
|
|
807
|
+
function daysBetween(date1, date2) {
|
|
808
|
+
return Math.floor((date2 - date1) / (1000 * 60 * 60 * 24));
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function getLast12Weeks() {
|
|
812
|
+
const weeks = [];
|
|
813
|
+
const now = new Date();
|
|
814
|
+
|
|
815
|
+
for (let i = 11; i >= 0; i--) {
|
|
816
|
+
const weekStart = new Date(now);
|
|
817
|
+
weekStart.setDate(now.getDate() - (i * 7));
|
|
818
|
+
const weekEnd = new Date(weekStart);
|
|
819
|
+
weekEnd.setDate(weekStart.getDate() + 6);
|
|
820
|
+
|
|
821
|
+
weeks.push({
|
|
822
|
+
label: `Week ${getWeekNumber(weekStart)}`,
|
|
823
|
+
start: weekStart,
|
|
824
|
+
end: weekEnd
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
return weeks;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function getWeekNumber(date) {
|
|
832
|
+
const firstDayOfYear = new Date(date.getFullYear(), 0, 1);
|
|
833
|
+
const pastDaysOfYear = (date - firstDayOfYear) / 86400000;
|
|
834
|
+
return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// Session filtering
|
|
838
|
+
function filterSessionsByWeek(sessions, week) {
|
|
839
|
+
return sessions.filter(session => {
|
|
840
|
+
const sessionDate = new Date(session.timestamp);
|
|
841
|
+
return sessionDate >= week.start && sessionDate <= week.end;
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
function countSkipsByReason(sessions, reason) {
|
|
846
|
+
return sessions.reduce((count, session) => {
|
|
847
|
+
return count + session.skips.filter(s => s.reason === reason).length;
|
|
848
|
+
}, 0);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function countAnswers(sessions) {
|
|
852
|
+
return sessions.reduce((count, session) => {
|
|
853
|
+
return count + session.answers.length;
|
|
854
|
+
}, 0);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// Inactivity detection
|
|
858
|
+
function isInactive(pattern, months) {
|
|
859
|
+
const now = new Date();
|
|
860
|
+
const lastSeen = new Date(pattern.lastSeen);
|
|
861
|
+
const monthsInactive = (now - lastSeen) / (1000 * 60 * 60 * 24 * 30);
|
|
862
|
+
return monthsInactive >= months;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// ASCII chart generation
|
|
866
|
+
function generateBarChart(data, maxWidth = 20) {
|
|
867
|
+
const maxValue = Math.max(...data.map(d => d.value));
|
|
868
|
+
|
|
869
|
+
return data.map(item => {
|
|
870
|
+
const barWidth = Math.round((item.value / maxValue) * maxWidth);
|
|
871
|
+
const bar = '█'.repeat(barWidth) + '░'.repeat(maxWidth - barWidth);
|
|
872
|
+
return `${item.label.padEnd(15)} ${bar} ${item.value}`;
|
|
873
|
+
}).join('\n');
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
function generatePieChart(data, totalWidth = 10) {
|
|
877
|
+
const total = data.reduce((sum, d) => sum + d.value, 0);
|
|
878
|
+
|
|
879
|
+
return data.map(item => {
|
|
880
|
+
const percentage = Math.round((item.value / total) * 100);
|
|
881
|
+
const barWidth = Math.round((item.value / total) * totalWidth);
|
|
882
|
+
const bar = '█'.repeat(barWidth) + '░'.repeat(totalWidth - barWidth);
|
|
883
|
+
return `${item.label.padEnd(15)} ${bar} ${item.value} (${percentage}%)`;
|
|
884
|
+
}).join('\n');
|
|
885
|
+
}
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
---
|
|
889
|
+
|
|
890
|
+
## Export Formats
|
|
891
|
+
|
|
892
|
+
### JSON Export Format
|
|
893
|
+
|
|
894
|
+
```json
|
|
895
|
+
{
|
|
896
|
+
"version": "1.0",
|
|
897
|
+
"exportedAt": "2025-10-27T12:34:56Z",
|
|
898
|
+
"exportType": "learning-analytics",
|
|
899
|
+
"dataRange": {
|
|
900
|
+
"firstSession": "2025-09-15T...",
|
|
901
|
+
"lastSession": "2025-10-27T...",
|
|
902
|
+
"totalSessions": 15,
|
|
903
|
+
"daysSpanned": 42
|
|
904
|
+
},
|
|
905
|
+
"overview": {
|
|
906
|
+
"totalSessions": 15,
|
|
907
|
+
"totalSkips": 45,
|
|
908
|
+
"totalAnswers": 120,
|
|
909
|
+
"activePatterns": 8,
|
|
910
|
+
"activeRules": 3,
|
|
911
|
+
"learningAge": 42,
|
|
912
|
+
"dataSizeMB": 0.15
|
|
913
|
+
},
|
|
914
|
+
"skipTrends": [
|
|
915
|
+
{
|
|
916
|
+
"week": "Week 40",
|
|
917
|
+
"manualSkips": 10,
|
|
918
|
+
"filteredSkips": 5,
|
|
919
|
+
"totalSkips": 15,
|
|
920
|
+
"skipRate": 30
|
|
921
|
+
}
|
|
922
|
+
],
|
|
923
|
+
"categoryPreferences": [
|
|
924
|
+
{
|
|
925
|
+
"category": "deployment",
|
|
926
|
+
"skips": 19,
|
|
927
|
+
"answers": 1,
|
|
928
|
+
"total": 20,
|
|
929
|
+
"skipRate": 95,
|
|
930
|
+
"level": "high"
|
|
931
|
+
}
|
|
932
|
+
],
|
|
933
|
+
"patternDistribution": {
|
|
934
|
+
"distribution": {
|
|
935
|
+
"high": 8,
|
|
936
|
+
"medium": 4,
|
|
937
|
+
"low": 2,
|
|
938
|
+
"veryLow": 1
|
|
939
|
+
},
|
|
940
|
+
"avgConfidence": 78,
|
|
941
|
+
"totalPatterns": 15,
|
|
942
|
+
"atRiskCount": 2
|
|
943
|
+
},
|
|
944
|
+
"effectiveness": {
|
|
945
|
+
"timeSavedMinutes": 145,
|
|
946
|
+
"questionsFiltered": 58,
|
|
947
|
+
"patternAccuracy": 87,
|
|
948
|
+
"totalRuleApplications": 42,
|
|
949
|
+
"avgApplicationsPerRule": 3.5,
|
|
950
|
+
"overallEffectiveness": 87
|
|
951
|
+
},
|
|
952
|
+
"decayStatus": {
|
|
953
|
+
"healthy": 8,
|
|
954
|
+
"warning": 4,
|
|
955
|
+
"critical": 2,
|
|
956
|
+
"recentlyRenewed": 1,
|
|
957
|
+
"avgAge": 45
|
|
958
|
+
},
|
|
959
|
+
"questionStats": {
|
|
960
|
+
"mostAnswered": [...],
|
|
961
|
+
"mostSkipped": [...],
|
|
962
|
+
"lowestSkipRate": [...]
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
### CSV Export Format
|
|
968
|
+
|
|
969
|
+
**File 1: `analytics-overview.csv`**
|
|
970
|
+
```csv
|
|
971
|
+
Metric,Value,Unit
|
|
972
|
+
Total Sessions,15,sessions
|
|
973
|
+
Total Skips,45,questions
|
|
974
|
+
Total Answers,120,questions
|
|
975
|
+
Active Patterns,8,patterns
|
|
976
|
+
Active Rules,3,rules
|
|
977
|
+
Learning Age,42,days
|
|
978
|
+
Data Size,0.15,MB
|
|
979
|
+
Time Saved,145,minutes
|
|
980
|
+
Pattern Accuracy,87,%
|
|
981
|
+
Overall Effectiveness,87,%
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
**File 2: `analytics-skip-trends.csv`**
|
|
985
|
+
```csv
|
|
986
|
+
Week,Manual Skips,Filtered Skips,Total Skips,Skip Rate
|
|
987
|
+
Week 40,10,5,15,30
|
|
988
|
+
Week 41,13,7,20,35
|
|
989
|
+
Week 42,5,3,8,20
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
**File 3: `analytics-category-preferences.csv`**
|
|
993
|
+
```csv
|
|
994
|
+
Category,Skips,Answers,Total,Skip Rate,Level
|
|
995
|
+
deployment,19,1,20,95,high
|
|
996
|
+
UI/UX,14,6,20,70,high
|
|
997
|
+
documentation,8,12,20,40,medium
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
**File 4: `analytics-patterns.csv`**
|
|
1001
|
+
```csv
|
|
1002
|
+
Pattern ID,Type,Confidence,Status,Created At,Last Seen,Times Renewed
|
|
1003
|
+
pattern_skip_q_deployment,consistent_skip,100,active,2025-10-15,2025-10-27,2
|
|
1004
|
+
```
|
|
1005
|
+
|
|
1006
|
+
---
|
|
1007
|
+
|
|
1008
|
+
## Implementation Plan
|
|
1009
|
+
|
|
1010
|
+
### Phase 1: Core Analytics Module
|
|
1011
|
+
**Files to create:**
|
|
1012
|
+
- `lib/learning/analytics.js` - Core analytics module with all calculation functions
|
|
1013
|
+
|
|
1014
|
+
**Features:**
|
|
1015
|
+
1. Overview statistics calculation
|
|
1016
|
+
2. Skip trends calculation
|
|
1017
|
+
3. Category preferences calculation
|
|
1018
|
+
4. Pattern distribution calculation
|
|
1019
|
+
5. Effectiveness calculation
|
|
1020
|
+
6. Decay status calculation
|
|
1021
|
+
|
|
1022
|
+
**Estimated effort:** 4-6 hours
|
|
1023
|
+
|
|
1024
|
+
---
|
|
1025
|
+
|
|
1026
|
+
### Phase 2: Dashboard UI
|
|
1027
|
+
**Files to create:**
|
|
1028
|
+
- `lib/learning/analytics-view.js` - CLI dashboard UI
|
|
1029
|
+
|
|
1030
|
+
**Features:**
|
|
1031
|
+
1. Main dashboard view with all metrics
|
|
1032
|
+
2. ASCII chart rendering
|
|
1033
|
+
3. Interactive navigation
|
|
1034
|
+
4. Drill-down views
|
|
1035
|
+
5. Color-coded output
|
|
1036
|
+
|
|
1037
|
+
**Estimated effort:** 3-4 hours
|
|
1038
|
+
|
|
1039
|
+
---
|
|
1040
|
+
|
|
1041
|
+
### Phase 3: Export Functionality
|
|
1042
|
+
**Files to create:**
|
|
1043
|
+
- `lib/learning/analytics-exporter.js` - Export to JSON/CSV
|
|
1044
|
+
|
|
1045
|
+
**Features:**
|
|
1046
|
+
1. JSON export with complete analytics data
|
|
1047
|
+
2. CSV export with multiple files
|
|
1048
|
+
3. Export configuration options
|
|
1049
|
+
4. Export validation
|
|
1050
|
+
|
|
1051
|
+
**Estimated effort:** 2-3 hours
|
|
1052
|
+
|
|
1053
|
+
---
|
|
1054
|
+
|
|
1055
|
+
### Phase 4: Integration
|
|
1056
|
+
**Files to modify:**
|
|
1057
|
+
- `lib/commands/config.js` - Add "Analytics Dashboard" to learning menu
|
|
1058
|
+
- `lib/learning/learning-manager.js` - Integrate analytics module
|
|
1059
|
+
|
|
1060
|
+
**Features:**
|
|
1061
|
+
1. Add "Analytics Dashboard" option to learning menu
|
|
1062
|
+
2. Integrate analytics module into learning manager
|
|
1063
|
+
3. Add export shortcuts
|
|
1064
|
+
|
|
1065
|
+
**Estimated effort:** 1-2 hours
|
|
1066
|
+
|
|
1067
|
+
---
|
|
1068
|
+
|
|
1069
|
+
### Phase 5: Testing
|
|
1070
|
+
**Files to create:**
|
|
1071
|
+
- `tests/analytics.test.js` - Analytics calculations tests
|
|
1072
|
+
- `tests/analytics-view.test.js` - Dashboard UI tests (snapshot tests)
|
|
1073
|
+
- `tests/analytics-exporter.test.js` - Export functionality tests
|
|
1074
|
+
|
|
1075
|
+
**Test Coverage:**
|
|
1076
|
+
- All metrics calculation functions
|
|
1077
|
+
- Edge cases (no data, single session, large datasets)
|
|
1078
|
+
- Export format validation
|
|
1079
|
+
- Performance tests (100+ sessions)
|
|
1080
|
+
|
|
1081
|
+
**Estimated effort:** 3-4 hours
|
|
1082
|
+
|
|
1083
|
+
---
|
|
1084
|
+
|
|
1085
|
+
**Total Estimated Effort:** 13-19 hours (2-3 days)
|
|
1086
|
+
|
|
1087
|
+
---
|
|
1088
|
+
|
|
1089
|
+
## Testing Strategy
|
|
1090
|
+
|
|
1091
|
+
### Unit Tests
|
|
1092
|
+
|
|
1093
|
+
```javascript
|
|
1094
|
+
// tests/analytics.test.js
|
|
1095
|
+
|
|
1096
|
+
describe('Analytics Calculations', () => {
|
|
1097
|
+
describe('calculateOverviewStats', () => {
|
|
1098
|
+
it('should calculate correct overview statistics', () => {
|
|
1099
|
+
const stats = { totalSessions: 15, totalSkips: 45, totalAnswers: 120 };
|
|
1100
|
+
const patterns = { patterns: [{}, {}, {}] };
|
|
1101
|
+
const rules = { rules: [{}, {}] };
|
|
1102
|
+
|
|
1103
|
+
const result = calculateOverviewStats(stats, patterns, rules);
|
|
1104
|
+
|
|
1105
|
+
expect(result.totalSessions).toBe(15);
|
|
1106
|
+
expect(result.totalSkips).toBe(45);
|
|
1107
|
+
expect(result.activePatterns).toBe(3);
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
it('should handle empty data', () => {
|
|
1111
|
+
const result = calculateOverviewStats({}, { patterns: [] }, { rules: [] });
|
|
1112
|
+
expect(result.totalSessions).toBe(0);
|
|
1113
|
+
expect(result.activePatterns).toBe(0);
|
|
1114
|
+
});
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
describe('calculateSkipTrends', () => {
|
|
1118
|
+
it('should calculate skip trends for last 12 weeks', () => {
|
|
1119
|
+
const skipHistory = createMockSkipHistory(15);
|
|
1120
|
+
const trends = calculateSkipTrends(skipHistory);
|
|
1121
|
+
|
|
1122
|
+
expect(trends).toHaveLength(12);
|
|
1123
|
+
expect(trends[0]).toHaveProperty('week');
|
|
1124
|
+
expect(trends[0]).toHaveProperty('manualSkips');
|
|
1125
|
+
expect(trends[0]).toHaveProperty('skipRate');
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
it('should handle weeks with no sessions', () => {
|
|
1129
|
+
const skipHistory = { sessions: [] };
|
|
1130
|
+
const trends = calculateSkipTrends(skipHistory);
|
|
1131
|
+
|
|
1132
|
+
expect(trends).toHaveLength(12);
|
|
1133
|
+
expect(trends[0].manualSkips).toBe(0);
|
|
1134
|
+
});
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
describe('calculateCategoryPreferences', () => {
|
|
1138
|
+
it('should calculate skip rates by category', () => {
|
|
1139
|
+
const skipHistory = createMockSkipHistoryWithCategories();
|
|
1140
|
+
const answerHistory = createMockAnswerHistoryWithCategories();
|
|
1141
|
+
|
|
1142
|
+
const prefs = calculateCategoryPreferences(skipHistory, answerHistory);
|
|
1143
|
+
|
|
1144
|
+
expect(prefs[0]).toHaveProperty('category');
|
|
1145
|
+
expect(prefs[0]).toHaveProperty('skipRate');
|
|
1146
|
+
expect(prefs[0]).toHaveProperty('level');
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
it('should sort categories by skip rate descending', () => {
|
|
1150
|
+
const prefs = calculateCategoryPreferences(mockSkipHistory, mockAnswerHistory);
|
|
1151
|
+
|
|
1152
|
+
for (let i = 0; i < prefs.length - 1; i++) {
|
|
1153
|
+
expect(prefs[i].skipRate).toBeGreaterThanOrEqual(prefs[i + 1].skipRate);
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
describe('calculateEffectiveness', () => {
|
|
1159
|
+
it('should calculate time saved correctly', () => {
|
|
1160
|
+
const skipHistory = { sessions: [{ skips: [{ reason: 'filtered' }] }] };
|
|
1161
|
+
const result = calculateEffectiveness(skipHistory, [], []);
|
|
1162
|
+
|
|
1163
|
+
expect(result.timeSavedMinutes).toBeGreaterThan(0);
|
|
1164
|
+
expect(result.questionsFiltered).toBe(1);
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
it('should calculate pattern accuracy', () => {
|
|
1168
|
+
const patterns = [
|
|
1169
|
+
{ userApproved: true },
|
|
1170
|
+
{ userApproved: true },
|
|
1171
|
+
{ userApproved: false }
|
|
1172
|
+
];
|
|
1173
|
+
|
|
1174
|
+
const result = calculateEffectiveness({}, patterns, []);
|
|
1175
|
+
expect(result.patternAccuracy).toBe(67); // 2/3 = 66.67%
|
|
1176
|
+
});
|
|
1177
|
+
});
|
|
1178
|
+
});
|
|
1179
|
+
```
|
|
1180
|
+
|
|
1181
|
+
### Integration Tests
|
|
1182
|
+
|
|
1183
|
+
```javascript
|
|
1184
|
+
// tests/analytics-integration.test.js
|
|
1185
|
+
|
|
1186
|
+
describe('Analytics Integration', () => {
|
|
1187
|
+
it('should generate complete analytics from real data', async () => {
|
|
1188
|
+
const projectPath = createTestProject();
|
|
1189
|
+
await populateWithMockData(projectPath, 15);
|
|
1190
|
+
|
|
1191
|
+
const analytics = await generateAnalytics(projectPath);
|
|
1192
|
+
|
|
1193
|
+
expect(analytics).toHaveProperty('overview');
|
|
1194
|
+
expect(analytics).toHaveProperty('skipTrends');
|
|
1195
|
+
expect(analytics).toHaveProperty('categoryPreferences');
|
|
1196
|
+
expect(analytics).toHaveProperty('effectiveness');
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
it('should export analytics to JSON', async () => {
|
|
1200
|
+
const projectPath = createTestProject();
|
|
1201
|
+
const analytics = await generateAnalytics(projectPath);
|
|
1202
|
+
|
|
1203
|
+
const exported = await exportAnalytics(analytics, 'json');
|
|
1204
|
+
const parsed = JSON.parse(exported);
|
|
1205
|
+
|
|
1206
|
+
expect(parsed.version).toBe('1.0');
|
|
1207
|
+
expect(parsed).toHaveProperty('overview');
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
it('should export analytics to CSV', async () => {
|
|
1211
|
+
const projectPath = createTestProject();
|
|
1212
|
+
const analytics = await generateAnalytics(projectPath);
|
|
1213
|
+
|
|
1214
|
+
const files = await exportAnalytics(analytics, 'csv');
|
|
1215
|
+
|
|
1216
|
+
expect(files).toHaveProperty('overview.csv');
|
|
1217
|
+
expect(files).toHaveProperty('trends.csv');
|
|
1218
|
+
expect(files).toHaveProperty('categories.csv');
|
|
1219
|
+
});
|
|
1220
|
+
});
|
|
1221
|
+
```
|
|
1222
|
+
|
|
1223
|
+
### Performance Tests
|
|
1224
|
+
|
|
1225
|
+
```javascript
|
|
1226
|
+
// tests/analytics-performance.test.js
|
|
1227
|
+
|
|
1228
|
+
describe('Analytics Performance', () => {
|
|
1229
|
+
it('should handle 100 sessions in < 2 seconds', async () => {
|
|
1230
|
+
const projectPath = createTestProject();
|
|
1231
|
+
await populateWithMockData(projectPath, 100);
|
|
1232
|
+
|
|
1233
|
+
const startTime = Date.now();
|
|
1234
|
+
await generateAnalytics(projectPath);
|
|
1235
|
+
const elapsed = Date.now() - startTime;
|
|
1236
|
+
|
|
1237
|
+
expect(elapsed).toBeLessThan(2000); // < 2 seconds
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
it('should handle 500 patterns efficiently', () => {
|
|
1241
|
+
const patterns = createMockPatterns(500);
|
|
1242
|
+
|
|
1243
|
+
const startTime = Date.now();
|
|
1244
|
+
const distribution = calculatePatternDistribution(patterns);
|
|
1245
|
+
const elapsed = Date.now() - startTime;
|
|
1246
|
+
|
|
1247
|
+
expect(elapsed).toBeLessThan(500); // < 500ms
|
|
1248
|
+
});
|
|
1249
|
+
});
|
|
1250
|
+
```
|
|
1251
|
+
|
|
1252
|
+
---
|
|
1253
|
+
|
|
1254
|
+
## Success Criteria
|
|
1255
|
+
|
|
1256
|
+
✅ **Functional Requirements:**
|
|
1257
|
+
- Dashboard displays 10+ meaningful metrics
|
|
1258
|
+
- All calculations are accurate
|
|
1259
|
+
- Export works in both JSON and CSV
|
|
1260
|
+
- Performance < 2 seconds for 100 sessions
|
|
1261
|
+
- UI is clear and intuitive
|
|
1262
|
+
|
|
1263
|
+
✅ **Code Quality:**
|
|
1264
|
+
- 80%+ test coverage for analytics module
|
|
1265
|
+
- All edge cases handled (no data, single session, etc.)
|
|
1266
|
+
- Code follows existing patterns in codebase
|
|
1267
|
+
- Comprehensive JSDoc documentation
|
|
1268
|
+
|
|
1269
|
+
✅ **User Experience:**
|
|
1270
|
+
- Dashboard loads instantly
|
|
1271
|
+
- Navigation is intuitive
|
|
1272
|
+
- Charts render correctly in all terminals
|
|
1273
|
+
- Export files are well-formatted
|
|
1274
|
+
|
|
1275
|
+
✅ **Integration:**
|
|
1276
|
+
- Seamlessly integrates into `adf config` → Learning System menu
|
|
1277
|
+
- No breaking changes to existing functionality
|
|
1278
|
+
- Compatible with all other Phase 6 features
|
|
1279
|
+
|
|
1280
|
+
---
|
|
1281
|
+
|
|
1282
|
+
## Future Enhancements (Post-v0.6.0)
|
|
1283
|
+
|
|
1284
|
+
1. **Visual Enhancements**
|
|
1285
|
+
- Web-based dashboard (HTML export)
|
|
1286
|
+
- More chart types (line charts, scatter plots)
|
|
1287
|
+
- Color themes
|
|
1288
|
+
|
|
1289
|
+
2. **Advanced Analytics**
|
|
1290
|
+
- Predictive analytics (which patterns likely to decay)
|
|
1291
|
+
- Anomaly detection (unusual skip behavior)
|
|
1292
|
+
- Session comparison
|
|
1293
|
+
- A/B testing for learned rules
|
|
1294
|
+
|
|
1295
|
+
3. **Team Features**
|
|
1296
|
+
- Aggregate analytics across team members
|
|
1297
|
+
- Team benchmarking
|
|
1298
|
+
- Shared insights
|
|
1299
|
+
|
|
1300
|
+
4. **Real-Time Analytics**
|
|
1301
|
+
- Live dashboard during interview
|
|
1302
|
+
- Real-time effectiveness monitoring
|
|
1303
|
+
- In-session recommendations
|
|
1304
|
+
|
|
1305
|
+
---
|
|
1306
|
+
|
|
1307
|
+
## Appendix
|
|
1308
|
+
|
|
1309
|
+
### A. Mock Data for Testing
|
|
1310
|
+
|
|
1311
|
+
```javascript
|
|
1312
|
+
function createMockSkipHistory(sessionCount) {
|
|
1313
|
+
const sessions = [];
|
|
1314
|
+
|
|
1315
|
+
for (let i = 0; i < sessionCount; i++) {
|
|
1316
|
+
const sessionDate = new Date();
|
|
1317
|
+
sessionDate.setDate(sessionDate.getDate() - (i * 2));
|
|
1318
|
+
|
|
1319
|
+
sessions.push({
|
|
1320
|
+
sessionId: `session_${i}`,
|
|
1321
|
+
timestamp: sessionDate.toISOString(),
|
|
1322
|
+
projectType: i % 2 === 0 ? 'cli-tool' : 'web-app',
|
|
1323
|
+
frameworks: i % 2 === 0 ? ['commander'] : ['react'],
|
|
1324
|
+
skips: generateRandomSkips(3, 8),
|
|
1325
|
+
answers: []
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
return { version: '1.0', sessions };
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
function generateRandomSkips(min, max) {
|
|
1333
|
+
const count = Math.floor(Math.random() * (max - min + 1)) + min;
|
|
1334
|
+
const skips = [];
|
|
1335
|
+
|
|
1336
|
+
const categories = ['deployment', 'UI/UX', 'documentation', 'architecture', 'core-features'];
|
|
1337
|
+
const reasons = ['manual', 'filtered'];
|
|
1338
|
+
|
|
1339
|
+
for (let i = 0; i < count; i++) {
|
|
1340
|
+
skips.push({
|
|
1341
|
+
questionId: `q_${Math.floor(Math.random() * 50)}`,
|
|
1342
|
+
text: `Sample question ${i}`,
|
|
1343
|
+
category: categories[Math.floor(Math.random() * categories.length)],
|
|
1344
|
+
phase: 'planning',
|
|
1345
|
+
action: 'skipped',
|
|
1346
|
+
reason: reasons[Math.floor(Math.random() * reasons.length)],
|
|
1347
|
+
timeViewed: Math.random() * 5,
|
|
1348
|
+
relevanceScore: Math.floor(Math.random() * 100)
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
return skips;
|
|
1353
|
+
}
|
|
1354
|
+
```
|
|
1355
|
+
|
|
1356
|
+
### B. ASCII Chart Examples
|
|
1357
|
+
|
|
1358
|
+
```
|
|
1359
|
+
Bar Chart Example:
|
|
1360
|
+
deployment ████████████████████ 95%
|
|
1361
|
+
UI/UX ██████████████░░░░░░ 70%
|
|
1362
|
+
documentation ████████░░░░░░░░░░░░ 40%
|
|
1363
|
+
|
|
1364
|
+
Pie Chart Example:
|
|
1365
|
+
High: ████████░░ 8 (53%)
|
|
1366
|
+
Medium: ████░░░░░░ 4 (27%)
|
|
1367
|
+
Low: ██░░░░░░░░ 2 (13%)
|
|
1368
|
+
|
|
1369
|
+
Line Chart Example (Trend):
|
|
1370
|
+
20 | ╭──╮
|
|
1371
|
+
15 | ╭─────────╯ ╰╮
|
|
1372
|
+
10 | ╭─╯ ╰─╮
|
|
1373
|
+
5 | ╭╯ ╰─
|
|
1374
|
+
0 └──────────────────────
|
|
1375
|
+
W1 W4 W7 W10 W12
|
|
1376
|
+
```
|
|
1377
|
+
|
|
1378
|
+
---
|
|
1379
|
+
|
|
1380
|
+
**End of Design Specification**
|
|
1381
|
+
|
|
1382
|
+
**Status:** ✅ Design Complete - Ready for Implementation
|
|
1383
|
+
**Next Steps:** Review design → Create implementation task → Begin coding
|