diff --git a/features/exercises.py b/features/exercises.py index 876d5e6..8c3af98 100644 --- a/features/exercises.py +++ b/features/exercises.py @@ -78,18 +78,19 @@ class Exercises: return self.get_exercise(exercise_id) - def get_workout_muscle_group_distribution(self, workout_id): + def get_workout_attribute_distribution(self, workout_id, category_name): query = """ - SELECT attr.name as muscle_group, COUNT(*) as count + SELECT attr.name as attribute_name, COUNT(*) as count FROM topset t - JOIN exercise_to_attribute eta ON t.exercise_id = eta.exercise_id + JOIN exercise e ON t.exercise_id = e.exercise_id + JOIN exercise_to_attribute eta ON e.exercise_id = eta.exercise_id JOIN exercise_attribute attr ON eta.attribute_id = attr.attribute_id JOIN exercise_attribute_category cat ON attr.category_id = cat.category_id - WHERE t.workout_id = %s AND cat.name = 'Muscle Group' + WHERE t.workout_id = %s AND cat.name = %s GROUP BY attr.name ORDER BY count DESC """ - distribution = self.execute(query, [workout_id]) + distribution = self.execute(query, [workout_id, category_name]) # Calculate percentages and SVG parameters total_counts = sum(item['count'] for item in distribution) @@ -105,6 +106,8 @@ class Exercises: "#10b981", # emerald-500 "#6366f1", # indigo-500 "#f43f5e", # rose-500 + "#84cc16", # lime-500 + "#0ea5e9", # sky-500 ] if total_counts > 0: diff --git a/routes/workout.py b/routes/workout.py index 9caecd0..5fbce1f 100644 --- a/routes/workout.py +++ b/routes/workout.py @@ -124,8 +124,10 @@ def _get_workout_view_model(person_id, workout_id): # Sort tags alphabetically by name for consistent display workout_data["tags"].sort(key=lambda x: x.get('tag_name', '')) - # Add muscle group distribution - workout_data["muscle_distribution"] = db.exercises.get_workout_muscle_group_distribution(workout_id) + # Add multi-category breakdowns + workout_data["muscle_distribution"] = db.exercises.get_workout_attribute_distribution(workout_id, 'Muscle Group') + workout_data["equipment_distribution"] = db.exercises.get_workout_attribute_distribution(workout_id, 'Machine vs Free Weight') + workout_data["movement_distribution"] = db.exercises.get_workout_attribute_distribution(workout_id, 'Compound vs Isolation') return workout_data @@ -182,13 +184,15 @@ def get_workout_start_date(person_id, workout_id): start_date = workout.get('start_date') if workout else None return render_template('partials/start_date.html', person_id=person_id, workout_id=workout_id, start_date=start_date) -@workout_bp.route("/person//workout//muscle_distribution", methods=['GET']) -def get_workout_muscle_distribution(person_id, workout_id): - distribution = db.exercises.get_workout_muscle_group_distribution(workout_id) +@workout_bp.route("/person//workout//distribution", methods=['GET']) +def get_workout_distribution(person_id, workout_id): + category = request.args.get('category', 'Muscle Group') + distribution = db.exercises.get_workout_attribute_distribution(workout_id, category) return render_template('partials/workout_breakdown.html', person_id=person_id, workout_id=workout_id, - muscle_distribution=distribution) + distribution=distribution, + category_name=category) @workout_bp.route("/person//workout//topset//achievements", methods=['GET']) @validate_topset diff --git a/static/img/schema.svg b/static/img/schema.svg index 45c684f..7a87cc3 100644 --- a/static/img/schema.svg +++ b/static/img/schema.svg @@ -1 +1 @@ -

category_id

attribute_id

exercise_id

person_id

person_id

person_id

program_id

program_id

tag_id

person_id

person_id

exercise_id

workout_id

person_id

tag_id

workout_id

exercise

int

exercise_id

PK

string

name

exercise_attribute

int

attribute_id

PK

int

category_id

FK

string

name

exercise_attribute_category

int

category_id

PK

string

name

exercise_to_attribute

int

exercise_id

PK,FK

int

attribute_id

PK,FK

llm_audit

int

id

PK

int

person_id

FK

string

prompt

string

response

string

model

string

ip_address

bool

success

string

error_message

datetime

timestamp

login_attempts

int

id

PK

string

email

string

ip_address

bool

success

