From 5d4db5de6b3ee23a876bbf92454e87a4a1cae806 Mon Sep 17 00:00:00 2001 From: Christopher Charbonneau Wells Date: Fri, 10 Nov 2017 07:20:34 -0500 Subject: [PATCH] Add simple weight report. --- .../dashboard/child_button_group.html | 1 + reports/graphs/__init__.py | 1 + reports/graphs/weight_weight.py | 37 +++++++++++ reports/templates/reports/weight_change.html | 13 ++++ reports/tests/tests_views.py | 3 + reports/urls.py | 4 ++ reports/views.py | 66 +++++++++++++------ 7 files changed, 104 insertions(+), 21 deletions(-) create mode 100644 reports/graphs/weight_weight.py create mode 100644 reports/templates/reports/weight_change.html diff --git a/dashboard/templates/dashboard/child_button_group.html b/dashboard/templates/dashboard/child_button_group.html index 2e3ee7f4..8066d9b3 100644 --- a/dashboard/templates/dashboard/child_button_group.html +++ b/dashboard/templates/dashboard/child_button_group.html @@ -18,6 +18,7 @@ Diaper Lifetimes Sleep Pattern Sleep Totals + Weight diff --git a/reports/graphs/__init__.py b/reports/graphs/__init__.py index 0957cd42..e3a47533 100644 --- a/reports/graphs/__init__.py +++ b/reports/graphs/__init__.py @@ -2,3 +2,4 @@ from .diaperchange_lifetimes import diaperchange_lifetimes # NOQA from .diaperchange_types import diaperchange_types # NOQA from .sleep_pattern import sleep_pattern # NOQA from .sleep_totals import sleep_totals # NOQA +from .weight_weight import weight_weight # NOQA diff --git a/reports/graphs/weight_weight.py b/reports/graphs/weight_weight.py new file mode 100644 index 00000000..44b7d418 --- /dev/null +++ b/reports/graphs/weight_weight.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import plotly.offline as plotly +import plotly.graph_objs as go + +from reports import utils + + +def weight_weight(objects): + """ + Create a graph showing weight over time. + :param objects: a QuerySet of Weight instances. + :returns: a tuple of the the graph's html and javascript. + """ + objects = objects.order_by('-date') + + trace = go.Scatter( + name='Weight', + x=list(objects.values_list('date', flat=True)), + y=list(objects.values_list('weight', flat=True)), + fill='tozeroy', + ) + + layout_args = utils.default_graph_layout_options() + layout_args['barmode'] = 'stack' + layout_args['title'] = 'Weight' + layout_args['xaxis']['title'] = 'Date' + layout_args['xaxis']['rangeselector'] = utils.rangeselector_date() + layout_args['yaxis']['title'] = 'Weight' + + fig = go.Figure({ + 'data': [trace], + 'layout': go.Layout(**layout_args) + }) + output = plotly.plot(fig, output_type='div', include_plotlyjs=False) + return utils.split_graph_output(output) diff --git a/reports/templates/reports/weight_change.html b/reports/templates/reports/weight_change.html new file mode 100644 index 00000000..add911d1 --- /dev/null +++ b/reports/templates/reports/weight_change.html @@ -0,0 +1,13 @@ +{% extends 'reports/report_base.html' %} + +{% block title %}Weight - {{ object }}{% endblock %} + +{% block breadcrumbs %} + {{ block.super }} + +{% endblock %} + +{% block javascript %} + {{ block.super }} + {{ javascript|safe }} +{% endblock %} \ No newline at end of file diff --git a/reports/tests/tests_views.py b/reports/tests/tests_views.py index abf886cc..32b70850 100644 --- a/reports/tests/tests_views.py +++ b/reports/tests/tests_views.py @@ -44,3 +44,6 @@ class ViewsTestCase(TestCase): self.assertEqual(page.status_code, 200) page = self.c.get('{}/sleep/totals/'.format(base_url)) self.assertEqual(page.status_code, 200) + + page = self.c.get('{}/weight/weight/'.format(base_url)) + self.assertEqual(page.status_code, 200) diff --git a/reports/urls.py b/reports/urls.py index ee6aeb2c..839ef406 100644 --- a/reports/urls.py +++ b/reports/urls.py @@ -19,4 +19,8 @@ urlpatterns = [ url(r'^children/(?P[^/.]+)/reports/sleep/totals/$', views.SleepTotalsChildReport.as_view(), name='report-sleep-totals-child'), + + url(r'^children/(?P[^/.]+)/reports/weight/weight/$', + views.WeightWeightChildReoport.as_view(), + name='report-weight-weight-child'), ] diff --git a/reports/views.py b/reports/views.py index 234b61f8..973f675c 100644 --- a/reports/views.py +++ b/reports/views.py @@ -4,16 +4,16 @@ from __future__ import unicode_literals from django.contrib.auth.mixins import PermissionRequiredMixin from django.views.generic.detail import DetailView -from core.models import Child, DiaperChange, Sleep +from core import models -from .graphs import (diaperchange_types, diaperchange_lifetimes, sleep_pattern, - sleep_totals) +from . import graphs class DiaperChangeLifetimesChildReport(PermissionRequiredMixin, DetailView): - """Graph of diaper changes by day and type. """ - model = Child + Graph of diaper "lifetimes" - time between diaper changes. + """ + model = models.Child permission_required = ('core.view_child',) template_name = 'reports/diaperchange_lifetimes.html' @@ -21,17 +21,18 @@ class DiaperChangeLifetimesChildReport(PermissionRequiredMixin, DetailView): context = super( DiaperChangeLifetimesChildReport, self).get_context_data(**kwargs) child = context['object'] - changes = DiaperChange.objects.filter(child=child) + changes = models.DiaperChange.objects.filter(child=child) if changes and changes.count() > 1: - context['html'], context['javascript'] = diaperchange_lifetimes( - changes) + context['html'], context['javascript'] = \ + graphs.diaperchange_lifetimes(changes) return context class DiaperChangeTypesChildReport(PermissionRequiredMixin, DetailView): - """Graph of diaper changes by day and type. """ - model = Child + Graph of diaper changes by day and type. + """ + model = models.Child permission_required = ('core.view_child',) template_name = 'reports/diaperchange_types.html' @@ -39,17 +40,18 @@ class DiaperChangeTypesChildReport(PermissionRequiredMixin, DetailView): context = super(DiaperChangeTypesChildReport, self).get_context_data( **kwargs) child = context['object'] - changes = DiaperChange.objects.filter(child=child) + changes = models.DiaperChange.objects.filter(child=child) if changes: - context['html'], context['javascript'] = diaperchange_types( - changes) + context['html'], context['javascript'] = \ + graphs.diaperchange_types(changes) return context class SleepPatternChildReport(PermissionRequiredMixin, DetailView): - """Graph of sleep pattern comparing sleep to wake times by day. """ - model = Child + Graph of sleep pattern comparing sleep to wake times by day. + """ + model = models.Child permission_required = ('core.view_child',) template_name = 'reports/sleep_pattern.html' @@ -62,16 +64,18 @@ class SleepPatternChildReport(PermissionRequiredMixin, DetailView): context = super(SleepPatternChildReport, self).get_context_data( **kwargs) child = context['object'] - instances = Sleep.objects.filter(child=child).order_by('start') + instances = models.Sleep.objects.filter(child=child).order_by('start') if instances: - context['html'], context['javascript'] = sleep_pattern(instances) + context['html'], context['javascript'] = \ + graphs.sleep_pattern(instances) return context class SleepTotalsChildReport(PermissionRequiredMixin, DetailView): - """Graph of total sleep by day. """ - model = Child + Graph of total sleep by day. + """ + model = models.Child permission_required = ('core.view_child',) template_name = 'reports/sleep_totals.html' @@ -84,7 +88,27 @@ class SleepTotalsChildReport(PermissionRequiredMixin, DetailView): context = super(SleepTotalsChildReport, self).get_context_data( **kwargs) child = context['object'] - instances = Sleep.objects.filter(child=child).order_by('start') + instances = models.Sleep.objects.filter(child=child).order_by('start') if instances: - context['html'], context['javascript'] = sleep_totals(instances) + context['html'], context['javascript'] = \ + graphs.sleep_totals(instances) + return context + + +class WeightWeightChildReoport(PermissionRequiredMixin, DetailView): + """ + Graph of weight change over time. + """ + model = models.Child + permission_required = ('core.view_child',) + template_name = 'reports/weight_change.html' + + def get_context_data(self, **kwargs): + context = super(WeightWeightChildReoport, self).get_context_data( + **kwargs) + child = context['object'] + objects = models.Weight.objects.filter(child=child) + if objects: + context['html'], context['javascript'] = \ + graphs.weight_weight(objects) return context