def diaperchange_amounts(instances):
    """
    Create a graph showing daily diaper change amounts over time.
    :param instances: a QuerySet of DiaperChange instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    totals = {}
    for instance in instances:
        time_local = timezone.localtime(instance.time)
        date = time_local.date()
        if date not in totals.keys():
            totals[date] = 0
        totals[date] += instance.amount or 0

    amounts = [round(amount, 2) for amount in totals.values()]
    trace = go.Bar(
        name=_("Diaper change amount"),
        x=list(totals.keys()),
        y=amounts,
        hoverinfo="text",
        textposition="outside",
        text=amounts,
    )

    layout_args = utils.default_graph_layout_options()
    layout_args["title"] = _("<b>Diaper Change Amounts</b>")
    layout_args["xaxis"]["title"] = _("Date")
    layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
    layout_args["yaxis"]["title"] = _("Change amount")

    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)
Exemple #2
0
def diaperchange_lifetimes(changes):
    """
    Create a graph showing how long diapers last (time between changes).
    :param changes: a QuerySet of Diaper Change instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    changes = changes.order_by("time")
    durations = []
    last_change = changes.first()
    for change in changes[1:]:
        duration = change.time - last_change.time
        if duration.seconds > 0:
            durations.append(duration)
        last_change = change

    trace = go.Box(
        y=[round(d.seconds / 3600, 2) for d in durations],
        name=_("Changes"),
        jitter=0.3,
        pointpos=-1.8,
        boxpoints="all",
    )

    layout_args = utils.default_graph_layout_options()
    layout_args["height"] = 800
    layout_args["title"] = _("<b>Diaper Lifetimes</b>")
    layout_args["yaxis"]["title"] = _("Time between changes (hours)")
    layout_args["yaxis"]["zeroline"] = False
    layout_args["yaxis"]["dtick"] = 1

    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)
Exemple #3
0
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'] = '<b>Weight</b>'
    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)
Exemple #4
0
def diaperchange_lifetimes(changes):
    """
    Create a graph showing how long diapers last (time between changes).
    :param changes: a QuerySet of Diaper Change instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    changes = changes.order_by('time')
    durations = []
    last_change = changes.first()
    for change in changes[1:]:
        duration = change.time - last_change.time
        if duration.seconds > 0:
            durations.append(duration)
        last_change = change

    trace = go.Box(y=[round(d.seconds / 3600, 2) for d in durations],
                   name=_('Changes'),
                   jitter=0.3,
                   pointpos=-1.8,
                   boxpoints='all')

    layout_args = utils.default_graph_layout_options()
    layout_args['height'] = 800
    layout_args['title'] = _('<b>Diaper Lifetimes</b>')
    layout_args['yaxis']['title'] = _('Time between changes (hours)')
    layout_args['yaxis']['zeroline'] = False
    layout_args['yaxis']['dtick'] = 1

    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)
Exemple #5
0
def height_change(objects):
    """
    Create a graph showing height over time.
    :param objects: a QuerySet of Height instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    objects = objects.order_by("-date")

    trace = go.Scatter(
        name=_("Height"),
        x=list(objects.values_list("date", flat=True)),
        y=list(objects.values_list("height", flat=True)),
        fill="tozeroy",
    )

    layout_args = utils.default_graph_layout_options()
    layout_args["barmode"] = "stack"
    layout_args["title"] = _("<b>Height</b>")
    layout_args["xaxis"]["title"] = _("Date")
    layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
    layout_args["yaxis"]["title"] = _("Height")

    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)
