Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sleep history #485

Merged
merged 6 commits into from Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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.