diff --git a/core/templatetags/duration.py b/core/templatetags/duration.py
index 02a17748..0571b4ae 100644
--- a/core/templatetags/duration.py
+++ b/core/templatetags/duration.py
@@ -10,12 +10,12 @@ register = template.Library()
@register.filter
-def duration_string(duration):
+def duration_string(duration, precision='s'):
"""Format a duration (e.g. "2 hours, 3 minutes, 35 seconds")."""
if not duration:
return ''
try:
- return d_string(duration)
+ return d_string(duration, precision)
except (ValueError, TypeError):
return ''
diff --git a/core/utils.py b/core/utils.py
index 8a55a8a4..2efd7abf 100644
--- a/core/utils.py
+++ b/core/utils.py
@@ -15,18 +15,20 @@ def filter_by_params(request, model, available_params):
return queryset
-def duration_string(duration):
+def duration_string(duration, precision='s'):
"""Format hours, minutes and seconds as a human-friendly string (e.g. "2
- hours, 25 minutes, 31 seconds")"""
+ hours, 25 minutes, 31 seconds") with precision to h = hours, m = minutes or
+ s = seconds.
+ """
h, m, s = duration_parts(duration)
duration = ''
if h > 0:
duration = '{} hour{}'.format(h, 's' if h > 1 else '')
- if m > 0:
+ if m > 0 and precision != 'h':
duration += '{}{} minute{}'.format(
'' if duration is '' else ', ', m, 's' if m > 1 else '')
- if s > 0:
+ if s > 0 and precision != 'h' and precision != 'm':
duration += '{}{} second{}'.format(
'' if duration is '' else ', ', s, 's' if s > 1 else '')
@@ -34,7 +36,8 @@ def duration_string(duration):
def duration_parts(duration):
- """Get hours, minutes and seconds from a timedelta."""
+ """Get hours, minutes and seconds from a timedelta.
+ """
if not isinstance(duration, timezone.timedelta):
raise TypeError('Duration provided must be a timedetla')
h, remainder = divmod(duration.seconds, 3600)
diff --git a/dashboard/static_src/scss/cards.scss b/dashboard/static_src/scss/cards.scss
index 91271d06..0d203434 100644
--- a/dashboard/static_src/scss/cards.scss
+++ b/dashboard/static_src/scss/cards.scss
@@ -17,6 +17,21 @@
}
}
+.card-averages {
+ @extend .border-light;
+
+ .card-header {
+ @extend .text-dark, .bg-light;
+ }
+
+ .card-body {
+ @extend .text-light;
+
+ .container {
+ padding: 0;
+ }
+ }
+}
.card-diaperchange {
@extend .border-danger;
diff --git a/dashboard/templates/cards/averages.html b/dashboard/templates/cards/averages.html
new file mode 100644
index 00000000..8322ceb1
--- /dev/null
+++ b/dashboard/templates/cards/averages.html
@@ -0,0 +1,46 @@
+{% load duration %}
+
+
+
+
+
+
+
+
Time asleep
+ {% if sleep.average %}
+ {{ sleep.average|duration_string:'m' }}
+ {% else %}
+
Not enough data
+ {% endif %}
+
+
+
Time awake
+ {% if sleep.btwn_average %}
+ {{ sleep.btwn_average|duration_string:'m' }}
+ {% else %}
+
Not enough data
+ {% endif %}
+
+
+
Change freq.
+ {% if changes.btwn_average %}
+ {{ changes.btwn_average|duration_string:'m' }}
+ {% else %}
+
Not enough data
+ {% endif %}
+
+
+
Feeding freq.
+ {% if feedings.btwn_average %}
+ {{ feedings.btwn_average|duration_string:'m' }}
+ {% else %}
+
Not enough data
+ {% endif %}
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/templates/dashboard/child.html b/dashboard/templates/dashboard/child.html
index 3ea854c3..14efa016 100644
--- a/dashboard/templates/dashboard/child.html
+++ b/dashboard/templates/dashboard/child.html
@@ -12,6 +12,7 @@
{% block content %}
+ {% card_averages object %}
{% card_feeding_last object %}
{% card_feeding_last_method object %}
{% card_timer_list %}
diff --git a/dashboard/templatetags/cards.py b/dashboard/templatetags/cards.py
index 5e57677e..4a21d4aa 100644
--- a/dashboard/templatetags/cards.py
+++ b/dashboard/templatetags/cards.py
@@ -11,6 +11,64 @@ from core.models import DiaperChange, Feeding, Sleep, Timer, TummyTime
register = template.Library()
+@register.inclusion_tag('cards/averages.html')
+def card_averages(child):
+ """Averages data for all models.
+ """
+ instances = Sleep.objects.filter(child=child).order_by('start')
+ sleep = {
+ 'total': instances.aggregate(Sum('duration'))['duration__sum'],
+ 'count': instances.count(),
+ 'average': 0,
+ 'btwn_total': timezone.timedelta(0),
+ 'btwn_count': instances.count() - 1,
+ 'btwn_average': 0}
+
+ last_instance = None
+ for instance in instances:
+ if last_instance:
+ sleep['btwn_total'] += instance.start - last_instance.end
+ last_instance = instance
+
+ if sleep['count'] > 0:
+ sleep['average'] = sleep['total']/sleep['count']
+ if sleep['btwn_count'] > 0:
+ sleep['btwn_average'] = sleep['btwn_total']/sleep['btwn_count']
+
+ instances = DiaperChange.objects.filter(child=child).order_by('time')
+ changes = {
+ 'btwn_total': timezone.timedelta(0),
+ 'btwn_count': instances.count() - 1,
+ 'btwn_average': 0}
+ last_instance = None
+
+ for instance in instances:
+ if last_instance:
+ changes['btwn_total'] += instance.time - last_instance.time
+ last_instance = instance
+
+ if changes['btwn_count'] > 0:
+ changes['btwn_average'] = changes['btwn_total']/changes['btwn_count']
+
+ instances = Feeding.objects.filter(child=child).order_by('start')
+ feedings = {
+ 'btwn_total': timezone.timedelta(0),
+ 'btwn_count': instances.count() - 1,
+ 'btwn_average': 0}
+ last_instance = None
+
+ for instance in instances:
+ if last_instance:
+ feedings['btwn_total'] += instance.start - last_instance.end
+ last_instance = instance
+
+ if feedings['btwn_count'] > 0:
+ feedings['btwn_average'] = \
+ feedings['btwn_total']/feedings['btwn_count']
+
+ return {'changes': changes, 'feedings': feedings, 'sleep': sleep}
+
+
@register.inclusion_tag('cards/diaperchange_last.html')
def card_diaperchange_last(child):
"""Information about the most recent diaper change.