Exemple #6
0
def feeding_duration(instances):
    """
    Create a graph showing average duration of feeding instances over time.

    This function originally used the Avg() function from django.db.models but
    for some reason it was returning None any time the exact count of entries
    was equal to seven.

    :param instances: a QuerySet of Feeding 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')

    averages = []
    for total in totals:
        averages.append(total['sum']/total['count'])

    trace_avg = go.Scatter(
        name=_('Average duration'),
        line=dict(shape='spline'),
        x=list(totals.values_list('date', flat=True)),
        y=[td.seconds/60 for td in averages],
        hoverinfo='text',
        text=[_duration_string_ms(td) for td in averages]
    )
    trace_count = go.Scatter(
        name=_('Total feedings'),
        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'] = _('<b>Average Feeding Durations</b>')
    layout_args['xaxis']['title'] = _('Date')
    layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()
    layout_args['yaxis']['title'] = _('Average duration (minutes)')
    layout_args['yaxis2'] = dict(layout_args['yaxis'])
    layout_args['yaxis2']['title'] = _('Number of feedings')
    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)
Exemple #7
0
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"] = _("<b>Sleep Totals</b>")
    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)
Exemple #8
0
def feeding_duration(instances):
    """
    Create a graph showing average duration of feeding instances over time.

    This function originally used the Avg() function from django.db.models but
    for some reason it was returning None any time the exact count of entries
    was equal to seven.

    :param instances: a QuerySet of Feeding 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"))

    averages = []
    for total in totals:
        averages.append(total["sum"] / total["count"])

    trace_avg = go.Scatter(
        name=_("Average duration"),
        line=dict(shape="spline"),
        x=list(totals.values_list("date", flat=True)),
        y=[td.seconds / 60 for td in averages],
        hoverinfo="text",
        text=[_duration_string_ms(td) for td in averages],
    )
    trace_count = go.Scatter(
        name=_("Total feedings"),
        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"] = _("<b>Average Feeding Durations</b>")
    layout_args["xaxis"]["title"] = _("Date")
    layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
    layout_args["yaxis"]["title"] = _("Average duration (minutes)")
    layout_args["yaxis2"] = dict(layout_args["yaxis"])
    layout_args["yaxis2"]["title"] = _("Number of feedings")
    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)
Exemple #9
0
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"] = _("<b>Total Tummy Time Durations</b>")
    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)
Exemple #10
0
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'] = '<b>Sleep Totals</b>'
    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)
