513: Make nap user toggleable (#641)

* Add `nap` field, remove `napping` field.

* Default `nap` property to NULL

* Update existing tests

* Format the code!!

* Allow `Sleep.nap` to be blank

* Migrate settings from `NAP_START_MIN` AND `NAP_START_MAX`

* Remove `active` class from boolean fields

* Correct test method name and user config

* Add forms test

* Use settings for Sleep nap model tests
This commit is contained in:
Christopher Charbonneau Wells 2023-05-07 15:10:50 -07:00 committed by GitHub
parent a669decc88
commit 31a0aa4741
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 256 additions and 110 deletions

View File

@ -527,15 +527,15 @@ class SleepAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
def test_get(self): def test_get(self):
response = self.client.get(self.endpoint) response = self.client.get(self.endpoint)
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertDictEqual(
response.data["results"][0], response.data["results"][0],
{ {
"id": 4, "id": 4,
"child": 1, "child": 1,
"start": "2017-11-18T19:00:00-05:00", "start": "2017-11-19T03:00:00-05:00",
"end": "2017-11-18T23:00:00-05:00", "end": "2017-11-19T04:30:00-05:00",
"duration": "04:00:00", "duration": "01:30:00",
"nap": False, "nap": True,
"notes": "lots of squirming", "notes": "lots of squirming",
"tags": [], "tags": [],
}, },
@ -558,7 +558,7 @@ class SleepAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
endpoint = "{}{}/".format(self.endpoint, 4) endpoint = "{}{}/".format(self.endpoint, 4)
response = self.client.get(endpoint) response = self.client.get(endpoint)
entry = response.data entry = response.data
entry["end"] = "2017-11-18T23:30:00-05:00" entry["end"] = "2017-11-19T08:30:00-05:00"
response = self.client.patch( response = self.client.patch(
endpoint, endpoint,
{ {

View File

@ -1,10 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import datetime
import os import os
from django.apps import AppConfig from django.apps import AppConfig
from django.conf import settings from django.conf import settings
from django.db.models.signals import post_migrate from django.db.models.signals import post_migrate
from dbsettings.loading import set_setting_value, setting_in_db
from babybuddy import VERSION from babybuddy import VERSION
@ -14,6 +17,39 @@ def create_read_only_group(sender, **kwargs):
Group.objects.get_or_create(name=settings.BABY_BUDDY["READ_ONLY_GROUP_NAME"]) Group.objects.get_or_create(name=settings.BABY_BUDDY["READ_ONLY_GROUP_NAME"])
def set_default_site_settings(sender, **kwargs):
"""
Sets default values for site-wide settings.
Based on `dbsettings.utils.set_defaults` which no longer seem to work in
the latest versions of Django.
"""
from core import models
# Removed `NAP_START_MIN` and `NAP_START_MAX` values are referenced here
# for pre-2.0.0 migrations.
try:
nap_start_min = datetime.datetime.strptime(
os.environ.get("NAP_START_MIN"), "%H:%M"
).time()
except (TypeError, ValueError):
nap_start_min = models.Sleep.settings.nap_start_min
try:
nap_start_max = datetime.datetime.strptime(
os.environ.get("NAP_START_MAX"), "%H:%M"
).time()
except (TypeError, ValueError):
nap_start_max = models.Sleep.settings.nap_start_max
defaults = (
("Sleep", "nap_start_min", nap_start_min),
("Sleep", "nap_start_max", nap_start_max),
)
for class_name, attribute_name, value in defaults:
if not setting_in_db("core.models", class_name, attribute_name):
set_setting_value("core.models", class_name, attribute_name, value)
class BabyBuddyConfig(AppConfig): class BabyBuddyConfig(AppConfig):
name = "babybuddy" name = "babybuddy"
verbose_name = "Baby Buddy" verbose_name = "Baby Buddy"
@ -25,3 +61,4 @@ class BabyBuddyConfig(AppConfig):
commit = open(".git/refs/heads/master").read() commit = open(".git/refs/heads/master").read()
self.version_string += " ({})".format(commit[0:7]) self.version_string += " ({})".format(commit[0:7])
post_migrate.connect(create_read_only_group, sender=self) post_migrate.connect(create_read_only_group, sender=self)
post_migrate.connect(set_default_site_settings, sender=self)

View File

@ -332,10 +332,10 @@
"fields": "fields":
{ {
"child": 1, "child": 1,
"start": "2017-11-18T04:30:00Z", "start": "2017-11-17T20:30:00Z",
"end": "2017-11-18T05:30:00Z", "end": "2017-11-18T05:30:00Z",
"duration": "01:00:00", "duration": "09:00:00",
"napping": false "nap": false
} }
}, },
{ {
@ -347,7 +347,7 @@
"start": "2017-11-18T15:00:00Z", "start": "2017-11-18T15:00:00Z",
"end": "2017-11-18T17:00:00Z", "end": "2017-11-18T17:00:00Z",
"duration": "02:00:00", "duration": "02:00:00",
"napping": true "nap": true
} }
}, },
{ {
@ -357,9 +357,9 @@
{ {
"child": 1, "child": 1,
"start": "2017-11-18T19:00:00Z", "start": "2017-11-18T19:00:00Z",
"end": "2017-11-18T19:30:00Z", "end": "2017-11-19T04:30:00Z",
"duration": "00:30:00", "duration": "09:30:00",
"napping": false "nap": false
} }
}, },
{ {
@ -368,11 +368,11 @@
"fields": "fields":
{ {
"child": 1, "child": 1,
"start": "2017-11-19T00:00:00Z", "start": "2017-11-19T08:00:00Z",
"end": "2017-11-19T04:00:00Z", "end": "2017-11-19T09:30:00Z",
"duration": "04:00:00", "duration": "01:30:00",
"notes": "lots of squirming", "notes": "lots of squirming",
"napping": false "nap": true
} }
}, },
{ {

View File

@ -8,6 +8,8 @@ from django.core.management import call_command
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.core.management.commands.flush import Command as Flush from django.core.management.commands.flush import Command as Flush
from dbsettings.models import Setting
from .fake import Command as Fake from .fake import Command as Fake
from .migrate import Command as Migrate from .migrate import Command as Migrate
@ -38,6 +40,9 @@ class Command(BaseCommand):
if verbosity > 0: if verbosity > 0:
self.stdout.write(self.style.SUCCESS("Database flushed.")) self.stdout.write(self.style.SUCCESS("Database flushed."))
# Remove all site-wide settings.
Setting.objects.all().delete()
# Run migrations for all Baby Buddy apps. # Run migrations for all Baby Buddy apps.
for config in apps.app_configs.values(): for config in apps.app_configs.values():
if path.split(path.split(config.path)[0])[1] == "babybuddy": if path.split(path.split(config.path)[0])[1] == "babybuddy":

View File

@ -19,3 +19,8 @@ EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
# See https://django-axes.readthedocs.io/en/latest/4_configuration.html # See https://django-axes.readthedocs.io/en/latest/4_configuration.html
AXES_ENABLED = False AXES_ENABLED = False
# DBSettings configuration
# See https://github.com/zlorf/django-dbsettings#a-note-about-caching
DBSETTINGS_USE_CACHE = False

View File

@ -11,7 +11,7 @@
{% else %} {% else %}
{{ field|add_class:"btn-check" }} {{ field|add_class:"btn-check" }}
{% endif %} {% endif %}
<label for="id_{{ field.name }}" class="btn btn-outline-light btn-no-hover{% if field.value %} active{% endif %}"> <label for="id_{{ field.name }}" class="btn btn-outline-light btn-no-hover">
{{ field.label }} {{ field.label }}
</label> </label>
{% elif 'choice' in field|field_type %} {% elif 'choice' in field|field_type %}

View File

@ -27,7 +27,7 @@ class SiteSettingsTestCase(TestCase):
is_superuser=True, is_staff=True, **cls.credentials is_superuser=True, is_staff=True, **cls.credentials
) )
def test_default_settings(self): def test_settings_default(self):
self.c.login(**self.credentials) self.c.login(**self.credentials)
page = self.c.get("/settings/") page = self.c.get("/settings/")
self.assertEqual(page.status_code, 200) self.assertEqual(page.status_code, 200)
@ -40,9 +40,8 @@ class SiteSettingsTestCase(TestCase):
"06:00:00", "06:00:00",
) )
def test_nap_start_settings(self): def test_settings_nap_start(self):
self.c.login(**self.credentials) self.c.login(**self.credentials)
self.assert_naps()
params = { params = {
"core.models__Sleep__nap_start_max": "20:00:00", "core.models__Sleep__nap_start_max": "20:00:00",
"core.models__Sleep__nap_start_min": "09:00:00", "core.models__Sleep__nap_start_min": "09:00:00",
@ -57,17 +56,3 @@ class SiteSettingsTestCase(TestCase):
Sleep.settings.nap_start_min.strftime("%H:%M:%S"), Sleep.settings.nap_start_min.strftime("%H:%M:%S"),
params["core.models__Sleep__nap_start_min"], params["core.models__Sleep__nap_start_min"],
) )
self.assert_naps()
def assert_naps(self):
"""
Asserts sleep instances filtered with nap start min and max match nap instances.
"""
instances = Sleep.objects.filter(
start__time__range=(
Sleep.settings.nap_start_min.strftime("%H:%M:%S"),
Sleep.settings.nap_start_max.strftime("%H:%M:%S"),
)
)
naps = Sleep.naps.all()
self.assertQuerySetEqual(instances, naps)

