experiENS/avisstage/models.py
2019-08-07 18:14:33 +02:00

311 lines
11 KiB
Python

# coding: utf-8
from __future__ import unicode_literals
from allauth.account.models import EmailAddress
from allauth.socialaccount.models import SocialAccount
from django.db import models
from django.db.models.signals import post_save
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 django.utils.functional import cached_property
from django.utils.html import strip_tags
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, TYPE_LIEU_DICT, TYPE_STAGE_DICT, NIVEAU_SCOL_OPTIONS, NIVEAU_SCOL_DICT
import ldap
#
# Profil Normalien (extension du modèle User)
#
class Normalien(models.Model):
user = models.OneToOneField(User, related_name="profil")
# Infos spécifiques
nom = models.CharField(u"Nom complet", max_length=255, blank=True)
promotion = models.CharField(u"Promotion", max_length=40, blank=True)
mail = models.EmailField(u"Adresse e-mail permanente",
max_length=200, blank=True)
contactez_moi = models.BooleanField(u"Inviter les visiteurs à me contacter",
default=True)
bio = models.TextField(u"À propos de moi", blank=True, default="");
class Meta:
verbose_name = u"Profil élève"
verbose_name_plural = u"Profils élèves"
def __str__(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).order_by('-date_debut')
@cached_property
def en_scolarite(self):
return SocialAccount.objects.filter(user_id=self.user_id,
provider="clipper").exists()
def has_nonENS_email(self):
a = EmailAddress.objects.filter(user_id=self.user_id,
verified=True) \
.exclude(email__endswith="ens.fr")
return a.exists()
@property
def preferred_email(self):
a = EmailAddress.objects.filter(user_id=self.user_id,
verified=True) \
.exclude(email__endswith="ens.fr")\
.order_by('-primary')
if len(a) == 0:
a = EmailAddress.objects.filter(user_id=self.user_id,
verified=True) \
.order_by('-primary')
if len(a) == 0:
return ""
else:
return a[0].email
# Hook à la création d'un nouvel utilisateur : récupération de ses infos par LDAP
def create_user_profile(sender, instance, created, **kwargs):
if created:
profil, created = Normalien.objects.get_or_create(user=instance)
try:
saccount = SocialAccount.objects.get(user=instance,
provider="clipper")
except SocialAccount.DoesNotExist:
profil.save()
return
edata = saccount.extra_data.get("ldap", {})
dep = ""
if "department_code" in edata:
dep = dict(DEPARTEMENTS_DEFAUT).get(
edata["department_code"].lower(), '')
profil.promotion = "%s %s" % (dep, edata["entrance_year"])
profil.nom = edata.get("name", "")
profil.save()
post_save.connect(create_user_profile, sender=User)
#
# Lieu de stage
#
class Lieu(models.Model):
# Général
nom = models.CharField(u"Nom de l'institution d'accueil",
max_length=250)
type_lieu = models.CharField(u"Type de structure d'accueil",
default="universite",
choices=TYPE_LIEU_OPTIONS,
max_length=choices_length(TYPE_LIEU_OPTIONS))
# Infos géographiques
ville = models.CharField(u"Ville",
max_length=200)
pays = models.CharField(u"Pays",
choices=PAYS_OPTIONS,
max_length=choices_length(PAYS_OPTIONS))
# Coordonnées
objects = geomodels.GeoManager() # Requis par GeoDjango
coord = geomodels.PointField(u"Coordonnées",
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 __str__(self):
return u"%s (%s)" % (self.nom, self.ville)
class Meta:
verbose_name = "Lieu"
verbose_name_plural = "Lieux"
#
# Matières des stages
#
class StageMatiere(models.Model):
nom = models.CharField(u"Nom", max_length=30)
slug = models.SlugField()
class Meta:
verbose_name = "Matière des stages"
verbose_name_plural = "Matières des stages"
def __str__(self):
return self.nom
#
# Un stage
#
class Stage(models.Model):
# Misc
auteur = models.ForeignKey(Normalien, related_name="stages")
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)
date_debut = models.DateField(u"Date de début", null=True)
date_fin = models.DateField(u"Date de fin", null=True)
type_stage = models.CharField(u"Type",
default="stage",
choices=TYPE_STAGE_OPTIONS,
max_length=choices_length(TYPE_STAGE_OPTIONS))
niveau_scol = models.CharField(u"Année de scolarité",
default="",
choices=NIVEAU_SCOL_OPTIONS,
max_length=choices_length(NIVEAU_SCOL_OPTIONS),
blank=True)
thematiques = TaggableManager(u"Thématiques", blank=True)
matieres = models.ManyToManyField(StageMatiere, verbose_name=u"Matière(s)", related_name="stages")
encadrants = models.CharField(u"Encadrant⋅e⋅s", max_length=500, blank=True)
structure = models.CharField(u"Structure d'accueil", max_length=500, blank=True)
# Avis
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]
# Niveau scolaire en plus joli
@property
def niveau_scol_fancy(self):
return NIVEAU_SCOL_DICT.get(self.niveau_scol, "")
# Optimisation de requêtes
@cached_property
def all_lieux(self):
return self.lieux.all()
def get_absolute_url(self):
return reverse('avisstage:stage', self)
def __str__(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"
#
# Les avis
#
class AvisStage(models.Model):
stage = models.OneToOneField(Stage, related_name="avis_stage")
chapo = models.TextField(u"En quelques mots", blank=True)
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)
def __str__(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_sujet', 'avis_ambiance', 'avis_admin', 'avis_prestage']
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"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)
les_plus = models.TextField(u"Les plus du lieu", blank=True)
les_moins = models.TextField(u"Les moins du lieu", blank=True)
class Meta:
verbose_name = "Avis sur un lieu de stage"
verbose_name_plural = "Avis sur un lieu de stage"
def __str__(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']
return [(AvisLieu._meta.get_field(field).verbose_name,
getattr(self, field, '')) for field in fields]