string

user_agent

datetime

timestamp

int

person_id

FK

person

int

person_id

PK

string

name

string

password_hash

string

email

person_program_assignment

int

assignment_id

PK

int

person_id

FK

int

program_id

FK

date

start_date

bool

is_active

datetime

assigned_at

program_session

int

session_id

PK

int

program_id

FK

int

session_order

string

session_name

int

tag_id

FK

saved_query

int

id

PK

string

title

string

query

datetime

created_at

sql_audit

int

id

PK

int

person_id

FK

string

query

string

ip_address

bool

success

string

error_message

datetime

timestamp

tag

int

tag_id

PK

int

person_id

FK

string

name

string

filter

topset

int

topset_id

PK

int

workout_id

FK

int

exercise_id

FK

int

repetitions

float

weight

workout

int

workout_id

PK

int

person_id

FK

date

start_date

string

note

workout_program

int

program_id

PK

string

name

string

description

datetime

created_at

workout_tag

int

workout_id

PK,FK

int

tag_id

PK,FK

\ No newline at end of file +

category_id

attribute_id

exercise_id

person_id

person_id

person_id

program_id

program_id

tag_id

person_id

person_id

exercise_id

workout_id

person_id

tag_id

workout_id

exercise

int

exercise_id

PK

string

name

exercise_attribute

int

attribute_id

PK

int

category_id

FK

string

name

exercise_attribute_category

int

category_id

PK

string

name

exercise_to_attribute

int

exercise_id

PK,FK

int

attribute_id

PK,FK

llm_audit

int

id

PK

int

person_id

FK

string

prompt

string

response

string

model

string

ip_address

bool

success

string

error_message

datetime

timestamp

login_attempts

int

id

PK

string

email

string

ip_address

bool

success

string

user_agent

datetime

timestamp

int

person_id

FK

person

int

person_id

PK

string

name

string

password_hash

string

email

person_program_assignment

int

assignment_id

PK

int

person_id

FK

int

program_id

FK

date

start_date

bool

is_active

datetime

assigned_at

program_session

int

session_id

PK

int

program_id

FK

int

session_order

string

session_name

int

tag_id

FK

saved_query

int

id

PK

string

title

string

query

datetime

created_at

sql_audit

int

id

PK

int

person_id

FK

string

query

string

ip_address

bool

success

string

error_message

datetime

timestamp

tag

int

tag_id

PK

int

person_id

FK

string

name

string

filter

topset

int

topset_id

PK

int

workout_id

FK

int

exercise_id

FK

int

repetitions

float

weight

workout

int

workout_id

PK

int

person_id

FK

date

start_date

string

note

workout_program

int

program_id

PK

string

name

string

description

datetime

created_at

workout_tag

int

workout_id

PK,FK

int

tag_id

PK,FK

\ No newline at end of file diff --git a/templates/partials/workout_breakdown.html b/templates/partials/workout_breakdown.html index ac60ddc..a780337 100644 --- a/templates/partials/workout_breakdown.html +++ b/templates/partials/workout_breakdown.html @@ -1,15 +1,19 @@ -{% if muscle_distribution %} -
-
-

Session Muscle Distribution

+

{{ category_name }} Distribution +

- {% for item in muscle_distribution %} + {% for item in distribution %}
+
' into #content-{{ breakdown_id }} + remove .hidden from #popover-{{ breakdown_id }}" + title="{{ item.attribute_name }}: {{ item.percentage }}%">
{% if item.percentage > 18 %} {{ - item.muscle_group }} + item.attribute_name }} {{ item.percentage }}% {% elif item.percentage > 8 %} diff --git a/templates/workout.html b/templates/workout.html index ffde5e5..dd79d3a 100644 --- a/templates/workout.html +++ b/templates/workout.html @@ -76,10 +76,16 @@
- -
+ +
{{ render_partial('partials/workout_breakdown.html', person_id=person_id, workout_id=workout_id, - muscle_distribution=muscle_distribution) }} + distribution=muscle_distribution, category_name='Muscle Group') }} + + {{ render_partial('partials/workout_breakdown.html', person_id=person_id, workout_id=workout_id, + distribution=equipment_distribution, category_name='Machine vs Free Weight') }} + + {{ render_partial('partials/workout_breakdown.html', person_id=person_id, workout_id=workout_id, + distribution=movement_distribution, category_name='Compound vs Isolation') }}