From cec37fe1f792bbbdeb8acd484798d805e92e6c39 Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Thu, 26 Dec 2024 16:15:36 +1100 Subject: [PATCH] Add progress badge denoting current streak length, and refactor dashboard route --- app/routes.py | 135 +++++++++++++++++++++++++++++--------------------- 1 file changed, 78 insertions(+), 57 deletions(-) diff --git a/app/routes.py b/app/routes.py index 15a7100..2a339b7 100644 --- a/app/routes.py +++ b/app/routes.py @@ -58,84 +58,105 @@ def landing(): @main.route('/dashboard', methods=['GET', 'POST']) @login_required def dashboard(): - # Initialize start_date and end_date - start_date = None - end_date = None - - # Retrieve the first and last timestamps in a single query - first_last_readings = ( - db.session.query( - func.min(Reading.timestamp).label('first'), - func.max(Reading.timestamp).label('last') + # Helper function to get first and last reading timestamps + def get_reading_date_range(user_id): + result = ( + db.session.query( + func.min(Reading.timestamp).label('first'), + func.max(Reading.timestamp).label('last') + ) + .filter(Reading.user_id == user_id) + .first() ) - .filter(Reading.user_id == current_user.id) - .first() - ) + return result.first, result.last - # Extract the first and last timestamps - first_reading_timestamp = first_last_readings.first - last_reading_timestamp = first_last_readings.last + # Helper function to calculate weekly summary averages + def calculate_weekly_summary(readings): + one_week_ago = datetime.now() - timedelta(days=7) + weekly_readings = [r for r in readings if r.timestamp >= one_week_ago] + if weekly_readings: + systolic_avg = round(sum(r.systolic for r in weekly_readings) / len(weekly_readings), 1) + diastolic_avg = round(sum(r.diastolic for r in weekly_readings) / len(weekly_readings), 1) + heart_rate_avg = round(sum(r.heart_rate for r in weekly_readings) / len(weekly_readings), 1) + else: + systolic_avg = diastolic_avg = heart_rate_avg = 0 + return systolic_avg, diastolic_avg, heart_rate_avg - # Default to first and last reading dates if not provided - if not start_date and first_reading_timestamp: - start_date = first_reading_timestamp.strftime('%Y-%m-%d') - if not end_date and last_reading_timestamp: - end_date = last_reading_timestamp.strftime('%Y-%m-%d') + # Helper function to calculate progress badges + def calculate_progress_badges(readings, weekly_readings): + badges = [] + + # General badges + if len(readings) >= 10: + badges.append("10 Readings Logged") + if len(readings) >= 100: + badges.append("100 Readings Milestone") + if len(weekly_readings) >= 7: + badges.append("Logged Readings for 7 Days") + + # Streak badge + if readings: + streak_count = 1 # Start streak count with today's reading + today = datetime.now().date() - # Default to all readings + # Sort readings by timestamp in ascending order + sorted_readings = sorted(readings, key=lambda r: r.timestamp) + + for i in range(len(sorted_readings) - 1, 0, -1): + current_day = sorted_readings[i].timestamp.date() + previous_day = sorted_readings[i - 1].timestamp.date() + + # Check if days are consecutive + if (current_day - previous_day).days == 1: + streak_count += 1 + elif (current_day - previous_day).days > 1: + break # Streak is broken + + # Add streak badge if the streak is greater than 1 + if streak_count > 1 and sorted_readings[-1].timestamp.date() == today: + badges.append(f"Current Streak: {streak_count} Days") + + return badges + + + # Get the first and last reading timestamps + first_reading_timestamp, last_reading_timestamp = get_reading_date_range(current_user.id) + + # Set default start and end dates + start_date = first_reading_timestamp.strftime('%Y-%m-%d') if first_reading_timestamp else None + end_date = last_reading_timestamp.strftime('%Y-%m-%d') if last_reading_timestamp else None + + # Handle filtering for POST request readings_query = Reading.query.filter_by(user_id=current_user.id) - - # Handle filtering if it's a POST request if request.method == 'POST': - start_date = request.form.get('start_date') - end_date = request.form.get('end_date') - + start_date = request.form.get('start_date') or start_date + end_date = request.form.get('end_date') or end_date if start_date and end_date: - start_date_obj = datetime.strptime(start_date, '%Y-%m-%d') - end_date_obj = datetime.strptime(end_date, '%Y-%m-%d') readings_query = readings_query.filter( - Reading.timestamp >= start_date_obj, - Reading.timestamp <= end_date_obj + Reading.timestamp >= datetime.strptime(start_date, '%Y-%m-%d'), + Reading.timestamp <= datetime.strptime(end_date, '%Y-%m-%d') ) - # Format start_date and end_date for the template - start_date = start_date_obj.strftime('%Y-%m-%d') - end_date = end_date_obj.strftime('%Y-%m-%d') - - # Fetch and order readings + # Fetch readings readings = readings_query.order_by(Reading.timestamp.desc()).all() - # Add human-readable relative timestamps to readings + # Add relative timestamps to readings now = datetime.utcnow() for reading in readings: reading.relative_timestamp = humanize.naturaltime(now - reading.timestamp) + # Calculate weekly summary and progress badges + systolic_avg, diastolic_avg, heart_rate_avg = calculate_weekly_summary(readings) + weekly_readings = [r for r in readings if r.timestamp >= (datetime.now() - timedelta(days=7))] + badges = calculate_progress_badges(readings, weekly_readings) - # Weekly summary (last 7 days) - one_week_ago = datetime.now() - timedelta(days=7) - weekly_readings = [r for r in readings if r.timestamp >= one_week_ago] - systolic_avg = round(sum(r.systolic for r in weekly_readings) / len(weekly_readings), 1) if weekly_readings else 0 - diastolic_avg = round(sum(r.diastolic for r in weekly_readings) / len(weekly_readings), 1) if weekly_readings else 0 - heart_rate_avg = round(sum(r.heart_rate for r in weekly_readings) / len(weekly_readings), 1) if weekly_readings else 0 - - # Progress badges - badges = [] - if len(readings) >= 10: - badges.append("10 Readings Logged") - if len(readings) >= 100: - badges.append("100 Readings Milestone") - if len(weekly_readings) >= 7: - badges.append("Logged Readings for 7 Days") - - # Prepare data for the graphs + # Prepare graph data timestamps = [reading.timestamp.strftime('%b %d') for reading in readings] systolic = [reading.systolic for reading in readings] diastolic = [reading.diastolic for reading in readings] heart_rate = [reading.heart_rate for reading in readings] - # Pass the delete form to the template - delete_form = DeleteForm() - + # Render template return render_template( 'dashboard.html', readings=readings, @@ -144,7 +165,7 @@ def dashboard(): systolic_avg=systolic_avg, diastolic_avg=diastolic_avg, heart_rate_avg=heart_rate_avg, - delete_form=delete_form, + delete_form=DeleteForm(), timestamps=timestamps, systolic=systolic, diastolic=diastolic,