Ajout du champ préstage, améliorations UX

This commit is contained in:
Evarin 2017-10-02 23:59:20 +02:00
parent 7f69e44094
commit 0c70d1b76b
11 changed files with 110 additions and 116 deletions

View file

@ -57,6 +57,8 @@ class StageForm(forms.ModelForm):
# Date de modification # Date de modification
self.instance.date_maj = timezone.now() self.instance.date_maj = timezone.now()
self.instance.update_stats(False)
stage = super(StageForm, self).save(commit=commit) stage = super(StageForm, self).save(commit=commit)
return stage return stage
@ -64,12 +66,13 @@ class StageForm(forms.ModelForm):
class AvisStageForm(HTMLTrimmerForm): class AvisStageForm(HTMLTrimmerForm):
class Meta: class Meta:
model = AvisStage model = AvisStage
fields = ['chapo', 'avis_sujet', 'avis_ambiance', 'avis_admin', 'les_plus', 'les_moins'] fields = ['chapo', 'avis_sujet', 'avis_ambiance', 'avis_admin', 'avis_prestage', 'les_plus', 'les_moins']
help_texts = { help_texts = {
"chapo": u"\"Trop long, pas lu\" : une accroche résumant ce que vous avez pensé de ce séjour", "chapo": u"\"Trop long, pas lu\" : une accroche résumant ce que vous avez pensé de ce séjour",
"avis_ambiance": u"Avez-vous passé un bon moment à ce travail ? Étiez-vous assez guidé⋅e ? Aviez-vous un bon contact avec vos encadrant⋅e⋅s ? Y avait-il une bonne ambiance dans l'équipe ?", "avis_ambiance": u"Avez-vous passé un bon moment à ce travail ? Étiez-vous assez guidé⋅e ? Aviez-vous un bon contact avec vos encadrant⋅e⋅s ? Y avait-il une bonne ambiance dans l'équipe ?",
"avis_sujet": u"Quelle était votre mission ? Qu'en avez-vous retiré ? Le travail correspondait-il à vos attentes ? Était-ce à votre niveau, trop dur, trop facile ?", "avis_sujet": u"Quelle était votre mission ? Qu'en avez-vous retiré ? Le travail correspondait-il à vos attentes ? Était-ce à votre niveau, trop dur, trop facile ?",
"avis_admin": u"Avez-vous commencé votre travail à la date prévue ? Était-ce compliqué d'obtenir les documents nécessaires (visa, contrats, etc) ? L'administration de l'établissement vous a-t-elle aidé⋅e ? Étiez-vous rémunéré⋅e ?", "avis_admin": u"Avez-vous commencé votre travail à la date prévue ? Était-ce compliqué d'obtenir les documents nécessaires (visa, contrats, etc) ? L'administration de l'établissement vous a-t-elle aidé⋅e ? Étiez-vous rémunéré⋅e ?",
"avis_prestage": u"Comment avez-vous trouvé où aller pour cette expérience ? À quel moment avez-vous commencé à chercher ? Avez-vous eu des entretiens pour obtenir votre place ? Avez-vous eu d'autres pistes, pourquoi avez-vous choisi cette option ?",
"les_plus": u"Les principaux points positifs de cette expérience", "les_plus": u"Les principaux points positifs de cette expérience",
"les_moins": u"Ce qui aurait pu être mieux", "les_moins": u"Ce qui aurait pu être mieux",
} }

View file

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-10-02 20:43
from __future__ import unicode_literals
from django.db import migrations, models
import tinymce.models
class Migration(migrations.Migration):
dependencies = [
('avisstage', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='avisstage',
name='avis_prestage',
field=tinymce.models.HTMLField(blank=True, default='', verbose_name='Avant le stage'),
),
migrations.AddField(
model_name='stage',
name='len_avis_lieux',
field=models.IntegerField(default=0, verbose_name='Longueur des avis de lieu'),
),
migrations.AddField(
model_name='stage',
name='len_avis_stage',
field=models.IntegerField(default=0, verbose_name='Longueur des avis de stage'),
),
]

View file

