Skip to content

Commit

Permalink
Sleep history (#485)
Browse files Browse the repository at this point in the history
* Fix dayssince sometimes being off by 1 day depending on timezone

* Add sleep stats carousel for last 7 days

* Renamed sleep_day to sleep_recent

* Rename Today's Sleep to Recent Sleep in dashboard
  • Loading branch information
DanBeard committed Jun 28, 2022
1 parent c98f6cd commit 834e763
Show file tree
Hide file tree
Showing 30 changed files with 2,327 additions and 2,003 deletions.
22 changes: 0 additions & 22 deletions dashboard/templates/cards/sleep_day.html

This file was deleted.

54 changes: 54 additions & 0 deletions dashboard/templates/cards/sleep_recent.html
@@ -0,0 +1,54 @@
{% extends 'cards/base.html' %}
{% load duration i18n %}

{% block header %}
<a href="{% url "core:sleep-list" %}">
{% trans "Recent Sleep" %}
</a>
{% endblock %}

{% block title %}
{% if sleeps|length > 0 %}
<div id="sleep-days-carousel" class="carousel slide" data-interval="false">
<div class="carousel-inner">
{% for sleep in sleeps %}
<div class="carousel-item{% if forloop.counter == 1 %} active{% endif %}">
<div class="last-sleep-method text-center">
{% if sleep.total %}
{{ sleep.total|duration_string:"m" }}
{% else %}
{% trans "None" %}
{% endif %}
</div>
<div class="text-center small">
{% if sleep.count > 0 %}
{% blocktrans trimmed count counter=sleep.count %}
{{ counter }} sleep
{% plural %}
{{ counter }} sleep
{% endblocktrans %}
{% endif %}
</div>
{% blocktrans trimmed with since=sleep.date.date|dayssince %}
<div class="text-center small text-muted">
{{ since }}
</div>
{% endblocktrans %}
</div>
{% endfor %}
</div>
{% if sleeps|length > 1 %}
<a class="carousel-control-prev" href="#sleep-days-carousel" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">{% trans "Previous" %}</span>
</a>
<a class="carousel-control-next" href="#sleep-days-carousel" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">{% trans "Next" %}</span>
</a>
{% endif %}
</div>
{% else %}
{% trans "None" %}
{% endif %}
{% endblock %}
2 changes: 1 addition & 1 deletion dashboard/templates/dashboard/child.html
Expand Up @@ -18,7 +18,7 @@
{% card_feeding_last_method object %}
{% card_feeding_day object %}
{% card_statistics object %}
{% card_sleep_day object %}
{% card_sleep_recent object %}
{% card_sleep_naps_day object %}
{% card_tummytime_day object %}
{% card_diaperchange_types object %}
Expand Down
75 changes: 52 additions & 23 deletions dashboard/templatetags/cards.py
Expand Up @@ -221,42 +221,71 @@ def card_sleep_last(context, child):
}


@register.inclusion_tag("cards/sleep_day.html", takes_context=True)
def card_sleep_day(context, child, date=None):
@register.inclusion_tag("cards/sleep_recent.html", takes_context=True)
def card_sleep_recent(context, child, end_date=None):
"""
Filters Sleep instances to get count and total values for a specific date.
Filters sleeping instances to get total amount for a specific date and for 7 days before
:param child: an instance of the Child model.
:param date: a Date object for the day to filter.
:returns: a dictionary with count and total values for the Sleep instances.
:param end_date: a Date object for the day to filter.
:returns: a dict with count and total amount for the sleeping instances.
"""
if not date:
date = timezone.localtime().date()
if not end_date:
end_date = timezone.localtime()

# push end_date to very end of that day
end_date = end_date.replace(hour=23, minute=59, second=59, microsecond=9999)
# we need a datetime to use the range helper in the model
start_date = end_date - timezone.timedelta(
days=8
) # end of the -8th day so we get the FULL 7th day

instances = models.Sleep.objects.filter(child=child).filter(
start__year=date.year, start__month=date.month, start__day=date.day
start__range=[start_date, end_date]
) | models.Sleep.objects.filter(child=child).filter(
end__year=date.year, end__month=date.month, end__day=date.day
end__range=[start_date, end_date]
)
empty = len(instances) == 0

total = timezone.timedelta(seconds=0)
# prepare the result list for the last 7 days
dates = [end_date - timezone.timedelta(days=i) for i in range(8)]
results = [{"date": d, "total": timezone.timedelta(), "count": 0} for d in dates]

# do one pass over the data and add it to the appropriate day
for instance in instances:
# convert to local tz and push feed_date to end so we're comparing apples to apples for the date
start = timezone.localtime(instance.start)
end = timezone.localtime(instance.end)
# Account for dates crossing midnight.
if start.date() != date:
start = start.replace(
year=end.year, month=end.month, day=end.day, hour=0, minute=0, second=0
)

total += end - start

count = len(instances)
sleep_start_date = start.replace(
hour=23, minute=59, second=59, microsecond=9999
)
sleep_end_date = end.replace(hour=23, minute=59, second=59, microsecond=9999)
start_idx = (end_date - sleep_start_date).days
end_idx = (end_date - sleep_end_date).days
# this is more complicated than feedings because we only want to capture the PORTION of sleep
# that is a part of this day (e.g. starts sleep at 7PM and finished at 7AM = 5 hrs yesterday 7 hrs today)
# (Assuming you have a unicorn sleeper. Congratulations)
if start_idx == end_idx: # if we're in the same day it's simple
result = results[start_idx]
result["total"] += end - start
result["count"] += 1
else: # otherwise we need to split the time up
midnight = end.replace(hour=0, minute=0, second=0)

if 0 <= start_idx < len(results):
result = results[start_idx]
# only the portion that is today
result["total"] += midnight - start
result["count"] += 1

if 0 <= end_idx < len(results):
result = results[end_idx]
# only the portion that is tomorrow
result["total"] += end - midnight
result["count"] += 1

return {
"sleeps": results,
"type": "sleep",
"total": total,
"count": count,
"empty": empty,
"empty": len(instances) == 0,
"hide_empty": _hide_empty(context),
}

Expand Down
9 changes: 6 additions & 3 deletions dashboard/tests/tests_templatetags.py
Expand Up @@ -204,12 +204,15 @@ def test_card_sleep_last_empty(self):
self.assertFalse(data["hide_empty"])

def test_card_sleep_day(self):
data = cards.card_sleep_day(self.context, self.child, self.date)
data = cards.card_sleep_recent(self.context, self.child, self.date)
self.assertEqual(data["type"], "sleep")
self.assertFalse(data["empty"])
self.assertFalse(data["hide_empty"])
self.assertEqual(data["total"], timezone.timedelta(2, 7200))
self.assertEqual(data["count"], 4)
self.assertEqual(data["sleeps"][0]["total"], timezone.timedelta(hours=7))
self.assertEqual(data["sleeps"][0]["count"], 4)

self.assertEqual(data["sleeps"][1]["total"], timezone.timedelta(minutes=30))
self.assertEqual(data["sleeps"][1]["count"], 1)

def test_card_sleep_naps_day(self):
data = cards.card_sleep_naps_day(self.context, self.child, self.date)
Expand Down
Binary file modified locale/ca/LC_MESSAGES/django.mo
Binary file not shown.

0 comments on commit 834e763

Please sign in to comment.