Exemple #11
0
def diaperchange_types(changes):
    """
    Create a graph showing types of totals for diaper changes.
    :param changes: a QuerySet of Diaper Change instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    changes = changes.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'),
        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'),
        x=list(changes.values_list('date', flat=True)),
        y=list(changes.values_list('wet_count', flat=True))
    )
    total_trace = go.Scatter(
        name=_('Total'),
        x=list(changes.values_list('date', flat=True)),
        y=list(changes.values_list('total', flat=True))
    )

    layout_args = utils.default_graph_layout_options()
    layout_args['barmode'] = 'stack'
    layout_args['title'] = _('<b>Diaper Change Types</b>')
    layout_args['xaxis']['title'] = _('Date')
    layout_args['xaxis']['rangeselector'] = utils.rangeselector_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,
        config={'locale': get_language()}
    )
    return utils.split_graph_output(output)
Exemple #12
0
def diaperchange_types(changes):
    """
    Create a graph showing types of totals for diaper changes.
    :param changes: a QuerySet of Diaper Change instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    changes = (changes.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"),
        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"),
        x=list(changes.values_list("date", flat=True)),
        y=list(changes.values_list("wet_count", flat=True)),
    )
    total_trace = go.Scatter(
        name=_("Total"),
        x=list(changes.values_list("date", flat=True)),
        y=list(changes.values_list("total", flat=True)),
    )

    layout_args = utils.default_graph_layout_options()
    layout_args["barmode"] = "stack"
    layout_args["title"] = _("<b>Diaper Change Types</b>")
    layout_args["xaxis"]["title"] = _("Date")
    layout_args["xaxis"]["rangeselector"] = utils.rangeselector_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,
        config={"locale": get_language()},
    )
    return utils.split_graph_output(output)
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'] = _('<b>Total Tummy Time Durations</b>')
    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 diaperchange_amounts(instances):
    """
    Create a graph showing daily diaper change amounts over time.
    :param instances: a QuerySet of DiaperChange instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    totals = {}
    for instance in instances:
        time_local = timezone.localtime(instance.time)
        date = time_local.date()
        if date not in totals.keys():
            totals[date] = 0
        totals[date] += instance.amount or 0

    amounts = [round(amount, 2) for amount in totals.values()]
    trace = go.Bar(
        name=_('Diaper change amount'),
        x=list(totals.keys()),
        y=amounts,
        hoverinfo='text',
        textposition='outside',
        text=amounts
    )

    layout_args = utils.default_graph_layout_options()
    layout_args['title'] = _('<b>Diaper Change Amounts</b>')
    layout_args['xaxis']['title'] = _('Date')
    layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()
    layout_args['yaxis']['title'] = _('Change amount')

    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)
Exemple #15
0
def sleep_pattern(instances):
    """
    Create a graph showing blocked out periods of sleep during each day.
    :param instances: a QuerySet of Sleep instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    # TODO: Simplify this using the bar charts "base" property.
    y_df = pd.DataFrame()
    text_df = pd.DataFrame()
    last_end_time = None
    adjustment = 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()
        duration = instance.duration

        # Check if the previous entry crossed midnight (see below).
        if adjustment:
            # Fake (0) entry to keep the color switching logic working.
            df_index = _add_sleep_entry(y_df, text_df, 0, adjustment['column'],
                                        0)
            # Real adjustment entry.
            df_index = _add_sleep_entry(
                y_df, text_df, df_index, adjustment['column'],
                adjustment['duration'].seconds / 60,
                'Asleep {} ({} to {})'.format(
                    duration_string(adjustment['duration']),
                    adjustment['start_time'].strftime('%I:%M %p'),
                    adjustment['end_time'].strftime('%I:%M %p')))
            last_end_time = timezone.localtime(adjustment['end_time'])
            adjustment = None

        # If the dates do not match, set up an adjustment for the next day.
        if end_time.date() != start_time.date():
            adj_start_time = end_time.replace(hour=0, minute=0, second=0)
            adjustment = {
                'column': end_time.date().isoformat(),
                'start_time': adj_start_time,
                'end_time': end_time,
                'duration': end_time - adj_start_time
            }

            # Adjust end_time for the current entry.
            end_time = end_time.replace(year=start_time.year,
                                        month=start_time.month,
                                        day=start_time.day,
                                        hour=23,
                                        minute=59,
                                        second=0)
            duration = end_time - start_time

        if start_date not in y_df:
            last_end_time = start_time.replace(hour=0, minute=0, second=0)

        # Awake time.
        df_index = _add_sleep_entry(y_df, text_df, df_index, start_date,
                                    (start_time - last_end_time).seconds / 60)

        # Asleep time.
        df_index = _add_sleep_entry(
            y_df, text_df, df_index, start_date, duration.seconds / 60,
            'Asleep {} ({} to {})'.format(duration_string(duration),
                                          start_time.strftime('%I:%M %p'),
                                          end_time.strftime('%I:%M %p')))

        # Update the previous entry duration if an offset change occurred.
        # This can happen when an entry crosses a daylight savings time change.
        if start_time.utcoffset() != end_time.utcoffset():
            diff = start_time.utcoffset() - end_time.utcoffset()
            duration -= timezone.timedelta(seconds=diff.seconds)
            y_df.set_value(df_index - 1, start_date, duration.seconds / 60)

        last_end_time = end_time

    dates = list(y_df)
    traces = []
    color = 'rgba(255, 255, 255, 0)'
    for index, row in y_df.iterrows():
        traces.append(
            go.Bar(
                x=dates,
                y=row,
                text=text_df.ix[index],
                hoverinfo='text',
                marker={'color': color},
                showlegend=False,
            ))
        if color == 'rgba(255, 255, 255, 0)':
            color = 'rgb(35, 110, 150)'
        else:
            color = 'rgba(255, 255, 255, 0)'

    layout_args = utils.default_graph_layout_options()
    layout_args['margin']['b'] = 100

    layout_args['barmode'] = 'stack'
    layout_args['hovermode'] = 'closest'
    layout_args['title'] = '<b>Sleep Pattern</b>'
    layout_args['height'] = 800

    layout_args['xaxis']['title'] = 'Date'
    layout_args['xaxis']['tickangle'] = -65
    layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()

    start = timezone.localtime().strptime('12:00 AM', '%I:%M %p')
    ticks = OrderedDict()
    ticks[0] = start.strftime('%I:%M %p')
    for i in range(30, 60 * 24, 30):
        ticks[i] = (start + timezone.timedelta(minutes=i)).strftime('%I:%M %p')

    layout_args['yaxis']['title'] = 'Time of day'
    layout_args['yaxis']['rangemode'] = 'tozero'
    layout_args['yaxis']['tickmode'] = 'array'
    layout_args['yaxis']['tickvals'] = list(ticks.keys())
    layout_args['yaxis']['ticktext'] = list(ticks.values())
    layout_args['yaxis']['tickfont'] = {'size': 10}

    fig = go.Figure({'data': traces, 'layout': go.Layout(**layout_args)})
    output = plotly.plot(fig, output_type='div', include_plotlyjs=False)
    return utils.split_graph_output(output)