@ -10,6 +10,7 @@ from django.template.defaultfilters import slugify
from django.forms.widgets import DateInput from django.forms.widgets import DateInput
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.html import strip_tags
from taggit_autosuggest.managers import TaggableManager from taggit_autosuggest.managers import TaggableManager
from tinymce.models import HTMLField as RichTextField from tinymce.models import HTMLField as RichTextField
@ -162,6 +163,8 @@ class Stage(models.Model):
public = models.BooleanField(u"Visible publiquement", default=False) public = models.BooleanField(u"Visible publiquement", default=False)
date_creation = models.DateTimeField(u"Créé le", default=timezone.now) date_creation = models.DateTimeField(u"Créé le", default=timezone.now)
date_maj = models.DateTimeField(u"Mis à jour le", default=timezone.now) date_maj = models.DateTimeField(u"Mis à jour le", default=timezone.now)
len_avis_stage = models.IntegerField(u"Longueur des avis de stage", default=0)
len_avis_lieux = models.IntegerField(u"Longueur des avis de lieu", default=0)
# Caractéristiques du stage # Caractéristiques du stage
sujet = models.CharField(u"Sujet", max_length=500) sujet = models.CharField(u"Sujet", max_length=500)
@ -221,6 +224,26 @@ class Stage(models.Model):
def __unicode__(self): def __unicode__(self):
return u"%s (par %s)" % (self.sujet, self.auteur.user.username) return u"%s (par %s)" % (self.sujet, self.auteur.user.username)
def update_stats(self, save=True):
def get_len(obj):
length = 0
avis = obj.avis_all
for k, av in avis:
length += len(av.split())
length += len(obj.chapo.split())
length += len(obj.les_plus.split())
length += len(obj.les_moins.split())
return length
if self.avis_stage:
self.len_avis_stage = get_len(self.avis_stage)
self.len_avis_lieux = 0
for avis in self.avislieu_set.all():
self.len_avis_lieux += get_len(avis)
if save:
self.save()
class Meta: class Meta:
verbose_name = "Stage" verbose_name = "Stage"
@ -235,6 +258,7 @@ class AvisStage(models.Model):
avis_ambiance = RichTextField(u"L'ambiance de travail", blank=True) avis_ambiance = RichTextField(u"L'ambiance de travail", blank=True)
avis_sujet = RichTextField(u"La mission", blank=True) avis_sujet = RichTextField(u"La mission", blank=True)
avis_admin = RichTextField(u"Formalités et administration", blank=True) avis_admin = RichTextField(u"Formalités et administration", blank=True)
avis_prestage = RichTextField(u"Avant le stage", blank=True, default="")
les_plus = models.TextField(u"Les plus de cette expérience", blank=True) les_plus = models.TextField(u"Les plus de cette expérience", blank=True)
les_moins = models.TextField(u"Les moins de cette expérience", blank=True) les_moins = models.TextField(u"Les moins de cette expérience", blank=True)
@ -245,7 +269,7 @@ class AvisStage(models.Model):
# Liste des champs d'avis, couplés à leur nom (pour l'affichage) # Liste des champs d'avis, couplés à leur nom (pour l'affichage)
@property @property
def avis_all(self): def avis_all(self):
fields = ['avis_sujet', 'avis_ambiance', 'avis_admin'] fields = ['avis_sujet', 'avis_ambiance', 'avis_admin', 'avis_prestage']
return [(AvisStage._meta.get_field(field).verbose_name, return [(AvisStage._meta.get_field(field).verbose_name,
getattr(self, field, '')) for field in fields] getattr(self, field, '')) for field in fields]

View file

@ -158,6 +158,11 @@ article.stage {
} }
} }
} }
.avis-vides {
font-style: italic;
font-size: 0.9em;
text-align: center;
}
} }
// Sommaire sur le côté // Sommaire sur le côté

View file

