Read distance, calories, max_rpm, min_rpm, average_rpm etc from workout rather then recalculating on each render, still need to modify add workout endpoint to calculate properties and set on workout
This commit is contained in:
126
app.py
126
app.py
@@ -14,6 +14,7 @@ import sparklines
|
|||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
# TODO CHANGE SECRET KEY TO ENVIRONMENT VARIABLE
|
# TODO CHANGE SECRET KEY TO ENVIRONMENT VARIABLE
|
||||||
app.config['SECRET_KEY'] = 'secret!'
|
app.config['SECRET_KEY'] = 'secret!'
|
||||||
|
app.config["SQLALCHEMY_ECHO"] = False # True for debugging
|
||||||
uri = os.getenv("DATABASE_URL")
|
uri = os.getenv("DATABASE_URL")
|
||||||
if uri and uri.startswith("postgres://"):
|
if uri and uri.startswith("postgres://"):
|
||||||
uri = uri.replace("postgres://", "postgresql://", 1)
|
uri = uri.replace("postgres://", "postgresql://", 1)
|
||||||
@@ -50,8 +51,23 @@ class Workout(db.Model):
|
|||||||
created_at = db.Column(db.DateTime, nullable=False, default=db.func.now())
|
created_at = db.Column(db.DateTime, nullable=False, default=db.func.now())
|
||||||
bike_id = db.Column(db.Integer, db.ForeignKey(
|
bike_id = db.Column(db.Integer, db.ForeignKey(
|
||||||
'bikes.id', ondelete='CASCADE'), nullable=False)
|
'bikes.id', ondelete='CASCADE'), nullable=False)
|
||||||
|
started_at = db.Column(db.DateTime, nullable=False)
|
||||||
|
duration = db.Column(db.Numeric, nullable=False)
|
||||||
|
average_rpm = db.Column(db.Numeric, nullable=False)
|
||||||
|
min_rpm = db.Column(db.Integer, nullable=False)
|
||||||
|
max_rpm = db.Column(db.Integer, nullable=False)
|
||||||
|
calories = db.Column(db.Numeric, nullable=False)
|
||||||
|
distance = db.Column(db.Numeric, nullable=False)
|
||||||
|
average_bpm = db.Column(db.Numeric, nullable=False)
|
||||||
|
min_bpm = db.Column(db.Integer, nullable=False)
|
||||||
|
max_bpm = db.Column(db.Integer, nullable=False)
|
||||||
|
is_heart_rate_available = db.Column(
|
||||||
|
db.Boolean, nullable=False, default=False)
|
||||||
|
is_cadence_available = db.Column(db.Boolean, nullable=False, default=False)
|
||||||
cadence_readings = db.relationship(
|
cadence_readings = db.relationship(
|
||||||
'CadenceReading', backref='workout', lazy=True)
|
'CadenceReading', backref='workout', lazy=True)
|
||||||
|
heart_rate_readings = db.relationship(
|
||||||
|
'HeartRateReading', backref='workout', lazy=True)
|
||||||
bike = db.relationship('Bike', backref='workouts', lazy=True)
|
bike = db.relationship('Bike', backref='workouts', lazy=True)
|
||||||
|
|
||||||
|
|
||||||
@@ -154,39 +170,39 @@ def workouts(user_id):
|
|||||||
|
|
||||||
@app.route('/user/<int:user_id>/workout/<int:workout_id>/<string:graph_type>', 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, graph_type):
|
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).join(
|
||||||
if workout:
|
Workout.cadence_readings).join(Workout.heart_rate_readings).first()
|
||||||
# Get the cadence readings for the workout
|
|
||||||
cadence_readings = CadenceReading.query.filter_by(
|
if not workout:
|
||||||
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]
|
|
||||||
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
|
|
||||||
heart_rate_readings = HeartRateReading.query.filter_by(
|
|
||||||
workout_id=workout_id).all()
|
|
||||||
if heart_rate_readings:
|
|
||||||
x_values = [reading.created_at for reading in heart_rate_readings]
|
|
||||||
y_values = [reading.bpm for reading in heart_rate_readings]
|
|
||||||
return create_graph(x_values=x_values, y_values=y_values, y_label='Heart Rate (BPM)', filename='heart_rate'), 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
|
return jsonify({'message': 'Workout {} not found for user {}.'.format(workout_id, user_id)}), 404
|
||||||
|
|
||||||
|
if workout.is_cadence_available:
|
||||||
|
x_values = [reading.created_at for reading in workout.cadence_readings]
|
||||||
|
if graph_type == 'cadence':
|
||||||
|
y_values = [reading.rpm for reading in workout.cadence_readings]
|
||||||
|
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 workout.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 workout.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 workout.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 workout.cadence_readings]
|
||||||
|
return create_graph(x_values=x_values, y_values=y_values, y_label='Power (WATTS)', filename='power'), 200
|
||||||
|
if workout.is_heart_rate_available:
|
||||||
|
x_values = [
|
||||||
|
reading.created_at for reading in workout.heart_rate_readings]
|
||||||
|
y_values = [reading.bpm for reading in workout.heart_rate_readings]
|
||||||
|
return create_graph(x_values=x_values, y_values=y_values, y_label='Heart Rate (BPM)', filename='heart_rate'), 200
|
||||||
|
|
||||||
|
return jsonify({'message': 'Unable to generate {} for workout {}.'.format(graph_type, workout_id)}), 409
|
||||||
|
|
||||||
|
|
||||||
@app.route('/user/<int:user_id>/workout/<int:workout_id>/view', methods=['GET'])
|
@app.route('/user/<int:user_id>/workout/<int:workout_id>/view', methods=['GET'])
|
||||||
def view_workout(user_id, workout_id):
|
def view_workout(user_id, workout_id):
|
||||||
@@ -296,32 +312,28 @@ def get_workouts_for_user(user_id):
|
|||||||
workouts_data = []
|
workouts_data = []
|
||||||
workouts = Workout.query.filter_by(user_id=user_id).order_by(
|
workouts = Workout.query.filter_by(user_id=user_id).order_by(
|
||||||
Workout.created_at.desc()).all()
|
Workout.created_at.desc()).all()
|
||||||
|
|
||||||
for workout in workouts:
|
for workout in workouts:
|
||||||
cadence_readings = CadenceReading.query.filter_by(
|
|
||||||
workout_id=workout.id).all()
|
|
||||||
|
|
||||||
if cadence_readings:
|
duration = timedelta(
|
||||||
start_time = min(
|
seconds=int(workout.duration)) if workout.duration else timedelta(seconds=0)
|
||||||
reading.created_at for reading in cadence_readings)
|
average_rpm = workout.average_rpm if workout.average_rpm else 0
|
||||||
end_time = max(
|
min_rpm = workout.min_rpm if workout.min_rpm else 0
|
||||||
reading.created_at for reading in cadence_readings)
|
max_rpm = workout.max_rpm if workout.max_rpm else 0
|
||||||
duration = end_time - start_time
|
calories = workout.calories if workout.calories else 0
|
||||||
average_rpm = sum(
|
distance = workout.distance if workout.distance else 0
|
||||||
reading.rpm for reading in cadence_readings) / len(cadence_readings)
|
average_bpm = workout.average_bpm if workout.average_bpm else 0
|
||||||
calories = cadence_readings[-1].calories
|
min_bpm = workout.min_bpm if workout.min_bpm else 0
|
||||||
distance = cadence_readings[-1].distance
|
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
|
||||||
|
|
||||||
selected_graph_types = ['speed']
|
selected_graph_types = ['speed']
|
||||||
is_heart_rate_available = False
|
if is_heart_rate_available:
|
||||||
heart_rate_readings = HeartRateReading.query.filter_by(
|
selected_graph_types.append('heart_rate')
|
||||||
workout_id=workout.id).all()
|
|
||||||
average_bpm = 0
|
|
||||||
if heart_rate_readings:
|
|
||||||
selected_graph_types.append('heart_rate')
|
|
||||||
is_heart_rate_available = True
|
|
||||||
average_bpm = sum(heartrate.bpm for heartrate in heart_rate_readings) / \
|
|
||||||
len(heart_rate_readings)
|
|
||||||
|
|
||||||
|
if is_cadence_available or is_heart_rate_available:
|
||||||
workouts_data.append({
|
workouts_data.append({
|
||||||
'id': workout.id,
|
'id': workout.id,
|
||||||
'user_id': user_id,
|
'user_id': user_id,
|
||||||
@@ -331,13 +343,19 @@ def get_workouts_for_user(user_id):
|
|||||||
'duration': format_duration(duration),
|
'duration': format_duration(duration),
|
||||||
'duration_minutes': duration.total_seconds() / 60,
|
'duration_minutes': duration.total_seconds() / 60,
|
||||||
'average_rpm': int(average_rpm),
|
'average_rpm': int(average_rpm),
|
||||||
|
'min_rpm': int(min_rpm),
|
||||||
|
'max_rpm': int(max_rpm),
|
||||||
'calories': int(calories),
|
'calories': int(calories),
|
||||||
'distance': int(distance),
|
'distance': int(distance),
|
||||||
'bike_display_name': workout.bike.display_name,
|
'bike_display_name': workout.bike.display_name,
|
||||||
'selected_graph_types': selected_graph_types,
|
'selected_graph_types': selected_graph_types,
|
||||||
'is_heart_rate_available': is_heart_rate_available,
|
'is_heart_rate_available': is_heart_rate_available,
|
||||||
'average_bpm': int(average_bpm)
|
'is_cadence_available': is_cadence_available,
|
||||||
|
'average_bpm': int(average_bpm),
|
||||||
|
'min_bpm': int(min_bpm),
|
||||||
|
'max_bpm': int(max_bpm),
|
||||||
})
|
})
|
||||||
|
|
||||||
return workouts_data
|
return workouts_data
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user