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
|
||||
|
||||
|
||||
@app.route('/user/<int:user_id>/workout/<int:workout_id>', methods=['GET', 'DELETE'])
|
||||
def workout(user_id, workout_id):
|
||||
@app.route('/user/<int:user_id>/workout/<int:workout_id>/<string:graph_type>', methods=['GET', 'DELETE'])
|
||||
def workout(user_id, workout_id, graph_type):
|
||||
workout = Workout.query.filter_by(user_id=user_id, id=workout_id).first()
|
||||
if workout:
|
||||
if request.method == 'GET':
|
||||
# Get the cadence readings for the workout
|
||||
cadence_readings = CadenceReading.query.filter_by(
|
||||
workout_id=workout_id).all()
|
||||
if cadence_readings:
|
||||
# Create a graph of cadence readings
|
||||
x_values = [reading.created_at for reading in cadence_readings]
|
||||
# Get the cadence readings for the workout
|
||||
cadence_readings = CadenceReading.query.filter_by(
|
||||
workout_id=workout_id).all()
|
||||
if cadence_readings:
|
||||
x_values = [reading.created_at for reading in cadence_readings]
|
||||
if graph_type == 'cadence':
|
||||
y_values = [reading.rpm for reading in cadence_readings]
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x_values, y_values)
|
||||
ax.set_xlabel('Time')
|
||||
ax.set_ylabel('Cadence (RPM)')
|
||||
|
||||
# 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=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)
|
||||
return create_graph(x_values=x_values, y_values=y_values, y_label='Cadence (RPM)', filename='cadence'), 200
|
||||
elif graph_type == 'speed':
|
||||
y_values = [reading.speed for reading in cadence_readings]
|
||||
return create_graph(x_values=x_values, y_values=y_values, y_label='Speed (KPH)', filename='speed'), 200
|
||||
elif graph_type == 'distance':
|
||||
y_values = [reading.distance for reading in cadence_readings]
|
||||
return create_graph(x_values=x_values, y_values=y_values, y_label='Distance (KM)', filename='distance'), 200
|
||||
elif graph_type == 'calories':
|
||||
y_values = [reading.calories for reading in cadence_readings]
|
||||
return create_graph(x_values=x_values, y_values=y_values, y_label='Calories (KCAL)', filename='calories'), 200
|
||||
elif graph_type == 'power':
|
||||
y_values = [reading.power for reading in cadence_readings]
|
||||
return create_graph(x_values=x_values, y_values=y_values, y_label='Power (WATTS)', filename='power'), 200
|
||||
else:
|
||||
return jsonify({'message': 'No cadence readings for workout {}.'.format(workout_id)}), 404
|
||||
else:
|
||||
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'])
|
||||
def 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
|
||||
|
||||
|
||||
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):
|
||||
ordinal = {'1': 'st', '2': 'nd', '3': 'rd'}.get(str(d.day)[-1:], 'th')
|
||||
return d.strftime(format_string).replace('{th}', ordinal)
|
||||
|
||||
@@ -19,11 +19,19 @@
|
||||
</button>
|
||||
</h2>
|
||||
<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"
|
||||
class="mx-auto">
|
||||
<img src="{{ url_for('workout', user_id=w.user_id, workout_id=w.id, graph_type='cadence') }}" loading="lazy"
|
||||
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
|
||||
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-target="#container">
|
||||
<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