From 9d6c41a8d4518ba851c9de7426abe49de0660077 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Fri, 27 Oct 2017 14:15:41 +0200 Subject: [PATCH 1/1] various fixes and updates --- .gitignore | 1 + catalogue/static/catalogue/css/layout.css | 86 -------------- catalogue/static/catalogue/css/layout.scss | 112 ------------------ edumed/static/catalogue/css/layout.css | 6 +- edumed/static/catalogue/css/layout.scss | 5 +- edumed/static/css/base.css | 18 ++- edumed/static/css/base.scss | 16 +++ wtem/forms.py | 14 ++- wtem/templates/wtem/email_key.txt | 16 +-- .../wtem/exercises/edumed_wybor.html | 14 +-- .../wtem/exercises/edumed_wybor_auto.html | 46 +++++++ .../templates/wtem/exercises/exercise_no.html | 2 +- wtem/templates/wtem/main_before.html | 8 +- wtem/templates/wtem/single.html | 41 ++----- wtem/templates/wtem/start.html | 20 ++++ wtem/templates/wtem/thanks_single.html | 4 +- wtem/urls.py | 4 +- wtem/views.py | 52 ++++++-- 18 files changed, 192 insertions(+), 273 deletions(-) delete mode 100644 catalogue/static/catalogue/css/layout.css delete mode 100755 catalogue/static/catalogue/css/layout.scss create mode 100644 wtem/templates/wtem/exercises/edumed_wybor_auto.html create mode 100644 wtem/templates/wtem/start.html diff --git a/.gitignore b/.gitignore index d4d845a..7bbaa41 100644 --- a/.gitignore +++ b/.gitignore @@ -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 index acf699d..0000000 --- a/catalogue/static/catalogue/css/layout.css +++ /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 index 230e4aa..0000000 --- a/catalogue/static/catalogue/css/layout.scss +++ /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;} -} diff --git a/edumed/static/catalogue/css/layout.css b/edumed/static/catalogue/css/layout.css index acf699d..6a0746d 100644 --- a/edumed/static/catalogue/css/layout.css +++ b/edumed/static/catalogue/css/layout.css @@ -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 */ diff --git a/edumed/static/catalogue/css/layout.scss b/edumed/static/catalogue/css/layout.scss index 230e4aa..671e9f0 100755 --- a/edumed/static/catalogue/css/layout.scss +++ b/edumed/static/catalogue/css/layout.scss @@ -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; diff --git a/edumed/static/css/base.css b/edumed/static/css/base.css index 99182a2..292a122 100644 --- a/edumed/static/css/base.css +++ b/edumed/static/css/base.css @@ -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 */ diff --git a/edumed/static/css/base.scss b/edumed/static/css/base.scss index 88d2033..d40f461 100644 --- a/edumed/static/css/base.scss +++ b/edumed/static/css/base.scss @@ -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; +} diff --git a/wtem/forms.py b/wtem/forms.py index 15c6503..a434854 100644 --- a/wtem/forms.py +++ b/wtem/forms.py @@ -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) diff --git a/wtem/templates/wtem/email_key.txt b/wtem/templates/wtem/email_key.txt index aaa86ca..76e629a 100644 --- a/wtem/templates/wtem/email_key.txt +++ b/wtem/templates/wtem/email_key.txt @@ -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 diff --git a/wtem/templates/wtem/exercises/edumed_wybor.html b/wtem/templates/wtem/exercises/edumed_wybor.html index edd9dad..2ac1e2c 100644 --- a/wtem/templates/wtem/exercises/edumed_wybor.html +++ b/wtem/templates/wtem/exercises/edumed_wybor.html @@ -8,22 +8,16 @@ {{para}}

