Set explicit bounds on some graphs

Fixes #706
This commit is contained in:
Christopher C. Wells 2024-01-27 19:42:12 -08:00
parent 17635a71a1
commit c1e4876c20
18 changed files with 59 additions and 17 deletions

View File

@ -791,7 +791,7 @@ class TimerFormsTestCase(FormsTestCaseBase):
class ValidationsTestCase(FormsTestCaseBase): class ValidationsTestCase(FormsTestCaseBase):
def test_validate_date(self): def test_validate_date(self):
future = timezone.localtime() + timezone.timedelta(days=1) future = timezone.localtime() + timezone.timedelta(days=10)
params = { params = {
"child": self.child, "child": self.child,
"weight": "8.5", "weight": "8.5",

View File

@ -11,7 +11,7 @@ def bmi_change(objects):
""" """
Create a graph showing bmi over time. Create a graph showing bmi over time.
:param objects: a QuerySet of BMI instances. :param objects: a QuerySet of BMI instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
objects = objects.order_by("-date") objects = objects.order_by("-date")

View File

@ -12,7 +12,7 @@ def diaperchange_amounts(instances):
""" """
Create a graph showing daily diaper change amounts over time. Create a graph showing daily diaper change amounts over time.
:param instances: a QuerySet of DiaperChange instances. :param instances: a QuerySet of DiaperChange instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
totals = {} totals = {}
for instance in instances: for instance in instances:

View File

@ -16,7 +16,7 @@ def diaperchange_intervals(changes):
""" """
Create a graph showing intervals of diaper changes. Create a graph showing intervals of diaper changes.
:param changes: a QuerySet of Diaper Change instances. :param changes: a QuerySet of Diaper Change instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
changes = changes.order_by("time") changes = changes.order_by("time")
@ -65,6 +65,9 @@ def diaperchange_intervals(changes):
layout_args["barmode"] = "stack" layout_args["barmode"] = "stack"
layout_args["title"] = _("<b>Diaper Change Intervals</b>") layout_args["title"] = _("<b>Diaper Change Intervals</b>")
layout_args["xaxis"]["title"] = _("Date") layout_args["xaxis"]["title"] = _("Date")
layout_args["xaxis"]["type"] = "date"
layout_args["xaxis"]["autorange"] = True
layout_args["xaxis"]["autorangeoptions"] = utils.autorangeoptions(trace_total.x)
layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date() layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
layout_args["yaxis"]["title"] = _("Interval (hours)") layout_args["yaxis"]["title"] = _("Interval (hours)")

View File

@ -11,7 +11,7 @@ def diaperchange_lifetimes(changes):
""" """
Create a graph showing how long diapers last (time between changes). Create a graph showing how long diapers last (time between changes).
:param changes: a QuerySet of Diaper Change instances. :param changes: a QuerySet of Diaper Change instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
changes = changes.order_by("time") changes = changes.order_by("time")
durations = [] durations = []

View File

@ -14,7 +14,7 @@ def diaperchange_types(changes):
""" """
Create a graph showing types of totals for diaper changes. Create a graph showing types of totals for diaper changes.
:param changes: a QuerySet of Diaper Change instances. :param changes: a QuerySet of Diaper Change instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
changes = ( changes = (
changes.annotate(date=TruncDate("time")) changes.annotate(date=TruncDate("time"))
@ -47,6 +47,9 @@ def diaperchange_types(changes):
layout_args["barmode"] = "stack" layout_args["barmode"] = "stack"
layout_args["title"] = _("<b>Diaper Change Types</b>") layout_args["title"] = _("<b>Diaper Change Types</b>")
layout_args["xaxis"]["title"] = _("Date") layout_args["xaxis"]["title"] = _("Date")
layout_args["xaxis"]["type"] = "date"
layout_args["xaxis"]["autorange"] = True
layout_args["xaxis"]["autorangeoptions"] = utils.autorangeoptions(total_trace.x)
layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date() layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
layout_args["yaxis"]["title"] = _("Number of changes") layout_args["yaxis"]["title"] = _("Number of changes")

View File

@ -13,7 +13,7 @@ def feeding_amounts(instances):
""" """
Create a graph showing daily feeding amounts over time. Create a graph showing daily feeding amounts over time.
:param instances: a QuerySet of Feeding instances. :param instances: a QuerySet of Feeding instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
feeding_types, feeding_types_desc = map( feeding_types, feeding_types_desc = map(
list, zip(*models.Feeding._meta.get_field("type").choices) list, zip(*models.Feeding._meta.get_field("type").choices)

View File

@ -1,4 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import time
from django.db.models import Count, Sum from django.db.models import Count, Sum
from django.db.models.functions import TruncDate from django.db.models.functions import TruncDate
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
@ -20,7 +22,7 @@ def feeding_duration(instances):
was equal to seven. was equal to seven.
:param instances: a QuerySet of Feeding instances. :param instances: a QuerySet of Feeding instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
totals = ( totals = (
instances.annotate(date=TruncDate("start")) instances.annotate(date=TruncDate("start"))
@ -54,6 +56,9 @@ def feeding_duration(instances):
layout_args = utils.default_graph_layout_options() layout_args = utils.default_graph_layout_options()
layout_args["title"] = _("<b>Average Feeding Durations</b>") layout_args["title"] = _("<b>Average Feeding Durations</b>")
layout_args["xaxis"]["title"] = _("Date") layout_args["xaxis"]["title"] = _("Date")
layout_args["xaxis"]["type"] = "date"
layout_args["xaxis"]["autorange"] = True
layout_args["xaxis"]["autorangeoptions"] = utils.autorangeoptions(trace_avg.x)
layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date() layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
layout_args["yaxis"]["title"] = _("Average duration (minutes)") layout_args["yaxis"]["title"] = _("Average duration (minutes)")
layout_args["yaxis2"] = dict(layout_args["yaxis"]) layout_args["yaxis2"] = dict(layout_args["yaxis"])

View File

@ -16,7 +16,7 @@ def feeding_intervals(instances):
Create a graph showing intervals of feeding instances over time. Create a graph showing intervals of feeding instances over time.
:param instances: a QuerySet of Feeding instances. :param instances: a QuerySet of Feeding instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
totals = instances.annotate(count=Count("id")).order_by("start") totals = instances.annotate(count=Count("id")).order_by("start")
@ -40,6 +40,9 @@ def feeding_intervals(instances):
layout_args = utils.default_graph_layout_options() layout_args = utils.default_graph_layout_options()
layout_args["title"] = _("<b>Feeding intervals</b>") layout_args["title"] = _("<b>Feeding intervals</b>")
layout_args["xaxis"]["title"] = _("Date") layout_args["xaxis"]["title"] = _("Date")
layout_args["xaxis"]["type"] = "date"
layout_args["xaxis"]["autorange"] = True
layout_args["xaxis"]["autorangeoptions"] = utils.autorangeoptions(trace_avg.x)
layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date() layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
layout_args["yaxis"]["title"] = _("Feeding interval (hours)") layout_args["yaxis"]["title"] = _("Feeding interval (hours)")

View File

@ -11,7 +11,7 @@ def head_circumference_change(objects):
""" """
Create a graph showing head_circumference over time. Create a graph showing head_circumference over time.
:param objects: a QuerySet of Head Circumference instances. :param objects: a QuerySet of Head Circumference instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
objects = objects.order_by("-date") objects = objects.order_by("-date")

View File

@ -17,7 +17,7 @@ def height_change(
:param actual_heights: a QuerySet of Height instances. :param actual_heights: a QuerySet of Height instances.
:param percentile_heights: a QuerySet of Height Percentile instances. :param percentile_heights: a QuerySet of Height Percentile instances.
:param birthday: a datetime of the child's birthday :param birthday: a datetime of the child's birthday
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
actual_heights = actual_heights.order_by("-date") actual_heights = actual_heights.order_by("-date")

View File

@ -12,7 +12,7 @@ def pumping_amounts(objects):
""" """
Create a graph showing pumping amounts over time. Create a graph showing pumping amounts over time.
:param instances: a QuerySet of Pumping instances. :param instances: a QuerySet of Pumping instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
objects = objects.order_by("start") objects = objects.order_by("start")

View File

@ -21,7 +21,7 @@ def sleep_pattern(sleeps):
""" """
Create a graph showing blocked out periods of sleep during each day. Create a graph showing blocked out periods of sleep during each day.
:param sleeps: a QuerySet of Sleep instances. :param sleeps: a QuerySet of Sleep instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
last_end_time = None last_end_time = None
adjustment = None adjustment = None

View File

@ -14,7 +14,7 @@ def sleep_totals(instances):
""" """
Create a graph showing total time sleeping for each day. Create a graph showing total time sleeping for each day.
:param instances: a QuerySet of Sleep instances. :param instances: a QuerySet of Sleep instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
totals = {} totals = {}
for instance in instances: for instance in instances:
@ -57,6 +57,9 @@ def sleep_totals(instances):
layout_args["barmode"] = "stack" layout_args["barmode"] = "stack"
layout_args["title"] = _("<b>Sleep Totals</b>") layout_args["title"] = _("<b>Sleep Totals</b>")
layout_args["xaxis"]["title"] = _("Date") layout_args["xaxis"]["title"] = _("Date")
layout_args["xaxis"]["type"] = "date"
layout_args["xaxis"]["autorange"] = True
layout_args["xaxis"]["autorangeoptions"] = utils.autorangeoptions(trace.x)
layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date() layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
layout_args["yaxis"]["title"] = _("Hours of sleep") layout_args["yaxis"]["title"] = _("Hours of sleep")

View File

@ -25,6 +25,9 @@ def temperature_change(objects):
layout_args["barmode"] = "stack" layout_args["barmode"] = "stack"
layout_args["title"] = _("<b>Temperature</b>") layout_args["title"] = _("<b>Temperature</b>")
layout_args["xaxis"]["title"] = _("Time") layout_args["xaxis"]["title"] = _("Time")
layout_args["xaxis"]["type"] = "date"
layout_args["xaxis"]["autorange"] = True
layout_args["xaxis"]["autorangeoptions"] = utils.autorangeoptions(trace.x)
layout_args["xaxis"]["rangeselector"] = utils.rangeselector_time() layout_args["xaxis"]["rangeselector"] = utils.rangeselector_time()
layout_args["yaxis"]["title"] = _("Temperature") layout_args["yaxis"]["title"] = _("Temperature")

View File

@ -16,7 +16,7 @@ def tummytime_duration(instances):
Create a graph showing total duration of tummy time instances per day. Create a graph showing total duration of tummy time instances per day.
:param instances: a QuerySet of TummyTime instances. :param instances: a QuerySet of TummyTime instances.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
totals = ( totals = (
instances.annotate(date=TruncDate("start")) instances.annotate(date=TruncDate("start"))
@ -49,6 +49,11 @@ def tummytime_duration(instances):
layout_args = utils.default_graph_layout_options() layout_args = utils.default_graph_layout_options()
layout_args["title"] = _("<b>Total Tummy Time Durations</b>") layout_args["title"] = _("<b>Total Tummy Time Durations</b>")
layout_args["xaxis"]["title"] = _("Date") layout_args["xaxis"]["title"] = _("Date")
layout_args["xaxis"]["type"] = "date"
layout_args["xaxis"]["autorange"] = True
layout_args["xaxis"]["autorangeoptions"] = utils.autorangeoptions(
trace_avg.x, 35000000
)
layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date() layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
layout_args["yaxis"]["title"] = _("Total duration (minutes)") layout_args["yaxis"]["title"] = _("Total duration (minutes)")
layout_args["yaxis2"] = dict(layout_args["yaxis"]) layout_args["yaxis2"] = dict(layout_args["yaxis"])

View File

@ -17,7 +17,7 @@ def weight_change(
:param actual_weights: a QuerySet of Weight instances. :param actual_weights: a QuerySet of Weight instances.
:param percentile_weights: a QuerySet of Weight Percentile instances. :param percentile_weights: a QuerySet of Weight Percentile instances.
:param birthday: a datetime of the child's birthday :param birthday: a datetime of the child's birthday
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
actual_weights = actual_weights.order_by("-date") actual_weights = actual_weights.order_by("-date")

View File

@ -1,4 +1,21 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import time
def autorangeoptions(dates, padding=10000000):
"""
Default autorange mix and max for all graphs.
See: https://github.com/babybuddy/babybuddy/issues/706
:param dates: list of datetime.date objects organized latest to oldest.
:param padding: additional padding to add to the bounds.
:return: a dict of our autorange options.
"""
return dict(
{
"minallowed": int(time.mktime(dates[-1].timetuple())) * 1000 - padding,
"maxallowed": int(time.mktime(dates[0].timetuple())) * 1000 + padding,
},
)
def default_graph_layout_options(): def default_graph_layout_options():
@ -73,7 +90,7 @@ def split_graph_output(output):
""" """
Split out of a Plotly graph in to html and javascript. Split out of a Plotly graph in to html and javascript.
:param output: a string of html and javascript comprising the graph. :param output: a string of html and javascript comprising the graph.
:returns: a tuple of the the graph's html and javascript. :returns: a tuple of the graph's html and javascript.
""" """
html, js = output.split("<script") html, js = output.split("<script")
js = "<script" + js js = "<script" + js