Persists list of rendered user graphs in db, graph is removed list on click

This commit is contained in:
Peter Stockings
2023-10-24 21:29:52 +11:00
parent b128ede9d0
commit 57753fc2fc
2 changed files with 94 additions and 6 deletions

90
app.py
View File

@@ -105,6 +105,17 @@ class HeartRateReading(db.Model):
bpm = db.Column(db.Integer, nullable=False) bpm = db.Column(db.Integer, nullable=False)
class UserGraphs(db.Model):
__tablename__ = 'user_graphs'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey(
'users.id', ondelete='CASCADE'), nullable=False)
start_date = db.Column(db.DateTime, nullable=False)
end_date = db.Column(db.DateTime, nullable=False)
period = db.Column(db.String(255), nullable=False)
attributes = db.Column(db.ARRAY(db.String(255)))
@app.route('/', methods=['GET']) @app.route('/', methods=['GET'])
def overview(): def overview():
return render_users_and_workouts() return render_users_and_workouts()
@@ -243,9 +254,27 @@ def graph_user_workouts(user_id):
period = request.args.get('period', default='day', type=str) period = request.args.get('period', default='day', type=str)
# Add record of user graph
insert_usergraph_if_not_exists(
user_id, start_date, end_date, period, attributes)
return plot_averaged_attributes(workouts, user, start_date, end_date, period, attributes) return plot_averaged_attributes(workouts, user, start_date, end_date, period, attributes)
@app.route('/user/<int:user_id>/workouts/graph/delete', methods=['DELETE'])
def delete_user_graph(user_id):
attributes = request.args.getlist(
'attributes', type=str)
start_date = request.args.get(
'start_date', default=datetime.now().date(), type=toDate)
end_date = request.args.get(
'end_date', default=datetime.now().date(), type=toDate)
period = request.args.get('period', default='day', type=str)
remove_usergraph(user_id, start_date, end_date, period, attributes)
return ""
def daterange(start_date, end_date, delta=timedelta(days=1)): def daterange(start_date, end_date, delta=timedelta(days=1)):
"""Helper generator to iterate over date ranges.""" """Helper generator to iterate over date ranges."""
curr_date = start_date curr_date = start_date
@@ -486,7 +515,8 @@ def generate_user_data(user, workouts=[]):
'attributes': [('workout_count', 'Workout count'), ('duration_seconds', 'Duration (sec)'), ('duration_minutes', 'Duration (min)'), ('average_rpm', 'Average RPM'), ('max_rpm', 'Max RPM'), ('average_bpm', 'Average BPM'), ('max_bpm', 'Max BPM'), ('distance', 'Distance'), ('calories', 'Calories')], 'attributes': [('workout_count', 'Workout count'), ('duration_seconds', 'Duration (sec)'), ('duration_minutes', 'Duration (min)'), ('average_rpm', 'Average RPM'), ('max_rpm', 'Max RPM'), ('average_bpm', 'Average BPM'), ('max_bpm', 'Max BPM'), ('distance', 'Distance'), ('calories', 'Calories')],
'periods': [('day', 'Day'), ('week', 'Week'), ('month', 'Month')], 'periods': [('day', 'Day'), ('week', 'Week'), ('month', 'Month')],
# (period: str, attributes: [str]) # (period: str, attributes: [str])
'graphs': [('month', ['duration_minutes']), ('week', ['average_rpm', 'average_bpm']), ('week', ['workout_count'])], # 'graphs': [('month', ['duration_minutes']), ('week', ['average_rpm', 'average_bpm']), ('week', ['workout_count'])],
'graphs': get_user_graphs(user.id),
'first_workout_date': workouts[-1]['start_time_date'] if workouts else None, 'first_workout_date': workouts[-1]['start_time_date'] if workouts else None,
'last_workout_date': workouts[0]['start_time_date'] if workouts else None, 'last_workout_date': workouts[0]['start_time_date'] if workouts else None,
} }
@@ -638,7 +668,8 @@ def generate_calendar_monthly_view(workouts, selected_date):
start_date, end_date = get_month_bounds(selected_date) start_date, end_date = get_month_bounds(selected_date)
# Build a lookup dictionary for faster access # Build a lookup dictionary for faster access
workout_lookup = {w['start_time_date'] : w for w in workouts if start_date <= w['start_time_date'] <= end_date} workout_lookup = {w['start_time_date']
: w for w in workouts if start_date <= w['start_time_date'] <= end_date}
current_date = datetime.now().date() current_date = datetime.now().date()
days_of_month = [ days_of_month = [
@@ -695,6 +726,61 @@ def generate_daily_duration_sparkline(workouts):
return sparklines.sparklines(daily_durations)[0] return sparklines.sparklines(daily_durations)[0]
def get_user_graphs(user_id):
"""Retrieve a list of UserGraphs entries for the given user_id."""
user_graphs = UserGraphs.query.filter_by(
user_id=user_id).order_by(UserGraphs.id.desc()).all()
# change start_date, end_date from datetime to dates
for user_graph in user_graphs:
user_graph.start_date = user_graph.start_date.date()
user_graph.end_date = user_graph.end_date.date()
return user_graphs
def insert_usergraph_if_not_exists(user_id, start_date, end_date, period, attributes):
"""Insert a UserGraphs entry if it doesn't already exist based on specified attributes and return its ID."""
existing_graph = UserGraphs.query.filter_by(
user_id=user_id,
start_date=start_date,
end_date=end_date,
period=period,
attributes=attributes
).first()
if not existing_graph:
new_graph = UserGraphs(
user_id=user_id,
start_date=start_date,
end_date=end_date,
period=period,
attributes=attributes
)
db.session.add(new_graph)
db.session.commit()
return new_graph.id # Return the ID of the newly added object
return None # Return None if the record already exists
def remove_usergraph(user_id, start_date, end_date, period, attributes):
"""Remove a UserGraphs entry based on specified attributes."""
existing_graph = UserGraphs.query.filter_by(
user_id=user_id,
start_date=start_date,
end_date=end_date,
period=period,
attributes=attributes
).first()
if existing_graph:
db.session.delete(existing_graph)
db.session.commit()
def toDate(dateString): def toDate(dateString):
return datetime.strptime(dateString, "%Y-%m-%d").date() return datetime.strptime(dateString, "%Y-%m-%d").date()

View File

@@ -1,4 +1,4 @@
<div class="flex flex-wrap mb-1"> <div class="flex flex-wrap mb-5">
<div class="w-full md:w-1/5 px-3 mb-6 md:mb-0"> <div class="w-full md:w-1/5 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">
@@ -89,8 +89,10 @@
</div> </div>
<div id="user-workouts-overview-graphs-{{ user.id }}"> <div id="user-workouts-overview-graphs-{{ user.id }}">
{% for period, attributes in user.graphs %} {% for graph in user.graphs %}
<img src="{{ url_for('graph_user_workouts', user_id=user.id, period=period, attributes=attributes) }}" <img src="{{ url_for('graph_user_workouts', user_id=user.id, start_date=graph.start_date, end_date=graph.end_date, period=graph.period, attributes=graph.attributes) }}"
loading="lazy" alt="No image" class="mx-auto" _="on click remove me"> loading="lazy" alt="No image" class="mx-auto"
hx-delete="{{ url_for('delete_user_graph', user_id=user.id, start_date=graph.start_date, end_date=graph.end_date, period=graph.period, attributes=graph.attributes ) }}"
hx-swap="outerHTML">
{% endfor %} {% endfor %}
</div> </div>