From d539bd94abf4dc8b65690ac10ad4c8930da76a18 Mon Sep 17 00:00:00 2001 From: Nadya2502 Date: Tue, 19 Mar 2024 16:32:59 +0100 Subject: [PATCH] =?UTF-8?q?[~]=20=D0=9E=D1=82=D1=80=D0=B5=D0=B4=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20=D0=B2?= =?UTF-8?q?=D0=B8=D0=B4=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D1=8C=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D0=BA=D1=82=D0=BE=D0=B2=20=D0=B8=20=D0=B2?= =?UTF-8?q?=D1=81=D0=B5=D0=B3=D0=BE=20=D0=BF=D1=80=D0=BE=D1=84=D0=B8=D0=BB?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/api/v1/profile/serializers.py | 46 ++++--- src/backend/api/v1/profile/views.py | 33 +++-- src/backend/apps/profile/constants.py | 6 +- .../apps/profile/migrations/0001_initial.py | 115 ++++++++++-------- src/backend/apps/profile/models.py | 66 ++++------ src/backend/apps/profile/validators.py | 15 +++ 6 files changed, 147 insertions(+), 134 deletions(-) diff --git a/src/backend/api/v1/profile/serializers.py b/src/backend/api/v1/profile/serializers.py index a79b208..22a8ef7 100644 --- a/src/backend/api/v1/profile/serializers.py +++ b/src/backend/api/v1/profile/serializers.py @@ -1,36 +1,46 @@ from rest_framework import generics, serializers from apps.profile.models import Profile, UserSkill +from apps.projects.models import Project class ProfileUpdateSerializer(serializers.ModelSerializer): + """Модель сериализатора профиля с учетом выбора видимости контактов""" + user = serializers.HiddenField(default=serializers.CurrentUserDefault()) class Meta: model = Profile - fields = "__all__" + fields = [ + "avatar", + "name", + "about", + "portfolio_link", + "birthday", + "country", + "city", + "specialization", + "skill", + "level", + "ready_to_participate", + ] def to_representation(self, instance): user = self.context["request"].user - visibile_status = instance.visible_choices - - representation = {} + visible_status_contacts = instance.visible_status_contacts if user.is_authenticated: - if visibile_status == "all": - representation["email"] = instance.email - representation["telegram"] = instance.telegram - representation["phone_number"] = instance.phone_number - elif visibile_status == "only_creator" and instance.user == user: - representation["email"] = instance.email - representation["telegram"] = instance.telegram - representation["phone_number"] = instance.phone_number - else: - representation["message"] = "Недоступно" - else: - representation["message"] = "Недоступно" - - return representation + if ( + visible_status_contacts == 2 + and Project.objects.filter(creator=user).exists() + ): + return super().to_representation(instance) + if visible_status_contacts == 3: + self.fields.pop("phone_number") + self.fields.pop("email") + self.fields.pop("telegram") + return super().to_representation(instance) + return {} class UserSkillSerializer(serializers.ModelSerializer): diff --git a/src/backend/api/v1/profile/views.py b/src/backend/api/v1/profile/views.py index ca4d4cb..945d8b1 100644 --- a/src/backend/api/v1/profile/views.py +++ b/src/backend/api/v1/profile/views.py @@ -1,9 +1,10 @@ -from rest_framework import generics, status -from rest_framework.response import Response +from django.db.models import Q +from rest_framework import generics from api.v1.profile.permissions import IsOwnerOrReadOnly from api.v1.profile.serializers import ProfileUpdateSerializer from apps.profile.models import Profile +from apps.projects.models import Project class ProfileView(generics.RetrieveAPIView): @@ -21,17 +22,23 @@ class ProfileListAPIView(generics.ListAPIView): def get_queryset(self): user = self.request.user - visibile_status = self.request.query_params.get( - "visibile_status", "all" + visible_status = self.request.query_params.get("visible_status", "All") + is_organizer = ( + user.is_authenticated + and Project.objects.filter(creator=user).exists() ) - if visibile_status == "all": - queryset = Profile.objects.all() - elif visibile_status == "creator": - queryset = Profile.objects.filter( - visibility="creator" - ) # Выводится в списке, если стоит видимость всем или организаторам проекта + + if visible_status == "Only creator" and user.is_authenticated: + # Проверяем, является ли пользователь организатором каких-либо проектов + if is_organizer: + queryset = Profile.objects.filter( + visible_status__in=["All", "Only creator"] + ) + else: + queryset = Profile.objects.filter( + visible_status="all" + ) # Если пользователь не организатор, возвращаем только то, что видно всем else: - queryset = ( - Profile.objects.none() - ) # Пустой queryset, если не выбраны организаторы + queryset = Profile.objects.filter(visible_status="All") + return queryset diff --git a/src/backend/apps/profile/constants.py b/src/backend/apps/profile/constants.py index 7db89d5..dad123f 100644 --- a/src/backend/apps/profile/constants.py +++ b/src/backend/apps/profile/constants.py @@ -1,8 +1,5 @@ BOOL_CHOICES = [(True, "Готов"), (False, "Не готов")] MAX_LENGTH_NAME = 30 -MAX_LENGTH_EMAIL = 256 -MAX_LENGTH_TELEGRAM = 32 -MAX_LENGTH_PHONE_NUMBER = 12 MAX_LENGTH_COUNTRY = 255 MAX_LENGTH_CITY = 255 MAX_LENGTH_ABOUT = 750 @@ -10,10 +7,9 @@ MIN_LENGTH_NAME = 2 MIN_LENGTH_ABOUT = 50 MIN_LENGTH_PORTFOLIO = 5 -MIN_LENGTH_TELEGRAM = 5 -MIN_LENGTH_EMAIL = 5 VISIBLE_CHOICES = [ (1, "All"), (2, "Only creator"), (3, "Nobody"), ] +LEVEL_CHOICES = [(1, "Junior"), (2, "Middle"), (3, "Senior"), (4, "Lead")] diff --git a/src/backend/apps/profile/migrations/0001_initial.py b/src/backend/apps/profile/migrations/0001_initial.py index 6e8f46e..3589b25 100644 --- a/src/backend/apps/profile/migrations/0001_initial.py +++ b/src/backend/apps/profile/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.0.1 on 2024-03-14 06:48 +# Generated by Django 5.0.1 on 2024-03-19 12:08 import apps.profile.validators import django.core.validators @@ -11,7 +11,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ("general", "0002_section_page_id"), + ("general", "0002_alter_specialist_specialization_and_more"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] @@ -60,7 +60,7 @@ class Migration(migrations.Migration): "specialization", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to="general.specialization", + to="general.specialist", ), ), ( @@ -84,6 +84,44 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), + ( + "phone_number", + models.TextField( + blank=True, + max_length=12, + validators=[ + django.core.validators.RegexValidator( + message="Телефон может содержать: цифры, спецсимволы, длина не должна превышать 12 символов", + regex="^\\+7\\(\\d{3}\\)\\d{3}-\\d{2}-\\d{2}$", + ) + ], + verbose_name="Номер телефона", + ), + ), + ( + "telegram_nick", + models.CharField( + blank=True, + max_length=32, + validators=[ + django.core.validators.MinLengthValidator( + limit_value=5, + message="Длина поля от 5 до 32 символов.", + ), + django.core.validators.RegexValidator( + message="Введите корректное имя пользователя. Оно может состоять из латинских букв, цифр и символа подчеркивания.", + regex="^[a-zA-Z0-9_]+$", + ), + ], + verbose_name="Ник в телеграм", + ), + ), + ( + "email", + models.EmailField( + blank=True, max_length=256, verbose_name="E-mail" + ), + ), ( "avatar", models.ImageField( @@ -140,50 +178,6 @@ class Migration(migrations.Migration): ], ), ), - ( - "phone_number", - models.TextField( - max_length=12, - validators=[ - django.core.validators.RegexValidator( - message="Телефон может содержать: цифры, спецсимволы, длина не должна превышать 12 символов", - regex="^\\+7\\(\\d{3}\\)\\d{3}-\\d{2}-\\d{2}$", - ) - ], - verbose_name="Номер телефона", - ), - ), - ( - "telegram", - models.CharField( - max_length=32, - validators=[ - django.core.validators.RegexValidator( - message="Некорректный формат введенных данных", - regex="^[a-zA-Z0-9_]+$", - ), - django.core.validators.MinLengthValidator( - limit_value=5 - ), - ], - verbose_name="Ник в телеграме", - ), - ), - ( - "email", - models.EmailField( - max_length=256, - validators=[ - django.core.validators.MinLengthValidator( - limit_value=5 - ), - django.core.validators.EmailValidator( - message="Введите корректный e-mail" - ), - ], - verbose_name="E-mail", - ), - ), ( "birthday", models.DateField( @@ -203,11 +197,10 @@ class Migration(migrations.Migration): "level", models.IntegerField( choices=[ - (1, "intern"), - (2, "junior"), - (3, "middle"), - (4, "senior"), - (5, "lead"), + (1, "Junior"), + (2, "Middle"), + (3, "Senior"), + (4, "Lead"), ], verbose_name="Уровень квалификации", ), @@ -216,6 +209,7 @@ class Migration(migrations.Migration): "ready_to_participate", models.BooleanField( choices=[(True, "Готов"), (False, "Не готов")], + default=False, verbose_name="Готовность к участию в проектах", ), ), @@ -227,10 +221,22 @@ class Migration(migrations.Migration): (2, "Only creator"), (3, "Nobody"), ], - default=1, + default="All", verbose_name="Видимость", ), ), + ( + "visible_status_contacts", + models.PositiveSmallIntegerField( + choices=[ + (1, "All"), + (2, "Only creator"), + (3, "Nobody"), + ], + default=3, + verbose_name="Видимость контактов", + ), + ), ( "user", models.OneToOneField( @@ -256,5 +262,8 @@ class Migration(migrations.Migration): ), ), ], + options={ + "abstract": False, + }, ), ] diff --git a/src/backend/apps/profile/models.py b/src/backend/apps/profile/models.py index 149aa44..8d94b76 100644 --- a/src/backend/apps/profile/models.py +++ b/src/backend/apps/profile/models.py @@ -1,31 +1,29 @@ from django.core.validators import ( - EmailValidator, MinLengthValidator, RegexValidator, URLValidator, ) from django.db import models -from apps.general.constants import LEVEL_CHOICES -from apps.general.models import Skill, Specialization +from apps.general.models import ContactsFields, Skill, Specialist from apps.profile.constants import ( BOOL_CHOICES, + LEVEL_CHOICES, MAX_LENGTH_ABOUT, MAX_LENGTH_CITY, MAX_LENGTH_COUNTRY, - MAX_LENGTH_EMAIL, MAX_LENGTH_NAME, - MAX_LENGTH_PHONE_NUMBER, - MAX_LENGTH_TELEGRAM, MAX_LENGTH_URL, MIN_LENGTH_ABOUT, - MIN_LENGTH_EMAIL, MIN_LENGTH_NAME, MIN_LENGTH_PORTFOLIO, - MIN_LENGTH_TELEGRAM, VISIBLE_CHOICES, ) -from apps.profile.validators import BirthdayValidator, validate_image +from apps.profile.validators import ( + BirthdayValidator, + validate_image, + visibility_correspondence, +) from apps.users.models import User @@ -40,12 +38,10 @@ class UserSpecialization(models.Model): """Модель специализации пользователя""" user = models.ForeignKey(User, on_delete=models.CASCADE) - specialization = models.ForeignKey( - Specialization, on_delete=models.CASCADE - ) + specialization = models.ForeignKey(Specialist, on_delete=models.CASCADE) -class Profile(models.Model): +class Profile(ContactsFields, models.Model): """Модель профиля пользователя""" avatar = models.ImageField( @@ -87,35 +83,7 @@ class Profile(models.Model): MinLengthValidator(limit_value=MIN_LENGTH_PORTFOLIO), ], ) - phone_number = models.TextField( - max_length=MAX_LENGTH_PHONE_NUMBER, - verbose_name="Номер телефона", - validators=[ - RegexValidator( - regex=r"^\+7\(\d{3}\)\d{3}-\d{2}-\d{2}$", - message="Телефон может содержать: цифры, спецсимволы, длина не должна превышать 12 символов", - ) - ], - ) - telegram = models.CharField( - max_length=MAX_LENGTH_TELEGRAM, - verbose_name="Ник в телеграме", - validators=[ - RegexValidator( - regex=r"^[a-zA-Z0-9_]+$", - message="Некорректный формат введенных данных", - ), - MinLengthValidator(limit_value=MIN_LENGTH_TELEGRAM), - ], - ) - email = models.EmailField( - verbose_name="E-mail", - max_length=MAX_LENGTH_EMAIL, - validators=[ - MinLengthValidator(limit_value=MIN_LENGTH_EMAIL), - EmailValidator(message="Введите корректный e-mail"), - ], - ) + birthday = models.DateField( verbose_name="Дата рождения", blank=False, @@ -138,11 +106,19 @@ class Profile(models.Model): choices=LEVEL_CHOICES, ) ready_to_participate = models.BooleanField( - verbose_name="Готовность к участию в проектах", choices=BOOL_CHOICES - ) + verbose_name="Готовность к участию в проектах", + choices=BOOL_CHOICES, + default=False, + ) # готовность к участию в проектах по умолчанию отключена user = models.OneToOneField( User, verbose_name="Пользователь", on_delete=models.CASCADE ) visibile_status = models.PositiveSmallIntegerField( - verbose_name="Видимость", choices=VISIBLE_CHOICES, default=1 + verbose_name="Видимость", choices=VISIBLE_CHOICES, default="All" + ) + visible_status_contacts = models.PositiveSmallIntegerField( + verbose_name="Видимость контактов", + choices=VISIBLE_CHOICES, + default=3, + validators=[visibility_correspondence], ) diff --git a/src/backend/apps/profile/validators.py b/src/backend/apps/profile/validators.py index e7b5a30..8f77c83 100644 --- a/src/backend/apps/profile/validators.py +++ b/src/backend/apps/profile/validators.py @@ -58,3 +58,18 @@ def validate_image(value): validate_image_format(value) validate_image_size(value) validate_image_resolution(value) + + # Проверяем соотвествие видимости общего профиля - видимости контактов + + +def visibility_correspondence(value, profile): + if profile.visibile_status == 2 or profile.visibile_status == 3: + if value != 2 and value != 3: + raise ValidationError( + "Видимость контактов не должна противоречить видимости профиля" + ) + elif profile.visibile_status == 3: + if value != 3: + raise ValidationError( + "Видимость контактов не должна противоречить видимости профиля" + )