diff --git a/dashboard/templates/dashboard/child_button_group.html b/dashboard/templates/dashboard/child_button_group.html index 88490f08..1719eec2 100644 --- a/dashboard/templates/dashboard/child_button_group.html +++ b/dashboard/templates/dashboard/child_button_group.html @@ -27,6 +27,7 @@ {% trans "Feeding Durations (Average)" %} {% trans "Sleep Pattern" %} {% trans "Sleep Totals" %} + {% trans "Tummy Time Durations (Sum)" %} {% trans "Weight" %} diff --git a/reports/graphs/__init__.py b/reports/graphs/__init__.py index b6de25e1..66453167 100644 --- a/reports/graphs/__init__.py +++ b/reports/graphs/__init__.py @@ -5,4 +5,5 @@ from .feeding_amounts import feeding_amounts # NOQA from .feeding_duration import feeding_duration # NOQA from .sleep_pattern import sleep_pattern # NOQA from .sleep_totals import sleep_totals # NOQA +from .tummytime_duration import tummytime_duration # NOQA from .weight_weight import weight_weight # NOQA diff --git a/reports/graphs/tummytime_duration.py b/reports/graphs/tummytime_duration.py new file mode 100644 index 00000000..e3e8b15f --- /dev/null +++ b/reports/graphs/tummytime_duration.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +from django.db.models import Count, Sum +from django.db.models.functions import TruncDate +from django.utils.translation import gettext as _ + +import plotly.offline as plotly +import plotly.graph_objs as go + +from core.utils import duration_parts + +from reports import utils + + +def tummytime_duration(instances): + """ + Create a graph showing total duration of tummy time instances per day. + + :param instances: a QuerySet of TummyTime instances. + :returns: a tuple of the the graph's html and javascript. + """ + totals = instances.annotate(date=TruncDate('start')) \ + .values('date') \ + .annotate(count=Count('id')) \ + .annotate(sum=Sum('duration')) \ + .order_by('-date') + + sums = [] + for total in totals: + sums.append(total['sum']) + + trace_avg = go.Bar( + name=_('Total duration'), + x=list(totals.values_list('date', flat=True)), + y=[td.seconds/60 for td in sums], + hoverinfo='text', + text=[_duration_string_ms(td) for td in sums] + ) + trace_count = go.Scatter( + name=_('Number of sessions'), + mode='markers', + x=list(totals.values_list('date', flat=True)), + y=list(totals.values_list('count', flat=True)), + yaxis='y2', + hoverinfo='y' + ) + + layout_args = utils.default_graph_layout_options() + layout_args['title'] = _('Total Tummy Time Durations') + layout_args['xaxis']['title'] = _('Date') + layout_args['xaxis']['rangeselector'] = utils.rangeselector_date() + layout_args['yaxis']['title'] = _('Total duration (minutes)') + layout_args['yaxis2'] = dict(layout_args['yaxis']) + layout_args['yaxis2']['title'] = _('Number of sessions') + layout_args['yaxis2']['overlaying'] = 'y' + layout_args['yaxis2']['side'] = 'right' + + fig = go.Figure({ + 'data': [trace_avg, trace_count], + 'layout': go.Layout(**layout_args) + }) + output = plotly.plot(fig, output_type='div', include_plotlyjs=False) + return utils.split_graph_output(output) + + +def _duration_string_ms(duration): + """ + Format a "short" duration string with only minutes and seconds. This is + intended to fit better in smaller spaces on a graph. + :returns: a string of the form Xm. + """ + h, m, s = duration_parts(duration) + return '{}m{}s'.format(m, s) diff --git a/reports/templates/reports/tummytime_duration.html b/reports/templates/reports/tummytime_duration.html new file mode 100644 index 00000000..9e49c872 --- /dev/null +++ b/reports/templates/reports/tummytime_duration.html @@ -0,0 +1,9 @@ +{% extends 'reports/report_base.html' %} +{% load i18n %} + +{% block title %}{% trans "Total Tummy Time Durations" %} - {{ object }}{% endblock %} + +{% block breadcrumbs %} + {{ block.super }} +