from utils import calculate_estimated_1rm, get_exercise_graph_model class Dashboard: def __init__(self, db_connection_method): self.execute = db_connection_method def get_people_ids(self): query = """ SELECT person_id FROM Person ORDER BY person_id """ result = self.execute(query) # Extract and return the list of IDs return [row["person_id"] for row in result] def get_earliest_and_latest_workout_dates(self, selected_people_ids): # Create placeholders for the person IDs placeholders = ", ".join(["%s"] * len(selected_people_ids)) sql_query = f""" 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 IN ({placeholders}); """ result = self.execute(sql_query, selected_people_ids) 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, selected_people_ids, min_date, max_date): # Create placeholders for the person IDs placeholders = ", ".join(["%s"] * len(selected_people_ids)) sql_query = f""" 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 IN ({placeholders}) """ # Add min_date, max_date, and selected_people_ids to the parameters params = [min_date, max_date] + selected_people_ids result = self.execute(sql_query, params) if not result or not result[0]: return [] return result[0]['exercise_ids'] def get_exercises_with_selection(self, selected_people_ids, start_date, end_date, selected_exercise_ids): # Create placeholders for the person IDs placeholders = ", ".join(["%s"] * len(selected_people_ids)) # SQL query to fetch all exercises performed by the selected people in the given time range sql_query = f""" 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 IN ({placeholders}) AND w.start_date BETWEEN %s AND %s ORDER BY e.name ASC; """ # Add parameters for the query params = selected_people_ids + [start_date, end_date] # Execute the query with parameters result = self.execute(sql_query, params) 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_people_with_selection(self, selected_people_ids): # SQL query to fetch all people sql_query = """ SELECT DISTINCT p.person_id AS id, p.name AS name FROM person p ORDER BY p.name ASC; """ # Execute the query (no parameters required since we're fetching all people) result = self.execute(sql_query) if not result: return [] # No people found # Add the "selected" property to each person people = [] for row in result: people.append({ "id": row["id"], "name": row["name"], "selected": row["id"] in selected_people_ids }) return people def generate_exercise_progress_graphs(self, person_id, exercise_id, exercise_name, exercise_sets): # Extract the required data estimated_1rm = [t["estimated_1rm"] for t in exercise_sets] repetitions = [t["reps"] for t in exercise_sets] weight = [t["weight"] for t in exercise_sets] start_dates = [t["workout_start_date"] for t in exercise_sets] messages = [ f'{t["reps"]} x {t["weight"]}kg ({t["estimated_1rm"]}kg E1RM) on {t["workout_start_date"].strftime("%d %b %y")}' for t in exercise_sets ] epoch = "All" # Check for valid data before generating the graph if exercise_name and estimated_1rm and repetitions and weight and start_dates and messages: exercise_progress = get_exercise_graph_model( title=exercise_name, estimated_1rm=estimated_1rm, repetitions=repetitions, weight=weight, start_dates=start_dates, messages=messages, epoch=epoch, person_id=person_id, exercise_id=exercise_id, ) return exercise_progress def get(self, selected_people_ids, start_date, end_date, selected_exercise_ids): # Create placeholders for selected_people_ids and selected_exercise_ids people_placeholders = ", ".join(["%s"] * len(selected_people_ids)) exercise_placeholders = ", ".join(["%s"] * len(selected_exercise_ids)) # SQL query to fetch data sql_query = f""" SELECT p.person_id, p.name AS person_name, e.exercise_id, e.name AS exercise_name, t.topset_id, t.repetitions, t.weight, w.start_date AS workout_date, w.workout_id 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 IN ({people_placeholders}) AND w.start_date BETWEEN %s AND %s AND e.exercise_id IN ({exercise_placeholders}) ORDER BY p.person_id ASC, e.exercise_id ASC, t.topset_id DESC; """ # Add parameters for the query params = selected_people_ids + [start_date, end_date] + selected_exercise_ids # Execute the query result = self.execute(sql_query, params) # Handle empty result if not result: return {"people": []} # Organize data into the desired structure people_map = {} for row in result: # Person level person_id = row["person_id"] if person_id not in people_map: people_map[person_id] = { "id": person_id, "name": row["person_name"], "exercises": {} } # Exercise level exercise_id = row["exercise_id"] person_exercises = people_map[person_id]["exercises"] if exercise_id not in person_exercises: person_exercises[exercise_id] = { "id": exercise_id, "name": row["exercise_name"], "sets": [] } # Set level person_exercises[exercise_id]["sets"].append({ "id": row["topset_id"], "reps": row["repetitions"], "weight": row["weight"], "exercise_id": row["exercise_id"], "exercise_name": row["exercise_name"], "workout_id": row["workout_id"], "workout_start_date": row["workout_date"], "estimated_1rm": calculate_estimated_1rm(row["weight"], row["repetitions"]) }) # Convert the map into a list of people, generate graphs, and organize exercises people = [] for person_id, person_data in people_map.items(): exercises = [] for exercise_id, exercise_data in person_data["exercises"].items(): # Sort sets by timestamp (descending) exercise_data["sets"] = sorted( exercise_data["sets"], key=lambda x: x["id"], reverse=True ) # Generate the graph for the exercise graph = self.generate_exercise_progress_graphs( person_id=person_id, exercise_id=exercise_id, exercise_name=exercise_data["name"], exercise_sets=exercise_data["sets"] ) # Add the graph to the exercise data exercises.append({ "id": exercise_data["id"], "name": exercise_data["name"], "graph": graph, "sets": exercise_data["sets"] }) person_data["exercises"] = exercises people.append(person_data) return {"dashboard": people}