Repository: 3cn-ecn/nantralPlatform

Issue:
Colocathlon
Préparation du support de fonctionnalités pour le colocathlon, à la demande du BDE. Fonctionnalités demandées :

- [x] Possibilité pour chaque coloc de renseigner ses horaires d'ouvertures et activités proposées, et si elles participent ou non
- [x] Affichage des infos ci-dessous pour tout le monde sur la page de la coloc
- [x] Possibilité pour n'importe quel utilisateur connecté de s'inscrire à une et une seule coloc, sur le principe du shotgun
- [x] Pour les membres de la coloc, voir les utilisateurs inscrits
- [x] Activer un mode "Colocathlon" sur la carte pour afficher uniquement les colocs participantes
- [x] En mode colocathlon, afficher sur la carte les activités et heures proposées au lieu des descriptions et membres dans les infobulles


Discussion:
Pour afficher ou non le mode colocathlon, j'ai ajouté une extension qui permet de gérer des paramètres directement depuis l'interface admin (cf utilisation dans la vue des détails d'une coloc, sur la branche colocathlon)
J'ai pas encore fait de doc mais en gros :
* PHASE_COLOCATHLON == 0: Pas de colocathlon à afficher
* PHASE_COLOCATHLON == 1: Première phase, on demande aux colocs ce qu'elles veulent proposer
* PHASE_COLOCATHLON == 2: On affiche les infos pour tout le monde et les utilisateurs peuvent s'inscrire à une coloc

Resolving patch:
diff --git a/server/apps/club/api_views.py b/server/apps/club/api_views.py
--- a/server/apps/club/api_views.py
+++ b/server/apps/club/api_views.py
@@ -55,6 +55,7 @@ def post(self, request, *args, **kwargs):
         # editMode == 1 -> Edit the order of the members
         # editMode == 2 -> Edit a member
         # editMode == 3 -> Delete a member
+        # editMode == 4 -> Add a member
         if editMode == 1:
             newOrderedMembers = request.data.get("orderedMembers")
             for member in newOrderedMembers:
@@ -78,3 +79,37 @@ def post(self, request, *args, **kwargs):
             id = request.data.get("id")
             NamedMembershipClub.objects.get(id=id).delete()
             return HttpResponse(status=200)
+
+        elif editMode == 4:
+            studentIDToAdd = request.data.get("id")
+            studentToAdd = Student.objects.get(id=studentIDToAdd)
+            # Check if student already exists
+            if NamedMembershipClub.objects.filter(
+                    student=studentToAdd, group=club).exists():
+                return HttpResponse(status=403)
+            admin = request.data.get("admin")
+            function = request.data.get("function")
+            beginDate = parse_date(request.data.get("date_begin")) if request.data.get(
+                "beginDate") is not None else None
+            endDate = parse_date(request.data.get("date_end")) if request.data.get(
+                "endDate") is not None else None
+
+            # Check if dates are valid
+            if beginDate is not None and endDate is not None and beginDate > endDate:
+                return HttpResponse(status=500)
+
+            if beginDate is not None:
+                NamedMembershipClub.objects.create(
+                    group=club,
+                    student=studentToAdd,
+                    admin=admin,
+                    function=function,
+                    date_begin=beginDate,
+                    date_end=endDate).save()
+            else:
+                NamedMembershipClub.objects.create(
+                    group=club,
+                    student=studentToAdd,
+                    admin=admin,
+                    function=function).save()
+            return HttpResponse(status=200)
diff --git a/server/apps/club/models.py b/server/apps/club/models.py
--- a/server/apps/club/models.py
+++ b/server/apps/club/models.py
@@ -9,27 +9,29 @@
 from apps.student.models import Student
 
 
-
 class Club(Group):
     members = models.ManyToManyField(Student, through='NamedMembershipClub')
     bdx_type = models.ForeignKey(
         'BDX', on_delete=models.SET_NULL, verbose_name='Type de club BDX', null=True, blank=True)
-    email = models.EmailField("Email de l'asso", max_length=50, null=True, blank=True)
-    meeting_place = models.CharField("Local / Lieu de réunion", max_length=50, null=True, blank=True)
-    meeting_hour = models.CharField("Heure et jour de réunion périodique", max_length=50, null=True, blank=True)
-    
+    email = models.EmailField(
+        "Email de l'asso", max_length=50, null=True, blank=True)
+    meeting_place = models.CharField(
+        "Local / Lieu de réunion", max_length=50, null=True, blank=True)
+    meeting_hour = models.CharField(
+        "Heure et jour de réunion périodique", max_length=50, null=True, blank=True)
+
     class Meta:
         ordering = [F('bdx_type').asc(nulls_first=True), 'name']
         verbose_name = "club/asso"
         verbose_name_plural = "clubs & assos"
-    
+
     def is_admin(self, user) -> bool:
         is_admin = super(Club, self).is_admin(user)
         if not is_admin and self.bdx_type:
             return self.bdx_type.is_admin(user)
         else:
             return is_admin
-    
+
     def save(self, *args, **kwargs):
         # mise à jour du cache de la liste des clubs
         key = make_template_fragment_key('club_list')
@@ -48,13 +50,14 @@ class Meta:
         verbose_name_plural = "BDX"
 
 
-
 class NamedMembershipClub(NamedMembership):
     group = models.ForeignKey(Club, on_delete=models.CASCADE)
     function = models.CharField(
         verbose_name='Rôle (facultatif)', max_length=200, blank=True)
-    date_begin = models.DateField(verbose_name='Date de début', default=timezone.now().today)
-    date_end = models.DateField(verbose_name='Date de fin (facultatif)', blank=True, null=True)
+    date_begin = models.DateField(
+        verbose_name='Date de début', default=timezone.now().today)
+    date_end = models.DateField(
+        verbose_name='Date de fin (facultatif)', blank=True, null=True)
     order = models.IntegerField(verbose_name='Hiérarchie', default=0)
 
     @property
@@ -66,4 +69,11 @@ def year(self, **kwargs):
         if m >= 8:
             return y + 1
         else:
-            return y
\ No newline at end of file
+            return y
+
+    def get_or_create(self, **kwargs):
+        defaults = kwargs.pop('defaults', {})  # popping defaults from values
+        for key, value in kwargs.items():
+            if value == None:
+                kwargs[key] = defaults.get(key)
+        return super(NamedMembershipClub, self).get_or_create(**kwargs)
diff --git a/server/apps/student/api_urls.py b/server/apps/student/api_urls.py
--- a/server/apps/student/api_urls.py
+++ b/server/apps/student/api_urls.py
@@ -1,4 +1,3 @@
-from django.conf.urls import url
 from django.urls import path
 
 from .api_views import *


Test patch: