Create a sleep times graph (WIP).

Adding a Pandas requirement for dealing with the data and separating graph processing in to a separate file.
This commit is contained in:
Christopher Charbonneau Wells 2017-08-25 12:19:06 -04:00
parent ae71c5933e
commit 9a2facfa72
3 changed files with 133 additions and 62 deletions

View File

@ -8,6 +8,7 @@ djangorestframework = "*"
django-filter = "*" django-filter = "*"
django-widget-tweaks = "*" django-widget-tweaks = "*"
plotly = "*" plotly = "*"
pandas = "*"
[dev-packages] [dev-packages]
faker = "*" faker = "*"

113
reports/graphs.py Normal file
View File

@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db.models import Count, Case, When
from django.db.models.functions import TruncDate
from django.utils import timezone
import pandas as pd
import plotly.offline as plotly
import plotly.graph_objs as go
from core.models import DiaperChange, Sleep
from .utils import default_graph_layout_options, split_graph_output
def diaperchange_types(child):
changes = DiaperChange.objects.filter(child=child) \
.annotate(date=TruncDate('time')) \
.values('date') \
.annotate(wet_count=Count(Case(When(wet=True, then=1)))) \
.annotate(solid_count=Count(Case(When(solid=True, then=1)))) \
.annotate(total=Count('id')) \
.order_by('-date')
solid_trace = go.Scatter(
mode='markers',
name='Solid changes',
x=list(changes.values_list('date', flat=True)),
y=list(changes.values_list('solid_count', flat=True)),
)
wet_trace = go.Scatter(
mode='markers',
name='Wet changes',
x=list(changes.values_list('date', flat=True)),
y=list(changes.values_list('wet_count', flat=True))
)
total_trace = go.Scatter(
name='Total changes',
x=list(changes.values_list('date', flat=True)),
y=list(changes.values_list('total', flat=True))
)
layout_args = default_graph_layout_options()
layout_args['barmode'] = 'stack'
layout_args['title'] = 'Diaper Change types'
layout_args['xaxis']['title'] = 'Date'
layout_args['yaxis']['title'] = 'Number of changes'
fig = go.Figure({
'data': [solid_trace, wet_trace, total_trace],
'layout': go.Layout(**layout_args)
})
output = plotly.plot(fig, output_type='div', include_plotlyjs=False)
return split_graph_output(output)
def sleep_times(child):
instances = Sleep.objects.filter(child=child).order_by('start')
df = pd.DataFrame()
last_end_time = None
df_index = 0
for instance in instances:
start_time = timezone.localtime(instance.start)
end_time = timezone.localtime(instance.end)
start_date = start_time.date().isoformat()
if start_date not in df:
df.assign(**{start_date: 0 in range(0, len(df.index))})
last_end_time = start_time.replace(hour=0, minute=0, second=0)
df_index = 0
df.set_value(
df_index, start_date, (start_time - last_end_time).seconds/60)
df_index += 1
df.set_value(df_index, start_date, instance.duration.seconds/60)
df_index += 1
last_end_time = end_time
dates = list(df)
traces = []
color = 'rgba(255, 255, 255, 0)'
for index, row in df.iterrows():
traces.append(go.Bar(
x=dates,
y=row,
marker={'color': color},
showlegend=False,
))
if color == 'rgba(255, 255, 255, 0)':
color = 'rgb(0, 0, 255)'
else:
color = 'rgba(255, 255, 255, 0)'
layout_args = default_graph_layout_options()
layout_args['barmode'] = 'stack'
layout_args['hovermode'] = 'closest'
layout_args['title'] = 'Sleep entries per day'
layout_args['xaxis']['title'] = 'Date'
layout_args['xaxis']['type'] = 'category'
layout_args['yaxis']['title'] = 'Time of day'
layout_args['yaxis']['rangemode'] = 'tozero'
layout_args['yaxis']['tickmode'] = 'array'
layout_args['yaxis']['tickvals'] = list(range(0, 1441, 60))
layout_args['yaxis']['ticktext'] = list(range(0, 1441, 60))
fig = go.Figure({
'data': traces,
'layout': go.Layout(**layout_args)
})
output = plotly.plot(fig, output_type='div', include_plotlyjs=False)
return split_graph_output(output)

View File

@ -2,82 +2,39 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Count, Case, When
from django.db.models.functions import TruncDate
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
import plotly.offline as plotly from core.models import Child
import plotly.graph_objs as go
from core.models import Child, DiaperChange from .graphs import diaperchange_types, sleep_times
from .utils import default_graph_layout_options, split_graph_output
class DiaperChangesChildReport(PermissionRequiredMixin, DetailView): class DiaperChangesChildReport(PermissionRequiredMixin, DetailView):
"""All sleep data for a child.""" """Diaper change information by type."""
model = Child model = Child
permission_required = ('core.view_child',) permission_required = ('core.view_child',)
template_name = 'reports/diaperchange.html' template_name = 'reports/diaperchange.html'
def get_context_data(self, **kwargs):
context = super(DiaperChangesChildReport, self).get_context_data(**kwargs)
child = context['object']
context['html'], context['javascript'] = diaperchange_types(child)
return context
class SleepChildReport(PermissionRequiredMixin, DetailView):
"""All sleep times by date."""
model = Child
permission_required = ('core.view_child',)
template_name = 'reports/sleep.html'
def __init__(self): def __init__(self):
super(DiaperChangesChildReport, self).__init__() super(SleepChildReport, self).__init__()
self.html = '' self.html = ''
self.javascript = '' self.javascript = ''
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(DiaperChangesChildReport, self).get_context_data(**kwargs) context = super(SleepChildReport, self).get_context_data(**kwargs)
child = context['object'] child = context['object']
self._change_types_over_time(child) context['html'], context['javascript'] = sleep_times(child)
context['html'] = self.html
context['javascript'] = self.javascript
return context return context
def _change_types_over_time(self, child):
changes = DiaperChange.objects.filter(child=child) \
.annotate(date=TruncDate('time')) \
.values('date') \
.annotate(wet_count=Count(Case(When(wet=True, then=1)))) \
.annotate(solid_count=Count(Case(When(solid=True, then=1)))) \
.annotate(total=Count('id')) \
.order_by('-date')
solid_trace = go.Scatter(
mode='markers',
name='Solid changes',
x=list(changes.values_list('date', flat=True)),
y=list(changes.values_list('solid_count', flat=True)),
)
wet_trace = go.Scatter(
mode='markers',
name='Wet changes',
x=list(changes.values_list('date', flat=True)),
y=list(changes.values_list('wet_count', flat=True))
)
total_trace = go.Scatter(
name='Total changes',
x=list(changes.values_list('date', flat=True)),
y=list(changes.values_list('total', flat=True))
)
layout_args = default_graph_layout_options()
layout_args['barmode'] = 'stack'
layout_args['title'] = 'Diaper change types over time'
layout_args['xaxis']['title'] = 'Date'
layout_args['yaxis']['title'] = 'Number of changes'
fig = go.Figure({
'data': [solid_trace, wet_trace, total_trace],
'layout': go.Layout(**layout_args)
})
output = plotly.plot(fig, output_type='div', include_plotlyjs=False)
html, javascript = split_graph_output(output)
self.html += '<a name="change_types_over_time">' + html
self.javascript += javascript
class SleepChildReport(PermissionRequiredMixin, DetailView):
"""All sleep data for a child."""
model = Child
permission_required = ('core.view_child',)
template_name = 'reports/sleep.html'