Here is a conventional commit message summarizing the refactoring work:

```
feat: Refactor calendar feature into blueprint

- Moved calendar logic from `features/calendar.py` and `app.py` into a new blueprint at `routes/calendar.py`.
- Removed the `Calendar` class and refactored logic into helper functions within the blueprint module for better organization and readability.
- Eliminated the `pandas` dependency for date range generation, using standard `datetime` operations instead.
- Resolved circular import issues between `db.py`, `extensions.py`, and `routes/calendar.py` by adjusting import locations.
- Corrected `url_for` calls in templates (`calendar.html`, `partials/people_link.html`) to reference the new blueprint endpoint (`calendar.get_calendar`).
- Fixed an `AttributeError` related to HTMX request checking in the calendar route.
- Corrected `AttributeError` related to `.date()` calls on `datetime.date` objects in view processing functions.
- Updated `templates/changelog/changelog.html` to document the refactoring and associated fixes.
```
This commit is contained in:
Peter Stockings
2025-03-30 22:20:48 +11:00
parent 4a822ea2ba
commit 6095e76f10
12 changed files with 244 additions and 175 deletions

View File

@@ -1,142 +0,0 @@
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
import pandas as pd
class Calendar:
def __init__(self, db_connection_method):
self.execute = db_connection_method
def fetch_workouts_for_person(self, person_id, date, view):
prev_date, next_date = None, None
if view == 'month':
first_day_of_month = date.replace(day=1)
days_to_subtract = (first_day_of_month.weekday() + 1) % 7
start_date = first_day_of_month - timedelta(days=days_to_subtract)
end_date = start_date + timedelta(days=6 * 7 - 1)
prev_date = first_day_of_month - relativedelta(months=1)
next_date = first_day_of_month + relativedelta(months=1)
elif view == 'year':
start_date = date.replace(month=1, day=1)
end_date = date.replace(year=date.year + 1, month=1, day=1) - timedelta(days=1)
prev_date = date - relativedelta(years=1)
next_date = date + relativedelta(years=1)
else:
raise ValueError('Invalid view')
query = """
SELECT
w.workout_id,
w.start_date,
t.topset_id,
t.repetitions,
t.weight,
e.name AS exercise_name,
p.name AS person_name
FROM
person p
LEFT JOIN workout w ON p.person_id = w.person_id AND w.start_date BETWEEN %s AND %s
LEFT JOIN topset t ON w.workout_id = t.workout_id
LEFT JOIN exercise e ON t.exercise_id = e.exercise_id
WHERE
p.person_id = %s
ORDER BY
w.start_date,
t.topset_id;
"""
workouts_data = self.execute(query, [start_date, end_date, person_id])
# Assuming person_name is the same for all rows as we filter by person_id
person_name = workouts_data[0]['person_name'] if workouts_data else 'Unknown'
calendar_view = {'prev_date': prev_date, 'next_date': next_date, 'person_id': person_id, 'person_name': person_name, 'view': view, 'date': date}
if view == 'month':
calendar_view['days'] = []
workouts_by_date = {}
for row in workouts_data:
if row['workout_id'] is None:
continue # Skip rows that don't have workout data
workout_date_str = row['start_date'].strftime("%Y-%m-%d")
workout_id = row['workout_id']
if workout_date_str not in workouts_by_date:
workouts_by_date[workout_date_str] = {}
if workout_id not in workouts_by_date[workout_date_str]:
workouts_by_date[workout_date_str][workout_id] = {
'workout_id': workout_id,
'start_date': row['start_date'],
'sets': []
}
if row['topset_id']:
workouts_by_date[workout_date_str][workout_id]['sets'].append({
'repetitions': row['repetitions'],
'weight': row['weight'],
'exercise_name': row['exercise_name']
})
for current_date in pd.date_range(start_date, end_date, freq='D'):
date_str = current_date.strftime("%Y-%m-%d")
day_workouts = workouts_by_date.get(date_str, {})
today = datetime.today().date()
calendar_view['days'].append({
'day': current_date.day,
'is_today': current_date == today,
'is_in_current_month': current_date.month == date.month, # Ensure it compares with the selected month
'has_workouts': len(day_workouts) > 0,
'workouts': list(day_workouts.values())
})
elif view == 'year':
calendar_view['months'] = []
workouts_by_date = {}
for row in workouts_data:
if row['start_date'] is None:
continue # Skip rows that don't have workout data
workout_date_str = row['start_date'].strftime("%Y-%m-%d")
if workout_date_str not in workouts_by_date:
workouts_by_date[workout_date_str] = []
workouts_by_date[workout_date_str].append({
'workout_id': row['workout_id'],
'start_date': row['start_date'],
'topset_id': row['topset_id'],
'repetitions': row['repetitions'],
'weight': row['weight'],
'exercise_name': row['exercise_name']
})
for month in range(1, 13):
first_day_of_month = date.replace(month=month, day=1)
days_to_subtract = (first_day_of_month.weekday() + 1) % 7
start_date = first_day_of_month - timedelta(days=days_to_subtract)
end_date = start_date + timedelta(days=6 * 7 - 1)
month_data = {'name': first_day_of_month.strftime('%B'), 'first_day_of_month': first_day_of_month, 'days': []}
current_day = start_date
while current_day <= end_date:
day_workouts = workouts_by_date.get(current_day.strftime('%Y-%m-%d'), [])
has_workouts = len(day_workouts) > 0
first_workout_id = day_workouts[0]['workout_id'] if has_workouts else None
day_data = {
'day': current_day.day,
'is_today': current_day == datetime.today().date(),
'is_in_current_month': current_day.month == month,
'workouts': day_workouts,
'has_workouts': has_workouts,
'first_workout_id': first_workout_id
}
month_data['days'].append(day_data)
current_day += timedelta(days=1)
calendar_view['months'].append(month_data)
return calendar_view