// SPDX-FileCopyrightText: © 2025 Nøkken.io // SPDX-License-Identifier: AGPL-3.0 // // mood_data_generator.dart // import 'dart:math' show Random; import 'package:flutter/material.dart'; import 'package:nokken/src/core/services/database/database_service.dart'; import 'package:nokken/src/core/services/database/database_service_mood.dart'; import 'package:nokken/src/features/mood_tracker/models/mood_entry.dart'; import 'package:uuid/uuid.dart'; /// A tool class to generate historical mood data class MoodDataGenerator { final DatabaseService databaseService; MoodDataGenerator(this.databaseService); /// Generate 365 days of mood entries Future generateMoodEntries() async { final random = Random(); // Start date: 364 days ago from today final endDate = DateTime.now(); final startDate = endDate.subtract(const Duration(days: 364)); debugPrint( 'Generating mood entries from ${startDate.toIso8601String()} to ${endDate.toIso8601String()}'); // Generate an entry for each day for (int i = 0; i <= 364; i++) { final date = startDate.add(Duration(days: i)); // Generate random mood entry final entry = _generateRandomMoodEntry(date, random); // Insert into database await databaseService.insertMoodEntry(entry); // Print progress if (i % 30 == 0) { debugPrint('Generated ${i + 1} entries...'); } } debugPrint('Successfully generated 365 mood entries.'); } /// Generate a random but realistic mood entry for the given date MoodEntry _generateRandomMoodEntry(DateTime date, Random random) { // Generate a unique ID final id = const Uuid().v4(); // Generate a base mood index with some weekly and seasonal patterns int baseMoodModifier = 0; // Weekday effects: people often feel better on weekends, worse on Mondays if (date.weekday == DateTime.saturday || date.weekday == DateTime.sunday) { baseMoodModifier = random.nextDouble() < 0.7 ? 1 : 0; // Better mood on weekends } else if (date.weekday == DateTime.monday) { baseMoodModifier = random.nextDouble() < 0.6 ? -1 : 0; // Worse mood on Mondays } // Seasonal effects: worse in winter, better in summer final month = date.month; int seasonalModifier = 0; if (month >= 11 || month <= 2) { // Winter months seasonalModifier = random.nextDouble() < 0.6 ? -1 : 0; } else if (month >= 5 && month <= 8) { // Summer months seasonalModifier = random.nextDouble() < 0.6 ? 1 : 0; } // Calculate final mood index with modifiers and randomness // Assuming mood from terrible (0) to excellent (4) int moodIndex = 2 + baseMoodModifier + seasonalModifier; // Start from neutral (2) moodIndex += (random.nextInt(3) - 1); // Add -1, 0, or 1 for randomness moodIndex = moodIndex.clamp(0, 4); // Ensure within valid range // Convert index to actual MoodRating enum final mood = MoodRating.values[moodIndex]; // Generate 1-6 random emotions final emotionCount = random.nextInt(6) + 1; final emotions = {}; while (emotions.length < emotionCount && emotions.length < Emotion.values.length) { final emotionIndex = random.nextInt(Emotion.values.length); emotions.add(Emotion.values[emotionIndex]); } // Decide which health metrics to include (3 to all) final healthMetricsCount = random.nextInt(5) + 3; // 3 to 7 metrics final healthMetricsToInclude = List.generate(7, (index) => index) ..shuffle(random); final selectedHealthMetrics = healthMetricsToInclude.take(healthMetricsCount).toList(); // Helper function to generate a correlated index int correlatedIndex( int baseIndex, double correlationStrength, int maxIndex) { if (random.nextDouble() < correlationStrength) { // Correlated value final variation = random.nextInt(3) - 1; // -1, 0, or 1 return (baseIndex + variation).clamp(0, maxIndex - 1); } else { // Random value return random.nextInt(maxIndex); } } // Generate health metrics SleepQuality? sleepQuality; EnergyLevel? energyLevel; LibidoLevel? libidoLevel; AppetiteLevel? appetiteLevel; FocusLevel? focusLevel; DysphoriaLevel? dysphoriaLevel; ExerciseLevel? exerciseLevel; // Sleep quality - somewhat correlated with mood if (selectedHealthMetrics.contains(0)) { final sleepIndex = correlatedIndex(moodIndex, 0.7, SleepQuality.values.length); sleepQuality = SleepQuality.values[sleepIndex]; } // Energy level - often follows sleep quality if available if (selectedHealthMetrics.contains(1)) { int energyIndex; if (sleepQuality != null && random.nextDouble() < 0.8) { // Correlate with sleep energyIndex = correlatedIndex(SleepQuality.values.indexOf(sleepQuality), 0.8, EnergyLevel.values.length); } else { // Correlate with mood energyIndex = correlatedIndex(moodIndex, 0.6, EnergyLevel.values.length); } energyLevel = EnergyLevel.values[energyIndex]; } // Libido level - moderately correlated with mood if (selectedHealthMetrics.contains(2)) { final libidoIndex = correlatedIndex(moodIndex, 0.5, LibidoLevel.values.length); libidoLevel = LibidoLevel.values[libidoIndex]; } // Appetite level - less correlated with mood if (selectedHealthMetrics.contains(3)) { final appetiteIndex = correlatedIndex(moodIndex, 0.4, AppetiteLevel.values.length); appetiteLevel = AppetiteLevel.values[appetiteIndex]; } // Focus level - often correlates with sleep and energy if (selectedHealthMetrics.contains(4)) { int focusIndex; if (sleepQuality != null && energyLevel != null && random.nextDouble() < 0.7) { // Derive from average of sleep and energy final avgIndex = (SleepQuality.values.indexOf(sleepQuality) + EnergyLevel.values.indexOf(energyLevel)) ~/ 2; focusIndex = correlatedIndex(avgIndex, 0.8, FocusLevel.values.length); } else { // Correlate with mood focusIndex = correlatedIndex(moodIndex, 0.6, FocusLevel.values.length); } focusLevel = FocusLevel.values[focusIndex]; } // Dysphoria level - often inversely correlates with mood if (selectedHealthMetrics.contains(5)) { // Invert the mood index for dysphoria correlation (higher mood = lower dysphoria) final invertedMood = 4 - moodIndex; // Assuming 5 mood levels (0-4) final dysphoriaIndex = correlatedIndex(invertedMood, 0.75, DysphoriaLevel.values.length); dysphoriaLevel = DysphoriaLevel.values[dysphoriaIndex]; } // Exercise level - less strongly correlated with other metrics if (selectedHealthMetrics.contains(6)) { final exerciseIndex = random.nextInt(ExerciseLevel.values.length); exerciseLevel = ExerciseLevel.values[exerciseIndex]; } // Generate optional notes (30% chance) String? notes; if (random.nextDouble() < 0.3) { final noteTemplates = [ "Feeling ${moodIndex > 2 ? 'pretty good' : 'a bit down'} today.", "Today was ${moodIndex > 3 ? 'excellent' : moodIndex > 2 ? 'decent' : 'challenging'}.", "${moodIndex > 3 ? 'Great' : moodIndex > 2 ? 'Good' : 'Tough'} day overall.", "Noticed ${sleepQuality != null ? (SleepQuality.values.indexOf(sleepQuality) > 2 ? 'good sleep' : 'poor sleep') : 'fluctuating energy'} today.", "Mood tracker note for ${date.day}/${date.month}/${date.year}.", "${energyLevel != null && EnergyLevel.values.indexOf(energyLevel) > 2 ? 'High energy' : 'Low energy'} but ${moodIndex > 2 ? 'good spirits' : 'feeling down'}.", "Trying to stay ${moodIndex < 2 ? 'positive despite challenges' : 'grateful for the good things'}.", ]; notes = noteTemplates[random.nextInt(noteTemplates.length)]; } // Create and return the mood entry return MoodEntry( id: id, date: date, mood: mood, emotions: emotions, notes: notes, sleepQuality: sleepQuality, energyLevel: energyLevel, libidoLevel: libidoLevel, appetiteLevel: appetiteLevel, focusLevel: focusLevel, dysphoriaLevel: dysphoriaLevel, exerciseLevel: exerciseLevel, ); } }