new olimpiada contact form (with student confirmations)
authorJan Szejko <janek37@gmail.com>
Fri, 1 Sep 2017 08:57:17 +0000 (10:57 +0200)
committerJan Szejko <janek37@gmail.com>
Fri, 1 Sep 2017 08:57:17 +0000 (10:57 +0200)
contact/models.py
edumed/contact_forms.py
edumed/templates/contact/olimpiada/form.html
edumed/templates/contact/olimpiada/mail_body.txt
edumed/templates/contact/olimpiada/student_mail_body.html
edumed/templates/contact/olimpiada/thanks.html
wtem/migrations/0010_auto__add_confirmation.py [new file with mode: 0644]
wtem/models.py
wtem/templates/wtem/confirmed.html [new file with mode: 0644]
wtem/urls.py
wtem/views.py

index 1c92610..5f031c2 100644 (file)
@@ -1,5 +1,4 @@
 # -*- coding: utf-8 -*-
-import yaml
 from django.db import models
 from django.utils.encoding import smart_unicode
 from django.utils.translation import ugettext_lazy as _
@@ -17,6 +16,7 @@ class Contact(models.Model):
     @staticmethod
     def pretty_print(value, for_html=False):
         if type(value) in (tuple, list, dict):
+            import yaml
             value = yaml.safe_dump(value, allow_unicode=True, default_flow_style=False)
             if for_html:
                 value = smart_unicode(value).replace(u" ", unichr(160))
index 7e7faca..2cb7dd9 100644 (file)
@@ -10,6 +10,7 @@ from django.core.validators import validate_email
 from django.template.loader import render_to_string
 from django.utils.translation import ugettext_lazy as _
 
