2021-12-29 07:53:36 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2023-12-19 17:36:07 +00:00
|
|
|
from datetime import datetime, timedelta
|
2021-12-29 07:53:36 +00:00
|
|
|
from django.utils.translation import gettext as _
|
2023-12-19 17:36:07 +00:00
|
|
|
from django.db.models.manager import BaseManager
|
2021-12-29 07:53:36 +00:00
|
|
|
|
|
|
|
import plotly.offline as plotly
|
|
|
|
import plotly.graph_objs as go
|
|
|
|
|
|
|
|
from reports import utils
|
|
|
|
|
|
|
|
|
2023-12-19 17:36:07 +00:00
|
|
|
def height_change(
|
|
|
|
actual_heights: BaseManager, percentile_heights: BaseManager, birthday: datetime
|
|
|
|
):
|
2021-12-29 07:53:36 +00:00
|
|
|
"""
|
|
|
|
Create a graph showing height over time.
|
2023-12-19 17:36:07 +00:00
|
|
|
:param actual_heights: a QuerySet of Height instances.
|
|
|
|
:param percentile_heights: a QuerySet of Height Percentile instances.
|
|
|
|
:param birthday: a datetime of the child's birthday
|
2024-01-28 03:42:12 +00:00
|
|
|
:returns: a tuple of the graph's html and javascript.
|
2021-12-29 07:53:36 +00:00
|
|
|
"""
|
2023-12-19 17:36:07 +00:00
|
|
|
actual_heights = actual_heights.order_by("-date")
|
2021-12-29 07:53:36 +00:00
|
|
|
|
2023-12-23 15:14:49 +00:00
|
|
|
measuring_dates: list[datetime] = list(
|
|
|
|
actual_heights.values_list("date", flat=True)
|
|
|
|
)
|
2023-12-19 17:36:07 +00:00
|
|
|
measured_heights = list(actual_heights.values_list("height", flat=True))
|
|
|
|
|
|
|
|
actual_heights_trace = go.Scatter(
|
2022-02-10 00:00:30 +00:00
|
|
|
name=_("Height"),
|
2023-12-23 15:14:49 +00:00
|
|
|
x=measuring_dates,
|
2023-12-19 17:36:07 +00:00
|
|
|
y=measured_heights,
|
2022-02-10 00:00:30 +00:00
|
|
|
fill="tozeroy",
|
2023-12-19 17:36:07 +00:00
|
|
|
mode="lines+markers",
|
2021-12-29 07:53:36 +00:00
|
|
|
)
|
|
|
|
|
2023-12-19 17:36:07 +00:00
|
|
|
if percentile_heights:
|
|
|
|
dates = list(
|
|
|
|
map(
|
|
|
|
lambda timedelta: birthday + timedelta,
|
|
|
|
percentile_heights.values_list("age_in_days", flat=True),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2023-12-23 15:14:49 +00:00
|
|
|
# reduce percentile data xrange to end 1 day after last height measurement in for formatting purposes
|
2023-12-19 17:36:07 +00:00
|
|
|
# https://github.com/babybuddy/babybuddy/pull/708#discussion_r1332335789
|
2023-12-23 15:14:49 +00:00
|
|
|
last_date_for_percentiles = max(measuring_dates) + timedelta(days=2)
|
2023-12-19 17:36:07 +00:00
|
|
|
dates = dates[: dates.index(last_date_for_percentiles)]
|
|
|
|
|
|
|
|
percentile_height_3_trace = go.Scatter(
|
|
|
|
name=_("P3"),
|
|
|
|
x=dates,
|
|
|
|
y=list(percentile_heights.values_list("p3_height", flat=True)),
|
|
|
|
line={"color": "red"},
|
|
|
|
)
|
|
|
|
percentile_height_15_trace = go.Scatter(
|
|
|
|
name=_("P15"),
|
|
|
|
x=dates,
|
|
|
|
y=list(percentile_heights.values_list("p15_height", flat=True)),
|
|
|
|
line={"color": "orange"},
|
|
|
|
)
|
|
|
|
percentile_height_50_trace = go.Scatter(
|
|
|
|
name=_("P50"),
|
|
|
|
x=dates,
|
|
|
|
y=list(percentile_heights.values_list("p50_height", flat=True)),
|
|
|
|
line={"color": "green"},
|
|
|
|
)
|
|
|
|
percentile_height_85_trace = go.Scatter(
|
|
|
|
name=_("P85"),
|
|
|
|
x=dates,
|
|
|
|
y=list(percentile_heights.values_list("p85_height", flat=True)),
|
|
|
|
line={"color": "orange"},
|
|
|
|
)
|
|
|
|
percentile_height_97_trace = go.Scatter(
|
|
|
|
name=_("P97"),
|
|
|
|
x=dates,
|
|
|
|
y=list(percentile_heights.values_list("p97_height", flat=True)),
|
|
|
|
line={"color": "red"},
|
|
|
|
)
|
|
|
|
|
|
|
|
data = [
|
|
|
|
actual_heights_trace,
|
|
|
|
]
|
2021-12-29 07:53:36 +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>Height</b>")
|
|
|
|
layout_args["xaxis"]["title"] = _("Date")
|
|
|
|
layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
|
|
|
|
layout_args["yaxis"]["title"] = _("Height")
|
2023-12-19 17:36:07 +00:00
|
|
|
if percentile_heights:
|
|
|
|
# zoom in on the relevant dates
|
|
|
|
layout_args["xaxis"]["range"] = [
|
|
|
|
birthday,
|
2023-12-23 15:14:49 +00:00
|
|
|
max(measuring_dates) + timedelta(days=1),
|
2023-12-19 17:36:07 +00:00
|
|
|
]
|
|
|
|
layout_args["yaxis"]["range"] = [0, max(measured_heights) * 1.5]
|
|
|
|
data.extend(
|
|
|
|
[
|
|
|
|
percentile_height_97_trace,
|
|
|
|
percentile_height_85_trace,
|
|
|
|
percentile_height_50_trace,
|
|
|
|
percentile_height_15_trace,
|
|
|
|
percentile_height_3_trace,
|
|
|
|
]
|
|
|
|
)
|
2021-12-29 07:53:36 +00:00
|
|
|
|
2023-12-19 17:36:07 +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)
|
2021-12-29 07:53:36 +00:00
|
|
|
return utils.split_graph_output(output)
|