/// What the user can realistically absorb, learned from history rather than /// asserted. Drives the capacity block at plan time. class CapacityProfile { /// p50 of historically completed minutes, keyed by weekday (1 = Monday). Map completedMinutesByWeekday; /// Per-category estimation multiplier — you say 30min, you take 70min -> 2.3. Map estimationMultipliers; CapacityProfile({ Map? completedMinutesByWeekday, Map? estimationMultipliers, }) : completedMinutesByWeekday = completedMinutesByWeekday ?? {}, estimationMultipliers = estimationMultipliers ?? {}; factory CapacityProfile.fromJson(Map json) { final Map minutes = {}; if (json['completedMinutesByWeekday'] != null) { (json['completedMinutesByWeekday'] as Map) .forEach((key, value) { minutes[int.tryParse(key) ?? 1] = (value ?? 0).toDouble(); }); } final Map multipliers = {}; if (json['estimationMultipliers'] != null) { (json['estimationMultipliers'] as Map) .forEach((key, value) { multipliers[key] = (value ?? 1).toDouble(); }); } return CapacityProfile( completedMinutesByWeekday: minutes, estimationMultipliers: multipliers, ); } Map toJson() { final Map data = {}; data['completedMinutesByWeekday'] = completedMinutesByWeekday .map((key, value) => MapEntry(key.toString(), value)); data['estimationMultipliers'] = estimationMultipliers; return data; } /// The learned multiplier for a category, defaulting to honest 1.0 until /// there is enough history to say otherwise. double multiplierFor(String category) { return estimationMultipliers[category] ?? 1.0; } /// The p50 of what actually gets done on this weekday. double capacityFor(int weekday) { return completedMinutesByWeekday[weekday] ?? 0; } }