experiENS/avisstage/views.py
2019-02-25 17:17:58 +01:00

336 lines
12 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.conf import settings
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, Http404
from django.core.mail import send_mail
from django.db.models import Q, Count
from collections import Counter, defaultdict
from .models import Normalien, Stage, Lieu, AvisLieu, AvisStage
from .forms import StageForm, LieuForm, AvisStageForm, AvisLieuForm, FeedbackForm
from .utils import en_scolarite
from .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')
# 403 Archicubes
@login_required
def archicubes_interdits(request):
return render(request, 'avisstage/403-archicubes.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):
# Restriction d'accès pour les archicubes
if (en_scolarite(self.request.user) or
self.kwargs.get('username') == self.request.user.username):
return get_object_or_404(
Normalien, user__username=self.kwargs.get('username'))
else:
raise Http404
# 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)
# Restriction d'accès pour les archicubes
if en_scolarite(self.request.user):
filtre |= Q(public=True)
return Stage.objects.filter(filtre)
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['MAPBOX_API_KEY'] = settings.MAPBOX_API_KEY
return context
# FAQ
def faq(request):
return render(request, 'avisstage/faq.html')
#
# EDITION
#
# Profil
#login_required
class ProfilEdit(LoginRequiredMixin, UpdateView):
model = Normalien
fields = ['nom', 'promotion', '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,
'GOOGLE_API_KEY': settings.GOOGLE_API_KEY,
'MAPBOX_API_KEY': settings.MAPBOX_API_KEY})
# 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()
raise Http404()
#
# 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.get("publics", 0))
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,
})