# 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)