Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create api files for IPR #38

Merged
merged 7 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion api/v1/filters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from django.contrib.auth import get_user_model
from django_filters.rest_framework import BooleanFilter, FilterSet
from django_filters.rest_framework import BooleanFilter, CharFilter, FilterSet

from ipr.models import IPR

User = get_user_model()

Expand All @@ -15,3 +17,11 @@ def filter_no_ipr(self, queryset, name, value):
if value:
return queryset.filter(ipr=None)
return queryset


class IPRFilter(FilterSet):
executor = CharFilter()

class Meta:
model = IPR
fields = ["executor"]
19 changes: 19 additions & 0 deletions api/v1/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404
from rest_framework import permissions

User = get_user_model()


class TeamBossPermission(permissions.BasePermission):
def has_permission(self, request, view):
user = request.user
executor_id = view.kwargs.get("user_id")
executor = get_object_or_404(User, id=executor_id)
if (
user.is_authenticated
and user.team == executor.team
and user.is_boss()
):
return True
return False
65 changes: 65 additions & 0 deletions api/v1/serializers/api/ipr_serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from django.contrib.auth import get_user_model
from rest_framework import serializers

from api.v1.serializers.api.users_serializer import CustomUserSerializer
from api.v1.serializers.task import TaskSerializer
from core.statuses_for_ipr_tests import Status
from ipr.models import IPR

User = get_user_model()


class ExecutorSerializer(CustomUserSerializer):
class Meta:
fields = (
"first_name",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

надо добавить поле id

"last_name",
"patronymic",
"position",
"userpic",
)
model = User


class IPRSerializer(serializers.ModelSerializer):
tasks = TaskSerializer(many=True, read_only=True)
creator = serializers.PrimaryKeyRelatedField(read_only=True)
executor = ExecutorSerializer(read_only=True)
status = serializers.SerializerMethodField()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не понимаю, зачем тут переопределять status и creator?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creato - переопределил, что бы при создании ИПР он не запрашивался, а автоматически проставлялся в методе performe_create во view.

Status - что бы у статуса было актуальное значение. Но с учетом того что мы делаем это через сигналы, то я могу либо оставить пока как есть либо удалить и ждать пока будут готовы сигналы

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В смысле, у тебя же для создания ИПР другой сериализатор.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, а этот для отображения при GET запросе, при создании статус ставится автоматически в работе

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Все понял, затупил


class Meta:
fields = (
"id",
"title",
"creator",
"executor",
"creation_date",
"start_date",
"end_date",
"status",
"tasks",
)
model = IPR

def get_status(self, obj):
if obj.status == Status.IN_PROGRESS and obj.start_date > obj.end_date:
obj.status = Status.TRAIL
return obj.status
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это лучше сделать через сигналы, @greenpandorik взялся это делать.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Таким образом, без obj.save() наверно даже значение в БД не сохранится.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

мне тогда пока оставить как есть? как появятся сигналы поменяю

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ок

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, сегодня скорее всего добью это. Сорри приболел просто , выпал на пару дней



class IPRSerializerPost(serializers.ModelSerializer):
creator = serializers.PrimaryKeyRelatedField(read_only=True)
executor = serializers.PrimaryKeyRelatedField(read_only=True)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ты проверил создание ИПР ручками?
На первый взгляд это поля и def perform_create конфликтуют, но тут я могу ошибаться.


class Meta:
fields = (
"id",
"title",
"creator",
"executor",
"creation_date",
"start_date",
"end_date",
"status",
)
model = IPR
2 changes: 2 additions & 0 deletions api/v1/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
)
from rest_framework import routers

from api.v1.views.ipr_views import IPRViewSet
from api.v1.views.notifications_view import NotificationViewSet
from api.v1.views.ratings_view import IPRRatingCreateView, TaskRatingCreateView
from api.v1.views.task import TaskViewSet
Expand All @@ -17,6 +18,7 @@
v1_router.register(
"notifications", NotificationViewSet, basename="notifications"
)
v1_router.register("iprs", IPRViewSet, basename="ipr")

