Show workouts on monthly calendar, not yet interactive (Will make it so you can change month and select workout to view on click)
This commit is contained in:
60
app.py
60
app.py
@@ -11,6 +11,7 @@ import os
|
|||||||
import sparklines
|
import sparklines
|
||||||
from dateutil.parser import isoparse
|
from dateutil.parser import isoparse
|
||||||
import humanize
|
import humanize
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
@@ -326,6 +327,62 @@ def render_users_and_workouts():
|
|||||||
duration_sparkline = sparklines.sparklines(
|
duration_sparkline = sparklines.sparklines(
|
||||||
[int(w['duration_minutes']) for w in workouts])[0]
|
[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)]
|
||||||
|
]
|
||||||
|
|
||||||
|
calendar_month = {
|
||||||
|
'month_year': current_date.strftime('%B, %Y'),
|
||||||
|
'days_of_month': days_of_month
|
||||||
|
}
|
||||||
|
|
||||||
user_data = {
|
user_data = {
|
||||||
'id': user.id,
|
'id': user.id,
|
||||||
'name': user.name,
|
'name': user.name,
|
||||||
@@ -336,7 +393,8 @@ def render_users_and_workouts():
|
|||||||
'duration_by_week': duration_by_week,
|
'duration_by_week': duration_by_week,
|
||||||
'num_days': num_days,
|
'num_days': num_days,
|
||||||
'workout_counts_sparkline': workout_counts_sparkline,
|
'workout_counts_sparkline': workout_counts_sparkline,
|
||||||
'duration_sparkline': duration_sparkline
|
'duration_sparkline': duration_sparkline,
|
||||||
|
'calendar_month': calendar_month
|
||||||
}
|
}
|
||||||
users_data.append(user_data)
|
users_data.append(user_data)
|
||||||
|
|
||||||
|
|||||||
@@ -28,238 +28,60 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between pt-12 overflow-x-auto">
|
|
||||||
<table class="w-full">
|
<div class="flex flex-col pt-6">
|
||||||
<thead>
|
<div class="grid grid-cols-7">
|
||||||
<tr>
|
<div class="py-3 px-4">
|
||||||
<th>
|
|
||||||
<div class="w-full flex justify-center">
|
|
||||||
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Mo</p>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<div class="w-full flex justify-center">
|
|
||||||
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Tu</p>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<div class="w-full flex justify-center">
|
|
||||||
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">We</p>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<div class="w-full flex justify-center">
|
|
||||||
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Th</p>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<div class="w-full flex justify-center">
|
|
||||||
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Fr</p>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<div class="w-full flex justify-center">
|
|
||||||
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Sa</p>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<div class="w-full flex justify-center">
|
|
||||||
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Su</p>
|
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Su</p>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
<div class="py-3 px-4">
|
||||||
</tr>
|
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Mo</p>
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td class="pt-6">
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-300 dark:text-gray-100 font-medium">29</p>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
<div class="py-3 px-4">
|
||||||
<td class="pt-6">
|
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Tu</p>
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-300 dark:text-gray-100 font-medium">30</p>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
<div class="py-3 px-4">
|
||||||
<td class="pt-6">
|
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">We</p>
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-300 dark:text-gray-100 font-medium">31</p>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
<div class="py-3 px-4">
|
||||||
<td class="pt-6">
|
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Th</p>
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">1</p>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
<div class="py-3 px-4">
|
||||||
<td class="pt-6">
|
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Fr</p>
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">2</p>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
<div class="py-3 px-4">
|
||||||
<td class="pt-6">
|
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Sa</p>
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100">3</p>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
|
||||||
<td class="pt-6">
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100">4</p>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
|
||||||
</tr>
|
<div class="grid grid-cols-7 overflow-hidden flex-1 w-full">
|
||||||
<tr>
|
{% for d in calendar_month.days_of_month %}
|
||||||
<td>
|
<div class="py-3 px-4">
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
<p
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">5</p>
|
class="text-base {% if d.is_current_month %} text-gray-500 {% else %} text-gray-300 {% endif %} dark:text-gray-100 font-medium text-center">
|
||||||
</div>
|
{% if d.is_current_date %}
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">6</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<a role="link" tabindex="0"
|
|
||||||
class="focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-700 focus:bg-indigo-200 hover:bg-indigo-200 text-base w-8 h-8 flex items-center justify-center font-medium border border-indigo-700 rounded-full">7</a>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="w-full h-full">
|
<div class="w-full h-full">
|
||||||
<div class="flex items-center justify-center w-full rounded-full cursor-pointer">
|
<div class="flex items-center justify-center w-full rounded-full cursor-pointer">
|
||||||
<a role="link" tabindex="0"
|
<a role="link" tabindex="0"
|
||||||
class="focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-700 focus:bg-indigo-500 hover:bg-indigo-500 text-base w-8 h-8 flex items-center justify-center font-medium text-white bg-indigo-700 rounded-full">8</a>
|
class="focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-700 focus:bg-indigo-500 hover:bg-indigo-500 text-base w-8 h-8 flex items-center justify-center font-medium text-white bg-indigo-700 rounded-full">{{
|
||||||
|
d.day_of_month }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
{% elif d.is_workout %}
|
||||||
<td>
|
<div class="flex items-center justify-center w-full rounded-full cursor-pointer"><a role="link"
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
tabindex="0"
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">9</p>
|
class="focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-700 focus:bg-indigo-200 hover:bg-indigo-200 text-base w-8 h-8 flex items-center justify-center font-medium border border-indigo-700 rounded-full">{{
|
||||||
|
d.day_of_month }}</a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
{% else %}
|
||||||
<td>
|
{{ d.day_of_month }}
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
{% endif %}
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100">10</p>
|
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
{% endfor %}
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100">11</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">12</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">13</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">14</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">15</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">16</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100">17</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100">18</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">19</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">20</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">21</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">22</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">23</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100">24</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100">25</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">26</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">27</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">28</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">29</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">30</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-300 dark:text-gray-100 font-medium">1</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
|
|
||||||
<p class="text-base text-gray-300 dark:text-gray-100 font-medium">2</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="!visible collapse p-4 hidden">
|
<div class="!visible collapse p-4 hidden">
|
||||||
{{ render_partial('partials/calendar.html', foo=1) }}
|
{{ render_partial('partials/calendar.html', calendar_month=u.calendar_month) }}
|
||||||
|
|
||||||
|
|
||||||
<ol class="relative border-l border-gray-200 dark:border-gray-700">
|
<ol class="relative border-l border-gray-200 dark:border-gray-700">
|
||||||
|
|||||||
Reference in New Issue
Block a user