Compare commits

..

2 Commits

Author SHA1 Message Date
Peter Stockings
26dda12fff Make login/signup links responsive on mobile 2026-01-29 15:54:09 +11:00
Peter Stockings
2dc2b62d7b Improve performance of calendar view 2026-01-29 15:21:58 +11:00
2 changed files with 51 additions and 40 deletions

View File

@@ -32,8 +32,9 @@ def _get_date_range_and_links(date_obj, view):
raise ValueError('Invalid view type specified.') raise ValueError('Invalid view type specified.')
return start_date, end_date, prev_date, next_date return start_date, end_date, prev_date, next_date
def _fetch_raw_workout_data(db_executor, person_id, start_date, end_date): def _fetch_workout_data(db_executor, person_id, start_date, end_date, include_details=True):
"""Fetches workout data for a person within a date range.""" """Fetches workout data for a person within a date range."""
if include_details:
query = """ query = """
SELECT SELECT
w.workout_id, w.workout_id,
@@ -54,16 +55,29 @@ def _fetch_raw_workout_data(db_executor, person_id, start_date, end_date):
w.start_date, w.start_date,
t.topset_id; t.topset_id;
""" """
else:
query = """
SELECT
w.workout_id,
w.start_date,
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
WHERE
p.person_id = %s
ORDER BY
w.start_date;
"""
# Ensure dates are passed in a format the DB understands (e.g., YYYY-MM-DD strings) # Ensure dates are passed in a format the DB understands (e.g., YYYY-MM-DD strings)
return db_executor(query, [start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d'), person_id]) return db_executor(query, [start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d'), person_id])
def _group_workouts_by_date(workouts_data): def _group_workouts_by_date(workouts_data):
"""Groups workout data by date and workout ID.""" """Groups workout data by date and workout ID."""
# Structure: { 'YYYY-MM-DD': { workout_id: { workout_details..., 'sets': [...] } } } # Structure: { date_object: { workout_id: { workout_details..., 'sets': [...] } } }
workouts_by_date = defaultdict(lambda: defaultdict(lambda: {'sets': []})) workouts_by_date = defaultdict(lambda: defaultdict(lambda: {'sets': []}))
person_name = 'Unknown' person_name = 'Unknown'
if workouts_data: if workouts_data:
# Use .get() for safer access in case the key doesn't exist
person_name = workouts_data[0].get('person_name', 'Unknown') person_name = workouts_data[0].get('person_name', 'Unknown')
for row in workouts_data: for row in workouts_data:
@@ -72,29 +86,27 @@ def _group_workouts_by_date(workouts_data):
continue continue
workout_date = row['start_date'] workout_date = row['start_date']
workout_date_str = workout_date.strftime("%Y-%m-%d")
workout_id = row['workout_id'] workout_id = row['workout_id']
# Initialize workout details if this workout_id hasn't been seen for this date # Initialize workout details if this workout_id hasn't been seen for this date
if workout_id not in workouts_by_date[workout_date_str]: if workout_id not in workouts_by_date[workout_date]:
workouts_by_date[workout_date_str][workout_id].update({ workouts_by_date[workout_date][workout_id].update({
'workout_id': workout_id, 'workout_id': workout_id,
'start_date': workout_date, 'start_date': workout_date,
# 'sets' is already initialized by defaultdict
}) })
# Add set details if topset_id exists # Add set details if topset_id exists
if row.get('topset_id'): if row.get('topset_id'):
workouts_by_date[workout_date_str][workout_id]['sets'].append({ workouts_by_date[workout_date][workout_id]['sets'].append({
'repetitions': row.get('repetitions'), 'repetitions': row.get('repetitions'),
'weight': row.get('weight'), 'weight': row.get('weight'),
'exercise_name': row.get('exercise_name') 'exercise_name': row.get('exercise_name')
}) })
# Convert nested defaultdict to regular dict for easier handling/JSON serialization # Convert nested defaultdict to regular dict
processed_workouts = { processed_workouts = {
date_str: dict(workouts) d: dict(w)
for date_str, workouts in workouts_by_date.items() for d, w in workouts_by_date.items()
} }
return processed_workouts, person_name return processed_workouts, person_name
@@ -104,8 +116,7 @@ def _process_workouts_for_month_view(grouped_workouts, start_date, end_date, sel
today = datetime.today().date() today = datetime.today().date()
current_date = start_date current_date = start_date
while current_date <= end_date: while current_date <= end_date:
date_str = current_date.strftime("%Y-%m-%d") day_workouts_dict = grouped_workouts.get(current_date, {})
day_workouts_dict = grouped_workouts.get(date_str, {})
day_workouts_list = list(day_workouts_dict.values()) # Convert workout dicts to list day_workouts_list = list(day_workouts_dict.values()) # Convert workout dicts to list
days_data.append({ days_data.append({
@@ -134,8 +145,7 @@ def _process_workouts_for_year_view(grouped_workouts, year_date):
month_days_data = [] month_days_data = []
current_day = start_date_month current_day = start_date_month
while current_day <= end_date_month: while current_day <= end_date_month:
date_str = current_day.strftime('%Y-%m-%d') day_workouts_dict = grouped_workouts.get(current_day, {})
day_workouts_dict = grouped_workouts.get(date_str, {})
day_workouts_list = list(day_workouts_dict.values()) day_workouts_list = list(day_workouts_dict.values())
has_workouts = len(day_workouts_list) > 0 has_workouts = len(day_workouts_list) > 0
# Get first workout ID if workouts exist # Get first workout ID if workouts exist
@@ -184,8 +194,9 @@ def get_calendar(person_id):
# For now, returning a simple error response # For now, returning a simple error response
return f"Error: Invalid view type '{selected_view}'.", 400 return f"Error: Invalid view type '{selected_view}'.", 400
# Fetch and process data # Fetch and process data (only fetch details if in month view)
raw_workouts = _fetch_raw_workout_data(db.execute, person_id, start_date, end_date) include_details = (selected_view == 'month')
raw_workouts = _fetch_workout_data(db.execute, person_id, start_date, end_date, include_details=include_details)
grouped_workouts, person_name = _group_workouts_by_date(raw_workouts) grouped_workouts, person_name = _group_workouts_by_date(raw_workouts)
# Prepare base context for the template # Prepare base context for the template

View File

@@ -62,7 +62,7 @@
A2.25 2.25 0 006 21h7.5 A2.25 2.25 0 006 21h7.5
a2.25 2.25 0 002.25-2.25V15m-3-3h10.5m0 0l-3-3m3 3l-3 3" /> a2.25 2.25 0 002.25-2.25V15m-3-3h10.5m0 0l-3-3m3 3l-3 3" />
</svg> </svg>
Logout <span class="hidden sm:inline">Logout</span>
</a> </a>
{% else %} {% else %}
<!-- Show Login and Sign Up links --> <!-- Show Login and Sign Up links -->
@@ -77,7 +77,7 @@
a2.25 2.25 0 002.25-2.25V15m-3-3h10.5 a2.25 2.25 0 002.25-2.25V15m-3-3h10.5
m0 0l-3-3m3 3l-3 3" /> m0 0l-3-3m3 3l-3 3" />
</svg> </svg>
Login <span class="hidden sm:inline">Login</span>
</a> </a>
<a href="{{ url_for('auth.signup') }}" <a href="{{ url_for('auth.signup') }}"
class="text-slate-400 hover:text-slate-500 flex items-center gap-1"> class="text-slate-400 hover:text-slate-500 flex items-center gap-1">
@@ -89,7 +89,7 @@
-3.315-2.685-6-6-6H6c -3.315-2.685-6-6-6H6c
-3.315 0-6 2.685-6 6m17.25-11.25h3m0 0v3m0-3l-3 0" /> -3.315 0-6 2.685-6 6m17.25-11.25h3m0 0v3m0-3l-3 0" />
</svg> </svg>
Sign Up <span class="hidden sm:inline">Sign Up</span>
</a> </a>
{% endif %} {% endif %}
<a href="https://github.com/GabePope/WorkoutTracker" <a href="https://github.com/GabePope/WorkoutTracker"