from collections import defaultdict from datetime import date class Stats: def __init__(self, db_connection_method): self.execute = db_connection_method def get_stats_from_topsets(self, topsets): if not topsets: return [] # Extract necessary fields workout_ids = [t["WorkoutId"] for t in topsets if t["WorkoutId"]] person_ids = [t["PersonId"] for t in topsets if t["PersonId"]] start_dates = [t["StartDate"] for t in topsets if t["StartDate"]] exercise_ids = [t["ExerciseId"] for t in topsets if t["ExerciseId"]] workout_count = len(set(workout_ids)) people_count = len(set(person_ids)) total_sets = len(topsets) exercise_count = len(set(exercise_ids)) # Group sets by workout and exercise sets_per_exercise_per_workout = defaultdict(lambda: defaultdict(int)) for t in topsets: if t["WorkoutId"] and t["ExerciseId"]: sets_per_exercise_per_workout[t["WorkoutId"]][t["ExerciseId"]] += 1 # Calculate the average sets per exercise across all workouts total_sets_per_exercise = [] for workout_exercises in sets_per_exercise_per_workout.values(): total_sets_per_exercise.extend(workout_exercises.values()) average_sets_per_exercise = round(sum(total_sets_per_exercise) / len(total_sets_per_exercise), 1) if total_sets_per_exercise else 0 # Group exercises by workout exercises_by_workout = defaultdict(set) for t in topsets: if t["WorkoutId"] and t["ExerciseId"]: exercises_by_workout[t["WorkoutId"]].add(t["ExerciseId"]) # Calculate average exercises per workout average_exercises_per_workout = round( sum(len(exercises) for exercises in exercises_by_workout.values()) / workout_count, 1 ) if workout_count > 0 else 0 # Stats stats = [ {"Text": "Total Workouts", "Value": workout_count}, {"Text": "Total Sets", "Value": total_sets}, {"Text": "Average Sets Per Exercise", "Value": average_sets_per_exercise} ] if exercise_count > 1: stats.append({"Text": "Total Exercises", "Value": exercise_count}) stats.append({"Text": "Average Exercises Per Workout", "Value": average_exercises_per_workout}) if people_count > 1: stats.append({"Text": "People Tracked", "Value": people_count}) if workout_count > 0: first_workout_date = min(start_dates) last_workout_date = max(start_dates) current_date = date.today() stats.append({"Text": "Days Since First Workout", "Value": (current_date - first_workout_date).days}) if workout_count >= 2: stats.append({"Text": "Days Since Last Workout", "Value": (current_date - last_workout_date).days}) average_sets_per_workout = round(total_sets / workout_count, 1) stats.append({"Text": "Average Sets Per Workout", "Value": average_sets_per_workout}) training_duration = last_workout_date - first_workout_date if training_duration.days > 0: average_workouts_per_week = round( workout_count / (training_duration.days / 7), 1) stats.append({"Text": "Average Workouts Per Week", "Value": average_workouts_per_week}) return stats def fetch_stats(self, selected_people_ids=None, min_date=None, max_date=None, selected_exercise_ids=None): # Base query query = """ SELECT t.workout_id AS "WorkoutId", w.person_id AS "PersonId", w.start_date AS "StartDate", e.exercise_id AS "ExerciseId" FROM topset t JOIN workout w ON t.workout_id = w.workout_id JOIN person p ON w.person_id = p.person_id JOIN exercise e ON t.exercise_id = e.exercise_id """ # Parameters for the query params = [] # Add optional filters conditions = [] # Collect conditions dynamically if selected_people_ids: placeholders = ", ".join(["%s"] * len(selected_people_ids)) conditions.append(f"p.person_id IN ({placeholders})") params.extend(selected_people_ids) if min_date: conditions.append("w.start_date >= %s") params.append(min_date) if max_date: conditions.append("w.start_date <= %s") params.append(max_date) if selected_exercise_ids: placeholders = ", ".join(["%s"] * len(selected_exercise_ids)) conditions.append(f"e.exercise_id IN ({placeholders})") params.extend(selected_exercise_ids) # Add conditions to the query if conditions: query += " WHERE " + " AND ".join(conditions) # Execute the query workouts_data = self.execute(query, params) # Generate stats from the retrieved data stats = self.get_stats_from_topsets(workouts_data) return stats