Cleaner code and better pages

This commit is contained in:
Evarin 2017-05-02 23:23:26 +02:00
parent 5e58c7b59d
commit cc47be491c
11 changed files with 657 additions and 458 deletions

View file

@ -1,30 +1,34 @@
# coding: utf-8
from tastypie.resources import ModelResource
from avisstage.models import Lieu, Stage, Normalien, StageMatiere
from tastypie.authentication import SessionAuthentication
from django.contrib.gis import geos
from tastypie import fields, utils
from django.contrib.gis import geos
from django.urls import reverse
from .models import Lieu, Stage, Normalien, StageMatiere
# API principale pour les lieux
class LieuResource(ModelResource):
stages = fields.ToManyField("avisstage.api.StageResource", "stages", use_in="detail", full=True)
stages = fields.ToManyField("avisstage.api.StageResource",
"stages", use_in="detail", full=True)
class Meta:
queryset = Lieu.objects.all()
resource_name = "lieu"
fields = ["nom", "ville", "pays", "coord", "type_lieu", "id"]
authentication = SessionAuthentication()
#login_required
authentication = SessionAuthentication()
# Filtres personnalisés
def build_filters(self, filters=None, **kwargs):
if filters is None:
filters = {}
orm_filters = super(LieuResource, self).build_filters(filters, **kwargs)
# Trouver les lieux à proximités d'un point donné
if "lng" in filters and "lat" in filters:
lat = float(filters['lat'])
lng = float(filters['lng'])
@ -32,60 +36,78 @@ class LieuResource(ModelResource):
self.reference_point = pt
orm_filters['coord__distance_lte'] = (pt, 50)
# Filtrer les lieux qui ont déjà des stages
if "has_stage" in filters:
orm_filters['stages__public'] = True
return orm_filters
# Ajout d'informations
def dehydrate(self, bundle):
bundle = super(LieuResource, self).dehydrate(bundle)
obj = bundle.obj
bundle.data['coord'] = {'lat': float(obj.coord.y),
'lng': float(obj.coord.x)}
# Distance au point recherché (inutile en fait)
#if "lat" in bundle.request.GET and "lng" in bundle.request.GET:
# bundle.data['distance'] = self.reference_point.distance(bundle.obj.coord)
if "lat" in bundle.request.GET and "lng" in bundle.request.GET:
bundle.data['distance'] = self.reference_point.distance(bundle.obj.coord)
# Autres infos utiles
bundle.data["pays_nom"] = obj.get_pays_display()
bundle.data["type_lieu_nom"] = obj.get_type_lieu_display()
bundle.data["type_lieu_nom"] = obj.type_lieu_fancy
# TODO use annotate?
bundle.data["num_stages"] = obj.stages.filter(public=True).count()
return bundle
# API sur un stage
class StageResource(ModelResource):
class Meta:
queryset = Stage.objects.filter(public=True)
resource_name = "stage"
fields = ["sujet", "date_debut", "date_fin", "matieres", "id"]
#login_required
authentication = SessionAuthentication()
# Filtres personnalisés
def build_filters(self, filters=None, **kwargs):
if filters is None:
filters = {}
orm_filters = super(StageResource, self).build_filters(filters, **kwargs)
# Récupération des stages à un lieu donné
if "lieux" in filters:
flieux = map(int, filters['lieux'].split(','))
orm_filters['lieux__id__in'] = flieux
return orm_filters
# Informations à ajouter
def dehydrate(self, bundle):
bundle = super(StageResource, self).dehydrate(bundle)
obj = bundle.obj
# Affichage des manytomany en condensé
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))
# Adresse de la fiche de stage
bundle.data['url'] = reverse("avisstage:stage", kwargs={"pk": obj.id});
return bundle
# Auteurs des fiches (TODO supprimer ?)
class AuteurResource(ModelResource):
stages = fields.ToManyField("avisstage.api.StageResource", "stages", use_in="detail")
stages = fields.ToManyField("avisstage.api.StageResource",
"stages", use_in="detail")
class Meta:
queryset = Normalien.objects.all()
resource_name = "profil"
fields = ["id", "nom", "stages"]
#login_required
authentication = SessionAuthentication()

View file