@ -542,25 +542,31 @@ article.stage section .plusmoins .moins:before {
vertical-align: top; vertical-align: top;
color: #c70660; color: #c70660;
} }
/* line 165, ../../sass/_stage_detail.scss */ /* line 161, ../../sass/_stage_detail.scss */
article.stage section .avis-vides {
font-style: italic;
font-size: 0.9em;
text-align: center;
}
/* line 170, ../../sass/_stage_detail.scss */
article.stage .section-wrapper { article.stage .section-wrapper {
display: table; display: table;
margin-left: -15px; margin-left: -15px;
width: 100%; width: 100%;
} }
/* line 170, ../../sass/_stage_detail.scss */ /* line 175, ../../sass/_stage_detail.scss */
article.stage .section-wrapper .toc-wrapper, article.stage .section-wrapper > section { article.stage .section-wrapper .toc-wrapper, article.stage .section-wrapper > section {
display: table-cell; display: table-cell;
vertical-align: top; vertical-align: top;
} }
/* line 174, ../../sass/_stage_detail.scss */ /* line 179, ../../sass/_stage_detail.scss */
article.stage .section-wrapper .toc-wrapper { article.stage .section-wrapper .toc-wrapper {
max-width: 230px; max-width: 230px;
width: 25%; width: 25%;
padding: 5px; padding: 5px;
padding-right: 25px; padding-right: 25px;
} }
/* line 180, ../../sass/_stage_detail.scss */ /* line 185, ../../sass/_stage_detail.scss */
article.stage .section-wrapper .toc { article.stage .section-wrapper .toc {
font-family: "Dosis", sans-serif; font-family: "Dosis", sans-serif;
position: -webkit-sticky; position: -webkit-sticky;
@ -571,12 +577,12 @@ article.stage .section-wrapper .toc {
padding: 5px; padding: 5px;
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.2); box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.2);
} }
/* line 190, ../../sass/_stage_detail.scss */ /* line 195, ../../sass/_stage_detail.scss */
article.stage .section-wrapper .toc ul { article.stage .section-wrapper .toc ul {
list-style: none; list-style: none;
padding: 0; padding: 0;
} }
/* line 195, ../../sass/_stage_detail.scss */ /* line 200, ../../sass/_stage_detail.scss */
article.stage .section-wrapper .toc a { article.stage .section-wrapper .toc a {
display: block; display: block;
color: inherit; color: inherit;
@ -585,45 +591,45 @@ article.stage .section-wrapper .toc a {
padding: 5px; padding: 5px;
line-height: 1; line-height: 1;
} }
/* line 203, ../../sass/_stage_detail.scss */ /* line 208, ../../sass/_stage_detail.scss */
article.stage .section-wrapper .toc a:hover { article.stage .section-wrapper .toc a:hover {
color: #8fcc33; color: #8fcc33;
} }
/* line 207, ../../sass/_stage_detail.scss */ /* line 212, ../../sass/_stage_detail.scss */
article.stage .section-wrapper .toc .toc-h1 a { article.stage .section-wrapper .toc .toc-h1 a {
font-weight: 900; font-weight: 900;
} }
/* line 210, ../../sass/_stage_detail.scss */ /* line 215, ../../sass/_stage_detail.scss */
article.stage .section-wrapper .toc .toc-h2 { article.stage .section-wrapper .toc .toc-h2 {
margin-top: 10px; margin-top: 10px;
font-weight: 400; font-weight: 400;
} }
/* line 214, ../../sass/_stage_detail.scss */ /* line 219, ../../sass/_stage_detail.scss */
article.stage .section-wrapper .toc .toc-h3 a { article.stage .section-wrapper .toc .toc-h3 a {
font-weight: 300; font-weight: 300;
} }
/* line 217, ../../sass/_stage_detail.scss */ /* line 222, ../../sass/_stage_detail.scss */
article.stage .section-wrapper .toc .toc-active a { article.stage .section-wrapper .toc .toc-active a {
color: #0f4c82; color: #0f4c82;
} }
/* line 224, ../../sass/_stage_detail.scss */ /* line 229, ../../sass/_stage_detail.scss */
.misc-hdr { .misc-hdr {
display: table; display: table;
width: 100%; width: 100%;
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
} }
/* line 229, ../../sass/_stage_detail.scss */ /* line 234, ../../sass/_stage_detail.scss */
.misc-hdr > * { .misc-hdr > * {
display: table-cell; display: table-cell;
vertical-align: bottom; vertical-align: bottom;
} }
/* line 234, ../../sass/_stage_detail.scss */ /* line 239, ../../sass/_stage_detail.scss */
.misc-hdr h1, .misc-hdr h3 { .misc-hdr h1, .misc-hdr h3 {
width: 100%; width: 100%;
padding-right: 5px; padding-right: 5px;
} }
/* line 238, ../../sass/_stage_detail.scss */ /* line 243, ../../sass/_stage_detail.scss */
.misc-hdr .dates { .misc-hdr .dates {
width: 50px; width: 50px;
background-color: #950548; background-color: #950548;
@ -634,28 +640,28 @@ article.stage .section-wrapper .toc .toc-active a {
font-size: 0.8em; font-size: 0.8em;
text-align: center; text-align: center;
} }
/* line 248, ../../sass/_stage_detail.scss */ /* line 253, ../../sass/_stage_detail.scss */
.misc-hdr .dates span { .misc-hdr .dates span {
display: block; display: block;
} }
/* line 251, ../../sass/_stage_detail.scss */ /* line 256, ../../sass/_stage_detail.scss */
.misc-hdr .dates .year { .misc-hdr .dates .year {
font-size: 1.8em; font-size: 1.8em;
} }
/* line 259, ../../sass/_stage_detail.scss */ /* line 264, ../../sass/_stage_detail.scss */
.edit-box { .edit-box {
background: #eee; background: #eee;
margin: 10px; margin: 10px;
padding: 10px 20px; padding: 10px 20px;
text-align: center; text-align: center;
} }
/* line 265, ../../sass/_stage_detail.scss */ /* line 270, ../../sass/_stage_detail.scss */
.edit-box.public { .edit-box.public {
background: #cae3f9; background: #cae3f9;
border: 1px solid #125b9b; border: 1px solid #125b9b;
} }
/* line 270, ../../sass/_stage_detail.scss */ /* line 275, ../../sass/_stage_detail.scss */
.edit-box.prive { .edit-box.prive {
background: #fdcfe4; background: #fdcfe4;
border: 1px solid #ad0654; border: 1px solid #ad0654;

View file

@ -34,4 +34,7 @@
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% if longueur < 15 %}
<p class="avis-vides">Pas grand-chose par ici. Peut-être aurez-vous plus d'informations en <a href="{% url "avisstage:profil" auteur %}">contactant directement l'auteur⋅e</a> ?</p>
{% endif %}
</section> </section>

View file

@ -109,10 +109,10 @@
</div> </div>
<section class="avis"> <section class="avis">
{% include "avisstage/detail/avis.html" with avis=object.avis_stage titre="Avis sur le travail" %} {% include "avisstage/detail/avis.html" with avis=object.avis_stage titre="Avis sur le travail" auteur=object.auteur.user.username longueur=object.len_avis_stage %}
{% for avis in object.avislieu_set.all %} {% for avis in object.avislieu_set.all %}
{% include "avisstage/detail/avis.html" with avis=avis titre=avis.lieu %} {% include "avisstage/detail/avis.html" with avis=avis titre=avis.lieu auteur=object.auteur.user.username longueur=object.len_avis_lieux %}
{% endfor %} {% endfor %}
</section> </section>
</div> </div>

View file

@ -1,89 +0,0 @@
{% extends "avisstage/base.html" %}
{% load staticfiles %}
{% block title %}Chercher un stage - ExperiENS{% endblock %}
{% block extra_head %}
<script type="text/javascript" src="{% static "js/leaflet.js" %}"></script>
<script type="text/javascript" src="{% static "js/leaflet.markercluster.js" %}"></script>
<link rel="stylesheet" type="text/css" href="{% static "css/leaflet.css" %}" />
<link rel="stylesheet" type="text/css" href="{% static "css/MarkerCluster.css" %}" />
<link rel="stylesheet" type="text/css" href="{% static "css/MarkerCluster.Default.css" %}" />
<script type="text/javascript">
$(function () {
var STATIC_ROOT = "{{ STATIC_URL|escapejs }}";
var API_LIEU = "{% url 'avisstage:api_dispatch_list' resource_name="lieu" api_name="v1" %}";
var map = L.map("stages-map").panTo([30, 15]).setZoom(2);
var layer = new L.TileLayer("https://korona.geog.uni-heidelberg.de/tiles/roads/x={x}&y={y}&z={z}", {attribution: 'Map tiles by <a href="http://korona.geog.uni-heidelberg.de/">GIScience Heidelberg</a>'});
map.addLayer(layer);
function makeIcon(couleur){
return L.icon({
iconUrl: STATIC_ROOT + 'images/marker-'+couleur+'.png',
iconSize: [36, 46],
iconAnchor: [18, 45],
popupAnchor: [0, -48]
})
}
var greenIcon = makeIcon('red');
$.getJSON(API_LIEU + "?format=json&has_stage&limit=10000", onLoadLieux);
var marqueurs = L.markerClusterGroup();
var marqueurs_db = {};
function onLoadLieux(data){
console.log(data);
var lieux = data.objects;
$.each(lieux, function(i, item) {
var marqueur = L.marker(item.coord, {icon: greenIcon});
var txt = item.num_stages > 1 ? item.num_stages+" stages ici": "1 stage ici";
txt = "<h3>"+item.nom+"</h3>"+
"<p>"+txt+"</p>";
marqueur.bindPopup(txt + "<p>Chargement...</p>");
marqueurs.addLayer(marqueur);
marqueur.on("popupopen", loadDetailLieu);
marqueur._lieu_data = item;
marqueur._popup_header = txt;
marqueur._lieu_data_loading = false;
marqueurs_db[item.id] = marqueur;
});
map.addLayer(marqueurs);
}
function loadDetailLieu(){
if(this._lieu_data_loading) return;
var id = this._lieu_data.id;
$.getJSON(API_LIEU + id + "/?format=json", detailLieu);
}
function detailLieu(data){
var marqueur = marqueurs_db[data.id];
marqueur._lieu_data = data;
var html = $("<div>").html(marqueur._popup_header);
var stageliste = $("<ul>");
$.each(data.stages, function(i, item) {
var stage = $("<li>")
.append($("<a>", {href: item.url}).text(item.sujet))
.append($("<span>").text(" par "+item.auteur));
stageliste.append(stage);
});
html.append(stageliste);
marqueur.setPopupContent(html[0]);
}
});
</script>
{% endblock %}
{% block content %}
<h1>Recherche</h1>
<div class="entrer">Ici, bientôt, un vrai formulaire de recherche</div>
<p><a href="{% url "avisstage:stage_majs" %}">Dernières mises à jour</a></p>
<article>
<h2>Carte des expériences</h2>
<div id="stages-map"></div>
</article>
{% endblock %}

View file

@ -9,6 +9,6 @@
<h1>Chercher un stage</h1> <h1>Chercher un stage</h1>
{% include "avisstage/recherche/formulaire.html" with form=form %} {% include "avisstage/recherche/formulaire.html" with form=form %}
<h2>Ou alors</h2> <h2>Ou alors</h2>
<p><a href="{% url 'avisstage:recherche_resultats' %}?vue=carte">Afficher la carte de tous les stages</a></p> <p><a href="{% url 'avisstage:recherche_resultats' %}?vue=carte">Afficher la carte de tous les stages, triés par dernière mise à jour</a></p>
<p><a href="{% url 'avisstage:recherche_resultats' %}?vue=liste&tri=-date_maj">Afficher les dernières mises à jour</a></p> {# <p><a href="{% url 'avisstage:recherche_resultats' %}?vue=liste&tri=-date_maj">Afficher les dernières mises à jour</a></p> #}
{% endblock %} {% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "avisstage/base.html" %} {% extends "avisstage/base.html" %}
{% load staticfiles %} {% load staticfiles avisstage_tags %}
{% block title %}Chercher un stage - ExperiENS{% endblock %} {% block title %}Chercher un stage - ExperiENS{% endblock %}
@ -54,6 +54,8 @@
<li class="thematique">{{ thematique.name }}</li> <li class="thematique">{{ thematique.name }}</li>
{% endfor %} {% endfor %}
<li class="year">{{ stage.date_debut|date:"Y" }}</li> <li class="year">{{ stage.date_debut|date:"Y" }}</li>
<li class="avis-len avis-{{ stage.len_avis_stage|avis_len }}">Avis stage {{ stage.len_avis_stage|avis_len }}</li>
<li class="avis-len avis-{{ stage.len_avis_lieux|avis_len }}">Avis lieux {{ stage.len_avis_lieux|avis_len }}</li>
</ul> </ul>
</div> </div>
<a href="{% url "avisstage:stage" stage.id %}" class="hoverlink">&nbsp;</a> <a href="{% url "avisstage:stage" stage.id %}" class="hoverlink">&nbsp;</a>

View file

@ -24,3 +24,12 @@ def typonazisme(value):
value = re.sub(r'(\w)\s*([,.])', u'\\1\\2', value) value = re.sub(r'(\w)\s*([,.])', u'\\1\\2', value)
value = re.sub(r'([?!:,.])(\w)', u'\\1 \\2', value) value = re.sub(r'([?!:,.])(\w)', u'\\1 \\2', value)
return value return value
@register.filter
def avis_len(value):
if value < 5:
return "vide"
elif value < 50:
return "court"
else:
return "fourni"