From 02087044c2d72d6d1e274b011082f330650952de Mon Sep 17 00:00:00 2001 From: Alexey Kudelko Date: Wed, 20 Mar 2024 10:53:50 +0300 Subject: [PATCH 1/2] fixed chat error and optimized get chat list queries --- chats/views.py | 40 +++++++++------ project_rates/tests.py | 114 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 17 deletions(-) create mode 100644 project_rates/tests.py diff --git a/chats/views.py b/chats/views.py index a99b5190..11597b5c 100644 --- a/chats/views.py +++ b/chats/views.py @@ -1,4 +1,5 @@ from django.contrib.auth import get_user_model +from django.db.models import Q from rest_framework import status from rest_framework.generics import ( @@ -12,7 +13,7 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response -from chats.models import ProjectChat, DirectChat +from chats.models import ProjectChat, DirectChat, DirectChatMessage, ProjectChatMessage from chats.pagination import MessageListPagination from chats.permissions import IsProjectChatMember from chats.serializers import ( @@ -41,14 +42,15 @@ def get_queryset(self): def get(self, request, *args, **kwargs): chats = self.get_queryset() serialized_chats = [] - for chat in chats: + chats_users_ids = [[int(num) for num in chat.pk.split("_")] for chat in chats] + all_chats_users = User.objects.filter(pk__in=sum(chats_users_ids, [])) + for user1_id, user2_id in chats_users_ids: # fixme: move to function like get_user() and get_opponent() - chat_id = chat.id - user1_id, user2_id = map(int, chat_id.split("_")) + chat = chats.get(id=f"{user1_id}_{user2_id}") try: - user1 = User.objects.get(pk=user1_id) - user2 = User.objects.get(pk=user2_id) + user1 = all_chats_users.get(pk=user1_id) + user2 = all_chats_users.get(pk=user2_id) except User.DoesNotExist: # fixme: show deleted profile continue @@ -117,7 +119,9 @@ def get(self, request, *args, **kwargs) -> Response: except ValueError: return Response( status=status.HTTP_400_BAD_REQUEST, - data={"detail": "processed id must contain two integers separated by underscore"}, + data={ + "detail": "processed id must contain two integers separated by underscore" + }, ) except AssertionError as e: return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": str(e)}) @@ -218,16 +222,18 @@ class HasChatUnreadsView(GenericAPIView): ) def get(self, request, *args, **kwargs): user = request.user - # get all user chats - direct_messages = user.direct_chats.all().prefetch_related("messages") - project_chats = user.get_project_chats().prefetch_related("messages") - has_direct_messages_unread = ( - direct_messages.filter(messages__is_read=False).distinct().exists() + unread_dir_messages = ( + DirectChatMessage.objects.filter(chat__users=user, is_read=False) + .exclude(Q(author=user) | Q(is_deleted=True)) + .distinct() + .exists() ) - has_project_messages_unread = ( - project_chats.filter(messages__is_read=False).distinct().exists() - ) - return Response( - {"has_unreads": has_direct_messages_unread or has_project_messages_unread} + + unread_proj_messages = ( + ProjectChatMessage.objects.filter(is_read=False) + .exclude(is_deleted=True) + .distinct() + .exists() ) + return Response({"has_unreads": unread_proj_messages or unread_dir_messages}) diff --git a/project_rates/tests.py b/project_rates/tests.py new file mode 100644 index 00000000..0026fd15 --- /dev/null +++ b/project_rates/tests.py @@ -0,0 +1,114 @@ +from datetime import datetime +from unittest import TestCase +from rest_framework.test import APIRequestFactory, force_authenticate + +from industries.models import Industry +from partner_programs.models import PartnerProgram +from project_rates.models import Criteria, ProjectScore, User +from project_rates.views import RateProjects, RateProject, RateProjectsDetails +from projects.models import Project +from tests.constants import USER_CREATE_DATA +from users.models import Expert, CustomUser +from users.views import UserList + + +class ProjectRateTestCase(TestCase): + def setUp(self): + self.factory = APIRequestFactory() + self.project_rate_list_view = RateProjects.as_view() + self.project_rate_detail_view = RateProjectsDetails.as_view() + self.project_rate_create_view = RateProject.as_view() + self.user_list_view = UserList.as_view() + + self.program = PartnerProgram.objects.create( + name="Название программы", + tag="Тег программы", + description="Описание программы", + city="Город", + data_schema={ + "field1": "type1", + "field2": "type2" + }, + datetime_registration_ends=datetime.now(), + datetime_started=datetime.now(), + datetime_finished=datetime.now(), + datetime_created=datetime.now(), + datetime_updated=datetime.now(), + ) + + self.criteria_numeric = Criteria.objects.create( + name="Тестовое качество", + type="int", + min_value=0, + max_value=100, + partner_program=self.program + ) + + self.criteria_comment = Criteria.objects.create( + name="Комментарий", + type="str", + partner_program=self.program + ) + + self.user = None + self.project = None + + self.project_rate_create_data = [ + { + "criterion_id": self.criteria_numeric.id, + "value": 1 + }, + { + "criterion_id": self.criteria_comment.id, + "value": "Тестовые слова" + }, + ] + + def test_successful_project_rates_creation(self): + self.create_user_project() + request = self.factory.post(f"rate-project/rate/{self.project.id}", self.project_rate_create_data, format="json") + force_authenticate(request, user=self.user) + + response = self.project_rate_create_view(request, project_id=self.project.id) + self.assertEqual(response.status_code, 201) + self.assertEqual(response.data['success'], True) + + # def test_successful_project_rates_list_should_succeed(self): + # self.create_user_project() + # ProjectScore.objects.bulk_create([ + # ProjectScore(criteria=self.criteria_numeric, user=self.user, project=self.project, value=1), + # ProjectScore(criteria=self.criteria_comment, user=self.user, project=self.project, value="Test text") + # ]) + # request = self.factory.get(f"rate-project/{self.criteria_numeric.partner_program.id}") + # force_authenticate(request, user=self.user) + # + # response = self.project_rate_list_view(request, program_id=self.criteria_numeric.partner_program.id) + # raise ValueError(f"{response.data}, self.") + # self.assertEqual(response.status_code, 200) + + + def user_create(self): + USER_CREATE_DATA['user_type'] = 3 + request = self.factory.post("auth/users/", USER_CREATE_DATA) + response = self.user_list_view(request) + user_id = response.data["id"] + user = CustomUser.objects.get(id=user_id) + user.is_active = True + user.save() + return user + + def create_user_project(self): + try: + self.user = self.user_create() + except KeyError: + self.user = CustomUser.objects.get(email="only_for_test@test.test") + self.expert = Expert.objects.get(user=self.user) + self.expert.programs.add(self.program) + + self.project = Project.objects.create( + name="Test project", + description="Test desc", + industry=Industry.objects.create(name="Test"), + leader=self.user, + step=1 + ) \ No newline at end of file From 2b02afc225b65b6abc4b8c14ec668913f5a6145a Mon Sep 17 00:00:00 2001 From: Alexey Kudelko Date: Wed, 20 Mar 2024 10:55:58 +0300 Subject: [PATCH 2/2] lintered texts --- project_rates/tests.py | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/project_rates/tests.py b/project_rates/tests.py index 0026fd15..0d21d54d 100644 --- a/project_rates/tests.py +++ b/project_rates/tests.py @@ -4,7 +4,7 @@ from industries.models import Industry from partner_programs.models import PartnerProgram -from project_rates.models import Criteria, ProjectScore, User +from project_rates.models import Criteria from project_rates.views import RateProjects, RateProject, RateProjectsDetails from projects.models import Project from tests.constants import USER_CREATE_DATA @@ -25,10 +25,7 @@ def setUp(self): tag="Тег программы", description="Описание программы", city="Город", - data_schema={ - "field1": "type1", - "field2": "type2" - }, + data_schema={"field1": "type1", "field2": "type2"}, datetime_registration_ends=datetime.now(), datetime_started=datetime.now(), datetime_finished=datetime.now(), @@ -41,37 +38,33 @@ def setUp(self): type="int", min_value=0, max_value=100, - partner_program=self.program + partner_program=self.program, ) self.criteria_comment = Criteria.objects.create( - name="Комментарий", - type="str", - partner_program=self.program + name="Комментарий", type="str", partner_program=self.program ) self.user = None self.project = None self.project_rate_create_data = [ - { - "criterion_id": self.criteria_numeric.id, - "value": 1 - }, - { - "criterion_id": self.criteria_comment.id, - "value": "Тестовые слова" - }, + {"criterion_id": self.criteria_numeric.id, "value": 1}, + {"criterion_id": self.criteria_comment.id, "value": "Тестовые слова"}, ] def test_successful_project_rates_creation(self): self.create_user_project() - request = self.factory.post(f"rate-project/rate/{self.project.id}", self.project_rate_create_data, format="json") + request = self.factory.post( + f"rate-project/rate/{self.project.id}", + self.project_rate_create_data, + format="json", + ) force_authenticate(request, user=self.user) response = self.project_rate_create_view(request, project_id=self.project.id) self.assertEqual(response.status_code, 201) - self.assertEqual(response.data['success'], True) + self.assertEqual(response.data["success"], True) # def test_successful_project_rates_list_should_succeed(self): # self.create_user_project() @@ -86,9 +79,8 @@ def test_successful_project_rates_creation(self): # raise ValueError(f"{response.data}, self.") # self.assertEqual(response.status_code, 200) - def user_create(self): - USER_CREATE_DATA['user_type'] = 3 + USER_CREATE_DATA["user_type"] = 3 request = self.factory.post("auth/users/", USER_CREATE_DATA) response = self.user_list_view(request) user_id = response.data["id"] @@ -110,5 +102,5 @@ def create_user_project(self): description="Test desc", industry=Industry.objects.create(name="Test"), leader=self.user, - step=1 - ) \ No newline at end of file + step=1, + )