From be57b976d28627b2737ca7aa0dbf296b2ceaa8fd Mon Sep 17 00:00:00 2001 From: Evarin Date: Thu, 20 Apr 2017 23:04:07 +0200 Subject: [PATCH] Feedback et autres trucs --- avisstage/api.py | 52 ++++++++++++++-- avisstage/forms.py | 4 ++ avisstage/sass/screen.scss | 19 +++++- avisstage/static/css/screen.css | 37 ++++++++---- avisstage/static/js/select_lieu.js | 24 ++++++-- avisstage/templates/avisstage/base.html | 28 ++------- .../templates/avisstage/detail/profil.html | 2 + .../templates/avisstage/detail/stage.html | 2 + .../avisstage/formulaires/stage.html | 15 +++-- avisstage/templates/avisstage/index.html | 2 + avisstage/templates/avisstage/perso.html | 2 + avisstage/templates/avisstage/recherche.html | 7 ++- .../templatetags/widget_feedback.html | 60 +++++++++++++++++++ .../avisstage/templatetags/widget_lieu.html | 1 + avisstage/templatetags/avisstage_tags.py | 7 ++- avisstage/urls.py | 2 + avisstage/views.py | 32 +++++++++- experiENS/settings_dev.py | 2 + 18 files changed, 243 insertions(+), 55 deletions(-) create mode 100644 avisstage/templates/avisstage/templatetags/widget_feedback.html diff --git a/avisstage/api.py b/avisstage/api.py index 49b96e4..8a47666 100644 --- a/avisstage/api.py +++ b/avisstage/api.py @@ -1,21 +1,27 @@ # coding: utf-8 from tastypie.resources import ModelResource -from avisstage.models import Lieu +from avisstage.models import Lieu, Stage, Normalien, StageMatiere + +from tastypie.authentication import SessionAuthentication from django.contrib.gis import geos +from tastypie import fields, utils class LieuResource(ModelResource): + stages = fields.ToManyField("avisstage.api.StageResource", "stages", use_in="detail", full=True) + class Meta: queryset = Lieu.objects.all() resource_name = "lieu" fields = ["nom", "pays", "coord", "type_lieu", "id"] - + authentication = SessionAuthentication() + def build_filters(self, filters=None, **kwargs): if filters is None: filters = {} - orm_filters = super(LieuResource, self).build_filters() + orm_filters = super(LieuResource, self).build_filters(filters, **kwargs) if "lng" in filters and "lat" in filters: lat = float(filters['lat']) @@ -40,5 +46,43 @@ class LieuResource(ModelResource): bundle.data['distance'] = self.reference_point.distance(bundle.obj.coord) bundle.data["pays_nom"] = obj.get_pays_display() bundle.data["type_lieu_nom"] = obj.get_type_lieu_display() - + bundle.data["num_stages"] = obj.stages.filter(public=True).count() + return bundle + +class StageResource(ModelResource): + class Meta: + queryset = Stage.objects.filter(public=True) + resource_name = "stage" + fields = ["sujet", "date_debut", "date_fin", "matieres", "id"] + authentication = SessionAuthentication() + + + def build_filters(self, filters=None, **kwargs): + if filters is None: + filters = {} + + orm_filters = super(StageResource, self).build_filters(filters, **kwargs) + + if "lieux" in filters: + flieux = map(int, filters['lieux'].split(',')) + orm_filters['lieux__id__in'] = flieux + + return orm_filters + + def dehydrate(self, bundle): + bundle = super(StageResource, self).dehydrate(bundle) + obj = bundle.obj + bundle.data['auteur'] = obj.auteur.nom + bundle.data['thematiques'] = list(obj.thematiques.all().values_list("name", flat=True)) + bundle.data['matieres'] = list(obj.matieres.all().values_list("nom", flat=True)) + return bundle + +class AuteurResource(ModelResource): + stages = fields.ToManyField("avisstage.api.StageResource", "stages", use_in="detail") + + class Meta: + queryset = Normalien.objects.all() + resource_name = "profil" + fields = ["id", "nom", "stages"] + authentication = SessionAuthentication() diff --git a/avisstage/forms.py b/avisstage/forms.py index 3402464..aca828f 100644 --- a/avisstage/forms.py +++ b/avisstage/forms.py @@ -82,3 +82,7 @@ class LieuForm(forms.ModelForm): model = Lieu fields = ['nom', 'type_lieu', 'ville', 'pays', 'coord'] +class FeedbackForm(forms.Form): + objet = forms.CharField(label="Objet", required=True) + message = forms.CharField(label="Message", required=True, widget=forms.widgets.Textarea()) + diff --git a/avisstage/sass/screen.scss b/avisstage/sass/screen.scss index c34b0c3..cfc1ebb 100644 --- a/avisstage/sass/screen.scss +++ b/avisstage/sass/screen.scss @@ -5,8 +5,8 @@ $fond: #8fcc33; $barre: darken($fond, 10%); $compl: #f99b20; -$jaune: #007afc; -$vert: #09f7b0; +$jaune: #fff60a; +$vert: #1a82dd;//09f7b0; $rouge: #f70978; $textfont: 'Dosis', sans-serif; @@ -584,6 +584,9 @@ div.as-results { .hidden { display: none; } + .masked { + visibility: hidden; + } } #avis_lieu_vide { @@ -612,3 +615,15 @@ a.lieu-change { width:100%; height: 600px; } + +#feedback-button { + position:fixed; + left:0; + top:30%; + color:#fff; + z-index:4; + background: #000; + padding: 14px; + transform: rotateZ(90deg); + transform-origin: bottom left; +} diff --git a/avisstage/static/css/screen.css b/avisstage/static/css/screen.css index cfa0523..3b14d21 100644 --- a/avisstage/static/css/screen.css +++ b/avisstage/static/css/screen.css @@ -217,7 +217,7 @@ header h1 { /* line 159, ../../sass/screen.scss */ .stagelist li.stage-publie:before { content: "Publié"; - background: #3af9c0; + background: #419be9; } /* line 163, ../../sass/screen.scss */ .stagelist li.stage-ajout:before { @@ -231,7 +231,7 @@ article.stage { } /* line 175, ../../sass/screen.scss */ article.stage h2 { - background: #6ea3db; + background: #ddda78; color: #fff; padding: 10px 20px; margin: -20px; @@ -353,13 +353,13 @@ article.stage section .plusmoins > div > div p { } /* line 289, ../../sass/screen.scss */ article.stage section .plusmoins .plus > div { - background: #07df9f; + background: #1775c6; } /* line 292, ../../sass/screen.scss */ article.stage section .plusmoins .plus:before { content: "Les +"; vertical-align: bottom; - color: #06c78d; + color: #1567af; } /* line 299, ../../sass/screen.scss */ article.stage section .plusmoins .moins > div { @@ -381,8 +381,8 @@ article.stage section .plusmoins .moins:before { } /* line 318, ../../sass/screen.scss */ .edit-box.public { - background: #cffdef; - border: 1px solid #06ad7b; + background: #cae3f9; + border: 1px solid #125b9b; } /* line 323, ../../sass/screen.scss */ .edit-box.prive { @@ -657,13 +657,17 @@ div.as-results ul li.as-message { .lieu-ui .hidden { display: none; } +/* line 587, ../../sass/screen.scss */ +.lieu-ui .masked { + visibility: hidden; +} -/* line 589, ../../sass/screen.scss */ +/* line 592, ../../sass/screen.scss */ #avis_lieu_vide { display: none; } -/* line 593, ../../sass/screen.scss */ +/* line 596, ../../sass/screen.scss */ a.lieu-change { color: #fff; background: #f99b20; @@ -676,14 +680,27 @@ a.lieu-change { border-radius: 5px; margin-right: 7px; } -/* line 605, ../../sass/screen.scss */ +/* line 608, ../../sass/screen.scss */ a.lieu-change.ajout:before { content: "+"; margin-right: 5px; } -/* line 611, ../../sass/screen.scss */ +/* line 614, ../../sass/screen.scss */ #stages-map { width: 100%; height: 600px; } + +/* line 619, ../../sass/screen.scss */ +#feedback-button { + position: fixed; + left: 0; + top: 30%; + color: #fff; + z-index: 4; + background: #000; + padding: 14px; + transform: rotateZ(90deg); + transform-origin: bottom left; +} diff --git a/avisstage/static/js/select_lieu.js b/avisstage/static/js/select_lieu.js index 3ebbb36..45a627c 100644 --- a/avisstage/static/js/select_lieu.js +++ b/avisstage/static/js/select_lieu.js @@ -1,5 +1,5 @@ function SelectLieuWidget(STATIC_ROOT, API_LIEU, target, callback) { - var map, input, autocomplete; + var map, input, autocomplete, map_el; var $el = $(target); var ui_el = $el.find('.lieu-ui'); var form_el = $el.find('form'); @@ -18,7 +18,7 @@ function SelectLieuWidget(STATIC_ROOT, API_LIEU, target, callback) { closer.on("click", closeWidget).attr("href", "javascript:void(0);"); $el.find(".window-bg").on("click", closeWidget); - var map_el = $("
", {class: "map"}); + map_el = $("
", {class: "map"}); input = $("", {type:"text", placeholder:"Chercher un établissement..."}); @@ -37,12 +37,21 @@ function SelectLieuWidget(STATIC_ROOT, API_LIEU, target, callback) { autocomplete.addListener('place_changed', handlePlaceSearch); } + function showMessage(message) { + content_el.find(".message").text(message); + } + + function hideMessage() { + showMessage(""); + } + function setLieuOrigine (lieu) { map.panTo(lieu.coord); lieuSurCarte(lieu, greenIcon); } function handleLieuOrigine(data) { + hideMessage(); lieux_db[data.id] = data; setLieuOrigine(data); askForSuggestions(data.coord); @@ -52,16 +61,19 @@ function SelectLieuWidget(STATIC_ROOT, API_LIEU, target, callback) { $el.addClass("visible").removeClass("ajout"); if(!ui_ready) initUI(); + hideMessage(); ui_el.removeClass("hidden"); form_el.detach(); if (lieu_id === undefined) { $el.find("h3").text("Ajouter un lieu"); + map_el.addClass("masked"); } else { lieu_id = lieu_id * 1; $el.find("h3").text("Modifier le lieu"); if(lieux_db[lieu_id] === undefined) { $.getJSON(API_LIEU + lieu_id + "/?format=json", handleLieuOrigine); + showMessage("Chargement..."); ui_el.addClass("hidden"); } else { handleLieuOrigine(lieux_db[lieu_id]); @@ -150,6 +162,7 @@ function SelectLieuWidget(STATIC_ROOT, API_LIEU, target, callback) { } function lieuSurCarte(data, icone) { + map_el.removeClass("masked"); // data : des données sur un lieu, sérialisé comme par tastypie if(data.marqueur !== undefined) { if(map.hasLayer(data.marqueur)) @@ -219,15 +232,16 @@ function SelectLieuWidget(STATIC_ROOT, API_LIEU, target, callback) { form_el.serialize(), onLieuCreated); form_el.detach(); - content_el.append($("

