class PersonOverview: def __init__(self, db_connection_method): self.execute = db_connection_method def get_earliest_and_latest_workout_dates(self, person_id): sql_query = """ SELECT MIN(w.start_date) AS earliest_date, MAX(w.start_date) AS latest_date FROM workout w INNER JOIN topset t ON w.workout_id = t.workout_id WHERE w.person_id = %s; """ result = self.execute(sql_query, [person_id]) if not result or not result[0]: return None, None return result[0]['earliest_date'], result[0]['latest_date'] def list_of_performed_exercise_ids(self, person_id, min_date, max_date): sql_query = """ SELECT ARRAY_AGG(DISTINCT e.exercise_id) AS exercise_ids FROM workout w LEFT JOIN topset t ON w.workout_id = t.workout_id LEFT JOIN exercise e ON t.exercise_id = e.exercise_id WHERE w.start_date BETWEEN %s AND %s AND w.person_id = %s """ result = self.execute(sql_query, [min_date, max_date, person_id]) if not result or not result[0]: return [] return result[0]['exercise_ids'] def get_exercises_with_selection(self, person_id, start_date, end_date, selected_exercise_ids): # SQL query to fetch all exercises performed by the person in the given time range sql_query = """ SELECT DISTINCT e.exercise_id, e.name AS exercise_name FROM workout w JOIN topset t ON w.workout_id = t.workout_id JOIN exercise e ON t.exercise_id = e.exercise_id WHERE w.person_id = %s AND w.start_date BETWEEN %s AND %s ORDER BY e.name ASC; """ # Execute the query with parameters result = self.execute(sql_query, [person_id, start_date, end_date]) if not result: return [] # No exercises found in the given time range # Add the "selected" property to each exercise exercises = [] for row in result: exercises.append({ "id": row["exercise_id"], "name": row["exercise_name"], "selected": row["exercise_id"] in selected_exercise_ids }) return exercises def get(self, person_id, start_date, end_date, selected_exercise_ids): # Build placeholders for exercise IDs placeholders = ", ".join(["%s"] * len(selected_exercise_ids)) # Dynamically inject placeholders into the query sql_query = f""" SELECT p.person_id, p.name AS person_name, w.workout_id, w.start_date, w.note AS workout_note, e.exercise_id, e.name AS exercise_name, t.topset_id, t.repetitions, t.weight FROM person p JOIN workout w ON p.person_id = w.person_id JOIN topset t ON w.workout_id = t.workout_id JOIN exercise e ON t.exercise_id = e.exercise_id WHERE p.person_id = %s AND w.start_date BETWEEN %s AND %s AND e.exercise_id IN ({placeholders}) ORDER BY w.start_date DESC, e.exercise_id ASC, t.topset_id ASC; """ # Add parameters for the query params = [person_id, start_date, end_date] + selected_exercise_ids result = self.execute(sql_query, params) if not result: return {"person_id": person_id, "person_name": None, "workouts": [], "selected_exercises": []} # Extract person info from the first row person_info = {"person_id": result[0]["person_id"], "person_name": result[0]["person_name"]} # Extract and sort all unique exercises by name exercises = [] unique_exercise_ids = set() for row in result: if row["exercise_id"] not in unique_exercise_ids: unique_exercise_ids.add(row["exercise_id"]) exercises.append({"id": row["exercise_id"], "name": row["exercise_name"]}) # Sort the exercises by name exercises = sorted(exercises, key=lambda ex: ex["name"]) # Initialize the table structure workouts = [] workout_map = {} # Map to track workouts for row in result: workout_id = row["workout_id"] # Initialize the workout if not already present if workout_id not in workout_map: workout_map[workout_id] = { "id": workout_id, "start_date": row["start_date"], "note": row["workout_note"], "exercises": {exercise["id"]: [] for exercise in exercises} # Keyed by exercise_id } # Add topset to the corresponding exercise if row["exercise_id"] and row["topset_id"]: workout_map[workout_id]["exercises"][row["exercise_id"]].append({ "repetitions": row["repetitions"], "weight": row["weight"] }) # Transform into a list of rows for workout_id, workout in workout_map.items(): workouts.append(workout) return {**person_info, "workouts": workouts, "selected_exercises": exercises}