CoffeeAtHome/lib/services/statistics_service.dart
2026-03-29 08:13:38 -07:00

212 lines
5.9 KiB
Dart

import '../models/bean.dart';
import '../models/machine.dart';
import '../models/recipe.dart';
import '../models/journal_entry.dart';
import '../models/drink.dart';
class CoffeeStatistics {
final int totalBeans;
final int totalMachines;
final int totalRecipes;
final int totalJournalEntries;
final int totalDrinks;
final double averageRating;
final int preferredBeans;
final String mostUsedRoastLevel;
final List<Map<String, dynamic>> coffeeConsumptionTrend;
final List<Drink> topRatedDrinks;
final List<Map<String, dynamic>> recentActivity;
final Map<String, dynamic> monthlyStats;
CoffeeStatistics({
required this.totalBeans,
required this.totalMachines,
required this.totalRecipes,
required this.totalJournalEntries,
required this.totalDrinks,
required this.averageRating,
required this.preferredBeans,
required this.mostUsedRoastLevel,
required this.coffeeConsumptionTrend,
required this.topRatedDrinks,
required this.recentActivity,
required this.monthlyStats,
});
}
class StatisticsService {
static Future<CoffeeStatistics> getStatistics(
List<Bean> beans,
List<Machine> machines,
List<Recipe> recipes,
List<JournalEntry> journalEntries,
List<Drink> drinks,
) async {
try {
// Calculate average rating
final averageRating = drinks.isNotEmpty
? drinks.fold(0.0, (sum, drink) => sum + drink.rating) / drinks.length
: 0.0;
// Find most used roast level
final roastLevelCounts = <String, int>{};
for (final bean in beans) {
roastLevelCounts[bean.roastLevel.toString().split('.').last] =
(roastLevelCounts[bean.roastLevel.toString().split('.').last] ??
0) +
1;
}
final mostUsedRoastLevel =
roastLevelCounts.entries
.fold<MapEntry<String, int>?>(
null,
(max, entry) =>
max == null || entry.value > max.value ? entry : max,
)
?.key ??
'MEDIUM';
// Calculate coffee consumption trend (last 6 months)
final coffeeConsumptionTrend = _calculateConsumptionTrend(journalEntries);
// Get top rated drinks
final topRatedDrinks = List<Drink>.from(drinks)
..sort((a, b) => b.rating.compareTo(a.rating))
..take(5);
// Recent activity
final recentActivity = _getRecentActivity(
beans,
machines,
recipes,
journalEntries,
drinks,
);
// Monthly stats
final monthlyStats = _calculateMonthlyStats(journalEntries);
return CoffeeStatistics(
totalBeans: beans.length,
totalMachines: machines.length,
totalRecipes: recipes.length,
totalJournalEntries: journalEntries.length,
totalDrinks: drinks.length,
averageRating: (averageRating * 10).round() / 10,
preferredBeans: beans.where((bean) => bean.preferred).length,
mostUsedRoastLevel: mostUsedRoastLevel,
coffeeConsumptionTrend: coffeeConsumptionTrend,
topRatedDrinks: topRatedDrinks,
recentActivity: recentActivity,
monthlyStats: monthlyStats,
);
} catch (error) {
throw Exception('Error calculating statistics: $error');
}
}
static List<Map<String, dynamic>> _calculateConsumptionTrend(
List<JournalEntry> journalEntries,
) {
final now = DateTime.now();
final months = <Map<String, dynamic>>[];
for (int i = 5; i >= 0; i--) {
final date = DateTime(now.year, now.month - i, 1);
final monthName = _getMonthName(date);
final count = journalEntries.where((entry) {
final entryDate = entry.date;
return entryDate.month == date.month && entryDate.year == date.year;
}).length;
months.add({'month': monthName, 'count': count});
}
return months;
}
static List<Map<String, dynamic>> _getRecentActivity(
List<Bean> beans,
List<Machine> machines,
List<Recipe> recipes,
List<JournalEntry> journalEntries,
List<Drink> drinks,
) {
final activities = <Map<String, dynamic>>[];
// Add recent journal entries
for (final entry in journalEntries.take(5)) {
activities.add({
'date': entry.date,
'type': 'Journal',
'description': 'Enjoyed ${entry.drink.name}',
});
}
// Add recent beans
for (final bean in beans.take(3)) {
activities.add({
'date': bean.roastedDate,
'type': 'Bean',
'description':
'Added ${bean.name} from ${bean.originCountry ?? 'Unknown'}',
});
}
activities.sort(
(a, b) => (b['date'] as DateTime).compareTo(a['date'] as DateTime),
);
return activities.take(10).toList();
}
static Map<String, dynamic> _calculateMonthlyStats(
List<JournalEntry> journalEntries,
) {
final now = DateTime.now();
final currentMonth = now.month;
final currentYear = now.year;
final currentMonthCount = journalEntries.where((entry) {
return entry.date.month == currentMonth && entry.date.year == currentYear;
}).length;
final lastMonth = currentMonth == 1 ? 12 : currentMonth - 1;
final lastYear = currentMonth == 1 ? currentYear - 1 : currentYear;
final lastMonthCount = journalEntries.where((entry) {
return entry.date.month == lastMonth && entry.date.year == lastYear;
}).length;
final growth = lastMonthCount > 0
? (((currentMonthCount - lastMonthCount) / lastMonthCount) * 100)
.round()
: 0;
return {
'currentMonth': currentMonthCount,
'lastMonth': lastMonthCount,
'growth': growth,
};
}
static String _getMonthName(DateTime date) {
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
];
return '${months[date.month - 1]} ${date.year.toString().substring(2)}';
}
}