]> git.parisson.com Git - teleforma.git/commitdiff
Added mass submit form
authorGael Le Mignot <gael@pilotsystems.net>
Wed, 19 Dec 2018 11:11:59 +0000 (12:11 +0100)
committerGael Le Mignot <gael@pilotsystems.net>
Wed, 19 Dec 2018 11:11:59 +0000 (12:11 +0100)
teleforma/exam/forms.py
teleforma/exam/templates/exam/mass_score_form.html [new file with mode: 0644]
teleforma/exam/templates/exam/score_form.html
teleforma/exam/templates/exam/scores.html
teleforma/exam/urls.py
teleforma/exam/views.py

index b7c8183442997fa975198fca2389ac479a8cdd44..d695091449a4753604e990abfc1d75b4314f916f 100644 (file)
@@ -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 (file)
index 0000000..23dec38
--- /dev/null
@@ -0,0 +1,112 @@
+{% extends "exam/scores.html" %}
+{% load telemeta_utils %}
+{% load teleforma_tags %}
+{% load i18n %}
+{% load thumbnail %}
+
+{% block extra_javascript %}
+{% if upload %}
+  <script>
+    $(document).ready(function(){
+        $('#loading').hide();
+    });
+
+    $(function() {
+        $('#submit_button').unbind('click').click(function() {
+            $(window).unbind('beforeunload');
+            // b2.unbind('click');
+            $('#id_status').val("5");
+            $('#id_period').val("{{ period.id }}");
+            $(this).hide();
+            $('#loading').show();
+            $('#_ScriptForm').submit();
+        });
+    });
+    </script>
+{% endif %}
+{% endblock extra_javascript %}
+
+{% block answers %}
+
+<div class="course_title">
+  Saisie en masse de notes
+</div>
+<br />
+
+{% if messages %}
+    <div class="messages">
+        {% for message in messages %}
+          <div{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</div>
+        {% endfor %}
+    </div>
+{% endif %}
+
+<div class="course_content" id="media_infos" style="font-size: 115%;">
+    <form method="post" id="_ScriptForm" action="" enctype="multipart/form-data" data-ajax="false">{% csrf_token %}
+       <table>
+       <br />
+       <tr><td colspan="2">{% for error in form.non_field_errors %}<li class="error">{{ error }}</li>{% endfor %}</td></tr>
+       {% for field in form %}
+       <tr>
+        {% if not field.html_name in create_fields %}
+            <td>{{ field.label_tag.as_hidden }}</td><td>{{ field.as_hidden }}</td>
+        {% else %}
+        <td>
+          {{ field.label_tag }}:
+        </td>
+        <td>
+          {{ field }}
+        </td>
+        <td>
+          {% for error in field.errors %}
+          <div class="error">
+            {{ error|escape }}
+          </div>
+          {% endfor %}          
+        </td>
+        {% endif %}
+        </tr>
+       {% endfor %}
+       </table>
+
+       <table>
+         <thead>
+           <tr>
+             <th>Étudiant</th>
+             <th>Note</th>
+           </tr>
+         </thead>
+         <tbody>
+           {% for row in rows %}
+           <tr>
+             <td>
+               <input name="{{ row.student_name }}" value="{{ row.student_value }}" />
+             </td>
+             <td>
+               <input name="{{ row.score_name }}" value="{{ row.score_value }}" />
+             </td>
+             {% if row.error %}
+             <td>
+               <div class="error">
+                 {{ row.error }}
+               </div>
+             </td>
+             {% endif %}
+           </tr>
+           {% endfor %}
+         </tbody>
+       </table>
+    </form>
+    {% if upload %}
+    <br />
+    <center>
+    <a href="#" id="submit_button" class="component_icon button icon_next">{% trans "Submit" %}</a>
+    <img id="loading" src="/static/teleforma/images/loading.gif" style="vertical-align:middle" alt="loading" />
+    </center>
+    {% endif %}
+    <br /><br />
+
+</div>
+</div>
+
+{% endblock answers %}
index c420b9f1cd839a4d7d03afcdc4da3af5447f806e..c47faf47481c8519fb752ef2fabcefe2a74977c4 100644 (file)
 </div>
 <br />
 
+{% if messages %}
+    <div class="messages">
+        {% for message in messages %}
+          <div{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</div>
+        {% endfor %}
+    </div>
+{% endif %}
+
+
 <div>Ce formulaire vous permet d'enregistrer la note d'une copie papier <b>déjà corrigée hors de la plateforme e-learning</b>.</div>
 <div>Pour soumettre une nouvelle copie d'épreuve scannée à la correction en ligne, merci d'utiliser <a href="{% url teleforma-exam-script-create period.id %}">ce formulaire</a>.</div>
 <br/>
index 38aeb8484a9a466fcbf047bc35e15b3609001760..f2105043e737f4f4b26550696207533abcccf82e 100644 (file)
 <a href="{% url teleforma-exam-scores-create period.id %}" class="component_icon button" id="action_green">{% trans "New score" %}</a>
 </div>
 {% endif %}
+{% if admin %}
+<div class="module_action">
+<a href="{% url teleforma-exam-scores-mass-create period.id %}" class="component_icon button" id="action_green">Saisie en masse de notes</a>
+</div>
+{% endif %}
 {% endblock module-action %}
 
 {% endblock modules %}
index 31619c60861b4a5acad4fc2d6c069e1b099423d0..c1acef276187e52a026a481c81a5bd73c364debc 100644 (file)
@@ -52,6 +52,7 @@ urlpatterns = patterns('',
     url(r'^scores/periods/(?P<period_id>.*)/all/$', ScriptsScoreAllView.as_view(), name="teleforma-exam-scripts-scores-all"),
     url(r'^scores/periods/(?P<period_id>.*)/courses/(?P<course_id>.*)/$', ScriptsScoreCourseView.as_view(), name="teleforma-exam-scripts-scores-course"),
     url(r'^scores/periods/(?P<period_id>.*)/create/$', ScoreCreateView.as_view(), name="teleforma-exam-scores-create"),
+    url(r'^scores/periods/(?P<period_id>.*)/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<period_id>.*)/quotas/$', QuotasView.as_view(), name="teleforma-exam-quotas"),
index be363af7b85b3228db68b3ca9eb4500a75ea3013..efa9bec876d0e029c58bb405f0070f08c364186f 100755 (executable)
@@ -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)