Skip to content

Commit

Permalink
Add bool field in update endpoint and correct test.
Browse files Browse the repository at this point in the history
  • Loading branch information
VladislavYar committed Jun 13, 2024
1 parent ada7fd8 commit dc2f7f3
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 18 deletions.
5 changes: 4 additions & 1 deletion backend/api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class MeSerializer(serializers.ModelSerializer):
last_completed_training = serializers.IntegerField(
source="last_completed_training.training_day.day_number", read_only=True
)
blocked_training = serializers.BooleanField(read_only=True)
date_last_skips = serializers.DateTimeField(required=False)
amount_of_skips = serializers.IntegerField(required=False)
avatar = Base64ImageField(allow_null=True, required=False)
Expand All @@ -70,6 +71,7 @@ class Meta:
"height_cm",
"weight_kg",
"last_completed_training",
"blocked_training",
"date_last_skips",
"amount_of_skips",
"avatar",
Expand Down Expand Up @@ -290,7 +292,7 @@ def _check_lock_training(self, value: datetime) -> None:
return
now = value.astimezone(pytz.timezone(user.timezone))
days_missed, *_ = counts_missed_days(user, user.timezone, now)
if user.amount_of_skips < days_missed:
if user.blocked_training or user.amount_of_skips < days_missed:
raise serializers.ValidationError("Невозможно сохранить тренировку при заблокированном челлендже.")

def validate_training_start(self, value: datetime) -> datetime:
Expand Down Expand Up @@ -342,6 +344,7 @@ class ResponseUpdateSerializer(serializers.Serializer):
"""Сериализатор возрващаемого значения UpdateView."""

enough = serializers.BooleanField()
skip = serializers.BooleanField()


class ResponseResendCodeSerializer(serializers.Serializer):
Expand Down
2 changes: 2 additions & 0 deletions backend/api/v1/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@


class CustomUniqueValidator(UniqueValidator):
"""Кастомный валидатор уникальности email."""

message = _("Такой email уже существует.")
50 changes: 34 additions & 16 deletions backend/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
User = get_user_model()


def send_auth_code(user) -> None:
def send_auth_code(user: ClassUser) -> None:
auth_code = authcode.AuthCode(user)
auth_code.set_sender(mailsender.DefaultMailSender())
auth_code.create_code()
Expand All @@ -53,7 +53,7 @@ class HealthCheckView(APIView):
description="Проверка работы API",
tags=("System",),
)
def get(self, request):
def get(self, request: Request) -> Response:
return Response({"Health": "OK"})


Expand All @@ -69,7 +69,7 @@ class RegisterUserView(APIView):
serializer_class = UserSerializer
permission_classes = (AllowAny,)

def post(self, request, format=None):
def post(self, request: Request) -> Response:
serializer = self.serializer_class(data=request.data, context={"request": request})
if serializer.is_valid():
serializer.save()
Expand All @@ -91,7 +91,7 @@ class ResendCodeView(APIView):
throttle_classes = (DurationCooldownRequestThrottle,)
serializer_class = UserSerializer

def post(self, request):
def post(self, request: Request) -> Response:
user = users.get_user_by_email_or_404(request.data.get("email"))
send_auth_code(user)
return Response({"result": "Код создан и отправлен"}, status=status.HTTP_201_CREATED)
Expand All @@ -110,7 +110,7 @@ class TokenRefreshView(APIView):
throttle_classes = (DurationCooldownRequestThrottle,)
permission_classes = (AllowAny,)

def post(self, request, format=None):
def post(self, request: Request) -> Response:
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
token_data: dict = serializer.save()
Expand Down Expand Up @@ -143,7 +143,7 @@ def post(self, request, format=None):
class MyInfoView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = MeSerializer

def get_object(self):
def get_object(self) -> ClassUser:
"""Отдаёт объект пользователя."""
return self.request.user

Expand All @@ -160,7 +160,7 @@ class TrainingView(generics.ListAPIView):
queryset = Day.objects.all()
serializer_class = TrainingSerializer

