diff --git a/core/admin.py b/core/admin.py index 8982378..b2cbc08 100644 --- a/core/admin.py +++ b/core/admin.py @@ -14,6 +14,8 @@ from core.models.AssetBundleRoom import AssetBundleRoom from core.models.AssignRoomUser import AssignRoomUser # from .model import user from core.models.AssignedRule import AssignedRule +from core.models.Meeting import Meeting +from core.models.Invitation import Invitation admin.site.register(Image) @@ -29,3 +31,5 @@ admin.site.register(Space) admin.site.register(TeamMember) admin.site.register(AssetBundleRoom) admin.site.register(AssignRoomUser) +admin.site.register(Meeting) +admin.site.register(Invitation) diff --git a/core/migrations/0006_meeting_code.py b/core/migrations/0006_meeting_code.py new file mode 100644 index 0000000..15bc5df --- /dev/null +++ b/core/migrations/0006_meeting_code.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2 on 2025-06-02 09:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0005_meeting'), + ] + + operations = [ + migrations.AddField( + model_name='meeting', + name='code', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/core/migrations/0007_invitation.py b/core/migrations/0007_invitation.py new file mode 100644 index 0000000..8c29f3c --- /dev/null +++ b/core/migrations/0007_invitation.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2 on 2025-06-02 10:02 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('core', '0006_meeting_code'), + ] + + operations = [ + migrations.CreateModel( + name='Invitation', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('is_admin', models.BooleanField(default=False)), + ('is_sms_sent', models.BooleanField(default=False)), + ('meeting', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invitations', to='core.meeting')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invitations', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/core/models/Invitation.py b/core/models/Invitation.py new file mode 100644 index 0000000..290e09c --- /dev/null +++ b/core/models/Invitation.py @@ -0,0 +1,13 @@ +# core/models.py + +from django.db import models +from django.contrib.auth.models import User + +class Invitation(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='invitations') + meeting = models.ForeignKey('core.Meeting', on_delete=models.CASCADE, related_name='invitations') + is_admin = models.BooleanField(default=False) + is_sms_sent = models.BooleanField(default=False) + + def __str__(self): + return f"Invitation of {self.user.username} to {self.meeting.name}" diff --git a/core/models/Meeting.py b/core/models/Meeting.py index d82fb1a..e060d87 100644 --- a/core/models/Meeting.py +++ b/core/models/Meeting.py @@ -15,6 +15,7 @@ class Meeting(models.Model): space = models.ForeignKey(Space, on_delete=models.SET_NULL, null=True, blank=True) asset_bundle = models.ForeignKey(AssetBundleRoom, on_delete=models.SET_NULL, null=True, blank=True) use_space = models.BooleanField(default=False) + code = models.CharField(max_length=255, null=True, blank=True ) def __str__(self): return self.name diff --git a/core/urls.py b/core/urls.py index 8d78be1..22bf4c2 100644 --- a/core/urls.py +++ b/core/urls.py @@ -92,6 +92,7 @@ urlpatterns = [ re_path('add_team', teamView.addTeam), re_path('get_team', teamView.getTeams), + re_path('get_all_team_members', teamView.get_all_team_members), path('add_meeting', meetingView. addMeeting, name='add_meeting'), diff --git a/core/views/meetingView.py b/core/views/meetingView.py index 6dfca82..c01c3be 100644 --- a/core/views/meetingView.py +++ b/core/views/meetingView.py @@ -8,11 +8,54 @@ from drf_yasg import openapi from core.serializers.MeetingSerializer import MeetingSerializer +# @swagger_auto_schema( +# method='post', +# request_body=openapi.Schema( +# type=openapi.TYPE_OBJECT, +# required=['name', 'description', 'date_time'], +# properties={ +# 'name': openapi.Schema(type=openapi.TYPE_STRING, default='Sprint Planning'), +# 'description': openapi.Schema(type=openapi.TYPE_STRING, default='Discuss the next sprint'), +# 'date_time': openapi.Schema(type=openapi.TYPE_STRING, format='date-time', default='2025-06-01T14:00:00Z'), +# 'space': openapi.Schema(type=openapi.TYPE_INTEGER, default=1), +# 'asset_bundle': openapi.Schema(type=openapi.TYPE_INTEGER, default=1), +# 'use_space': openapi.Schema(type=openapi.TYPE_BOOLEAN, default=False), +# } +# ) +# ) +# @api_view(['POST']) +# @authentication_classes([SessionAuthentication, TokenAuthentication]) +# @permission_classes([IsAuthenticated]) +# def addMeeting(request): +# data = request.data.copy() +# data['creator_user'] = request.user.id + +# serializer = MeetingSerializer(data=data) +# if serializer.is_valid(): +# meeting = serializer.save() +# return Response({ +# "message": "Meeting created successfully.", +# "meeting": serializer.data +# }, status=status.HTTP_201_CREATED) + +# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + + + + + + + +from core.models.Invitation import Invitation # Ensure Invitation is imported +from django.contrib.auth.models import User +from django.db import transaction + @swagger_auto_schema( method='post', request_body=openapi.Schema( type=openapi.TYPE_OBJECT, - required=['name', 'description', 'date_time'], + required=['name', 'description', 'date_time', 'user_ids'], properties={ 'name': openapi.Schema(type=openapi.TYPE_STRING, default='Sprint Planning'), 'description': openapi.Schema(type=openapi.TYPE_STRING, default='Discuss the next sprint'), @@ -20,6 +63,11 @@ from core.serializers.MeetingSerializer import MeetingSerializer 'space': openapi.Schema(type=openapi.TYPE_INTEGER, default=1), 'asset_bundle': openapi.Schema(type=openapi.TYPE_INTEGER, default=1), 'use_space': openapi.Schema(type=openapi.TYPE_BOOLEAN, default=False), + 'user_ids': openapi.Schema( + type=openapi.TYPE_ARRAY, + items=openapi.Items(type=openapi.TYPE_INTEGER), + description='List of user IDs to invite' + ), } ) ) @@ -28,14 +76,39 @@ from core.serializers.MeetingSerializer import MeetingSerializer @permission_classes([IsAuthenticated]) def addMeeting(request): data = request.data.copy() + user_ids = data.pop('user_ids', []) data['creator_user'] = request.user.id serializer = MeetingSerializer(data=data) - if serializer.is_valid(): - meeting = serializer.save() - return Response({ - "message": "Meeting created successfully.", - "meeting": serializer.data - }, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + if serializer.is_valid(): + try: + with transaction.atomic(): + meeting = serializer.save() + + for user_id in user_ids: + user = User.objects.get(id=user_id) # will raise exception if not found + Invitation.objects.create( + user=user, + meeting=meeting, + is_admin=False, + is_sms_sent=False + ) + + return Response({ + "message": "Meeting created successfully with invitations.", + "meeting": serializer.data + }, status=status.HTTP_201_CREATED) + + except User.DoesNotExist: + return Response( + {"error": f"User with id {user_id} does not exist."}, + status=status.HTTP_400_BAD_REQUEST + ) + except Exception as e: + return Response( + {"error": str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file diff --git a/core/views/teamView.py b/core/views/teamView.py index 9cf3006..f336aa8 100644 --- a/core/views/teamView.py +++ b/core/views/teamView.py @@ -5,6 +5,7 @@ from rest_framework.response import Response from rest_framework import status from core.models.Team import Team +from core.models.TeamMember import TeamMember from core.serializers.TeamSerializer import TeamSerializer @@ -62,4 +63,51 @@ def getTeams(request): return Response({ "teams": serializer.data - }, status=status.HTTP_200_OK) \ No newline at end of file + }, status=status.HTTP_200_OK) + + + + + + + + + +from django.contrib.auth.models import User +from django.db.models import Q + +from rest_framework.serializers import ModelSerializer + + +class SimpleUserSerializer(ModelSerializer): + class Meta: + model = User + fields = ['id', 'username', 'first_name', 'last_name' ] + +@api_view(['GET']) +@authentication_classes([SessionAuthentication, TokenAuthentication]) +@permission_classes([IsAuthenticated]) +def get_all_team_members(request): + try: + # Get all team IDs for current user + # team_ids = TeamMember.objects.filter(user=request.user).values_list('team_id', flat=True) + # team_ids = Team.objects.filter(Q(members__user=request.user) | Q(admin=request.user)).values_list('id', flat=True).distinct() + + # team_ids = Team.objects.filter(admin_id=7) + team_ids = Team.objects.filter(Q(admin_id=request.user.id) | Q(members__user=request.user)).values_list('id', flat=True).distinct() + + + # Get all members of those teams + team_members = TeamMember.objects.filter(team_id__in=team_ids) + + for tm in team_members: + print(f" - {tm.user.username} (Team: {tm.team.name})") + + # Extract user objects + users = [tm.user for tm in team_members] + + serializer = SimpleUserSerializer(users, many=True) + return Response({'members': serializer.data}, status=status.HTTP_200_OK) + + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) \ No newline at end of file diff --git a/test.rest b/test.rest index e75886c..25b09a1 100644 --- a/test.rest +++ b/test.rest @@ -149,3 +149,13 @@ GET http://127.0.0.1:8000/get_team Content-Type: application/json Authorization: token e2792ac06153e10f00949a1924483a07629cc753 + + +### + +GET http://127.0.0.1:8000/get_all_team_members +Content-Type: application/json +Authorization: token e2792ac06153e10f00949a1924483a07629cc753 +# Authorization: token 53e2b003a92e22aca85c95088a438ece8d9a5dfb + +