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 %}
- -
- {% if field|field_type == "booleanfield" %} -
- -
- {% elif field|field_type == "datetimefield" or field|field_type == "datefield" %} -
-
-
- -
- {% if field.errors %} - {{ field|add_class:"form-control is-invalid" }} - {% else %} - {{ field|add_class:"form-control" }} - {% endif %} -
-
- {% 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 %} -
+ {% include 'babybuddy/form_field.html' %}
{% 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" %} +
+
+
+ +
+ {% if field.errors %} + {{ field|add_class:"form-control is-invalid" }} + {% else %} + {{ field|add_class:"form-control" }} + {% endif %} +
+
+ {% 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 %} + + +{% endblock %} + +{% block content %} +

User Settings

+
+
+ {% csrf_token %} + {% if form.non_field_errors %} + {% for error in form.non_field_errors %} + + {% endfor %} + {% elif form.errors %} + + {% endif %} +
+ User Profile +
+ {% with form_user.first_name as field %} + {% include 'babybuddy/form_field.html' %} + {% endwith %} +
+
+ {% with form_user.last_name as field %} + {% include 'babybuddy/form_field.html' %} + {% endwith %} +
+
+ {% with form_user.email as field %} + {% include 'babybuddy/form_field.html' %} + {% endwith %} +
+
+
+ Dashboard +
+ {% with form_settings.dashboard_refresh_rate as field %} + {% include 'babybuddy/form_field.html' %} + {% endwith %} +
+
+ +
+
+{% 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