def get_queryset(self) -> QuerySet:
def get_queryset(self) -> QuerySet[History]:
"""
Формирует список тренировок с динамическими фразами
и флагом завершения тренировки.
Expand Down Expand Up @@ -189,7 +189,7 @@ def get_queryset(self) -> QuerySet:
class AchievementView(generics.ListAPIView):
serializer_class = AchievementSerializer

def get_queryset(self) -> QuerySet:
def get_queryset(self) -> QuerySet[Achievement]:
"""Формирует список ачивок c флагом получения и датой."""
user = self.request.user
sub_queryset = UserAchievement.objects.filter(user_id=user).values("achievement_id", "achievement_date")
Expand Down Expand Up @@ -226,7 +226,7 @@ def get_queryset(self) -> QuerySet:
class HistoryView(generics.ListCreateAPIView):
serializer_class = HistorySerializer

def get_queryset(self) -> QuerySet:
def get_queryset(self) -> QuerySet[History]:
"""Формирует список историй тренировок пользователя."""
return self.request.user.user_history.order_by("training_day")

Expand Down Expand Up @@ -278,8 +278,12 @@ def _updates_skip_data(
user.save()

def _set_null_amount_of_skip(self, user: ClassUser) -> None:
"""Устанавливает значение заморозок у пользователя равное нулю."""
"""
Устанавливает значение заморозок у пользователя равное нулю
и блокирует тренировки.
"""
user.amount_of_skips = 0
user.blocked_training = True
user.save()

def _update_user_timezone_data(self, user: ClassUser, user_timezone: str) -> None:
Expand All @@ -289,27 +293,40 @@ def _update_user_timezone_data(self, user: ClassUser, user_timezone: str) -> Non
user.save()

def patch(self, request: Request, *args, **kwargs) -> Response:
"""Обновляет timezone пользователя и просчитывает пропуски тренировок."""
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
user_timezone = request.data.get("timezone")
response = Response({"enough": True}, status=status.HTTP_200_OK)
response_data = {
"data": {
"enough": True,
"skip": False,
},
"status": status.HTTP_200_OK,
}
user: ClassUser = request.user
last_traning: History = user.last_completed_training

if user.blocked_training:
response_data["data"]["enough"] = False
self._update_user_timezone_data(user, user_timezone)
return Response(**response_data)
if not last_traning or last_traning.training_day.day_number == 100:
self._update_user_timezone_data(user, user_timezone)
return response
return Response(**response_data)
now = timezone.localtime(timezone=pytz.timezone(user_timezone))
days_missed, date_day_ago, amount_of_skips = counts_missed_days(user, user_timezone, now)
if days_missed <= 0:
self._update_user_timezone_data(user, user_timezone)
return response
return Response(**response_data)
user.timezone = user_timezone
if amount_of_skips >= days_missed:
self._updates_skip_data(user, amount_of_skips, days_missed, date_day_ago)
return response
response_data["data"]["skip"] = True
return Response(**response_data)
self._set_null_amount_of_skip(user)
return Response({"enough": False}, status=status.HTTP_200_OK)
response_data["data"]["enough"] = False
response_data["data"]["skip"] = True
return Response(**response_data)


@extend_schema_view(
Expand All @@ -327,6 +344,7 @@ def patch(self, request: Request, *args, **kwargs) -> Response:
user.date_last_skips = None
user.amount_of_skips = DEFAULT_AMOUNT_OF_SKIPS
user.total_m_run = 0
user.blocked_training = False
user.save()
user_history: QuerySet[History] = user.user_history.all()
user_history.delete()
Expand Down
2 changes: 2 additions & 0 deletions backend/users/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class CustomUserAdmin(admin.ModelAdmin):
{
"fields": (
"last_completed_training",
"blocked_training",
"date_last_skips",
"amount_of_skips",
"avatar",
Expand Down Expand Up @@ -53,6 +54,7 @@ class CustomUserAdmin(admin.ModelAdmin):
if not DEBUG:
readonly_fields = (
"last_completed_training",
"blocked_training",
"date_last_skips",
"timezone",
"email",
Expand Down
18 changes: 18 additions & 0 deletions backend/users/migrations/0011_user_blocked_training.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.4 on 2024-06-13 21:49

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('users', '0010_alter_user_total_m_run'),
]

operations = [
migrations.AddField(
model_name='user',
name='blocked_training',
field=models.BooleanField(default=False, verbose_name='Блокировка тренировок'),
),
]
1 change: 1 addition & 0 deletions backend/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class User(AbstractUser):
null=True,
blank=True,
)
blocked_training = models.BooleanField(_("Блокировка тренировок"), default=False)
date_last_skips = models.DateTimeField(_("Дата последнего пропуска"), null=True, blank=True)
amount_of_skips = models.PositiveSmallIntegerField(
_("Количество доступных пропусков/заморозок"), default=DEFAULT_AMOUNT_OF_SKIPS
Expand Down
10 changes: 10 additions & 0 deletions docs/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,9 @@ components:
last_completed_training:
type: integer
readOnly: true
blocked_training:
type: boolean
readOnly: true
date_last_skips:
type: string
format: date-time
Expand All @@ -493,6 +496,7 @@ components:
format: uri
nullable: true
required:
- blocked_training
- email
- last_completed_training
PatchedMe:
Expand Down Expand Up @@ -521,6 +525,9 @@ components:
last_completed_training:
type: integer
readOnly: true
blocked_training:
type: boolean
readOnly: true
date_last_skips:
type: string
format: date-time
Expand Down Expand Up @@ -559,8 +566,11 @@ components:
properties:
enough:
type: boolean
skip:
type: boolean
required:
- enough
- skip
ResponseUserDefault:
type: object
description: Сериализатор возвращаемого значения UserDefaultView.
Expand Down
3 changes: 2 additions & 1 deletion test/api_tests/achievment_tests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
import pytz
from django.contrib.auth import get_user_model
from django.urls import reverse
from django.utils import timezone
Expand All @@ -20,5 +21,5 @@ def test_all_achievments_returns_correct(user_client, achievements) -> None:
assert response.status_code == status.HTTP_200_OK
assert len(response.data) == 3
achievement = response.data[0]
assert achievement["achievement_date"] == date.strftime(FORMAT_DATE)
assert achievement["achievement_date"] == date.astimezone(pytz.timezone(user.timezone)).strftime(FORMAT_DATE)
assert achievement["received"] is not None

0 comments on commit dc2f7f3

Please sign in to comment.