Skip to content

Commit effcae3

Browse files
committed
Добавлена ручка для получения публичных данных пользователей
1 parent 055dbde commit effcae3

3 files changed

Lines changed: 145 additions & 59 deletions

File tree

users/serializers.py

Lines changed: 85 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ def to_representation(self, data):
4747
if isinstance(data, list):
4848
return data
4949
return [
50-
i.replace("'", "") for i in data.strip("][").split(",") if i.replace("'", "")
50+
i.replace("'", "")
51+
for i in data.strip("][").split(",")
52+
if i.replace("'", "")
5153
]
5254

5355

@@ -107,6 +109,7 @@ class Meta:
107109

108110
class UserDataConfirmationSerializer(serializers.ModelSerializer):
109111
"""Information about the User to add to the skill confirmation information."""
112+
110113
v2_speciality = SpecializationSerializer()
111114

112115
class Meta:
@@ -148,12 +151,15 @@ def to_representation(self, instance):
148151
"""Returns correct data about user in `confirmed_by`."""
149152
data = super().to_representation(instance)
150153
data.pop("skill_to_object", None)
151-
data["confirmed_by"] = UserDataConfirmationSerializer(instance.confirmed_by).data
154+
data["confirmed_by"] = UserDataConfirmationSerializer(
155+
instance.confirmed_by
156+
).data
152157
return data
153158

154159

155160
class UserApproveSkillResponse(serializers.Serializer):
156161
"""For swagger response presentation."""
162+
157163
confirmed_by = UserDataConfirmationSerializer(read_only=True)
158164

159165

@@ -173,14 +179,14 @@ class Meta:
173179

174180
def get_approves(self, obj):
175181
"""Adds information about confirm to the skill."""
176-
confirmations = (
177-
UserSkillConfirmation.objects
178-
.filter(skill_to_object=obj)
179-
.select_related('confirmed_by')
180-
)
182+
confirmations = UserSkillConfirmation.objects.filter(
183+
skill_to_object=obj
184+
).select_related("confirmed_by")
181185
return [
182186
{
183-
"confirmed_by": UserDataConfirmationSerializer(confirmation.confirmed_by).data,
187+
"confirmed_by": UserDataConfirmationSerializer(
188+
confirmation.confirmed_by
189+
).data,
184190
}
185191
for confirmation in confirmations
186192
]
@@ -300,14 +306,15 @@ def validate(self, attrs):
300306
completion_year = attrs.get("completion_year")
301307
entry_year = attrs.get("entry_year")
302308
if (entry_year and completion_year) and (entry_year > completion_year):
303-
raise ValidationError({
304-
"entry_year": constants.USER_EXPERIENCE_YEAR_VALIDATION_MESSAGE,
305-
})
309+
raise ValidationError(
310+
{
311+
"entry_year": constants.USER_EXPERIENCE_YEAR_VALIDATION_MESSAGE,
312+
}
313+
)
306314
return attrs
307315

308316

