From 5be0b20060b1b90b89a739a0bacc18025996422d Mon Sep 17 00:00:00 2001 From: Gael Le Mignot Date: Wed, 19 Dec 2018 12:11:59 +0100 Subject: [PATCH] Added mass submit form --- teleforma/exam/forms.py | 31 +++++ .../exam/templates/exam/mass_score_form.html | 112 ++++++++++++++++++ teleforma/exam/templates/exam/score_form.html | 9 ++ teleforma/exam/templates/exam/scores.html | 5 + teleforma/exam/urls.py | 1 + teleforma/exam/views.py | 89 +++++++++++++- 6 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 teleforma/exam/templates/exam/mass_score_form.html diff --git a/teleforma/exam/forms.py b/teleforma/exam/forms.py index b7c81834..d6950914 100644 --- a/teleforma/exam/forms.py +++ b/teleforma/exam/forms.py @@ -33,3 +33,34 @@ class ScoreForm(ScriptForm): super(ScoreForm, self).__init__(*args, **kwargs) self.fields['file'].required = False self.fields['score'].required = True + +class MassScoreForm(ScoreForm): + def __init__(self, *args, **kwargs): + super(MassScoreForm, self).__init__(*args, **kwargs) + self.table_errors = {} + self.fields['score'].required = False + + def clean(self): + cleaned_data = super(MassScoreForm, self).clean() + + errors = {} + valid = [] + + for key in self.data.keys(): + if key.startswith('student'): + student = self.data[key] + if student: + score = self.data[key.replace('student', 'score')] + try: + score = int(score) + except ValueError: + errors[key] = u"Note invalide" + continue + valid.append((student, score)) + + cleaned_data['scores'] = valid + self.table_errors = errors + + if errors: + raise forms.ValidationError("Certaines notes sont invalides") + return cleaned_data diff --git a/teleforma/exam/templates/exam/mass_score_form.html b/teleforma/exam/templates/exam/mass_score_form.html new file mode 100644 index 00000000..23dec380 --- /dev/null +++ b/teleforma/exam/templates/exam/mass_score_form.html @@ -0,0 +1,112 @@ +{% extends "exam/scores.html" %} +{% load telemeta_utils %} +{% load teleforma_tags %} +{% load i18n %} +{% load thumbnail %} + +{% block extra_javascript %} +{% if upload %} + +{% endif %} +{% endblock extra_javascript %} + +{% block answers %} + +
+ Saisie en masse de notes +
+
+ +{% if messages %} +
+ {% for message in messages %} + {{ message }}
+ {% endfor %} + +{% endif %} + +
+
{% csrf_token %} + +
+ + {% for field in form %} + + {% if not field.html_name in create_fields %} + + {% else %} + + + + {% endif %} + + {% endfor %} +
{% for error in form.non_field_errors %}
  • {{ error }}
  • {% endfor %}
    {{ field.label_tag.as_hidden }}{{ field.as_hidden }} + {{ field.label_tag }}: + + {{ field }} + + {% for error in field.errors %} +
    + {{ error|escape }} +
    + {% endfor %} +
    + + + + + + + + + + {% for row in rows %} + + + + {% if row.error %} + + {% endif %} + + {% endfor %} + +
    ÉtudiantNote
    + + + + +
    + {{ row.error }} +
    +
    +
    + {% if upload %} +
    +
    + {% trans "Submit" %} + loading +
    + {% endif %} +

    + +
    + + +{% endblock answers %} diff --git a/teleforma/exam/templates/exam/score_form.html b/teleforma/exam/templates/exam/score_form.html index c420b9f1..c47faf47 100644 --- a/teleforma/exam/templates/exam/score_form.html +++ b/teleforma/exam/templates/exam/score_form.html @@ -33,6 +33,15 @@
    +{% if messages %} +
    + {% for message in messages %} + {{ message }}
    + {% endfor %} + +{% endif %} + +
    Ce formulaire vous permet d'enregistrer la note d'une copie papier déjà corrigée hors de la plateforme e-learning.
    Pour soumettre une nouvelle copie d'épreuve scannée à la correction en ligne, merci d'utiliser ce formulaire.

    diff --git a/teleforma/exam/templates/exam/scores.html b/teleforma/exam/templates/exam/scores.html index 38aeb848..f2105043 100644 --- a/teleforma/exam/templates/exam/scores.html +++ b/teleforma/exam/templates/exam/scores.html @@ -37,6 +37,11 @@ {% trans "New score" %} {% endif %} +{% if admin %} +
    +Saisie en masse de notes +
    +{% endif %} {% endblock module-action %} {% endblock modules %} diff --git a/teleforma/exam/urls.py b/teleforma/exam/urls.py index 31619c60..c1acef27 100644 --- a/teleforma/exam/urls.py +++ b/teleforma/exam/urls.py @@ -52,6 +52,7 @@ urlpatterns = patterns('', url(r'^scores/periods/(?P.*)/all/$', ScriptsScoreAllView.as_view(), name="teleforma-exam-scripts-scores-all"), url(r'^scores/periods/(?P.*)/courses/(?P.*)/$', ScriptsScoreCourseView.as_view(), name="teleforma-exam-scripts-scores-course"), url(r'^scores/periods/(?P.*)/create/$', ScoreCreateView.as_view(), name="teleforma-exam-scores-create"), + url(r'^scores/periods/(?P.*)/mass_create/$', MassScoreCreateView.as_view(), name="teleforma-exam-scores-mass-create"), url(r'^scripts/get-correctors/$', get_correctors, name="teleforma-exam-get-correctors"), # url(r'^exam/periods/(?P.*)/quotas/$', QuotasView.as_view(), name="teleforma-exam-quotas"), diff --git a/teleforma/exam/views.py b/teleforma/exam/views.py index be363af7..efa9bec8 100755 --- a/teleforma/exam/views.py +++ b/teleforma/exam/views.py @@ -11,6 +11,7 @@ from django.core.urlresolvers import reverse_lazy, reverse from django.utils.translation import ugettext_lazy as _ import json import numpy as np +from django.contrib.auth.decorators import permission_required STUDENT = 0 CORRECTOR = 1 @@ -42,6 +43,8 @@ class ScriptMixinView(View): else: context['upload'] = False + context['admin'] = self.request.user.is_superuser + return context class ScriptsListMixinView(ScriptMixinView): @@ -240,7 +243,7 @@ class ScriptCreateView(ScriptMixinView, CreateView): return super(ScriptCreateView, self).form_valid(form) def form_invalid(self, form): - messages.info(self.request, _("There was a problem with your submission. Please try again, later if possible.")) + messages.error(self.request, _("There was a problem with your submission. Please try again, later if possible.")) return super(ScriptCreateView, self).form_invalid(form) def get_context_data(self, **kwargs): @@ -377,6 +380,10 @@ class ScoreCreateView(ScriptCreateView): template_name='exam/score_form.html' form_class = ScoreForm + def form_invalid(self, form): + messages.error(self.request, "Il y une erreur sur ce formulaire, veuillez le corriger.") + return super(ScriptCreateView, self).form_invalid(form) + def get_success_url(self): period = Period.objects.get(id=self.kwargs['period_id']) return reverse_lazy('teleforma-exam-scripts-scores-all', kwargs={'period_id':period.id}) @@ -400,3 +407,83 @@ def get_correctors(request): correctors.append({'label': '%s %s' % (corrector.last_name, corrector.first_name), 'value':corrector.id}) dump = json.dumps(correctors) return HttpResponse(dump, content_type='application/json') + +class MassScoreCreateView(ScoreCreateView): + + template_name='exam/mass_score_form.html' + form_class = MassScoreForm + + def get_allowed_students(self, course_id, session): + """ + Get allowed students for given course and session + """ + students = Student.objects.filter(period = self.period) + + # Exclude students who already have a script for this session + scripts = Script.objects.filter(period = self.period, + session = session, + course_id = course_id).values('author_id') + scripts = set([s['author_id'] for s in scripts]) + + students = students.exclude(user_id__in = scripts) + + res = [] + for student in students: + user = student.user + # FIXME : Filter those who access the course, but that's very slow, + # so I disable it for now - we'll see if we can do that faster later + # courses = get_courses(user) + # if course_id in [ c['course'].id for c in courses ]: + res.append({ 'id': user.id, + 'name': str(user) }) + + return res + + def form_valid(self, form): + course = form.cleaned_data['course'] + session = form.cleaned_data['session'] + sc_type = form.cleaned_data['type'] + + allowed_students = self.get_allowed_students(course.id, + session) + allowed_students = set([ str(s['id']) for s in allowed_students ]) + done = set() + nb_errors = 0 + nb_ok = 0 + + for student, score in form.cleaned_data['scores']: + if student in done: + nb_errors += 1 + continue + if not student in allowed_students: + nb_errors += 1 + continue + nb_ok += 1 + done.add(student) + obj = Script(course = course, + period = self.period, + session = session, + author_id = student, + type = sc_type, + score = score, + status = 7) + obj.save() + + messages.info(self.request, "%d notes créées, %d en erreur (copie déjà existante, ...)" % (nb_ok, nb_errors)) + + return redirect(self.get_success_url()) + + def get_context_data(self, **kwargs): + context = super(MassScoreCreateView, self).get_context_data(**kwargs) + context['create_fields'] = ['course', 'session', 'type' ] + context['rows'] = [ { 'student_name': 'student%d' % i, + 'score_name': 'score%d' % i } for i in range(20) ] + for row in context['rows']: + row['student_value'] = self.request.POST.get(row['student_name'], '') + row['score_value'] = self.request.POST.get(row['score_name'], '') + row['error'] = context['form'].table_errors.get(row['student_name'], None) + return context + + @method_decorator(permission_required('is_superuser')) + def dispatch(self, *args, **kwargs): + return super(ScoreCreateView, self).dispatch(*args, **kwargs) -- 2.39.5