232 lines
7.9 KiB
Dart
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}');
|
|
}
|
|
}
|