# 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: 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]