kpsul/gestioncof/models.py

317 lines
10 KiB
Python
Raw Normal View History

core -- Install django-allauth-ens Refer to allauth doc for an accurate features list: http://django-allauth.readthedocs.io/en/latest/ Users can now change their password, ask for a password reset, or set one if they don't have one. In particular, it allows users whose account has been created via a clipper authentication to configure a password before losing their clipper. Even if they have already lost it, they are able to get one using the "Reset password" functionality. Allauth multiple emails management is deactivated. Requests to the related url redirect to the home page. All the login and logout views are replaced by the allauth' ones. It also concerns the Django and Wagtail admin sites. Note that users are no longer logged out of the clipper CAS server when they authenticated via this server. Instead a message suggests the user to disconnect. Clipper connections and `login_clipper` --------------------------------------- - Non-empty `login_clipper` are now unique among `CofProfile` instances. - They are created once for users with a non-empty 'login_clipper' (with the data migration 0014_create_clipper_connections). - The `login_clipper` of CofProfile instances are sync with their clipper connections: * `CofProfile.sync_clipper_connections` method updates the connections based on `login_clipper`. * Signals receivers `sync_clipper…` update `login_clipper` based on connections creations/updates/deletions. Misc ---- - Add NullCharField (model field) which allows to use `unique=True` on CharField (even with empty strings). - Parts of kfet mixins for TestCase are now in shared.tests.testcase, as they are used elsewhere than in the kfet app.
2017-10-19 01:12:52 +02:00
from allauth.socialaccount.models import SocialAccount
from django.contrib.auth.models import User
2012-06-27 23:28:35 +02:00
from django.db import models
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
2012-06-27 23:28:35 +02:00
from django.utils.translation import ugettext_lazy as _
from bda.models import Spectacle
from gestioncof.petits_cours_models import choices_length
core -- Install django-allauth-ens Refer to allauth doc for an accurate features list: http://django-allauth.readthedocs.io/en/latest/ Users can now change their password, ask for a password reset, or set one if they don't have one. In particular, it allows users whose account has been created via a clipper authentication to configure a password before losing their clipper. Even if they have already lost it, they are able to get one using the "Reset password" functionality. Allauth multiple emails management is deactivated. Requests to the related url redirect to the home page. All the login and logout views are replaced by the allauth' ones. It also concerns the Django and Wagtail admin sites. Note that users are no longer logged out of the clipper CAS server when they authenticated via this server. Instead a message suggests the user to disconnect. Clipper connections and `login_clipper` --------------------------------------- - Non-empty `login_clipper` are now unique among `CofProfile` instances. - They are created once for users with a non-empty 'login_clipper' (with the data migration 0014_create_clipper_connections). - The `login_clipper` of CofProfile instances are sync with their clipper connections: * `CofProfile.sync_clipper_connections` method updates the connections based on `login_clipper`. * Signals receivers `sync_clipper…` update `login_clipper` based on connections creations/updates/deletions. Misc ---- - Add NullCharField (model field) which allows to use `unique=True` on CharField (even with empty strings). - Parts of kfet mixins for TestCase are now in shared.tests.testcase, as they are used elsewhere than in the kfet app.
2017-10-19 01:12:52 +02:00
from utils.models import NullCharField
TYPE_COMMENT_FIELD = (("text", _("Texte long")), ("char", _("Texte court")))
2012-06-27 23:28:35 +02:00
class CofProfile(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")),
)
COTIZ_ETUDIANT = "etudiant"
COTIZ_NORMALIEN = "normalien"
COTIZ_EXTE = "exterieur"
COTIZ_GRATIS = "gratis"
TYPE_COTIZ_CHOICES = (
(COTIZ_ETUDIANT, _("Normalien étudiant")),
(COTIZ_NORMALIEN, _("Normalien élève")),
(COTIZ_EXTE, _("Extérieur")),
(COTIZ_GRATIS, _("Gratuit")),
)
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
core -- Install django-allauth-ens Refer to allauth doc for an accurate features list: http://django-allauth.readthedocs.io/en/latest/ Users can now change their password, ask for a password reset, or set one if they don't have one. In particular, it allows users whose account has been created via a clipper authentication to configure a password before losing their clipper. Even if they have already lost it, they are able to get one using the "Reset password" functionality. Allauth multiple emails management is deactivated. Requests to the related url redirect to the home page. All the login and logout views are replaced by the allauth' ones. It also concerns the Django and Wagtail admin sites. Note that users are no longer logged out of the clipper CAS server when they authenticated via this server. Instead a message suggests the user to disconnect. Clipper connections and `login_clipper` --------------------------------------- - Non-empty `login_clipper` are now unique among `CofProfile` instances. - They are created once for users with a non-empty 'login_clipper' (with the data migration 0014_create_clipper_connections). - The `login_clipper` of CofProfile instances are sync with their clipper connections: * `CofProfile.sync_clipper_connections` method updates the connections based on `login_clipper`. * Signals receivers `sync_clipper…` update `login_clipper` based on connections creations/updates/deletions. Misc ---- - Add NullCharField (model field) which allows to use `unique=True` on CharField (even with empty strings). - Parts of kfet mixins for TestCase are now in shared.tests.testcase, as they are used elsewhere than in the kfet app.
2017-10-19 01:12:52 +02:00
login_clipper = NullCharField(
_("Login clipper"), max_length=32, blank=True, unique=True, default=""
)
is_cof = models.BooleanField("Membre du COF", default=False)
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)
type_cotiz = models.CharField(
_("Type de cotisation"),
default="normalien",
choices=TYPE_COTIZ_CHOICES,
max_length=choices_length(TYPE_COTIZ_CHOICES),
)
mailing_cof = models.BooleanField("Recevoir les mails COF", default=False)
mailing_bda = models.BooleanField("Recevoir les mails BdA", default=False)
mailing_unernestaparis = models.BooleanField(
"Recevoir les mails unErnestAParis", default=False
)
mailing_bda_revente = models.BooleanField(
"Recevoir les mails de revente de places BdA", default=False
)
comments = models.TextField("Commentaires visibles par l'utilisateur", blank=True)
is_buro = models.BooleanField("Membre du Burô", default=False)
petits_cours_accept = models.BooleanField(
"Recevoir des petits cours", default=False
)
petits_cours_remarques = models.TextField(
_("Remarques et précisions pour les petits cours"), blank=True, default=""
)
2012-06-27 23:28:35 +02:00
2012-07-11 17:39:20 +02:00
class Meta:
verbose_name = "Profil COF"
verbose_name_plural = "Profils COF"
def __str__(self):
2017-05-22 00:39:36 +02:00
return self.user.username
2012-07-11 17:39:20 +02:00
core -- Install django-allauth-ens Refer to allauth doc for an accurate features list: http://django-allauth.readthedocs.io/en/latest/ Users can now change their password, ask for a password reset, or set one if they don't have one. In particular, it allows users whose account has been created via a clipper authentication to configure a password before losing their clipper. Even if they have already lost it, they are able to get one using the "Reset password" functionality. Allauth multiple emails management is deactivated. Requests to the related url redirect to the home page. All the login and logout views are replaced by the allauth' ones. It also concerns the Django and Wagtail admin sites. Note that users are no longer logged out of the clipper CAS server when they authenticated via this server. Instead a message suggests the user to disconnect. Clipper connections and `login_clipper` --------------------------------------- - Non-empty `login_clipper` are now unique among `CofProfile` instances. - They are created once for users with a non-empty 'login_clipper' (with the data migration 0014_create_clipper_connections). - The `login_clipper` of CofProfile instances are sync with their clipper connections: * `CofProfile.sync_clipper_connections` method updates the connections based on `login_clipper`. * Signals receivers `sync_clipper…` update `login_clipper` based on connections creations/updates/deletions. Misc ---- - Add NullCharField (model field) which allows to use `unique=True` on CharField (even with empty strings). - Parts of kfet mixins for TestCase are now in shared.tests.testcase, as they are used elsewhere than in the kfet app.
2017-10-19 01:12:52 +02:00
def save(self, *args, **kwargs):
created = self.pk is None
res = super().save(*args, **kwargs)
self.sync_clipper_connections(created=created)
return res
def sync_clipper_connections(self, created):
"""
Update the clipper connections of the user according to the value of
`login_clipper`.
If empty, all clipper connections are removed.
See also `sync_clipper` signals handlers (in `signals` module). They
sync `login_clipper` from the clipper connections.
Raises
IntegrityError: login_clipper is already used by another user.
"""
user, clipper = self.user, self.login_clipper
conns = user.socialaccount_set
clipper_conns = conns.filter(provider="clipper")
if created and clipper:
conns.create(provider="clipper", uid=clipper)
return
if clipper:
try:
# If a clipper connection already exists with the uid, call
# save to update last_login value (an auto_now field).
conn = clipper_conns.get(uid=clipper)
conn.save()
except SocialAccount.DoesNotExist:
# Nothing prevents the user from having multiple clipper
# connections. Let's update the most recently used with the
# given identifier. If none exists, create it.
try:
conn = clipper_conns.latest("last_login")
if conn.uid != clipper:
conn.uid = clipper
conn.save(update_fields=["uid"])
except SocialAccount.DoesNotExist:
conns.create(provider="clipper", uid=clipper)
else:
clipper_conns.delete()
@receiver(post_save, sender=User)
2012-06-27 23:28:35 +02:00
def create_user_profile(sender, instance, created, **kwargs):
if created:
CofProfile.objects.get_or_create(user=instance)
2012-06-27 23:28:35 +02:00
@receiver(post_delete, sender=CofProfile)
def post_delete_user(sender, instance, *args, **kwargs):
instance.user.delete()
2012-06-27 23:28:35 +02:00
class Club(models.Model):
name = models.CharField("Nom", max_length=200, unique=True)
description = models.TextField("Description", blank=True)
respos = models.ManyToManyField(User, related_name="clubs_geres", blank=True)
membres = models.ManyToManyField(User, related_name="clubs", blank=True)
def __str__(self):
return self.name
2012-06-27 23:28:35 +02:00
class Event(models.Model):
title = models.CharField("Titre", max_length=200)
location = models.CharField("Lieu", max_length=200)
start_date = models.DateTimeField("Date de début", blank=True, null=True)
end_date = models.DateTimeField("Date de fin", blank=True, null=True)
description = models.TextField("Description", blank=True)
image = models.ImageField("Image", blank=True, null=True, upload_to="imgs/events/")
registration_open = models.BooleanField("Inscriptions ouvertes", default=True)
old = models.BooleanField("Archiver (événement fini)", default=False)
2012-06-27 23:28:35 +02:00
class Meta:
verbose_name = "Événement"
def __str__(self):
2017-05-22 00:39:36 +02:00
return self.title
2012-06-27 23:28:35 +02:00
class EventCommentField(models.Model):
event = models.ForeignKey(
Event, on_delete=models.CASCADE, related_name="commentfields"
)
name = models.CharField("Champ", max_length=200)
fieldtype = models.CharField(
"Type", max_length=10, choices=TYPE_COMMENT_FIELD, default="text"
)
default = models.TextField("Valeur par défaut", blank=True)
class Meta:
verbose_name = "Champ"
def __str__(self):
2017-05-22 00:39:36 +02:00
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, null=True)
def __str__(self):
return "Commentaire de %s" % self.commentfield
2012-06-27 23:28:35 +02:00
class EventOption(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="options")
name = models.CharField("Option", max_length=200)
multi_choices = models.BooleanField("Choix multiples", default=False)
2012-06-27 23:28:35 +02:00
class Meta:
verbose_name = "Option"
def __str__(self):
2017-05-22 00:39:36 +02:00
return self.name
2012-06-27 23:28:35 +02:00
2012-06-27 23:28:35 +02:00
class EventOptionChoice(models.Model):
event_option = models.ForeignKey(
EventOption, on_delete=models.CASCADE, related_name="choices"
)
value = models.CharField("Valeur", max_length=200)
2012-06-27 23:28:35 +02:00
class Meta:
verbose_name = "Choix"
verbose_name_plural = "Choix"
2012-06-27 23:28:35 +02:00
def __str__(self):
2017-05-22 00:39:36 +02:00
return self.value
2012-06-27 23:28:35 +02:00
2012-06-27 23:28:35 +02:00
class EventRegistration(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
2012-06-27 23:28:35 +02:00
options = models.ManyToManyField(EventOptionChoice)
filledcomments = models.ManyToManyField(
EventCommentField, through=EventCommentValue
)
paid = models.BooleanField("A payé", default=False)
2012-06-27 23:28:35 +02:00
class Meta:
verbose_name = "Inscription"
2012-07-11 17:39:20 +02:00
unique_together = ("user", "event")
2012-06-27 23:28:35 +02:00
2016-07-15 00:02:56 +02:00
def __str__(self):
2017-05-22 00:39:36 +02:00
return "Inscription de {} à {}".format(self.user, self.event.title)
2012-06-27 23:28:35 +02:00
class Survey(models.Model):
title = models.CharField("Titre", max_length=200)
details = models.TextField("Détails", blank=True)
survey_open = models.BooleanField("Sondage ouvert", default=True)
old = models.BooleanField("Archiver (sondage fini)", default=False)
2012-06-27 23:28:35 +02:00
class Meta:
verbose_name = "Sondage"
def __str__(self):
2017-05-22 00:39:36 +02:00
return self.title
2012-06-27 23:28:35 +02:00
2012-06-27 23:28:35 +02:00
class SurveyQuestion(models.Model):
survey = models.ForeignKey(
Survey, on_delete=models.CASCADE, related_name="questions"
)
question = models.CharField("Question", max_length=200)
multi_answers = models.BooleanField("Choix multiples", default=False)
2012-06-27 23:28:35 +02:00
class Meta:
verbose_name = "Question"
def __str__(self):
2017-05-22 00:39:36 +02:00
return self.question
2012-06-27 23:28:35 +02:00
2012-06-27 23:28:35 +02:00
class SurveyQuestionAnswer(models.Model):
survey_question = models.ForeignKey(
SurveyQuestion, on_delete=models.CASCADE, related_name="answers"
)
answer = models.CharField("Réponse", max_length=200)
2012-06-27 23:28:35 +02:00
class Meta:
verbose_name = "Réponse"
def __str__(self):
2017-05-22 00:39:36 +02:00
return self.answer
2012-06-27 23:28:35 +02:00
2012-06-27 23:28:35 +02:00
class SurveyAnswer(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
answers = models.ManyToManyField(SurveyQuestionAnswer, related_name="selected_by")
2012-06-27 23:28:35 +02:00
class Meta:
verbose_name = "Réponses"
2012-07-11 17:39:20 +02:00
unique_together = ("user", "survey")
def __str__(self):
return "Réponse de %s sondage %s" % (
self.user.get_full_name(),
self.survey.title,
)
class CalendarSubscription(models.Model):
token = models.UUIDField()
user = models.OneToOneField(User, on_delete=models.CASCADE)
other_shows = models.ManyToManyField(Spectacle)
subscribe_to_events = models.BooleanField(default=True)
subscribe_to_my_shows = models.BooleanField(default=True)
def __str__(self):
return "Calendrier de %s" % self.user.get_full_name()