@ -1,23 +1,66 @@
# coding: utf-8
from django import forms
from avisstage.models import Normalien, Stage, Lieu, AvisLieu, AvisStage
from widgets import LatLonField
from django.utils import timezone
import re
from .models import Normalien, Stage, Lieu, AvisLieu, AvisStage
from .widgets import LatLonField
# Sur-classe utile
class HTMLTrimmerForm(forms.ModelForm):
def clean(self):
# Suppression des espaces blanc avant et après le texte pour les champs html
leading_white = re.compile(r"^( \t\n)*(<p>(&nbsp;|[ \n\t]|<br[ /]*>)*</p>( \t\n)*)+?", re.IGNORECASE)
trailing_white = re.compile(r"(( \t\n)*<p>(&nbsp;|[ \n\t]|<br[ /]*>)*</p>)+?( \t\n)*$", re.IGNORECASE)
cleaned_data = super(HTMLTrimmerForm, self).clean()
for (fname, fval) in cleaned_data.items():
# Heuristique : les champs commençant par "avis_" sont des champs html
if fname[:5] == "avis_":
cleaned_data[fname] = leading_white.sub("", trailing_white.sub("", fval))
return cleaned_data
# Infos sur un stage
class StageForm(forms.ModelForm):
date_widget = forms.DateInput(attrs={"class":"datepicker",
"placeholder":"JJ/MM/AAAA"})
date_debut = forms.DateField(label=u"Date de début",
input_formats=["%d/%m/%Y"], widget=date_widget)
date_fin = forms.DateField(label=u"Date de fin",
input_formats=["%d/%m/%Y"], widget=date_widget)
class Meta:
model = Stage
fields = ['sujet', 'date_debut', 'date_fin', 'type_stage', 'thematiques', 'matieres', 'structure', 'encadrants']
help_texts = {
"thematiques": u"Mettez une virgule pour valider votre thématique si la suggestion ne correspond pas ou si elle n'existe pas encore",
"structure": u"Nom de l'équipe, du laboratoire, de la startup... (si le lieu ne suffit pas)"
}
labels = {
"date_debut": u"Date de début",
}
def __init__(self, *args, **kwargs):
# Sauvegarde de la request pour avoir l'user
if "request" in kwargs:
self.request = kwargs.pop("request")
super(StageForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
# Lors de la création : attribution à l'utilisateur connecté
if self.instance.id is None and hasattr(self, 'request'):
self.instance.auteur = self.request.user.profil
# Date de modification
self.instance.date_maj = timezone.now()
stage = super(StageForm, self).save(commit=commit)
return stage
# Sous-formulaire des avis sur le stage
class AvisStageForm(HTMLTrimmerForm):
class Meta:
model = AvisStage
@ -47,35 +90,7 @@ class AvisLieuForm(HTMLTrimmerForm):
"lieu": forms.HiddenInput(attrs={"class":"lieu-hidden"})
}
class StageForm(forms.ModelForm):
date_widget = forms.DateInput(attrs={"class":"datepicker", "placeholder":"JJ/MM/AAAA"})
date_debut = forms.DateField(label=u"Date de début", input_formats=["%d/%m/%Y"], widget=date_widget)
date_fin = forms.DateField(label=u"Date de fin", input_formats=["%d/%m/%Y"], widget=date_widget)
class Meta:
model = Stage
fields = ['sujet', 'date_debut', 'date_fin', 'type_stage', 'thematiques', 'matieres', 'structure', 'encadrants']
help_texts = {
"thematiques": u"Mettez une virgule pour valider votre thématique si la suggestion ne correspond pas ou si elle n'existe pas encore",
"structure": u"Nom de l'équipe, de la startup..."
}
labels = {
"date_debut": u"Date de début",
}
def __init__(self, *args, **kwargs):
if "request" in kwargs:
self.request = kwargs.pop("request")
super(StageForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
if self.instance.id is None and hasattr(self, 'request'):
self.instance.auteur = self.request.user.profil
self.instance.date_maj = timezone.now()
stage = super(StageForm, self).save(commit=commit)
return stage
# Création d'un nouveau lieu
class LieuForm(forms.ModelForm):
coord = LatLonField()
@ -83,6 +98,7 @@ class LieuForm(forms.ModelForm):
model = Lieu
fields = ['nom', 'type_lieu', 'ville', 'pays', 'coord']
# Widget de feedback
class FeedbackForm(forms.Form):
objet = forms.CharField(label="Objet", required=True)
message = forms.CharField(label="Message", required=True, widget=forms.widgets.Textarea())

View file

@ -3,18 +3,19 @@
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.template.defaultfilters import slugify
from django.contrib.auth.models import User
from django.contrib.gis.db import models as geomodels
from django.template.defaultfilters import slugify
from django.forms.widgets import DateInput
from django.urls import reverse
from django.utils import timezone
from taggit_autosuggest.managers import TaggableManager
from taggit_autosuggest.managers import TaggableManager
from tinymce.models import HTMLField as RichTextField
from .utils import choices_length
from .statics import DEPARTEMENTS_DEFAUT, PAYS_OPTIONS, TYPE_LIEU_OPTIONS, TYPE_STAGE_OPTIONS
from .statics import DEPARTEMENTS_DEFAUT, PAYS_OPTIONS, TYPE_LIEU_OPTIONS, TYPE_STAGE_OPTIONS, TYPE_LIEU_DICT, TYPE_STAGE_DICT
import ldap
@ -38,6 +39,7 @@ class Normalien(models.Model):
def __unicode__(self):
return u"%s (%s)" % (self.nom, self.user.username)
# Liste des stages publiés
def stages_publics(self):
return self.stages.filter(public=True)
@ -51,17 +53,26 @@ def create_user_profile(sender, instance, created, **kwargs):
l.set_option(ldap.OPT_REFERRALS, 0)
l.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
l.set_option(ldap.OPT_X_TLS,ldap.OPT_X_TLS_DEMAND)
l.set_option( ldap.OPT_X_TLS_DEMAND, True )
l.set_option( ldap.OPT_DEBUG_LEVEL, 255 )
l.set_option(ldap.OPT_X_TLS_DEMAND, True)
l.set_option(ldap.OPT_DEBUG_LEVEL, 255)
l.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
l.set_option(ldap.OPT_TIMEOUT, 10)
info = l.search_s('dc=spi,dc=ens,dc=fr',
ldap.SCOPE_SUBTREE,
('(uid=%s)' % (instance.username,)),
[str("cn"),
str("mailRoutingAddress"),
str("homeDirectory")])
# Si des informations sont disponibles
if len(info) > 0:
infos = info[0][1]
# Nom
profil.nom = infos.get('cn', [''])[0]
# Parsing du homeDirectory pour la promotion
if 'homeDirectory' in infos:
dirs = infos['homeDirectory'][0].split('/')
if dirs[1] == 'users':
@ -69,10 +80,13 @@ def create_user_profile(sender, instance, created, **kwargs):
dep = dirs[3]
dep = dict(DEPARTEMENTS_DEFAUT).get(dep.lower(), '')
profil.promotion = u'%s %s' % (dep, annee)
# Mail
pmail = infos.get('mailRoutingAddress',
['%s@clipper.ens.fr'%instance.username])
if len(pmail) > 0:
profil.mail = pmail[0]
profil.save()
except ldap.LDAPError:
pass
@ -104,6 +118,13 @@ class Lieu(models.Model):
geography=True,
srid = 4326)
# Type du lieu en plus joli
@property
def type_lieu_fancy(self):
return TYPE_LIEU_DICT.get(self.type_lieu, ("lieu", False))[0]
@property
def type_lieu_fem(self):
return TYPE_LIEU_DICT.get(self.type_lieu, ("lieu", False))[1]
def __unicode__(self):
return u"%s (%s)" % (self.nom, self.ville)
@ -158,14 +179,27 @@ class Stage(models.Model):
lieux = models.ManyToManyField(Lieu, related_name="stages",
through="AvisLieu", blank=True)
# Affichage des avis ordonnés
@property
def avis_lieux(self):
return self.avislieu_set.order_by('order')
# Shortcut pour affichage rapide
@property
def lieu_principal(self):
avis_lieux = self.avis_lieux
if len(avis_lieux) == 0:
return None
return self.avis_lieux[0].lieu
# Type du stage en plus joli
@property
def type_stage_fancy(self):
return TYPE_STAGE_DICT.get(self.type_stage, ("stage", False))[0]
@property
def type_stage_fem(self):
return TYPE_STAGE_DICT.get(self.type_stage, ("stage", False))[1]
def get_absolute_url(self):
return reverse('avisstage:stage', self)
@ -193,19 +227,21 @@ class AvisStage(models.Model):
def __unicode__(self):
return u"Avis sur {%s} par %s" % (self.stage.sujet, self.stage.auteur.user.username)
# Liste des champs d'avis, couplés à leur nom (pour l'affichage)
@property
def avis_all(self):
fields = ['avis_ambiance', 'avis_sujet', 'avis_admin']
return [(AvisStage._meta.get_field(field).verbose_name,
getattr(self, field, '')) for field in fields]
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", blank=True)
avis_lieustage = RichTextField(u"Le lieu du stage", blank=True)
avis_lieustage = RichTextField(u"Les lieux de travail", blank=True)
avis_pratique = RichTextField(u"S'installer - conseils pratiques",
blank=True)
avis_tourisme = RichTextField(u"Dans les parages", blank=True)
@ -219,7 +255,8 @@ class AvisLieu(models.Model):
def __unicode__(self):
return u"Avis sur {%s} par %s" % (self.lieu.nom, self.stage.auteur.user_id)
# Liste des champs d'avis, couplés à leur nom (pour l'affichage)
@property
def avis_all(self):
fields = ['avis_lieustage', 'avis_pratique', 'avis_tourisme']

View file

