various fixes and updates
authorJan Szejko <janek37@gmail.com>
Fri, 27 Oct 2017 12:15:41 +0000 (14:15 +0200)
committerJan Szejko <janek37@gmail.com>
Fri, 27 Oct 2017 12:15:41 +0000 (14:15 +0200)
18 files changed:
.gitignore
catalogue/static/catalogue/css/layout.css [deleted file]
catalogue/static/catalogue/css/layout.scss [deleted file]
edumed/static/catalogue/css/layout.css
edumed/static/catalogue/css/layout.scss
edumed/static/css/base.css
edumed/static/css/base.scss
wtem/forms.py
wtem/templates/wtem/email_key.txt
wtem/templates/wtem/exercises/edumed_wybor.html
wtem/templates/wtem/exercises/edumed_wybor_auto.html [new file with mode: 0644]
wtem/templates/wtem/exercises/exercise_no.html
wtem/templates/wtem/main_before.html
wtem/templates/wtem/single.html
wtem/templates/wtem/start.html [new file with mode: 0644]
wtem/templates/wtem/thanks_single.html
wtem/urls.py
wtem/views.py

index d4d845a..7bbaa41 100644 (file)
@@ -39,5 +39,6 @@ TAGS
 media
 search_index
 .sass-cache
+*.css.map
 
 exercises.json
\ No newline at end of file
diff --git a/catalogue/static/catalogue/css/layout.css b/catalogue/static/catalogue/css/layout.css
deleted file mode 100644 (file)
index acf699d..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-@charset "UTF-8";
-.box-button {
-  background-color: #ed7831;
-  border-radius: 0.9375em; }
-  .box-button .dl-button {
-    color: white;
-    padding: 1.0625em 0.75em 1.0625em 0.75em; }
-
-.dl-button {
-  color: #363a3e;
-  display: block;
-  font-weight: bold;
-  text-align: center;
-  text-transform: uppercase;
-  font-size: .9em; }
-
-.dl-button:after {
-  content: " ↓"; }
-
-#main-bar section.button {
-  margin-bottom: 1.0625em; }
-
-#sidebar {
-  position: absolute;
-  right: 0;
-  top: 0;
-  width: 13.75em;
-  color: #363a3e; }
-  #sidebar section {
-    margin-bottom: 1.0625em; }
-    #sidebar section h1 {
-      margin: 0; }
-    #sidebar section h1.realisation {
-      font-weight: normal; }
-  #sidebar .section {
-    border-top: 1px solid #c9ccce;
-    padding-top: 1.0625em; }
-  #sidebar .section-minor {
-    border-top: 1px solid #c9ccce;
-    padding-top: 1.0625em; }
-    #sidebar .section-minor h1 {
-      font-weight: normal;
-      font-size: 1em; }
-  #sidebar .section-micro {
-    font-size: .8em;
-    color: #888;
-    border-top: 1px solid #c9ccce;
-    padding-top: 1.0625em; }
-    #sidebar .section-micro h1 {
-      font-weight: normal;
-      font-size: 1em; }
-    #sidebar .section-micro .link-list a {
-      color: #888; }
-  #sidebar section:first-child {
-    border-top: 0;
-    padding-top: 0; }
-
-#main-bar {
-  width: 40em; }
-  #main-bar .top-link {
-    float: right; }
-  #main-bar .box {
-    background-color: #d4d6d8;
-    border-radius: 0.9375em;
-    padding: 1.0625em; }
-    #main-bar .box h1 {
-      font-size: 1em;
-      text-transform: uppercase; }
-    #main-bar .box p {
-      margin: 0; }
-    #main-bar .box .box-icon {
-      margin: -.2em 0 -.2em 1em;
-      float: right;
-      text-align: center; }
-
-.lesson-footer {
-  clear: both;
-  border-top: 1px solid #777;
-  margin-top: 2em;
-  padding-top: 1em; }
-  .lesson-footer .section-info {
-    text-align: center; }
-  .lesson-footer .previous-lesson {
-    float: left; }
-  .lesson-footer .next-lesson {
-    float: right; }
diff --git a/catalogue/static/catalogue/css/layout.scss b/catalogue/static/catalogue/css/layout.scss
deleted file mode 100755 (executable)
index 230e4aa..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-$px: .0625em;
-$new_black: #363a3e;
-
-.box-button {
-    background-color: #ed7831;
-    border-radius: 15*$px;
-    .dl-button {
-        color: white;
-        padding: 17*$px 12*$px 17*$px 12*$px;
-    }
-}
-.dl-button {
-    color: $new_black;
-    display: block;
-    font-weight: bold;
-    text-align: center;
-    text-transform: uppercase;
-    font-size: .9em;
-}
-.dl-button:after {
-    content: " ↓";
-}
-
-#main-bar section.button {
-    margin-bottom: 17*$px;
-}
-
-#sidebar {
-    position: absolute;
-    right: 0;
-    top: 0;
-    width: 220*$px;
-    color: $new_black;
-
-    section {
-        margin-bottom: 17*$px;
-        h1 {
-            margin: 0;
-        }
-        h1.realisation {
-            font-weight: normal;
-        }
-    }
-
-    .section {
-        border-top: 1px solid #c9ccce;
-        padding-top: 17*$px;
-    }
-    .section-minor {
-        border-top: 1px solid #c9ccce;
-        padding-top: 17*$px;
-        h1 {
-            font-weight: normal;
-            font-size: 1em;
-        }
-    }
-    .section-micro {
-        font-size: .8em;
-        color: #888;
-        border-top: 1px solid #c9ccce;
-        padding-top: 17*$px;
-        h1 {
-            font-weight: normal;
-            font-size: 1em;
-        }
-        .link-list a {
-            color: #888;
-        }
-    }
-    section:first-child {
-        border-top: 0;
-        padding-top: 0;
-    }
-}
-#main-bar {
-    width: 640*$px;
-
-    .top-link {
-        float:right;
-    }
-
-    .box {
-        background-color: #d4d6d8;
-        border-radius: 15*$px;
-        padding: 17*$px;
-        h1 {
-            font-size: 1em;
-            text-transform: uppercase;
-        }
-        p {
-            margin: 0;
-        }
-        .box-icon {
-            margin: -.2em 0 -.2em 1em;
-            float: right;
-            text-align: center;
-        }
-    }
-}
-
-.lesson-footer {
-    clear: both;
-    border-top: 1px solid #777;
-    margin-top: 2em;
-    padding-top: 1em;
-
-    .section-info {
-        text-align: center;
-    }
-    .previous-lesson {float: left;}
-    .next-lesson {float: right;}
-}
index acf699d..6a0746d 100644 (file)
@@ -2,11 +2,11 @@
 .box-button {
   background-color: #ed7831;
   border-radius: 0.9375em; }