309317
class UserEducationSerializer(UserExperienceMixin, serializers.ModelSerializer):
310-
311318
class Meta:
312319
model = UserEducation
313320
fields = [
@@ -321,7 +328,6 @@ class Meta:
321328

322329

323330
class UserWorkExperienceSerializer(UserExperienceMixin, serializers.ModelSerializer):
324-
325331
class Meta:
326332
model = UserWorkExperience
327333
fields = [
@@ -334,7 +340,6 @@ class Meta:
334340

335341

336342
class UserLanguagesSerializer(serializers.ModelSerializer):
337-
338343
class Meta:
339344
model = UserLanguages
340345
fields = [
@@ -391,11 +396,9 @@ def get_projects(self, user: CustomUser):
391396
).data
392397

393398
def get_programs(self, user: CustomUser):
394-
user_program_profiles = (
395-
user.partner_program_profiles
396-
.select_related('partner_program')
397-
.filter(partner_program__draft=False)
398-
)
399+
user_program_profiles = user.partner_program_profiles.select_related(
400+
"partner_program"
401+
).filter(partner_program__draft=False)
399402
return UserProgramsSerializer(
400403
[profile.partner_program for profile in user_program_profiles],
401404
context={"request": self.context.get("request"), "user": user},
@@ -523,7 +526,10 @@ def update(self, instance, validated_data):
523526
if attr in IMMUTABLE_FIELDS + USER_TYPE_FIELDS + RELATED_FIELDS:
524527
continue
525528
if attr == "user_type":
526-
if value == instance.user_type or value not in user_types_to_attr.keys():
529+
if (
530+
value == instance.user_type
531+
or value not in user_types_to_attr.keys()
532+
):
527533
continue
528534
# we can't change user type to Member
529535
if value == CustomUser.MEMBER:
@@ -556,13 +562,17 @@ def _update_user_education(self, instance: CustomUser, data: list[dict]) -> None
556562
serializer.save(user=instance)
557563

558564
@transaction.atomic
559-
def _update_user_work_experience(self, instance: CustomUser, data: list[dict]) -> None:
565+
def _update_user_work_experience(
566+
self, instance: CustomUser, data: list[dict]
567+
) -> None:
560568
"""
561569
Update user work experience.
562570
`PUT`/ `PATCH` methods require full data about education.
563571
"""
564572
instance.work_experience.all().delete()
565-
serializer = UserWorkExperienceSerializer(data=data, many=True, context=self.context)
573+
serializer = UserWorkExperienceSerializer(
574+
data=data, many=True, context=self.context
575+
)
566576
if serializer.is_valid(raise_exception=True):
567577
serializer.save(user=instance)
568578

@@ -575,7 +585,9 @@ def _update_user_languages(self, instance: CustomUser, data: list[dict]) -> None
575585
# Only unique languages in profile.
576586
languages = [lang_data["language"] for lang_data in data]
577587
if len(languages) != len(set(languages)):
578-
raise ValidationError({"language": constants.UNIQUE_LANGUAGES_VALIDATION_MESSAGE})
588+
raise ValidationError(
589+
{"language": constants.UNIQUE_LANGUAGES_VALIDATION_MESSAGE}
590+
)
579591
# Custom validation to limit the number of languages per user to `USER_MAX_LANGUAGES_COUNT`.
580592
if len(languages) > constants.USER_MAX_LANGUAGES_COUNT:
581593
raise ValidationError(constants.COUNT_LANGUAGES_VALIDATION_MESSAGE)
@@ -591,7 +603,9 @@ def _update_user_skills(self, instance: CustomUser, data: list[int]) -> None:
591603
Required count of skills between 1 and `USER_MAX_SKILL_QUANTITY`.
592604
"""
593605
if not (1 <= len(data) <= constants.USER_MAX_SKILL_QUANTITY):
594-
raise serializers.ValidationError(constants.USER_SKILL_QUANTITY_VALIDATIONS_MESSAGE)
606+
raise serializers.ValidationError(
607+
constants.USER_SKILL_QUANTITY_VALIDATIONS_MESSAGE
608+
)
595609

596610
user_content_type = ContentType.objects.get_for_model(CustomUser)
597611

@@ -624,7 +638,9 @@ def _update_user_skills(self, instance: CustomUser, data: list[int]) -> None:
624638

625639
def _user_skills_quantity_limit_validation(self, instance: CustomUser) -> None:
626640
if instance.skills_count > constants.USER_MAX_SKILL_QUANTITY:
627-
raise serializers.ValidationError(constants.USER_SKILL_QUANTITY_VALIDATIONS_MESSAGE)
641+
raise serializers.ValidationError(
642+
constants.USER_SKILL_QUANTITY_VALIDATIONS_MESSAGE
643+
)
628644

629645
def to_representation(self, instance) -> dict[str, Any]:
630646
"""
@@ -734,6 +750,50 @@ class Meta:
734750
}
735751

736752

753+
class PublicUserSerializer(serializers.ModelSerializer):
754+
firstName = serializers.CharField(source="first_name")
755+
lastName = serializers.CharField(source="last_name")
756+
skills = serializers.SerializerMethodField()
757+
is_online = serializers.SerializerMethodField()
758+
759+
def get_skills(self, user: CustomUser) -> list:
760+
"""Возвращает список навыков без поля approves"""
761+
skills = []
762+
for sto in getattr(user, "prefetched_skills", []):
763+
skill = sto.skill
764+
skills.append(
765+
{
766+
"id": skill.id,
767+
"name": skill.name,
768+
"category": {"id": skill.category.id, "name": skill.category.name},
769+
}
770+
)
771+
return skills
772+
773+
def get_is_online(self, user: CustomUser) -> bool:
774+
"""Логика проверки онлайн-статуса"""
775+
request = self.context.get("request")
776+
if request and request.user.is_authenticated and request.user.id == user.id:
777+
return True
778+
779+
cache_key = get_user_online_cache_key(user)
780+
return cache.get(cache_key, False)
781+
782+
class Meta:
783+
model = CustomUser
784+
fields = [
785+
"id",
786+
"firstName",
787+
"lastName",
788+
"avatar",
789+
"user_type",
790+
"skills",
791+
"is_online",
792+
"birthday",
793+
"speciality",
794+
]
795+
796+
737797
class UserFeedSerializer(serializers.ModelSerializer, SkillsSerializerMixin):
738798
class Meta:
739799
model = CustomUser

users/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
AchievementDetail,
66
AchievementList,
77
CurrentUser,
8+
PublicUserListView,
89
SpecialistsList,
910
UserAdditionalRolesView,
1011
UserDetail,
@@ -38,6 +39,7 @@
3839
"specialists/", SpecialistsList.as_view()
3940
), # this url actually returns mentors, experts and investors
4041
path("users/", UserList.as_view()),
42+
path('public-users/', PublicUserListView.as_view(), name='public-users'),
4143
path("users/projects/", UserProjectsList.as_view()),
4244
path("users/liked/", LikedProjectList.as_view()),
4345
path("users/roles/", UserAdditionalRolesView.as_view()),

users/views.py

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,86 @@
1-
import jwt
2-
import requests
31
import urllib.parse
42

3+
import jwt
4+
import requests
55
from django.apps import apps
66
from django.conf import settings
7-
from django.core.cache import cache
8-
from django.utils import timezone
97
from django.contrib.auth import get_user_model
108
from django.contrib.contenttypes.models import ContentType
9+
from django.core.cache import cache
1110
from django.db import transaction
12-
from django.db.models import Q
11+
from django.db.models import Prefetch, Q
1312
from django.http import HttpResponse
14-
from django.shortcuts import redirect, get_object_or_404
13+
from django.shortcuts import get_object_or_404, redirect
1514
from django.template.loader import render_to_string
16-
from rest_framework import status, permissions, exceptions
15+
from django.utils import timezone
16+
from django_filters import rest_framework as filters
17+
from drf_yasg.utils import swagger_auto_schema
18+
from rest_framework import exceptions, permissions, status
1719
from rest_framework.generics import (
1820
GenericAPIView,
1921
ListAPIView,
2022
ListCreateAPIView,
21-
RetrieveUpdateDestroyAPIView,
2223
RetrieveAPIView,
24+
RetrieveUpdateDestroyAPIView,
2325
)
24-
from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser
26+
from rest_framework.permissions import AllowAny, IsAdminUser, IsAuthenticated
2527
from rest_framework.request import Request
2628
from rest_framework.response import Response
2729
from rest_framework.views import APIView
2830
from rest_framework_simplejwt.tokens import RefreshToken, TokenError
29-
30-
from django_filters import rest_framework as filters
31-
from drf_yasg.utils import swagger_auto_schema
3231
from weasyprint import HTML
3332

34-
from core.models import SpecializationCategory, Specialization, SkillToObject
33+
from core.models import SkillToObject, Specialization, SpecializationCategory
3534
from core.pagination import Pagination
3635
from core.permissions import IsOwnerOrReadOnly
3736
from events.models import Event
3837
from events.serializers import EventsListSerializer
3938
from partner_programs.models import PartnerProgram
4039
from partner_programs.serializers import (
41-
UserProgramsSerializer,
4240
PartnerProgramListSerializer,
41+
UserProgramsSerializer,
4342
)
4443
from projects.pagination import ProjectsPagination
4544
from projects.serializers import ProjectListSerializer
46-
from users.helpers import (
47-
verify_email,
48-
check_related_fields_update,
49-
force_verify_user,
50-
)
5145
from users.constants import (
5246
VERBOSE_ROLE_TYPES,
5347
VERBOSE_USER_TYPES,
5448
VERIFY_EMAIL_REDIRECT_URL,
5549
OnboardingStage,
5650
)
57-
from users.models import UserAchievement, LikesOnProject, UserSkillConfirmation
51+
from users.helpers import (
52+
check_related_fields_update,
53+
force_verify_user,
54+
verify_email,
55+
)
56+
from users.models import LikesOnProject, UserAchievement, UserSkillConfirmation
5857
from users.permissions import IsAchievementOwnerOrReadOnly
5958
from users.serializers import (
6059
AchievementDetailSerializer,
6160
AchievementListSerializer,
61+
PublicUserSerializer,
62+
RemoteBuySubSerializer,
63+
ResendVerifyEmailSerializer,
64+
SpecializationSerializer,
65+
SpecializationsSerializer,
66+
UserApproveSkillResponse,
67+
UserCloneDataSerializer,
6268
UserDetailSerializer,
6369
UserListSerializer,
64-
VerifyEmailSerializer,
65-
ResendVerifyEmailSerializer,
6670
UserProjectListSerializer,
67-
UserSubscribedProjectsSerializer,
6871
UserSkillConfirmationSerializer,
69-
UserApproveSkillResponse,
70-
SpecializationsSerializer,
71-
SpecializationSerializer,
72-
UserCloneDataSerializer,
72+
UserSubscribedProjectsSerializer,
7373
UserSubscriptionDataSerializer,
74-
RemoteBuySubSerializer,
74+
VerifyEmailSerializer,
7575
)
7676
from users.typing import UserCVDataV2
77+
78+
from .filters import SpecializationFilter, UserFilter
7779
from .helpers import check_chache_for_cv
78-
from .filters import UserFilter, SpecializationFilter
7980
from .pagination import UsersPagination
80-
from .services.verification import VerificationTasks
81+
from .schema import SKILL_PK_PARAM, USER_PK_PARAM
8182
from .services.cv_data_prepare import UserCVDataPreparerV2
82-
from .schema import USER_PK_PARAM, SKILL_PK_PARAM
83+
from .services.verification import VerificationTasks
8384
from .tasks import send_mail_cv
8485

8586
User = get_user_model()
@@ -97,9 +98,7 @@ def get_permissions(self):
9798
if self.request.method == "POST":
9899
permission_classes = [AllowAny]
99100
else:
100-
permission_classes = [
101-
IsAdminUser
102-
]
101+
permission_classes = [IsAdminUser]
103102
return [permission() for permission in permission_classes]
104103

105104
def post(self, request, *args, **kwargs):
@@ -671,3 +670,28 @@ def get(self, request, *args, **kwargs):
671670
cache.set(cache_key, timezone.now(), timeout=cooldown_time)
672671

673672
return Response(data={"detail": "success"}, status=status.HTTP_200_OK)
673+
674+
675+
class PublicUserListView(ListAPIView):
676+
queryset = User.objects.get_active()
677+
serializer_class = PublicUserSerializer
678+
pagination_class = UsersPagination
679+
filter_backends = (filters.DjangoFilterBackend,)
680+
filterset_class = UserFilter
681+
permission_classes = [AllowAny]
682+
683+
def get_queryset(self):
684+
"""Оптимизация запросов для навыков и категорий"""
685+
return (
686+
super()
687+
.get_queryset()
688+
.prefetch_related(
689+
Prefetch(
690+
"skills",
691+
queryset=SkillToObject.objects.select_related(
692+
"skill", "skill__category"
693+
),
694+
to_attr="prefetched_skills",
695+
)
696+
)
697+
)

0 commit comments

Comments
 (0)