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

@ -56,6 +56,8 @@ class StageForm(forms.ModelForm):
# Date de modification
self.instance.date_maj = timezone.now()
self.instance.update_stats(False)
stage = super(StageForm, self).save(commit=commit)
return stage
@ -64,12 +66,13 @@ class StageForm(forms.ModelForm):
class AvisStageForm(HTMLTrimmerForm):
class Meta:
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 = {
"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_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_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_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.urls import reverse
from django.utils import timezone
from django.utils.html import strip_tags
from taggit_autosuggest.managers import TaggableManager
from tinymce.models import HTMLField as RichTextField
@ -162,6 +163,8 @@ class Stage(models.Model):
public = models.BooleanField(u"Visible publiquement", default=False)
date_creation = models.DateTimeField(u"Créé 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
sujet = models.CharField(u"Sujet", max_length=500)
@ -221,6 +224,26 @@ class Stage(models.Model):
def __unicode__(self):
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:
verbose_name = "Stage"
@ -235,6 +258,7 @@ class AvisStage(models.Model):
avis_ambiance = RichTextField(u"L'ambiance de travail", blank=True)
avis_sujet = RichTextField(u"La mission", 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_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)
@property
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,
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é

View file

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

View file

@ -34,4 +34,7 @@
</div>
{% endif %}
</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>

View file

@ -109,10 +109,10 @@
</div>
<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 %}
{% 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 %}
</section>
</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>
{% include "avisstage/recherche/formulaire.html" with form=form %}
<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=liste&tri=-date_maj">Afficher les dernières mises à jour</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> #}
{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "avisstage/base.html" %}
{% load staticfiles %}
{% load staticfiles avisstage_tags %}
{% block title %}Chercher un stage - ExperiENS{% endblock %}
@ -54,6 +54,8 @@
<li class="thematique">{{ thematique.name }}</li>
{% endfor %}
<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>
</div>
<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)', u'\\1 \\2', value)
return value
@register.filter
def avis_len(value):
if value < 5:
return "vide"
elif value < 50:
return "court"
else:
return "fourni"