# -*- coding: utf-8 -*- from django.utils import timezone import plotly.offline as plotly import plotly.graph_objs as go from core.utils import duration_parts from reports import utils def sleep_totals(instances): """ Create a graph showing total time sleeping for each day. :param instances: a QuerySet of Sleep instances. :returns: a tuple of the the graph's html and javascript. """ totals = {} for instance in instances: start = timezone.localtime(instance.start) end = timezone.localtime(instance.end) if start.date() not in totals.keys(): totals[start.date()] = timezone.timedelta(seconds=0) if end.date() not in totals.keys(): totals[end.date()] = timezone.timedelta(seconds=0) # Account for dates crossing midnight. if start.date() != end.date(): totals[start.date()] += end.replace( year=start.year, month=start.month, day=start.day, hour=23, minute=59, second=59) - start totals[end.date()] += end - start.replace( year=end.year, month=end.month, day=end.day, hour=0, minute=0, second=0) else: totals[start.date()] += instance.duration trace = go.Bar( name='Total sleep', x=list(totals.keys()), y=[td.seconds/3600 for td in totals.values()], hoverinfo='text', textposition='outside', text=[_duration_string_short(td) for td in totals.values()] ) layout_args = utils.default_graph_layout_options() layout_args['barmode'] = 'stack' layout_args['title'] = 'Sleep Totals' layout_args['xaxis']['title'] = 'Date' layout_args['xaxis']['rangeselector'] = utils.rangeselector_date() layout_args['yaxis']['title'] = 'Hours of sleep' 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) def _duration_string_short(duration): """ Format a "short" duration string without seconds precision. This is intended to fit better in smaller spaces on a graph. :returns: a string of the form XhXm. """ h, m, s = duration_parts(duration) return '{}h{}m'.format(h, m)