2017-04-04 00:31:50 +02:00
|
|
|
from django.db import models
|
2017-04-04 00:28:25 +02:00
|
|
|
from django.db.models.signals import post_save
|
2017-05-02 23:23:26 +02:00
|
|
|
from django.contrib.auth.models import User
|
2017-04-04 00:28:25 +02:00
|
|
|
from django.contrib.gis.db import models as geomodels
|
2017-05-02 23:23:26 +02:00
|
|
|
from django.template.defaultfilters import slugify
|
2017-04-07 03:01:27 +02:00
|
|
|
from django.forms.widgets import DateInput
|
|
|
|
from django.urls import reverse
|
2018-12-28 00:20:14 +01:00
|
|
|
|
2017-04-20 01:53:29 +02:00
|
|
|
from django.utils import timezone
|
2018-12-29 16:23:57 +01:00
|
|
|
from django.utils.functional import cached_property
|
2017-10-02 23:59:20 +02:00
|
|
|
from django.utils.html import strip_tags
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2017-05-02 23:23:26 +02:00
|
|
|
from taggit_autosuggest.managers import TaggableManager
|
2017-04-13 23:40:07 +02:00
|
|
|
from tinymce.models import HTMLField as RichTextField
|
2017-05-02 23:23:26 +02:00
|
|
|
|
2021-01-17 23:48:40 +01:00
|
|
|
from authens.signals import post_cas_connect
|
|
|
|
from authens.models import CASAccount
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2021-01-31 22:53:59 +01:00
|
|
|
from datetime import timedelta
|
|
|
|
|
2021-01-17 23:48:40 +01:00
|
|
|
from .utils import choices_length, is_email_ens
|
|
|
|
from .statics import (
|
2021-02-07 00:41:22 +01:00
|
|
|
DEPARTEMENTS_DEFAUT,
|
|
|
|
PAYS_OPTIONS,
|
|
|
|
TYPE_LIEU_OPTIONS,
|
|
|
|
TYPE_STAGE_OPTIONS,
|
|
|
|
TYPE_LIEU_DICT,
|
|
|
|
TYPE_STAGE_DICT,
|
|
|
|
NIVEAU_SCOL_OPTIONS,
|
|
|
|
NIVEAU_SCOL_DICT,
|
2021-01-17 23:48:40 +01:00
|
|
|
)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2021-01-31 22:53:59 +01:00
|
|
|
def _default_cas_login():
|
2021-02-07 00:41:22 +01:00
|
|
|
return (timezone.now() - timedelta(days=365)).date()
|
|
|
|
|
2021-01-31 22:53:59 +01:00
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
#
|
|
|
|
# Profil Normalien (extension du modèle User)
|
|
|
|
#
|
|
|
|
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
class Normalien(models.Model):
|
2021-02-07 00:41:22 +01:00
|
|
|
user = models.OneToOneField(
|
|
|
|
User, related_name="profil", on_delete=models.SET_NULL, null=True
|
|
|
|
)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
|
|
|
# Infos spécifiques
|
2021-02-07 00:40:55 +01:00
|
|
|
nom = models.CharField("Nom complet", max_length=255, blank=True)
|
|
|
|
promotion = models.CharField("Promotion", max_length=40, blank=True)
|
2021-01-24 23:11:10 +01:00
|
|
|
contactez_moi = models.BooleanField(
|
2021-02-07 00:40:55 +01:00
|
|
|
"Inviter les visiteurs à me contacter",
|
2021-02-07 00:41:22 +01:00
|
|
|
default=True,
|
|
|
|
help_text="Affiche votre adresse e-mail principale sur votre profil public",
|
|
|
|
)
|
2021-02-07 00:40:55 +01:00
|
|
|
bio = models.TextField("À propos de moi", blank=True, default="")
|
2021-01-31 22:53:59 +01:00
|
|
|
last_cas_login = models.DateField(default=_default_cas_login)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
|
|
|
class Meta:
|
2021-02-07 00:40:55 +01:00
|
|
|
verbose_name = "Profil élève"
|
|
|
|
verbose_name_plural = "Profils élèves"
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2018-12-26 22:00:36 +01:00
|
|
|
def __str__(self):
|
2021-02-07 00:40:55 +01:00
|
|
|
return "%s (%s)" % (self.nom, self.user.username)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2017-05-02 23:23:26 +02:00
|
|
|
# Liste des stages publiés
|
2017-04-20 03:00:19 +02:00
|
|
|
def stages_publics(self):
|
2021-02-07 00:41:22 +01:00
|
|
|
return self.stages.filter(public=True).order_by("-date_debut")
|
2017-04-20 03:00:19 +02:00
|
|
|
|
2018-12-28 00:20:14 +01:00
|
|
|
def has_nonENS_email(self):
|
2021-01-24 23:42:13 +01:00
|
|
|
return (
|
2021-02-07 00:41:22 +01:00
|
|
|
self.user.email_address_set.exclude(confirmed_at__isnull=True)
|
2021-01-24 23:42:13 +01:00
|
|
|
.exclude(email__endswith="ens.fr")
|
|
|
|
.exclude(email__endswith="ens.psl.eu")
|
|
|
|
.exists()
|
2021-01-17 23:48:40 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
def nom_complet(self):
|
|
|
|
if self.nom.strip():
|
|
|
|
return self.nom
|
|
|
|
return self.user.username
|
2018-12-28 00:20:14 +01:00
|
|
|
|
2021-01-31 22:53:59 +01:00
|
|
|
@property
|
|
|
|
def en_scolarite(self):
|
|
|
|
return self.last_cas_login > (timezone.now() - timedelta(days=60)).date()
|
|
|
|
|
2018-12-28 23:46:24 +01:00
|
|
|
@property
|
|
|
|
def preferred_email(self):
|
2021-01-17 23:48:40 +01:00
|
|
|
return self.user.email
|
|
|
|
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2021-01-17 23:48:40 +01:00
|
|
|
# Hook à la création d'un nouvel utilisateur : information de base
|
|
|
|
def create_basic_user_profile(sender, instance, created, **kwargs):
|
2017-04-04 00:28:25 +02:00
|
|
|
if created:
|
2017-04-25 23:18:52 +02:00
|
|
|
profil, created = Normalien.objects.get_or_create(user=instance)
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2021-01-17 23:48:40 +01:00
|
|
|
if not created and profil.promotion != "":
|
2018-12-28 23:46:24 +01:00
|
|
|
return
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2021-01-17 23:48:40 +01:00
|
|
|
if "@" in instance.username:
|
|
|
|
profil.promotion = instance.username.split("@")[1]
|
|
|
|
profil.save()
|
|
|
|
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2021-01-17 23:48:40 +01:00
|
|
|
post_save.connect(create_basic_user_profile, sender=User)
|
|
|
|
|
|
|
|
# Hook d'authENS : information du CAS
|
|
|
|
def handle_cas_connection(sender, instance, created, cas_login, attributes, **kwargs):
|
|
|
|
profil, created = Normalien.objects.get_or_create(user=instance)
|
|
|
|
|
2021-01-31 22:53:59 +01:00
|
|
|
profil.last_cas_login = timezone.now().date()
|
2021-01-17 23:48:40 +01:00
|
|
|
if not created:
|
2021-01-31 22:53:59 +01:00
|
|
|
profil.save()
|
2021-01-17 23:48:40 +01:00
|
|
|
return
|
|
|
|
|
|
|
|
dirs = attributes.get("homeDirectory", "").split("/")
|
|
|
|
if len(dirs) < 4:
|
|
|
|
print("HomeDirectory invalide", dirs)
|
|
|
|
return
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2021-01-17 23:48:40 +01:00
|
|
|
year = dirs[2]
|
|
|
|
departement = dirs[3]
|
|
|
|
|
|
|
|
dep = dict(DEPARTEMENTS_DEFAUT).get(departement.lower(), "")
|
|
|
|
|
|
|
|
profil.promotion = "%s %s" % (dep, year)
|
|
|
|
profil.nom = attributes.get("name", "")
|
|
|
|
profil.save()
|
2018-12-28 23:46:24 +01:00
|
|
|
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2021-01-17 23:48:40 +01:00
|
|
|
post_cas_connect.connect(handle_cas_connection, sender=User)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
|
|
|
#
|
|
|
|
# Lieu de stage
|
|
|
|
#
|
|
|
|
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
class Lieu(models.Model):
|
|
|
|
# Général
|
2021-02-07 00:41:22 +01:00
|
|
|
nom = models.CharField("Nom de l'institution d'accueil", max_length=250)
|
|
|
|
type_lieu = models.CharField(
|
|
|
|
"Type de structure d'accueil",
|
|
|
|
default="universite",
|
|
|
|
choices=TYPE_LIEU_OPTIONS,
|
|
|
|
max_length=choices_length(TYPE_LIEU_OPTIONS),
|
|
|
|
)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
|
|
|
# Infos géographiques
|
2021-02-07 00:41:22 +01:00
|
|
|
ville = models.CharField("Ville", max_length=200)
|
|
|
|
pays = models.CharField(
|
|
|
|
"Pays", choices=PAYS_OPTIONS, max_length=choices_length(PAYS_OPTIONS)
|
|
|
|
)
|
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
# Coordonnées
|
2021-02-07 00:41:22 +01:00
|
|
|
# objects = geomodels.GeoManager() # Requis par GeoDjango
|
|
|
|
coord = geomodels.PointField("Coordonnées", geography=True, srid=4326)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2017-05-02 23:23:26 +02:00
|
|
|
# Type du lieu en plus joli
|
|
|
|
@property
|
|
|
|
def type_lieu_fancy(self):
|
|
|
|
return TYPE_LIEU_DICT.get(self.type_lieu, ("lieu", False))[0]
|
2017-05-16 23:37:10 +02:00
|
|
|
|
2017-05-02 23:23:26 +02:00
|
|
|
@property
|
|
|
|
def type_lieu_fem(self):
|
|
|
|
return TYPE_LIEU_DICT.get(self.type_lieu, ("lieu", False))[1]
|
2017-05-16 23:37:10 +02:00
|
|
|
|
2018-12-26 22:00:36 +01:00
|
|
|
def __str__(self):
|
2021-02-07 00:40:55 +01:00
|
|
|
return "%s (%s)" % (self.nom, self.ville)
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
class Meta:
|
|
|
|
verbose_name = "Lieu"
|
|
|
|
verbose_name_plural = "Lieux"
|
2021-02-07 00:41:22 +01:00
|
|
|
|
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
#
|
|
|
|
# Matières des stages
|
|
|
|
#
|
|
|
|
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
class StageMatiere(models.Model):
|
2021-02-07 00:40:55 +01:00
|
|
|
nom = models.CharField("Nom", max_length=30)
|
2017-04-04 00:28:25 +02:00
|
|
|
slug = models.SlugField()
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
class Meta:
|
|
|
|
verbose_name = "Matière des stages"
|
|
|
|
verbose_name_plural = "Matières des stages"
|
|
|
|
|
2018-12-26 22:00:36 +01:00
|
|
|
def __str__(self):
|
2017-04-07 03:01:27 +02:00
|
|
|
return self.nom
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
#
|
|
|
|
# Un stage
|
|
|
|
#
|
|
|
|
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
class Stage(models.Model):
|
|
|
|
# Misc
|
2021-02-07 00:41:22 +01:00
|
|
|
auteur = models.ForeignKey(
|
|
|
|
Normalien, related_name="stages", on_delete=models.SET_NULL, null=True
|
|
|
|
)
|
2021-02-07 00:40:55 +01:00
|
|
|
public = models.BooleanField("Visible publiquement", default=False)
|
|
|
|
date_creation = models.DateTimeField("Créé le", default=timezone.now)
|
|
|
|
date_maj = models.DateTimeField("Mis à jour le", default=timezone.now)
|
|
|
|
len_avis_stage = models.IntegerField("Longueur des avis de stage", default=0)
|
|
|
|
len_avis_lieux = models.IntegerField("Longueur des avis de lieu", default=0)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
|
|
|
# Caractéristiques du stage
|
2021-02-07 00:40:55 +01:00
|
|
|
sujet = models.CharField("Sujet", max_length=500)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2021-02-07 00:40:55 +01:00
|
|
|
date_debut = models.DateField("Date de début", null=True)
|
|
|
|
date_fin = models.DateField("Date de fin", null=True)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2021-02-07 00:41:22 +01:00
|
|
|
type_stage = models.CharField(
|
|
|
|
"Type",
|
|
|
|
default="stage",
|
|
|
|
choices=TYPE_STAGE_OPTIONS,
|
|
|
|
max_length=choices_length(TYPE_STAGE_OPTIONS),
|
|
|
|
)
|
|
|
|
niveau_scol = models.CharField(
|
|
|
|
"Année de scolarité",
|
|
|
|
default="",
|
|
|
|
choices=NIVEAU_SCOL_OPTIONS,
|
|
|
|
max_length=choices_length(NIVEAU_SCOL_OPTIONS),
|
|
|
|
blank=True,
|
|
|
|
)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2021-02-07 00:40:55 +01:00
|
|
|
thematiques = TaggableManager("Thématiques", blank=True)
|
2021-02-07 00:41:22 +01:00
|
|
|
matieres = models.ManyToManyField(
|
|
|
|
StageMatiere, verbose_name="Matière(s)", related_name="stages"
|
|
|
|
)
|
2021-02-07 00:40:55 +01:00
|
|
|
encadrants = models.CharField("Encadrant⋅e⋅s", max_length=500, blank=True)
|
|
|
|
structure = models.CharField("Structure d'accueil", max_length=500, blank=True)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
|
|
|
# Avis
|
2021-02-07 00:41:22 +01:00
|
|
|
lieux = models.ManyToManyField(
|
|
|
|
Lieu, related_name="stages", through="AvisLieu", blank=True
|
|
|
|
)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2017-05-02 23:23:26 +02:00
|
|
|
# Affichage des avis ordonnés
|
2017-04-04 00:28:25 +02:00
|
|
|
@property
|
|
|
|
def avis_lieux(self):
|
2021-02-07 00:41:22 +01:00
|
|
|
return self.avislieu_set.order_by("order")
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2017-05-02 23:23:26 +02:00
|
|
|
# Shortcut pour affichage rapide
|
2017-04-04 00:28:25 +02:00
|
|
|
@property
|
|
|
|
def lieu_principal(self):
|
2017-05-02 23:23:26 +02:00
|
|
|
avis_lieux = self.avis_lieux
|
|
|
|
if len(avis_lieux) == 0:
|
|
|
|
return None
|
2017-04-04 00:28:25 +02:00
|
|
|
return self.avis_lieux[0].lieu
|
2017-04-07 03:01:27 +02:00
|
|
|
|
2017-05-02 23:23:26 +02:00
|
|
|
# Type du stage en plus joli
|
|
|
|
@property
|
|
|
|
def type_stage_fancy(self):
|
|
|
|
return TYPE_STAGE_DICT.get(self.type_stage, ("stage", False))[0]
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-05-02 23:23:26 +02:00
|
|
|
@property
|
|
|
|
def type_stage_fem(self):
|
|
|
|
return TYPE_STAGE_DICT.get(self.type_stage, ("stage", False))[1]
|
2017-05-16 23:37:10 +02:00
|
|
|
|
|
|
|
# Niveau scolaire en plus joli
|
|
|
|
@property
|
|
|
|
def niveau_scol_fancy(self):
|
|
|
|
return NIVEAU_SCOL_DICT.get(self.niveau_scol, "")
|
2018-12-29 16:23:57 +01:00
|
|
|
|
|
|
|
# Optimisation de requêtes
|
|
|
|
@cached_property
|
|
|
|
def all_lieux(self):
|
|
|
|
return self.lieux.all()
|
2017-05-16 23:37:10 +02:00
|
|
|
|
2017-04-07 03:01:27 +02:00
|
|
|
def get_absolute_url(self):
|
2021-02-07 00:41:22 +01:00
|
|
|
return reverse("avisstage:stage", self)
|
|
|
|
|
2018-12-26 22:00:36 +01:00
|
|
|
def __str__(self):
|
2021-02-07 00:40:55 +01:00
|
|
|
return "%s (par %s)" % (self.sujet, self.auteur.user.username)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2017-10-02 23:59:20 +02:00
|
|
|
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
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-10-02 23:59:20 +02:00
|
|
|
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)
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-10-02 23:59:20 +02:00
|
|
|
if save:
|
|
|
|
self.save()
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
class Meta:
|
|
|
|
verbose_name = "Stage"
|
|
|
|
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
#
|
|
|
|
# Les avis
|
|
|
|
#
|
|
|
|
|
2021-02-07 00:41:22 +01:00
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
class AvisStage(models.Model):
|
2021-02-07 00:41:22 +01:00
|
|
|
stage = models.OneToOneField(
|
|
|
|
Stage, related_name="avis_stage", on_delete=models.CASCADE
|
|
|
|
)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2021-02-07 00:40:55 +01:00
|
|
|
chapo = models.TextField("En quelques mots", blank=True)
|
|
|
|
avis_ambiance = RichTextField("L'ambiance de travail", blank=True)
|
|
|
|
avis_sujet = RichTextField("La mission", blank=True)
|
|
|
|
avis_admin = RichTextField("Formalités et administration", blank=True)
|
|
|
|
avis_prestage = RichTextField("Avant le stage", blank=True, default="")
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2021-02-07 00:40:55 +01:00
|
|
|
les_plus = models.TextField("Les plus de cette expérience", blank=True)
|
|
|
|
les_moins = models.TextField("Les moins de cette expérience", blank=True)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2018-12-26 22:00:36 +01:00
|
|
|
def __str__(self):
|
2021-02-07 00:41:22 +01:00
|
|
|
return "Avis sur {%s} par %s" % (
|
|
|
|
self.stage.sujet,
|
|
|
|
self.stage.auteur.user.username,
|
|
|
|
)
|
2017-04-12 00:00:36 +02:00
|
|
|
|
2017-05-02 23:23:26 +02:00
|
|
|
# Liste des champs d'avis, couplés à leur nom (pour l'affichage)
|
2017-04-12 00:00:36 +02:00
|
|
|
@property
|
|
|
|
def avis_all(self):
|
2021-02-07 00:41:22 +01:00
|
|
|
fields = ["avis_sujet", "avis_ambiance", "avis_admin", "avis_prestage"]
|
|
|
|
return [
|
|
|
|
(AvisStage._meta.get_field(field).verbose_name, getattr(self, field, ""))
|
|
|
|
for field in fields
|
|
|
|
]
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2017-05-02 23:23:26 +02:00
|
|
|
|
2017-04-04 00:28:25 +02:00
|
|
|
class AvisLieu(models.Model):
|
2021-01-17 12:14:11 +01:00
|
|
|
stage = models.ForeignKey(Stage, on_delete=models.CASCADE)
|
|
|
|
lieu = models.ForeignKey(Lieu, on_delete=models.CASCADE)
|
2017-04-04 00:28:25 +02:00
|
|
|
order = models.IntegerField("Ordre", default=0)
|
|
|
|
|
2021-02-07 00:40:55 +01:00
|
|
|
chapo = models.TextField("En quelques mots", blank=True)
|
|
|
|
avis_lieustage = RichTextField("Les lieux de travail", blank=True)
|
2021-02-07 00:41:22 +01:00
|
|
|
avis_pratique = RichTextField("S'installer - conseils pratiques", blank=True)
|
2021-02-07 00:40:55 +01:00
|
|
|
avis_tourisme = RichTextField("Dans les parages", blank=True)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
2021-02-07 00:40:55 +01:00
|
|
|
les_plus = models.TextField("Les plus du lieu", blank=True)
|
|
|
|
les_moins = models.TextField("Les moins du lieu", blank=True)
|
2017-04-04 00:28:25 +02:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
verbose_name = "Avis sur un lieu de stage"
|
|
|
|
verbose_name_plural = "Avis sur un lieu de stage"
|
2017-04-04 00:31:50 +02:00
|
|
|
|
2018-12-26 22:00:36 +01:00
|
|
|
def __str__(self):
|
2021-02-07 00:40:55 +01:00
|
|
|
return "Avis sur {%s} par %s" % (self.lieu.nom, self.stage.auteur.user_id)
|
2017-05-02 23:23:26 +02:00
|
|
|
|
|
|
|
# Liste des champs d'avis, couplés à leur nom (pour l'affichage)
|
2017-04-12 00:00:36 +02:00
|
|
|
@property
|
|
|
|
def avis_all(self):
|
2021-02-07 00:41:22 +01:00
|
|
|
fields = ["avis_lieustage", "avis_pratique", "avis_tourisme"]
|
|
|
|
return [
|
|
|
|
(AvisLieu._meta.get_field(field).verbose_name, getattr(self, field, ""))
|
|
|
|
for field in fields
|
|
|
|
]
|