nokken/lib/tools/medication_data_generator.dart
2025-04-20 11:17:03 -04:00

232 lines
7.9 KiB
Dart

// SPDX-FileCopyrightText: © 2025 Nøkken.io <nokken.io@proton.me>
// SPDX-License-Identifier: AGPL-3.0
//
// medication_data_generator.dart
//
import 'dart:math' show Random;
import 'package:flutter/material.dart';
import 'package:nokken/src/core/constants/date_constants.dart';
import 'package:nokken/src/core/services/database/database_service.dart';
import 'package:nokken/src/features/medication_tracker/models/medication.dart';
import 'package:nokken/src/features/medication_tracker/models/medication_dose.dart';
/// A tool class to generate historical medication data
class MedicationDataGenerator {
final DatabaseService databaseService;
MedicationDataGenerator(this.databaseService);
/// Generate sample medications and adherence data for 365 days
Future<void> generateMedicationData() 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 medication data from ${startDate.toIso8601String()} to ${endDate.toIso8601String()}');
// Check existing medications and add more if needed
final existingMeds = await databaseService.getAllMedications();
debugPrint('Found ${existingMeds.length} existing medications');
// We want to have at least 5 medications
if (existingMeds.length < 5) {
debugPrint('Creating additional sample medications...');
final targetCount = 5 - existingMeds.length;
// Get sample medications and only use as many as needed
final allSampleMeds = _createSampleMedications();
final medsToAdd = allSampleMeds.take(targetCount).toList();
for (final med in medsToAdd) {
await databaseService.insertMedication(med);
}
debugPrint('Created ${medsToAdd.length} additional medications.');
}
// Get all medications
final allMeds = await databaseService.getAllMedications();
// Generate taking records for each medication
for (final med in allMeds) {
debugPrint('Generating adherence data for ${med.name}...');
// Set adherence rate based on medication type
double adherenceRate;
if (med.medicationType == MedicationType.injection ||
med.medicationType == MedicationType.patch) {
// Higher adherence for injections and patches (96-98%)
adherenceRate = 0.96 + (random.nextDouble() * 0.02);
} else {
// Lower adherence for pills and topical (95-97%)
adherenceRate = 0.95 + (random.nextDouble() * 0.02);
}
debugPrint(
'Using adherence rate of ${(adherenceRate * 100).toStringAsFixed(1)}% for ${med.name}');
// Generate records for each day
await _generateMedicationRecords(
med, startDate, endDate, adherenceRate, random);
}
debugPrint('Successfully generated medication adherence data.');
}
/// Create 5 sample medications of different types
List<Medication> _createSampleMedications() {
final now = DateTime.now();
final allDays = Set<String>.from(DateConstants.dayMap.values);
// Common morning and evening times
final morning = DateTime(now.year, now.month, now.day, 8, 0);
final evening = DateTime(now.year, now.month, now.day, 20, 0);
final noon = DateTime(now.year, now.month, now.day, 12, 0);
return [
// Pill 1: Daily antidepressant in the morning
Medication(
name: 'Sertraline',
dosage: '50mg',
startDate: DateTime.now().subtract(const Duration(days: 400)),
frequency: 1,
timeOfDay: [morning],
daysOfWeek: allDays,
currentQuantity: 28,
refillThreshold: 7,
medicationType: MedicationType.oral,
oralSubtype: OralSubtype.tablets,
notes: 'SSRI antidepressant',
doctor: 'Dr. Smith',
pharmacy: 'Local Pharmacy',
),
// Pill 2: Multivitamin taken once daily
Medication(
name: 'Multivitamin',
dosage: '1 tablet',
startDate: DateTime.now().subtract(const Duration(days: 380)),
frequency: 1,
timeOfDay: [noon],
daysOfWeek: allDays,
currentQuantity: 60,
refillThreshold: 10,
medicationType: MedicationType.oral,
oralSubtype: OralSubtype.tablets,
),
// Topical: Gel applied twice daily
Medication(
name: 'Topical Treatment',
dosage: 'Thin layer',
startDate: DateTime.now().subtract(const Duration(days: 390)),
frequency: 2,
timeOfDay: [morning, evening],
daysOfWeek: allDays,
currentQuantity: 1,
refillThreshold: 1,
medicationType: MedicationType.topical,
topicalSubtype: TopicalSubtype.gel,
),
// Patch: Applied weekly
Medication(
name: 'Hormone Patch',
dosage: '1 patch',
startDate: DateTime.now().subtract(const Duration(days: 395)),
frequency: 1,
timeOfDay: [morning],
daysOfWeek: {'Su'}, // Applied on Sundays
currentQuantity: 4,
refillThreshold: 2,
medicationType: MedicationType.patch,
),
// Injection: Intramuscular, biweekly
Medication(
name: 'Hormone Injection',
dosage: '0.5ml',
startDate: DateTime.now().subtract(const Duration(days: 392)),
frequency: 1,
timeOfDay: [evening],
daysOfWeek: {'F'}, // Injected on Fridays
currentQuantity: 2,
refillThreshold: 1,
medicationType: MedicationType.injection,
injectionDetails: InjectionDetails(
drawingNeedleType: '18G 1.5"',
drawingNeedleCount: 8,
drawingNeedleRefills: 2,
injectingNeedleType: '23G 1"',
injectingNeedleCount: 8,
injectingNeedleRefills: 2,
syringeType: '3ml Luer Lock',
syringeCount: 8,
syringeRefills: 2,
injectionSiteNotes: 'Rotate between thighs and glutes',
frequency: InjectionFrequency.biweekly,
subtype: InjectionSubtype.intramuscular,
siteRotation: InjectionSiteRotation(
sites: [
InjectionSite(siteNumber: 1, bodyArea: InjectionBodyArea.thigh),
InjectionSite(siteNumber: 2, bodyArea: InjectionBodyArea.thigh),
],
currentSiteIndex: 0,
),
),
doctor: 'Dr. Johnson',
pharmacy: 'Specialty Pharmacy',
),
];
}
/// Generate medication taking records based on specified adherence patterns
Future<void> _generateMedicationRecords(
Medication medication,
DateTime startDate,
DateTime endDate,
double adherenceRate,
Random random) async {
int recordCount = 0;
// For each day in the range
for (int i = 0; i <= 364; i++) {
final date = startDate.add(Duration(days: i));
// Check if this medication is scheduled for this day
if (medication.isDueOnDate(date)) {
// For each time slot
for (int timeIndex = 0; timeIndex < medication.frequency; timeIndex++) {
final timeSlot = timeIndex < medication.timeOfDay.length
? medication.timeOfDay[timeIndex].hour.toString()
: '$timeIndex';
// Create a proper MedicationDose object
final dose = MedicationDose(
medicationId: medication.id,
date: date,
timeSlot: timeSlot,
);
// Generate a custom key from the dose plus a unique identifier
final customKey = '${dose.toKey()}-historical-$i';
// Determine if the dose was taken based on adherence rate
final wasTaken = random.nextDouble() <= adherenceRate;
// Save the taking record
await databaseService.setMedicationTakenWithCustomKey(
medication.id, date, timeSlot, wasTaken, customKey);
if (wasTaken) {
recordCount++;
}
}
}
}
debugPrint('Generated $recordCount taking records for ${medication.name}');
}
}