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/group/models.py b/server/apps/group/models.py
--- a/server/apps/group/models.py
+++ b/server/apps/group/models.py
@@ -66,7 +66,7 @@ def is_admin(self, user: User) -> bool:
         if user.is_anonymous or not user.is_authenticated or not hasattr(user, 'student'):
             return False
         student = Student.objects.filter(user=user).first()
-        if user.is_superuser or user.is_staff:
+        if user.is_superuser:
             return True
         if self.is_member(user):
             members_list = self.members.through.objects.filter(group=self)
diff --git a/server/apps/roommates/api_urls.py b/server/apps/roommates/api_urls.py
--- a/server/apps/roommates/api_urls.py
+++ b/server/apps/roommates/api_urls.py
@@ -1,11 +1,12 @@
-from django.urls import path
-
-from .api_views import *
-
-app_name = 'roommates_api'
-
-urlpatterns = [
-    path('geocoding/', SearchGeocodingView.as_view(), name='geocoding'),
-    path('housing', HousingView.as_view(), name='housing'),
-    path('housing/check', CheckAddressView.as_view(), name='address-check'),
-]
+from django.urls import path
+
+from .api_views import *
+
+app_name = 'roommates_api'
+
+urlpatterns = [
+    path('geocoding/', SearchGeocodingView.as_view(), name='geocoding'),
+    path('housing', HousingView.as_view(), name='housing'),
+    path('housing/check', CheckAddressView.as_view(), name='address-check'),
+    path('roommates-details', RoommatesDetails.as_view(), name="roommates-details")
+]
diff --git a/server/apps/roommates/api_views.py b/server/apps/roommates/api_views.py
--- a/server/apps/roommates/api_views.py
+++ b/server/apps/roommates/api_views.py
@@ -1,50 +1,81 @@
-from rest_framework import generics, permissions
-from rest_framework.response import Response
-from rest_framework.views import APIView
-
-from .serializers import HousingLastRoommatesSerializer
-from .models import Housing
-from apps.utils.geocoding import geocode
-
-from django.utils import timezone
-from django.db.models import Q
-
-
-class SearchGeocodingView(APIView):
-    """A view to query the external Geocoding service."""
-    permission_classes = [permissions.IsAuthenticated]
-
-    def get(self, request):
-        return Response(data=geocode(request.GET.get("search_string")))
-
-
-class HousingView(generics.ListCreateAPIView):
-    """API View to get all the housing and their current roommates"""
-    serializer_class = HousingLastRoommatesSerializer
-    permission_classes = [permissions.IsAuthenticated]
-
-    def get_queryset(self):
-        colocathlonParticipants = int(self.request.GET.get(
-            'colocathlonParticipants'))
-        now = timezone.now()
-        if colocathlonParticipants == 1:
-            query = Housing.objects.filter((
-                Q(Q(roommates__begin_date__lte=now) & (Q(roommates__end_date__gte=now) | Q(roommates__end_date=None))) | Q(roommates__members=None)) & Q(roommates__colocathlon_agree=True)).distinct()
-            return query
-        query = Housing.objects.filter(
-            Q(Q(roommates__begin_date__lte=now) & (Q(roommates__end_date__gte=now) | Q(roommates__end_date=None))) | Q(roommates__members=None)).distinct()
-        return query
-
-
-class CheckAddressView(APIView):
-    """An API view to check whether a housing already exists at the selected address.
-    Returns the pk if it does, None otherwise"""
-    permission_classes = [permissions.IsAuthenticated]
-
-    def post(self, request):
-        query = Housing.objects.filter(address=request.data.get("address"))
-        data = [{
-            'pk': housing.pk,
-            'name': f'{housing.address} - {housing.details} ({housing.current_roommates})'
-        } for housing in query]
-        return Response(data=data)
+from django.http.response import HttpResponse
+from rest_framework import generics, permissions
+from rest_framework.response import Response
+from rest_framework.views import APIView
+
+from .serializers import HousingLastRoommatesSerializer, RoommatesSerializer
+from .models import Housing, Roommates
+from apps.utils.geocoding import geocode
+
+from django.utils import timezone
+from django.db.models import Q
+
+
+class SearchGeocodingView(APIView):
+    """A view to query the external Geocoding service."""
+    permission_classes = [permissions.IsAuthenticated]
+
+    def get(self, request):
+        return Response(data=geocode(request.GET.get("search_string")))
+
+
+class HousingView(generics.ListCreateAPIView):
+    """API View to get all the housing and their current roommates"""
+    serializer_class = HousingLastRoommatesSerializer
+    permission_classes = [permissions.IsAuthenticated]
+
+    def get_queryset(self):
+        colocathlonParticipants = int(self.request.GET.get(
+            'colocathlonParticipants'))
+        now = timezone.now()
+        if colocathlonParticipants == 1:
+            query = Housing.objects.filter((
+                Q(Q(roommates__begin_date__lte=now) & (Q(roommates__end_date__gte=now) | Q(roommates__end_date=None))) | Q(roommates__members=None)) & Q(roommates__colocathlon_agree=True)).distinct()
+            return query
+        query = Housing.objects.filter(
+            Q(Q(roommates__begin_date__lte=now) & (Q(roommates__end_date__gte=now) | Q(roommates__end_date=None))) | Q(roommates__members=None)).distinct()
+        return query
+
+
+class CheckAddressView(APIView):
+    """An API view to check whether a housing already exists at the selected address.
+    Returns the pk if it does, None otherwise"""
+    permission_classes = [permissions.IsAuthenticated]
+
+    def post(self, request):
+        query = Housing.objects.filter(address=request.data.get("address"))
+        data = [{
+            'pk': housing.pk,
+            'name': f'{housing.address} - {housing.details} ({housing.current_roommates})'
+        } for housing in query]
+        return Response(data=data)
+
+
+class RoommatesDetails(APIView):
+    """An API view to return the details of a roommates instance"""
+    serializer_class = RoommatesSerializer
+    permission_classes = [permissions.IsAuthenticated]
+
+    def get(self, request):
+        roommatesSlug = self.request.GET.get(
+            'slug')
+        roommates = [generics.get_object_or_404(Roommates, slug=roommatesSlug)]
+        serializer = self.serializer_class(roommates, many=True)
+        return Response(serializer.data)
+
+    def post(self, request):
+        roommates = generics.get_object_or_404(
+            Roommates, slug=request.data.get("slug"))
+        if not roommates.colocathlon_agree:
+            return Response(status=403)
+        addOrDelete = int(request.data.get("addOrDelete"))
+
+        # addOrDelete == 1 -> Delete user
+        # addOrDelete == 0 -> Add user
+        if addOrDelete == 0:
+            if roommates.colocathlon_quota > roommates.colocathlon_participants.count():
+                roommates.colocathlon_participants.add(request.user.student)
+                return Response(status=200)
+            return Response(status=403)
+        roommates.colocathlon_participants.remove(request.user.student)
+        return Response(status=200)


Test patch: