340 lines
No EOL
15 KiB
Python
340 lines
No EOL
15 KiB
Python
# coding: utf-8
|
|
|
|
from django.shortcuts import get_object_or_404, render
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, HttpResponseNotFound
|
|
from django.core.urlresolvers import reverse
|
|
from django import forms
|
|
from django.conf import settings
|
|
from django.contrib.gis.geos import GEOSGeometry
|
|
from django.contrib.gis import measure
|
|
from django.forms.widgets import HiddenInput
|
|
from django.core.mail import send_mail
|
|
from django.template.defaultfilters import slugify
|
|
|
|
from monstage.models import *
|
|
|
|
def addslashes(s):
|
|
d = {'"':'\\"', "\0":"\\\0", "\\":"\\\\", '\n':'\\n'}
|
|
return ''.join(d.get(c, c) for c in s)
|
|
|
|
def index(request):
|
|
stats = { 'num_stages': Stage.objects.filter(published=True).count() }
|
|
return render(request, 'monstage/index.html', {'stats': stats})
|
|
|
|
def forbidden(request, type):
|
|
context = {}
|
|
context[type] = True
|
|
return render(request, 'monstage/forbidden.html', context)
|
|
|
|
@login_required
|
|
def home(request):
|
|
stages = request.user.profil.stages.all()
|
|
return render(request, 'monstage/home.html', {"stages" : stages})
|
|
|
|
#
|
|
# Utilisateurs et profils
|
|
#
|
|
|
|
def profil(request, profil_id):
|
|
user = get_object_or_404( User, username = profil_id )
|
|
profil = user.profil
|
|
stages = Stage.objects.filter( profil_user = profil )
|
|
return render(request, 'monstage/profil.html', {'profil': profil, 'stages': stages})
|
|
|
|
@login_required
|
|
def profil_edit(request):
|
|
if request.POST:
|
|
user = request.user
|
|
user.first_name = request.POST['first_name']
|
|
user.last_name = request.POST['last_name']
|
|
user.save()
|
|
return HttpResponseRedirect(reverse('monstage:home'))
|
|
else:
|
|
(profil, _) = Normalien.objects.get_or_create( user = request.user )
|
|
return render(request, 'monstage/profil_edit.html', {'normalien': profil})
|
|
|
|
#
|
|
# Stages
|
|
#
|
|
|
|
@login_required
|
|
def stage(request, stage_id):
|
|
stage = get_object_or_404( Stage, pk = stage_id)
|
|
lieux_latlng = []
|
|
if not stage.published and not stage.profil_user == request.user.profil:
|
|
return forbidden(request, 'unpublished')
|
|
for lieu in stage.lieux.all():
|
|
# GEOS Format : (longitude, latitude)
|
|
lieux_latlng.append("%f, %f" % (lieu.coord.y, lieu.coord.x))
|
|
context = {
|
|
'stage': stage,
|
|
'modifiable': (stage.profil_user == request.user.profil),
|
|
'lieux_latlng': lieux_latlng,
|
|
'published':stage.published,
|
|
}
|
|
return render(request, 'monstage/stage.html', context)
|
|
|
|
class StageForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Stage
|
|
fields = ("type_stage", "start_date", "end_date", "matieres", "sujet", "thematiques", "encadrants")
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(StageForm, self).__init__(*args, **kwargs)
|
|
self.fields['matieres'].label = "Matières"
|
|
self.fields['thematiques'].help_text = "Une liste de tags séparés par des virgules (ou des espaces)"
|
|
self.fields['start_date'].widget.attrs['placeholder'] = "JJ/MM/AAAA"
|
|
self.fields['end_date'].widget.attrs['placeholder'] = "JJ/MM/AAAA"
|
|
|
|
@login_required
|
|
def stage_add(request):
|
|
if request.POST:
|
|
form = StageForm(request.POST)
|
|
if form.is_valid:
|
|
new_stage = form.save(commit = False)
|
|
new_stage.profil_user = request.user.profil
|
|
new_stage.save()
|
|
form.save_m2m()
|
|
return HttpResponseRedirect(reverse('monstage:stage_edit_lieu', args=(new_stage.id,)))
|
|
else:
|
|
form = StageForm()
|
|
return render(request, 'monstage/stage_add.html', { 'form': form })
|
|
|
|
@login_required
|
|
def stage_edit_desc(request, stage_id):
|
|
stage = get_object_or_404( Stage, pk = stage_id)
|
|
if stage.profil_user != request.user.profil:
|
|
return forbidden(request, 'notowned')
|
|
bullshit = ""
|
|
if request.POST:
|
|
form = StageForm(request.POST, instance = stage)
|
|
if form.is_valid():
|
|
form.save()
|
|
return HttpResponseRedirect(reverse('monstage:stage', args=(stage.id,)))
|
|
else:
|
|
form = StageForm(instance = stage)
|
|
return render(request, 'monstage/stage_edit_desc.html', { 'stage': stage, 'form':form })
|
|
|
|
class LieuStageForm(forms.Form):
|
|
lieu_id = forms.IntegerField(widget = HiddenInput())
|
|
delete = forms.BooleanField(help_text=(u'Supprimer ce lieu'), required = False, label = '')
|
|
|
|
def is_to_delete(self):
|
|
return self.cleaned_data['delete']
|
|
|
|
class LieuForm(forms.ModelForm):
|
|
latitude = forms.DecimalField()
|
|
longitude = forms.DecimalField()
|
|
delete = forms.BooleanField(help_text=(u'Supprimer ce lieu'), required = False, label = '')
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(LieuForm, self).__init__(*args, **kwargs)
|
|
self.fields['latitude'].widget = HiddenInput()
|
|
self.fields['longitude'].widget = HiddenInput()
|
|
lieu = kwargs.pop('instance', None)
|
|
if lieu:
|
|
# GEOS Format : (longitude, latitude)
|
|
self.fields['longitude'].initial = lieu.coord.x
|
|
self.fields['latitude'].initial = lieu.coord.y
|
|
|
|
class Meta:
|
|
model = Lieu
|
|
fields = ("name", "type_lieu", "ville", "pays")
|
|
|
|
def is_to_delete(self):
|
|
return self.cleaned_data['delete']
|
|
|
|
def save(self, *args, **kwargs):
|
|
lieu = super(LieuForm, self).save(commit=False, *args, **kwargs)
|
|
# GEOS Format : (longitude, latitude)
|
|
lieu.coord = GEOSGeometry('POINT(%f %f)' % (self.cleaned_data['longitude'], self.cleaned_data['latitude']), srid=4326)
|
|
return lieu
|
|
|
|
def lieux_candidats(request):
|
|
if request.GET:
|
|
lat = float(request.GET.get('lat', False))
|
|
lon = float(request.GET.get('lon', False))
|
|
if lat and lon:
|
|
# GEOS Format : (longitude, latitude)
|
|
coords = GEOSGeometry('POINT(%f %f)' % (lon, lat), srid=4326)
|
|
distance = {'km': 0.5}
|
|
lieux = Lieu.objects.filter(coord__distance_lte=(coords, measure.D(**distance)))
|
|
lieux = lieux.distance(coords).order_by('distance')
|
|
retour = [ {'name': addslashes(lieu.name), 'id': lieu.id, 'lat':str(lieu.coord.coords[0]), 'lon':str(lieu.coord.coords[1]), 'distance':lieu.distance } for lieu in lieux.distance(coords) ]
|
|
return render(request, 'monstage/lieux_proches.json', { 'lieux':retour }, content_type='text/plain; charset=utf-8')
|
|
return HttpResponseNotFound()
|
|
|
|
def stage_edit_lieu(request, stage_id):
|
|
stage = get_object_or_404( Stage, pk = stage_id)
|
|
bullshit = ''
|
|
if stage.profil_user != request.user.profil:
|
|
return forbidden(request, 'notowned')
|
|
if request.POST:
|
|
valid = True
|
|
prevLieuxStage = [k for k in stage.lieustage_set.all()]
|
|
lieuforms = []
|
|
deleted = []
|
|
for i in range(0, int(request.POST['numplaces'])): # Pour chaque lieu
|
|
toSave = False
|
|
if request.POST.get('%d-name' % i, False): # Lieu à créer
|
|
bullshit += str(i) + 'a '
|
|
lieuform = LieuForm(request.POST, prefix = str(i))
|
|
if not lieuform.is_valid():
|
|
valid = False
|
|
lieuforms.append(lieuform)
|
|
continue
|
|
if lieuform.is_to_delete(): # On ne fait rien : le lieu est nouveau donc n'était pas encore en mémoire
|
|
pass
|
|
else: # On crée un nouveau lieu
|
|
lieu = lieuform.save()
|
|
lieu.save()
|
|
lieuform.save_m2m()
|
|
toSave = True
|
|
lieuforms.append(LieuStageForm(initial = {'lieu_id': lieu.id}, prefix = str(i)))
|
|
elif request.POST.get('%d-lieu_id' % i, False): # Lien vers un lieu déjà existant
|
|
bullshit += str(i) + 'b '
|
|
lieuform = LieuStageForm(request.POST, prefix = str(i))
|
|
if not lieuform.is_valid():
|
|
bullshit += 'invalid '
|
|
valid = False
|
|
lieuforms.append(lieuform)
|
|
continue
|
|
if lieuform.is_to_delete(): # On transvase de prevLieuxStage vers deleted pour conserver l'assignation des commentaires
|
|
if len(prevLieuxStage) > 0:
|
|
deleted.append(prevLieuxStage.pop(0))
|
|
else: # On récupère le lieu concerné
|
|
lieuforms.append(lieuform)
|
|
lieu = Lieu.objects.get( pk = lieuform.cleaned_data['lieu_id'] )
|
|
toSave = True
|
|
if toSave: # Mise à jour des lieuStage
|
|
if len(prevLieuxStage) > 0: # On met à jour un lieuStage conservé (même lieu)
|
|
lieustage = prevLieuxStage.pop(0)
|
|
lieustage.lieu = lieu
|
|
lieustage.save()
|
|
elif len(deleted) > 0: # On réutilise l'emplacement d'un lieuStage qui a été supprimé (pour éviter de perdre les commentaires)
|
|
lieustage = deleted.pop(0)
|
|
lieustage.lieu = lieu
|
|
lieustage.save()
|
|
else: # On en crée un nouveau
|
|
LieuStage.objects.create(lieu = lieu, stage = stage)
|
|
if valid:
|
|
for lieustage in deleted: # On supprime effectivement les lieuStages non réutilisé
|
|
lieustage.delete()
|
|
return HttpResponseRedirect(reverse('monstage:stage_edit_feedback', args=(stage.id,)))
|
|
else:
|
|
lieuforms = [(lieu, LieuStageForm(initial={'lieu_id': lieu.id}, prefix=str(counter))) for counter, lieu in enumerate(stage.lieux.all())]
|
|
emptyform = LieuForm(prefix='{{ID}}')
|
|
emptylieu = LieuStageForm(prefix='{{ID}}')
|
|
return render(request, 'monstage/stage_edit_lieu.html', { 'stage': stage, 'debug': bullshit, 'lieuforms': lieuforms, 'emptyform': addslashes(emptyform.as_p()), 'emptylieu': addslashes(emptylieu.as_p()), 'numforms':len(lieuforms) })
|
|
|
|
class StageFeedbackForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Stage
|
|
fields = ("avis_encadrants", "avis_equipe", "avis_stage", "avis_admin")
|
|
|
|
class LieuStageFeedbackForm(forms.ModelForm):
|
|
class Meta:
|
|
model = LieuStage
|
|
fields = ("avis_global", "avis_lieudevie", "avis_lieustage", "avis_pratique", "avis_visite", "avis_anecdotes")
|
|
|
|
def stage_edit_feedback(request, stage_id):
|
|
stage = get_object_or_404( Stage, pk = stage_id)
|
|
if stage.profil_user != request.user.profil:
|
|
return forbidden(request, 'notowned')
|
|
if request.POST:
|
|
form_gen = StageFeedbackForm(request.POST, instance = stage, prefix = 'gen')
|
|
forms_lieux = [(LieuStageFeedbackForm(request.POST, instance = lieustage, prefix = lieustage.id), lieustage.lieu) for lieustage in stage.lieustage_set.all()]
|
|
valid = form_gen.is_valid()
|
|
for (form, _) in forms_lieux:
|
|
if not form.is_valid():
|
|
valid = False
|
|
if valid:
|
|
form_gen.save()
|
|
for (form, _) in forms_lieux:
|
|
form.save()
|
|
return HttpResponseRedirect(reverse('monstage:stage', args=(stage.id,)))
|
|
else:
|
|
form_gen = StageFeedbackForm(instance = stage, prefix = 'gen')
|
|
forms_lieux = [(LieuStageFeedbackForm(instance = lieustage, prefix = lieustage.id), lieustage.lieu) for lieustage in stage.lieustage_set.all()]
|
|
return render(request, 'monstage/stage_edit_feedback.html', { 'stage': stage, 'form_gen':form_gen, 'forms_lieux':forms_lieux })
|
|
|
|
def stage_publish(request, stage_id):
|
|
stage = get_object_or_404( Stage, pk = stage_id)
|
|
if request.POST:
|
|
publishit = request.POST.get('publishit', False)
|
|
if publishit == "yes":
|
|
stage.published = True
|
|
stage.save()
|
|
elif publishit == "no":
|
|
stage.published = False
|
|
stage.save()
|
|
return HttpResponseRedirect(reverse('monstage:stage', args=(stage_id,)))
|
|
|
|
def detail(request, question_id):
|
|
question = get_object_or_404(Question, pk=question_id)
|
|
return render(request, 'monstage/detail.html', {'question': question})
|
|
|
|
|
|
#
|
|
# Recherche de stages
|
|
#
|
|
from django.db.models import Q
|
|
|
|
class SearchForm(forms.Form):
|
|
matiere = forms.ModelChoiceField(label='Matière :', required=False, queryset = StageMatiere.objects.all(), empty_label="Toute matière")
|
|
thematiques = forms.CharField(label='Thématiques :', required=False)
|
|
|
|
latitude = forms.DecimalField(widget=HiddenInput(), required=False)
|
|
longitude = forms.DecimalField(widget=HiddenInput(), required=False)
|
|
lieu = forms.CharField(label=u'A proximité de :', required=False)
|
|
tolerance = forms.DecimalField(label='Dans un rayon de (en km) :', initial=100, required=False)
|
|
|
|
@login_required
|
|
def search(request):
|
|
stages = None
|
|
if request.GET:
|
|
form = SearchForm(request.GET)
|
|
if form.is_valid():
|
|
lon = form.cleaned_data['longitude']
|
|
lat = form.cleaned_data['latitude']
|
|
lieu = form.cleaned_data['lieu']
|
|
stages = Stage.objects.filter(published=True)
|
|
if lat and lon and lieu:
|
|
coords = GEOSGeometry('POINT(%f %f)' % (lon, lat), srid=4326)
|
|
distance = {'km': form.cleaned_data['tolerance']}
|
|
lieux = Lieu.objects.filter(coord__distance_lte=(coords, measure.D(**distance)))
|
|
lieux = lieux.distance(coords).order_by('distance')
|
|
stages = stages.filter(lieux__in=lieux)
|
|
matiere = form.cleaned_data['matiere']
|
|
if matiere:
|
|
stages = stages.filter(matieres=matiere)
|
|
thematiques = form.cleaned_data['thematiques'].split(',')
|
|
if thematiques:
|
|
q = Q()
|
|
for thematique in thematiques:
|
|
q |= Q(thematiques__slug__contains = slugify(thematique))
|
|
stages = stages.filter(q).distinct()
|
|
stages = stages.all()
|
|
else:
|
|
form = SearchForm()
|
|
return render(request, 'monstage/search.html', {'form':form, 'resultats':stages})
|
|
|
|
#
|
|
# Feedback
|
|
#
|
|
|
|
class FeedbackForm(forms.Form):
|
|
message = forms.CharField(label='Commentaire')
|
|
|
|
@login_required
|
|
def send_feedback(request):
|
|
redirection = request.GET.get('next', reverse('monstage:index')) if request.GET else reverse('monstage:index')
|
|
if request.POST:
|
|
form = FeedbackForm(request.POST, prefix="feedback")
|
|
if form.is_valid():
|
|
dests = [ mail for (nom, mail) in settings.ADMINS ]
|
|
send_mail('Commentaire à propos d\'experiENS', form.cleaned_data["message"], ("%s@clipper.ens.fr" % (request.user.username,)), dests)
|
|
return HttpResponseRedirect(redirection+"?feedback_sent=1")
|
|
return HttpResponseRedirect(redirection+"?feedback_error=1")
|
|
return HttpResponseRedirect(redirection) |