").text("Envoi en cours...")); + showMessage("Envoi en cours..."); return false; } function onLieuCreated(data) { console.log(data); - if(data.success = false) + if(data.success = false) { + content_el.find(".message").text("Erreur : "+data.errors); content_el.append(form_el); - else { + } else { lieux_db.suggestion.id = data.id; callback(lieux_db.suggestion); } diff --git a/avisstage/templates/avisstage/base.html b/avisstage/templates/avisstage/base.html index b0bb750..bdbab14 100644 --- a/avisstage/templates/avisstage/base.html +++ b/avisstage/templates/avisstage/base.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load staticfiles avisstage_tags %} @@ -24,14 +24,14 @@

- {# BLOCK FEEDBACK #} - {% if request.user.is_authenticated %} -
-
-
-

Envoyer un avis sur le siteX

-
- {% csrf_token %} -

Connecté en tant que {{ user.profil }}

-

- -
-
-
-
- - Feedback - + {% if user.is_authenticated %} + {% feedback_widget %} {% endif %} - {# ENDBLOCK FEEDBACK #} -
{% if messages %} diff --git a/avisstage/templates/avisstage/detail/profil.html b/avisstage/templates/avisstage/detail/profil.html index c0e7d31..b598c88 100644 --- a/avisstage/templates/avisstage/detail/profil.html +++ b/avisstage/templates/avisstage/detail/profil.html @@ -1,6 +1,8 @@ {% extends "avisstage/base.html" %} {% load staticfiles %} +{% block title %}Profil de {{ object.nom }} - ExperiENS{% endblock %} + {% block content %}

Profil de {{ object.nom }}

{% if object.user == user %} diff --git a/avisstage/templates/avisstage/detail/stage.html b/avisstage/templates/avisstage/detail/stage.html index 08a4b06..4ef5f6e 100644 --- a/avisstage/templates/avisstage/detail/stage.html +++ b/avisstage/templates/avisstage/detail/stage.html @@ -1,6 +1,8 @@ {% extends "avisstage/base.html" %} {% load staticfiles %} +{% block title %}{{ object.sujet }} - ExperiENS{% endblock %} + {% block extra_head %} diff --git a/avisstage/templates/avisstage/formulaires/stage.html b/avisstage/templates/avisstage/formulaires/stage.html index 246735c..51f033c 100644 --- a/avisstage/templates/avisstage/formulaires/stage.html +++ b/avisstage/templates/avisstage/formulaires/stage.html @@ -12,8 +12,11 @@ @@ -34,7 +37,9 @@ var lieux = data.objects; $.each(lieux, function(i, item) { var marqueur = L.marker(item.coord, {icon: greenIcon}); - marqueur.bindPopup("

"+item.nom+"

"); + var txt = item.num_stages > 1 ? item.num_stages+" stages ici": "1 stage ici"; + marqueur.bindPopup("

"+item.nom+"

"+ + "

"+txt+"

"); marqueurs.addLayer(marqueur); }); map.addLayer(marqueurs); diff --git a/avisstage/templates/avisstage/templatetags/widget_feedback.html b/avisstage/templates/avisstage/templatetags/widget_feedback.html new file mode 100644 index 0000000..ba1ba5a --- /dev/null +++ b/avisstage/templates/avisstage/templatetags/widget_feedback.html @@ -0,0 +1,60 @@ +{% load staticfiles %} + +
+ +
+
+ +

Donnez votre avis !

+ +
+
+Feedback diff --git a/avisstage/templates/avisstage/templatetags/widget_lieu.html b/avisstage/templates/avisstage/templatetags/widget_lieu.html index 2e4ec14..0611734 100644 --- a/avisstage/templates/avisstage/templatetags/widget_lieu.html +++ b/avisstage/templates/avisstage/templatetags/widget_lieu.html @@ -5,6 +5,7 @@

Choisir un lieu

+
{% load staticfiles %} diff --git a/avisstage/templatetags/avisstage_tags.py b/avisstage/templatetags/avisstage_tags.py index e11f1ab..82c3b10 100644 --- a/avisstage/templatetags/avisstage_tags.py +++ b/avisstage/templatetags/avisstage_tags.py @@ -1,6 +1,6 @@ from django import template -from avisstage.forms import LieuForm +from avisstage.forms import LieuForm, FeedbackForm register = template.Library() @@ -8,3 +8,8 @@ register = template.Library() def lieu_widget(): form = LieuForm() return {"form": form} + +@register.inclusion_tag('avisstage/templatetags/widget_feedback.html') +def feedback_widget(): + form = FeedbackForm() + return {"form": form} diff --git a/avisstage/urls.py b/avisstage/urls.py index bf4aafa..b479670 100644 --- a/avisstage/urls.py +++ b/avisstage/urls.py @@ -4,6 +4,8 @@ from tastypie.api import Api v1_api = Api(api_name='v1') v1_api.register(api.LieuResource()) +v1_api.register(api.StageResource()) +v1_api.register(api.AuteurResource()) urlpatterns = [ url(r'^$', views.index, name='index'), diff --git a/avisstage/views.py b/avisstage/views.py index 3d65f8d..3a3f97e 100644 --- a/avisstage/views.py +++ b/avisstage/views.py @@ -9,9 +9,10 @@ from django.urls import reverse 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 avisstage.models import Normalien, Stage, Lieu, AvisLieu, AvisStage -from avisstage.forms import StageForm, LieuForm, AvisStageForm, AvisLieuForm +from avisstage.forms import StageForm, LieuForm, AvisStageForm, AvisLieuForm, FeedbackForm # Page d'accueil def index(request): @@ -123,9 +124,34 @@ def publier_stage(request, pk): stage.public = False stage.save() return redirect(reverse("avisstage:stage", kwargs={"pk": pk})) - + +@login_required def recherche(request): return render(request, 'avisstage/recherche.html') + +# FEEDBACK +@login_required def feedback(request): - return render(request, 'avisstage/feedback.html') + 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}) diff --git a/experiENS/settings_dev.py b/experiENS/settings_dev.py index e9abd38..5bea4d0 100644 --- a/experiENS/settings_dev.py +++ b/experiENS/settings_dev.py @@ -22,3 +22,5 @@ MIDDLEWARE_CLASSES += ( SPATIALITE_LIBRARY_PATH = 'mod_spatialite' STATIC_ROOT = "/home/evarin/Bureau/experiENS/static/" + +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'