From e151fc3d1e8312046b8388e091edccfbfbceee9a Mon Sep 17 00:00:00 2001 From: Evarin Date: Tue, 11 Apr 2017 00:20:14 +0200 Subject: [PATCH] Modification des stages, bases pour la gestion des lieux --- avisstage/models.py | 38 ++++---- .../templates/avisstage/detail/profil.html | 2 +- .../templates/avisstage/detail/stage.html | 42 +++++---- .../templates/avisstage/formulaires/lieu.html | 24 +++++ avisstage/urls.py | 2 + avisstage/views.py | 89 +++++++++++++++++-- avisstage/widget.py | 47 ++++++++++ 7 files changed, 204 insertions(+), 40 deletions(-) create mode 100644 avisstage/templates/avisstage/formulaires/lieu.html create mode 100644 avisstage/widget.py diff --git a/avisstage/models.py b/avisstage/models.py index f2f8aea..8d09372 100644 --- a/avisstage/models.py +++ b/avisstage/models.py @@ -36,7 +36,7 @@ class Normalien(models.Model): verbose_name_plural = u"Profils élèves" def __unicode__(self): - return unicode("%s (%s)", self.nom, self.user.username) + return u"%s (%s)" % (self.nom, self.user.username) # Hook à la création d'un nouvel utilisateur : récupération de ses infos par LDAP def create_user_profile(sender, instance, created, **kwargs): @@ -96,11 +96,12 @@ class Lieu(models.Model): # Coordonnées objects = geomodels.GeoManager() # Requis par GeoDjango coord = geomodels.PointField(u"Coordonnées", - geography=True) + geography=True, + srid = 4326) def __unicode__(self): - return u"%s (%s)" % (self.name, self.ville) + return u"%s (%s)" % (self.nom, self.ville) class Meta: verbose_name = "Lieu" @@ -161,7 +162,7 @@ class Stage(models.Model): return reverse('avisstage:stage', self) def __unicode__(self): - return u"%s (par %s)" % (self.sujet, self.auteur.user_id) + return u"%s (par %s)" % (self.sujet, self.auteur.user.username) class Meta: verbose_name = "Stage" @@ -173,34 +174,35 @@ class Stage(models.Model): class AvisStage(models.Model): stage = models.OneToOneField(Stage, related_name="avis_stage") - chapo = models.TextField(u"En quelques mots") - avis_personnes = RichTextField(u"Les encadrants et l'équipe") - avis_sujet = RichTextField(u"Le sujet de stage") - avis_admin = RichTextField(u"Formalités et administration") + chapo = models.TextField(u"En quelques mots", blank=True) + avis_personnes = RichTextField(u"Les encadrants et l'équipe", blank=True) + avis_sujet = RichTextField(u"Le sujet de stage", blank=True) + avis_admin = RichTextField(u"Formalités et administration", blank=True) - les_plus = models.TextField(u"Les plus du stage") - les_moins = models.TextField(u"Les moins du stage") + les_plus = models.TextField(u"Les plus du stage", blank=True) + les_moins = models.TextField(u"Les moins du stage", blank=True) def __unicode__(self): - return u"Avis sur {%s} par %" % (stage.sujet, stage.auteur.user_id) + return u"Avis sur {%s} par %" % (stage.sujet, stage.auteur.user.username) class AvisLieu(models.Model): stage = models.ForeignKey(Stage) lieu = models.ForeignKey(Lieu) order = models.IntegerField("Ordre", default=0) - chapo = models.TextField(u"En quelques mots") - avis_lieustage = RichTextField(u"Le lieu du stage") - avis_pratique = RichTextField(u"S'installer - conseils pratiques") - avis_tourisme = RichTextField(u"Dans les parages") + chapo = models.TextField(u"En quelques mots", blank=True) + avis_lieustage = RichTextField(u"Le lieu du stage", blank=True) + avis_pratique = RichTextField(u"S'installer - conseils pratiques", + blank=True) + avis_tourisme = RichTextField(u"Dans les parages", blank=True) - les_plus = models.TextField(u"Les plus du lieu") - les_moins = models.TextField(u"Les moins du lieu") + les_plus = models.TextField(u"Les plus du lieu", blank=True) + les_moins = models.TextField(u"Les moins du lieu", blank=True) class Meta: verbose_name = "Avis sur un lieu de stage" verbose_name_plural = "Avis sur un lieu de stage" def __unicode__(self): - return u"Avis sur {%s} par %" % (lieu.nom, stage.auteur.user_id) + return u"Avis sur {%s} par %s" % (self.lieu.nom, self.stage.auteur.user_id) diff --git a/avisstage/templates/avisstage/detail/profil.html b/avisstage/templates/avisstage/detail/profil.html index d872b18..c0e7d31 100644 --- a/avisstage/templates/avisstage/detail/profil.html +++ b/avisstage/templates/avisstage/detail/profil.html @@ -3,7 +3,7 @@ {% block content %}

