diff --git a/app/routes/main.py b/app/routes/main.py index 82d4af8..59262cc 100644 --- a/app/routes/main.py +++ b/app/routes/main.py @@ -24,89 +24,118 @@ def health(): @main.route('/dashboard', methods=['GET', 'POST']) @login_required def dashboard(): - """Render the dashboard with readings, stats, and calendar views.""" + """Render the dashboard shell and default list view.""" user_tz = timezone(current_user.profile.timezone or 'UTC') - # Get date range for readings + # Get date range for filters first_reading, last_reading = get_reading_date_range(current_user.id, user_tz) - start_date = request.form.get('start_date') or (first_reading and first_reading.strftime('%Y-%m-%d')) - end_date = request.form.get('end_date') or (last_reading and last_reading.strftime('%Y-%m-%d')) + start_date = request.form.get('start_date') or request.args.get('start_date') or (first_reading and first_reading.strftime('%Y-%m-%d')) + end_date = request.form.get('end_date') or request.args.get('end_date') or (last_reading and last_reading.strftime('%Y-%m-%d')) - # Pagination for list view - page = request.args.get('page', 1, type=int) - - # Weekly View Navigation Offset - week_offset = request.args.get('week_offset', 0, type=int) - - # Monthly View Navigation Offset - month_offset = request.args.get('month_offset', 0, type=int) - - # Fetch paginated readings for the list view - paginated = fetch_readings_paginated(current_user.id, start_date, end_date, user_tz, page, PAGE_SIZE) - - # For calendar/graph/badges, fetch only current month + week readings (much smaller set) - now = datetime.now(user_tz) - - # Calculate target week date for weekly view - target_week_date = now + timedelta(weeks=week_offset) - # Ensure we fetch enough data back to cover the week_offset - month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) - month_start_utc = month_start.astimezone(utc) - target_week_start = target_week_date - timedelta(days=target_week_date.weekday()) - target_week_start_utc = target_week_start.replace(hour=0, minute=0, second=0, microsecond=0).astimezone(utc) - - # Calculate target month date for monthly view - target_month_year = now.year + (now.month + month_offset - 1) // 12 - target_month_month = (now.month + month_offset - 1) % 12 + 1 - target_month_date = now.replace(year=target_month_year, month=target_month_month, day=1, hour=0, minute=0, second=0, microsecond=0) - target_month_start_utc = target_month_date.astimezone(utc) - - # Fetch from the earliest of the current month OR the requested target week/month - fetch_start_utc = min(month_start_utc, target_week_start_utc, target_month_start_utc) - calendar_readings = fetch_readings_for_range(current_user.id, fetch_start_utc) - - # Annotate all readings with relative and localized timestamps - annotate_readings(paginated.items, user_tz) - annotate_readings(calendar_readings, user_tz) - - # Build shared lookup for calendar views (single pass) - readings_by_day = build_readings_by_day(calendar_readings, user_tz) - - # Generate calendar views from the shared lookup - week_view = generate_weekly_calendar(readings_by_day, target_week_date, user_tz) - month_view = generate_monthly_calendar(readings_by_day, target_month_date, user_tz) - - # Calculate weekly averages via SQL (much faster than Python) + # Calculate weekly averages via SQL systolic_avg, diastolic_avg, heart_rate_avg = calculate_weekly_summary_sql(current_user.id) - # Badges from the paginated readings (or full set if needed) - badges = calculate_progress_badges(paginated.items) - - # Prepare graph data from calendar readings (current month) - graph_data = prepare_graph_data(calendar_readings) + # Badges need paginated or all readings. We'll fetch all readings for badges + # Note: To avoid huge queries, we might want a specific badge query in the future. + # For now, let's fetch current month readings + previous as a rough approximation or keep behavior. + # A generic query for badges: + all_readings = Reading.query.filter_by(user_id=current_user.id).order_by(Reading.timestamp.desc()).all() + badges = calculate_progress_badges(all_readings) + + # We will default to showing the list view on initial load + page = request.args.get('page', 1, type=int) + paginated = fetch_readings_paginated(current_user.id, start_date, end_date, user_tz, page, PAGE_SIZE) + annotate_readings(paginated.items, user_tz) return render_template( 'dashboard.html', - readings=paginated.items, - pagination=paginated, profile=current_user.profile, badges=badges, systolic_avg=systolic_avg, diastolic_avg=diastolic_avg, heart_rate_avg=heart_rate_avg, - delete_form=DeleteForm(), start_date=start_date, end_date=end_date, - month=month_view, - week=week_view, - date=date, - target_month_date=target_month_date, - week_offset=week_offset, - month_offset=month_offset, - active_view=request.args.get('activeView', 'list'), - **graph_data + delete_form=DeleteForm(), + active_view='list', + # default view context + readings=paginated.items, + pagination=paginated ) +@main.route('/dashboard/list', methods=['GET']) +@login_required +def dashboard_list(): + user_tz = timezone(current_user.profile.timezone or 'UTC') + first_reading, last_reading = get_reading_date_range(current_user.id, user_tz) + start_date = request.args.get('start_date') or (first_reading and first_reading.strftime('%Y-%m-%d')) + end_date = request.args.get('end_date') or (last_reading and last_reading.strftime('%Y-%m-%d')) + page = request.args.get('page', 1, type=int) + + paginated = fetch_readings_paginated(current_user.id, start_date, end_date, user_tz, page, PAGE_SIZE) + annotate_readings(paginated.items, user_tz) + + return render_template('partials/dashboard_list.html', readings=paginated.items, pagination=paginated) + +@main.route('/dashboard/weekly', methods=['GET']) +@login_required +def dashboard_weekly(): + user_tz = timezone(current_user.profile.timezone or 'UTC') + week_offset = request.args.get('week_offset', 0, type=int) + now = datetime.now(user_tz) + + target_week_date = now + timedelta(weeks=week_offset) + target_week_start = target_week_date - timedelta(days=target_week_date.weekday()) + target_week_start_utc = target_week_start.replace(hour=0, minute=0, second=0, microsecond=0).astimezone(utc) + target_week_end_utc = (target_week_start + timedelta(days=7)).replace(hour=0, minute=0, second=0, microsecond=0).astimezone(utc) + + calendar_readings = fetch_readings_for_range(current_user.id, target_week_start_utc, target_week_end_utc) + annotate_readings(calendar_readings, user_tz) + readings_by_day = build_readings_by_day(calendar_readings, user_tz) + week_view = generate_weekly_calendar(readings_by_day, target_week_date, user_tz) + + return render_template('partials/dashboard_weekly.html', week=week_view, week_offset=week_offset) + +@main.route('/dashboard/monthly', methods=['GET']) +@login_required +def dashboard_monthly(): + user_tz = timezone(current_user.profile.timezone or 'UTC') + month_offset = request.args.get('month_offset', 0, type=int) + now = datetime.now(user_tz) + + target_month_year = now.year + (now.month + month_offset - 1) // 12 + target_month_month = (now.month + month_offset - 1) % 12 + 1 + target_month_date = now.replace(year=target_month_year, month=target_month_month, day=1, hour=0, minute=0, second=0, microsecond=0) + + first_day = target_month_date + start_date = first_day - timedelta(days=(first_day.weekday() + 1) % 7) + end_date = start_date + timedelta(days=42) + + start_utc = start_date.replace(hour=0, minute=0, second=0, microsecond=0).astimezone(utc) + end_utc = end_date.replace(hour=0, minute=0, second=0, microsecond=0).astimezone(utc) + + calendar_readings = fetch_readings_for_range(current_user.id, start_utc, end_utc) + annotate_readings(calendar_readings, user_tz) + readings_by_day = build_readings_by_day(calendar_readings, user_tz) + month_view = generate_monthly_calendar(readings_by_day, target_month_date, user_tz) + + return render_template('partials/dashboard_monthly.html', month=month_view, target_month_date=target_month_date, month_offset=month_offset) + +@main.route('/dashboard/graph', methods=['GET']) +@login_required +def dashboard_graph(): + user_tz = timezone(current_user.profile.timezone or 'UTC') + now = datetime.now(user_tz) + month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) + month_start_utc = month_start.astimezone(utc) + + calendar_readings = fetch_readings_for_range(current_user.id, month_start_utc) + annotate_readings(calendar_readings, user_tz) + + graph_data = prepare_graph_data(calendar_readings) + + return render_template('partials/dashboard_graph.html', **graph_data) + def get_reading_date_range(user_id, user_tz): """Fetch the earliest and latest reading timestamps for a user.""" result = db.session.query( diff --git a/app/templates/_layout.html b/app/templates/_layout.html index d6907e7..28d312e 100644 --- a/app/templates/_layout.html +++ b/app/templates/_layout.html @@ -9,6 +9,7 @@ + diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index 95054dd..e1b3f6b 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -87,310 +87,25 @@