from django.contrib.auth.models import Group, Permission, User from django.db import models from django.db.models.signals import post_delete, post_save from django.dispatch import receiver from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from cof.petits_cours_models import choices_length # --- # User management # --- class Profile(models.Model): STATUS_EXTE = "exterieur" STATUS_1A = "1A" STATUS_2A = "2A" STATUS_3A = "3A" STATUS_4A = "4A" STATUS_ARCHI = "archicube" STATUS_DOCTORANT = "doctorant" STATUS_CST = "CST" STATUS_PEI = "PEI" OCCUPATION_CHOICES = ( (STATUS_EXTE, _("Extérieur")), (STATUS_1A, _("1A")), (STATUS_2A, _("2A")), (STATUS_3A, _("3A")), (STATUS_4A, _("4A")), (STATUS_ARCHI, _("Archicube")), (STATUS_DOCTORANT, _("Doctorant")), (STATUS_CST, _("CST")), (STATUS_PEI, _("PEI")), ) user = models.OneToOneField( User, on_delete=models.CASCADE, related_name="profile", verbose_name=_("utilisateur"), ) login_clipper = models.CharField( _("login clipper"), max_length=8, blank=True, ) phone = models.CharField(_("téléphone"), max_length=20, blank=True) occupation = models.CharField(_("occupation"), default="1A", choices=OCCUPATION_CHOICES, max_length=choices_length( OCCUPATION_CHOICES)) departement = models.CharField(_("département"), max_length=50, blank=True) comments = models.TextField( _("commentaires visibles par l'utilisateur"), blank=True ) class Meta: verbose_name = _("profil") verbose_name_plural = _("profils") def __str__(self): return self.user.username @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): if created: Profile.objects.get_or_create(user=instance) @receiver(post_delete, sender=Profile) def post_delete_user(sender, instance, *args, **kwargs): instance.user.delete() class AssociationManager(models.Manager): def get_by_natural_key(self, name): return self.get(name=name) class Association(models.Model): objects = AssociationManager() name = models.CharField( _("nom de l'association"), unique=True, max_length=30 ) staff_group = models.ForeignKey( Group, on_delete=models.PROTECT, related_name="staff_group_of", blank=True, null=True, verbose_name=_("groupe des membres du bureau"), ) members_group = models.ForeignKey( Group, on_delete=models.PROTECT, related_name="member_group_of", blank=True, null=True, verbose_name=_("groupe des membres"), ) def natural_key(self): return [self.name] class Meta: verbose_name = _("association") verbose_name_plural = _("associations") def __str__(self): return self.name def setup_perms(self, buro_of_apps=[], perms=[]): """ Setup permissions of the staff and members groups. Permission '.member' is added to the 'members_group' of the association. Permission '.buro' is added to the 'staff_group' of the association. All permissions of applications from 'buro_of_apps' are also added to this group. Arguments buro_of_apps (list of 'app_label', optional) perms (list of permission codes, optional) Should be used in receiver of 'post_migrate' signal, after permissions creation. """ def try_add_perm(group, app_label, codename): try: perm = Permission.objects.get( content_type__app_label=app_label, codename=codename, ) except Permission.DoesNotExist: pass else: group.permissions.add(perm) assoc_app_label = self.name.lower() # Buro group has perm '.buro'. try_add_perm(self.staff_group, assoc_app_label, 'buro') # Add all permissions of applications given 'buro_of_apps'. apps_perms = Permission.objects.filter( content_type__app_label__in=buro_of_apps, ) self.staff_group.permissions.add(*apps_perms) # Add extra permissions from 'perms' for perm in perms: app_label, codename = perm.split('.', maxsplit=1) try_add_perm(self.staff_group, app_label, codename) # Members have perm '.member'. try_add_perm(self.members_group, assoc_app_label, 'member') # --- # Clubs # --- class Club(models.Model): ANNUAL = "ANN" SEMESTER = "SEM" COURSE = "COU" COTISATION_FREQUENCY_CHOICES = [ (ANNUAL, _("Annuel")), (SEMESTER, _("Semestriel")), (COURSE, _("Au cours")) ] association = models.ForeignKey( Association, on_delete=models.PROTECT, related_name="clubs", verbose_name=_("association"), ) name = models.CharField(_("nom"), max_length=200, unique=True) description = models.TextField(_("description"), blank=True) members = models.ManyToManyField( User, through="ClubUser", related_name="in_clubs", blank=True, verbose_name=_("membres du club"), ) price = models.DecimalField( _("cotisation (€)"), decimal_places=2, max_digits=5, blank=True, default=0 ) cotisation_frequency = models.CharField( _("fréquence de la cotisation"), default=ANNUAL, choices=COTISATION_FREQUENCY_CHOICES, max_length=3, blank=True ) class Meta: verbose_name = _("club") verbose_name_plural = _("clubs") def __str__(self): template = ( self.needs_cotiz and "%(name)s (%(price).2f€ / %(cotisation_frequency)s)" or "%(name)s" ) return template % vars(self) def get_absolute_url(self): return reverse('gestion:club_detail', args=[str(self.pk)]) @property def needs_cotiz(self): return bool(self.price) def is_respo(self, user): return self.clubuser_set.filter(user=user, is_respo=True).exists() class ClubUser(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) club = models.ForeignKey(Club, on_delete=models.CASCADE) is_respo = models.BooleanField(_("est responsable du club")) has_paid = models.BooleanField(_("a payé sa cotisation")) class Meta: verbose_name = _("inscription au club") verbose_name_plural = _("inscriptions aux clubs") # --- # Events # --- class Location(models.Model): name = models.CharField(_("nom du lieu"), max_length=200) class Meta: verbose_name = _("lieu") verbose_name_plural = _("lieux") def __str__(self): return self.name class Event(models.Model): associations = models.ManyToManyField( Association, related_name="events", verbose_name=_("associations"), ) title = models.CharField(_("titre"), max_length=200) location = models.ForeignKey( Location, blank=True, null=True, on_delete=models.PROTECT, verbose_name=_("lieux"), ) start_date = models.DateTimeField( _("début de l'événement"), blank=True, null=True, ) end_date = models.DateTimeField( _("fin de l'événement"), blank=True, null=True, ) description = models.TextField(_("description"), blank=True) image = models.ImageField( _("image"), blank=True, null=True, upload_to="public/imgs/events/", ) registration_open = models.NullBooleanField( _("les inscriptions sont ouvertes"), help_text=_("Indéfini signifie « l'inscription n'est pas requise »"), default=True, ) old = models.BooleanField(_("archiver (événement fini)"), default=False) class Meta: verbose_name = _("événement") verbose_name_plural = _("événements") def __str__(self): title, location = self.title, self.location start = self.start_date.date() end = self.end_date.date() if not self.start_date.date(): return "{} @ {}".format(title, location) elif start == end: return "{} @ {} ({})".format(title, location, start) else: return "{} @ {} ({} → {})".format(title, location, start, end) class EventCommentField(models.Model): TEXT = "text" CHAR = "char" FIELD_TYPE = [ (TEXT, _("Texte long")), (CHAR, _("Texte court")), ] event = models.ForeignKey( Event, on_delete=models.CASCADE, related_name="commentfields", verbose_name=_("événement"), ) name = models.CharField(_("nom du champ"), max_length=200) fieldtype = models.CharField( _("type de champ"), max_length=10, choices=FIELD_TYPE, default=TEXT, ) default = models.TextField(_("valeur par défaut"), blank=True) ordering = models.IntegerField( _("ordre des champs"), default=0, help_text=_( "plus petit en premier, ordre alphabétique sur le nom si " "ambiguïté" ), ) class Meta: verbose_name = _("champ") verbose_name_plural = _("champs") def __str__(self): return self.name class EventCommentValue(models.Model): commentfield = models.ForeignKey( EventCommentField, on_delete=models.CASCADE, related_name="values" ) registration = models.ForeignKey( "EventRegistration", on_delete=models.CASCADE, related_name="comments" ) content = models.TextField(_("contenu"), blank=True) class Meta: unique_together = ("commentfield", "registration") def __str__(self): return ( _("Commentaire de %(field_name)s") % {"field_name": self.commentfield} ) class EventOption(models.Model): event = models.ForeignKey( Event, on_delete=models.CASCADE, related_name="options", verbose_name=_("événement"), ) name = models.CharField(_("option"), max_length=200) multi_choices = models.BooleanField(_("choix multiples"), default=False) class Meta: verbose_name = _("option des événements") verbose_name_plural = _("options des événements") def __str__(self): return self.name class EventOptionChoice(models.Model): event_option = models.ForeignKey( EventOption, on_delete=models.CASCADE, related_name="choices", verbose_name=_("événement"), ) value = models.CharField("Valeur", max_length=200) class Meta: verbose_name = _("choix") verbose_name_plural = _("choix") def __str__(self): return self.value class EventRegistration(models.Model): user = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, verbose_name=_("utilisateur"), ) event = models.ForeignKey( Event, on_delete=models.CASCADE, verbose_name=_("événement"), ) options = models.ManyToManyField( EventOptionChoice, verbose_name=_("choix"), ) filledcomments = models.ManyToManyField( EventCommentField, through=EventCommentValue, verbose_name=_("commentaires"), ) paid = models.BooleanField(_("a payé"), default=False) class Meta: verbose_name = _("inscription") verbose_name_plural = _("inscriptions") unique_together = ("user", "event") def __str__(self): return ( _("Inscription de %(user)s à %(event)s") % {"user": self.user, "event": self.event} )