Profil de {{ object.nom }}

- {% if object.user == request.user %} + {% if object.user == user %}

Modifier mes infos

{% endif %}

Promotion : {{ object.promotion }}

diff --git a/avisstage/templates/avisstage/detail/stage.html b/avisstage/templates/avisstage/detail/stage.html index d872b18..835916c 100644 --- a/avisstage/templates/avisstage/detail/stage.html +++ b/avisstage/templates/avisstage/detail/stage.html @@ -2,22 +2,32 @@ {% load staticfiles %} {% block content %} -

Profil de {{ object.nom }}

- {% if object.user == request.user %} -

Modifier mes infos

+

{{ object.sujet }}

+ {% if object.auteur == user.profil %} +

Modifier ce stage

{% endif %} -

Promotion : {{ object.promotion }}

-

Adresse de contact : {{ object.mail }}

-
-

Ses stages

- +
+
+

À propos du stage

+

{{ object.auteur.nom }} a fait ce stage du {{ object.date_debut }} au {{ object.date_fin }}, supervisé par {{ object.encadrants }}

+
    + {% for matiere in object.matieres.all %} +
  • {{ matiere.nom }}
  • + {% endfor %} + {% for thematique in object.thematiques.all %} +
  • {{ thematique.name }}
  • + {% endfor %} +
+
+ + {% with object.avis_stage as avis %} +
+ {% if avis.chapo %} +

+ {{ avis.chapo }} +

+ {% endif %} +
+ {% endwith %}
{% endblock %} diff --git a/avisstage/templates/avisstage/formulaires/lieu.html b/avisstage/templates/avisstage/formulaires/lieu.html new file mode 100644 index 0000000..00d9c69 --- /dev/null +++ b/avisstage/templates/avisstage/formulaires/lieu.html @@ -0,0 +1,24 @@ +{% extends "avisstage/base.html" %} +{% load staticfiles %} + +{% block extra_head %} + + + + +{% endblock %} + +{% block content %} +

Ajouter un stage