+from wtem.models import Confirmation
 
 WOJEWODZTWA = (
     u'dolnośląskie',
@@ -227,7 +228,7 @@ class WTEMForm(ContactForm):
                 except ValidationError:
                     pass
                 else:
-                    send_mail(mail_subject, mail_body, 'edukacjamedialna@nowoczesnapolska.org.pl', [email],
+                    send_mail(mail_subject, mail_body, 'olimpiada@nowoczesnapolska.org.pl', [email],
                               fail_silently=True)
 
         return contact
@@ -239,7 +240,7 @@ class CommissionForm(forms.Form):
 
 
 class OlimpiadaForm(ContactForm):
-    disabled = True
+    disabled = False
     disabled_template = 'wtem/disabled_contact_form.html'
     form_tag = "olimpiada"
     form_title = u"Olimpiada Cyfrowa - Elektroniczny System Zgłoszeń"
@@ -254,6 +255,7 @@ class OlimpiadaForm(ContactForm):
     przewodniczacy = forms.CharField(label=u'Imię i nazwisko Przewodniczącego/Przewodniczącej', max_length=128)
     school = forms.CharField(label=u'Nazwa szkoły', max_length=255)
     school_address = forms.CharField(label=u'Adres szkoły', widget=forms.Textarea, max_length=1000)
+    school_wojewodztwo = forms.ChoiceField(label=u'Województwo', choices=WOJEWODZTWO_CHOICES)
     school_email = forms.EmailField(label=u'Adres e-mail szkoły', max_length=128)
     school_phone = forms.CharField(label=u'Numer telefonu szkoły', max_length=32)
     school_www = forms.URLField(label=u'Strona WWW szkoły', max_length=255, required=False)
@@ -307,17 +309,25 @@ class OlimpiadaForm(ContactForm):
         contact = super(OlimpiadaForm, self).save(request, formsets)
 
         mail_subject = render_to_string('contact/olimpiada/student_mail_subject.html').strip()
-        mail_body = render_to_string('contact/olimpiada/student_mail_body.html')
         for formset in formsets or []:
             if formset.prefix == 'student':
                 for f in formset.forms:
                     email = f.cleaned_data.get('email', None)
+                    try:
+                        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)
+                        confirmation = Confirmation.create(
+                            first_name=first_name, last_name=last_name, email=email, contact=contact)
+                    mail_body = render_to_string(
+                        'contact/olimpiada/student_mail_body.html', {'confirmation': confirmation})
                     try:
                         validate_email(email)
                     except ValidationError:
                         pass
                     else:
-                        send_mail(mail_subject, mail_body, 'edukacjamedialna@nowoczesnapolska.org.pl', [email],
+                        send_mail(mail_subject, mail_body, 'olimpiada@nowoczesnapolska.org.pl', [email],
                                   fail_silently=True)
 
         return contact
index c28925a..3a4f5be 100755 (executable)
@@ -22,7 +22,7 @@
     <form method="POST" action="." enctype="multipart/form-data" class="submit-form">
     {% csrf_token %}
     {% render_honeypot_field %}
-    <h3>Dane Przewodniczącego i szkoły zgłaszającej Uczestników:</h3>
+    <h3>Dane Przewodniczącego Komisji Szkolnej i szkoły zgłaszającej Uczestników:</h3>
     <table>
         {{ form.as_table }}
     </table>
index 0a63f92..3505dca 100755 (executable)
@@ -1,6 +1,7 @@
-Dziękujemy za rejestrację w Olimpiadzie Cyfrowej.
-Do udziału zostały zgłoszone następujące osoby:
-{% for student in contact.body.student %}* {{ student.first_name }} {{ student.last_name }}{% endfor %}
+Dziękujemy za rejestrację Komisji Szkolnej do Olimpiady Cyfrowej.
+Do udziału w Olimpiadzie zostały zgłoszone następujące osoby:
+{% for student in contact.body.student %}* {{ student.first_name }} {{ student.last_name }}
+{% endfor %}
 
 Każdy zgłoszony uczeń powinien otrzymać wiadomość z potwierdzeniem
 rejestracji. Prosimy upewnić się, czy potwierdzenie dotarło do każdego
@@ -8,12 +9,12 @@ ze zgłoszonych uczniów. W ten sposób zweryfikujemy, czy podane adresy
 są prawidłowe.
 
 Pierwszy etap Olimpiady (test on-line) odbędzie się
-15 listopada 2016 r. o godz. 10:00 i potrwa 90 minut.
+23 listopada 2017 r. godz. 10:00 i potrwa 90 minut.
 
-Wszystkie ogłoszenia związane z Turniejem będą publikowane na stronie https://olimpiadacyfrowa.pl.
+Wszystkie ogłoszenia związane z Olimpiadą będą publikowane na stronie https://olimpiadacyfrowa.pl.
 W razie pytań lub wątpliwości można kontaktować się z nami, pisząc na adres: olimpiada@nowoczesnapolska.org.pl.
 
-Z pozdrowieniami,
+Pozdrawiamy,
 
 Zespół Olimpiady Cyfrowej
 Fundacja Nowoczesna Polska
\ No newline at end of file
index eb3df3e..bcc32c9 100644 (file)
@@ -1,17 +1,19 @@
-Witaj,
+Dzień dobry,
 
 Przewodniczący Komisji Szkolnej właśnie zgłosił Cię do Olimpiady Cyfrowej.
 Cieszymy się, że chcesz wziąć w niej udział.
 
-Pierwszy etap Olimpiady (test on-line) odbędzie się 15 listopada 2016 r.
-o godz. 10:00 i potrwa 90 minut.
+Prosimy o potwierdzenie udziału przez kliknięcie w poniższy link:
+https://olimpiadacyfrowa.pl{{ confirmation.absolute_url }}
+
+Pierwszy etap Olimpiady (test on-line) odbędzie się 23 listopada 2017 r. o godz. 10:00 i potrwa 90 minut.
 
 Szczegółowe informacje na temat pierwszego etapu Olimpiady oraz wszystkie
 ogłoszenia z nią związane będą publikowane na stronie https://olimpiadacyfrowa.pl.
 W razie pytań lub wątpliwości możesz kontaktować się z nami, pisząc na adres
 olimpiada@nowoczesnapolska.org.pl.
 
-Z pozdrowieniami,
+Pozdrawiamy,
 
 Zespół Olimpiady Cyfrowej
-Fundacja Nowoczesna Polska
+Fundacja Nowoczesna Polska
\ No newline at end of file
index f05dd36..1e314d1 100755 (executable)
@@ -1,7 +1,7 @@
 {% extends "contact/thanks.html" %}
 
 {% block contact_form_description %}
-<p>Dziękujemy za rejestrację w Olimpiadzie Cyfrowej.</p>
+<p>Dziękujemy za rejestrację Komisji Szkolnej do Olimpiady Cyfrowej.</p>
 
 <p>Na adres e-mail Przewodniczącego/Przewodniczącej została wysłana wiadomość potwierdzająca
 rejestrację.</p>
diff --git a/wtem/migrations/0010_auto__add_confirmation.py b/wtem/migrations/0010_auto__add_confirmation.py
new file mode 100644 (file)
index 0000000..63f9d60
--- /dev/null
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'Confirmation'
+        db.create_table(u'wtem_confirmation', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('first_name', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('last_name', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('email', self.gf('django.db.models.fields.EmailField')(unique=True, max_length=100)),
+            ('contact', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contact.Contact'], null=True)),
+            ('key', self.gf('django.db.models.fields.CharField')(max_length=30)),
+            ('confirmed', self.gf('django.db.models.fields.BooleanField')(default=False)),
+        ))
+        db.send_create_signal(u'wtem', ['Confirmation'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'Confirmation'
+        db.delete_table(u'wtem_confirmation')
+
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        u'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        u'contact.contact': {
+            'Meta': {'ordering': "('-created_at',)", 'object_name': 'Contact'},
+            'body': ('jsonfield.fields.JSONField', [], {}),
+            'contact': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'form_tag': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'})
+        },
+        u'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'wtem.assignment': {
+            'Meta': {'object_name': 'Assignment'},
+            'exercises': ('jsonfield.fields.JSONField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'unique': 'True'})
+        },
+        u'wtem.attachment': {
+            'Meta': {'object_name': 'Attachment'},
+            'exercise_id': ('django.db.models.fields.IntegerField', [], {}),
+            'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wtem.Submission']"}),
+            'tag': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'})
+        },
+        u'wtem.confirmation': {
+            'Meta': {'object_name': 'Confirmation'},
+            'confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contact.Contact']", 'null': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '100'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'wtem.submission': {
+            'Meta': {'object_name': 'Submission'},
+            'answers': ('django.db.models.fields.CharField', [], {'max_length': '65536', 'null': 'True', 'blank': 'True'}),
+            'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contact.Contact']", 'null': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '100'}),
+            'end_time': ('django.db.models.fields.CharField', [], {'max_length': '5', 'null': 'True', 'blank': 'True'}),
+            'examiners': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}),
+            'key_sent': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'marks': ('jsonfield.fields.JSONField', [], {'default': '{}'})
+        }
+    }
+
+    complete_apps = ['wtem']
\ No newline at end of file
index 3947632..69337a0 100644 (file)
@@ -7,6 +7,7 @@ import json
 from django.db import models
 from django.contrib.auth.models import User
 from django.core.exceptions import ValidationError
