mirror of https://github.com/snachodog/mybuddy.git
Add Settings model extending User.
This commit is contained in:
parent
f9f6ea5e88
commit
60ac9a7f5d
|
@ -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)
|
|
@ -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']
|
|
@ -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)),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -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()
|
|
@ -16,50 +16,7 @@
|
|||
{% endif %}
|
||||
{% for field in form %}
|
||||
<div class="form-group row">
|
||||
<label for="id_{{ field.name }}" class="col-sm-1 col-form-label">
|
||||
{% if field|field_type != "booleanfield" %}
|
||||
{{ field.label }}
|
||||
{% endif %}
|
||||
</label>
|
||||
<div class="col-sm-11">
|
||||
{% if field|field_type == "booleanfield" %}
|
||||
<div class="form-check">
|
||||
<label for="id_{{ field.name }}" class="form-check-label">
|
||||
{% if field.errors %}
|
||||
{{ field|add_class:"form-check-input is-invalid" }}
|
||||
{% else %}
|
||||
{{ field|add_class:"form-check-input" }}
|
||||
{% endif %}
|
||||
{{ field.label }}
|
||||
</label>
|
||||
</div>
|
||||
{% elif field|field_type == "datetimefield" or field|field_type == "datefield" %}
|
||||
<div class="form-group">
|
||||
<div class="input-group date" id="datetimepicker_{{ field.name }}" data-target-input="nearest">
|
||||
<div class="input-group-addon" data-target="#datetimepicker_{{ field.name }}" data-toggle="datetimepicker">
|
||||
<i class="fa fa-calendar"></i>
|
||||
</div>
|
||||
{% if field.errors %}
|
||||
{{ field|add_class:"form-control is-invalid" }}
|
||||
{% else %}
|
||||
{{ field|add_class:"form-control" }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if field.errors %}
|
||||
{{ field|add_class:"form-control is-invalid" }}
|
||||
{% else %}
|
||||
{{ field|add_class:"form-control" }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if field.help_text %}
|
||||
<div class="help-block"><small>{{ field.help_text }}</small></div>
|
||||
{% endif %}
|
||||
{% if field.errors %}
|
||||
<div class="invalid-feedback">{% for error in field.errors %}{{ error }}{% endfor %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'babybuddy/form_field.html' %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
{% load widget_tweaks %}
|
||||
|
||||
<label for="id_{{ field.name }}" class="col-sm-2 col-form-label">
|
||||
{% if field|field_type != "booleanfield" %}
|
||||
{{ field.label }}
|
||||
{% endif %}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
{% if field|field_type == "booleanfield" %}
|
||||
<div class="form-check">
|
||||
<label for="id_{{ field.name }}" class="form-check-label">
|
||||
{% if field.errors %}
|
||||
{{ field|add_class:"form-check-input is-invalid" }}
|
||||
{% else %}
|
||||
{{ field|add_class:"form-check-input" }}
|
||||
{% endif %}
|
||||
{{ field.label }}
|
||||
</label>
|
||||
</div>
|
||||
{% elif field|field_type == "datetimefield" or field|field_type == "datefield" %}
|
||||
<div class="form-group">
|
||||
<div class="input-group date" id="datetimepicker_{{ field.name }}" data-target-input="nearest">
|
||||
<div class="input-group-addon" data-target="#datetimepicker_{{ field.name }}" data-toggle="datetimepicker">
|
||||
<i class="fa fa-calendar"></i>
|
||||
</div>
|
||||
{% if field.errors %}
|
||||
{{ field|add_class:"form-control is-invalid" }}
|
||||
{% else %}
|
||||
{{ field|add_class:"form-control" }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if field.errors %}
|
||||
{{ field|add_class:"form-control is-invalid" }}
|
||||
{% else %}
|
||||
{{ field|add_class:"form-control" }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if field.help_text %}
|
||||
<div class="help-block"><small>{{ field.help_text }}</small></div>
|
||||
{% endif %}
|
||||
{% if field.errors %}
|
||||
<div class="invalid-feedback">{% for error in field.errors %}{{ error }}{% endfor %}</div>
|
||||
{% endif %}
|
||||
</div>
|
|
@ -173,16 +173,17 @@
|
|||
aria-expanded="false"><i class="icon icon-user" aria-hidden="true"></i> {{ request.user }}
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="nav-user-menu-link">
|
||||
<a href="{% url 'user-settings' %}" class="dropdown-item">Settings</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="{% url 'api:api-root' %}"
|
||||
class="dropdown-item"
|
||||
target="_blank">API Browser</a>
|
||||
|
||||
{% if request.user.is_staff %}
|
||||
<a href="{% url 'admin:index' %}"
|
||||
class="dropdown-item"
|
||||
target="_blank">Admin</a>
|
||||
target="_blank">Site Admin</a>
|
||||
{% endif %}
|
||||
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="{% url 'logout' %}"
|
||||
class="dropdown-item">Logout</a>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
{% extends 'babybuddy/page.html' %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block title %}User Settings{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li class="breadcrumb-item">User</li>
|
||||
<li class="breadcrumb-item active">Settings</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>User Settings</h1>
|
||||
<div class="container-fluid">
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% if form.non_field_errors %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<strong>Error:</strong> {{ error }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% elif form.errors %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<strong>Error:</strong> Some fields have errors. See below for details.
|
||||
</div>
|
||||
{% endif %}
|
||||
<fieldset>
|
||||
<legend>User Profile</legend>
|
||||
<div class="form-group row">
|
||||
{% with form_user.first_name as field %}
|
||||
{% include 'babybuddy/form_field.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
{% with form_user.last_name as field %}
|
||||
{% include 'babybuddy/form_field.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
{% with form_user.email as field %}
|
||||
{% include 'babybuddy/form_field.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Dashboard</legend>
|
||||
<div class="form-group row">
|
||||
{% with form_settings.dashboard_refresh_rate as field %}
|
||||
{% include 'babybuddy/form_field.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</fieldset>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -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')),
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -31,7 +31,9 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block javascript %}
|
||||
<script type="application/javascript">
|
||||
BabyBuddy.Dashboard.watch('dashboard-child');
|
||||
</script>
|
||||
{% if user.settings.dashboard_refresh_rate %}
|
||||
<script type="application/javascript">
|
||||
BabyBuddy.Dashboard.watch('dashboard-child', {{ user.settings.dashboard_refresh_rate_milliseconds }});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
Loading…
Reference in New Issue