Graph speed, distance, calories, & power (crashes locally, possible revert, will attempt to combine graphs)
This commit is contained in:
98
app.py
98
app.py
@@ -127,53 +127,46 @@ def workouts(user_id):
|
|||||||
return jsonify({'message': 'Workout created successfully.'}), 201
|
return jsonify({'message': 'Workout created successfully.'}), 201
|
||||||
|
|
||||||
|
|
||||||
@app.route('/user/<int:user_id>/workout/<int:workout_id>', methods=['GET', 'DELETE'])
|
@app.route('/user/<int:user_id>/workout/<int:workout_id>/<string:graph_type>', methods=['GET', 'DELETE'])
|
||||||
def workout(user_id, workout_id):
|
def workout(user_id, workout_id, graph_type):
|
||||||
workout = Workout.query.filter_by(user_id=user_id, id=workout_id).first()
|
workout = Workout.query.filter_by(user_id=user_id, id=workout_id).first()
|
||||||
if workout:
|
if workout:
|
||||||
if request.method == 'GET':
|
# Get the cadence readings for the workout
|
||||||
# Get the cadence readings for the workout
|
cadence_readings = CadenceReading.query.filter_by(
|
||||||
cadence_readings = CadenceReading.query.filter_by(
|
workout_id=workout_id).all()
|
||||||
workout_id=workout_id).all()
|
if cadence_readings:
|
||||||
if cadence_readings:
|
x_values = [reading.created_at for reading in cadence_readings]
|
||||||
# Create a graph of cadence readings
|
if graph_type == 'cadence':
|
||||||
x_values = [reading.created_at for reading in cadence_readings]
|
|
||||||
y_values = [reading.rpm for reading in cadence_readings]
|
y_values = [reading.rpm for reading in cadence_readings]
|
||||||
fig, ax = plt.subplots()
|
return create_graph(x_values=x_values, y_values=y_values, y_label='Cadence (RPM)', filename='cadence'), 200
|
||||||
ax.plot(x_values, y_values)
|
elif graph_type == 'speed':
|
||||||
ax.set_xlabel('Time')
|
y_values = [reading.speed for reading in cadence_readings]
|
||||||
ax.set_ylabel('Cadence (RPM)')
|
return create_graph(x_values=x_values, y_values=y_values, y_label='Speed (KPH)', filename='speed'), 200
|
||||||
|
elif graph_type == 'distance':
|
||||||
# ax.set_title(
|
y_values = [reading.distance for reading in cadence_readings]
|
||||||
# 'Cadence Readings for Workout {}'.format(workout_id))
|
return create_graph(x_values=x_values, y_values=y_values, y_label='Distance (KM)', filename='distance'), 200
|
||||||
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M"))
|
elif graph_type == 'calories':
|
||||||
|
y_values = [reading.calories for reading in cadence_readings]
|
||||||
# set the y-axis limits to start at 0
|
return create_graph(x_values=x_values, y_values=y_values, y_label='Calories (KCAL)', filename='calories'), 200
|
||||||
ax.set_ylim(bottom=0)
|
elif graph_type == 'power':
|
||||||
|
y_values = [reading.power for reading in cadence_readings]
|
||||||
# Save the graph to a bytes buffer
|
return create_graph(x_values=x_values, y_values=y_values, y_label='Power (WATTS)', filename='power'), 200
|
||||||
buffer = io.BytesIO()
|
else:
|
||||||
plt.savefig(buffer, format='png',
|
return jsonify({'message': 'No cadence readings for workout {}.'.format(workout_id)}), 404
|
||||||
transparent=True, bbox_inches='tight')
|
|
||||||
buffer.seek(0)
|
|
||||||
# Create a response object with the graph image
|
|
||||||
response = make_response(buffer.getvalue())
|
|
||||||
response.headers['Content-Type'] = 'image/png'
|
|
||||||
response.headers['Content-Disposition'] = 'attachment; filename=cadence.png'
|
|
||||||
return response, 200
|
|
||||||
else:
|
|
||||||
return jsonify({'message': 'No cadence readings for workout {}.'.format(workout_id)}), 404
|
|
||||||
elif request.method == 'DELETE':
|
|
||||||
# Delete the workout and its associated cadence readings
|
|
||||||
CadenceReading.query.filter_by(workout_id=workout_id).delete()
|
|
||||||
db.session.delete(workout)
|
|
||||||
db.session.commit()
|
|
||||||
workouts_data = get_workouts_for_user(user_id)
|
|
||||||
return render_template('workouts_list.html', workouts=workouts_data)
|
|
||||||
else:
|
else:
|
||||||
return jsonify({'message': 'Workout {} not found for user {}.'.format(workout_id, user_id)}), 404
|
return jsonify({'message': 'Workout {} not found for user {}.'.format(workout_id, user_id)}), 404
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/user/<int:user_id>/workout/<int:workout_id>/delete', methods=['DELETE'])
|
||||||
|
def delete_workout(user_io, workout_id):
|
||||||
|
# Delete the workout and its associated cadence readings
|
||||||
|
CadenceReading.query.filter_by(workout_id=workout_id).delete()
|
||||||
|
db.session.delete(workout)
|
||||||
|
db.session.commit()
|
||||||
|
workouts_data = get_workouts_for_user(user_id)
|
||||||
|
return render_template('workouts_list.html', workouts=workouts_data)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/user/<int:user_id>/workouts', methods=['GET', 'DELETE'])
|
@app.route('/user/<int:user_id>/workouts', methods=['GET', 'DELETE'])
|
||||||
def workouts_for_user(user_id):
|
def workouts_for_user(user_id):
|
||||||
workouts_data = get_workouts_for_user(user_id)
|
workouts_data = get_workouts_for_user(user_id)
|
||||||
@@ -238,6 +231,31 @@ def get_workouts_for_user(user_id):
|
|||||||
return workouts_data
|
return workouts_data
|
||||||
|
|
||||||
|
|
||||||
|
def create_graph(x_values, y_values, y_label, filename, x_label='Time'):
|
||||||
|
fig, ax = plt.subplots()
|
||||||
|
ax.plot(x_values, y_values)
|
||||||
|
ax.set_xlabel(x_label)
|
||||||
|
ax.set_ylabel(y_label)
|
||||||
|
|
||||||
|
# ax.set_title(
|
||||||
|
# 'Cadence Readings for Workout {}'.format(workout_id))
|
||||||
|
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M"))
|
||||||
|
|
||||||
|
# set the y-axis limits to start at 0
|
||||||
|
ax.set_ylim(bottom=0)
|
||||||
|
|
||||||
|
# Save the graph to a bytes buffer
|
||||||
|
buffer = io.BytesIO()
|
||||||
|
plt.savefig(buffer, format='png',
|
||||||
|
transparent=True, bbox_inches='tight')
|
||||||
|
buffer.seek(0)
|
||||||
|
# Create a response object with the graph image
|
||||||
|
response = make_response(buffer.getvalue())
|
||||||
|
response.headers['Content-Type'] = 'image/png'
|
||||||
|
response.headers['Content-Disposition'] = 'attachment; filename={filename}.png'
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def format_date_with_ordinal(d, format_string):
|
def format_date_with_ordinal(d, format_string):
|
||||||
ordinal = {'1': 'st', '2': 'nd', '3': 'rd'}.get(str(d.day)[-1:], 'th')
|
ordinal = {'1': 'st', '2': 'nd', '3': 'rd'}.get(str(d.day)[-1:], 'th')
|
||||||
return d.strftime(format_string).replace('{th}', ordinal)
|
return d.strftime(format_string).replace('{th}', ordinal)
|
||||||
|
|||||||
@@ -19,11 +19,19 @@
|
|||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="!visible collapse {% if loop.index != 1 %}hidden{% endif %}">
|
<div class="!visible collapse {% if loop.index != 1 %}hidden{% endif %}">
|
||||||
<img src="{{ url_for('workout', user_id=w.user_id, workout_id=w.id) }}" loading="lazy" alt="No image"
|
<img src="{{ url_for('workout', user_id=w.user_id, workout_id=w.id, graph_type='cadence') }}" loading="lazy"
|
||||||
class="mx-auto">
|
alt="No image" class="mx-auto">
|
||||||
|
<img src="{{ url_for('workout', user_id=w.user_id, workout_id=w.id, graph_type='speed') }}" loading="lazy"
|
||||||
|
alt="No image" class="mx-auto">
|
||||||
|
<img src="{{ url_for('workout', user_id=w.user_id, workout_id=w.id, graph_type='distance') }}" loading="lazy"
|
||||||
|
alt="No image" class="mx-auto">
|
||||||
|
<img src="{{ url_for('workout', user_id=w.user_id, workout_id=w.id, graph_type='calories') }}" loading="lazy"
|
||||||
|
alt="No image" class="mx-auto">
|
||||||
|
<img src="{{ url_for('workout', user_id=w.user_id, workout_id=w.id, graph_type='power') }}" loading="lazy"
|
||||||
|
alt="No image" class="mx-auto">
|
||||||
<button
|
<button
|
||||||
class="mx-4 mb-4 bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"
|
class="mx-4 mb-4 bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"
|
||||||
hx-delete="{{ url_for('workout', user_id=w.user_id, workout_id=w.id) }}"
|
hx-delete="{{ url_for('delete_workout', user_id=w.user_id, workout_id=w.id) }}"
|
||||||
hx-confirm="Are you sure you wish to delete this {{ w.duration }} workout at {{ w.start_time }}"
|
hx-confirm="Are you sure you wish to delete this {{ w.duration }} workout at {{ w.start_time }}"
|
||||||
hx-target="#container">
|
hx-target="#container">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
|
|||||||
Reference in New Issue
Block a user