Exemple #16
0
def sleep_pattern(sleeps):
    """
    Create a graph showing blocked out periods of sleep during each day.
    :param sleeps: a QuerySet of Sleep instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    last_end_time = None
    adjustment = None

    first_day = timezone.localtime(sleeps.first().start)
    last_day = timezone.localtime(sleeps.last().end)
    days = _init_days(first_day, last_day)

    for sleep in sleeps:
        start_time = timezone.localtime(sleep.start)
        end_time = timezone.localtime(sleep.end)
        start_date = start_time.date().isoformat()
        end_date = end_time.date().isoformat()
        duration = sleep.duration

        # Check if the previous entry crossed midnight (see below).
        if adjustment:
            _add_adjustment(adjustment, days)
            last_end_time = timezone.localtime(adjustment["end_time"])
            adjustment = None

        # If the dates do not match, set up an adjustment for the next day.
        if end_time.date() != start_time.date():
            adj_start_time = end_time.replace(hour=0, minute=0, second=0)
            adjustment = {
                "column": end_date,
                "start_time": adj_start_time,
                "end_time": end_time,
                "duration": end_time - adj_start_time,
            }

            # Adjust end_time for the current entry.
            end_time = end_time.replace(
                year=start_time.year,
                month=start_time.month,
                day=start_time.day,
                hour=23,
                minute=59,
                second=0,
            )
            duration = end_time - start_time

        if not last_end_time:
            last_end_time = start_time.replace(hour=0, minute=0, second=0)

        # Awake time.
        days[start_date].append({
            "time": (start_time - last_end_time).seconds / 60,
            "label": None
        })

        # Asleep time.
        days[start_date].append({
            "time":
            duration.seconds / 60,
            "label":
            _format_label(duration, start_time, end_time),
        })

        # Update the previous entry duration if an offset change occurred.
        # This can happen when an entry crosses a daylight savings time change.
        if start_time.utcoffset() != end_time.utcoffset():
            diff = start_time.utcoffset() - end_time.utcoffset()
            duration -= timezone.timedelta(seconds=diff.seconds)
            yesterday = end_time - timezone.timedelta(days=1)
            yesterday = yesterday.date().isoformat()
            days[yesterday][len(days[yesterday]) - 1] = {
                "time": duration.seconds / 60,
                "label": _format_label(duration, start_time, end_time),
            }

        last_end_time = end_time

    # Handle any left over adjustment (if the last entry crossed midnight).
    if adjustment:
        _add_adjustment(adjustment, days)

    # Create dates for x-axis using a 12:00:00 time to ensure correct
    # positioning of bars (covering entire day).
    dates = []
    for time in list(days.keys()):
        dates.append("{} 12:00:00".format(time))

    traces = []
    color = AWAKE_COLOR

    # Set iterator and determine maximum iteration for dates.
    i = 0
    max_i = 0
    for date_times in days.values():
        max_i = max(len(date_times), max_i)
    while i < max_i:
        y = {}
        text = {}
        for date in days.keys():
            try:
                y[date] = days[date][i]["time"]
                text[date] = days[date][i]["label"]
            except IndexError:
                y[date] = None
                text[date] = None
        i += 1
        traces.append(
            go.Bar(
                x=dates,
                y=list(y.values()),
                hovertext=list(text.values()),
                # `hoverinfo` is deprecated but if we use the new `hovertemplate`
                # the "filler" areas for awake time get a hover that says "null"
                # and there is no way to prevent this currently with Plotly.
                hoverinfo="text",
                marker={"color": color},
                showlegend=False,
            ))
        if color == AWAKE_COLOR:
            color = ASLEEP_COLOR
        else:
            color = AWAKE_COLOR

    layout_args = utils.default_graph_layout_options()
    layout_args["margin"]["b"] = 100

    layout_args["barmode"] = "stack"
    layout_args["bargap"] = 0
    layout_args["hovermode"] = "closest"
    layout_args["title"] = _("<b>Sleep Pattern</b>")
    layout_args["height"] = 800

    layout_args["xaxis"]["title"] = _("Date")
    layout_args["xaxis"]["tickangle"] = -65
    layout_args["xaxis"]["tickformat"] = "%b %e\n%Y"
    layout_args["xaxis"]["ticklabelmode"] = "period"
    layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()

    start = timezone.localtime().strptime("12:00 AM", "%I:%M %p")
    ticks = OrderedDict()
    ticks[0] = start.strftime("%I:%M %p")
    for i in range(0, 60 * 24, 30):
        ticks[i] = formats.time_format(start + timezone.timedelta(minutes=i),
                                       "TIME_FORMAT")

    layout_args["yaxis"]["title"] = _("Time of day")
    layout_args["yaxis"]["range"] = [24 * 60, 0]
    layout_args["yaxis"]["tickmode"] = "array"
    layout_args["yaxis"]["tickvals"] = list(ticks.keys())
    layout_args["yaxis"]["ticktext"] = list(ticks.values())
    layout_args["yaxis"]["tickfont"] = {"size": 10}

    fig = go.Figure({"data": traces, "layout": go.Layout(**layout_args)})
    output = plotly.plot(fig, output_type="div", include_plotlyjs=False)
    return utils.split_graph_output(output)
Exemple #17
0
def sleep_pattern(instances):
    """
    Create a graph showing blocked out periods of sleep during each day.
    :param instances: a QuerySet of Sleep instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    times = {}
    labels = {}
    last_end_time = None
    adjustment = None
    for instance in instances:
        start_time = timezone.localtime(instance.start)
        end_time = timezone.localtime(instance.end)
        start_date = start_time.date().isoformat()
        end_date = end_time.date().isoformat()
        duration = instance.duration

        # Ensure that lists are initialized for the start and end date (as they
        # may be different dates).
        if start_date not in times:
            times[start_date] = []
        if start_date not in labels:
            labels[start_date] = []
        if end_date not in times:
            times[end_date] = []
        if end_date not in labels:
            labels[end_date] = []

        # Check if the previous entry crossed midnight (see below).
        if adjustment:
            _add_adjustment(adjustment, times, labels)
            last_end_time = timezone.localtime(adjustment['end_time'])
            adjustment = None

        # If the dates do not match, set up an adjustment for the next day.
        if end_time.date() != start_time.date():
            adj_start_time = end_time.replace(hour=0, minute=0, second=0)
            adjustment = {
                'column': end_time.date().isoformat(),
                'start_time': adj_start_time,
                'end_time': end_time,
                'duration': end_time - adj_start_time
            }

            # Adjust end_time for the current entry.
            end_time = end_time.replace(year=start_time.year,
                                        month=start_time.month,
                                        day=start_time.day,
                                        hour=23,
                                        minute=59,
                                        second=0)
            duration = end_time - start_time

        if not last_end_time:
            last_end_time = start_time.replace(hour=0, minute=0, second=0)

        # Awake time.
        times[start_date].append((start_time - last_end_time).seconds / 60)
        labels[start_date].append(None)

        # Asleep time.
        times[start_date].append(duration.seconds / 60)
        labels[start_date].append(_format_label(duration, start_time,
                                                end_time))

        # Update the previous entry duration if an offset change occurred.
        # This can happen when an entry crosses a daylight savings time change.
        if start_time.utcoffset() != end_time.utcoffset():
            diff = start_time.utcoffset() - end_time.utcoffset()
            duration -= timezone.timedelta(seconds=diff.seconds)
            yesterday = (end_time - timezone.timedelta(days=1))
            yesterday = yesterday.date().isoformat()
            times[yesterday][len(times[yesterday]) - 1] = duration.seconds / 60
            labels[yesterday][len(times[yesterday]) - 1] = _format_label(
                duration, start_time, end_time)

        last_end_time = end_time

    # Handle any left over adjustment (if the last entry crossed midnight).
    if adjustment:
        _add_adjustment(adjustment, times, labels)

    # Create dates for x-axis using a 12:00:00 time to ensure correct
    # positioning of bars (covering entire day).
    dates = []
    for time in list(times.keys()):
        dates.append('{} 12:00:00'.format(time))

    traces = []
    color = 'rgba(255, 255, 255, 0)'

    # Set iterator and determine maximum iteration for dates.
    i = 0
    max_i = 0
    for date_times in times.values():
        max_i = max(len(date_times), max_i)
    while i < max_i:
        y = {}
        text = {}
        for date in times.keys():
            try:
                y[date] = times[date][i]
                text[date] = labels[date][i]
            except IndexError:
                y[date] = None
                text[date] = None
        i += 1
        traces.append(
            go.Bar(
                x=dates,
                y=list(y.values()),
                hovertext=list(text.values()),
                # `hoverinfo` is deprecated but if we use the new `hovertemplate`
                # the "filler" areas for awake time get a hover that says "null"
                # and there is no way to prevent this currently with Plotly.
                hoverinfo='text',
                marker={'color': color},
                showlegend=False,
            ))
        if color == 'rgba(255, 255, 255, 0)':
            color = 'rgb(35, 110, 150)'
        else:
            color = 'rgba(255, 255, 255, 0)'

    layout_args = utils.default_graph_layout_options()
    layout_args['margin']['b'] = 100

    layout_args['barmode'] = 'stack'
    layout_args['bargap'] = 0
    layout_args['hovermode'] = 'closest'
    layout_args['title'] = _('<b>Sleep Pattern</b>')
    layout_args['height'] = 800

    layout_args['xaxis']['title'] = _('Date')
    layout_args['xaxis']['tickangle'] = -65
    layout_args['xaxis']['tickformat'] = '%b %e\n%Y'
    layout_args['xaxis']['ticklabelmode'] = 'period'
    layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()

    start = timezone.localtime().strptime('12:00 AM', '%I:%M %p')
    ticks = OrderedDict()
    ticks[0] = start.strftime('%I:%M %p')
    for i in range(0, 60 * 24, 30):
        ticks[i] = formats.time_format(start + timezone.timedelta(minutes=i),
                                       'TIME_FORMAT')

    layout_args['yaxis']['title'] = _('Time of day')
    layout_args['yaxis']['range'] = [24 * 60, 0]
    layout_args['yaxis']['tickmode'] = 'array'
    layout_args['yaxis']['tickvals'] = list(ticks.keys())
    layout_args['yaxis']['ticktext'] = list(ticks.values())
    layout_args['yaxis']['tickfont'] = {'size': 10}

    fig = go.Figure({'data': traces, 'layout': go.Layout(**layout_args)})
    output = plotly.plot(fig, output_type='div', include_plotlyjs=False)
    return utils.split_graph_output(output)