+
+ {% csrf_token %} + {{ form.as_p }} + +
+{% endblock %} diff --git a/avisstage/urls.py b/avisstage/urls.py index 1dd50d4..76f9c92 100644 --- a/avisstage/urls.py +++ b/avisstage/urls.py @@ -6,7 +6,9 @@ urlpatterns = [ url(r'^perso/$', views.perso, name='perso'), url(r'^stage/nouveau/$', views.StageAjout.as_view(), name='stage_ajout'), url(r'^stage/(?P\w+)/$', views.StageView.as_view(), name='stage'), + url(r'^stage/(?P\w+)/edit/$', views.StageEdit.as_view(), name='stage_edit'), + url(r'^lieu/ajout/$', views.LieuAjout.as_view(), name='lieu_ajout'), url(r'^profil/show/(?P\w+)/$', views.ProfilView.as_view(), name='profil'), url(r'^profil/edit/$', views.ProfilEdit.as_view(), name='profil_edit'), diff --git a/avisstage/views.py b/avisstage/views.py index 2b0fc60..4d65d10 100644 --- a/avisstage/views.py +++ b/avisstage/views.py @@ -1,3 +1,5 @@ +# coding: utf-8 + from django.shortcuts import render from django.views.generic import DetailView @@ -7,7 +9,8 @@ from django.urls import reverse from django.contrib.auth.decorators import login_required from braces.views import LoginRequiredMixin -from avisstage.models import Normalien, Stage +from avisstage.models import Normalien, Stage, Lieu, AvisLieu, AvisStage +from widget import LatLonField # Page d'accueil def index(request): @@ -49,15 +52,58 @@ class StageForm(forms.ModelForm): class Meta: model = Stage - fields = ['sujet', 'date_debut', 'date_fin', 'type_stage', 'thematiques', 'matieres', 'encadrants'] + fields = ['sujet', 'date_debut', 'date_fin', 'type_stage', 'thematiques', 'matieres', 'encadrants', 'lieux'] def __init__(self, *args, **kwargs): - self.request = kwargs.pop("request") + if "request" in kwargs: + self.request = kwargs.pop("request") super(StageForm, self).__init__(*args, **kwargs) + if hasattr(self, 'instance'): + self.prev_lieux = self.instance.avislieu_set.all() + else: + self.prev_lieux = None def save(self, commit=True): - self.instance.auteur = self.request.user.profil - super(StageForm, self).save(commit=commit) + if self.instance.id is None: + self.instance.auteur = self.request.user.profil + + stage = super(StageForm, self).save(commit=False) + + if commit: + stage.save() + + # Lecture des lieux, conservation des critiques précédentes + alieux = self.cleaned_data['lieux'] + new_lieux = [] + #print self.instance.lieux + if not (self.prev_lieux is None) and len(self.prev_lieux) > 0: + old_avislieux = {avis.lieu_id: avis for avis in self.prev_lieux} + for (k, nlieu) in enumerate(alieux): + if nlieu in old_avislieux: + # Ce lien existait déjà avant : rien à faire + del old_avislieux[nlieu] + else: + # C'est un nouveau lien + new_lieux.append(nlieu) + + old_avislieux = old_avislieux.values() + for k, avislieu in enumerate(old_avislieux): + if k > len(new_lieux): + # Avis à supprimer + avislieu.delete() + else: + # On récupère le(s) avis pour le(s) nouveau(x) lieu(x) + avislieu.lieu = new_lieux[k] + avislieu.save() + new_lieux = new_lieux[len(old_avislieux):] + else: + new_lieux = alieux + print(new_lieux) + # On crée les nouveaux avislieu qui vont bien + AvisLieu.objects.bulk_create([AvisLieu(lieu=lieu, stage=stage) for lieu in new_lieux]) + + + class StageAjout(CreateView, LoginRequiredMixin): model = Stage @@ -69,10 +115,43 @@ class StageAjout(CreateView, LoginRequiredMixin): kwargs.update({'request': self.request}) return kwargs +class StageEdit(UpdateView, LoginRequiredMixin): + model = Stage + form_class = StageForm + template_name = 'avisstage/formulaires/stage.html' + + def get_queryset(self): + # Autorise les modifications des seuls stages qu'on "possède" + queryset = super(StageEdit, self).get_queryset() + return queryset.filter(auteur=self.request.user.profil) + + def get_form_kwargs(self): + kwargs = super(StageEdit, self).get_form_kwargs() + kwargs.update({'request': self.request}) + return kwargs + + def get_success_url(self): + return reverse('avisstage:stage', self.instance.id) + class StageView(DetailView, LoginRequiredMixin): model = Stage template_name = 'avisstage/detail/stage.html' + + +# Lieux des stages +class LieuForm(forms.ModelForm): + coord = LatLonField() + class Meta: + model = Lieu + fields = ['nom', 'type_lieu', 'ville', 'pays', 'coord'] + +class LieuAjout(CreateView, LoginRequiredMixin): + model = Lieu + form_class = LieuForm + template_name = 'avisstage/formulaires/lieu.html' + + def recherche(request): return render(request, 'avisstage/recherche.html') diff --git a/avisstage/widget.py b/avisstage/widget.py new file mode 100644 index 0000000..528066e --- /dev/null +++ b/avisstage/widget.py @@ -0,0 +1,47 @@ +from django import forms +from django.core import validators + +class LatLonWidget(forms.MultiWidget): + """ + A Widget that splits Point input into two latitude/longitude boxes. + """ + + def __init__(self, attrs=None, date_format=None, time_format=None): + widgets = (forms.TextInput(attrs=attrs), + forms.TextInput(attrs=attrs)) + super(LatLonWidget, self).__init__(widgets, attrs) + + def decompress(self, value): + if value: + return tuple(reversed(value.coords)) + return (None, None) + + +class LatLonField(forms.MultiValueField): + + widget = LatLonWidget + srid = 4326 + + default_error_messages = { + 'invalid_latitude' : (u'Entrez une latitude valide.'), + 'invalid_longitude' : (u'Entrez une longitude valide.'), + } + + def __init__(self, *args, **kwargs): + fields = (forms.FloatField(min_value=-90, max_value=90), + forms.FloatField(min_value=-180, max_value=180)) + super(LatLonField, self).__init__(fields, *args, **kwargs) + + def compress(self, data_list): + if data_list: + # Raise a validation error if latitude or longitude is empty + # (possible if LatLongField has required=False). + if data_list[0] in validators.EMPTY_VALUES: + raise forms.ValidationError(self.error_messages['invalid_latitude']) + if data_list[1] in validators.EMPTY_VALUES: + raise forms.ValidationError(self.error_messages['invalid_longitude']) + # SRID=4326;POINT(1.12345789 1.123456789) + srid_str = 'SRID=%d'%self.srid + point_str = 'POINT(%f %f)'%tuple(reversed(data_list)) + return ';'.join([srid_str, point_str]) + return None