Anchor forecast top sets to latest rep target

This commit is contained in:
Peter Stockings
2026-09-06 14:59:57 +10:00
parent 50ddbced84
commit 499d859478
+19 -8
View File
@@ -173,8 +173,9 @@ def _round_weight(value, increment, direction='nearest'):
def infer_rep_targets(repetitions, buffer=5):
"""Return an expanded rep range based on the currently visible history."""
numeric_reps = sorted({int(round(float(value))) for value in repetitions if value is not None})
if not numeric_reps:
numeric_rep_values = [int(round(float(value))) for value in repetitions if value is not None]
numeric_reps = sorted(set(numeric_rep_values))
if not numeric_rep_values:
return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5, None, None
observed_min = min(numeric_reps)
@@ -182,9 +183,10 @@ def infer_rep_targets(repetitions, buffer=5):
target_min = max(1, observed_min - buffer)
target_max = min(20, observed_max + buffer)
target_reps = list(range(target_min, target_max + 1))
centered_reps = int(round(float(np.median(numeric_reps))))
centered_reps = max(target_min, min(target_max, centered_reps))
return target_reps, centered_reps, observed_min, observed_max
# Default to the latest recorded rep target so the first forecast comparison
# is like-for-like. The full expanded range remains available in the table.
default_reps = max(target_min, min(target_max, numeric_rep_values[-1]))
return target_reps, default_reps, observed_min, observed_max
def _top_set_estimates(e1rm, lower_e1rm, upper_e1rm, target_reps, increment):
@@ -349,7 +351,7 @@ def get_exercise_graph_model(title, estimated_1rm, repetitions, weight, start_da
forecast_start_position = None
if forecast.get('available'):
forecast_start_position = round((observed_max_date - observed_min_date).days / total_span, 3)
for point in forecast['points']:
for point_index, point in enumerate(forecast['points']):
position = round((point['date'] - observed_min_date).days / total_span, 3)
forecast_points.append((round(((point['value'] - min_e1rm) / e1rm_range) * vb_height, 1), position))
forecast_band_lower.append((round(((point['lower'] - min_e1rm) / e1rm_range) * vb_height, 1), position))
@@ -358,8 +360,17 @@ def get_exercise_graph_model(title, estimated_1rm, repetitions, weight, start_da
(top_set for top_set in point['top_sets'] if top_set['repetitions'] == forecast['selected_reps']),
None
)
top_set_message = ''
if selected_top_set:
if point_index == 0:
top_set_message = f" · Recorded {_format_kg(weight[-1])}kg × {int(repetitions[-1])}"
forecast_details.append({
'position': position,
'date': point['date'].strftime('%d %b %y'),
'kind': 'Current reading',
'e1rm': f"{_format_kg(forecast['last_value'])}kg",
'top_set': f"{_format_kg(weight[-1])}kg × {int(repetitions[-1])} reps",
'range': 'Observed value'
})
elif selected_top_set:
top_set_message = (
f" · Est. {selected_top_set['weight_display']}kg × {selected_top_set['repetitions']} "
f"(range {selected_top_set['lower_weight_display']}–{selected_top_set['upper_weight_display']}kg)"