@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,557 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mock Data Generator for Learning Analytics Dashboard Testing
|
|
5
|
+
*
|
|
6
|
+
* Generates realistic learning data for testing the Analytics Dashboard
|
|
7
|
+
* with various scenarios: empty state, new user, active user, power user, edge cases
|
|
8
|
+
*
|
|
9
|
+
* Usage: node scripts/generate-test-data.js <scenario>
|
|
10
|
+
* Scenarios: empty, new, active, power, edge
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs-extra');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const crypto = require('crypto');
|
|
16
|
+
|
|
17
|
+
// Test data output path
|
|
18
|
+
const OUTPUT_DIR = path.join(process.cwd(), '.adf', 'learning');
|
|
19
|
+
|
|
20
|
+
// Sample questions database
|
|
21
|
+
const QUESTIONS = {
|
|
22
|
+
overview: [
|
|
23
|
+
{ id: 'q_core_purpose', text: 'What is the core purpose of this project?', category: 'overview' },
|
|
24
|
+
{ id: 'q_target_users', text: 'Who are the target users?', category: 'overview' },
|
|
25
|
+
{ id: 'q_key_features', text: 'What are the key features?', category: 'overview' }
|
|
26
|
+
],
|
|
27
|
+
deployment: [
|
|
28
|
+
{ id: 'q_deployment_strategy', text: 'What deployment strategy will you use?', category: 'deployment' },
|
|
29
|
+
{ id: 'q_hosting_provider', text: 'Which hosting provider?', category: 'deployment' },
|
|
30
|
+
{ id: 'q_cicd_pipeline', text: 'What CI/CD pipeline?', category: 'deployment' }
|
|
31
|
+
],
|
|
32
|
+
testing: [
|
|
33
|
+
{ id: 'q_testing_framework', text: 'What testing framework?', category: 'testing' },
|
|
34
|
+
{ id: 'q_test_coverage', text: 'What is your target test coverage?', category: 'testing' },
|
|
35
|
+
{ id: 'q_e2e_testing', text: 'Will you implement E2E testing?', category: 'testing' }
|
|
36
|
+
],
|
|
37
|
+
security: [
|
|
38
|
+
{ id: 'q_auth_method', text: 'What authentication method?', category: 'security' },
|
|
39
|
+
{ id: 'q_data_encryption', text: 'How will data be encrypted?', category: 'security' },
|
|
40
|
+
{ id: 'q_security_audit', text: 'Will you conduct security audits?', category: 'security' }
|
|
41
|
+
],
|
|
42
|
+
database: [
|
|
43
|
+
{ id: 'q_database_type', text: 'What type of database?', category: 'database' },
|
|
44
|
+
{ id: 'q_orm_choice', text: 'Which ORM will you use?', category: 'database' },
|
|
45
|
+
{ id: 'q_data_backup', text: 'What is your backup strategy?', category: 'database' }
|
|
46
|
+
]
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Flatten questions for easy access
|
|
50
|
+
const ALL_QUESTIONS = Object.values(QUESTIONS).flat();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generate UUID
|
|
54
|
+
*/
|
|
55
|
+
function uuid() {
|
|
56
|
+
return crypto.randomUUID();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Generate timestamp for days ago
|
|
61
|
+
*/
|
|
62
|
+
function daysAgo(days) {
|
|
63
|
+
const date = new Date();
|
|
64
|
+
date.setDate(date.getDate() - days);
|
|
65
|
+
return date.toISOString();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Random element from array
|
|
70
|
+
*/
|
|
71
|
+
function randomFrom(array) {
|
|
72
|
+
return array[Math.floor(Math.random() * array.length)];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Generate a session with skips and answers
|
|
77
|
+
*/
|
|
78
|
+
function generateSession(daysBack, skipRate = 0.3, skipCategories = []) {
|
|
79
|
+
const sessionId = uuid();
|
|
80
|
+
const timestamp = daysAgo(daysBack);
|
|
81
|
+
const skips = [];
|
|
82
|
+
const answers = [];
|
|
83
|
+
|
|
84
|
+
// Process each question
|
|
85
|
+
for (const question of ALL_QUESTIONS) {
|
|
86
|
+
const shouldSkip = skipCategories.includes(question.category) || Math.random() < skipRate;
|
|
87
|
+
|
|
88
|
+
if (shouldSkip) {
|
|
89
|
+
skips.push({
|
|
90
|
+
questionId: question.id,
|
|
91
|
+
text: question.text,
|
|
92
|
+
category: question.category,
|
|
93
|
+
phase: 'discovery',
|
|
94
|
+
action: 'skipped',
|
|
95
|
+
reason: skipCategories.includes(question.category) ? 'filtered' : 'manual',
|
|
96
|
+
timeViewed: Math.random() * 5 + 1,
|
|
97
|
+
relevanceScore: Math.floor(Math.random() * 50) + 20,
|
|
98
|
+
skipReason: 'Not relevant for this project'
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
answers.push({
|
|
102
|
+
questionId: question.id,
|
|
103
|
+
text: question.text,
|
|
104
|
+
category: question.category,
|
|
105
|
+
phase: 'discovery',
|
|
106
|
+
action: 'answered',
|
|
107
|
+
answerLength: Math.floor(Math.random() * 300) + 50,
|
|
108
|
+
wordCount: Math.floor(Math.random() * 50) + 10,
|
|
109
|
+
timeSpent: Math.random() * 60 + 15,
|
|
110
|
+
qualityScore: Math.floor(Math.random() * 30) + 70,
|
|
111
|
+
richness: Math.random() * 0.3 + 0.6,
|
|
112
|
+
relevanceScore: Math.floor(Math.random() * 20) + 80
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
skipSession: {
|
|
119
|
+
sessionId,
|
|
120
|
+
timestamp,
|
|
121
|
+
projectType: 'web-app',
|
|
122
|
+
frameworks: ['react', 'express'],
|
|
123
|
+
languages: ['javascript', 'typescript'],
|
|
124
|
+
skips,
|
|
125
|
+
answers: []
|
|
126
|
+
},
|
|
127
|
+
answerSession: {
|
|
128
|
+
sessionId,
|
|
129
|
+
timestamp,
|
|
130
|
+
projectType: 'web-app',
|
|
131
|
+
frameworks: ['react', 'express'],
|
|
132
|
+
answers
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Generate patterns from session data
|
|
139
|
+
*/
|
|
140
|
+
function generatePatterns(skipHistory, confidence = 90) {
|
|
141
|
+
const patterns = [];
|
|
142
|
+
const questionSkipCounts = {};
|
|
143
|
+
|
|
144
|
+
// Count skips per question
|
|
145
|
+
for (const session of skipHistory.sessions) {
|
|
146
|
+
for (const skip of session.skips) {
|
|
147
|
+
if (!questionSkipCounts[skip.questionId]) {
|
|
148
|
+
questionSkipCounts[skip.questionId] = {
|
|
149
|
+
count: 0,
|
|
150
|
+
question: skip
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
questionSkipCounts[skip.questionId].count++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Create patterns for frequently skipped questions
|
|
158
|
+
const totalSessions = skipHistory.sessions.length;
|
|
159
|
+
for (const [questionId, data] of Object.entries(questionSkipCounts)) {
|
|
160
|
+
if (data.count >= 3) { // Minimum 3 occurrences for pattern
|
|
161
|
+
const patternConfidence = Math.min(100, Math.round((data.count / totalSessions) * 100));
|
|
162
|
+
const daysOld = Math.floor(Math.random() * 60) + 10; // 10-70 days old
|
|
163
|
+
|
|
164
|
+
patterns.push({
|
|
165
|
+
id: `pattern_skip_${questionId}`,
|
|
166
|
+
type: 'consistent_skip',
|
|
167
|
+
questionId,
|
|
168
|
+
questionText: data.question.text,
|
|
169
|
+
category: data.question.category,
|
|
170
|
+
confidence: patternConfidence,
|
|
171
|
+
sessionsAnalyzed: totalSessions,
|
|
172
|
+
skipCount: data.count,
|
|
173
|
+
recommendation: 'Auto-filter this question',
|
|
174
|
+
status: 'active',
|
|
175
|
+
userApproved: patternConfidence >= 85,
|
|
176
|
+
createdAt: daysAgo(daysOld),
|
|
177
|
+
lastSeen: daysAgo(Math.floor(Math.random() * 10)),
|
|
178
|
+
timesApplied: data.count,
|
|
179
|
+
timesRenewed: Math.floor(data.count / 3),
|
|
180
|
+
lastDecayCalculation: daysAgo(1)
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return patterns;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Generate learned rules from patterns
|
|
190
|
+
*/
|
|
191
|
+
function generateRules(patterns) {
|
|
192
|
+
return patterns
|
|
193
|
+
.filter(p => p.userApproved)
|
|
194
|
+
.map(pattern => ({
|
|
195
|
+
id: `rule_${pattern.id}`,
|
|
196
|
+
patternId: pattern.id,
|
|
197
|
+
type: 'question_filter',
|
|
198
|
+
enabled: true,
|
|
199
|
+
createdAt: pattern.createdAt,
|
|
200
|
+
appliedCount: pattern.timesApplied,
|
|
201
|
+
lastApplied: pattern.lastSeen
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Calculate stats
|
|
207
|
+
*/
|
|
208
|
+
function calculateStats(skipHistory, answerHistory, patterns, rules) {
|
|
209
|
+
const totalSessions = skipHistory.sessions.length;
|
|
210
|
+
const totalSkips = skipHistory.sessions.reduce((sum, s) => sum + s.skips.length, 0);
|
|
211
|
+
const totalAnswers = answerHistory.sessions.reduce((sum, s) => sum + s.answers.length, 0);
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
version: '1.0',
|
|
215
|
+
totalSessions,
|
|
216
|
+
totalSkips,
|
|
217
|
+
totalAnswers,
|
|
218
|
+
patternsDetected: patterns.length,
|
|
219
|
+
rulesApplied: rules.filter(r => r.enabled).length,
|
|
220
|
+
lastUpdated: new Date().toISOString()
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Generate default config
|
|
226
|
+
*/
|
|
227
|
+
function generateConfig() {
|
|
228
|
+
return {
|
|
229
|
+
version: '1.0',
|
|
230
|
+
enabled: true,
|
|
231
|
+
trackSkips: true,
|
|
232
|
+
trackAnswers: true,
|
|
233
|
+
minSessionsForPattern: 3,
|
|
234
|
+
minConfidenceForAutoFilter: 75,
|
|
235
|
+
maxPatternsToStore: 100,
|
|
236
|
+
autoApproveHighConfidence: false,
|
|
237
|
+
decayConfig: {
|
|
238
|
+
enabled: true,
|
|
239
|
+
decayRates: {
|
|
240
|
+
high: 0.075,
|
|
241
|
+
medium: 0.15,
|
|
242
|
+
low: 0.225
|
|
243
|
+
},
|
|
244
|
+
removalThresholds: {
|
|
245
|
+
minConfidence: 40,
|
|
246
|
+
maxInactiveMonths: 6
|
|
247
|
+
},
|
|
248
|
+
renewalBonus: 10,
|
|
249
|
+
protectionMultiplier: 0.5
|
|
250
|
+
},
|
|
251
|
+
lastUpdated: new Date().toISOString()
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ============================================================================
|
|
256
|
+
// SCENARIO GENERATORS
|
|
257
|
+
// ============================================================================
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Scenario 1: Empty State
|
|
261
|
+
*/
|
|
262
|
+
function generateEmptyScenario() {
|
|
263
|
+
console.log('📭 Generating Empty State scenario...');
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
skipHistory: { version: '1.0', sessions: [] },
|
|
267
|
+
answerHistory: { version: '1.0', sessions: [] },
|
|
268
|
+
patterns: { version: '1.0', lastUpdated: new Date().toISOString(), patterns: [] },
|
|
269
|
+
rules: { version: '1.0', rules: [] },
|
|
270
|
+
stats: {
|
|
271
|
+
version: '1.0',
|
|
272
|
+
totalSessions: 0,
|
|
273
|
+
totalSkips: 0,
|
|
274
|
+
totalAnswers: 0,
|
|
275
|
+
patternsDetected: 0,
|
|
276
|
+
rulesApplied: 0,
|
|
277
|
+
lastUpdated: new Date().toISOString()
|
|
278
|
+
},
|
|
279
|
+
config: generateConfig()
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Scenario 2: New User (2 sessions, minimal data)
|
|
285
|
+
*/
|
|
286
|
+
function generateNewUserScenario() {
|
|
287
|
+
console.log('🆕 Generating New User scenario (2 sessions)...');
|
|
288
|
+
|
|
289
|
+
const skipSessions = [];
|
|
290
|
+
const answerSessions = [];
|
|
291
|
+
|
|
292
|
+
// Generate 2 sessions
|
|
293
|
+
for (let i = 0; i < 2; i++) {
|
|
294
|
+
const session = generateSession(i * 7, 0.25, []); // 25% skip rate, no filters
|
|
295
|
+
skipSessions.push(session.skipSession);
|
|
296
|
+
answerSessions.push(session.answerSession);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const skipHistory = { version: '1.0', sessions: skipSessions };
|
|
300
|
+
const answerHistory = { version: '1.0', sessions: answerSessions };
|
|
301
|
+
const patterns = { version: '1.0', lastUpdated: new Date().toISOString(), patterns: [] }; // No patterns yet
|
|
302
|
+
const rules = { version: '1.0', rules: [] };
|
|
303
|
+
const stats = calculateStats(skipHistory, answerHistory, [], []);
|
|
304
|
+
const config = generateConfig();
|
|
305
|
+
|
|
306
|
+
return { skipHistory, answerHistory, patterns, rules, stats, config };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Scenario 3: Active User (12 sessions, 5 patterns, varied skip rates)
|
|
311
|
+
*/
|
|
312
|
+
function generateActiveUserScenario() {
|
|
313
|
+
console.log('💼 Generating Active User scenario (12 sessions, 5+ patterns)...');
|
|
314
|
+
|
|
315
|
+
const skipSessions = [];
|
|
316
|
+
const answerSessions = [];
|
|
317
|
+
|
|
318
|
+
// Categories to consistently skip (will create patterns)
|
|
319
|
+
const skipCategories = ['deployment', 'security'];
|
|
320
|
+
|
|
321
|
+
// Generate 12 sessions over 8 weeks
|
|
322
|
+
for (let i = 0; i < 12; i++) {
|
|
323
|
+
const daysBack = i * 4; // Every 4 days
|
|
324
|
+
const skipRate = 0.3 + Math.random() * 0.2; // 30-50% skip rate
|
|
325
|
+
const session = generateSession(daysBack, skipRate, skipCategories);
|
|
326
|
+
skipSessions.push(session.skipSession);
|
|
327
|
+
answerSessions.push(session.answerSession);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const skipHistory = { version: '1.0', sessions: skipSessions };
|
|
331
|
+
const answerHistory = { version: '1.0', sessions: answerSessions };
|
|
332
|
+
const allPatterns = generatePatterns(skipHistory, 85);
|
|
333
|
+
const patterns = {
|
|
334
|
+
version: '1.0',
|
|
335
|
+
lastUpdated: new Date().toISOString(),
|
|
336
|
+
lastDecayCheck: new Date().toISOString(),
|
|
337
|
+
patterns: allPatterns
|
|
338
|
+
};
|
|
339
|
+
const allRules = generateRules(allPatterns);
|
|
340
|
+
const rules = { version: '1.0', rules: allRules };
|
|
341
|
+
const stats = calculateStats(skipHistory, answerHistory, allPatterns, allRules);
|
|
342
|
+
const config = generateConfig();
|
|
343
|
+
|
|
344
|
+
return { skipHistory, answerHistory, patterns, rules, stats, config };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Scenario 4: Power User (20 sessions, 10+ patterns, decay effects)
|
|
349
|
+
*/
|
|
350
|
+
function generatePowerUserScenario() {
|
|
351
|
+
console.log('🚀 Generating Power User scenario (20 sessions, 10+ patterns, decay effects)...');
|
|
352
|
+
|
|
353
|
+
const skipSessions = [];
|
|
354
|
+
const answerSessions = [];
|
|
355
|
+
|
|
356
|
+
// More categories to skip
|
|
357
|
+
const skipCategories = ['deployment', 'security', 'testing'];
|
|
358
|
+
|
|
359
|
+
// Generate 20 sessions over 12 weeks
|
|
360
|
+
for (let i = 0; i < 20; i++) {
|
|
361
|
+
const daysBack = i * 4; // Every 4 days
|
|
362
|
+
const skipRate = 0.4 + Math.random() * 0.15; // 40-55% skip rate
|
|
363
|
+
const session = generateSession(daysBack, skipRate, skipCategories);
|
|
364
|
+
skipSessions.push(session.skipSession);
|
|
365
|
+
answerSessions.push(session.answerSession);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const skipHistory = { version: '1.0', sessions: skipSessions };
|
|
369
|
+
const answerHistory = { version: '1.0', sessions: answerSessions };
|
|
370
|
+
const allPatterns = generatePatterns(skipHistory, 90);
|
|
371
|
+
|
|
372
|
+
// Add some patterns with decay effects
|
|
373
|
+
allPatterns.push({
|
|
374
|
+
id: 'pattern_old_critical',
|
|
375
|
+
type: 'consistent_skip',
|
|
376
|
+
questionId: 'q_old_question',
|
|
377
|
+
questionText: 'Old question (critical decay)',
|
|
378
|
+
category: 'testing',
|
|
379
|
+
confidence: 45,
|
|
380
|
+
sessionsAnalyzed: 20,
|
|
381
|
+
skipCount: 9,
|
|
382
|
+
recommendation: 'Auto-filter this question',
|
|
383
|
+
status: 'active',
|
|
384
|
+
userApproved: false,
|
|
385
|
+
createdAt: daysAgo(180), // 6 months old
|
|
386
|
+
lastSeen: daysAgo(120), // Last seen 4 months ago
|
|
387
|
+
timesApplied: 9,
|
|
388
|
+
timesRenewed: 0,
|
|
389
|
+
lastDecayCalculation: daysAgo(1)
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
const patterns = {
|
|
393
|
+
version: '1.0',
|
|
394
|
+
lastUpdated: new Date().toISOString(),
|
|
395
|
+
lastDecayCheck: new Date().toISOString(),
|
|
396
|
+
patterns: allPatterns
|
|
397
|
+
};
|
|
398
|
+
const allRules = generateRules(allPatterns);
|
|
399
|
+
const rules = { version: '1.0', rules: allRules };
|
|
400
|
+
const stats = calculateStats(skipHistory, answerHistory, allPatterns, allRules);
|
|
401
|
+
const config = generateConfig();
|
|
402
|
+
|
|
403
|
+
return { skipHistory, answerHistory, patterns, rules, stats, config };
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Scenario 5: Edge Cases (100% skip rates, critical patterns, large dataset)
|
|
408
|
+
*/
|
|
409
|
+
function generateEdgeCasesScenario() {
|
|
410
|
+
console.log('⚠️ Generating Edge Cases scenario (extreme values, edge conditions)...');
|
|
411
|
+
|
|
412
|
+
const skipSessions = [];
|
|
413
|
+
const answerSessions = [];
|
|
414
|
+
|
|
415
|
+
// Generate 15 sessions
|
|
416
|
+
for (let i = 0; i < 15; i++) {
|
|
417
|
+
const daysBack = i * 5;
|
|
418
|
+
const skipRate = i % 3 === 0 ? 1.0 : 0.3; // Some sessions with 100% skip rate
|
|
419
|
+
const skipCategories = i < 5 ? ['deployment', 'security', 'database'] : [];
|
|
420
|
+
const session = generateSession(daysBack, skipRate, skipCategories);
|
|
421
|
+
skipSessions.push(session.skipSession);
|
|
422
|
+
answerSessions.push(session.answerSession);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const skipHistory = { version: '1.0', sessions: skipSessions };
|
|
426
|
+
const answerHistory = { version: '1.0', sessions: answerSessions };
|
|
427
|
+
const allPatterns = generatePatterns(skipHistory, 95);
|
|
428
|
+
|
|
429
|
+
// Add edge case patterns
|
|
430
|
+
allPatterns.push(
|
|
431
|
+
{
|
|
432
|
+
id: 'pattern_100pct_skip',
|
|
433
|
+
type: 'consistent_skip',
|
|
434
|
+
questionId: 'q_always_skip',
|
|
435
|
+
questionText: 'Always skipped question (100% skip rate)',
|
|
436
|
+
category: 'deployment',
|
|
437
|
+
confidence: 100,
|
|
438
|
+
sessionsAnalyzed: 15,
|
|
439
|
+
skipCount: 15,
|
|
440
|
+
recommendation: 'Auto-filter this question',
|
|
441
|
+
status: 'active',
|
|
442
|
+
userApproved: true,
|
|
443
|
+
createdAt: daysAgo(45),
|
|
444
|
+
lastSeen: daysAgo(1),
|
|
445
|
+
timesApplied: 15,
|
|
446
|
+
timesRenewed: 5,
|
|
447
|
+
lastDecayCalculation: daysAgo(1)
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
id: 'pattern_very_old',
|
|
451
|
+
type: 'consistent_skip',
|
|
452
|
+
questionId: 'q_very_old',
|
|
453
|
+
questionText: 'Very old inactive pattern',
|
|
454
|
+
category: 'security',
|
|
455
|
+
confidence: 35,
|
|
456
|
+
sessionsAnalyzed: 15,
|
|
457
|
+
skipCount: 5,
|
|
458
|
+
recommendation: 'Auto-filter this question',
|
|
459
|
+
status: 'active',
|
|
460
|
+
userApproved: false,
|
|
461
|
+
createdAt: daysAgo(365), // 1 year old
|
|
462
|
+
lastSeen: daysAgo(200), // Last seen 200 days ago
|
|
463
|
+
timesApplied: 5,
|
|
464
|
+
timesRenewed: 0,
|
|
465
|
+
lastDecayCalculation: daysAgo(1)
|
|
466
|
+
}
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
const patterns = {
|
|
470
|
+
version: '1.0',
|
|
471
|
+
lastUpdated: new Date().toISOString(),
|
|
472
|
+
lastDecayCheck: new Date().toISOString(),
|
|
473
|
+
patterns: allPatterns
|
|
474
|
+
};
|
|
475
|
+
const allRules = generateRules(allPatterns);
|
|
476
|
+
const rules = { version: '1.0', rules: allRules };
|
|
477
|
+
const stats = calculateStats(skipHistory, answerHistory, allPatterns, allRules);
|
|
478
|
+
const config = generateConfig();
|
|
479
|
+
|
|
480
|
+
return { skipHistory, answerHistory, patterns, rules, stats, config };
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// ============================================================================
|
|
484
|
+
// MAIN EXECUTION
|
|
485
|
+
// ============================================================================
|
|
486
|
+
|
|
487
|
+
async function writeData(data) {
|
|
488
|
+
await fs.ensureDir(OUTPUT_DIR);
|
|
489
|
+
|
|
490
|
+
await fs.writeJSON(path.join(OUTPUT_DIR, 'skip-history.json'), data.skipHistory, { spaces: 2 });
|
|
491
|
+
await fs.writeJSON(path.join(OUTPUT_DIR, 'answer-history.json'), data.answerHistory, { spaces: 2 });
|
|
492
|
+
await fs.writeJSON(path.join(OUTPUT_DIR, 'patterns.json'), data.patterns, { spaces: 2 });
|
|
493
|
+
await fs.writeJSON(path.join(OUTPUT_DIR, 'learned-rules.json'), data.rules, { spaces: 2 });
|
|
494
|
+
await fs.writeJSON(path.join(OUTPUT_DIR, 'stats.json'), data.stats, { spaces: 2 });
|
|
495
|
+
await fs.writeJSON(path.join(OUTPUT_DIR, 'config.json'), data.config, { spaces: 2 });
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
async function main() {
|
|
499
|
+
const scenario = process.argv[2] || 'active';
|
|
500
|
+
|
|
501
|
+
console.log('');
|
|
502
|
+
console.log('🧪 Learning Analytics Test Data Generator');
|
|
503
|
+
console.log('==========================================');
|
|
504
|
+
console.log('');
|
|
505
|
+
|
|
506
|
+
let data;
|
|
507
|
+
|
|
508
|
+
switch (scenario.toLowerCase()) {
|
|
509
|
+
case 'empty':
|
|
510
|
+
data = generateEmptyScenario();
|
|
511
|
+
break;
|
|
512
|
+
case 'new':
|
|
513
|
+
data = generateNewUserScenario();
|
|
514
|
+
break;
|
|
515
|
+
case 'active':
|
|
516
|
+
data = generateActiveUserScenario();
|
|
517
|
+
break;
|
|
518
|
+
case 'power':
|
|
519
|
+
data = generatePowerUserScenario();
|
|
520
|
+
break;
|
|
521
|
+
case 'edge':
|
|
522
|
+
data = generateEdgeCasesScenario();
|
|
523
|
+
break;
|
|
524
|
+
default:
|
|
525
|
+
console.error(`❌ Unknown scenario: ${scenario}`);
|
|
526
|
+
console.log('');
|
|
527
|
+
console.log('Available scenarios:');
|
|
528
|
+
console.log(' empty - Empty state (no data)');
|
|
529
|
+
console.log(' new - New user (2 sessions, no patterns)');
|
|
530
|
+
console.log(' active - Active user (12 sessions, 5+ patterns)');
|
|
531
|
+
console.log(' power - Power user (20 sessions, 10+ patterns, decay effects)');
|
|
532
|
+
console.log(' edge - Edge cases (100% skip rates, critical patterns)');
|
|
533
|
+
console.log('');
|
|
534
|
+
process.exit(1);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
await writeData(data);
|
|
538
|
+
|
|
539
|
+
console.log('');
|
|
540
|
+
console.log(`✅ Test data generated: ${scenario}`);
|
|
541
|
+
console.log(`📁 Location: ${OUTPUT_DIR}`);
|
|
542
|
+
console.log('');
|
|
543
|
+
console.log('📊 Summary:');
|
|
544
|
+
console.log(` Sessions: ${data.stats.totalSessions}`);
|
|
545
|
+
console.log(` Skips: ${data.stats.totalSkips}`);
|
|
546
|
+
console.log(` Answers: ${data.stats.totalAnswers}`);
|
|
547
|
+
console.log(` Patterns: ${data.stats.patternsDetected}`);
|
|
548
|
+
console.log(` Rules: ${data.stats.rulesApplied}`);
|
|
549
|
+
console.log('');
|
|
550
|
+
console.log('🚀 Ready to test! Run: adf config → Learning System → Analytics Dashboard');
|
|
551
|
+
console.log('');
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
main().catch(error => {
|
|
555
|
+
console.error('❌ Error:', error.message);
|
|
556
|
+
process.exit(1);
|
|
557
|
+
});
|