urlpatterns = [
path("", include(v1_router.urls)),
Expand Down
65 changes: 65 additions & 0 deletions api/v1/views/ipr_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from django.shortcuts import get_object_or_404
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import permissions
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet

from api.v1.filters import IPRFilter
from api.v1.permissions import TeamBossPermission
from api.v1.serializers.api.ipr_serializers import (
IPRSerializer,
IPRSerializerPost,
)
from core.statuses_for_ipr_tests import Status
from ipr.models import IPR
from users.models import User


class IPRViewSet(ModelViewSet):
serializer_class = IPRSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = IPRFilter

class Meta:
ordering = ["-creation_date"]

def get_queryset(self):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ты удалил creation_date в модели, но оставил тут сортировку.
Надо вернуть поле в модель и сортировку тоже.
Тут она просто дублирует это значение из модели, можно удалить.

    class Meta:
        ordering = ["-creation_date"]

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Так же во viewset можно ограничить количество доступных HTTP-методов.
Нам хватит `http_method_names = ["get", "post", "patch", "head", "options"]

return IPR.objects.select_related("executor", "creator")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

При запросе на api/v1/iprs/ мы выдаем все ИПР пользователя делающего запрос.
При запросе с параметром /api/v1/iprs/?user_id={id} мы выдаем ИПР указанного пользователя.
Проверь эту логику.


def get_serializer_class(self):
if self.request.method == "POST" or self.request.method == "PATCH":
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно короче:

if self.request.method in ("POST", "PATH"):

return IPRSerializerPost
return IPRSerializer

def perform_create(self, serializer):
executor_id = self.kwargs.get("user_id")
executor = get_object_or_404(User, id=executor_id)
serializer.save(creator=self.request.user, executor=executor)

def get_permissions(self):
if self.request.method in permissions.SAFE_METHODS:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

У нас весь проект на IsAuthenticated, можно сократить до:

        if self.request.method not in permissions.SAFE_METHODS:
            self.permission_classes = [TeamBossPermission]
        return super(IPRViewSet, self).get_permissions()

self.permission_classes = [permissions.IsAuthenticated]
else:
self.permission_classes = [TeamBossPermission]
return super(IPRViewSet, self).get_permissions()

@action(
detail=True,
methods=["get"],
permission_classes=[permissions.IsAuthenticated],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут тоже можно не указывать permission_classes=[permissions.IsAuthenticated],

)
def status(self, request, ipr_id):
"""Дополнительный эндпоинт и View-функция
для отображения прогресса выполнения ИПР"""
ipr = get_object_or_404(IPR, id=ipr_id)
tasks_without_trail = ipr.tasks.exclude(status=Status.TRAIL).count()
tasks_is_complete = ipr.tasks.filter(status=Status.COMPLETE).count()
progress = tasks_without_trail / 100 * tasks_is_complete

context = {
"request": request,
"progress": progress,
}
serializer = IPRSerializer(ipr, context=context)
return Response(serializer.data)
3 changes: 2 additions & 1 deletion config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
DB_HOST = env.str("DB_HOST", default="db")
DB_PORT = env.int("DB_PORT", default=5432)
CORS_ALLOWED_ORIGINS = env.list(
"CORS_ALLOWED_ORIGINS", default=["localhost:80", "127.0.0.1:80"]
"CORS_ALLOWED_ORIGINS",
default=["http://localhost:80", "http://127.0.0.1:80"],
)
CSRF_TRUSTED_ORIGINS = CORS_ORIGINS_WHITELIST = CORS_ALLOWED_ORIGINS
# -----------------------------------------------------------------------------
Expand Down
8 changes: 8 additions & 0 deletions core/statuses_for_ipr_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.db import models

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Название файла с ложным смыслом, это не единственное его использование.
Предлагаю просто оставить статусы


class Status(models.TextChoices):
COMPLETE = "complete", "Выполнен"
IN_PROGRESS = "in_progress", "В работе"
CANCEL = "cancel", "Отменен"
TRAIL = "trail", "Отстает"
42 changes: 7 additions & 35 deletions docs/fixture/ipr_ipr_dump.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,111 +4,83 @@
"pk": 1,
"fields": {
"title": "Senior Front-end разработчик",
"description": "Индивидуальный план развития до Senior Frontend-разработчика",
"creator": 2,
"creation_date": "2024-01-28",
"start_date": "2024-01-28",
"end_date": "2024-07-20",
"status": "STATUS_IN_PROGRESS",
"executor": 4,
"usability": 0,
"ease_of_creation": 0
"executor": 4
}
},
{
"model": "ipr.ipr",
"pk": 2,
"fields": {
"title": "Повышение уровня квалификации до Middle Designer",
"description": "Немаловажной частью проектирования ресурса в последнее время стало приведение ресурса в соответствие стандартам W3C, что обеспечивает доступность содержания для инвалидов и пользователей портативных устройств (смотри юзабилити — «удобство использования»), а также кроссплатформенность (в данном случае — так называемая кросс-браузерность) вёрстки ресурса. Также непосредственно с дизайном сайтов смежны маркетинг в Интернете (интернет-маркетинг).",
"creator": 8,
"creation_date": "2024-01-29",
"start_date": "2024-02-01",
"end_date": "2024-10-31",
"status": "STATUS_IN_PROGRESS",
"executor": 9,
"usability": 0,
"ease_of_creation": 0
"executor": 9
}
},
{
"model": "ipr.ipr",
"pk": 3,
"fields": {
"title": "Повышение навыков моделирования",
"description": "Моделирование — исследование объектов познания на их моделях; построение и изучение моделей реально существующих объектов, процессов или явлений с целью получения объяснений этих явлений, а также для предсказания явлений, интересующих исследователей.",
"creator": 5,
"creation_date": "2024-01-29",
"start_date": "2024-01-08",
"end_date": "2024-01-28",
"status": "STATUS_DELAYED",
"executor": 6,
"usability": 0,
"ease_of_creation": 0
"executor": 6
}
},
{
"model": "ipr.ipr",
"pk": 4,
"fields": {
"title": "Системный аналитик уровень Middle",
"description": "Системный анализ — прикладное направление теории систем, применяемое при решении сложных слабоформализуемых проблем.",
"creator": 5,
"creation_date": "2024-01-29",
"start_date": "2024-01-01",
"end_date": "2024-12-31",
"status": "STATUS_IN_PROGRESS",
"executor": 7,
"usability": 0,
"ease_of_creation": 0
"executor": 7
}
},
{
"model": "ipr.ipr",
"pk": 5,
"fields": {
"title": "Системный аналитик уровень Junior +",
"description": "Краткосрочный план развития системного аналитика.",
"creator": 5,
"creation_date": "2024-01-29",
"start_date": "2023-12-04",
"end_date": "2024-01-22",
"status": "STATUS_COMPLETED",
"executor": 7,
"usability": 0,
"ease_of_creation": 0
"executor": 7
}
},
{
"model": "ipr.ipr",
"pk": 6,
"fields": {
"title": "Переход в комманду Back-end на позицию Junior",
"description": "План перехода, на другую позицию в компании.",
"creator": 5,
"creation_date": "2024-01-29",
"start_date": "2023-11-06",
"end_date": "2023-11-15",
"status": "STATUS_CANCELLED",
"executor": 6,
"usability": 0,
"ease_of_creation": 0
"executor": 6
}
},
{
"model": "ipr.ipr",
"pk": 7,
"fields": {
"title": "Повышение квалификации по направлению \"Распределённые системы\"",
"description": "Архитектурный паттерн в распределённых системах",
"creator": 5,
"creation_date": "2024-01-29",
"start_date": "2023-12-20",
"end_date": "2024-01-10",
"status": "STATUS_NOT_COMPLETED",
"executor": 6,
"usability": 0,
"ease_of_creation": 0
"executor": 6
}
}
]
6 changes: 2 additions & 4 deletions ipr/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,22 @@
class IPRAdmin(admin.ModelAdmin):
list_display = (
"title",
"description",
"creator",
"creation_date",
"start_date",
"end_date",
"status",
"executor",
)
search_fields = ("title", "executor", "author")
list_filter = (
"creation_date",
"start_date",
"status",
"creator",
"executor",
"title",
"end_date",
)
ordering = ("-creation_date",)
ordering = ("-id",)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В админку тоже можно вернуть creation_date

def get_queryset(self, request):
qs = super().get_queryset(request)
Expand Down
Loading
Loading