@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.
@@ -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
+ });