Exemple #18
0
def sleep_pattern(instances):
    """
    Create a graph showing blocked out periods of sleep during each day.
    :param instances: a QuerySet of Sleep instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    times = {}
    labels = {}
    last_end_time = None
    adjustment = None
    for instance in instances:
        start_time = timezone.localtime(instance.start)
        end_time = timezone.localtime(instance.end)
        start_date = start_time.date().isoformat()
        duration = instance.duration

        if start_date not in times:
            times[start_date] = []
        if start_date not in labels:
            labels[start_date] = []

        # Check if the previous entry crossed midnight (see below).
        if adjustment:
            # Fake (0) entry to keep the color switching logic working.
            times[adjustment['column']].append(0)
            labels[adjustment['column']].append(0)

            # Real adjustment entry.
            times[adjustment['column']].append(adjustment['duration'].seconds /
                                               60)
            labels[adjustment['column']].append(
                _format_label(adjustment['duration'], adjustment['start_time'],
                              adjustment['end_time']))

            last_end_time = timezone.localtime(adjustment['end_time'])
            adjustment = None

        # If the dates do not match, set up an adjustment for the next day.
        if end_time.date() != start_time.date():
            adj_start_time = end_time.replace(hour=0, minute=0, second=0)
            adjustment = {
                'column': end_time.date().isoformat(),
                'start_time': adj_start_time,
                'end_time': end_time,
                'duration': end_time - adj_start_time
            }

            # Adjust end_time for the current entry.
            end_time = end_time.replace(year=start_time.year,
                                        month=start_time.month,
                                        day=start_time.day,
                                        hour=23,
                                        minute=59,
                                        second=0)
            duration = end_time - start_time

        if not last_end_time:
            last_end_time = start_time.replace(hour=0, minute=0, second=0)

        # Awake time.
        times[start_date].append((start_time - last_end_time).seconds / 60)
        labels[start_date].append(None)

        # Asleep time.
        times[start_date].append(duration.seconds / 60)
        labels[start_date].append(_format_label(duration, start_time,
                                                end_time))

        # Update the previous entry duration if an offset change occurred.
        # This can happen when an entry crosses a daylight savings time change.
        if start_time.utcoffset() != end_time.utcoffset():
            diff = start_time.utcoffset() - end_time.utcoffset()
            duration -= timezone.timedelta(seconds=diff.seconds)
            yesterday = (end_time - timezone.timedelta(days=1))
            yesterday = yesterday.date().isoformat()
            times[yesterday][len(times[yesterday]) - 1] = duration.seconds / 60
            labels[yesterday][len(times[yesterday]) - 1] = _format_label(
                duration, start_time, end_time)

        last_end_time = end_time

    dates = list(times.keys())
    traces = []
    color = 'rgba(255, 255, 255, 0)'

    # Set iterator and determine maximum iteration for dates.
    i = 0
    max_i = 0
    for date_times in times.values():
        max_i = max(len(date_times), max_i)
    while i < max_i:
        y = {}
        text = {}
        for date in times.keys():
            try:
                y[date] = times[date][i]
                text[date] = labels[date][i]
            except IndexError:
                y[date] = None
                text[date] = None
        i += 1
        traces.append(
            go.Bar(
                x=dates,
                y=list(y.values()),
                text=list(text.values()),
                hoverinfo='text',
                marker={'color': color},
                marker_line_width=0,
                showlegend=False,
            ))
        if color == 'rgba(255, 255, 255, 0)':
            color = 'rgb(35, 110, 150)'
        else:
            color = 'rgba(255, 255, 255, 0)'

    layout_args = utils.default_graph_layout_options()
    layout_args['margin']['b'] = 100

    layout_args['barmode'] = 'stack'
    layout_args['bargap'] = 0
    layout_args['hovermode'] = 'closest'
    layout_args['title'] = _('<b>Sleep Pattern</b>')
    layout_args['height'] = 800

    layout_args['xaxis']['title'] = _('Date')
    layout_args['xaxis']['tickangle'] = -65
    layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()

    start = timezone.localtime().strptime('12:00 AM', '%I:%M %p')
    ticks = OrderedDict()
    ticks[0] = start.strftime('%I:%M %p')
    for i in range(30, 60 * 24, 30):
        ticks[i] = (start + timezone.timedelta(minutes=i)).strftime('%I:%M %p')

    layout_args['yaxis']['title'] = _('Time of day')
    layout_args['yaxis']['range'] = [0, 24 * 60]
    layout_args['yaxis']['tickmode'] = 'array'
    layout_args['yaxis']['tickvals'] = list(ticks.keys())
    layout_args['yaxis']['ticktext'] = list(ticks.values())
    layout_args['yaxis']['tickfont'] = {'size': 10}

    fig = go.Figure({'data': traces, 'layout': go.Layout(**layout_args)})
    output = plotly.plot(fig, output_type='div', include_plotlyjs=False)
    return utils.split_graph_output(output)
def feeding_amounts(instances):
    """
    Create a graph showing daily feeding amounts over time.
    :param instances: a QuerySet of Feeding instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    feeding_types, feeding_types_desc = map(
        list, zip(*models.Feeding._meta.get_field("type").choices))
    total_idx = len(feeding_types) + 1  # +1 for aggregate total
    totals_list = list()
    for i in range(total_idx):
        totals_list.append({})
    for instance in instances:
        end = timezone.localtime(instance.end)
        date = end.date()
        if date not in totals_list[total_idx - 1].keys():
            for item in totals_list:
                item[date] = 0
        feeding_idx = feeding_types.index(instance.type)
        totals_list[feeding_idx][date] += instance.amount or 0
        totals_list[total_idx - 1][date] += instance.amount or 0
    zeros = [0 for a in totals_list[total_idx - 1].values()]

    # sum each feeding type for graph
    amounts_array = []
    for i in range(total_idx):
        amounts_array.append([round(a, 2) for a in totals_list[i].values()])

    traces = []
    for i in range(total_idx - 1):
        for x in amounts_array[i]:
            if x != 0:  # Only include if it has non zero values
                traces.append(
                    go.Bar(
                        name=str(feeding_types_desc[i]),
                        x=list(totals_list[total_idx - 1].keys()),
                        y=amounts_array[i],
                        text=amounts_array[i],
                        hovertemplate=str(feeding_types_desc[i]),
                    ))
                break

    traces.append(
        go.Bar(
            name=_("Total"),
            x=list(totals_list[total_idx - 1].keys()),
            y=zeros,
            hoverinfo="text",
            textposition="outside",
            text=amounts_array[total_idx - 1],
            showlegend=False,
        ))

    layout_args = utils.default_graph_layout_options()
    layout_args["title"] = _("<b>Total Feeding Amount by Type</b>")
    layout_args["xaxis"]["title"] = _("Date")
    layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
    layout_args["yaxis"]["title"] = _("Feeding amount")

    fig = go.Figure({"data": traces, "layout": go.Layout(**layout_args)})
    fig.update_layout(barmode="stack")
    output = plotly.plot(fig, output_type="div", include_plotlyjs=False)
    return utils.split_graph_output(output)
