From 73482dd4448b077aff87bfc947e8b70b0fbf1058 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Thu, 2 Jul 2020 16:29:06 +0200 Subject: [PATCH] wip --- .../0051_agendanotificationssettings.py | 108 ++++++++++++++++++ chrono/agendas/models.py | 70 ++++++++++++ chrono/manager/forms.py | 26 +++++ .../manager_agenda_notifications_form.html | 48 ++++++++ .../manager_events_agenda_settings.html | 18 +++ chrono/manager/urls.py | 5 + chrono/manager/views.py | 18 +++ 7 files changed, 293 insertions(+) create mode 100644 chrono/agendas/migrations/0051_agendanotificationssettings.py create mode 100644 chrono/manager/templates/chrono/manager_agenda_notifications_form.html diff --git a/chrono/agendas/migrations/0051_agendanotificationssettings.py b/chrono/agendas/migrations/0051_agendanotificationssettings.py new file mode 100644 index 0000000..301fb24 --- /dev/null +++ b/chrono/agendas/migrations/0051_agendanotificationssettings.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.18 on 2020-07-02 14:42 +from __future__ import unicode_literals + +import django.contrib.postgres.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0008_alter_user_username_max_length'), + ('agendas', '0050_event_slug'), + ] + + operations = [ + migrations.CreateModel( + name='AgendaNotificationsSettings', + fields=[ + ( + 'id', + models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ( + 'use_email_fields', + models.BooleanField(default=False, verbose_name='Use email addresses instead of roles'), + ), + ( + 'almost_full_emails', + django.contrib.postgres.fields.ArrayField( + base_field=models.EmailField(max_length=254), + blank=True, + help_text='Enter a comma separated list of email addresses.', + null=True, + size=None, + verbose_name='Almost full event (90%)', + ), + ), + ( + 'full_emails', + django.contrib.postgres.fields.ArrayField( + base_field=models.EmailField(max_length=254), + blank=True, + help_text='Enter a comma separated list of email addresses.', + null=True, + size=None, + verbose_name='Full event', + ), + ), + ( + 'cancelled_emails', + django.contrib.postgres.fields.ArrayField( + base_field=models.EmailField(max_length=254), + blank=True, + help_text='Enter a comma separated list of email addresses.', + null=True, + size=None, + verbose_name='Cancelled event', + ), + ), + ( + 'agenda', + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name='notifications_settings', + to='agendas.Agenda', + ), + ), + ( + 'almost_full_role', + models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='auth.Group', + verbose_name='Almost full event (90%)', + ), + ), + ( + 'cancelled_role', + models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='auth.Group', + verbose_name='Cancelled event', + ), + ), + ( + 'full_role', + models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='auth.Group', + verbose_name='Full event', + ), + ), + ], + ), + ] diff --git a/chrono/agendas/models.py b/chrono/agendas/models.py index 4c8d582..212a41d 100644 --- a/chrono/agendas/models.py +++ b/chrono/agendas/models.py @@ -29,6 +29,7 @@ import vobject import django from django.conf import settings from django.contrib.auth.models import Group +from django.contrib.postgres.fields import ArrayField from django.core.exceptions import FieldDoesNotExist from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator @@ -1363,3 +1364,72 @@ class TimePeriodException(models.Model): def as_interval(self): '''Simplify insertion into IntervalSet''' return Interval(self.start_datetime, self.end_datetime) + + +class AgendaNotificationsSettings(models.Model): + agenda = models.OneToOneField(Agenda, on_delete=models.CASCADE, related_name='notifications_settings') + use_email_fields = models.BooleanField( + verbose_name=_('Use email addresses instead of roles'), default=False + ) + + almost_full_role = models.ForeignKey( + Group, + blank=True, + null=True, + default=None, + related_name='+', + verbose_name=_('Almost full event (90%)'), + on_delete=models.SET_NULL, + ) + almost_full_emails = ArrayField( + models.EmailField(), + blank=True, + null=True, + verbose_name=_('Almost full event (90%)'), + help_text=_('Enter a comma separated list of email addresses.'), + ) + + full_role = models.ForeignKey( + Group, + blank=True, + null=True, + default=None, + related_name='+', + verbose_name=_('Full event'), + on_delete=models.SET_NULL, + ) + full_emails = ArrayField( + models.EmailField(), + blank=True, + null=True, + verbose_name=_('Full event'), + help_text=_('Enter a comma separated list of email addresses.'), + ) + + cancelled_role = models.ForeignKey( + Group, + blank=True, + null=True, + default=None, + related_name='+', + verbose_name=_('Cancelled event'), + on_delete=models.SET_NULL, + ) + cancelled_emails = ArrayField( + models.EmailField(), + blank=True, + null=True, + verbose_name=_('Cancelled event'), + help_text=_('Enter a comma separated list of email addresses.'), + ) + + def __iter__(self): + '''Yield enabled settings verbose names and values''' + if self.use_email_fields: + fields = ['almost_full_emails', 'full_emails', 'cancelled_emails'] + else: + fields = ['almost_full_role', 'full_role', 'cancelled_role'] + for field in fields: + value = getattr(self, field) + if value: + yield (self._meta.get_field(field).verbose_name, getattr(self, field)) diff --git a/chrono/manager/forms.py b/chrono/manager/forms.py index ad0dc37..b790fcb 100644 --- a/chrono/manager/forms.py +++ b/chrono/manager/forms.py @@ -37,6 +37,7 @@ from chrono.agendas.models import ( TimePeriodExceptionSource, VirtualMember, Resource, + AgendaNotificationsSettings, WEEKDAYS_LIST, ) @@ -428,3 +429,28 @@ class AgendasImportForm(forms.Form): class AgendaDuplicateForm(forms.Form): label = forms.CharField(label=_('New label'), max_length=150, required=False) + + +class AgendaNotificationsForm(forms.ModelForm): + role_fields = ['almost_full_role', 'full_role', 'cancelled_role'] + email_fields = ['almost_full_emails', 'full_emails', 'cancelled_emails'] + + class Meta: + model = AgendaNotificationsSettings + fields = '__all__' + widgets = { + 'agenda': forms.HiddenInput(), + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + for field in self.email_fields: + self.fields[field].widget.attrs['class'] = 'notification-email-field' + self.fields[field].widget.attrs['size'] = 80 + + for field in self.role_fields: + self.fields[field].widget.attrs['class'] = 'notification-role-field' + + # TODO restrict roles to view and edit, and only if email is present + # or use sort on initial qs so view and edit are top diff --git a/chrono/manager/templates/chrono/manager_agenda_notifications_form.html b/chrono/manager/templates/chrono/manager_agenda_notifications_form.html new file mode 100644 index 0000000..67ea76b --- /dev/null +++ b/chrono/manager/templates/chrono/manager_agenda_notifications_form.html @@ -0,0 +1,48 @@ +{% extends "chrono/manager_agenda_view.html" %} +{% load i18n %} + +{% block breadcrumb %} +{{ block.super }} +{% trans "Notification settings" %} +{% endblock %} + +{% block appbar %} +

{% trans "Notification settings" %}

+{% endblock %} + +{% block content %} +
+ {% csrf_token %} + {{ form.as_p }} +
+ + {% trans 'Cancel' %} +
+
+ + +{% endblock %} diff --git a/chrono/manager/templates/chrono/manager_events_agenda_settings.html b/chrono/manager/templates/chrono/manager_events_agenda_settings.html index 7d004d7..2854eb5 100644 --- a/chrono/manager/templates/chrono/manager_events_agenda_settings.html +++ b/chrono/manager/templates/chrono/manager_events_agenda_settings.html @@ -62,4 +62,22 @@ +
+

{% trans "Notifications" %}

+
+
    +{% for setting, value in object.notifications_settings %} +
  • + {% blocktrans %} + {{ setting }}: {{ value }} will receive a notification. + {% endblocktrans %} +
  • +{% empty %} +{% trans "Notifications are disabled for this agenda." %} +{% endfor %} +
+{% trans 'Configure' %} +
+
+ {% endblock %} diff --git a/chrono/manager/urls.py b/chrono/manager/urls.py index c5642eb..02b7ef6 100644 --- a/chrono/manager/urls.py +++ b/chrono/manager/urls.py @@ -59,6 +59,11 @@ urlpatterns = [ views.agenda_import_events, name='chrono-manager-agenda-import-events', ), + url( + r'^agendas/(?P\d+)/notifications$', + views.agenda_notifications_settings, + name='chrono-manager-agenda-notifications-settings', + ), url( r'^agendas/(?P\d+)/events/(?P\d+)/$', views.event_view, diff --git a/chrono/manager/views.py b/chrono/manager/views.py index 15cea5a..f25e7bd 100644 --- a/chrono/manager/views.py +++ b/chrono/manager/views.py @@ -59,6 +59,7 @@ from chrono.agendas.models import ( TimePeriodExceptionSource, VirtualMember, Resource, + AgendaNotificationsSettings, ) from .forms import ( @@ -82,6 +83,8 @@ from .forms import ( ResourceEditForm, AgendaResourceForm, AgendaDuplicateForm, + AgendaDuplicateForm, + AgendaNotificationsForm, ) from .utils import import_site @@ -1210,6 +1213,21 @@ class AgendaImportEventsView(ManagedAgendaMixin, FormView): agenda_import_events = AgendaImportEventsView.as_view() +class AgendaNotificationsSettingsView(ManagedAgendaMixin, UpdateView): + template_name = 'chrono/manager_agenda_notifications_form.html' + model = AgendaNotificationsSettings + form_class = AgendaNotificationsForm + + def get_object(self): + try: + return self.agenda.notifications_settings + except AgendaNotificationsSettings.DoesNotExist: + return AgendaNotificationsSettings.objects.create(agenda=self.agenda) + + +agenda_notifications_settings = AgendaNotificationsSettingsView.as_view() + + class EventDetailView(ViewableAgendaMixin, DetailView): model = Event pk_url_kwarg = 'event_pk' -- 2.20.1