send results for stage 2
[edumed.git] / stage2 / views.py
1 # -*- coding: utf-8 -*-
2 from django.contrib.auth.decorators import login_required
3 from django.core.urlresolvers import reverse
4 from django.http import Http404
5 from django.http.response import HttpResponseRedirect, HttpResponse, HttpResponseForbidden
6 from django.shortcuts import get_object_or_404, render
7 from django.utils import timezone
8 from django.utils.cache import patch_cache_control
9 from django.views.decorators.cache import never_cache
10 from django.views.decorators.http import require_POST
11 from unidecode import unidecode
12
13 from stage2.forms import AttachmentForm, MarkForm
14 from stage2.models import Participant, Assignment, Answer, Attachment, Mark
15
16
17 def all_assignments(participant):
18     assignments = Assignment.objects.all()
19     for assignment in assignments:
20         assignment.answer = assignment.answer_set.filter(participant=participant).first()
21         assignment.forms = [
22             (AttachmentForm(assignment=assignment, file_no=i, label=label, extensions=ext),
23              assignment.answer.attachment_set.filter(file_no=i).first() if assignment.answer else None)
24             for i, (label, ext) in enumerate(assignment.file_descriptions, 1)]
25     return assignments
26
27
28 @never_cache
29 def participant_view(request, participant_id, key):
30     participant = get_object_or_404(Participant, id=participant_id)
31     if not participant.check(key):
32         raise Http404
33     response = render(request, 'stage2/participant.html', {
34         'participant': participant,
35         'assignments': all_assignments(participant)})
36     # not needed in Django 1.8
37     patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True)
38     return response
39
40
41 @require_POST
42 def upload(request, assignment_id, participant_id, key):
43     participant = get_object_or_404(Participant, id=participant_id)
44     if not participant.check(key):
45         raise Http404
46     assignment = get_object_or_404(Assignment, id=assignment_id)
47     now = timezone.now()
48     if assignment.deadline < now:
49         raise Http404  # TODO za późno
50     for i, (label, ext) in enumerate(assignment.file_descriptions, 1):
51         answer, created = Answer.objects.get_or_create(participant=participant, assignment=assignment)
52         attachment, created = Attachment.objects.get_or_create(answer=answer, file_no=i)
53         form = AttachmentForm(
54             data=request.POST, files=request.FILES,
55             assignment=assignment, file_no=i, label=label, instance=attachment, extensions=ext)
56         if form.is_valid():
57             form.save()
58     return HttpResponseRedirect(reverse('stage2_participant', args=(participant_id, key)))
59
60
61 def attachment_download(attachment):
62     response = HttpResponse(content_type='application/force-download')
63     response.write(attachment.file.read())
64     # workaround to this: https://code.djangoproject.com/ticket/20889
65     response['Content-Disposition'] = 'attachment; filename="%s"' % unidecode(attachment.filename().replace('\n', ' '))
66     response['Content-Length'] = response.tell()
67     return response
68
69
70 def get_file(request, assignment_id, file_no, participant_id, key):
71     """We want to serve submitted files back to participants, but also validate their keys,
72        so static files are not good"""
73     participant = get_object_or_404(Participant, id=participant_id)
74     if not participant.check(key):
75         raise Http404
76     assignment = get_object_or_404(Assignment, id=assignment_id)
77     answer = get_object_or_404(Answer, participant=participant, assignment=assignment)
78     attachment = get_object_or_404(Attachment, answer=answer, file_no=file_no)
79     return attachment_download(attachment)
80
81
82 @login_required
83 def assignment_list(request):
84     assignments = request.user.stage2_assignments.all()
85     if not assignments:
86         return HttpResponseForbidden('Not allowed')
87     for assignment in assignments:
88         assignment.marked_count = Mark.objects.filter(expert=request.user, answer__assignment=assignment).count()
89         assignment.to_mark_count = assignment.available_answers(request.user).count()
90         assignment.supervisor = request.user in assignment.supervisors.all()
91         assignment.arbiter_count = assignment.answer_set.filter(need_arbiter=True).count()
92
93     non_empty_assignments = [ass for ass in assignments if ass.marked_count > 0 or ass.to_mark_count > 0]
94     if len(non_empty_assignments) == 1 and non_empty_assignments[0].to_mark_count > 0:
95         return HttpResponseRedirect(reverse('stage2_answer_list', args=[non_empty_assignments[0].id]))
96     return render(request, 'stage2/assignment_list.html', {'assignments': assignments})
97
98
99 def available_answers(assignment, expert, answer_with_errors=None, form_with_errors=None, marked=False):
100     if marked:
101         answers = Answer.objects.filter(mark__expert=expert, assignment=assignment)
102     else:
103         answers = assignment.available_answers(expert)
104     answers = answers.order_by('participant__last_name').prefetch_related('attachment_set')
105     for answer in answers:
106         attachments = answer.attachment_set.all()
107         attachments_by_file_no = {attachment.file_no: attachment for attachment in attachments}
108         answer.attachments = [
109             (desc, attachments_by_file_no.get(i))
110             for (i, (desc, ext)) in enumerate(assignment.file_descriptions, 1)]
111         if answer == answer_with_errors:
112             answer.form = form_with_errors
113         else:
114             answer.form = MarkForm(
115                 answer=answer, instance=answer.mark_set.filter(expert=expert).first(), prefix='ans%s' % answer.id)
116     return answers
117
118
119 @login_required
120 def answer_list(request, assignment_id):
121     assignment = get_object_or_404(Assignment, id=assignment_id)
122     if request.user not in assignment.experts.all():
123         return HttpResponseForbidden('Not allowed')
124     return render(request, 'stage2/answer_list.html',
125                   {'answers': available_answers(assignment, request.user), 'assignment': assignment})
126
127
128 @login_required
129 def marked_answer_list(request, assignment_id):
130     assignment = get_object_or_404(Assignment, id=assignment_id)
131     if request.user not in assignment.experts.all():
132         return HttpResponseForbidden('Not allowed')
133     return render(request, 'stage2/answer_list.html', {
134         'answers': available_answers(assignment, request.user, marked=True),
135         'assignment': assignment,
136         'marked': True,
137     })
138
139
140 @login_required
141 def expert_download(request, attachment_id):
142     attachment = get_object_or_404(Attachment, id=attachment_id)
143     return attachment_download(attachment)
144
145
146 @require_POST
147 @login_required
148 def mark_answer(request, answer_id):
149     answer = get_object_or_404(Answer, id=answer_id)
150     if request.user not in answer.assignment.experts.all():
151         return HttpResponseForbidden('Not allowed')
152     if answer.assignment.is_active():
153         return HttpResponseForbidden('Not allowed')
154     mark, created = Mark.objects.get_or_create(answer=answer, expert=request.user, defaults={'points': 0})
155     form = MarkForm(data=request.POST, answer=answer, instance=mark, prefix='ans%s' % answer.id)
156     if form.is_valid():
157         form.save()
158     elif created:
159         mark.delete()
160
161     return HttpResponseRedirect(reverse(
162         'stage2_answer_list' if created else 'stage2_marked_answers', args=[answer.assignment_id]))
163
164
165 @login_required
166 def csv_results(request):
167     import csv
168     response = HttpResponse(content_type='text/csv')
169     writer = csv.writer(response)
170     assignments = Assignment.objects.all()
171     participants = Participant.objects.filter(complete_set=True)
172     headers = [u'imię', u'nazwisko', u'szkoła']
173     assignments_experts = []
174     for assignment in assignments:
175         for expert in assignment.experts.filter(mark__answer__assignment=assignment).distinct():
176             assignments_experts.append((assignment, expert))
177             headers.append(u'%s %s' % (assignment.title, expert.last_name))
178     for assignment in assignments:
179         headers.append(u'%s - średnia' % assignment.title.encode('utf-8'))
180     headers.append(u'ostateczny wynik')
181     writer.writerow([unicode(item).encode('utf-8') for item in headers])
182     for participant in participants:
183         row = [
184             participant.first_name,
185             participant.last_name,
186             participant.contact.body['school'],
187         ]
188         for assignment, expert in assignments_experts:
189             try:
190                 row.append(
191                     Mark.objects.get(
192                         expert=expert, answer__assignment=assignment, answer__participant=participant).points)
193             except Mark.DoesNotExist:
194                 row.append('')
195         for assignment in assignments:
196             row.append('%.2f' % participant.answer_set.get(assignment=assignment).score())
197         row.append('%.2f' % participant.score())
198         writer.writerow([unicode(item).encode('utf-8') for item in row])
199     response['Content-Disposition'] = 'attachment; filename="wyniki.csv"'
200     return response