Allow user to browse next/prev month on calendar
This commit is contained in:
139
app.py
139
app.py
@@ -279,6 +279,14 @@ def update_users_bike(user_id):
|
||||
return jsonify({'error': 'User or bike not found.'}), 404
|
||||
|
||||
|
||||
@ app.route('/user/<int:user_id>/calendar', methods=['GET'])
|
||||
def calendar_view(user_id):
|
||||
workouts = get_workouts_for_user(user_id)
|
||||
date = request.args.get('date', default=datetime.now().date(), type=toDate)
|
||||
calendar_month = generate_calendar_monthly_view(workouts, date)
|
||||
return render_template('partials/calendar.html', calendar_month=calendar_month, user_id=user_id)
|
||||
|
||||
|
||||
def render_users_and_workouts():
|
||||
users = User.query.all()
|
||||
users_data = []
|
||||
@@ -327,67 +335,6 @@ def render_users_and_workouts():
|
||||
duration_sparkline = sparklines.sparklines(
|
||||
[int(w['duration_minutes']) for w in workouts])[0]
|
||||
|
||||
# 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
|
||||
|
||||
current_date = datetime.now().date()
|
||||
start_date, end_date = get_month_bounds(current_date)
|
||||
monthly_workouts = [w for w in workouts if start_date <=
|
||||
w['start_time_date'].date() <= end_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 == current_date.month and single_date.year == current_date.year
|
||||
}
|
||||
for single_date in date_range(start_date, end_date)
|
||||
for workout in [get_workout_for_date(monthly_workouts, single_date)]
|
||||
]
|
||||
|
||||
next_month_date = current_date + relativedelta(months=1)
|
||||
previous_month_date = current_date - relativedelta(months=1)
|
||||
|
||||
calendar_month = {
|
||||
'month_year': current_date.strftime('%B, %Y'),
|
||||
'days_of_month': days_of_month,
|
||||
'next_month': next_month_date,
|
||||
'previous_month': previous_month_date,
|
||||
}
|
||||
|
||||
user_data = {
|
||||
'id': user.id,
|
||||
'name': user.name,
|
||||
@@ -399,7 +346,7 @@ def render_users_and_workouts():
|
||||
'num_days': num_days,
|
||||
'workout_counts_sparkline': workout_counts_sparkline,
|
||||
'duration_sparkline': duration_sparkline,
|
||||
'calendar_month': calendar_month
|
||||
'calendar_month': generate_calendar_monthly_view(workouts, datetime.now().date())
|
||||
}
|
||||
users_data.append(user_data)
|
||||
|
||||
@@ -502,6 +449,74 @@ def format_duration(duration):
|
||||
return f"{minutes}m"
|
||||
|
||||
|
||||
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
|
||||
|
||||
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]
|
||||
|
||||
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
|
||||
}
|
||||
for single_date in date_range(start_date, end_date)
|
||||
for workout in [get_workout_for_date(monthly_workouts, single_date)]
|
||||
]
|
||||
|
||||
next_month_date = selected_date + relativedelta(months=1)
|
||||
previous_month_date = selected_date - relativedelta(months=1)
|
||||
|
||||
calendar_month = {
|
||||
'month_year': selected_date.strftime('%B, %Y'),
|
||||
'days_of_month': days_of_month,
|
||||
'next_month': next_month_date,
|
||||
'previous_month': previous_month_date,
|
||||
}
|
||||
return calendar_month
|
||||
|
||||
|
||||
def toDate(dateString):
|
||||
return datetime.strptime(dateString, "%Y-%m-%d").date()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Bind to PORT if defined, otherwise default to 5000.
|
||||
port = int(os.environ.get('PORT', 5000))
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<div class="md:p-8 p-5 dark:bg-gray-800 bg-white rounded-t">
|
||||
<div class="md:p-8 p-5 dark:bg-gray-800 bg-white rounded-t" id="monthly-calendar-{{ user_id }}">
|
||||
<div class="px-4 flex items-center justify-between">
|
||||
<span tabindex="0" class="focus:outline-none text-base font-bold dark:text-gray-100 text-gray-800">October
|
||||
2020</span>
|
||||
<span tabindex="0" class="focus:outline-none text-base font-bold dark:text-gray-100 text-gray-800">{{
|
||||
calendar_month.month_year }}</span>
|
||||
<div class="flex items-center">
|
||||
<button aria-label="calendar backward"
|
||||
class="focus:text-gray-400 hover:text-gray-400 text-gray-800 dark:text-gray-100">
|
||||
class="focus:text-gray-400 hover:text-gray-400 text-gray-800 dark:text-gray-100"
|
||||
hx-get="{{ url_for('calendar_view', user_id=user_id) }}"
|
||||
hx-vals='{"date": "{{ calendar_month.previous_month }}"}' hx-target="#monthly-calendar-{{ user_id }}"
|
||||
hx-swap="outerHTML">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-chevron-left" width="24"
|
||||
height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
|
||||
stroke-linecap="round" stroke-linejoin="round" data-darkreader-inline-stroke=""
|
||||
@@ -15,7 +18,10 @@
|
||||
</svg>
|
||||
</button>
|
||||
<button aria-label="calendar forward"
|
||||
class="focus:text-gray-400 hover:text-gray-400 ml-3 text-gray-800 dark:text-gray-100">
|
||||
class="focus:text-gray-400 hover:text-gray-400 ml-3 text-gray-800 dark:text-gray-100"
|
||||
hx-get="{{ url_for('calendar_view', user_id=user_id) }}"
|
||||
hx-vals='{"date": "{{ calendar_month.next_month }}"}' hx-target="#monthly-calendar-{{ user_id }}"
|
||||
hx-swap="outerHTML">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-chevron-right" width="24"
|
||||
height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
|
||||
stroke-linecap="round" stroke-linejoin="round" data-darkreader-inline-stroke=""
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
</button>
|
||||
</h2>
|
||||
<div class="!visible collapse p-4 hidden">
|
||||
{{ render_partial('partials/calendar.html', calendar_month=u.calendar_month) }}
|
||||
{{ render_partial('partials/calendar.html', calendar_month=u.calendar_month, user_id = u.id) }}
|
||||
|
||||
|
||||
<ol class="relative border-l border-gray-200 dark:border-gray-700">
|
||||
|
||||
Reference in New Issue
Block a user