/// A training program. Deload weeks are scheduled and enforced — training /// through a deload logs as non-compliance, same as skipping. class Program { String? id; String name; int weeks; int sessionsPerWeek; /// 1-based week indices that are deloads. List deloadWeeks; /// Weekly ceiling on plyometric ground contacts. Plyo is the one modality /// where the app stops you rather than pushes you. int weeklyContactCeiling; /// Mandatory hours between high-intensity lower-body sessions. int lowerBodyRecoveryHours; bool active; Program({ this.id, this.name = "", this.weeks = 0, this.sessionsPerWeek = 0, List? deloadWeeks, this.weeklyContactCeiling = 0, this.lowerBodyRecoveryHours = 48, this.active = false, }) : deloadWeeks = deloadWeeks ?? []; factory Program.fromJson(Map json) { return Program( id: json['id'], name: json['name'] ?? "", weeks: json['weeks'] ?? 0, sessionsPerWeek: json['sessionsPerWeek'] ?? 0, deloadWeeks: json['deloadWeeks'] == null ? [] : (json['deloadWeeks'] as List).map((item) => item as int).toList(), weeklyContactCeiling: json['weeklyContactCeiling'] ?? 0, lowerBodyRecoveryHours: json['lowerBodyRecoveryHours'] ?? 48, active: json['active'] ?? false, ); } Map toJson() { final Map data = {}; data['id'] = id; data['name'] = name; data['weeks'] = weeks; data['sessionsPerWeek'] = sessionsPerWeek; data['deloadWeeks'] = deloadWeeks; data['weeklyContactCeiling'] = weeklyContactCeiling; data['lowerBodyRecoveryHours'] = lowerBodyRecoveryHours; data['active'] = active; return data; } bool isDeloadWeek(int week) { return deloadWeeks.contains(week); } }