View File

@ -21,9 +21,9 @@ class TemplateTagsTestCase(TestCase):
) )
self.assertEqual(babybuddy.get_child_count(), 2) self.assertEqual(babybuddy.get_child_count(), 2)
def user_is_read_only(self): def test_user_is_read_only(self):
user = get_user_model().objects.create_user( user = get_user_model().objects.create_user(
username="readonly", password="readonly", is_superuser=False, is_staf=False username="readonly", password="readonly", is_superuser=False, is_staff=False
) )
self.assertFalse(babybuddy.user_is_read_only(user)) self.assertFalse(babybuddy.user_is_read_only(user))

View File

@ -62,7 +62,20 @@ def set_initial_values(kwargs, form_type):
last_feed_args["method"] = last_method last_feed_args["method"] = last_method
kwargs["initial"].update(last_feed_args) kwargs["initial"].update(last_feed_args)
# Remove custom kwargs so they do not interfere with `super` calls. # Set default "nap" value for Sleep instances.
if form_type == SleepForm and "nap" not in kwargs["initial"]:
try:
start = timezone.localtime(kwargs["initial"]["start"]).time()
except KeyError:
start = timezone.localtime().time()
nap = (
models.Sleep.settings.nap_start_min
<= start
<= models.Sleep.settings.nap_start_max
)
kwargs["initial"].update({"nap": nap})
# Remove custom kwargs, so they do not interfere with `super` calls.
for key in ["child", "timer"]: for key in ["child", "timer"]:
try: try:
kwargs.pop(key) kwargs.pop(key)
@ -181,7 +194,7 @@ class NoteForm(CoreModelForm, TaggableModelForm):
class SleepForm(CoreModelForm, TaggableModelForm): class SleepForm(CoreModelForm, TaggableModelForm):
class Meta: class Meta:
model = models.Sleep model = models.Sleep
fields = ["child", "start", "end", "notes", "tags"] fields = ["child", "start", "end", "nap", "notes", "tags"]
widgets = { widgets = {
"child": ChildRadioSelect, "child": ChildRadioSelect,
"start": DateTimeInput(), "start": DateTimeInput(),

View File

@ -20,7 +20,8 @@ class Migration(migrations.Migration):
name="napping", name="napping",
field=models.BooleanField(null=True, verbose_name="Napping"), field=models.BooleanField(null=True, verbose_name="Napping"),
), ),
migrations.RunPython(set_napping, reverse_code=migrations.RunPython.noop), # Migration superseded by 0028_alter_sleep_options_remove_sleep_napping_sleep_nap.py
# migrations.RunPython(set_napping, reverse_code=migrations.RunPython.noop),
migrations.AlterField( migrations.AlterField(
model_name="sleep", model_name="sleep",
name="napping", name="napping",

View File

@ -0,0 +1,53 @@
# Generated by Django 4.2 on 2023-05-07 00:28
from django.db import migrations, models
from django.utils import timezone
def set_sleep_nap_values(apps, schema_editor):
# The model must be imported to ensure its overridden `save` method is run.
from core.models import Sleep
for sleep in Sleep.objects.all():
sleep.nap = (
Sleep.settings.nap_start_min
<= timezone.localtime(sleep.start).time()
<= Sleep.settings.nap_start_max
)
sleep.save()
class Migration(migrations.Migration):
dependencies = [
("core", "0027_alter_timer_options_remove_timer_duration_and_more"),
]
operations = [
migrations.AlterModelOptions(
name="sleep",
options={
"default_permissions": ("view", "add", "change", "delete"),
"ordering": ["-start"],
"permissions": [("can_edit_sleep_settings", "Can edit Sleep settings")],
"verbose_name": "Sleep",
"verbose_name_plural": "Sleep",
},
),
migrations.RemoveField(
model_name="sleep",
name="napping",
),
migrations.AddField(
model_name="sleep",
name="nap",
field=models.BooleanField(null=True, verbose_name="Nap"),
),
migrations.RunPython(
set_sleep_nap_values, reverse_code=migrations.RunPython.noop
),
migrations.AlterField(
model_name="sleep",
name="nap",
field=models.BooleanField(blank=True, verbose_name="Nap"),
),
]

View File

@ -418,12 +418,6 @@ class Note(models.Model):
return str(_("Note")) return str(_("Note"))
class NapsManager(models.Manager):
def get_queryset(self):
qs = super(NapsManager, self).get_queryset()
return qs.filter(id__in=[obj.id for obj in qs if obj.nap])
class Pumping(models.Model): class Pumping(models.Model):
model_name = "pumping" model_name = "pumping"
child = models.ForeignKey( child = models.ForeignKey(
@ -459,7 +453,6 @@ class Sleep(models.Model):
child = models.ForeignKey( child = models.ForeignKey(
"Child", on_delete=models.CASCADE, related_name="sleep", verbose_name=_("Child") "Child", on_delete=models.CASCADE, related_name="sleep", verbose_name=_("Child")
) )
napping = models.BooleanField(editable=False, null=True, verbose_name=_("Napping"))
start = models.DateTimeField( start = models.DateTimeField(
blank=False, blank=False,
default=timezone.localtime, default=timezone.localtime,
@ -469,6 +462,7 @@ class Sleep(models.Model):
end = models.DateTimeField( end = models.DateTimeField(
blank=False, default=timezone.localtime, null=False, verbose_name=_("End time") blank=False, default=timezone.localtime, null=False, verbose_name=_("End time")
) )
nap = models.BooleanField(null=False, blank=True, verbose_name=_("Nap"))
duration = models.DurationField( duration = models.DurationField(
editable=False, null=True, verbose_name=_("Duration") editable=False, null=True, verbose_name=_("Duration")
) )
@ -476,7 +470,6 @@ class Sleep(models.Model):
tags = TaggableManager(blank=True, through=Tagged) tags = TaggableManager(blank=True, through=Tagged)
objects = models.Manager() objects = models.Manager()
naps = NapsManager()
settings = NapSettings(_("Nap settings")) settings = NapSettings(_("Nap settings"))
class Meta: class Meta:
@ -488,19 +481,15 @@ class Sleep(models.Model):
def __str__(self): def __str__(self):
return str(_("Sleep")) return str(_("Sleep"))
@property
def nap(self):
local_start_time = timezone.localtime(self.start).time()
return (
Sleep.settings.nap_start_min
<= local_start_time
<= Sleep.settings.nap_start_max
)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if self.nap is None:
self.nap = (
Sleep.settings.nap_start_min
<= timezone.localtime(self.start).time()
<= Sleep.settings.nap_start_max
)
if self.start and self.end: if self.start and self.end:
self.duration = self.end - self.start self.duration = self.end - self.start
self.napping = self.nap
super(Sleep, self).save(*args, **kwargs) super(Sleep, self).save(*args, **kwargs)
def clean(self): def clean(self):

View File

@ -1,40 +1,40 @@
child_id,start,end,notes child_id,start,nap,end,notes
1,2020-02-17 00:14:09,2020-02-17 02:48:09, 1,2020-02-17 00:14:09,0,2020-02-17 02:48:09,
1,2020-02-16 19:37:16,2020-02-16 22:43:16, 1,2020-02-16 19:37:16,0,2020-02-16 22:43:16,
1,2020-02-16 17:19:16,2020-02-16 19:00:16, 1,2020-02-16 17:19:16,1,2020-02-16 19:00:16,
1,2020-02-16 14:42:52,2020-02-16 16:35:52, 1,2020-02-16 14:42:52,1,2020-02-16 16:35:52,
1,2020-02-16 11:28:41,2020-02-16 13:21:41, 1,2020-02-16 11:28:41,1,2020-02-16 13:21:41,
1,2020-02-16 07:38:01,2020-02-16 08:55:01, 1,2020-02-16 07:38:01,1,2020-02-16 08:55:01,
1,2020-02-16 03:12:01,2020-02-16 06:17:01, 1,2020-02-16 03:12:01,0,2020-02-16 06:17:01,
1,2020-02-15 22:53:01,2020-02-16 01:21:01,Experience charge from woman fight. Court direction watch particular. Society price air difference chance consider find. Include word office story official statement life. Street kid develop enough need necessary. 1,2020-02-15 22:53:01,0,2020-02-16 01:21:01,Experience charge from woman fight. Court direction watch particular. Society price air difference chance consider find. Include word office story official statement life. Street kid develop enough need necessary.
1,2020-02-15 17:37:21,2020-02-15 19:04:21, 1,2020-02-15 17:37:21,1,2020-02-15 19:04:21,
1,2020-02-15 14:32:21,2020-02-15 16:02:21, 1,2020-02-15 14:32:21,1,2020-02-15 16:02:21,
1,2020-02-15 11:38:21,2020-02-15 13:10:21, 1,2020-02-15 11:38:21,1,2020-02-15 13:10:21,
1,2020-02-15 07:51:45,2020-02-15 08:25:45, 1,2020-02-15 07:51:45,1,2020-02-15 08:25:45,
1,2020-02-15 03:06:24,2020-02-15 05:41:24, 1,2020-02-15 03:06:24,0,2020-02-15 05:41:24,
1,2020-02-14 21:58:58,2020-02-15 01:02:58, 1,2020-02-14 21:58:58,0,2020-02-15 01:02:58,
1,2020-02-14 18:41:28,2020-02-14 19:23:28,Chance rate see consider still according. Technology series key recognize. Rise avoid growth weight. 1,2020-02-14 18:41:28,1,2020-02-14 19:23:28,Chance rate see consider still according. Technology series key recognize. Rise avoid growth weight.
1,2020-02-14 15:29:28,2020-02-14 16:57:28,Million tough many debate price. Tend south my home training free actually same. Mr imagine international. 1,2020-02-14 15:29:28,1,2020-02-14 16:57:28,Million tough many debate price. Tend south my home training free actually same. Mr imagine international.
1,2020-02-14 12:12:28,2020-02-14 13:51:28, 1,2020-02-14 12:12:28,1,2020-02-14 13:51:28,
1,2020-02-14 07:41:40,2020-02-14 09:30:40,Mrs politics risk million education reach spring. 1,2020-02-14 07:41:40,1,2020-02-14 09:30:40,Mrs politics risk million education reach spring.
1,2020-02-14 02:18:07,2020-02-14 05:29:07, 1,2020-02-14 02:18:07,0,2020-02-14 05:29:07,
1,2020-02-13 21:06:46,2020-02-13 23:40:46, 1,2020-02-13 21:06:46,0,2020-02-13 23:40:46,
1,2020-02-13 18:05:46,2020-02-13 19:40:46, 1,2020-02-13 18:05:46,0,2020-02-13 19:40:46,
1,2020-02-13 14:15:54,2020-02-13 15:40:54, 1,2020-02-13 14:15:54,1,2020-02-13 15:40:54,
1,2020-02-13 09:39:03,2020-02-13 11:38:03, 1,2020-02-13 09:39:03,1,2020-02-13 11:38:03,
1,2020-02-13 04:00:42,2020-02-13 08:03:42, 1,2020-02-13 04:00:42,0,2020-02-13 08:03:42,
1,2020-02-12 22:15:50,2020-02-13 01:46:50, 1,2020-02-12 22:15:50,0,2020-02-13 01:46:50,
1,2020-02-12 18:17:05,2020-02-12 19:35:05, 1,2020-02-12 18:17:05,1,2020-02-12 19:35:05,
1,2020-02-12 15:23:05,2020-02-12 16:43:05, 1,2020-02-12 15:23:05,1,2020-02-12 16:43:05,
1,2020-02-12 05:54:01,2020-02-12 11:48:01,Edge fact officer any. 1,2020-02-12 05:54:01,0,2020-02-12 11:48:01,Edge fact officer any.
1,2020-02-11 21:00:14,2020-02-12 01:58:14, 1,2020-02-11 21:00:14,0,2020-02-12 01:58:14,
1,2020-02-11 17:33:04,2020-02-11 19:21:04, 1,2020-02-11 17:33:04,1,2020-02-11 19:21:04,
1,2020-02-11 14:51:04,2020-02-11 15:46:04, 1,2020-02-11 14:51:04,1,2020-02-11 15:46:04,
1,2020-02-11 12:49:10,2020-02-11 13:20:10, 1,2020-02-11 12:49:10,1,2020-02-11 13:20:10,
1,2020-02-11 08:30:34,2020-02-11 09:17:34, 1,2020-02-11 08:30:34,1,2020-02-11 09:17:34,
1,2020-02-11 06:35:34,2020-02-11 07:10:34, 1,2020-02-11 06:35:34,0,2020-02-11 07:10:34,
1,2020-02-10 23:56:34,2020-02-11 05:00:34,Image last next important. Style oil season talk family television. 1,2020-02-10 23:56:34,0,2020-02-11 05:00:34,Image last next important. Style oil season talk family television.
1,2020-02-10 18:39:49,2020-02-10 20:39:49, 1,2020-02-10 18:39:49,1,2020-02-10 20:39:49,
1,2020-02-10 14:44:33,2020-02-10 16:31:33, 1,2020-02-10 14:44:33,1,2020-02-10 16:31:33,
1,2020-02-10 10:25:06,2020-02-10 11:37:06,Other area cover military. Personal art decade guy. Traditional majority time term on life north. Leg drop most message within believe. 1,2020-02-10 10:25:06,1,2020-02-10 11:37:06,Other area cover military. Personal art decade guy. Traditional majority time term on life north. Leg drop most message within believe.
1,2020-02-10 05:36:55,2020-02-10 07:48:55, 1,2020-02-10 05:36:55,0,2020-02-10 07:48:55,

1 child_id start nap end notes
2 1 2020-02-17 00:14:09 0 2020-02-17 02:48:09
3 1 2020-02-16 19:37:16 0 2020-02-16 22:43:16
4 1 2020-02-16 17:19:16 1 2020-02-16 19:00:16
5 1 2020-02-16 14:42:52 1 2020-02-16 16:35:52
6 1 2020-02-16 11:28:41 1 2020-02-16 13:21:41
7 1 2020-02-16 07:38:01 1 2020-02-16 08:55:01
8 1 2020-02-16 03:12:01 0 2020-02-16 06:17:01
9 1 2020-02-15 22:53:01 0 2020-02-16 01:21:01 Experience charge from woman fight. Court direction watch particular. Society price air difference chance consider find. Include word office story official statement life. Street kid develop enough need necessary.
10 1 2020-02-15 17:37:21 1 2020-02-15 19:04:21
11 1 2020-02-15 14:32:21 1 2020-02-15 16:02:21
12 1 2020-02-15 11:38:21 1 2020-02-15 13:10:21
13 1 2020-02-15 07:51:45 1 2020-02-15 08:25:45
14 1 2020-02-15 03:06:24 0 2020-02-15 05:41:24
15 1 2020-02-14 21:58:58 0 2020-02-15 01:02:58
16 1 2020-02-14 18:41:28 1 2020-02-14 19:23:28 Chance rate see consider still according. Technology series key recognize. Rise avoid growth weight.
17 1 2020-02-14 15:29:28 1 2020-02-14 16:57:28 Million tough many debate price. Tend south my home training free actually same. Mr imagine international.
18 1 2020-02-14 12:12:28 1 2020-02-14 13:51:28
19 1 2020-02-14 07:41:40 1 2020-02-14 09:30:40 Mrs politics risk million education reach spring.
20 1 2020-02-14 02:18:07 0 2020-02-14 05:29:07
21 1 2020-02-13 21:06:46 0 2020-02-13 23:40:46
22 1 2020-02-13 18:05:46 0 2020-02-13 19:40:46
23 1 2020-02-13 14:15:54 1 2020-02-13 15:40:54
24 1 2020-02-13 09:39:03 1 2020-02-13 11:38:03
25 1 2020-02-13 04:00:42 0 2020-02-13 08:03:42
26 1 2020-02-12 22:15:50 0 2020-02-13 01:46:50
27 1 2020-02-12 18:17:05 1 2020-02-12 19:35:05
28 1 2020-02-12 15:23:05 1 2020-02-12 16:43:05
29 1 2020-02-12 05:54:01 0 2020-02-12 11:48:01 Edge fact officer any.
30 1 2020-02-11 21:00:14 0 2020-02-12 01:58:14
31 1 2020-02-11 17:33:04 1 2020-02-11 19:21:04
32 1 2020-02-11 14:51:04 1 2020-02-11 15:46:04
33 1 2020-02-11 12:49:10 1 2020-02-11 13:20:10
34 1 2020-02-11 08:30:34 1 2020-02-11 09:17:34
35 1 2020-02-11 06:35:34 0 2020-02-11 07:10:34
36 1 2020-02-10 23:56:34 0 2020-02-11 05:00:34 Image last next important. Style oil season talk family television.
37 1 2020-02-10 18:39:49 1 2020-02-10 20:39:49
38 1 2020-02-10 14:44:33 1 2020-02-10 16:31:33
39 1 2020-02-10 10:25:06 1 2020-02-10 11:37:06 Other area cover military. Personal art decade guy. Traditional majority time term on life north. Leg drop most message within believe.
40 1 2020-02-10 05:36:55 0 2020-02-10 07:48:55

View File

@ -545,6 +545,26 @@ class SleepFormsTestCase(FormsTestCaseBase):
self.assertEqual(page.status_code, 200) self.assertEqual(page.status_code, 200)
self.assertContains(page, "Sleep entry deleted") self.assertContains(page, "Sleep entry deleted")
def test_nap_default(self):
models.Sleep.settings.nap_start_min = (
timezone.now() - timezone.timedelta(hours=1)
).time()
models.Sleep.settings.nap_start_max = (
timezone.now() + timezone.timedelta(hours=1)
).time()
response = self.c.get("/sleep/add/")
self.assertTrue(response.context["form"].initial["nap"])
def test_not_nap_default(self):
models.Sleep.settings.nap_start_min = (
timezone.now() + timezone.timedelta(hours=1)
).time()
models.Sleep.settings.nap_start_max = (
timezone.now() + timezone.timedelta(hours=2)
).time()
response = self.c.get("/sleep/add/")
self.assertFalse(response.context["form"].initial["nap"])
class TaggedFormsTestCase(FormsTestCaseBase): class TaggedFormsTestCase(FormsTestCaseBase):
@classmethod @classmethod

View File

@ -1,4 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from datetime import datetime
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.management import call_command from django.core.management import call_command
from django.test import TestCase from django.test import TestCase
@ -210,6 +212,42 @@ class SleepTestCase(TestCase):
self.assertEqual(str(sleep), "Sleep") self.assertEqual(str(sleep), "Sleep")
self.assertEqual(sleep.duration, sleep.end - sleep.start) self.assertEqual(sleep.duration, sleep.end - sleep.start)
def test_sleep_nap(self):
models.Sleep.settings.nap_start_min = (
timezone.now() - timezone.timedelta(hours=1)
).time()
models.Sleep.settings.nap_start_max = (
timezone.now() + timezone.timedelta(hours=1)
).time()
sleep = models.Sleep.objects.create(
child=self.child,
start=timezone.now(),
end=(timezone.now() + timezone.timedelta(hours=2)),
)
self.assertTrue(sleep.nap)
def test_sleep_not_nap(self):
models.Sleep.settings.nap_start_min = (
timezone.now() + timezone.timedelta(hours=1)
).time()
models.Sleep.settings.nap_start_max = (
timezone.now() + timezone.timedelta(hours=2)
).time()
sleep = models.Sleep.objects.create(
child=self.child,
start=timezone.now(),
end=(timezone.now() + timezone.timedelta(hours=8)),
)
self.assertFalse(sleep.nap)
sleep = models.Sleep.objects.create(
child=self.child,
start=timezone.now(),
end=(timezone.now() + timezone.timedelta(hours=8)),
nap=True,
)
self.assertTrue(sleep.nap)
class TagTestCase(TestCase): class TagTestCase(TestCase):
def setUp(self): def setUp(self):

View File

@ -324,9 +324,9 @@ def card_sleep_naps_day(context, child, date=None):
""" """
if not date: if not date:
date = timezone.localtime().date() date = timezone.localtime().date()
instances = models.Sleep.naps.filter(child=child).filter( instances = models.Sleep.objects.filter(child=child, nap=True).filter(
start__year=date.year, start__month=date.month, start__day=date.day start__year=date.year, start__month=date.month, start__day=date.day
) | models.Sleep.naps.filter(child=child).filter( ) | models.Sleep.objects.filter(child=child, nap=True).filter(
end__year=date.year, end__month=date.month, end__day=date.day end__year=date.year, end__month=date.month, end__day=date.day
) )
empty = len(instances) == 0 empty = len(instances) == 0
@ -548,7 +548,7 @@ def _nap_statistics(child):
:param child: an instance of the Child model. :param child: an instance of the Child model.
:returns: a dictionary of statistics. :returns: a dictionary of statistics.
""" """
instances = models.Sleep.naps.filter(child=child).order_by("start") instances = models.Sleep.objects.filter(child=child, nap=True).order_by("start")
if len(instances) == 0: if len(instances) == 0:
return False return False
naps = { naps = {

View File

@ -216,10 +216,10 @@ class TemplateTagsTestCase(TestCase):
self.assertEqual(data["type"], "sleep") self.assertEqual(data["type"], "sleep")
self.assertFalse(data["empty"]) self.assertFalse(data["empty"])
self.assertFalse(data["hide_empty"]) self.assertFalse(data["hide_empty"])
self.assertEqual(data["sleeps"][0]["total"], timezone.timedelta(hours=7)) self.assertEqual(data["sleeps"][0]["total"], timezone.timedelta(seconds=43200))
self.assertEqual(data["sleeps"][0]["count"], 4) self.assertEqual(data["sleeps"][0]["count"], 3)
self.assertEqual(data["sleeps"][1]["total"], timezone.timedelta(minutes=30)) self.assertEqual(data["sleeps"][1]["total"], timezone.timedelta(seconds=30600))
self.assertEqual(data["sleeps"][1]["count"], 1) self.assertEqual(data["sleeps"][1]["count"], 1)
def test_card_sleep_naps_day(self): def test_card_sleep_naps_day(self):
@ -227,8 +227,8 @@ class TemplateTagsTestCase(TestCase):
self.assertEqual(data["type"], "sleep") self.assertEqual(data["type"], "sleep")
self.assertFalse(data["empty"]) self.assertFalse(data["empty"])
self.assertFalse(data["hide_empty"]) self.assertFalse(data["hide_empty"])
self.assertEqual(data["total"], timezone.timedelta(0, 9000)) self.assertEqual(data["total"], timezone.timedelta(0, 7200))
self.assertEqual(data["count"], 2) self.assertEqual(data["count"], 1)
def test_card_statistics(self): def test_card_statistics(self):
data = cards.card_statistics(self.context, self.child) data = cards.card_statistics(self.context, self.child)
@ -271,18 +271,18 @@ class TemplateTagsTestCase(TestCase):
}, },
{ {
"title": "Average nap duration", "title": "Average nap duration",
"stat": timezone.timedelta(0, 4500), "stat": timezone.timedelta(0, 6300),
"type": "duration", "type": "duration",
}, },
{"title": "Average naps per day", "stat": 2.0, "type": "float"}, {"title": "Average naps per day", "stat": 1.0, "type": "float"},
{ {
"title": "Average sleep duration", "title": "Average sleep duration",
"stat": timezone.timedelta(0, 6750), "stat": timezone.timedelta(0, 19800),
"type": "duration", "type": "duration",
}, },
{ {
"title": "Average awake duration", "title": "Average awake duration",
"stat": timezone.timedelta(0, 19200), "stat": timezone.timedelta(0, 18000),
"type": "duration", "type": "duration",
}, },
{"title": "Weight change per week", "stat": 1.0, "type": "float"}, {"title": "Weight change per week", "stat": 1.0, "type": "float"},