Exemple #20
0
def pumping_amounts(objects):
    """
    Create a graph showing pumping amounts over time.
    :param instances: a QuerySet of Pumping instances.
    :returns: a tuple of the the graph's html and javascript.
    """
    objects = objects.order_by("time")

    # We need to find date totals for annotations at the end
    curr_date = ""
    date_totals = {}
    for object in objects:
        date_s = timezone.localtime(object.time)
        date_s = str(date_s.date())
        if curr_date != date_s:
            date_totals[date_s] = 0.0
            curr_date = date_s
        date_totals[date_s] += object.amount

    dates = []  # Single array for each bar
    amounts = []  # Array of arrays containing amounts
    index_x, index_y = 0, -1
    for object in objects:
        date_s = timezone.localtime(object.time)
        date_s = str(date_s.date())
        if date_s not in dates:
            dates.append(date_s)
            index_y += 1
            index_x = 0
        if len(amounts) == 0 or len(amounts) <= index_x:
            amounts.append([0] * len(date_totals.keys()))
        amounts[index_x][index_y] = object.amount
        index_x += 1

    traces = []
    for i in range(0, len(amounts) - 1):
        traces.append(
            go.Bar(
                name="Amount",
                x=dates,
                y=amounts[i],
                text=amounts[i],
                hovertemplate=amounts[i],
                showlegend=False,
            ))

    layout_args = utils.default_graph_layout_options()
    layout_args["title"] = _("<b>Total Pumping Amount</b>")
    layout_args["xaxis"]["title"] = _("Date")
    layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
    layout_args["yaxis"]["title"] = _("Pumping Amount")

    total_labels = [{
        "x": x,
        "y": total * 1.1,
        "text": str(total),
        "showarrow": False
    } for x, total in zip(list(dates), date_totals.values())]

    fig = go.Figure({"data": traces, "layout": go.Layout(**layout_args)})
    fig.update_layout(barmode="stack", annotations=total_labels)
    output = plotly.plot(fig, output_type="div", include_plotlyjs=False)
    return utils.split_graph_output(output)