+from django.core.urlresolvers import reverse
 from django.utils.translation import ugettext as _
 from jsonfield import JSONField
 
@@ -19,6 +20,12 @@ f.close()
 DEBUG_KEY = 'smerfetka159'
 
 
+def make_key(length):
+    return ''.join(
+        random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits)
+        for i in range(length))
+
+
 class Submission(models.Model):
     contact = models.ForeignKey(Contact, null=True)
     key = models.CharField(max_length=30, unique=True)
@@ -38,8 +45,7 @@ class Submission(models.Model):
     def generate_key(cls):
         key = ''
         while not key or key in [record['key'] for record in cls.objects.values('key')]:
-            key = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits)
-                          for i in range(30))
+            key = make_key(30)
         return key
 
     @classmethod
@@ -177,5 +183,30 @@ class Assignment(models.Model):
         return self.user.username + ': ' + ','.join(map(str, self.exercises))
 
 
+class Confirmation(models.Model):
+    first_name = models.CharField(max_length=100)
+    last_name = models.CharField(max_length=100)
+    email = models.EmailField(max_length=100, unique=True)
+    contact = models.ForeignKey(Contact, null=True)
+    key = models.CharField(max_length=30)
+    confirmed = models.BooleanField(default=False)
+
+    @classmethod
+    def create(cls, first_name, last_name, email, contact=None, key=None):
+        confirmation = cls(
+            contact=contact,
+            key=key if key else make_key(30),
+            first_name=first_name,
+            last_name=last_name,
+            email=email
+        )
+
+        confirmation.save()
+        return confirmation
+
+    def absolute_url(self):
+        return reverse('student_confirmation', args=(self.id, self.key))
+
+
 def exercise_checked_manually(exercise):
     return (exercise['type'] in ('open', 'file_upload')) or 'open_part' in exercise
