diff --git a/babybuddy/admin.py b/babybuddy/admin.py
new file mode 100644
index 00000000..e001ac6e
--- /dev/null
+++ b/babybuddy/admin.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.contrib import admin
+from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
+from django.contrib.auth.models import User
+
+from babybuddy import models
+
+
+class SettingsInline(admin.StackedInline):
+ model = models.Settings
+ verbose_name_plural = 'Settings'
+ can_delete = False
+ fieldsets = (
+ ('Dashboard', {
+ 'fields': ('dashboard_refresh_rate',)
+ }),
+ )
+
+
+class UserAdmin(BaseUserAdmin):
+ inlines = (SettingsInline, )
+
+
+admin.site.unregister(User)
+admin.site.register(User, UserAdmin)
diff --git a/babybuddy/forms.py b/babybuddy/forms.py
new file mode 100644
index 00000000..62bb158b
--- /dev/null
+++ b/babybuddy/forms.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django import forms
+from django.contrib.auth.models import User
+
+from .models import Settings
+
+
+class UserForm(forms.ModelForm):
+ class Meta:
+ model = User
+ fields = ['first_name', 'last_name', 'email']
+
+
+class UserSettingsForm(forms.ModelForm):
+ class Meta:
+ model = Settings
+ fields = ['dashboard_refresh_rate']
diff --git a/babybuddy/migrations/0001_initial.py b/babybuddy/migrations/0001_initial.py
new file mode 100644
index 00000000..efde82e8
--- /dev/null
+++ b/babybuddy/migrations/0001_initial.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.6 on 2017-11-12 17:05
+from __future__ import unicode_literals
+
+import datetime
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Settings',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('dashboard_refresh_rate', models.DurationField(blank=True, choices=[(None, 'disabled'), (datetime.timedelta(0, 60), '1 min.'), (datetime.timedelta(0, 120), '2 min.'), (datetime.timedelta(0, 180), '3 min.'), (datetime.timedelta(0, 240), '4 min.'), (datetime.timedelta(0, 300), '5 min.'), (datetime.timedelta(0, 600), '10 min.'), (datetime.timedelta(0, 900), '15 min.'), (datetime.timedelta(0, 1800), '30 min.')], default=datetime.timedelta(0, 60), null=True, verbose_name='Refresh rate')),
+ ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ ]
diff --git a/babybuddy/models.py b/babybuddy/models.py
new file mode 100644
index 00000000..7ebc9cdc
--- /dev/null
+++ b/babybuddy/models.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.contrib.auth.models import User
+from django.db import models
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+from django.utils.timezone import timedelta
+
+
+class Settings(models.Model):
+ user = models.OneToOneField(User)
+ dashboard_refresh_rate = models.DurationField(
+ verbose_name='Refresh rate',
+ blank=True,
+ null=True,
+ default=timedelta(minutes=1),
+ choices=[
+ (None, 'disabled'),
+ (timedelta(minutes=1), '1 min.'),
+ (timedelta(minutes=2), '2 min.'),
+ (timedelta(minutes=3), '3 min.'),
+ (timedelta(minutes=4), '4 min.'),
+ (timedelta(minutes=5), '5 min.'),
+ (timedelta(minutes=10), '10 min.'),
+ (timedelta(minutes=15), '15 min.'),
+ (timedelta(minutes=30), '30 min.'),
+ ])
+
+ def __str__(self):
+ return '{}\'s Settings'.format(self.user)
+
+ @property
+ def dashboard_refresh_rate_milliseconds(self):
+ """
+ Convert seconds to milliseconds to be used in a Javascript setInterval
+ function call.
+ :return: the refresh rate in milliseconds or None.
+ """
+ if self.dashboard_refresh_rate:
+ return self.dashboard_refresh_rate.seconds * 1000
+ return None
+
+
+@receiver(post_save, sender=User)
+def create_user_settings(sender, instance, created, **kwargs):
+ if created:
+ Settings.objects.create(user=instance)
+
+
+@receiver(post_save, sender=User)
+def save_user_settings(sender, instance, **kwargs):
+ instance.settings.save()
diff --git a/babybuddy/templates/babybuddy/form.html b/babybuddy/templates/babybuddy/form.html
index 414befc8..7e182ba2 100644
--- a/babybuddy/templates/babybuddy/form.html
+++ b/babybuddy/templates/babybuddy/form.html
@@ -16,50 +16,7 @@
{% endif %}
{% for field in form %}
{% endfor %}
diff --git a/babybuddy/templates/babybuddy/form_field.html b/babybuddy/templates/babybuddy/form_field.html
new file mode 100644
index 00000000..a683ebed
--- /dev/null
+++ b/babybuddy/templates/babybuddy/form_field.html
@@ -0,0 +1,46 @@
+{% load widget_tweaks %}
+
+
+
+ {% if field|field_type == "booleanfield" %}
+
+
+
+ {% elif field|field_type == "datetimefield" or field|field_type == "datefield" %}
+
+ {% else %}
+ {% if field.errors %}
+ {{ field|add_class:"form-control is-invalid" }}
+ {% else %}
+ {{ field|add_class:"form-control" }}
+ {% endif %}
+ {% endif %}
+ {% if field.help_text %}
+
{{ field.help_text }}
+ {% endif %}
+ {% if field.errors %}
+
{% for error in field.errors %}{{ error }}{% endfor %}
+ {% endif %}
+
\ No newline at end of file
diff --git a/babybuddy/templates/babybuddy/nav-dropdown.html b/babybuddy/templates/babybuddy/nav-dropdown.html
index 24fe061e..23109f08 100644
--- a/babybuddy/templates/babybuddy/nav-dropdown.html
+++ b/babybuddy/templates/babybuddy/nav-dropdown.html
@@ -173,16 +173,17 @@
aria-expanded="false"> {{ request.user }}
diff --git a/babybuddy/templates/babybuddy/user_settings_form.html b/babybuddy/templates/babybuddy/user_settings_form.html
new file mode 100644
index 00000000..b098606d
--- /dev/null
+++ b/babybuddy/templates/babybuddy/user_settings_form.html
@@ -0,0 +1,56 @@
+{% extends 'babybuddy/page.html' %}
+{% load widget_tweaks %}
+
+{% block title %}User Settings{% endblock %}
+
+{% block breadcrumbs %}
+ User
+ Settings
+{% endblock %}
+
+{% block content %}
+ User Settings
+
+{% endblock %}
diff --git a/babybuddy/urls.py b/babybuddy/urls.py
index 8297db65..ae1b8542 100644
--- a/babybuddy/urls.py
+++ b/babybuddy/urls.py
@@ -17,6 +17,8 @@ urlpatterns = [
url(r'^$', views.RootRouter.as_view(), name='root-router'),
url(r'^welcome/$', views.Welcome.as_view(), name='welcome'),
+ url(r'^user/settings/$', views.UserSettings.as_view(),
+ name='user-settings'),
url(r'', include('api.urls', namespace='api')),
url(r'', include('core.urls')),
diff --git a/babybuddy/views.py b/babybuddy/views.py
index 0bab8eff..f61231b5 100644
--- a/babybuddy/views.py
+++ b/babybuddy/views.py
@@ -2,13 +2,22 @@
from __future__ import unicode_literals
from django.contrib.auth.mixins import LoginRequiredMixin
+from django.shortcuts import redirect, render
from django.urls import reverse
+
+from django.views.generic import View
from django.views.generic.base import TemplateView, RedirectView
+from .forms import UserForm, UserSettingsForm
from core.models import Child
class RootRouter(LoginRequiredMixin, RedirectView):
+ """
+ Redirects to the welcome page if no children are in the database, a child
+ dashboard if only one child is in the database, and the dashboard page if
+ more than one child is in the database.
+ """
def get_redirect_url(self, *args, **kwargs):
children = Child.objects.count()
if children == 0:
@@ -21,5 +30,44 @@ class RootRouter(LoginRequiredMixin, RedirectView):
return super(RootRouter, self).get_redirect_url(self, *args, **kwargs)
+class UserSettings(LoginRequiredMixin, View):
+ """
+ Handles both the User and Settings models.
+ Based on this SO answer: https://stackoverflow.com/a/45056835.
+ """
+ form_user_class = UserForm
+ form_settings_class = UserSettingsForm
+ template_name = 'babybuddy/user_settings_form.html'
+
+ def get(self, request):
+ return render(request, self.template_name, {
+ 'form_user': self.form_user_class(instance=request.user),
+ 'form_settings': self.form_settings_class(
+ instance=request.user.settings)
+ })
+
+ def post(self, request):
+ form_user = self.form_user_class(
+ instance=request.user,
+ data=request.POST)
+ form_settings = self.form_settings_class(
+ instance=request.user.settings,
+ data=request.POST)
+ if form_user.is_valid() and form_settings.is_valid():
+ user = form_user.save(commit=False)
+ user_settings = form_settings.save(commit=False)
+ user.settings = user_settings
+ user.save()
+ return redirect('user-settings')
+ return render(request, self.template_name, {
+ 'user_form': form_user,
+ 'settings_form': form_settings
+ })
+
+
class Welcome(LoginRequiredMixin, TemplateView):
+ """
+ Basic introduction to Baby Buddy (meant to be shown when no data is in the
+ database).
+ """
template_name = 'babybuddy/welcome.html'
diff --git a/dashboard/static_src/js/dashboard.js b/dashboard/static_src/js/dashboard.js
index bde66ecd..9b7f0de2 100644
--- a/dashboard/static_src/js/dashboard.js
+++ b/dashboard/static_src/js/dashboard.js
@@ -9,7 +9,7 @@ BabyBuddy.Dashboard = function ($) {
var lastUpdate = moment();
var Dashboard = {
- watch: function(element_id) {
+ watch: function(element_id, refresh_rate) {
dashboardElement = $('#' + element_id);
if (dashboardElement.length == 0) {
@@ -17,7 +17,7 @@ BabyBuddy.Dashboard = function ($) {
return false;
}
- runIntervalId = setInterval(this.update, 60000);
+ runIntervalId = setInterval(this.update, refresh_rate);
Visibility.change(function (e, state) {
if (state == 'visible' && moment().diff(lastUpdate) > 60000) {
diff --git a/dashboard/templates/dashboard/child.html b/dashboard/templates/dashboard/child.html
index 779e712a..efa4876c 100644
--- a/dashboard/templates/dashboard/child.html
+++ b/dashboard/templates/dashboard/child.html
@@ -31,7 +31,9 @@
{% endblock %}
{% block javascript %}
-
+ {% if user.settings.dashboard_refresh_rate %}
+
+ {% endif %}
{% endblock %}
\ No newline at end of file