From: Jan Szejko Date: Wed, 24 Oct 2018 17:36:14 +0000 (+0200) Subject: add update forms + update deadline X-Git-Url: https://git.mdrn.pl/edumed.git/commitdiff_plain/408f2a8ab658eb038a9a98892a253761dfca20d4 add update forms + update deadline --- diff --git a/contact/forms.py b/contact/forms.py index cd5c918..2cc6971 100644 --- a/contact/forms.py +++ b/contact/forms.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from datetime import datetime + from django.contrib.sites.models import Site from django.core.exceptions import ValidationError from django.core.files.uploadedfile import UploadedFile @@ -8,11 +10,14 @@ from django import forms from django.template.loader import render_to_string from django.template import RequestContext from django.utils.translation import ugettext_lazy as _ +from django.utils import timezone +from edumed.utils import localtime_to_utc from . import mailing contact_forms = {} +update_forms = {} admin_list_width = 0 @@ -20,10 +25,14 @@ class ContactFormMeta(forms.Form.__class__): def __new__(cls, name, bases, attrs): global admin_list_width model = super(ContactFormMeta, cls).__new__(cls, name, bases, attrs) - assert model.form_tag not in contact_forms, 'Duplicate form_tag.' - if model.admin_list: - admin_list_width = max(admin_list_width, len(model.admin_list)) - contact_forms[model.form_tag] = model + if model.form_type == 'create': + assert model.form_tag not in contact_forms, 'Duplicate form_tag.' + if model.admin_list: + admin_list_width = max(admin_list_width, len(model.admin_list)) + contact_forms[model.form_tag] = model + elif model.form_type == 'update': + assert model.form_tag not in update_forms, 'Duplicate form_tag.' + update_forms[model.form_tag] = model return model @@ -32,17 +41,60 @@ class ContactForm(forms.Form): __metaclass__ = ContactFormMeta form_tag = None + form_type = 'create' old_form_tags = [] form_title = _('Contact form') submit_label = _('Submit') admin_list = None result_page = False mailing_field = None + form_formsets = {} required_css_class = 'required' contact = NotImplemented data_processing = None + disabled = False + ends_on = None + + confirmation_class = NotImplemented + + def __init__(self, *args, **kwargs): + self.instance = kwargs.pop('instance', None) + super(ContactForm, self).__init__(*args, **kwargs) + if not self.is_bound and self.instance: + # files are omitted (not necessary for now) + self.fields['contact'].initial = self.instance.contact + body = self.instance.body + for field, value in body.iteritems(): + if field in self.fields: + self.fields[field].initial = value + + @classmethod + def is_disabled(cls): + end_time = localtime_to_utc(datetime(*cls.ends_on)) if cls.ends_on else None + expired = end_time and end_time < timezone.now() + return cls.disabled or expired + + def formset_initial(self, prefix): + if not self.instance: + return None + return self.instance.body.get(prefix) + + def get_formsets(self, request=None): + request_data = {'data': request.POST, 'files': request.FILES} if request else {} + kwargs_instance = dict(request_data) + kwargs_instance['instance'] = self.instance + formsets = {} + for prefix, formset_class in self.form_formsets.iteritems(): + if getattr(formset_class, 'takes_instance', False): + kwargs = kwargs_instance + else: + kwargs = request_data + formsets[prefix] = formset_class( + prefix=prefix, initial=self.formset_initial(prefix), **kwargs) + return formsets + def get_dictionary(self, contact): site = Site.objects.get_current() return { @@ -68,11 +120,21 @@ class ContactForm(forms.Form): if sub_body: body.setdefault(f.form_tag, []).append(sub_body) - contact = Contact.objects.create( - body=body, - ip=request.META['REMOTE_ADDR'], - contact=self.cleaned_data['contact'], - form_tag=self.form_tag) + if self.instance: + contact = self.instance + contact.body = body + email_changed = contact.contact != self.cleaned_data['contact'] + contact.contact = self.cleaned_data['contact'] + assert contact.form_tag == self.form_tag + contact.save() + else: + contact = Contact.objects.create( + body=body, + ip=request.META['REMOTE_ADDR'], + contact=self.cleaned_data['contact'], + form_tag=self.form_tag) + email_changed = True + # not intended to be used with update forms for name, value in self.cleaned_data.items(): if isinstance(value, UploadedFile): attachment = Attachment(contact=contact, tag=name) @@ -97,25 +159,25 @@ class ContactForm(forms.Form): except ValidationError: pass else: - mail_subject = render_to_string([ - 'contact/%s/mail_subject.txt' % self.form_tag, - 'contact/mail_subject.txt', - ], dictionary, context).strip() - if self.result_page: - mail_body = render_to_string( - 'contact/%s/results_email.txt' % contact.form_tag, - { - 'contact': contact, - 'results': self.results(contact), - }, context) - else: - mail_body = render_to_string([ - 'contact/%s/mail_body.txt' % self.form_tag, - 'contact/mail_body.txt', - ], dictionary, context) - send_mail(mail_subject, mail_body, 'no-reply@%s' % site.domain, [contact.contact], fail_silently=True) - if self.mailing_field and self.cleaned_data[self.mailing_field]: - email = self.cleaned_data['contact'] - mailing.subscribe(email) + if not self.instance: + mail_subject = render_to_string([ + 'contact/%s/mail_subject.txt' % self.form_tag, + 'contact/mail_subject.txt', + ], dictionary, context).strip() + if self.result_page: + mail_body = render_to_string( + 'contact/%s/results_email.txt' % contact.form_tag, + { + 'contact': contact, + 'results': self.results(contact), + }, context) + else: + mail_body = render_to_string([ + 'contact/%s/mail_body.txt' % self.form_tag, + 'contact/mail_body.txt', + ], dictionary, context) + send_mail(mail_subject, mail_body, 'no-reply@%s' % site.domain, [contact.contact], fail_silently=True) + if email_changed and self.mailing_field and self.cleaned_data[self.mailing_field]: + mailing.subscribe(contact.contact) return contact diff --git a/contact/models.py b/contact/models.py index e7a2c4a..be16c4c 100644 --- a/contact/models.py +++ b/contact/models.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from django.db import models -from django.utils.encoding import smart_unicode +from django.db.models import permalink from django.utils.translation import ugettext_lazy as _ from jsonfield import JSONField from . import app_settings @@ -31,6 +31,13 @@ class Contact(models.Model): def __unicode__(self): return unicode(self.created_at) + @permalink + def update_url(self): + from contact.forms import update_forms, contact_forms + form_class = update_forms.get(self.form_tag, contact_forms.get(self.form_tag)) + confirmation = form_class.confirmation_class.objects.get(contact=self) + return 'edit_form', [], {'form_tag': self.form_tag, 'contact_id': self.id, 'key': confirmation.key} + class Attachment(models.Model): contact = models.ForeignKey(Contact) diff --git a/contact/urls.py b/contact/urls.py index 16033a1..823470b 100644 --- a/contact/urls.py +++ b/contact/urls.py @@ -5,6 +5,7 @@ from . import views urlpatterns = patterns( 'contact.views', url(r'^(?P[^/]+)/$', views.form, name='contact_form'), + url(r'^(?P[^/]+)/edit/(?P[0-9]+)/(?P[0-9a-zA-Z]+)/$', views.form, name='edit_form'), url(r'^(?P[^/]+)/thanks/$', views.thanks, name='contact_thanks'), url(r'^attachment/(?P\d+)/(?P[^/]+)/$', views.attachment, name='contact_attachment'), ) diff --git a/contact/views.py b/contact/views.py index 0d8c0bc..56bfd42 100644 --- a/contact/views.py +++ b/contact/views.py @@ -1,51 +1,54 @@ # -*- coding: utf-8 -*- from urllib import unquote -from datetime import datetime from django.contrib.auth.decorators import permission_required from django.http import Http404 from django.shortcuts import get_object_or_404, redirect, render -from django.utils import timezone from django.views.decorators.cache import never_cache from fnpdjango.utils.views import serve_file from honeypot.decorators import check_honeypot -from edumed.utils import localtime_to_utc -from .forms import contact_forms -from .models import Attachment +from .forms import contact_forms, update_forms +from .models import Attachment, Contact @check_honeypot @never_cache -def form(request, form_tag, force_enabled=False): +def form(request, form_tag, force_enabled=False, contact_id=None, key=None): + update = bool(contact_id and key) try: - form_class = contact_forms[form_tag] + if update and form_tag in update_forms: + form_class = update_forms[form_tag] + else: + form_class = contact_forms[form_tag] except KeyError: raise Http404 if not (force_enabled and request.user.is_superuser): - disabled = getattr(form_class, 'disabled', False) - end_tuple = getattr(form_class, 'ends_on', None) - end_time = localtime_to_utc(datetime(*end_tuple)) if end_tuple else None - expired = end_time and end_time < timezone.now() - if disabled or expired: + if form_class.is_disabled(): template = getattr(form_class, 'disabled_template', None) if template: return render(request, template, {'title': form_class.form_title}) raise Http404 + if contact_id: + contact = get_object_or_404(Contact, id=contact_id, form_tag=form_tag) + if form_tag != 'olimpiada': + raise Http404 + confirmation = form_class.confirmation_class.objects.get(contact=contact) + if key != confirmation.key: + raise Http404 + else: + contact = None if request.method == 'POST': - form = form_class(request.POST, request.FILES) + form = form_class(request.POST, request.FILES, instance=contact) else: - form = form_class(initial=request.GET) - formset_classes = getattr(form, 'form_formsets', {}) + form = form_class(initial=request.GET, instance=contact) if request.method == 'POST': - formsets = { - prefix: formset_class(request.POST, request.FILES, prefix=prefix) - for prefix, formset_class in formset_classes.iteritems()} + formsets = form.get_formsets(request) if form.is_valid() and all(formset.is_valid() for formset in formsets.itervalues()): form.save(request, formsets.values()) return redirect('contact_thanks', form_tag) else: - formsets = {prefix: formset_class(prefix=prefix) for prefix, formset_class in formset_classes.iteritems()} + formsets = form.get_formsets() return render( request, ['contact/%s/form.html' % form_tag, 'contact/form.html'], diff --git a/edumed/contact_forms.py b/edumed/contact_forms.py index e7af73c..7fe13c9 100644 --- a/edumed/contact_forms.py +++ b/edumed/contact_forms.py @@ -60,6 +60,11 @@ class WTEMStudentForm(forms.Form): return email +class WTEMStudentUpdateForm(WTEMStudentForm): + def clean_email(self): + return self.cleaned_data['email'] + + class NonEmptyBaseFormSet(BaseFormSet): """ Won't allow formset_factory to be submitted with no forms @@ -71,10 +76,9 @@ class NonEmptyBaseFormSet(BaseFormSet): forms.ValidationError(u"Proszę podać dane przynajmniej jednej osoby.") -class StudentFormset(forms.formsets.formset_factory(WTEMStudentForm, formset=NonEmptyBaseFormSet)): - def clean(self): +class StudentBaseFormSet(NonEmptyBaseFormSet): + def check_unique_emails(self): from django.forms.util import ErrorList - super(StudentFormset, self).clean() emails = set() for form in self.forms: @@ -82,12 +86,33 @@ class StudentFormset(forms.formsets.formset_factory(WTEMStudentForm, formset=Non continue if form.cleaned_data: email = form.cleaned_data['email'] + instance = getattr(self, 'instance', None) if email in emails: errors = form._errors.setdefault('email', ErrorList()) errors.append(u'Każdy zgłoszony uczeń powinien mieć własny adres email') + elif instance and Confirmation.objects.exclude(contact=instance).filter(email=email).exists(): + errors = form._errors.setdefault('email', ErrorList()) + errors.append(u'Uczeń z tym adresem już został zgłoszony w innym formularzu.') else: emails.add(email) + def clean(self): + super(StudentBaseFormSet, self).clean() + self.check_unique_emails() + + +class StudentUpdateFormSet(StudentBaseFormSet): + takes_instance = True + + def __init__(self, *args, **kwargs): + instance = kwargs.pop('instance', None) + super(StudentUpdateFormSet, self).__init__(*args, **kwargs) + self.instance = instance + + def clean(self): + super(StudentUpdateFormSet, self).clean() + self.check_unique_emails() + class CommissionForm(forms.Form): name = forms.CharField(label=u'Imię i nazwisko Członka Komisji', max_length=128) @@ -95,18 +120,19 @@ class CommissionForm(forms.Form): class OlimpiadaForm(ContactForm): - ends_on = (2018, 11, 14, 0, 5) + ends_on = (2018, 11, 1, 0, 5) disabled_template = 'wtem/disabled_contact_form.html' form_tag = "olimpiada" - old_form_tags = ["olimpiada-2016"] + old_form_tags = ["olimpiada-2016", "olimpiada-2017"] form_title = u"Olimpiada Cyfrowa - Elektroniczny System Zgłoszeń" submit_label = u"Wyślij zgłoszenie" admin_list = ['nazwisko', 'school'] form_formsets = { - 'student': StudentFormset, + 'student': forms.formsets.formset_factory(WTEMStudentForm, formset=StudentBaseFormSet), 'commission': forms.formsets.formset_factory(CommissionForm), } mailing_field = 'zgoda_newsletter' + confirmation_class = TeacherConfirmation contact = forms.EmailField(label=u'Adres e-mail Przewodniczącego/Przewodniczącej', max_length=128) przewodniczacy = forms.CharField(label=u'Imię i nazwisko Przewodniczącego/Przewodniczącej', max_length=128) @@ -187,7 +213,7 @@ class OlimpiadaForm(ContactForm): email = f.cleaned_data.get('email', None) if email: try: - Confirmation.objects.get(email=email) + confirmation = Confirmation.objects.get(email=email) except Confirmation.DoesNotExist: first_name = f.cleaned_data.get('first_name', None) last_name = f.cleaned_data.get('last_name', None) @@ -195,4 +221,16 @@ class OlimpiadaForm(ContactForm): confirmation = Confirmation.create( first_name=first_name, last_name=last_name, email=email, contact=contact) confirmation.send_mail() + else: + confirmation.first_name = f.cleaned_data.get('first_name', None) + confirmation.last_name = f.cleaned_data.get('last_name', None) + confirmation.save() return contact + + +class OlimpiadaUpdateForm(OlimpiadaForm): + form_type = 'update' + form_formsets = { + 'student': forms.formsets.formset_factory(WTEMStudentUpdateForm, formset=StudentUpdateFormSet), + 'commission': forms.formsets.formset_factory(CommissionForm), + } diff --git a/wtem/templates/wtem/teacher_confirmed.html b/wtem/templates/wtem/teacher_confirmed.html index bff9452..a4dfaaa 100644 --- a/wtem/templates/wtem/teacher_confirmed.html +++ b/wtem/templates/wtem/teacher_confirmed.html @@ -12,4 +12,6 @@

Dziękujemy za potwierdzenie zgłoszenia w Olimpiadzie Cyfrowej!

{% endif %} +

Pod tym linkiem możesz poprawić lub uzupełnić zgłoszenie.

+ {% endblock %} \ No newline at end of file diff --git a/wtem/views.py b/wtem/views.py index baacf44..d786846 100644 --- a/wtem/views.py +++ b/wtem/views.py @@ -171,4 +171,11 @@ def teacher_confirmation(request, id, key): if not was_confirmed: conf.confirmed = True conf.save() - return render(request, 'wtem/teacher_confirmed.html', {'confirmation': conf, 'was_confirmed': was_confirmed}) + from contact.forms import contact_forms + form_class = contact_forms['olimpiada'] + if not form_class.is_disabled(): + pass + return render(request, 'wtem/teacher_confirmed.html', { + 'confirmation': conf, + 'was_confirmed': was_confirmed, + })