diff --git a/wtem/templates/wtem/confirmed.html b/wtem/templates/wtem/confirmed.html
new file mode 100644 (file)
index 0000000..d08f9e9
--- /dev/null
@@ -0,0 +1,13 @@
+{% extends 'base_super.html' %}
+
+{% block body %}
+    <h1>{% include "wtem/title.html" %}</h1>
+    <h2>Potwierdzono udział</h2>
+
+    {% if was_confirmed %}
+        <p>Udział w Olimpiadzie Cyfrowej został już wcześniej potwierdzony.</p>
+    {% else %}
+        <p>Dziękujemy za potwierdzenie udziału w Olimpiadzie Cyfrowej!</p>
+    {% endif %}
+
+{% endblock %}
\ No newline at end of file
index 3e96c2e..58f39e3 100644 (file)
@@ -1,9 +1,10 @@
 # -*- coding: utf-8 -*-
 from django.conf.urls import patterns, url
-from .views import form, form_during
+from . import views
 
 urlpatterns = patterns(
     '',
-    url(r'^_test/(?P<key>.*)/$', form_during),
-    url(r'^(?P<key>.*)/$', form, name='wtem_form')
+    url(r'^potwierdzenie/(?P<id>.*)/(?P<key>.*)/$', views.confirmation, name='student_confirmation'),
+    url(r'^_test/(?P<key>.*)/$', views.form_during),
+    url(r'^(?P<key>.*)/$', views.form, name='wtem_form'),
 )
index 0696174..b5f7548 100644 (file)
@@ -4,10 +4,11 @@ from copy import deepcopy
 
 from django.conf import settings
 from django.http import HttpResponseForbidden
-from django.shortcuts import render
+from django.shortcuts import render, get_object_or_404
 from django.views.decorators.cache import never_cache
 from django.views.decorators.csrf import csrf_exempt
 
+from wtem.models import Confirmation
 from .forms import WTEMForm
 from .models import Submission, DEBUG_KEY, exercises
 
@@ -68,3 +69,12 @@ def form_during(request, key):
             return render(request, 'wtem/thanks.html', dict(end_time=submission.end_time))
         else:
             raise Exception
+
+
+def confirmation(request, id, key):
+    conf = get_object_or_404(Confirmation, id=id, key=key)
+    was_confirmed = conf.confirmed
+    if not was_confirmed:
+        conf.confirmed = True
+        conf.save()
+    return render(request, 'wtem/confirmed.html', {'confirmation': conf, 'was_confirmed': was_confirmed})