diff --git a/Pipfile b/Pipfile
index ba75c4e5..c100cbb6 100644
--- a/Pipfile
+++ b/Pipfile
@@ -25,6 +25,7 @@ uritemplate = "*"
whitenoise = "*"
django-taggit = "*"
django-qr-code = "*"
+django-dbsettings = "*"
[dev-packages]
coveralls = "*"
diff --git a/babybuddy/forms.py b/babybuddy/forms.py
index 004db117..5fb82d60 100644
--- a/babybuddy/forms.py
+++ b/babybuddy/forms.py
@@ -6,22 +6,7 @@ from django.contrib.auth.forms import PasswordChangeForm, UserCreationForm
from django.contrib.auth.models import Group
from django.utils.translation import gettext_lazy as _
-from core.widgets import TimeInput
-
-from .models import Settings, SiteSettings
-
-
-class SiteSettingsForm(forms.ModelForm):
- class Meta:
- model = SiteSettings
- fields = [
- "nap_start_min",
- "nap_start_max",
- ]
- widgets = {
- "nap_start_min": TimeInput(),
- "nap_start_max": TimeInput(),
- }
+from .models import Settings
class BabyBuddyUserForm(forms.ModelForm):
diff --git a/babybuddy/migrations/0028_sitesettings.py b/babybuddy/migrations/0028_sitesettings.py
deleted file mode 100644
index 5433643e..00000000
--- a/babybuddy/migrations/0028_sitesettings.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Generated by Django 4.0.7 on 2022-08-14 23:28
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("babybuddy", "0027_remove_standard_group"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="SiteSettings",
- fields=[
- (
- "id",
- models.AutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- (
- "nap_start_min",
- models.TimeField(
- blank=True,
- help_text="The minimum default time that a sleep entry is consider a nap.If set the nap property will be preselected if the starttime is within the bounds.",
- null=True,
- verbose_name="Default minimum nap start time",
- ),
- ),
- (
- "nap_start_max",
- models.TimeField(
- blank=True,
- help_text="The maximum default time that a sleep entry is consider a nap.If set the nap property will be preselected if the starttime is within the bounds.",
- null=True,
- verbose_name="Default maximum nap start time",
- ),
- ),
- ],
- ),
- ]
diff --git a/babybuddy/models.py b/babybuddy/models.py
index 1a44a55f..6ca19159 100644
--- a/babybuddy/models.py
+++ b/babybuddy/models.py
@@ -94,32 +94,6 @@ class Settings(models.Model):
return None
-class SiteSettings(models.Model):
- nap_start_min = models.TimeField(
- verbose_name=_("Default minimum nap start time"),
- help_text=_(
- "The minimum default time that a sleep entry is consider a nap."
- "If set the nap property will be preselected if the start"
- "time is within the bounds."
- ),
- blank=True,
- null=True,
- )
- nap_start_max = models.TimeField(
- verbose_name=_("Default maximum nap start time"),
- help_text=_(
- "The maximum default time that a sleep entry is consider a nap."
- "If set the nap property will be preselected if the start"
- "time is within the bounds."
- ),
- blank=True,
- null=True,
- )
-
- def __str__(self):
- return _("Site Settings")
-
-
@receiver(post_save, sender=get_user_model())
def create_user_settings(sender, instance, created, **kwargs):
if created:
diff --git a/babybuddy/settings/base.py b/babybuddy/settings/base.py
index bb80554a..285a1abf 100644
--- a/babybuddy/settings/base.py
+++ b/babybuddy/settings/base.py
@@ -37,6 +37,7 @@ INSTALLED_APPS = [
"storages",
"import_export",
"qr_code",
+ "dbsettings",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
@@ -353,7 +354,5 @@ DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
BABY_BUDDY = {
"ALLOW_UPLOADS": bool(strtobool(os.environ.get("ALLOW_UPLOADS") or "True")),
- "NAP_START_MAX": os.environ.get("NAP_START_MAX") or "18:00",
- "NAP_START_MIN": os.environ.get("NAP_START_MIN") or "06:00",
"READ_ONLY_GROUP_NAME": "read_only",
}
diff --git a/babybuddy/site_settings.py b/babybuddy/site_settings.py
new file mode 100644
index 00000000..92a58bdf
--- /dev/null
+++ b/babybuddy/site_settings.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+from datetime import time
+
+from django.utils.translation import gettext_lazy as _
+
+import dbsettings
+
+from .widgets import TimeInput
+
+
+class NapSettings(dbsettings.Group):
+ nap_start_min = dbsettings.TimeValue(
+ default=time(6),
+ description=_("Default minimum nap start time"),
+ help_text=_(
+ "The minimum default time that a sleep entry is consider a nap. If set the "
+ "nap property will be preselected if the start time is within the bounds."
+ ),
+ widget=TimeInput,
+ )
+ nap_start_max = dbsettings.TimeValue(
+ default=time(18),
+ description=_("Default maximum nap start time"),
+ help_text=_(
+ "The maximum default time that a sleep entry is consider a nap. If set the "
+ "nap property will be preselected if the start time is within the bounds."
+ ),
+ widget=TimeInput,
+ )
diff --git a/babybuddy/templates/babybuddy/nav-dropdown.html b/babybuddy/templates/babybuddy/nav-dropdown.html
index 907d614e..efc2e04d 100644
--- a/babybuddy/templates/babybuddy/nav-dropdown.html
+++ b/babybuddy/templates/babybuddy/nav-dropdown.html
@@ -336,7 +336,7 @@
{% trans "API Browser" %}
{% if request.user.is_staff %}
- {% trans "Settings" %}
{% trans "Users" %}
diff --git a/babybuddy/urls.py b/babybuddy/urls.py
index d05d0895..3cd7d579 100644
--- a/babybuddy/urls.py
+++ b/babybuddy/urls.py
@@ -35,7 +35,6 @@ app_patterns = [
name="password_reset_complete",
),
path("", views.RootRouter.as_view(), name="root-router"),
- path("settings/", views.SiteSettingsUpdate.as_view(), name="site-settings-update"),
path("welcome/", views.Welcome.as_view(), name="welcome"),
path("users/", views.UserList.as_view(), name="user-list"),
path("users/add/", views.UserAdd.as_view(), name="user-add"),
@@ -45,6 +44,7 @@ app_patterns = [
path("user/password/", views.UserPassword.as_view(), name="user-password"),
path("user/settings/", views.UserSettings.as_view(), name="user-settings"),
path("user/add-device/", views.UserAddDevice.as_view(), name="user-add-device"),
+ path("settings/", include("dbsettings.urls")),
]
urlpatterns = [
diff --git a/babybuddy/views.py b/babybuddy/views.py
index 8c7e3ce6..4d2b85c6 100644
--- a/babybuddy/views.py
+++ b/babybuddy/views.py
@@ -37,7 +37,6 @@ from django_filters.views import FilterView
from babybuddy import forms
from babybuddy.mixins import LoginRequiredMixin, PermissionRequiredMixin, StaffOnlyMixin
-from babybuddy.models import SiteSettings
def csrf_failure(request, reason=""):
@@ -96,17 +95,6 @@ class LogoutView(LogoutViewBase):
pass
-class SiteSettingsUpdate(StaffOnlyMixin, SuccessMessageMixin, UpdateView):
- model = SiteSettings
- template_name = "babybuddy/site_settings_form.html"
- form_class = forms.SiteSettingsForm
- success_url = reverse_lazy("babybuddy:site-settings-update")
- success_message = gettext_lazy("Site settings updated.")
-
- def get_object(self, queryset=None):
- return SiteSettings.objects.get_or_create(pk=1)[0]
-
-
class UserList(StaffOnlyMixin, BabyBuddyFilterView):
model = get_user_model()
template_name = "babybuddy/user_list.html"
diff --git a/babybuddy/widgets.py b/babybuddy/widgets.py
new file mode 100644
index 00000000..682ca63e
--- /dev/null
+++ b/babybuddy/widgets.py
@@ -0,0 +1,29 @@
+import datetime
+
+from django.forms import widgets
+
+
+class DateTimeBaseInput(widgets.DateTimeBaseInput):
+ def format_value(self, value):
+ if isinstance(value, datetime.datetime):
+ value = value.isoformat()
+ return value
+
+
+class DateTimeInput(DateTimeBaseInput):
+ input_type = "datetime-local"
+
+ def build_attrs(self, base_attrs, extra_attrs=None):
+ attrs = super().build_attrs(base_attrs, extra_attrs)
+ # Default to seconds granularity. Required for client validation in Safari.
+ if "step" not in attrs:
+ attrs["step"] = 1
+ return attrs
+
+
+class DateInput(DateTimeBaseInput):
+ input_type = "date"
+
+
+class TimeInput(DateTimeBaseInput):
+ input_type = "time"
diff --git a/core/forms.py b/core/forms.py
index 06b49602..35aed650 100644
--- a/core/forms.py
+++ b/core/forms.py
@@ -7,8 +7,9 @@ from django.utils.translation import gettext as _
from taggit.forms import TagField
+from babybuddy.widgets import DateInput, DateTimeInput
from core import models
-from core.widgets import TagsEditor, ChildRadioSelect, DateInput, DateTimeInput
+from core.widgets import TagsEditor, ChildRadioSelect
def set_initial_values(kwargs, form_type):
diff --git a/core/models.py b/core/models.py
index e6b77c25..82489430 100644
--- a/core/models.py
+++ b/core/models.py
@@ -2,7 +2,6 @@
import re
from datetime import timedelta
-from django.conf import settings
from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
@@ -13,6 +12,7 @@ from django.utils.translation import gettext_lazy as _
from taggit.managers import TaggableManager as TaggitTaggableManager
from taggit.models import GenericTaggedItemBase, TagBase
+from babybuddy.site_settings import NapSettings
from core.utils import random_color
@@ -477,6 +477,7 @@ class Sleep(models.Model):
objects = models.Manager()
naps = NapsManager()
+ settings = NapSettings(_("Nap settings"))
class Meta:
default_permissions = ("view", "add", "change", "delete")
@@ -489,14 +490,12 @@ class Sleep(models.Model):
@property
def nap(self):
- nap_start_min = timezone.datetime.strptime(
- settings.BABY_BUDDY["NAP_START_MIN"], "%H:%M"
- ).time()
- nap_start_max = timezone.datetime.strptime(
- settings.BABY_BUDDY["NAP_START_MAX"], "%H:%M"
- ).time()
local_start_time = timezone.localtime(self.start).time()
- return nap_start_min <= local_start_time <= nap_start_max
+ return (
+ Sleep.settings.nap_start_min
+ <= local_start_time
+ <= Sleep.settings.nap_start_max
+ )
def save(self, *args, **kwargs):
if self.start and self.end:
diff --git a/core/widgets.py b/core/widgets.py
index 834d092d..3fe1ed67 100644
--- a/core/widgets.py
+++ b/core/widgets.py
@@ -104,29 +104,3 @@ class ChildRadioSelect(RadioSelect):
if value != "":
option["picture"] = value.instance.picture
return option
-
-
-class DateTimeBaseInput(widgets.DateTimeBaseInput):
- def format_value(self, value):
- if isinstance(value, datetime.datetime):
- value = value.isoformat()
- return value
-
-
-class DateTimeInput(DateTimeBaseInput):
- input_type = "datetime-local"
-
- def build_attrs(self, base_attrs, extra_attrs=None):
- attrs = super().build_attrs(base_attrs, extra_attrs)
- # Default to seconds granularity. Required for client validation in Safari.
- if "step" not in attrs:
- attrs["step"] = 1
- return attrs
-
-
-class DateInput(DateTimeBaseInput):
- input_type = "date"
-
-
-class TimeInput(DateTimeBaseInput):
- input_type = "time"
diff --git a/docs/configuration/application.md b/docs/configuration/application.md
index 9a9f2ab5..7461c218 100644
--- a/docs/configuration/application.md
+++ b/docs/configuration/application.md
@@ -9,20 +9,6 @@ for exceptions. This setting should be *False* in production deployments.
See also [Django's documentation on the DEBUG setting](https://docs.djangoproject.com/en/4.0/ref/settings/#debug).
-## `NAP_START_MAX`
-
-*Default:* `18:00`
-
-The maximum nap *start* time (in the instance's time zone). Expects the 24-hour
-format %H:%M.
-
-## `NAP_START_MIN`
-
-*Default:* `06:00`
-
-The minimum nap *start* time (in the instance's time zone). Expects the 24-hour
-format %H:%M.
-
## `SUB_PATH`
*Default:* `None`