mybuddy/reports/graphs/weight_change.py

108 lines
3.7 KiB
Python
Raw Normal View History

2017-11-10 12:20:34 +00:00
# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
from django.utils.translation import gettext as _
from django.db.models.manager import BaseManager
2017-11-10 12:20:34 +00:00
import plotly.offline as plotly
import plotly.graph_objs as go
from reports import utils
def weight_change(
actual_weights: BaseManager, percentile_weights: BaseManager, birthday: datetime
):
2017-11-10 12:20:34 +00:00
"""
Create a graph showing weight over time.
:param actual_weights: a QuerySet of Weight instances.
:param percentile_weights: a QuerySet of Weight Percentile instances.
:param birthday: a datetime of the child's birthday
:returns: a tuple of the graph's html and javascript.
2017-11-10 12:20:34 +00:00
"""
actual_weights = actual_weights.order_by("-date")
2017-11-10 12:20:34 +00:00
weighing_dates: list[datetime] = list(actual_weights.values_list("date", flat=True))
measured_weights = list(actual_weights.values_list("weight", flat=True))
actual_weights_trace = go.Scatter(
2022-02-10 00:00:30 +00:00
name=_("Weight"),
x=weighing_dates,
y=measured_weights,
2022-02-10 00:00:30 +00:00
fill="tozeroy",
mode="lines+markers",
2017-11-10 12:20:34 +00:00
)
if percentile_weights:
dates = list(
map(
lambda timedelta: birthday + timedelta,
percentile_weights.values_list("age_in_days", flat=True),
)
)
# reduce percentile data xrange to end 1 day after last weigh in for formatting purposes
# https://github.com/babybuddy/babybuddy/pull/708#discussion_r1332335789
last_date_for_percentiles = max(weighing_dates) + timedelta(days=2)
dates = dates[: dates.index(last_date_for_percentiles)]
percentile_weight_3_trace = go.Scatter(
name=_("P3"),
x=dates,
y=list(percentile_weights.values_list("p3_weight", flat=True)),
line={"color": "red"},
)
percentile_weight_15_trace = go.Scatter(
name=_("P15"),
x=dates,
y=list(percentile_weights.values_list("p15_weight", flat=True)),
line={"color": "orange"},
)
percentile_weight_50_trace = go.Scatter(
name=_("P50"),
x=dates,
y=list(percentile_weights.values_list("p50_weight", flat=True)),
line={"color": "green"},
)
percentile_weight_85_trace = go.Scatter(
name=_("P85"),
x=dates,
y=list(percentile_weights.values_list("p85_weight", flat=True)),
line={"color": "orange"},
)
percentile_weight_97_trace = go.Scatter(
name=_("P97"),
x=dates,
y=list(percentile_weights.values_list("p97_weight", flat=True)),
line={"color": "red"},
)
data = [
actual_weights_trace,
]
2017-11-10 12:20:34 +00:00
layout_args = utils.default_graph_layout_options()
2022-02-10 00:00:30 +00:00
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")
if percentile_weights:
# zoom in on the relevant dates
layout_args["xaxis"]["range"] = [
birthday,
max(weighing_dates) + timedelta(days=1),
]
layout_args["yaxis"]["range"] = [0, max(measured_weights) * 1.5]
data.extend(
[
percentile_weight_97_trace,
percentile_weight_85_trace,
percentile_weight_50_trace,
percentile_weight_15_trace,
percentile_weight_3_trace,
]
)
2017-11-10 12:20:34 +00:00
fig = go.Figure({"data": data, "layout": go.Layout(**layout_args)})
2022-02-10 00:00:30 +00:00
output = plotly.plot(fig, output_type="div", include_plotlyjs=False)
2017-11-10 12:20:34 +00:00
return utils.split_graph_output(output)