experiENS/avisstage/views.py
2018-09-09 00:17:11 +02:00

319 lines
11 KiB
Python

# coding: utf-8
from django.shortcuts import render, redirect, get_object_or_404
from django.views.generic import DetailView, ListView
from django.views.generic.edit import UpdateView, CreateView
from django import forms
from django.urls import reverse
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from braces.views import LoginRequiredMixin
from django.http import JsonResponse, HttpResponseForbidden
from django.core.mail import send_mail
from django.db.models import Q, Count
from collections import Counter, defaultdict
from avisstage.models import Normalien, Stage, Lieu, AvisLieu, AvisStage
from avisstage.forms import StageForm, LieuForm, AvisStageForm, AvisLieuForm, FeedbackForm
from avisstage.views_search import *
import random, math
#
# LECTURE
#
# Page d'accueil
def index(request):
num_stages = Stage.objects.filter(public=True).count()
return render(request, 'avisstage/index.html',
{"num_stages": num_stages})
# Espace personnel
@login_required
def perso(request):
return render(request, 'avisstage/perso.html')
# Profil
#login_required
class ProfilView(LoginRequiredMixin, DetailView):
model = Normalien
template_name = 'avisstage/detail/profil.html'
# Récupération du profil
def get_object(self):
return Normalien.objects.get(user__username=self.kwargs.get('username'))
# Stage
#login_required
class StageView(LoginRequiredMixin, DetailView):
model = Stage
template_name = 'avisstage/detail/stage.html'
# Restriction aux stages publics ou personnels
def get_queryset(self):
filtre = Q(auteur__user_id=self.request.user.id) | Q(public=True)
return Stage.objects.filter(filtre)
# Liste des stages par dernière modification
#login_required
class StageListe(LoginRequiredMixin, ListView):
model = Stage
template_name = 'avisstage/recherche/stage.html'
def get_queryset(self):
return Stage.objects.filter(public=True).order_by('-date_maj')
# FAQ
def faq(request):
return render(request, 'avisstage/faq.html')
#
# EDITION
#
# Profil
#login_required
class ProfilEdit(LoginRequiredMixin, UpdateView):
model = Normalien
fields = ['nom', 'promotion', 'mail', 'contactez_moi', 'bio']
template_name = 'avisstage/formulaires/profil.html'
# Limitation à son propre profil
def get_object(self):
return self.request.user.profil
def get_success_url(self):
return reverse('avisstage:perso')
# Stage
@login_required
def manage_stage(request, pk=None):
# Objet de base
last_maj = None
if pk is None:
stage = Stage(auteur=request.user.profil)
avis_stage = AvisStage(stage=stage)
c_del = False
last_creation = Stage.objects.filter(auteur=request.user.profil)\
.order_by("-date_creation")[:1]
if len(last_creation) != 0:
last_maj = last_creation[0].date_creation
else:
try:
stage = Stage.objects.filter(auteur=request.user.profil).get(pk=pk)
except Stage.DoesNotExist:
return HttpResponseForbidden()
last_maj = stage.date_maj
avis_stage, _ = AvisStage.objects.get_or_create(stage=stage)
c_del = True
# Formset pour les avis des lieux
AvisLieuFormSet = forms.inlineformset_factory(
Stage, AvisLieu, form=AvisLieuForm, can_delete=c_del, extra=0)
if request.method == "POST":
# Lecture des données
form = StageForm(request.POST, request=request, instance=stage, prefix="stage")
avis_stage_form = AvisStageForm(request.POST,
instance=avis_stage, prefix="avis")
avis_lieu_formset = AvisLieuFormSet(request.POST, instance=stage,
prefix="lieux")
# Validation et enregistrement
if (form.is_valid() and
avis_stage_form.is_valid() and
avis_lieu_formset.is_valid()):
stage = form.save()
avis_stage_form.instance.stage = stage
avis_stage_form.save()
avis_lieu_formset.save()
print request.POST
if "continuer" in request.POST:
if pk is None:
return redirect(reverse('avisstage:stage_edit',kwargs={'pk':stage.id}))
else:
return redirect(reverse('avisstage:stage',
kwargs={'pk':stage.id}))
else:
form = StageForm(instance=stage, prefix="stage")
avis_stage_form = AvisStageForm(instance=avis_stage, prefix="avis")
avis_lieu_formset = AvisLieuFormSet(instance=stage, prefix="lieux")
# Affichage du formulaire
return render(request, "avisstage/formulaires/stage.html",
{'form': form, 'avis_stage_form': avis_stage_form,
'avis_lieu_formset': avis_lieu_formset,
'creation': pk is None, "last_maj": last_maj})
# Ajout d'un lieu de stage
#login_required
# Stage
@login_required
def save_lieu(request):
normalien = request.user.profil
if request.method == "POST":
pk = request.POST.get("id", None)
print request.POST
jitter = False
if pk is None or pk == '':
lieu = Lieu()
else:
# Modification du lieu
lieu = get_object_or_404(Lieu, pk=pk)
# On regarde si les stages associés à ce lieu "appartiennent" tous à l'utilisateur
not_same_user = lieu.stages.exclude(auteur=normalien).count()
# Si d'autres personnes ont un stage à cet endroit, on crée un nouveau lieu, un peu à côté
if not_same_user > 0:
lieu = Lieu()
# Servira à bouger un peu le lieu
jitter = True
# Lecture des données
form = LieuForm(request.POST, instance=lieu)
# Validation et enregistrement
if form.is_valid():
lieu = form.save(commit=False)
if jitter:
cdx, cdy = lieu.coord.get_coords()
ang = random.random() * 6.29;
rad = (random.random() + 0.5) * 3e-4
cdx += math.cos(ang) * rad;
cdy += math.sin(ang) * rad;
lieu.coord.set_coords((cdx, cdy))
lieu.save()
# Élimination des doublons
if pk is None or pk == "":
olieux = Lieu.objects.filter(nom=lieu.nom, coord__distance_lte=(lieu.coord, 10))
for olieu in olieux:
if olieu.type_lieu == lieu.type_lieu and \
olieu.ville == lieu.ville and \
olieu.pays == lieu.pays:
return JsonResponse({"success": True, "id": olieu.id})
lieu.save()
return JsonResponse({"success": True, "id": lieu.id})
else:
return JsonResponse({"success": False,
"errors": form.errors})
else:
return JsonResponse({"erreur": "Aucune donnée POST"})
class LieuAjout(LoginRequiredMixin, CreateView):
model = Lieu
form_class = LieuForm
template_name = 'avisstage/formulaires/lieu.html'
# Retourne d'un JSON si requête AJAX
def form_valid(self, form):
if self.request.GET.get("format", "") == "json":
self.object = form.save()
return JsonResponse({"success": True,
"id": self.object.id})
else:
super(LieuAjout, self).form_valid(form)
def form_invalid(self, form):
if self.request.GET.get("format", "") == "json":
return JsonResponse({"success": False,
"errors": form.errors})
else:
super(LieuAjout, self).form_valid(form)
# Passage d'un stage en mode public
@login_required
def publier_stage(request, pk):
if request.method != "POST":
return HttpResponseForbidden()
stage = get_object_or_404(Stage, pk=pk)
# Stage non possédé par l'utilisateur
if stage.auteur != request.user.profil:
return HttpResponseForbidden()
# Mise à jour du statut
if "publier" in request.POST:
stage.public = True
else:
stage.public = False
stage.save()
return redirect(reverse("avisstage:stage", kwargs={"pk": pk}))
#
# FEEDBACK
#
@login_required
def feedback(request):
if request.method == "POST":
form = FeedbackForm(request.POST)
if form.is_valid():
objet = form.cleaned_data['objet']
message = form.cleaned_data['message']
send_mail(
"[experiENS] "+ objet,
message,
request.user.username + "@clipper.ens.fr",
['champeno@clipper.ens.fr'],
fail_silently=False,
)
if request.GET.get("format", None) == "json":
return JsonResponse({"success": True})
return redirect(reverse("avisstage:index"))
else:
if request.GET.get("format", None) == "json":
return JsonResponse({"success": False,
"errors": form.errors})
else:
form = FeedbackForm()
return render(request, 'avisstage/formulaire/feedback.html', {"form": form})
#
# STATISTIQUES
#
@login_required
@staff_member_required
def statistiques(request):
nstages = Stage.objects.count()
npubstages = Stage.objects.filter(public=True).count()
nbymatiere_raw = Stage.objects.values('matieres__nom', 'public').annotate(scount=Count('matieres__nom'))
nbymatiere = defaultdict(dict)
for npm in nbymatiere_raw:
nbymatiere[npm["matieres__nom"]]["publics" if npm["public"] else "drafts"] = npm["scount"]
for mat, npm in nbymatiere.items():
npm["matiere"] = mat
nbymatiere = sorted(list(nbymatiere.values()), key=lambda npm: npm["publics"])
nbylength = [("Vide", Stage.objects.filter(len_avis_stage__lt=5).count(),
Stage.objects.filter(len_avis_lieux__lt=5).count()),
("Court", Stage.objects.filter(len_avis_stage__lt=30, len_avis_stage__gt=4).count(),
Stage.objects.filter(len_avis_lieux__lt=30, len_avis_lieux__gt=4).count()),
("Moyen", Stage.objects.filter(len_avis_stage__lt=100, len_avis_stage__gt=29).count(),
Stage.objects.filter(len_avis_lieux__lt=100, len_avis_lieux__gt=29).count()),
("Long", Stage.objects.filter(len_avis_stage__gt=99).count(),
Stage.objects.filter(len_avis_lieux__gt=99).count())]
nusers = Normalien.objects.count()
nauts = Normalien.objects.filter(stages__isnull=False).distinct().count()
nbyaut = Counter(Normalien.objects.filter(stages__isnull=False).annotate(scount=Count('stages')).values_list('scount', flat="True")).items()
nlieux = Lieu.objects.filter(stages__isnull=False).distinct().count()
return render(request, 'avisstage/moderation/statistiques.html',
{'num_stages': nstages,
'num_stages_pub': npubstages,
'num_par_matiere': nbymatiere,
'num_users': nusers,
'num_auteurs': nauts,
'num_par_auteur': nbyaut,
'num_lieux_utiles': nlieux,
'num_par_longueur': nbylength,
})