Files
flutter/lib/models/workouts/routine.dart
Roland Geider 70fba34f0c Better handling of time zones
We now send the current time zone to the server when serializing datetime objects.
This was causing entries to be saved some hours wrong or depending on the time, on
a different day.
2025-05-26 11:13:53 +02:00

180 lines
5.1 KiB
Dart

/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* Copyright (C) 2020 wger Team
*
* wger Workout Manager is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* wger Workout Manager is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:json_annotation/json_annotation.dart';
import 'package:wger/helpers/date.dart';
import 'package:wger/helpers/json.dart';
import 'package:wger/models/workouts/day.dart';
import 'package:wger/models/workouts/day_data.dart';
import 'package:wger/models/workouts/log.dart';
import 'package:wger/models/workouts/session_api.dart';
part 'routine.g.dart';
@JsonSerializable()
class Routine {
static const MIN_LENGTH_DESCRIPTION = 0;
static const MAX_LENGTH_DESCRIPTION = 1000;
static const MIN_LENGTH_NAME = 3;
static const MAX_LENGTH_NAME = 25;
/// In weeks
static const MIN_DURATION = 2;
static const MAX_DURATION = 16;
static const DEFAULT_DURATION = 12;
@JsonKey(required: true, includeToJson: false)
int? id;
@JsonKey(required: true, toJson: dateToUtcIso8601)
late DateTime created;
@JsonKey(required: true, name: 'name')
late String name;
@JsonKey(required: true, name: 'description')
late String description;
@JsonKey(required: true, name: 'fit_in_week')
late bool fitInWeek;
@JsonKey(required: true, toJson: dateToYYYYMMDD)
late DateTime start;
@JsonKey(required: true, toJson: dateToYYYYMMDD)
late DateTime end;
@JsonKey(includeFromJson: true, required: false, includeToJson: false)
List<Day> days = [];
@JsonKey(includeFromJson: false, includeToJson: false)
List<DayData> dayData = [];
@JsonKey(includeFromJson: false, includeToJson: false)
List<DayData> dayDataGym = [];
@JsonKey(required: false, includeToJson: false, defaultValue: [])
List<WorkoutSessionApi> sessions = [];
Routine({
this.id,
DateTime? created,
required this.name,
DateTime? start,
DateTime? end,
this.fitInWeek = false,
String? description,
this.days = const [],
this.dayData = const [],
this.dayDataGym = const [],
this.sessions = const [],
}) {
this.created = created ?? DateTime.now();
this.start = start ?? DateTime.now();
this.end = end ?? DateTime.now().add(const Duration(days: DEFAULT_DURATION * 7));
this.description = description ?? '';
}
Routine.empty() {
name = '';
description = '';
created = DateTime.now();
start = DateTime.now();
end = DateTime.now().add(const Duration(days: DEFAULT_DURATION * 7));
fitInWeek = true;
}
// Boilerplate
factory Routine.fromJson(Map<String, dynamic> json) => _$RoutineFromJson(json);
Map<String, dynamic> toJson() => _$RoutineToJson(this);
List<Log> get logs {
final out = <Log>[];
for (final session in sessions) {
out.addAll(session.logs);
}
return out;
}
int? getIteration({DateTime? date}) {
date ??= DateTime.now();
for (final data in dayData) {
if (data.date.isSameDayAs(date)) {
return data.iteration;
}
}
return null;
}
List<DayData> get dayDataCurrentIteration {
final iteration = getIteration(date: DateTime.now()) ?? 1;
return dayData.where((data) => data.iteration == iteration).toList();
}
List<DayData> get dayDataCurrentIterationGym {
final iteration = getIteration(date: DateTime.now()) ?? 1;
return dayDataGym.where((data) => data.iteration == iteration).toList();
}
/// Filters the workout logs by exercise and sorts them by date
///
/// Optionally, filters list so that only unique logs are returned. "Unique"
/// means here that the values are the same, i.e. logs with the same weight,
/// reps, etc. are considered equal. Workout ID, Log ID and date are not
/// considered.
List<Log> filterLogsByExercise(int exerciseId, {bool unique = false}) {
var out = logs.where((log) => log.exerciseId == exerciseId).toList();
if (unique) {
out = out.toSet().toList();
}
out.sort((a, b) => b.date.compareTo(a.date));
return out;
}
/// Groups logs by repetition
Map<num, List<Log>> groupLogsByRepetition({
List<Log>? logs,
filterNullWeights = false,
filterNullReps = false,
}) {
final workoutLogs = logs ?? this.logs;
final Map<num, List<Log>> groupedLogs = {};
for (final log in workoutLogs) {
if (log.repetitions == null ||
(filterNullWeights && log.weight == null) ||
(filterNullReps && log.repetitions == null)) {
continue;
}
if (!groupedLogs.containsKey(log.repetitions)) {
groupedLogs[log.repetitions!] = [];
}
groupedLogs[log.repetitions]!.add(log);
}
return groupedLogs;
}
}