Change matplotlib backend to speed up plottng time (~0.25 to ~0.03 seconds)

This commit is contained in:
Peter Stockings
2023-10-19 22:19:24 +11:00
parent fa1e76df47
commit dafc23af49

113
app.py
View File

@@ -1,3 +1,11 @@
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
from dateutil.relativedelta import relativedelta
import humanize
from dateutil.parser import isoparse
import sparklines
import os
import time
from sqlalchemy import func
from datetime import datetime, timedelta
import io
@@ -5,13 +13,8 @@ from flask_sqlalchemy import SQLAlchemy
from flask import Flask, make_response, render_template, request, jsonify
import jinja_partials
from flask_htmx import HTMX
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import os
import sparklines
from dateutil.parser import isoparse
import humanize
from dateutil.relativedelta import relativedelta
import matplotlib
matplotlib.use('Agg')
app = Flask(__name__)
@@ -358,63 +361,66 @@ def get_workouts_for_user(user, workouts):
return [get_workout_view_data(workout, user) for workout in workouts if get_workout_view_data(workout, user)]
def format_workout_data(workout, user):
"""
Formats the workout data for view.
Parameters:
- workout: The workout object.
- user: The user associated with the workout.
Returns:
- dict: A dictionary with formatted workout data.
"""
duration = timedelta(seconds=int(workout.duration or 0))
return {
'id': workout.id,
'user_id': user.id,
'user_name': user.name,
'start_time': format_date_with_ordinal(workout.started_at, '%#H:%M %B %dth %Y'),
'start_time_date': workout.started_at,
'start_time_ago': humanize.naturaltime(workout.started_at),
'duration': humanize.naturaldelta(duration),
'duration_minutes': duration.total_seconds() / 60,
'average_rpm': int(workout.average_rpm or 0),
'min_rpm': int(workout.min_rpm or 0),
'max_rpm': int(workout.max_rpm or 0),
'calories': int(workout.calories or 0),
'distance': int(workout.distance or 0),
'bike_display_name': workout.bike.display_name,
'selected_graph_types': ['speed'] + (['heart_rate'] if workout.is_heart_rate_available else []),
'is_heart_rate_available': workout.is_heart_rate_available,
'is_cadence_available': workout.is_cadence_available,
'average_bpm': int(workout.average_bpm or 0),
'min_bpm': int(workout.min_bpm or 0),
'max_bpm': int(workout.max_bpm or 0),
}
def get_workout_view_data(workout, user):
duration = timedelta(
seconds=int(workout.duration)) if workout.duration else timedelta(seconds=0)
average_rpm = workout.average_rpm if workout.average_rpm else 0
min_rpm = workout.min_rpm if workout.min_rpm else 0
max_rpm = workout.max_rpm if workout.max_rpm else 0
calories = workout.calories if workout.calories else 0
distance = workout.distance if workout.distance else 0
average_bpm = workout.average_bpm if workout.average_bpm else 0
min_bpm = workout.min_bpm if workout.min_bpm else 0
max_bpm = workout.max_bpm if workout.max_bpm else 0
is_heart_rate_available = workout.is_heart_rate_available
is_cadence_available = workout.is_cadence_available
start_time = workout.started_at
"""
Retrieve view data for a single workout.
selected_graph_types = ['speed']
if is_heart_rate_available:
selected_graph_types.append('heart_rate')
Parameters:
- workout: The workout object.
- user: The user associated with the workout.
if is_cadence_available or is_heart_rate_available:
return {
'id': workout.id,
'user_id': user.id,
'user_name': user.name,
'start_time': format_date_with_ordinal(start_time, '%#H:%M %B %dth %Y'),
'start_time_date': start_time,
'start_time_ago': humanize.naturaltime(start_time),
'duration': humanize.naturaldelta(duration),
'duration_minutes': duration.total_seconds() / 60,
'average_rpm': int(average_rpm),
'min_rpm': int(min_rpm),
'max_rpm': int(max_rpm),
'calories': int(calories),
'distance': int(distance),
'bike_display_name': workout.bike.display_name,
'selected_graph_types': selected_graph_types,
'is_heart_rate_available': is_heart_rate_available,
'is_cadence_available': is_cadence_available,
'average_bpm': int(average_bpm),
'min_bpm': int(min_bpm),
'max_bpm': int(max_bpm),
}
else:
return None
Returns:
- dict or None: A dictionary containing view data if cadence or heart rate is available, otherwise None.
"""
if workout.is_cadence_available or workout.is_heart_rate_available:
return format_workout_data(workout, user)
return None
def create_graph(x_values, y_values, y_label, filename, x_label='Time'):
# Plotting
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
@@ -422,6 +428,7 @@ def create_graph(x_values, y_values, y_label, filename, x_label='Time'):
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'