diff --git a/core/tests/tests_forms.py b/core/tests/tests_forms.py index 89346cdc..4f8fe53f 100644 --- a/core/tests/tests_forms.py +++ b/core/tests/tests_forms.py @@ -791,7 +791,7 @@ class TimerFormsTestCase(FormsTestCaseBase): class ValidationsTestCase(FormsTestCaseBase): def test_validate_date(self): - future = timezone.localtime() + timezone.timedelta(days=1) + future = timezone.localtime() + timezone.timedelta(days=10) params = { "child": self.child, "weight": "8.5", diff --git a/reports/graphs/bmi_change.py b/reports/graphs/bmi_change.py index 2dd8b471..caaee61e 100644 --- a/reports/graphs/bmi_change.py +++ b/reports/graphs/bmi_change.py @@ -11,7 +11,7 @@ def bmi_change(objects): """ Create a graph showing bmi over time. :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") diff --git a/reports/graphs/diaperchange_amounts.py b/reports/graphs/diaperchange_amounts.py index 196c4986..dd36248c 100644 --- a/reports/graphs/diaperchange_amounts.py +++ b/reports/graphs/diaperchange_amounts.py @@ -12,7 +12,7 @@ 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. + :returns: a tuple of the graph's html and javascript. """ totals = {} for instance in instances: diff --git a/reports/graphs/diaperchange_intervals.py b/reports/graphs/diaperchange_intervals.py index d8cca9b3..2973408a 100644 --- a/reports/graphs/diaperchange_intervals.py +++ b/reports/graphs/diaperchange_intervals.py @@ -16,7 +16,7 @@ def diaperchange_intervals(changes): """ Create a graph showing intervals of diaper changes. :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") @@ -65,6 +65,9 @@ def diaperchange_intervals(changes): layout_args["barmode"] = "stack" layout_args["title"] = _("Diaper Change Intervals") 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["yaxis"]["title"] = _("Interval (hours)") diff --git a/reports/graphs/diaperchange_lifetimes.py b/reports/graphs/diaperchange_lifetimes.py index 33070bae..bc31d81b 100644 --- a/reports/graphs/diaperchange_lifetimes.py +++ b/reports/graphs/diaperchange_lifetimes.py @@ -11,7 +11,7 @@ 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. + :returns: a tuple of the graph's html and javascript. """ changes = changes.order_by("time") durations = [] diff --git a/reports/graphs/diaperchange_types.py b/reports/graphs/diaperchange_types.py index 88000195..9a1ac528 100644 --- a/reports/graphs/diaperchange_types.py +++ b/reports/graphs/diaperchange_types.py @@ -14,7 +14,7 @@ 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. + :returns: a tuple of the graph's html and javascript. """ changes = ( changes.annotate(date=TruncDate("time")) @@ -47,6 +47,9 @@ def diaperchange_types(changes): layout_args["barmode"] = "stack" layout_args["title"] = _("Diaper Change Types") 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["yaxis"]["title"] = _("Number of changes") diff --git a/reports/graphs/feeding_amounts.py b/reports/graphs/feeding_amounts.py index 68a76443..8592ed8a 100644 --- a/reports/graphs/feeding_amounts.py +++ b/reports/graphs/feeding_amounts.py @@ -13,7 +13,7 @@ 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. + :returns: a tuple of the graph's html and javascript. """ feeding_types, feeding_types_desc = map( list, zip(*models.Feeding._meta.get_field("type").choices) diff --git a/reports/graphs/feeding_duration.py b/reports/graphs/feeding_duration.py index ad02ab03..89768e5f 100644 --- a/reports/graphs/feeding_duration.py +++ b/reports/graphs/feeding_duration.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +import time + from django.db.models import Count, Sum from django.db.models.functions import TruncDate from django.utils.translation import gettext as _ @@ -20,7 +22,7 @@ def feeding_duration(instances): was equal to seven. :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(date=TruncDate("start")) @@ -54,6 +56,9 @@ def feeding_duration(instances): layout_args = utils.default_graph_layout_options() layout_args["title"] = _("Average Feeding Durations") 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["yaxis"]["title"] = _("Average duration (minutes)") layout_args["yaxis2"] = dict(layout_args["yaxis"]) diff --git a/reports/graphs/feeding_intervals.py b/reports/graphs/feeding_intervals.py index 35db96a9..f75bce51 100644 --- a/reports/graphs/feeding_intervals.py +++ b/reports/graphs/feeding_intervals.py @@ -16,7 +16,7 @@ def feeding_intervals(instances): Create a graph showing intervals of feeding instances over time. :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") @@ -40,6 +40,9 @@ def feeding_intervals(instances): layout_args = utils.default_graph_layout_options() layout_args["title"] = _("Feeding intervals") 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["yaxis"]["title"] = _("Feeding interval (hours)") diff --git a/reports/graphs/head_circumference_change.py b/reports/graphs/head_circumference_change.py index 9d8d3afd..fce43a6f 100644 --- a/reports/graphs/head_circumference_change.py +++ b/reports/graphs/head_circumference_change.py @@ -11,7 +11,7 @@ def head_circumference_change(objects): """ Create a graph showing head_circumference over time. :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") diff --git a/reports/graphs/height_change.py b/reports/graphs/height_change.py index aa662f9d..6c0ef21d 100644 --- a/reports/graphs/height_change.py +++ b/reports/graphs/height_change.py @@ -17,7 +17,7 @@ def height_change( :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 - :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") diff --git a/reports/graphs/pumping_amounts.py b/reports/graphs/pumping_amounts.py index 85b121a9..a9dc2558 100644 --- a/reports/graphs/pumping_amounts.py +++ b/reports/graphs/pumping_amounts.py @@ -12,7 +12,7 @@ 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. + :returns: a tuple of the graph's html and javascript. """ objects = objects.order_by("start") diff --git a/reports/graphs/sleep_pattern.py b/reports/graphs/sleep_pattern.py index bf9eddab..95ffeb8f 100644 --- a/reports/graphs/sleep_pattern.py +++ b/reports/graphs/sleep_pattern.py @@ -21,7 +21,7 @@ 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. + :returns: a tuple of the graph's html and javascript. """ last_end_time = None adjustment = None diff --git a/reports/graphs/sleep_totals.py b/reports/graphs/sleep_totals.py index f8bb1033..3b6f5ed5 100644 --- a/reports/graphs/sleep_totals.py +++ b/reports/graphs/sleep_totals.py @@ -14,7 +14,7 @@ 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. + :returns: a tuple of the graph's html and javascript. """ totals = {} for instance in instances: @@ -57,6 +57,9 @@ def sleep_totals(instances): layout_args["barmode"] = "stack" layout_args["title"] = _("Sleep Totals") 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["yaxis"]["title"] = _("Hours of sleep") diff --git a/reports/graphs/temperature_change.py b/reports/graphs/temperature_change.py index 2a696772..f46b8ccb 100644 --- a/reports/graphs/temperature_change.py +++ b/reports/graphs/temperature_change.py @@ -25,6 +25,9 @@ def temperature_change(objects): layout_args["barmode"] = "stack" layout_args["title"] = _("Temperature") 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["yaxis"]["title"] = _("Temperature") diff --git a/reports/graphs/tummytime_duration.py b/reports/graphs/tummytime_duration.py index 06557a55..e1169c11 100644 --- a/reports/graphs/tummytime_duration.py +++ b/reports/graphs/tummytime_duration.py @@ -16,7 +16,7 @@ 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. + :returns: a tuple of the graph's html and javascript. """ totals = ( instances.annotate(date=TruncDate("start")) @@ -49,6 +49,11 @@ def tummytime_duration(instances): layout_args = utils.default_graph_layout_options() layout_args["title"] = _("Total Tummy Time Durations") 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["yaxis"]["title"] = _("Total duration (minutes)") layout_args["yaxis2"] = dict(layout_args["yaxis"]) diff --git a/reports/graphs/weight_change.py b/reports/graphs/weight_change.py index 36da9426..4d95ca24 100644 --- a/reports/graphs/weight_change.py +++ b/reports/graphs/weight_change.py @@ -17,7 +17,7 @@ def weight_change( :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 the graph's html and javascript. + :returns: a tuple of the graph's html and javascript. """ actual_weights = actual_weights.order_by("-date") diff --git a/reports/utils.py b/reports/utils.py index 2845c0cf..8b266525 100644 --- a/reports/utils.py +++ b/reports/utils.py @@ -1,4 +1,21 @@ # -*- 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(): @@ -73,7 +90,7 @@ def split_graph_output(output): """ Split out of a Plotly graph in to html and javascript. :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("