Show consecutive stall count beside previous performance

This commit is contained in:
Peter Stockings
2026-10-11 11:30:30 +11:00
parent 9612d322eb
commit 0c0bcdc4b6
5 changed files with 46 additions and 7 deletions
+2 -2
View File
@@ -371,8 +371,8 @@ class DataBase():
WHERE
e.exercise_id = %s AND (w.person_id = %s OR w.person_id IS NULL)
ORDER BY
w.start_date DESC
LIMIT 1;
w.start_date DESC, t.topset_id DESC
LIMIT 1;
""", [exercise_id, person_id], one=True)
if not topset:
+8 -2
View File
@@ -308,8 +308,14 @@ def get_most_recent_topset_for_exercise(person_id, workout_id):
if not topset:
exercise = db.execute("select name from exercise where exercise_id=%s", [exercise_id], one=True)
return render_template('partials/new_set_form.html', person_id=person_id, workout_id=workout_id, exercises=exercises, exercise_id=exercise_id, exercise_name=exercise['name'])
(repetitions, weight, exercise_name) = topset
return render_template('partials/new_set_form.html', person_id=person_id, workout_id=workout_id, exercise_id=exercise_id, exercise_name=exercise_name, repetitions=repetitions, weight=weight)
(repetitions, weight, exercise_name) = topset
stalled_sessions = 0
if repetitions is not None:
recent_sets = db.get_recent_topsets_for_exercise(person_id, exercise_id, limit=1)
if recent_sets:
achievements = db.get_topset_achievements(recent_sets[0]['topset_id'])
stalled_sessions = achievements.get('stalled_sessions', 0)
return render_template('partials/new_set_form.html', person_id=person_id, workout_id=workout_id, exercise_id=exercise_id, exercise_name=exercise_name, repetitions=repetitions, weight=weight, stalled_sessions=stalled_sessions)
@workout_bp.route("/person/<int:person_id>/exercise/<int:exercise_id>/history", methods=['GET'])
def get_exercise_history(person_id, exercise_id):
+8 -1
View File
@@ -64,7 +64,14 @@
.workout-secondary-details>summary,.workout-previous-details>summary{font-size:12px;color:#64748b;cursor:pointer}
.workout-secondary-details .workout-breakdown{margin-top:20px}
.workout-previous-details{border-top:1px solid #e8edf1;margin-top:18px;padding-top:14px}
.workout-previous-details>summary span{float:right;color:#334155;font-variant-numeric:tabular-nums}
.workout-previous-details>summary{display:flex;align-items:center;gap:10px;flex-wrap:wrap;list-style:none}
.workout-previous-details>summary::-webkit-details-marker{display:none}
.workout-previous-label{margin-right:auto}
.workout-previous-label:before{content:'›';display:inline-block;margin-right:8px}
.workout-previous-details[open] .workout-previous-label:before{transform:rotate(90deg)}
.workout-previous-value{color:#334155;font-variant-numeric:tabular-nums;white-space:nowrap}
.workout-stall-badge{padding:4px 8px;border-radius:5px;background:#fff4df;color:#8c5a10;font-size:11px;font-weight:500;white-space:nowrap}
@media(max-width:600px){.workout-previous-label{flex-basis:100%}}
.workout-table .extra-content-container:not(:empty){background:#f8fafc;padding:18px 20px;border-bottom:1px solid #e1e7ec}
.workout-table .extra-content-container .progress-heading{padding-right:22px}
.workout-workspace .forecast-chart{height:220px}
+8 -2
View File
@@ -72,7 +72,13 @@
{% if exercise_id %}
<details class="workout-previous-details">
<summary>Previous performance{% if repetitions %}<span>{{ repetitions }} &times; {{ weight }} kg</span>{% endif %}</summary>
<summary>
<span class="workout-previous-label">Previous performance</span>
{% if repetitions %}<span class="workout-previous-value">{{ repetitions }} &times; {{ weight }} kg</span>{% endif %}
{% if stalled_sessions|default(0) > 0 %}
<span class="workout-stall-badge" title="Consecutive repeats of the same weight and reps">Stalled {{ stalled_sessions }} {{ 'time' if stalled_sessions == 1 else 'times' }}</span>
{% endif %}
</summary>
<div class="w-full">
<div class="hidden"
hx-get="{{ url_for('get_exercise_progress_for_user', person_id=person_id, exercise_id=exercise_id) }}"
@@ -81,4 +87,4 @@
</div>
</details>
{% endif %}
</div>
</div>
+20
View File
@@ -91,11 +91,31 @@ class WorkoutViewTests(unittest.TestCase):
def test_previous_performance_is_loaded_on_expansion(self):
self.db.get_most_recent_topset_for_exercise.return_value = (7, 93.5, 'Smith Press')
self.db.get_recent_topsets_for_exercise.return_value = [self.top_sets[0]]
response = self.client.get(self.base + '/exercise/most_recent_topset_for_exercise?exercise_id=1')
self.assertEqual(response.status_code, 200)
self.assertIn(b'toggle once from:closest details', response.data)
self.assertNotIn(b'hx-trigger=load', response.data)
self.assertIn(b'Previous performance', response.data)
self.assertNotIn(b'workout-stall-badge', response.data)
def test_previous_performance_shows_consecutive_stall_count(self):
self.db.get_most_recent_topset_for_exercise.return_value = (7, 93.5, 'Smith Press')
self.db.get_recent_topsets_for_exercise.return_value = [self.top_sets[0]]
for count, label in ((1, b'Stalled 1 time'), (3, b'Stalled 3 times')):
self.db.get_topset_achievements.return_value = {'stalled_sessions': count}
response = self.client.get(self.base + '/exercise/most_recent_topset_for_exercise?exercise_id=1')
self.assertEqual(response.status_code, 200)
self.assertIn(label, response.data)
self.assertIn('7 × 93.5 kg', response.get_data(as_text=True))
self.db.get_topset_achievements.assert_called_with(1)
def test_previous_performance_without_history_hides_stall_badge(self):
self.db.get_most_recent_topset_for_exercise.return_value = (None, None, 'Smith Press')
response = self.client.get(self.base + '/exercise/most_recent_topset_for_exercise?exercise_id=1')
self.assertEqual(response.status_code, 200)
self.assertNotIn(b'workout-stall-badge', response.data)
self.db.get_recent_topsets_for_exercise.assert_not_called()
def test_create_edit_cancel_update_and_delete_targets(self):
data = dict(exercise_id=1, repetitions=8, weight=93.5)