diff --git a/app.py b/app.py index 139015e..8704437 100644 --- a/app.py +++ b/app.py @@ -1,7 +1,7 @@ from datetime import datetime, date, timedelta from dateutil.relativedelta import relativedelta import os -from flask import Flask, render_template, redirect, request, url_for +from flask import Flask, abort, render_template, redirect, request, url_for from jinja2 import Environment, FileSystemLoader, select_autoescape import jinja_partials from jinja2_fragments import render_block @@ -432,37 +432,12 @@ def calculate_relative_positions(start_dates): @ app.route("/person//exercise//sparkline", methods=['GET']) def get_exercise_progress_for_user(person_id, exercise_id): - width = request.args.get('width', 300, type=int) - height = request.args.get('height', 100, type=int) - (estimated_1rm, start_dates, messages) = db.get_exercise_progress_for_user(person_id, exercise_id) + exercise_progress = db.get_exercise_progress_for_user(person_id, exercise_id) - # Calculate vb_width - min_date = min(start_dates) - max_date = max(start_dates) - date_range = (max_date - min_date).days # e.g., 30 days - vb_width = date_range # This can be scaled if needed - - # Calculate vb_height - min_value = min(estimated_1rm) - max_value = max(estimated_1rm) - value_range = max_value - min_value # e.g., 100 - vb_height = value_range # This can be scaled if needed - - # Scaling factors (optional, for design) - width_scaling_factor = 200 / vb_width # e.g., if you want 200px width - height_scaling_factor = 75 / vb_height # e.g., if you want 100px height - - # Apply scaling - vb_width *= width_scaling_factor - vb_height *= height_scaling_factor - - # Scale estimated_1rm between 0 and vb_height - estimated_1rm = [((value - min_value) / value_range) * vb_height for value in estimated_1rm] - - relative_positions = calculate_relative_positions(start_dates) - data_points = list(zip(estimated_1rm, relative_positions, messages)) - - return render_template('partials/sparkline.html', title="GHR", vb_width=vb_width, vb_height=vb_height, data_points=data_points) + if not exercise_progress: + abort(404) + + return render_template('partials/sparkline.html', **exercise_progress) @app.teardown_appcontext diff --git a/db.py b/db.py index e189e3c..24b7e72 100644 --- a/db.py +++ b/db.py @@ -462,16 +462,13 @@ class DataBase(): exercises = self.execute( 'SELECT exercise_id, name FROM exercise') return exercises - + def get_exercise_progress_for_user(self, person_id, exercise_id): + # Execute SQL query to fetch topset data for a specific person and exercise topsets = self.execute(""" SELECT - T.topset_id, - E.name AS exercise_name, - W.person_id, - T.workout_id, - T.repetitions, - T.weight, + T.topset_id, E.name AS exercise_name, W.person_id, T.workout_id, + T.repetitions, T.weight, ROUND((100 * T.weight::NUMERIC::INTEGER) / (101.3 - 2.67123 * T.repetitions), 0)::NUMERIC::INTEGER AS estimated_1rm, W.start_date FROM @@ -481,15 +478,41 @@ class DataBase(): JOIN workout W ON T.workout_id = W.workout_id WHERE - W.person_id = %s AND - E.exercise_id = %s + W.person_id = %s AND E.exercise_id = %s ORDER BY - W.start_date;""", [person_id, exercise_id]) - # Get a list of all estimated_1rm values - estimated_1rm = [t['estimated_1rm'] for t in topsets] - # Get a list of all start_dates - start_dates = [t['start_date'] for t in topsets] - # Create a list of messages with the structure 'estimated_1rm kg on start_date' with start_date formatted as 'dd/mm/yyyy' - messages = [f'{t["repetitions"]} x {t["weight"]}kg ({t["estimated_1rm"]}kg E1RM) on {t["start_date"].strftime("%d/%m/%Y")}' for t in topsets] + W.start_date; + """, [person_id, exercise_id]) - return (estimated_1rm, start_dates, messages) \ No newline at end of file + # Return None if no topsets found + if not topsets: + return None + + # Extracting values and calculating value ranges for SVG dimensions + estimated_1rm = [t['estimated_1rm'] for t in topsets] + start_dates = [t['start_date'] for t in topsets] + min_date, max_date = min(start_dates), max(start_dates) + min_value, max_value = min(estimated_1rm), max(estimated_1rm) + + # Calculate viewBox dimensions + date_range = max_date - min_date + value_range = max_value - min_value + vb_width, vb_height = date_range.days, value_range + vb_width *= 200 / vb_width # Scale to 200px width + vb_height *= 75 / vb_height # Scale to 75px height + + # Scale estimated_1rm values for SVG plotting + estimated_1rm_scaled = [((value - min_value) / value_range) * vb_height for value in estimated_1rm] + total_span = date_range.days or 1 + relative_positions = [(date - min_date).days / total_span for date in start_dates] + + # Create messages and zip data for SVG plotting + messages = [f'{t["repetitions"]} x {t["weight"]}kg ({t["estimated_1rm"]}kg E1RM) on {t["start_date"].strftime("%d %b %y")}' for t in topsets] + data_points = zip(estimated_1rm_scaled, relative_positions, messages) + + # Return exercise data with SVG dimensions and data points + return { + 'exercise_name': topsets[0]['exercise_name'], + 'vb_width': vb_width, + 'vb_height': vb_height, + 'data_points': list(data_points) + } \ No newline at end of file