From 9cfab8c95cdcec0493ac2e8129f027cb1c1c686c Mon Sep 17 00:00:00 2001 From: sm4640 Date: Wed, 25 Jun 2025 22:58:00 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20[#69]=20?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EB=93=A4=EC=97=90=20=EC=95=88=20=EC=9D=BD=EC=9D=80=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=EC=B9=B4=EC=9A=B4=ED=8A=B8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- users/views.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/users/views.py b/users/views.py index 381eaf0..7ea9eea 100644 --- a/users/views.py +++ b/users/views.py @@ -227,7 +227,6 @@ class MyPageProfileAPIView(APIView): serializer = UserProfileSerializer(target_user) data = serializer.data data['represent_portfolio_id'] = UserToPortfolioService.get_represent_portfolio(target_user) - data['new_notification_count'] = UserToNotificationService.get_new_notification_count(user) return Response(data, status=status.HTTP_200_OK) # 프로필 수정 @@ -245,7 +244,6 @@ class MyPageProfileAPIView(APIView): serializer.save() data = serializer.data data['represent_portfolio_id'] = UserToPortfolioService.get_represent_portfolio(target_user) - data['new_notification_count'] = UserToNotificationService.get_new_notification_count(user) return Response(data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) else: @@ -301,8 +299,6 @@ class MyPageWorkListAPIView(APIView): } else: return Response({"message": "not allowed retreive_type"}, status=status.HTTP_400_BAD_REQUEST) - - data['new_notification_count'] = UserToNotificationService.get_new_notification_count(user) return Response(data, status=status.HTTP_200_OK) class MyPageMemberInfoAPIView(APIView): @@ -312,7 +308,6 @@ class MyPageMemberInfoAPIView(APIView): user = request.user serializer = UserMemberInfoSerializer(user) data = serializer.data - data['new_notification_count'] = UserToNotificationService.get_new_notification_count(user) return Response(data, status=status.HTTP_200_OK) # 내 정보 수정 From 9377506d8b1c8d82ef01f673f83b99cf858531d1 Mon Sep 17 00:00:00 2001 From: sm4640 Date: Wed, 25 Jun 2025 22:58:44 +0900 Subject: [PATCH 2/7] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20[#69]=2030?= =?UTF-8?q?=EC=9D=BC=20=EC=9D=B4=EC=A0=84=20=EB=82=A0=EC=A7=9C=20=EC=84=B8?= =?UTF-8?q?=EB=8A=94=20=EB=B0=A9=EC=8B=9D=20=EB=8F=99=EC=A0=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- users/services.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/users/services.py b/users/services.py index af36696..ca6c318 100644 --- a/users/services.py +++ b/users/services.py @@ -5,8 +5,6 @@ from portfolios.models import * from django.utils import timezone from datetime import timedelta -# 30일 이전 일수 계산 -thirty_days_ago = timezone.now() - timedelta(days=30) DUPLICATE_CHECK = { 'email': 'email', @@ -37,10 +35,12 @@ class CheckUserFieldValueExistService: class UserToNotificationService: @staticmethod def get_new_notification_count(user: User): + thirty_days_ago = timezone.now() - timedelta(days=30) return user.notifications.filter(created_at__gt=thirty_days_ago, is_read=False).count() @staticmethod def get_all_notification(user: User): + thirty_days_ago = timezone.now() - timedelta(days=30) return user.notifications.filter(created_at__gt=thirty_days_ago) From 4b94cb70ed912107f2ddd90b1b9349c6036dcbfb Mon Sep 17 00:00:00 2001 From: sm4640 Date: Wed, 25 Jun 2025 23:01:22 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20[#69]=20Notific?= =?UTF-8?q?ationType=20=EC=B4=88=EB=8C=80=20=EB=B3=80=EC=88=98=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=ED=99=94(=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20?= =?UTF-8?q?=EC=B4=88=EB=8C=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/models/choiceModels.py | 2 +- projects/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/models/choiceModels.py b/common/models/choiceModels.py index 318884e..c7387c7 100644 --- a/common/models/choiceModels.py +++ b/common/models/choiceModels.py @@ -13,7 +13,7 @@ class InviteCodeUseType(models.TextChoices): HACKATHON = 'h', 'h' class NotificationType(models.TextChoices): - INVITE = '초대', '초대' + PROJECT_INVITE = '프로젝트 초대', '프로젝트 초대' class InvitationStatus(models.TextChoices): PENDING = '대기', '대기' diff --git a/projects/views.py b/projects/views.py index 6409d85..b04368c 100644 --- a/projects/views.py +++ b/projects/views.py @@ -50,7 +50,7 @@ class ProjectTeamManageAPIView(APIView): new_member = get_object_or_404(User, nickname=nickname) if ProjectTeamList.objects.filter(project=project, user=new_member).exists(): return Response({"message": "already team member"}, status=status.HTTP_400_BAD_REQUEST) - new_notification = NotifiationService.create_notification(user=new_member, note_type=NotificationType.INVITE) + new_notification = NotifiationService.create_notification(user=new_member, note_type=NotificationType.PROJECT_INVITE) ProjectInvitationService.create_project_invitation( project=project, from_user=user, From 7ebeb962faea99dab0a59fe98fcd76faf358bf3c Mon Sep 17 00:00:00 2001 From: sm4640 Date: Wed, 25 Jun 2025 23:02:39 +0900 Subject: [PATCH 4/7] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20[#69]=20Notific?= =?UTF-8?q?ationType=20=EC=B4=88=EB=8C=80=20=EB=B3=80=EC=88=98=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=ED=99=94,=20=ED=94=8C=EC=A0=9D=20=EB=B3=B8=EC=9D=B8?= =?UTF-8?q?=20=EC=B4=88=EB=8C=80x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- projects/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/serializers.py b/projects/serializers.py index d4a2fd4..7a354db 100644 --- a/projects/serializers.py +++ b/projects/serializers.py @@ -38,10 +38,10 @@ class ProjectCreateSerializer(serializers.ModelSerializer): return project users = User.objects.filter(nickname__in=nicknames) - users = list(users) + [validated_data["owner"]] + ProjectTeamList.objects.create(project=project, user=validated_data['owner']) for user in users: - new_notification = NotifiationService.create_notification(user=user, note_type=NotificationType.INVITE) + new_notification = NotifiationService.create_notification(user=user, note_type=NotificationType.PROJECT_INVITE) ProjectInvitationService.create_project_invitation( project=project, from_user=validated_data['owner'], From 6948685eb730e9fbc7c17df997234b6ca3a6fde9 Mon Sep 17 00:00:00 2001 From: sm4640 Date: Wed, 25 Jun 2025 23:03:11 +0900 Subject: [PATCH 5/7] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20[#69]=20?= =?UTF-8?q?=EC=95=88=20=EC=9D=BD=EC=9D=80=20=EC=95=8C=EB=A6=BC=20=EC=B9=B4?= =?UTF-8?q?=EC=9A=B4=ED=8A=B8=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notifications/views.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/notifications/views.py b/notifications/views.py index 6e046be..f74c143 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -30,4 +30,11 @@ class NotificationReadViewSet(ReadOnlyModelViewSet): instance.is_read = True instance.save() serializer = self.get_serializer(instance) - return Response(serializer.data, status=status.HTTP_200_OK) \ No newline at end of file + return Response(serializer.data, status=status.HTTP_200_OK) + +class NewNotificationCountAPIView(APIView): + + def get(self, request): + user = request.user + count = UserToNotificationService.get_new_notification_count(user) + return Response({"new_notification_count" : count}) \ No newline at end of file From a61442c884677f8397889dd3f75f7691efcc2fe6 Mon Sep 17 00:00:00 2001 From: sm4640 Date: Wed, 25 Jun 2025 23:04:12 +0900 Subject: [PATCH 6/7] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20[#69]=20?= =?UTF-8?q?=EC=95=88=20=EC=9D=BD=EC=9D=80=20=EC=95=8C=EB=A6=BC=20=EC=B9=B4?= =?UTF-8?q?=EC=9A=B4=ED=8A=B8=20api=20url=EC=84=A4=EC=A0=95,=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EC=A1=B0=ED=9A=8C=20=EB=B7=B0=EC=85=8B=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notifications/urls.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/notifications/urls.py b/notifications/urls.py index c2d898b..efe5c3a 100644 --- a/notifications/urls.py +++ b/notifications/urls.py @@ -7,8 +7,9 @@ from rest_framework.routers import DefaultRouter app_name = 'notifications' router = DefaultRouter() -router.register(r'', NotificationReadViewSet, basename='notification') +router.register(r'read', NotificationReadViewSet, basename='notification') urlpatterns = [ path('', include(router.urls)), + path('count/', NewNotificationCountAPIView().as_view()), ] \ No newline at end of file From f864a54017e89c987a63e3ccfea02108bb90be40 Mon Sep 17 00:00:00 2001 From: sm4640 Date: Wed, 25 Jun 2025 23:05:09 +0900 Subject: [PATCH 7/7] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20[#69]=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=EC=83=81=EC=84=B8=EB=B3=B4=EA=B8=B0=20?= =?UTF-8?q?=EC=A7=81=EB=A0=AC=ED=99=94=20OCP=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notifications/serializers.py | 47 ++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/notifications/serializers.py b/notifications/serializers.py index a5c8514..22f6514 100644 --- a/notifications/serializers.py +++ b/notifications/serializers.py @@ -2,6 +2,30 @@ from .models import * from projects.models import * from rest_framework import serializers +from common.models.choiceModels import NotificationType + + +NOTIFICATION_REL_MAP_REGISTRY = { + # "model" : "model_cls", + # "serializer" : "serializer_cls" +} + +def register_notification_serializer(note_type:NotificationType, model_cls): + def wrapper(serializer_cls): + NOTIFICATION_REL_MAP_REGISTRY[note_type] = {'model' : model_cls, 'serializer' : serializer_cls} + return serializer_cls + return wrapper + +@register_notification_serializer(NotificationType.PROJECT_INVITE, ProjectInvitation) +class ProjectInvitationMetaSerializer(serializers.ModelSerializer): + project_invitation_id = serializers.CharField(source='id') + project_title = serializers.CharField(source='project.title') + from_user_nickname = serializers.CharField(source='from_user.nickname') + + class Meta: + model = ProjectInvitation + fields = ['project_invitation_id', 'project_title', 'from_user_nickname', 'status'] + class NotificationSerializer(serializers.ModelSerializer): meta = serializers.SerializerMethodField() @@ -10,23 +34,10 @@ class NotificationSerializer(serializers.ModelSerializer): fields = ['id', 'content', 'note_type', 'is_read', 'created_at', 'meta'] def get_meta(self, obj): - - REL_SERIALIZER_MAP = { - 'project_invitation' : ProjectInvitationMetaSerializer, - } - - for rel_name, serializer_cls in REL_SERIALIZER_MAP.items(): - rel_obj = getattr(obj, rel_name, None) - if rel_obj is not None: - return serializer_cls(rel_obj).data + note_type = obj.note_type + rel_name = NOTIFICATION_REL_MAP_REGISTRY[note_type]['model']._meta.get_field('notification').remote_field.related_name + rel_obj = getattr(obj, rel_name, None) + if rel_obj is not None: + return NOTIFICATION_REL_MAP_REGISTRY[note_type]['serializer'](rel_obj).data return None - -class ProjectInvitationMetaSerializer(serializers.ModelSerializer): - project_invitation_id = serializers.CharField(source='id') - project_title = serializers.CharField(source='project.title') - from_user_nickname = serializers.CharField(source='from_user.nickname') - - class Meta: - model = ProjectInvitation - fields = ['project_invitation_id', 'project_title', 'from_user_nickname', 'status'] \ No newline at end of file