{% endfor %} - {% if exercise.points %} - - {% if exercise.answer|length == 1 %} - Tylko jedna odpowiedź jest prawidłowa. - {% else %} - Zaznacz wszystkie prawidłowe odpowiedzi. - {% endif %} - - {% endif %} + + Zaznacz każdą prawidłową odpowiedź (jedną lub więcej). + {% endautoescape %}
    {% for option in exercise.options %}
  1. - +
  2. {% 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 index 0000000..edd9dad --- /dev/null +++ b/wtem/templates/wtem/exercises/edumed_wybor_auto.html @@ -0,0 +1,46 @@ +
    + + {% include "wtem/exercises/exercise_no.html" %} + {% autoescape off %} +
    + {% for para in exercise.description %} +

    + {{para}} +

    + {% endfor %} + {% if exercise.points %} + + {% if exercise.answer|length == 1 %} + Tylko jedna odpowiedź jest prawidłowa. + {% else %} + Zaznacz wszystkie prawidłowe odpowiedzi. + {% endif %} + + {% endif %} +
    + {% endautoescape %} +
    +
      + {% for option in exercise.options %} +
    1. + + +
    2. + {% endfor %} +
    +
    + + {% if exercise.open_part %} +
    +
    + {% for para in exercise.open_part %} +

    + {{para}} +

    + {% endfor %} +
    + +
    + {% endif %} + +
    diff --git a/wtem/templates/wtem/exercises/exercise_no.html b/wtem/templates/wtem/exercises/exercise_no.html index aecdd2b..0bd9c6d 100644 --- a/wtem/templates/wtem/exercises/exercise_no.html +++ b/wtem/templates/wtem/exercises/exercise_no.html @@ -1,3 +1,3 @@ {% if not exercise.continuation %} -

    Zadanie {{ no }}{# {{exercise.id_show|default:exercise.id}} #}{# ({{ exercise.max_points }} pkt)#}

    +

    Zadanie {{ no }}/{{ exercise_count }}{# {{exercise.id_show|default:exercise.id}} #}{# ({{ exercise.max_points }} pkt)#}

    {% endif %} \ No newline at end of file diff --git a/wtem/templates/wtem/main_before.html b/wtem/templates/wtem/main_before.html index 4ede52b..b3c2b23 100644 --- a/wtem/templates/wtem/main_before.html +++ b/wtem/templates/wtem/main_before.html @@ -4,6 +4,12 @@

    {% include "wtem/title.html" %}

    -

    I etap: 15 listopada, 10:00. Czas trwania: ok. 90 minut.

    +

    Witamy w I etapie Olimpiady Cyfrowej.

    +

    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.

    +

    Test rozpocznie się 23 listopada o godzinie 10:00 i potrwa do 11:00.

    + +

    Powodzenia!

    +

    Zespół Olimpiady Cyfrowej
    +Fundacja Nowoczesna Polska

    {% endblock %} \ No newline at end of file diff --git a/wtem/templates/wtem/single.html b/wtem/templates/wtem/single.html index f4b9c19..3380145 100644 --- a/wtem/templates/wtem/single.html +++ b/wtem/templates/wtem/single.html @@ -8,7 +8,6 @@ {% endblock %} - {% block body %} -

    {% include "wtem/title.html" %}

    -
    Rozwiązania można wysyłać do godziny {{end_time|default:"11:30"}}. Nie czekaj na ostatnią chwilę!
    +{% if messages %} +
      + {% for message in messages %} + {{ message }} + {% endfor %} +
    +{% endif %} -

    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ń.

    - -

    Wszelkie aktualności dotyczące Olimpiady możesz znaleźć tutaj.

    - -

    Powodzenia!
    -Zespół Olimpiady Cyfrowej, fundacja Nowoczesna Polska

    +

    {% include "wtem/title.html" %}

    -
    + {% 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


    -

    Sprawdź jeszcze raz wszystkie swoje odpowiedzi, a następnie wyślij je do nas, klikając w poniższy przycisk:

    - +

    +
    - Wysyłanie rozwiązań w toku... + Wysyłanie rozwiązania w toku... - Spróbuj jeszcze raz, jeśli wysyłanie trwa dłużej niż kilka minut. + Spróbuj jeszcze raz, jeśli wysyłanie trwa dłużej niż kilka chwil.

    -
    Rozwiązania można wysyłać do godziny {{end_time|default:"11:30"}}. Nie czekaj na ostatnią chwilę!
    -
    {% endblock %} diff --git a/wtem/templates/wtem/start.html b/wtem/templates/wtem/start.html new file mode 100644 index 0000000..c938b76 --- /dev/null +++ b/wtem/templates/wtem/start.html @@ -0,0 +1,20 @@ +{% extends 'base_super.html' %} + +{% block body %} + +

    {% include "wtem/title.html" %}

    + +

    Witamy w I etapie Olimpiady Cyfrowej.

    + +

    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.

    +

    Test rozpoczyna się o godzinie 10:00 i trwa do 11:00.

    + +

    Powodzenia!

    +

    Zespół Olimpiady Cyfrowej
    + Fundacja Nowoczesna Polska

    + +

    Start

    + +{% endblock %} diff --git a/wtem/templates/wtem/thanks_single.html b/wtem/templates/wtem/thanks_single.html index b403ae3..14ce51c 100644 --- a/wtem/templates/wtem/thanks_single.html +++ b/wtem/templates/wtem/thanks_single.html @@ -7,9 +7,9 @@

    Dziękujemy za udział w I etapie Olimpiady Cyfrowej. Twoja praca została wysłana i poprawnie przyjęta przez system.

    -

    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.

    +

    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.

    -

    Aktualności związane z Olimpiadą możesz sprawdzać tutaj. W razie dodatkowych pytań możesz kontaktować się z nami pod adresem olimpiada@nowoczesnapolska.org.pl lub numerem telefonu +48 515-502-666.

    +

    Aktualności związane z Olimpiadą możesz sprawdzać tutaj. W razie dodatkowych pytań możesz kontaktować się z nami pod adresem olimpiada@nowoczesnapolska.org.pl lub numerem telefonu +48 22 465 15 35.

    Zespół Olimpiady Cyfrowej
    fundacja Nowoczesna Polska

    diff --git a/wtem/urls.py b/wtem/urls.py index f7a5e99..e498743 100644 --- a/wtem/urls.py +++ b/wtem/urls.py @@ -6,6 +6,6 @@ urlpatterns = patterns( '', url(r'^potwierdzenie/(?P.*)/(?P.*)/$', views.confirmation, name='student_confirmation'), url(r'^_test/(?P.*)/$', views.form_during), - # url(r'^(?P.*)/$', views.form, name='wtem_form'), - url(r'^(?P.*)/(?P.*)/$', views.form_single, name='form_single'), + url(r'^(?P[^/]*)/(?P[^/]*)/$', views.form, name='wtem_form'), + url(r'^(?P[^/]*)/(?P[^/]*)/start/$', views.start, name='wtem_start'), ) diff --git a/wtem/views.py b/wtem/views.py index 6366e53..d47a2be 100644 --- a/wtem/views.py +++ b/wtem/views.py @@ -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 -- 2.20.1