Switch to using custom svg sparkline for exercise graphs on person view, this isnt a proper implementation as it separate requests for each exercise selected (Want to get rid of dependency on plotly)
This commit is contained in:
6
app.py
6
app.py
@@ -432,7 +432,11 @@ def calculate_relative_positions(start_dates):
|
|||||||
|
|
||||||
@ app.route("/person/<int:person_id>/exercise/<int:exercise_id>/sparkline", methods=['GET'])
|
@ app.route("/person/<int:person_id>/exercise/<int:exercise_id>/sparkline", methods=['GET'])
|
||||||
def get_exercise_progress_for_user(person_id, exercise_id):
|
def get_exercise_progress_for_user(person_id, exercise_id):
|
||||||
exercise_progress = db.get_exercise_progress_for_user(person_id, exercise_id)
|
min_date = convert_str_to_date(request.args.get(
|
||||||
|
'min_date'), '%Y-%m-%d')
|
||||||
|
max_date = convert_str_to_date(request.args.get(
|
||||||
|
'max_date'), '%Y-%m-%d')
|
||||||
|
exercise_progress = db.get_exercise_progress_for_user(person_id, exercise_id, min_date, max_date)
|
||||||
|
|
||||||
if not exercise_progress:
|
if not exercise_progress:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|||||||
15
db.py
15
db.py
@@ -464,7 +464,7 @@ class DataBase():
|
|||||||
'SELECT exercise_id, name FROM exercise')
|
'SELECT exercise_id, name FROM exercise')
|
||||||
return exercises
|
return exercises
|
||||||
|
|
||||||
def get_exercise_progress_for_user(self, person_id, exercise_id):
|
def get_exercise_progress_for_user(self, person_id, exercise_id, min_date=None, max_date=None):
|
||||||
# Execute SQL query to fetch topset data for a specific person and exercise
|
# Execute SQL query to fetch topset data for a specific person and exercise
|
||||||
topsets = self.execute("""
|
topsets = self.execute("""
|
||||||
SELECT
|
SELECT
|
||||||
@@ -479,10 +479,13 @@ class DataBase():
|
|||||||
JOIN
|
JOIN
|
||||||
workout W ON T.workout_id = W.workout_id
|
workout W ON T.workout_id = W.workout_id
|
||||||
WHERE
|
WHERE
|
||||||
W.person_id = %s AND E.exercise_id = %s
|
W.person_id = %s
|
||||||
|
AND E.exercise_id = %s AND
|
||||||
|
(%s IS NULL OR W.start_date >= %s) AND
|
||||||
|
(%s IS NULL OR W.start_date <= %s)
|
||||||
ORDER BY
|
ORDER BY
|
||||||
W.start_date;
|
W.start_date;
|
||||||
""", [person_id, exercise_id])
|
""", [person_id, exercise_id, min_date, min_date, max_date, max_date])
|
||||||
|
|
||||||
# Return None if no topsets found
|
# Return None if no topsets found
|
||||||
if not topsets:
|
if not topsets:
|
||||||
@@ -500,9 +503,9 @@ class DataBase():
|
|||||||
|
|
||||||
# Calculate viewBox dimensions
|
# Calculate viewBox dimensions
|
||||||
date_range = max_date - min_date
|
date_range = max_date - min_date
|
||||||
e1rm_range = max_e1rm - min_e1rm
|
e1rm_range = (max_e1rm - min_e1rm) or 1
|
||||||
reps_range = max_reps - min_reps
|
reps_range = (max_reps - min_reps) or 1
|
||||||
weight_range = max_weight - min_weight
|
weight_range = (max_weight - min_weight) or 1
|
||||||
vb_width, vb_height = date_range.days, e1rm_range
|
vb_width, vb_height = date_range.days, e1rm_range
|
||||||
vb_width *= 200 / vb_width # Scale to 200px width
|
vb_width *= 200 / vb_width # Scale to 200px width
|
||||||
vb_height *= 75 / vb_height # Scale to 75px height
|
vb_height *= 75 / vb_height # Scale to 75px height
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap mb-1">
|
<div class="flex flex-wrap mb-1">
|
||||||
<div class="w-full md:w-1/4 px-2 md:px-3 mb-6 md:mb-0">
|
<div class="w-full md:w-1/3 px-2 md:px-3 mb-6 md:mb-0">
|
||||||
<div class="mb-1 w-full">
|
<div class="mb-1 w-full">
|
||||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
||||||
Exercises
|
Exercises
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full md:w-1/4 px-2 md:px-3 mb-6 md:mb-0">
|
<div class="w-full md:w-1/3 px-2 md:px-3 mb-6 md:mb-0">
|
||||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
||||||
Min date
|
Min date
|
||||||
</label>
|
</label>
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
hx-target="#container" hx-push-url="true" hx-trigger="change">
|
hx-target="#container" hx-push-url="true" hx-trigger="change">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full md:w-1/4 px-2 md:px-3 mb-6 md:mb-0">
|
<div class="w-full md:w-1/3 px-2 md:px-3 mb-6 md:mb-0">
|
||||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-zip">
|
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-zip">
|
||||||
Max date
|
Max date
|
||||||
</label>
|
</label>
|
||||||
@@ -102,22 +102,6 @@
|
|||||||
hx-target="#container" hx-push-url="true" hx-trigger="change">
|
hx-target="#container" hx-push-url="true" hx-trigger="change">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full md:w-1/4 px-2 md:px-3 mb-6 md:mb-0">
|
|
||||||
<div class="mb-1 w-full">
|
|
||||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
|
||||||
Graph Axis
|
|
||||||
</label>
|
|
||||||
<select name="graph_axis" class="bg-gray-50 border border-gray-300 " multiple
|
|
||||||
hx-get="{{ url_for('get_person', person_id=person['PersonId']) }}"
|
|
||||||
hx-include="[name='exercise_id'],[name='min_date'],[name='max_date'],[name='graph_axis']"
|
|
||||||
hx-target="#container" hx-push-url="true"
|
|
||||||
_="init js(me) tail.select(me, { multiple: true, placeholder: 'Select graphs' }) end">
|
|
||||||
<option value="repetitions" {% if "repetitions" in graph_axis %}selected{% endif %}>Repetitions</option>
|
|
||||||
<option value="weight" {% if "weight" in graph_axis %}selected{% endif %}>Weigh</option>
|
|
||||||
<option value="estimated1rm" {% if "estimated1rm" in graph_axis %}selected{% endif %}>Estimated 1RM</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% with person_id=person['PersonId'], tags=tags %}
|
{% with person_id=person['PersonId'], tags=tags %}
|
||||||
@@ -186,95 +170,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if graph_axis %}
|
|
||||||
<div class="mt-4 mb-4 w-full grid grid-cols-1 2xl:grid-cols-2 gap-4">
|
<div class="mt-4 mb-4 w-full grid grid-cols-1 2xl:grid-cols-2 gap-4">
|
||||||
{% for exercise_graph in person['ExerciseGraphs'] %}
|
{% for exercise_id in selected_exercise_ids %}
|
||||||
<div class="bg-white shadow rounded-lg p-4 sm:p-6 xl:p-8 ">
|
<div class="bg-white shadow rounded-lg p-4 sm:p-6 xl:p-8 ">
|
||||||
<div class="flex flex-col items-center">
|
<div class="hidden"
|
||||||
<div class="flex-shrink-0">
|
hx-get="{{ url_for('get_exercise_progress_for_user', person_id=person['PersonId'], exercise_id=exercise_id, min_date=min_date, max_date=max_date) }}"
|
||||||
<span class="text-2xl sm:text-3xl leading-none font-bold text-gray-900">{{
|
hx-trigger="load" hx-target="this" hx-swap="outerHTML">
|
||||||
exercise_graph['ExerciseName'] }}</span>
|
|
||||||
</div>
|
|
||||||
<div id="e-{{ exercise_graph['ExerciseId'] }}" class="w-full mt-2 aspect-video"></div>
|
|
||||||
<script>
|
|
||||||
Plotly.newPlot("e-{{ exercise_graph['ExerciseId'] }}", [
|
|
||||||
{% if 'repetitions' in graph_axis %}
|
|
||||||
{
|
|
||||||
x: {{ exercise_graph['StartDates'] | replace('"', "'") | safe }},
|
|
||||||
y: {{ exercise_graph['Repetitions'] | replace('"', "'") | safe }},
|
|
||||||
name: 'Reps',
|
|
||||||
type: 'scatter'
|
|
||||||
},
|
|
||||||
{% endif %}
|
|
||||||
{% if 'weight' in graph_axis %}
|
|
||||||
{
|
|
||||||
x: {{ exercise_graph['StartDates'] | replace('"', "'") | safe }},
|
|
||||||
y: {{ exercise_graph['Weights'] | replace('"', "'") | safe }},
|
|
||||||
name: 'Weight',
|
|
||||||
yaxis: 'y2',
|
|
||||||
type: 'scatter'
|
|
||||||
},
|
|
||||||
{% endif %}
|
|
||||||
{% if 'estimated1rm' in graph_axis %}
|
|
||||||
{
|
|
||||||
x: {{ exercise_graph['StartDates'] | replace('"', "'") | safe }},
|
|
||||||
y: {{ exercise_graph['Estimated1RM'] | replace('"', "'") | safe }},
|
|
||||||
name: 'Estimated 1RM',
|
|
||||||
yaxis: 'y3',
|
|
||||||
type: 'scatter'
|
|
||||||
},
|
|
||||||
{% endif %}
|
|
||||||
],
|
|
||||||
{
|
|
||||||
margin: {
|
|
||||||
t: 20, // Increase top margin to move the graph downwards
|
|
||||||
l: 50, // Increase left margin if the graph is too far to the right
|
|
||||||
r: 50, // Increase right margin for the same reason
|
|
||||||
b: 40 // Bottom margin for better layout
|
|
||||||
},
|
|
||||||
xaxis: { type: 'date', showgrid: false },
|
|
||||||
yaxis: { title: 'Reps', showgrid: false },
|
|
||||||
yaxis2: {
|
|
||||||
title: 'Weight',
|
|
||||||
overlaying: 'y',
|
|
||||||
side: 'right',
|
|
||||||
showgrid: false,
|
|
||||||
position: 0.94
|
|
||||||
},
|
|
||||||
yaxis3: {
|
|
||||||
title: 'Estimated 1RM',
|
|
||||||
overlaying: 'y',
|
|
||||||
side: 'right',
|
|
||||||
showgrid: false,
|
|
||||||
position: 1.00
|
|
||||||
},
|
|
||||||
showlegend: false,
|
|
||||||
legend: {
|
|
||||||
|
|
||||||
font: {
|
|
||||||
size: 10 // Adjust the font size of the legend to make it smaller
|
|
||||||
},
|
|
||||||
"orientation": "h"
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
responsive: true,
|
|
||||||
displayModeBar: false,
|
|
||||||
//staticPlot: true,
|
|
||||||
displaylogo: false,
|
|
||||||
modeBarButtonsToRemove: ['toImage', 'sendDataToCloud', 'zoom2d', 'pan2d', 'select2d', 'lasso2d',
|
|
||||||
'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d', 'hoverClosestCartesian',
|
|
||||||
'hoverCompareCartesian', 'zoom3d', 'pan3d', 'orbitRotation', 'tableRotation',
|
|
||||||
'resetCameraDefault3d', 'resetCameraLastSave3d', 'hoverClosest3d', 'zoomInGeo',
|
|
||||||
'zoomOutGeo', 'resetGeo', 'hoverClosestGeo', 'hoverClosestGl2d', 'hoverClosestPie',
|
|
||||||
'toggleHover', 'resetViews', 'toggleSpikelines', 'resetViewMapbox'
|
|
||||||
]
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{{ render_partial('partials/stats.html', stats=person['Stats']) }}
|
{{ render_partial('partials/stats.html', stats=person['Stats']) }}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user