@ -2,6 +2,8 @@
@import "_definitions.scss";
@import url('https://fonts.googleapis.com/css?family=Dosis:300,500,700|Podkova:700');
// Général
* {
box-sizing: border-box;
}
@ -40,6 +42,13 @@ em, i {
font-style: italic;
}
a {
font-weight: bold;
color: $compl * 0.9;
text-decoration: none;
}
// experiens-BETA
.beta {
position: absolute;
opacity: 0.5;
@ -48,12 +57,11 @@ em, i {
transform: translate(-1em, 1em) rotate(-15deg);
}
a {
font-weight: bold;
color: $compl * 0.9;
text-decoration: none;
.leaflet-container {
z-index: 1;
}
// Header
header {
background: $barre;
display: flex;
@ -100,9 +108,7 @@ header {
}
}
p {
}
// General content
.content {
background: #efefef;
@ -114,31 +120,15 @@ p {
p {
margin: 0.5em 0;
}
}
// Index
.homeh1 {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1.2em;
padding-bottom: 10px;
border-bottom: 3px solid #000;
margin-bottom: 15px;
& > * {
display: inline-block;
}
p {
text-align: right;
h1 {
margin-bottom: 0.6em;
}
}
// Liste des stages
.stagelist {
// Liste des stages condensée sur le profil
.condensed-stages {
li {
display: table;
width: 100%;
@ -154,7 +144,7 @@ p {
a {
width: auto;
&:hover {
background: darken($compl, 10%);
background: darken($compl, 10%);
color: #fff;
}
}
@ -180,8 +170,34 @@ p {
}
}
// Les stages
.stage-liste {
li {
display: block;
&.date-maj {
font-weight: bold;
font-size: 0.9em;
padding: 3px 0;
}
&.stage {
padding: 10px;
background: #fff;
margin: 10px;
h3 {
font-size: 1.4em;
.auteur {
font-family: $textfont;
font-weight: bold;
font-size: 0.8em;
}
}
ul.infos {
display:inline;
}
}
}
}
ul.infos {
margin: 0 -3px;
@ -209,6 +225,10 @@ ul.infos {
}
}
//
//
// Détail d'un stage
article.stage {
font-weight: normal;
@ -329,6 +349,56 @@ article.stage {
}
}
// Sommaire sur le côté
.article-wrapper {
display: table;
margin-left: -15px;
.toc-wrapper, article {
display: table-cell;
vertical-align: top;
}
.toc-wrapper {
max-width: 250px;
width: 25%;
background: #f6f6f6;
padding: 5px;
}
.toc {
position: -webkit-sticky;
position: sticky;
top: 12px;
a {
display: block;
color: inherit;
font-weight: normal;
border-radius: 7px;
padding: 7px;
line-height: 1;
&:hover {
background-color: $fond;
}
}
.toc-h1 a {
font-weight: 900;
}
.toc-h2 {
margin-top: 15px;
}
.toc-h3 a {
font-weight: 300;
}
.toc-active a {
background-color: lighten($fond, 30%);
}
}
}
// Bandeau pour passer en public ou éditer
.edit-box {
background: #eee;
margin: 10px;
@ -347,39 +417,12 @@ article.stage {
}
.article-wrapper {
display: table;
.toc-wrapper, article {
display: table-cell;
vertical-align: top;
}
.toc-wrapper {
max-width: 250px;
width: 25%;
}
.toc {
position: -webkit-sticky;
position: sticky;
top: 0;
a {
color: inherit;
font-weight: normal;
}
.toc-h1 {
}
.toc-h3 a {
font-weight: 300;
}
.toc-active a {
color: $fond;
}
}
}
//
//
// Formulaires
// Général
input, textarea, select, div.tinymce, option, optgroup:before {
background: #fff;
font-size: 1em;
@ -395,19 +438,6 @@ input, textarea, select, div.tinymce, option, optgroup:before {
}
}
select option {
padding: 3px;
white-space: pre-wrap;
}
select optgroup {
option {
padding-left: 10px;
}
&:before {
font-weight:bold;
}
}
input[type='text'], input[type='password'],
input[type='email'], textarea, select {
@ -418,8 +448,29 @@ input[type='email'], textarea, select {
transition: border 1s ease-out, background 1s ease-out;
}
input[type="submit"],
.btn {
select {
-moz-appearance: none;
appearance: none;
width: auto;
margin-right: 5px;
cursor: pointer;
option {
padding: 3px;
white-space: pre-wrap;
}
optgroup {
option {
padding-left: 10px;
}
&:before {
font-weight:bold;
}
}
}
input[type="submit"], .btn {
font: $textfontsize $textfont;
background-color: $fond;
color: #fff;
@ -446,20 +497,12 @@ h1 .btn {
background-size: contain;
&:after {
content:"";
content: "";
width: 30px;
display: inline-block;
}
}
select {
-moz-appearance: none;
appearance: none;
width: auto;
margin-right: 5px;
cursor: pointer;
}
textarea, div.tinymce {
border:none;
border-left: 1px solid $fond;
@ -473,6 +516,8 @@ textarea {
resize: vertical;
}
// Formulaire et labels
form {
.field {
margin: 5px 0;
@ -505,6 +550,7 @@ form {
}
// taggit autosuggest
ul.as-selections {
display: flex;
flex-wrap: wrap;
@ -570,6 +616,8 @@ div.as-results {
}
//
//
// Fenêtre
.window {
display:none;
@ -628,6 +676,10 @@ div.as-results {
}
}
//
//
// Widget choix et ajout de lieux
#map_addlieu {
height: 500px;
}
@ -686,6 +738,29 @@ a.lieu-change {
}
#id_stage-thematiques {
display: none;
}
// Index
.homeh1 {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1.2em;
padding-bottom: 10px;
border-bottom: 3px solid #000;
margin-bottom: 15px;
& > * {
display: inline-block;
}
p {
text-align: right;
}
}
.betacadre {
background: lighten($rouge, 20%);
padding: 10px;
@ -725,41 +800,4 @@ article.promo {
}
}
#id_stage-thematiques {
display: none;
}
.leaflet-container {
z-index: 1;
}
.stage-liste {
li {
display: block;
&.date-maj {
font-weight: bold;
font-size: 0.9em;
padding: 3px 0;
}
&.stage {
padding: 10px;
background: #fff;
margin: 10px;
h3 {
font-size: 1.4em;
.auteur {
font-family: $textfont;
font-weight: bold;
font-size: 0.8em;
}
}
ul.infos {
display:inline;
}
}
}
}
@import "_responsive.scss";

View file

@ -65,12 +65,12 @@ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu,
display: block;
}
/* line 5, ../../sass/screen.scss */
/* line 7, ../../sass/screen.scss */
* {
box-sizing: border-box;
}
/* line 9, ../../sass/screen.scss */
/* line 11, ../../sass/screen.scss */
body {
background: #8fcc33;
font-family: "Dosis", sans-serif;
@ -79,39 +79,46 @@ body {
font-weight: 300;
}
/* line 17, ../../sass/screen.scss */
/* line 19, ../../sass/screen.scss */
h1, h2, h3, h4 {
font-family: Podkova, serif;
line-height: 1.2;
}
/* line 22, ../../sass/screen.scss */
/* line 24, ../../sass/screen.scss */
h1 {
font-size: 2.3em;
position: relative;
}
/* line 27, ../../sass/screen.scss */
/* line 29, ../../sass/screen.scss */
h2 {
font-size: 1.8em;
}
/* line 31, ../../sass/screen.scss */
/* line 33, ../../sass/screen.scss */
h3 {
font-size: 1.4em;
}
/* line 35, ../../sass/screen.scss */
/* line 37, ../../sass/screen.scss */
b, strong {
font-weight: bold;
}
/* line 39, ../../sass/screen.scss */
/* line 41, ../../sass/screen.scss */
em, i {
font-style: italic;
}
/* line 43, ../../sass/screen.scss */
/* line 45, ../../sass/screen.scss */
a {
font-weight: bold;
color: #e08c1d;
text-decoration: none;
}
/* line 52, ../../sass/screen.scss */
.beta {
position: absolute;
opacity: 0.5;
@ -120,37 +127,35 @@ em, i {
transform: translate(-1em, 1em) rotate(-15deg);
}
/* line 51, ../../sass/screen.scss */
a {
font-weight: bold;
color: #e08c1d;
text-decoration: none;
/* line 60, ../../sass/screen.scss */
.leaflet-container {
z-index: 1;
}
/* line 57, ../../sass/screen.scss */
/* line 65, ../../sass/screen.scss */
header {
background: #72a329;
display: flex;
justify-content: space-between;
align-items: center;
}
/* line 63, ../../sass/screen.scss */
/* line 71, ../../sass/screen.scss */
header #showmenu {
display: none;
}
/* line 67, ../../sass/screen.scss */
/* line 75, ../../sass/screen.scss */
header nav {
display: inline;
}
/* line 69, ../../sass/screen.scss */
/* line 77, ../../sass/screen.scss */
header nav ul {
display: inline-flex;
}
/* line 72, ../../sass/screen.scss */
/* line 80, ../../sass/screen.scss */
header nav ul li {
display: inline-table;
}
/* line 75, ../../sass/screen.scss */
/* line 83, ../../sass/screen.scss */
header nav ul li a {
text-align: center;
display: table-cell;
@ -159,23 +164,23 @@ header nav ul li a {
vertical-align: middle;
color: #e9f5d6;
}
/* line 83, ../../sass/screen.scss */
/* line 91, ../../sass/screen.scss */
header nav ul li a:hover {
background: #446219;
}
/* line 91, ../../sass/screen.scss */
/* line 99, ../../sass/screen.scss */
header a {
color: #fff;
text-decoration: none;
}
/* line 96, ../../sass/screen.scss */
/* line 104, ../../sass/screen.scss */
header h1 {
padding: 15px;
word-wrap: none;
display: inline-block;
}
/* line 107, ../../sass/screen.scss */
/* line 113, ../../sass/screen.scss */
.content {
background: #efefef;
width: 900px;
@ -183,81 +188,97 @@ header h1 {
padding: 30px;
margin: 15px auto;
}
/* line 114, ../../sass/screen.scss */
/* line 120, ../../sass/screen.scss */
.content p {
margin: 0.5em 0;
}
/* line 121, ../../sass/screen.scss */
.homeh1 {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1.2em;
padding-bottom: 10px;
border-bottom: 3px solid #000;
margin-bottom: 15px;
}
/* line 130, ../../sass/screen.scss */
.homeh1 > * {
display: inline-block;
}
/* line 133, ../../sass/screen.scss */
.homeh1 p {
text-align: right;
/* line 124, ../../sass/screen.scss */
.content h1 {
margin-bottom: 0.6em;
}
/* line 142, ../../sass/screen.scss */
.stagelist li {
/* line 132, ../../sass/screen.scss */
.condensed-stages li {
display: table;
width: 100%;
background: #fff;
margin: 12px;
}
/* line 149, ../../sass/screen.scss */
.stagelist li > *, .stagelist li:before {
/* line 139, ../../sass/screen.scss */
.condensed-stages li > *, .condensed-stages li:before {
display: table-cell;
vertical-align: middle;
padding: 15px;
}
/* line 154, ../../sass/screen.scss */
.stagelist li a {
/* line 144, ../../sass/screen.scss */
.condensed-stages li a {
width: auto;
}
/* line 156, ../../sass/screen.scss */
.stagelist li a:hover {
/* line 146, ../../sass/screen.scss */
.condensed-stages li a:hover {
background: #e08206;
color: #fff;
}
/* line 161, ../../sass/screen.scss */
.stagelist li:before {
/* line 151, ../../sass/screen.scss */
.condensed-stages li:before {
content: "";
text-align: right;
width: 150px;
opacity: 0.8;
color: #000;
}
/* line 168, ../../sass/screen.scss */
.stagelist li.stage-brouillon:before {
/* line 158, ../../sass/screen.scss */
.condensed-stages li.stage-brouillon:before {
content: "Brouillon";
background: #f93a93;
}
/* line 172, ../../sass/screen.scss */
.stagelist li.stage-publie:before {
/* line 162, ../../sass/screen.scss */
.condensed-stages li.stage-publie:before {
content: "Publié";
background: #419be9;
}
/* line 176, ../../sass/screen.scss */
.stagelist li.stage-ajout:before {
/* line 166, ../../sass/screen.scss */
.condensed-stages li.stage-ajout:before {
content: "+";
color: #000;
}
/* line 174, ../../sass/screen.scss */
.stage-liste li {
display: block;
}
/* line 176, ../../sass/screen.scss */
.stage-liste li.date-maj {
font-weight: bold;
font-size: 0.9em;
padding: 3px 0;
}
/* line 181, ../../sass/screen.scss */
.stage-liste li.stage {
padding: 10px;
background: #fff;
margin: 10px;
}
/* line 186, ../../sass/screen.scss */
.stage-liste li.stage h3 {
font-size: 1.4em;
}
/* line 189, ../../sass/screen.scss */
.stage-liste li.stage h3 .auteur {
font-family: "Dosis", sans-serif;
font-weight: bold;
font-size: 0.8em;
}
/* line 195, ../../sass/screen.scss */
.stage-liste li.stage ul.infos {
display: inline;
}
/* line 202, ../../sass/screen.scss */
ul.infos {
margin: 0 -3px;
}
/* line 189, ../../sass/screen.scss */
/* line 205, ../../sass/screen.scss */
ul.infos li {
display: inline-block;
padding: 5px;
@ -266,27 +287,27 @@ ul.infos li {
font-size: 0.9em;
border-radius: 4px;
}
/* line 197, ../../sass/screen.scss */
/* line 213, ../../sass/screen.scss */
ul.infos li.thematique {
background-color: #1a82dd;
color: #fff;
}
/* line 201, ../../sass/screen.scss */
/* line 217, ../../sass/screen.scss */
ul.infos li.matiere {
color: #fff;
background-color: #8fcc33;
}
/* line 205, ../../sass/screen.scss */
/* line 221, ../../sass/screen.scss */
ul.infos li.lieu {
color: #fff;
background-color: #f99b20;
}
/* line 212, ../../sass/screen.scss */
/* line 232, ../../sass/screen.scss */
article.stage {
font-weight: normal;
}
/* line 215, ../../sass/screen.scss */
/* line 235, ../../sass/screen.scss */
article.stage h2 {
background: #ddda78;
color: #000;
@ -295,7 +316,7 @@ article.stage h2 {
margin-bottom: 25px;
text-shadow: -3px 3px 0 rgba(255, 255, 255, 0.3);
}
/* line 223, ../../sass/screen.scss */
/* line 243, ../../sass/screen.scss */
article.stage h3 {
border-bottom: 2px solid #cb954e;
margin-left: -25px;
@ -305,58 +326,58 @@ article.stage h3 {
color: #000;
text-shadow: -3px 3px 0 rgba(0, 0, 0, 0.1);
}
/* line 234, ../../sass/screen.scss */
/* line 254, ../../sass/screen.scss */
article.stage #stage-map {
height: 300px;
width: 100%;
}
/* line 239, ../../sass/screen.scss */
/* line 259, ../../sass/screen.scss */
article.stage section {
background: #eee;
padding: 14px;
max-width: 600px;
margin: 30px auto;
}
/* line 245, ../../sass/screen.scss */
/* line 265, ../../sass/screen.scss */
article.stage section:first-child {
margin-top: 0;
}
/* line 247, ../../sass/screen.scss */
/* line 267, ../../sass/screen.scss */
article.stage section:first-child h3 {
margin-top: 0;
}
/* line 252, ../../sass/screen.scss */
/* line 272, ../../sass/screen.scss */
article.stage section.misc {
padding-top: 0;
}
/* line 256, ../../sass/screen.scss */
/* line 276, ../../sass/screen.scss */
article.stage section .chapo, article.stage section .avis-texte {
margin-bottom: 15px;
background: #fff;
padding: 20px;
}
/* line 261, ../../sass/screen.scss */
/* line 281, ../../sass/screen.scss */
article.stage section .chapo {
font-size: 1.1em;
font-weight: 500;
font-variant: small-caps;
}
/* line 266, ../../sass/screen.scss */
/* line 286, ../../sass/screen.scss */
article.stage section .avis-texte {
border-left: 1px solid #ccc;
padding-left: 15px;
}
/* line 271, ../../sass/screen.scss */
/* line 291, ../../sass/screen.scss */
article.stage section .plusmoins {
max-width: 600px;
margin: 15px auto;
}
/* line 275, ../../sass/screen.scss */
/* line 295, ../../sass/screen.scss */
article.stage section .plusmoins > div {
display: table;
width: 100%;
}
/* line 279, ../../sass/screen.scss */
/* line 299, ../../sass/screen.scss */
article.stage section .plusmoins > div:before {
content: "&nbsp";
width: 100px;
@ -365,101 +386,120 @@ article.stage section .plusmoins > div:before {
text-align: right;
padding-right: 12px;
}
/* line 288, ../../sass/screen.scss */
/* line 308, ../../sass/screen.scss */
article.stage section .plusmoins > div > *, article.stage section .plusmoins > div:before {
display: table-cell;
}
/* line 292, ../../sass/screen.scss */
/* line 312, ../../sass/screen.scss */
article.stage section .plusmoins > div > div {
padding: 15px;
color: #fff;
}
/* line 295, ../../sass/screen.scss */
/* line 315, ../../sass/screen.scss */
article.stage section .plusmoins > div > div h4 {
font-weight: normal;
margin-left: -5px;
font-size: 0.9em;
opacity: 0.9;
}
/* line 301, ../../sass/screen.scss */
/* line 321, ../../sass/screen.scss */
article.stage section .plusmoins > div > div p {
font-weight: bold;
margin: 2px;
}
/* line 309, ../../sass/screen.scss */
/* line 329, ../../sass/screen.scss */
article.stage section .plusmoins .plus > div {
background: #1775c6;
}
/* line 312, ../../sass/screen.scss */
/* line 332, ../../sass/screen.scss */
article.stage section .plusmoins .plus:before {
content: "Les +";
vertical-align: bottom;
color: #1567af;
}
/* line 319, ../../sass/screen.scss */
/* line 339, ../../sass/screen.scss */
article.stage section .plusmoins .moins > div {
background: #df076c;
}
/* line 322, ../../sass/screen.scss */
/* line 342, ../../sass/screen.scss */
article.stage section .plusmoins .moins:before {
content: "Les -";
vertical-align: top;
color: #c70660;
}
/* line 332, ../../sass/screen.scss */
/* line 354, ../../sass/screen.scss */
.article-wrapper {
display: table;
margin-left: -15px;
}
/* line 358, ../../sass/screen.scss */
.article-wrapper .toc-wrapper, .article-wrapper article {
display: table-cell;
vertical-align: top;
}
/* line 362, ../../sass/screen.scss */
.article-wrapper .toc-wrapper {
max-width: 250px;
width: 25%;
background: #f6f6f6;
padding: 5px;
}
/* line 368, ../../sass/screen.scss */
.article-wrapper .toc {
position: -webkit-sticky;
position: sticky;
top: 12px;
}
/* line 373, ../../sass/screen.scss */
.article-wrapper .toc a {
display: block;
color: inherit;
font-weight: normal;
border-radius: 7px;
padding: 7px;
line-height: 1;
}
/* line 381, ../../sass/screen.scss */
.article-wrapper .toc a:hover {
background-color: #8fcc33;
}
/* line 385, ../../sass/screen.scss */
.article-wrapper .toc .toc-h1 a {
font-weight: 900;
}
/* line 388, ../../sass/screen.scss */
.article-wrapper .toc .toc-h2 {
margin-top: 15px;
}
/* line 391, ../../sass/screen.scss */
.article-wrapper .toc .toc-h3 a {
font-weight: 300;
}
/* line 394, ../../sass/screen.scss */
.article-wrapper .toc .toc-active a {
background-color: #d2ebad;
}
/* line 402, ../../sass/screen.scss */
.edit-box {
background: #eee;
margin: 10px;
padding: 10px 20px;
text-align: center;
}
/* line 338, ../../sass/screen.scss */
/* line 408, ../../sass/screen.scss */
.edit-box.public {
background: #cae3f9;
border: 1px solid #125b9b;
}
/* line 343, ../../sass/screen.scss */
/* line 413, ../../sass/screen.scss */
.edit-box.prive {
background: #fdcfe4;
border: 1px solid #ad0654;
}
/* line 350, ../../sass/screen.scss */
.article-wrapper {
display: table;
}
/* line 352, ../../sass/screen.scss */
.article-wrapper .toc-wrapper, .article-wrapper article {
display: table-cell;
vertical-align: top;
}
/* line 356, ../../sass/screen.scss */
.article-wrapper .toc-wrapper {
max-width: 250px;
width: 25%;
}
/* line 360, ../../sass/screen.scss */
.article-wrapper .toc {
position: -webkit-sticky;
position: sticky;
top: 0;
}
/* line 364, ../../sass/screen.scss */
.article-wrapper .toc a {
color: inherit;
font-weight: normal;
}
/* line 371, ../../sass/screen.scss */
.article-wrapper .toc .toc-h3 a {
font-weight: 300;
}
/* line 374, ../../sass/screen.scss */
.article-wrapper .toc .toc-active a {
color: #8fcc33;
}
/* line 383, ../../sass/screen.scss */
/* line 426, ../../sass/screen.scss */
input, textarea, select, div.tinymce, option, optgroup:before {
background: #fff;
font-size: 1em;
@ -469,28 +509,13 @@ input, textarea, select, div.tinymce, option, optgroup:before {
padding: 5px;
text-align: left;
}
/* line 392, ../../sass/screen.scss */
/* line 435, ../../sass/screen.scss */
input:focus, input.mce-edit-focus, textarea:focus, textarea.mce-edit-focus, select:focus, select.mce-edit-focus, div.tinymce:focus, div.tinymce.mce-edit-focus, option:focus, option.mce-edit-focus, optgroup:before:focus, optgroup:before.mce-edit-focus {
background-color: #e9f5d6;
outline: none;
}
/* line 398, ../../sass/screen.scss */
select option {
padding: 3px;
white-space: pre-wrap;
}
/* line 404, ../../sass/screen.scss */
select optgroup option {
padding-left: 10px;
}
/* line 407, ../../sass/screen.scss */
select optgroup:before {
font-weight: bold;
}
/* line 412, ../../sass/screen.scss */
/* line 442, ../../sass/screen.scss */
input[type='text'], input[type='password'],
input[type='email'], textarea, select {
border: none;
@ -500,9 +525,30 @@ input[type='email'], textarea, select {
transition: border 1s ease-out, background 1s ease-out;
}
/* line 421, ../../sass/screen.scss */
input[type="submit"],
.btn {
/* line 451, ../../sass/screen.scss */
select {
-moz-appearance: none;
appearance: none;
width: auto;
margin-right: 5px;
cursor: pointer;
}
/* line 458, ../../sass/screen.scss */
select option {
padding: 3px;
white-space: pre-wrap;
}
/* line 464, ../../sass/screen.scss */
select optgroup option {
padding-left: 10px;
}
/* line 467, ../../sass/screen.scss */
select optgroup:before {
font-weight: bold;
}
/* line 473, ../../sass/screen.scss */
input[type="submit"], .btn {
font: 19px "Dosis", sans-serif;
background-color: #8fcc33;
color: #fff;
@ -514,14 +560,14 @@ input[type="submit"],
margin-right: 0;
}
/* line 434, ../../sass/screen.scss */
/* line 485, ../../sass/screen.scss */
p input[type="submit"],
p .btn,
h1 .btn {
display: inline-block;
}
/* line 440, ../../sass/screen.scss */
/* line 491, ../../sass/screen.scss */
.edit-btn {
border-color: #706c00;
color: #000;
@ -530,23 +576,14 @@ h1 .btn {
background-origin: content-box;
background-size: contain;
}
/* line 448, ../../sass/screen.scss */
/* line 499, ../../sass/screen.scss */
.edit-btn:after {
content: "";
width: 30px;
display: inline-block;
}
/* line 455, ../../sass/screen.scss */
select {
-moz-appearance: none;
appearance: none;
width: auto;
margin-right: 5px;
cursor: pointer;
}
/* line 463, ../../sass/screen.scss */
/* line 506, ../../sass/screen.scss */
textarea, div.tinymce {
border: none;
border-left: 1px solid #8fcc33;
@ -555,20 +592,20 @@ textarea, div.tinymce {
transition: border 1s ease-out, background 1s ease-out;
}
/* line 471, ../../sass/screen.scss */
/* line 514, ../../sass/screen.scss */
textarea {
height: 200px;
resize: vertical;
}
/* line 477, ../../sass/screen.scss */
/* line 522, ../../sass/screen.scss */
form .field {
margin: 5px 0;
display: flex;
background: #fff;
padding: 10px;
}
/* line 483, ../../sass/screen.scss */
/* line 528, ../../sass/screen.scss */
form .field label, form .field .label {
display: inline-block;
width: 250px;
@ -577,33 +614,33 @@ form .field label, form .field .label {
padding-top: 5px;
flex-shrink: 0;
}
/* line 491, ../../sass/screen.scss */
/* line 536, ../../sass/screen.scss */
form .field label {
font-family: Podkova, serif;
font-weight: bold;
}
/* line 495, ../../sass/screen.scss */
/* line 540, ../../sass/screen.scss */
form .field .help_text {
font-style: italic;
font-size: 0.9em;
}
/* line 499, ../../sass/screen.scss */
/* line 544, ../../sass/screen.scss */
form .field .input {
display: inline-block;
flex-grow: 1;
margin-right: 10px;
}
/* line 508, ../../sass/screen.scss */
/* line 554, ../../sass/screen.scss */
ul.as-selections {
display: flex;
flex-wrap: wrap;
}
/* line 512, ../../sass/screen.scss */
/* line 558, ../../sass/screen.scss */
ul.as-selections li {
display: inline-block;
}
/* line 516, ../../sass/screen.scss */
/* line 562, ../../sass/screen.scss */
ul.as-selections .as-selection-item {
padding: 0 5px;
background: #f99b20;
@ -612,52 +649,52 @@ ul.as-selections .as-selection-item {
border-radius: 2px;
font-weight: 500;
}
/* line 524, ../../sass/screen.scss */
/* line 570, ../../sass/screen.scss */
ul.as-selections .as-selection-item a.as-close {
color: #fff;
-webkit-cursor: pointer;
cursor: pointer;
margin-right: 5px;
}
/* line 531, ../../sass/screen.scss */
/* line 577, ../../sass/screen.scss */
ul.as-selections .as-selection-item.selected {
background: #8fcc33;
}
/* line 536, ../../sass/screen.scss */
/* line 582, ../../sass/screen.scss */
ul.as-selections .as-original {
flex-grow: 1;
min-width: 200px;
}
/* line 540, ../../sass/screen.scss */
/* line 586, ../../sass/screen.scss */
ul.as-selections .as-original input {
width: 100%;
}
/* line 546, ../../sass/screen.scss */
/* line 592, ../../sass/screen.scss */
div.as-results {
position: relative;
}
/* line 548, ../../sass/screen.scss */
/* line 594, ../../sass/screen.scss */
div.as-results ul {
position: absolute;
width: 100%;
background: #fff;
border: 1px solid #d2ebad;
}
/* line 555, ../../sass/screen.scss */
/* line 601, ../../sass/screen.scss */
div.as-results ul li {
padding: 3px 5px;
}
/* line 561, ../../sass/screen.scss */
/* line 607, ../../sass/screen.scss */
div.as-results ul li.as-result-item.active {
background: #fddeb5;
}
/* line 566, ../../sass/screen.scss */
/* line 612, ../../sass/screen.scss */
div.as-results ul li.as-message {
font-style: italic;
}
/* line 574, ../../sass/screen.scss */
/* line 622, ../../sass/screen.scss */
.window {
display: none;
position: fixed;
@ -668,11 +705,11 @@ div.as-results ul li.as-message {
left: 0;
z-index: 50;
}
/* line 584, ../../sass/screen.scss */
/* line 632, ../../sass/screen.scss */
.window.visible {
display: block;
}
/* line 588, ../../sass/screen.scss */
/* line 636, ../../sass/screen.scss */
.window .window-bg {
background: #000;
opacity: 0.7;
@ -683,7 +720,7 @@ div.as-results ul li.as-message {
top: 0;
z-index: -1;
}
/* line 599, ../../sass/screen.scss */
/* line 647, ../../sass/screen.scss */
.window .window-content {
position: relative;
margin: 0 auto;
@ -697,11 +734,11 @@ div.as-results ul li.as-message {
max-height: 100%;
overflow: auto;
}
/* line 613, ../../sass/screen.scss */
/* line 661, ../../sass/screen.scss */
.window .window-content form label, .window .window-content form .label {
width: 150px;
}
/* line 619, ../../sass/screen.scss */
/* line 667, ../../sass/screen.scss */
.window .window-closer {
position: absolute;
top: 0;
@ -709,36 +746,36 @@ div.as-results ul li.as-message {
padding: 12px;
z-index: 3;
}
/* line 625, ../../sass/screen.scss */
/* line 673, ../../sass/screen.scss */
.window .window-closer:after {
content: "×";
}
/* line 631, ../../sass/screen.scss */
/* line 683, ../../sass/screen.scss */
#map_addlieu {
height: 500px;
}
/* line 636, ../../sass/screen.scss */
/* line 688, ../../sass/screen.scss */
.lieu-ui .map {
height: 400px;
width: 100%;
}
/* line 640, ../../sass/screen.scss */
/* line 692, ../../sass/screen.scss */
.lieu-ui .hidden {
display: none;
}
/* line 643, ../../sass/screen.scss */
/* line 695, ../../sass/screen.scss */
.lieu-ui .masked {
visibility: hidden;
}
/* line 648, ../../sass/screen.scss */
/* line 700, ../../sass/screen.scss */
#avis_lieu_vide {
display: none;
}
/* line 652, ../../sass/screen.scss */
/* line 704, ../../sass/screen.scss */
a.lieu-change {
color: #fff;
background: #f99b20;
@ -751,20 +788,20 @@ a.lieu-change {
border-radius: 5px;
margin-right: 7px;
}
/* line 664, ../../sass/screen.scss */
/* line 716, ../../sass/screen.scss */
a.lieu-change.ajout:before {
content: "+";
margin-right: 5px;
}
/* line 670, ../../sass/screen.scss */
/* line 722, ../../sass/screen.scss */
#stages-map {
width: 100%;
height: 600px;
max-height: 90vh;
}
/* line 676, ../../sass/screen.scss */
/* line 728, ../../sass/screen.scss */
#feedback-button {
position: fixed;
left: 0;
@ -777,13 +814,37 @@ a.lieu-change.ajout:before {
transform-origin: bottom left;
}
/* line 689, ../../sass/screen.scss */
/* line 741, ../../sass/screen.scss */
#id_stage-thematiques {
display: none;
}
/* line 747, ../../sass/screen.scss */
.homeh1 {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1.2em;
padding-bottom: 10px;
border-bottom: 3px solid #000;
margin-bottom: 15px;
}
/* line 756, ../../sass/screen.scss */
.homeh1 > * {
display: inline-block;
}
/* line 759, ../../sass/screen.scss */
.homeh1 p {
text-align: right;
}
/* line 764, ../../sass/screen.scss */
.betacadre {
background: #fa6cae;
padding: 10px;
}
/* line 694, ../../sass/screen.scss */
/* line 769, ../../sass/screen.scss */
.entrer {
background: #fff;
max-width: 500px;
@ -792,75 +853,34 @@ a.lieu-change.ajout:before {
margin: 15px auto;
}
/* line 702, ../../sass/screen.scss */
/* line 777, ../../sass/screen.scss */
article.promo {
display: block;
font-size: 1.1em;
}
/* line 706, ../../sass/screen.scss */
/* line 781, ../../sass/screen.scss */
article.promo .explications {
display: table;
}
/* line 709, ../../sass/screen.scss */
/* line 784, ../../sass/screen.scss */
article.promo .explications:first-child {
direction: rtl;
}
/* line 711, ../../sass/screen.scss */
/* line 786, ../../sass/screen.scss */
article.promo .explications:first-child > * {
direction: ltr;
}
/* line 716, ../../sass/screen.scss */
/* line 791, ../../sass/screen.scss */
article.promo .explications > div {
display: table-cell;
vertical-align: middle;
text-align: center;
}
/* line 721, ../../sass/screen.scss */
/* line 796, ../../sass/screen.scss */
article.promo .explications > div p {
margin: 15px 15px;
}
/* line 728, ../../sass/screen.scss */
#id_stage-thematiques {
display: none;
}
/* line 732, ../../sass/screen.scss */
.leaflet-container {
z-index: 1;
}
/* line 737, ../../sass/screen.scss */
.stage-liste li {
display: block;
}
/* line 739, ../../sass/screen.scss */
.stage-liste li.date-maj {
font-weight: bold;
font-size: 0.9em;
padding: 3px 0;
}
/* line 744, ../../sass/screen.scss */
.stage-liste li.stage {
padding: 10px;
background: #fff;
margin: 10px;
}
/* line 749, ../../sass/screen.scss */
.stage-liste li.stage h3 {
font-size: 1.4em;
}
/* line 752, ../../sass/screen.scss */
.stage-liste li.stage h3 .auteur {
font-family: "Dosis", sans-serif;
font-weight: bold;
font-size: 0.8em;
}
/* line 758, ../../sass/screen.scss */
.stage-liste li.stage ul.infos {
display: inline;
}
@media screen and (max-width: 850px) {
/* line 2, ../../sass/_responsive.scss */
header {

View file

@ -30,6 +30,18 @@ TYPE_STAGE_OPTIONS = (
('autre', u"Autre"),
)
# Dictionnaire des type de stage (et de leur genre, True=féminin)
TYPE_STAGE_DICT = {
'recherche': (u"stage de recherche académique", False),
'recherche_autre': (u"stage de recherche non-académique", False),
'sejour_dri': (u"séjour de recherche DRI", False),
'pro': (u"stage en entreprise sans visée de recherche", False),
'admin': (u"stage en administration, ONG ou organisation internationale", False),
'lectorat': (u"lectorat DRI", False),
'autre_teach': (u"expérience de recherche", True),
'autre': (u"expérience", True),
}
TYPE_LIEU_OPTIONS = (
('universite', u"Université"),
('entreprise', u"Entreprise"),
@ -38,6 +50,15 @@ TYPE_LIEU_OPTIONS = (
('autre', u"Autre"),
)
# Dictionnaire des noms de lieux (et de leur genre, True=féminin)
TYPE_LIEU_DICT = {
'universite': (u"université", True),
'entreprise': (u"entreprise", True),
'centrerecherche': (u"centre de recherche", False),
'administration': (u"administration", True),
'autre': (u"lieu", False),
}
PAYS_OPTIONS = (
("AF", u"Afghanistan"),
("AL", u"Albanie"),

View file

@ -13,12 +13,25 @@
<p>Adresse de contact : <a href="mailto:{{ object.mail }}">{{ object.mail }}</a></p>
<article>
<h2>Ses stages</h2>
<ul class="stagelist">
<ul class="stage-liste">
{% for stage in object.stages_publics %}
<li>
<a href="{% url "avisstage:stage" stage.id %}">
{{ stage.sujet }}
</a>
{% ifchanged stage.date_maj.date %}<li class="date-maj">Mis à jour le {{ stage.date_maj.date }}</li>{% endifchanged %}
<li class="stage"><h3><a href="{% url "avisstage:stage" stage.id %}">{{ stage.sujet }}</a></h3>
<div>
<ul class="infos">
<li class="dates">Du {{ stage.date_debut }} au {{ stage.date_fin }}</li>
<li class="type">{{ stage.get_type_stage_display }}</li>
<li class="structure">{{ stage.structure }}</li>
{% for lieu in stage.lieux.all %}<li class="lieu">{{ lieu.nom }}</li>{% endfor %}
{% for matiere in stage.matieres.all %}
<li class="matiere">{{ matiere.nom }}</li>
{% endfor %}
{% for thematique in stage.thematiques.all %}
<li class="thematique">{{ thematique.name }}</li>
{% endfor %}
</ul>
</div>
</li>
{% endfor %}
</ul>

View file

@ -33,7 +33,7 @@
marqueur.bindPopup(item.popup);
marqueur.addTo(map);
});
map.fitBounds(bds, {maxZoom: 13});
map.fitBounds(bds, {maxZoom: 11});
}
</script>
{% endblock %}
@ -79,7 +79,7 @@
});
</script>
{% endif %}
<p><a href="{% url "avisstage:profil" object.auteur.user.username %}">{{ object.auteur.nom }}</a> a fait ce {{ object.get_type_stage_display|lower }}
<p><a href="{% url "avisstage:profil" object.auteur.user.username %}">{{ object.auteur.nom }}</a> a fait {{ object.type_stage_fem|yesno:"cette,ce" }} <b>{{ object.type_stage_fancy }}</b>
du {{ object.date_debut }} au {{ object.date_fin }}{% if object.structure %}, au sein de {{ object.structure }}{% endif %}{% if object.encadrants %}, supervisé par {{ object.encadrants }}{% endif %}.</p>
<ul class="infos">
{% for matiere in object.matieres.all %}

View file

@ -9,7 +9,7 @@
<article>
<h2>Mes stages</h2>
<ul class="stagelist">
<ul class="condensed-stages">
{% for stage in user.profil.stages.all %}
<li class="stage-{{ stage.public|yesno:"publie,brouillon" }}">
<a href="{% url "avisstage:stage" stage.id %}">

View file

@ -5,6 +5,7 @@
<div class="window-content">
<a class="window-closer"></a>
<h2>Choisir un lieu</h2>
<p>Restez général dans le lieu : choisissez l'université plutôt que le laboratoire, l'incubateur plutôt que la startup...</p>
<div class="message"></div>
<div class="lieu-ui">
</div>

View file

@ -15,6 +15,10 @@ from django.db.models import Q
from avisstage.models import Normalien, Stage, Lieu, AvisLieu, AvisStage
from avisstage.forms import StageForm, LieuForm, AvisStageForm, AvisLieuForm, FeedbackForm
#
# LECTURE
#
# Page d'accueil
def index(request):
return render(request, 'avisstage/index.html')
@ -25,31 +29,62 @@ def perso(request):
return render(request, 'avisstage/perso.html')
# Profil
#login_required
class ProfilView(DetailView, LoginRequiredMixin):
model = Normalien
template_name = 'avisstage/detail/profil.html'
# Récupération de son propre profil
def get_object(self):
return Normalien.objects.get(user__username=self.kwargs.get('username'))
# Stage
#login_required
class StageView(DetailView, LoginRequiredMixin):
model = Stage
template_name = 'avisstage/detail/stage.html'
# Restriction aux stages publics ou personnels
def get_queryset(self):
filtre = Q(auteur__user_id=self.request.user.id) | Q(public=True)
return Stage.objects.filter(filtre)
# Liste des stages par dernière modification
#login_required
class StageListe(ListView, LoginRequiredMixin):
model = Stage
template_name = 'avisstage/liste/stage.html'
def get_queryset(self):
return Stage.objects.filter(public=True).order_by('-date_maj')
# Recherche
@login_required
def recherche(request):
return render(request, 'avisstage/recherche.html')
#
# EDITION
#
# Profil
#login_required
class ProfilEdit(UpdateView, LoginRequiredMixin):
model = Normalien
fields = ['nom', 'promotion', 'mail']
template_name = 'avisstage/formulaires/profil.html'
# Limitation à son propre profil
def get_object(self):
return self.request.user.profil
def get_context_data(self, **kwargs):
print kwargs
return super(ProfilEdit, self).get_context_data(**kwargs)
def get_success_url(self):
return reverse('avisstage:perso')
class ProfilView(DetailView, LoginRequiredMixin):
model = Normalien
template_name = 'avisstage/detail/profil.html'
def get_object(self):
return Normalien.objects.get(user__username=self.kwargs.get('username'))
# Stages
# Stage
@login_required
def manage_stage(request, pk=None):
# Objet de base
if pk is None:
stage = Stage(auteur=request.user.profil)
avis_stage = AvisStage(stage=stage)
@ -59,15 +94,19 @@ def manage_stage(request, pk=None):
avis_stage, _ = AvisStage.objects.get_or_create(stage=stage)
c_del = True
# Formset pour les avis des lieux
AvisLieuFormSet = forms.inlineformset_factory(
Stage, AvisLieu, form=AvisLieuForm, can_delete=c_del, extra=0)
if request.method == "POST":
# Lecture des données
form = StageForm(request.POST, request=request, instance=stage, prefix="stage")
avis_stage_form = AvisStageForm(request.POST,
instance=avis_stage, prefix="avis")
avis_lieu_formset = AvisLieuFormSet(request.POST, instance=stage,
prefix="lieux")
# Validation et enregistrement
if (form.is_valid() and
avis_stage_form.is_valid() and
avis_lieu_formset.is_valid()):
@ -81,33 +120,20 @@ def manage_stage(request, pk=None):
avis_stage_form = AvisStageForm(instance=avis_stage, prefix="avis")
avis_lieu_formset = AvisLieuFormSet(instance=stage, prefix="lieux")
# Affichage du formulaire
return render(request, "avisstage/formulaires/stage.html",
{'form': form, 'avis_stage_form': avis_stage_form,
'avis_lieu_formset': avis_lieu_formset,
'creation': pk is None})
class StageView(DetailView, LoginRequiredMixin):
model = Stage
template_name = 'avisstage/detail/stage.html'
def get_queryset(self):
filtre = Q(auteur__user_id=self.request.user.id) | Q(public=True)
return Stage.objects.filter(filtre)
class StageListe(ListView, LoginRequiredMixin):
model = Stage
template_name = 'avisstage/liste/stage.html'
def get_queryset(self):
return Stage.objects.filter(public=True).order_by('-date_maj')
# Lieux des stages
# Ajout d'un lieu de stage
#login_required
class LieuAjout(CreateView, LoginRequiredMixin):
model = Lieu
form_class = LieuForm
template_name = 'avisstage/formulaires/lieu.html'
# Retourne d'un JSON si requête AJAX
def form_valid(self, form):
if self.request.GET.get("format", "") == "json":
self.object = form.save()
@ -123,26 +149,31 @@ class LieuAjout(CreateView, LoginRequiredMixin):
else:
super(LieuAjout, self).form_valid(form)
# Passage d'un stage en mode public
@login_required
def publier_stage(request, pk):
if request.method != "POST":
return HttpResponseForbidden()
stage = get_object_or_404(Stage, pk=pk)
# Stage non possédé par l'utilisateur
if stage.auteur != request.user.profil:
return HttpResponseForbidden()
# Mise à jour du statut
if "publier" in request.POST:
stage.public = True
else:
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):
if request.method == "POST":