forked from DGNum/gestioCOF
98abe71681
Sometimes the `errors` variable is `None` in `_finalize_traitement`. It was not an issue when it was just used in the templates but now we have to handle this in the view. Basically, we shall consider it is an empty list when it occurs.
350 lines
13 KiB
Python
350 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import json
|
|
from datetime import datetime
|
|
from custommail.shortcuts import render_custom_mail
|
|
|
|
from django.shortcuts import render, get_object_or_404, redirect
|
|
from django.core import mail
|
|
from django.contrib.auth.models import User
|
|
from django.views.generic import ListView, DetailView
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from django.conf import settings
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.contrib import messages
|
|
|
|
from gestioncof.models import CofProfile
|
|
from gestioncof.petits_cours_models import (
|
|
PetitCoursDemande, PetitCoursAttribution, PetitCoursAttributionCounter,
|
|
PetitCoursAbility, PetitCoursSubject
|
|
)
|
|
from gestioncof.petits_cours_forms import DemandeForm, MatieresFormSet
|
|
from gestioncof.decorators import buro_required
|
|
from gestioncof.shared import lock_table, unlock_tables
|
|
|
|
|
|
class DemandeListView(ListView):
|
|
model = PetitCoursDemande
|
|
template_name = "petits_cours_demandes_list.html"
|
|
paginate_by = 20
|
|
|
|
def get_queryset(self):
|
|
return PetitCoursDemande.objects.order_by('traitee', '-id').all()
|
|
|
|
|
|
class DemandeDetailView(DetailView):
|
|
model = PetitCoursDemande
|
|
template_name = "gestioncof/details_demande_petit_cours.html"
|
|
context_object_name = "demande"
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(DemandeDetailView, self).get_context_data(**kwargs)
|
|
obj = self.object
|
|
context['attributions'] = obj.petitcoursattribution_set.all()
|
|
return context
|
|
|
|
|
|
@buro_required
|
|
def traitement(request, demande_id, redo=False):
|
|
demande = get_object_or_404(PetitCoursDemande, id=demande_id)
|
|
if demande.niveau == "other":
|
|
return _traitement_other(request, demande, redo)
|
|
if request.method == "POST":
|
|
return _traitement_post(request, demande)
|
|
proposals = {}
|
|
proposed_for = {}
|
|
unsatisfied = []
|
|
attribdata = {}
|
|
for matiere, candidates in demande.get_candidates(redo):
|
|
if candidates:
|
|
tuples = []
|
|
for candidate in candidates:
|
|
user = candidate.user
|
|
tuples.append((
|
|
candidate,
|
|
PetitCoursAttributionCounter.get_uptodate(user, matiere)
|
|
))
|
|
tuples = sorted(tuples, key=lambda c: c[1].count)
|
|
candidates, _ = zip(*tuples)
|
|
candidates = candidates[0:min(3, len(candidates))]
|
|
attribdata[matiere.id] = []
|
|
proposals[matiere] = []
|
|
for candidate in candidates:
|
|
user = candidate.user
|
|
proposals[matiere].append(user)
|
|
attribdata[matiere.id].append(user.id)
|
|
if user not in proposed_for:
|
|
proposed_for[user] = [matiere]
|
|
else:
|
|
proposed_for[user].append(matiere)
|
|
else:
|
|
unsatisfied.append(matiere)
|
|
return _finalize_traitement(request, demande, proposals,
|
|
proposed_for, unsatisfied, attribdata, redo)
|
|
|
|
|
|
@buro_required
|
|
def retraitement(request, demande_id):
|
|
return traitement(request, demande_id, redo=True)
|
|
|
|
|
|
def _finalize_traitement(request, demande, proposals, proposed_for,
|
|
unsatisfied, attribdata, redo=False, errors=None):
|
|
proposals = proposals.items()
|
|
proposed_for = proposed_for.items()
|
|
attribdata = list(attribdata.items())
|
|
proposed_mails = _generate_eleve_email(demande, proposed_for)
|
|
mainmail = render_custom_mail("petits-cours-mail-demandeur", {
|
|
"proposals": proposals,
|
|
"unsatisfied": unsatisfied,
|
|
"extra":
|
|
'<textarea name="extra" '
|
|
'style="width:99%; height: 90px;">'
|
|
'</textarea>'
|
|
})
|
|
if errors is not None:
|
|
for error in errors:
|
|
messages.error(request, error)
|
|
return render(request, "gestioncof/traitement_demande_petit_cours.html",
|
|
{"demande": demande,
|
|
"unsatisfied": unsatisfied,
|
|
"proposals": proposals,
|
|
"proposed_for": proposed_for,
|
|
"proposed_mails": proposed_mails,
|
|
"mainmail": mainmail,
|
|
"attribdata": json.dumps(attribdata),
|
|
"redo": redo,
|
|
})
|
|
|
|
|
|
def _generate_eleve_email(demande, proposed_for):
|
|
return [
|
|
(
|
|
user,
|
|
render_custom_mail('petit-cours-mail-eleve', {
|
|
"demande": demande,
|
|
"matieres": matieres
|
|
})
|
|
)
|
|
for user, matieres in proposed_for
|
|
]
|
|
|
|
|
|
def _traitement_other_preparing(request, demande):
|
|
redo = "redo" in request.POST
|
|
unsatisfied = []
|
|
proposals = {}
|
|
proposed_for = {}
|
|
attribdata = {}
|
|
errors = []
|
|
for matiere, candidates in demande.get_candidates(redo):
|
|
if candidates:
|
|
candidates = dict([(candidate.user.id, candidate.user)
|
|
for candidate in candidates])
|
|
attribdata[matiere.id] = []
|
|
proposals[matiere] = []
|
|
for choice_id in range(min(3, len(candidates))):
|
|
choice = int(
|
|
request.POST["proposal-{:d}-{:d}"
|
|
.format(matiere.id, choice_id)]
|
|
)
|
|
if choice == -1:
|
|
continue
|
|
if choice not in candidates:
|
|
errors.append("Choix invalide pour la proposition {:d}"
|
|
"en {!s}".format(choice_id + 1, matiere))
|
|
continue
|
|
user = candidates[choice]
|
|
if user in proposals[matiere]:
|
|
errors.append("La proposition {:d} en {!s} est un doublon"
|
|
.format(choice_id + 1, matiere))
|
|
continue
|
|
proposals[matiere].append(user)
|
|
attribdata[matiere.id].append(user.id)
|
|
if user not in proposed_for:
|
|
proposed_for[user] = [matiere]
|
|
else:
|
|
proposed_for[user].append(matiere)
|
|
if not proposals[matiere]:
|
|
errors.append("Aucune proposition pour {!s}".format(matiere))
|
|
elif len(proposals[matiere]) < 3:
|
|
errors.append("Seulement {:d} proposition{:s} pour {!s}"
|
|
.format(
|
|
len(proposals[matiere]),
|
|
"s" if len(proposals[matiere]) > 1 else "",
|
|
matiere))
|
|
else:
|
|
unsatisfied.append(matiere)
|
|
return _finalize_traitement(request, demande, proposals, proposed_for,
|
|
unsatisfied, attribdata, errors=errors)
|
|
|
|
|
|
def _traitement_other(request, demande, redo):
|
|
if request.method == "POST":
|
|
if "preparing" in request.POST:
|
|
return _traitement_other_preparing(request, demande)
|
|
else:
|
|
return _traitement_post(request, demande)
|
|
proposals = {}
|
|
proposed_for = {}
|
|
unsatisfied = []
|
|
attribdata = {}
|
|
for matiere, candidates in demande.get_candidates(redo):
|
|
if candidates:
|
|
tuples = []
|
|
for candidate in candidates:
|
|
user = candidate.user
|
|
tuples.append((
|
|
candidate,
|
|
PetitCoursAttributionCounter.get_uptodate(user, matiere)
|
|
))
|
|
tuples = sorted(tuples, key=lambda c: c[1].count)
|
|
candidates, _ = zip(*tuples)
|
|
attribdata[matiere.id] = []
|
|
proposals[matiere] = []
|
|
for candidate in candidates:
|
|
user = candidate.user
|
|
proposals[matiere].append(user)
|
|
attribdata[matiere.id].append(user.id)
|
|
if user not in proposed_for:
|
|
proposed_for[user] = [matiere]
|
|
else:
|
|
proposed_for[user].append(matiere)
|
|
else:
|
|
unsatisfied.append(matiere)
|
|
proposals = proposals.items()
|
|
proposed_for = proposed_for.items()
|
|
return render(request,
|
|
"gestiocof/traitement_demande_petit_cours_autre_niveau.html",
|
|
{"demande": demande,
|
|
"unsatisfied": unsatisfied,
|
|
"proposals": proposals,
|
|
"proposed_for": proposed_for,
|
|
})
|
|
|
|
|
|
def _traitement_post(request, demande):
|
|
proposals = {}
|
|
proposed_for = {}
|
|
unsatisfied = []
|
|
extra = request.POST["extra"].strip()
|
|
redo = "redo" in request.POST
|
|
attribdata = request.POST["attribdata"]
|
|
attribdata = dict(json.loads(attribdata))
|
|
for matiere in demande.matieres.all():
|
|
if matiere.id not in attribdata:
|
|
unsatisfied.append(matiere)
|
|
else:
|
|
proposals[matiere] = []
|
|
for user_id in attribdata[matiere.id]:
|
|
user = User.objects.get(pk=user_id)
|
|
proposals[matiere].append(user)
|
|
if user not in proposed_for:
|
|
proposed_for[user] = [matiere]
|
|
else:
|
|
proposed_for[user].append(matiere)
|
|
proposals_list = proposals.items()
|
|
proposed_for = proposed_for.items()
|
|
proposed_mails = _generate_eleve_email(demande, proposed_for)
|
|
mainmail = render_custom_mail("petits-cours-mail-demandeur", {
|
|
"proposals": proposals_list,
|
|
"unsatisfied": unsatisfied,
|
|
"extra": extra,
|
|
})
|
|
frommail = settings.MAIL_DATA['petits_cours']['FROM']
|
|
bccaddress = settings.MAIL_DATA['petits_cours']['BCC']
|
|
replyto = settings.MAIL_DATA['petits_cours']['REPLYTO']
|
|
mails_to_send = []
|
|
for (user, msg) in proposed_mails:
|
|
msg = mail.EmailMessage("Petits cours ENS par le COF", msg,
|
|
frommail, [user.email],
|
|
[bccaddress], headers={'Reply-To': replyto})
|
|
mails_to_send.append(msg)
|
|
mails_to_send.append(mail.EmailMessage("Cours particuliers ENS", mainmail,
|
|
frommail, [demande.email],
|
|
[bccaddress],
|
|
headers={'Reply-To': replyto}))
|
|
connection = mail.get_connection(fail_silently=True)
|
|
connection.send_messages(mails_to_send)
|
|
lock_table(PetitCoursAttributionCounter, PetitCoursAttribution, User)
|
|
for matiere in proposals:
|
|
for rank, user in enumerate(proposals[matiere]):
|
|
counter = PetitCoursAttributionCounter.objects.get(user=user,
|
|
matiere=matiere)
|
|
counter.count += 1
|
|
counter.save()
|
|
attrib = PetitCoursAttribution(user=user, matiere=matiere,
|
|
demande=demande, rank=rank + 1)
|
|
attrib.save()
|
|
unlock_tables()
|
|
demande.traitee = True
|
|
demande.traitee_par = request.user
|
|
demande.processed = datetime.now()
|
|
demande.save()
|
|
return render(request,
|
|
"gestioncof/traitement_demande_petit_cours_success.html",
|
|
{"demande": demande,
|
|
"redo": redo,
|
|
})
|
|
|
|
|
|
@login_required
|
|
def inscription(request):
|
|
profile, created = CofProfile.objects.get_or_create(user=request.user)
|
|
if not profile.is_cof:
|
|
return redirect("cof-denied")
|
|
success = False
|
|
if request.method == "POST":
|
|
formset = MatieresFormSet(request.POST, instance=request.user)
|
|
if formset.is_valid():
|
|
formset.save()
|
|
profile.petits_cours_accept = "receive_proposals" in request.POST
|
|
profile.petits_cours_remarques = request.POST["remarques"]
|
|
profile.save()
|
|
lock_table(PetitCoursAttributionCounter, PetitCoursAbility, User,
|
|
PetitCoursSubject)
|
|
abilities = (
|
|
PetitCoursAbility.objects.filter(user=request.user).all()
|
|
)
|
|
for ability in abilities:
|
|
PetitCoursAttributionCounter.get_uptodate(
|
|
ability.user,
|
|
ability.matiere
|
|
)
|
|
unlock_tables()
|
|
success = True
|
|
formset = MatieresFormSet(instance=request.user)
|
|
else:
|
|
formset = MatieresFormSet(instance=request.user)
|
|
return render(request, "inscription-petit-cours.html",
|
|
{"formset": formset, "success": success,
|
|
"receive_proposals": profile.petits_cours_accept,
|
|
"remarques": profile.petits_cours_remarques})
|
|
|
|
|
|
@csrf_exempt
|
|
def demande(request):
|
|
success = False
|
|
if request.method == "POST":
|
|
form = DemandeForm(request.POST)
|
|
if form.is_valid():
|
|
form.save()
|
|
success = True
|
|
else:
|
|
form = DemandeForm()
|
|
return render(request, "demande-petit-cours.html", {"form": form,
|
|
"success": success})
|
|
|
|
|
|
@csrf_exempt
|
|
def demande_raw(request):
|
|
success = False
|
|
if request.method == "POST":
|
|
form = DemandeForm(request.POST)
|
|
if form.is_valid():
|
|
form.save()
|
|
success = True
|
|
else:
|
|
form = DemandeForm()
|
|
return render(request, "demande-petit-cours-raw.html",
|
|
{"form": form, "success": success})
|