diff --git a/app.py b/app.py index b5d3491..7ec0e98 100644 --- a/app.py +++ b/app.py @@ -436,56 +436,61 @@ def format_duration(duration): return f"{minutes}m" +def date_range(start_date, end_date): + """ + Generator for dates between two dates (inclusive). + """ + current_date = start_date + while current_date <= end_date: + yield current_date + current_date += timedelta(days=1) + + +def get_month_bounds(dt): + """ + Determine the bounds of a month for a given date. + This considers the starting and ending weekdays to fit a calendar view. + """ + first_day_of_month = dt.replace(day=1) + next_month = first_day_of_month + relativedelta(months=1) + last_day_of_month = next_month - timedelta(days=1) + + # Define weekday mappings to determine start and end bounds + weekday_to_start_offset = {6: 0, 0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6} + weekday_to_end_offset = {6: 6, 0: 5, 1: 4, 2: 3, 3: 2, 4: 1, 5: 0} + + start_date = first_day_of_month - \ + timedelta(days=weekday_to_start_offset[first_day_of_month.weekday()]) + end_date = last_day_of_month + \ + timedelta(days=weekday_to_end_offset[last_day_of_month.weekday()]) + + return start_date, end_date + + def generate_calendar_monthly_view(workouts, selected_date): - # Generate a monthly calendar view of the workouts - def get_month_bounds(dt): - # First day of the month - first_day_of_month = dt.replace(day=1) - - # Last day of the month - next_month = first_day_of_month + relativedelta(months=1) - last_day_of_month = next_month - timedelta(days=1) - - start = dict( - [(6, 0), (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]) - start_date = first_day_of_month - \ - timedelta(days=start[first_day_of_month.weekday()]) - - end = dict([(6, 6), (0, 5), (1, 4), - (2, 3), (3, 2), (4, 1), (5, 0)]) - end_date = last_day_of_month + \ - timedelta(days=end[last_day_of_month.weekday()]) - - return start_date, end_date - - def date_range(start_date, end_date): - current_date = start_date - while current_date <= end_date: - yield current_date - current_date += timedelta(days=1) - - def get_workout_for_date(workouts, date): - for w in workouts: - if w['start_time_date'].date() == date: - return w - return None + """ + Generate a monthly calendar view of the workouts. + """ + def get_workout_for_date(workout_lookup, date): + return workout_lookup.get(date) start_date, end_date = get_month_bounds(selected_date) - monthly_workouts = [w for w in workouts if start_date <= - w['start_time_date'].date() <= end_date] + + # Build a lookup dictionary for faster access + workout_lookup = {w['start_time_date'].date( + ): w for w in workouts if start_date <= w['start_time_date'].date() <= end_date} current_date = datetime.now().date() days_of_month = [ { - 'date': single_date, - 'day_of_month': single_date.day, - 'is_workout': bool(workout), - 'workout': workout if workout else None, - 'is_current_date': single_date == current_date, - 'is_current_month': single_date.month == selected_date.month and single_date.year == selected_date.year + 'date': day, + 'day_of_month': day.day, + 'is_workout': day in workout_lookup, + 'workout': get_workout_for_date(workout_lookup, day), + 'is_current_date': day == current_date, + 'is_current_month': day.month == selected_date.month and day.year == selected_date.year } - for single_date in date_range(start_date, end_date) - for workout in [get_workout_for_date(monthly_workouts, single_date)] + for day in date_range(start_date, end_date) ] next_month_date = selected_date + relativedelta(months=1) @@ -500,12 +505,6 @@ def generate_calendar_monthly_view(workouts, selected_date): return calendar_month -def date_range(start_date, end_date): - """Return a list of dates between the given start and end dates (inclusive).""" - delta = end_date - start_date - return [(start_date + timedelta(days=i)) for i in range(delta.days + 1)] - - def generate_daily_duration_sparkline(workouts): """ Generate a sparkline string representation of daily workout durations. @@ -522,14 +521,13 @@ def generate_daily_duration_sparkline(workouts): # Determine date range based on workouts data start_date = workouts[-1]['start_time_date'].date() end_date = workouts[0]['start_time_date'].date() - dates_in_range = date_range(start_date, end_date) # Build a mapping of dates to their respective durations for easier lookup workouts_by_date = {w['start_time_date'].date(): int( w['duration_minutes']) for w in workouts} daily_durations = [workouts_by_date.get( - date, 0) for date in dates_in_range] + date, 0) for date in date_range(start_date, end_date)] # Reverse the list to make the most recent day appear on the right daily_durations.reverse()