-  .box-button .dl-button {
+  .box-button .dl-button, .box-button .nice-button {
     color: white;
     padding: 1.0625em 0.75em 1.0625em 0.75em; }
 
-.dl-button {
+.dl-button, .nice-button {
   color: #363a3e;
   display: block;
   font-weight: bold;
@@ -84,3 +84,5 @@
     float: left; }
   .lesson-footer .next-lesson {
     float: right; }
+
+/*# sourceMappingURL=layout.css.map */
index 230e4aa..671e9f0 100755 (executable)
@@ -1,15 +1,14 @@
 $px: .0625em;
 $new_black: #363a3e;
-
 .box-button {
     background-color: #ed7831;
     border-radius: 15*$px;
-    .dl-button {
+    .dl-button, .nice-button {
         color: white;
         padding: 17*$px 12*$px 17*$px 12*$px;
     }
 }
-.dl-button {
+.dl-button, .nice-button {
     color: $new_black;
     display: block;
     font-weight: bold;
index 99182a2..292a122 100644 (file)
@@ -250,5 +250,21 @@ footer.main {
     margin: 1em 0; }
 
 .flatpage img {
-  border: 0.3125em solid #eeeeee;
+  border: 0.3125em solid #eee;
   margin: 1.3em; }
+
+ul.messages {
+  list-style: none; }
+
+.messages .error {
+  top: 20px;
+  left: 20px;
+  border: 1px solid black;
+  background: red;
+  border-radius: 0.938em;
+  padding: 5px 2px;
+  color: white;
+  text-align: center;
+  font-weight: bold; }
+
+/*# sourceMappingURL=base.css.map */
index 88d2033..d40f461 100644 (file)
@@ -279,3 +279,19 @@ footer.main {
         margin: 1.3em;
     }
 }
+
+ul.messages {
+    list-style: none;
+}
+
+.messages .error {
+    top: 20px;
+    left: 20px;
+    border: 1px solid black;
+    background: red;
+    border-radius: 0.938em;
+    padding: 5px 2px;
+    color: white;
+    text-align: center;
+    font-weight: bold;
+}
index 15c6503..a434854 100644 (file)
@@ -49,11 +49,17 @@ class WTEMSingleForm(forms.ModelForm):
         submission = self.instance
         answers = submission.get_answers()
         posted_answers = json.loads(self.cleaned_data['answers'])
-        assert type(posted_answers) == dict, 'answers not dict'
-        assert len(posted_answers) == 1, 'answers not single'
-        exercise_id = posted_answers.keys()[0]
+        if type(posted_answers) != dict:
+            raise ValueError('answers not dict')
+        if len(posted_answers) != 1:
+            raise ValueError('answers not single')
+        exercise_id, answer = posted_answers.items()[0]
+        # multipost
+        if answers.get(exercise_id) == answer:
+            return
         i, exercise = submission.current_exercise()
-        assert exercise_id == str(exercise['id']), 'wrong exercise id'
+        if exercise_id != str(exercise['id']):
+            raise ValueError('wrong exercise id')
         for answer in posted_answers.values():
             answers[exercise_id] = answer
         submission.answers = json.dumps(answers)
index aaa86ca..76e629a 100644 (file)
@@ -1,16 +1,16 @@
-Poniżej znajduje się wygenerowany specjalnie dla Ciebie link, pod którym będziesz mógł/mogła rozwiązać zadania I etapu Olimpiady Cyfrowej:
+Dzień dobry,
+Poniżej znajduje się wygenerowany specjalnie dla Ciebie link, pod którym znajdziesz zadania I etapu Olimpiady Cyfrowej:
 
-https://olimpiadacyfrowa.pl{% url 'form_single' submission_id=submission.id key=submission.key %}
+https://olimpiadacyfrowa.pl{% url 'wtem_start' submission_id=submission.id key=submission.key %}
 
-I etap Olimpiady odbędzie się we wtorek 15 listopada o godz. 10:00 w sali wyznaczonej przez Komisję Szkolną. Na rozwiązanie testu będziesz mieć ok. 90 min. Aby rozwiązać test, potrzebny Ci będzie komputer ze stabilnym łączem internetowym oraz zainstalowaną i zaktualizowaną przeglądarką.
+I etap Olimpiady odbędzie się w czwartek 23 listopada o godz. 10:00 w sali wyznaczonej przez Twoją Komisję Szkolną. Test będzie się składał z 50 pytań, które otrzymasz w losowej kolejności. Po udzieleniu odpowiedzi na dane pytanie nie ma możliwości powrotu do niego i zmiany odpowiedzi. Na rozwiązanie całego testu będziesz mieć 60 minut.
 
-Każdy uczestnik otrzymał indywidualny link, pod którym może rozwiązywać zadania samodzielnie. Wszelkie działania mogące świadczyć o tym, że uczestnik nie rozwiązuje testu samodzielnie, np. wchodzenie na stronę z zadaniami przez więcej niż jedną osobę, będą przez nas śledzone i poskutkują dyskwalifikacją.
+Aby rozwiązać test, potrzebny Ci będzie komputer ze stabilnym łączem internetowym oraz zainstalowaną i zaktualizowaną przeglądarką.
 
-W razie dodatkowych pytań możesz kontaktować się z nami pod adresem olimpiada@nowoczesnapolska.org.pl lub numerem telefonu +48 515-502-666.
+Każdy uczestnik i uczestniczka otrzymuje indywidualny link, pod którym znajdzie zadania I etapu Olimpiady Cyfrowej. Przypominamy, że zgodnie z Regulaminem, każdy zobowiązany jest do samodzielnego rozwiązania testu. Wszelkie działania świadczące o niesamodzielnej pracy poskutkują dyskwalifikacją.
 
-Przypominamy, że główną nagrodą w Olimpiadzie Cyfrowej jest indeks Collegium Civitas na dowolny kierunek studiów licencjackich oraz zwolnienie z opłat czesnego przez rok.
+W razie dodatkowych pytań możesz kontaktować się z nami pod adresem olimpiada@nowoczesnapolska.org.pl lub numerem telefonu +48 22 465 15 35.
 
 Powodzenia!
-
 Zespół Olimpiady Cyfrowej
-fundacja Nowoczesna Polska
\ No newline at end of file
+Fundacja Nowoczesna Polska
\ No newline at end of file
index edd9dad..2ac1e2c 100644 (file)
@@ -8,22 +8,16 @@
                 {{para}}
             </p>
         {% endfor %}
-        {% if exercise.points %}
-            <span class="instruction">
-                {% if exercise.answer|length == 1 %}
-                    Tylko jedna odpowiedź jest prawidłowa.
-                {% else %}
-                    Zaznacz wszystkie prawidłowe odpowiedzi.
-                {% endif %}
-            </span>
-        {% endif %}
+        <span class="instruction">
+            Zaznacz każdą prawidłową odpowiedź (jedną lub więcej).
+        </span>
     </div>
     {% endautoescape %}
     <div class="question" data-no="1">
         <ol class="lista num">
             {% for option in exercise.options %}
                 <li class="question-piece" data-name="{{option.id}}">
-                    <input type="{% if exercise.answer|length == 1 %}radio{% else %}checkbox{% endif %}" name="{% if exercise.answer|length == 1 %}e{{no}}{% else %}e{{no}}_{{option.id}}{% endif %}" id="e{{no}}_{{option.id}}"{% if option.id|stringformat:"s" in exercise.saved_answer.closed_part %} checked="checked"{% endif %}>
+                    <input type="checkbox" name="e{{no}}_{{option.id}}" id="e{{no}}_{{option.id}}"{% if option.id|stringformat:"s" in exercise.saved_answer.closed_part %} checked="checked"{% endif %}>
                     <label for="e{{no}}_{{option.id}}">{{option.text}}</label>
                 </li>
             {% endfor %}
diff --git a/wtem/templates/wtem/exercises/edumed_wybor_auto.html b/wtem/templates/wtem/exercises/edumed_wybor_auto.html
new file mode 100644 (file)
index 0000000..edd9dad
--- /dev/null
@@ -0,0 +1,46 @@
+<div class="exercise exercise-wtem wybor" data-type="wybor" data-id="{{exercise.id}}">
+
+    {% include "wtem/exercises/exercise_no.html" %}
+    {% autoescape off %}
+    <div class="description">
+        {% for para in exercise.description %}
+            <p class="paragraph">
+                {{para}}
+            </p>
+        {% endfor %}
+        {% if exercise.points %}
+            <span class="instruction">
+                {% if exercise.answer|length == 1 %}
+                    Tylko jedna odpowiedź jest prawidłowa.
+                {% else %}
+                    Zaznacz wszystkie prawidłowe odpowiedzi.
+                {% endif %}
+            </span>
+        {% endif %}
+    </div>
+    {% endautoescape %}
+    <div class="question" data-no="1">
+        <ol class="lista num">
+            {% for option in exercise.options %}
+                <li class="question-piece" data-name="{{option.id}}">
+                    <input type="{% if exercise.answer|length == 1 %}radio{% else %}checkbox{% endif %}" name="{% if exercise.answer|length == 1 %}e{{no}}{% else %}e{{no}}_{{option.id}}{% endif %}" id="e{{no}}_{{option.id}}"{% if option.id|stringformat:"s" in exercise.saved_answer.closed_part %} checked="checked"{% endif %}>
+                    <label for="e{{no}}_{{option.id}}">{{option.text}}</label>
+                </li>
+            {% endfor %}
+        </ol>
+    </div>
+
+    {% if exercise.open_part %}
+    <div class="open_part">
+        <div class="description">
+            {% for para in exercise.open_part %}
+                <p class="paragraph">
+                    {{para}}
+                </p>
+            {% endfor %}
+        </div>
+        <textarea style="width: 100%; margin-top:10px;" rows="{{exercise.open_part_rows|default:10}}">{{ exercise.saved_answer.open_part }}</textarea>
+    </div>
+    {% endif %}
+
+</div>
index aecdd2b..0bd9c6d 100644 (file)
@@ -1,3 +1,3 @@
 {% if not exercise.continuation %}
-    <h3>Zadanie {{ no }}{# {{exercise.id_show|default:exercise.id}} #}{# ({{ exercise.max_points }} pkt)#}</h3>
+    <h3>Zadanie {{ no }}/{{ exercise_count }}{# {{exercise.id_show|default:exercise.id}} #}{# ({{ exercise.max_points }} pkt)#}</h3>
 {% endif %}
\ No newline at end of file
index 4ede52b..b3c2b23 100644 (file)
@@ -4,6 +4,12 @@
 
 <h1>{% include "wtem/title.html" %}</h1>
 
-<p>I etap: 15 listopada, 10:00. Czas trwania: ok. 90 minut.</p>
+<p>Witamy w I etapie Olimpiady Cyfrowej.</p>
+<p>Test składa z 50 pytań, które otrzymasz w losowej kolejności. Po udzieleniu odpowiedzi na dane pytanie nie ma możliwości powrotu do niego i zmiany odpowiedzi. Uwaga: część pytań ma jedną, a część kilka poprawnych odpowiedzi.</p>
+<p>Test rozpocznie się 23 listopada o godzinie 10:00 i potrwa do 11:00.</p>
+
+<p>Powodzenia!</p>
+<p>Zespół Olimpiady Cyfrowej<br>
+Fundacja Nowoczesna Polska</p>
 
 {% endblock %}
\ No newline at end of file
index f4b9c19..3380145 100644 (file)
@@ -8,7 +8,6 @@
 {% endblock %}
 
 
-
 {% block body %}
 <style>
     .wtem-open-field {
     .wtem-open-field textarea {
         margin-top:0;
     }
-    .wtem-fixed-info {
-        top: 20px;
-        left: 20px;
-        border: 1px solid black;
-        background: #16a487;
-        border-radius: 0.938em;
-        padding: 5px 2px;
-        color: white;
-        text-align: center;
-        font-weight: bold;
-    }
     .wtem-items-inline li {
         display: inline-block;
     }
         font-style: italic;
         font-size: .9em;
     }
-
 </style>
 
-<h1>{% include "wtem/title.html" %}</h1>
-<div class="wtem-fixed-info">Rozwiązania można wysyłać do godziny {{end_time|default:"11:30"}}. <strong>Nie czekaj na ostatnią chwilę!</strong></div>
+{% if messages %}
+    <ul class="messages">
+        {% for message in messages %}
+            <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
+        {% endfor %}
+    </ul>
+{% endif %}
 
-<p>Witamy w I etapie Olimpiady Cyfrowej. Na rozwiązanie zadań masz czas do godz. {{end_time|default:"11:30"}}. Test składa się z 30 pytań.</p>
-
-<p>Wszelkie aktualności dotyczące Olimpiady możesz znaleźć <a href="/" target="_info">tutaj</a>.</p>
-
-<p>Powodzenia!<br/>
-Zespół Olimpiady Cyfrowej, fundacja Nowoczesna Polska</p>
+<h1>{% include "wtem/title.html" %}</h1>
 
-<form method="post" enctype="multipart/form-data">
+<form method="post">
 
 {% cache 300 wtem exercise.id %}
 {% with 'wtem/exercises/'|add:exercise.type|add:'.html' as template_name %}
@@ -63,18 +50,16 @@ Zespół Olimpiady Cyfrowej, fundacja Nowoczesna Polska</p>
 <hr/>
 <input type="hidden" name="answers" value=""/>
 
-<p style="text-align:center; margin-top:20px;">Sprawdź jeszcze raz wszystkie swoje odpowiedzi, a następnie wyślij je do nas, klikając w poniższy przycisk:<br/><br/>
-<input type="submit" value="Wyślij moje odpowiedzi" style="display: block; margin: auto;"/>
+<p style="text-align:center; margin-top:20px;">
+<input type="submit" value="{% if no < exercise_count %}Kolejne pytanie{% else %}Zakończ test{% endif %}" style="display: block; margin: auto;"/>
 <br/>
 <span class="wtem_spinner">
-    <span>Wysyłanie rozwiązań w toku...</span>
+    <span>Wysyłanie rozwiązania w toku...</span>
     <img src="{% static 'wtem/spinner.gif' %}"/>
-    <span>Spróbuj jeszcze raz, jeśli wysyłanie trwa dłużej niż kilka minut.</span>
+    <span>Spróbuj jeszcze raz, jeśli wysyłanie trwa dłużej niż kilka chwil.</span>
 </span>
 </p>
 
-<div class="wtem-fixed-info" style="margin-top:15px;">Rozwiązania można wysyłać do godziny {{end_time|default:"11:30"}}. <strong>Nie czekaj na ostatnią chwilę!</strong></div>
-
 </form>
 
 {% endblock %}
diff --git a/wtem/templates/wtem/start.html b/wtem/templates/wtem/start.html
new file mode 100644 (file)
index 0000000..c938b76
--- /dev/null
@@ -0,0 +1,20 @@
+{% extends 'base_super.html' %}
+
+{% block body %}
+
+    <h1>{% include "wtem/title.html" %}</h1>
+
+    <p>Witamy w I etapie Olimpiady Cyfrowej.</p>
+
+    <p>Test składa z {{ exercise_count }} pytań, które otrzymasz w losowej kolejności.
+        Po udzieleniu odpowiedzi na dane pytanie nie ma możliwości powrotu do niego i zmiany odpowiedzi.
+        Uwaga: część pytań ma jedną, a część kilka poprawnych odpowiedzi.</p>
+    <p>Test rozpoczyna się o godzinie 10:00 i trwa do 11:00.</p>
+
+    <p>Powodzenia!</p>
+    <p>Zespół Olimpiady Cyfrowej<br>
+    Fundacja Nowoczesna Polska</p>
+
+    <p class="box-button" style="max-width: 10em;"><a href="{% url 'wtem_form' submission_id=submission.id key=submission.key %}" class="nice-button">Start</a></p>
+
+{% endblock %}
index b403ae3..14ce51c 100644 (file)
@@ -7,9 +7,9 @@
 <p>Dziękujemy za udział w I etapie Olimpiady Cyfrowej.
 Twoja praca została wysłana i poprawnie przyjęta przez system.</p>
 
-<p>Do końca listopada otrzymasz e-mail z wynikami I etapu. Informacja o uzyskanych przez Ciebie punktach zostanie również przesłana do osoby, która zgłosiła Twój udział w Olimpiadzie.</p>
+<p>Do końca listopada otrzymasz e-mail z wynikami I etapu. Informacja o uzyskanych przez Ciebie punktach zostanie również przesłana do Przewodniczącego Komisji Szkolnej, który zgłosił Twój udział w Olimpiadzie.</p>
 
-<p>Aktualności związane z Olimpiadą możesz sprawdzać <a href="/">tutaj</a>. W razie dodatkowych pytań możesz kontaktować się z nami pod adresem olimpiada@nowoczesnapolska.org.pl lub numerem telefonu +48 515-502-666.</p>
+<p>Aktualności związane z Olimpiadą możesz sprawdzać <a href="/">tutaj</a>. W razie dodatkowych pytań możesz kontaktować się z nami pod adresem olimpiada@nowoczesnapolska.org.pl lub numerem telefonu <a href="tel:+48224651535">+48 22 465 15 35</a>.</p>
 
 <p>Zespół Olimpiady Cyfrowej<br>
 fundacja Nowoczesna Polska</p>
index f7a5e99..e498743 100644 (file)
@@ -6,6 +6,6 @@ urlpatterns = patterns(
     '',
     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'),
-    url(r'^(?P<submission_id>.*)/(?P<key>.*)/$', views.form_single, name='form_single'),
+    url(r'^(?P<submission_id>[^/]*)/(?P<key>[^/]*)/$', views.form, name='wtem_form'),
+    url(r'^(?P<submission_id>[^/]*)/(?P<key>[^/]*)/start/$', views.start, name='wtem_start'),
 )
index 6366e53..d47a2be 100644 (file)
@@ -3,6 +3,7 @@ import json
 from copy import deepcopy
 
 from django.conf import settings
+from django.contrib import messages
 from django.core.urlresolvers import reverse
 from django.http import HttpResponseForbidden
 from django.http.response import HttpResponseRedirect
@@ -14,24 +15,24 @@ from wtem.models import Confirmation
 from .forms import WTEMForm, WTEMSingleForm
 from .models import Submission, DEBUG_KEY, exercises, CompetitionState
 
-WTEM_CONTEST_STAGE = getattr(settings, 'WTEM_CONTEST_STAGE', 'before')
-
 
 @csrf_exempt
-def form(request, key):
-    return globals()['form_' + WTEM_CONTEST_STAGE](request, key)
+def form(request, submission_id, key):
+    state = CompetitionState.get_state()
+    if state == CompetitionState.DURING:
+        state = 'single'
+    return globals()['form_' + state](request, submission_id, key)
 
 
-def form_before(request, key):
-    try:
-        Submission.objects.get(key=key)
-    except Submission.DoesNotExist:
+def form_before(request, submission_id, key):
+    submission = Submission.objects.get(id=submission_id)
+    if submission.key != key:
         return render(request, 'wtem/key_not_found_before.html')
     else:
         return render(request, 'wtem/main_before.html')
 
 
-def form_after(request, key):
+def form_after(request, submission_id, key):
     return render(request, 'wtem/main_after.html')
 
 
@@ -39,7 +40,7 @@ def form_after(request, key):
 @csrf_exempt
 def form_during(request, key):
 
-    if WTEM_CONTEST_STAGE != 'during':
+    if CompetitionState.get_state() != CompetitionState.DURING:
         if request.META['REMOTE_ADDR'] not in getattr(settings, 'WTEM_CONTEST_IP_ALLOW', []):
             return HttpResponseForbidden('Not allowed')
 
@@ -86,20 +87,45 @@ def form_single(request, submission_id, key):
 
     i, exercise = submission.current_exercise()
 
+    exercise_count = len(exercises)
+
     if not exercise:
         return render(request, 'wtem/thanks_single.html')
 
     if request.method == 'GET':
-        return render(request, 'wtem/single.html', {'exercise': exercise, 'no': i})
+        return render(request, 'wtem/single.html', {'exercise': exercise, 'no': i, 'exercise_count': exercise_count})
     elif request.method == 'POST':
         form = WTEMSingleForm(request.POST, request.FILES, instance=submission)
         if form.is_valid():
-            form.save()
-            return HttpResponseRedirect(reverse('form_single', kwargs={'submission_id': submission_id, 'key': key}))
+            try:
+                form.save()
+            except ValueError as e:
+                if e.message == 'wrong exercise id':
+                    messages.error(request, u'Próba wysłania odpowiedzi ponownie lub poza kolejnością')
+            print 'wysyłam redirect', i
+            return HttpResponseRedirect(reverse('wtem_form', kwargs={'submission_id': submission_id, 'key': key}))
         else:
             raise Exception
 
 
+@never_cache
+@csrf_exempt
+def start(request, submission_id, key):
+    state = CompetitionState.get_state()
+    if state in (CompetitionState.BEFORE, CompetitionState.AFTER):
+        return globals()['form_' + state](request, submission_id, key)
+
+    submission = Submission.objects.get(id=submission_id)
+    if submission.key != key:
+        return render(request, 'wtem/key_not_found.html')
+
+    i, exercise = submission.current_exercise()
+    if not exercise:
+        return render(request, 'wtem/thanks_single.html')
+
+    return render(request, 'wtem/start.html', {'exercise_count': len(exercises), 'submission': submission})
+
+
 def confirmation(request, id, key):
     conf = get_object_or_404(Confirmation, id=id, key=key)
     was_confirmed = conf.confirmed