From 9a18dda657ed12c1b3146d5245760244998240ad Mon Sep 17 00:00:00 2001 From: xoliqberdiyev Date: Fri, 3 Apr 2026 17:32:05 +0500 Subject: [PATCH] feat: add appraisers for auto-evaluation and add curd for this --- core/apps/accounts/serializers/user.py | 3 +- core/apps/accounts/urls.py | 4 +- core/apps/accounts/views/__init__.py | 1 + core/apps/accounts/views/user.py | 28 +++++++ .../0028_autoevaluationmodel_appraisers.py | 20 +++++ core/apps/evaluation/models/auto.py | 7 ++ .../serializers/auto/AutoEvaluation.py | 17 +++- core/apps/evaluation/urls.py | 15 +++- core/apps/evaluation/views/auto.py | 82 ++++++++++++++++++- core/apps/evaluation/views/document.py | 2 +- 10 files changed, 172 insertions(+), 7 deletions(-) create mode 100644 core/apps/accounts/views/user.py create mode 100644 core/apps/evaluation/migrations/0028_autoevaluationmodel_appraisers.py diff --git a/core/apps/accounts/serializers/user.py b/core/apps/accounts/serializers/user.py index a3dbb8a..d155825 100644 --- a/core/apps/accounts/serializers/user.py +++ b/core/apps/accounts/serializers/user.py @@ -9,7 +9,8 @@ class UserSerializer(serializers.ModelSerializer): "updated_at", "password", "groups", - "user_permissions" + "user_permissions", + "role" ] model = get_user_model() diff --git a/core/apps/accounts/urls.py b/core/apps/accounts/urls.py index bc57560..f7a1e2d 100644 --- a/core/apps/accounts/urls.py +++ b/core/apps/accounts/urls.py @@ -4,7 +4,7 @@ Accounts app urls from django.urls import path, include from rest_framework_simplejwt import views as jwt_views -from .views import RegisterView, ResetPasswordView, MeView, ChangePasswordView +from .views import RegisterView, ResetPasswordView, MeView, ChangePasswordView, UserListApiView, AdminUserListApiView from rest_framework.routers import DefaultRouter router = DefaultRouter() @@ -23,4 +23,6 @@ urlpatterns = [ jwt_views.TokenRefreshView.as_view(), name="token_refresh", ), + path("user/list/", UserListApiView.as_view(), name="user-list"), + path("admin-user/list/", AdminUserListApiView.as_view(), name="admin-user-list"), ] diff --git a/core/apps/accounts/views/__init__.py b/core/apps/accounts/views/__init__.py index a9efb91..5d2bd17 100644 --- a/core/apps/accounts/views/__init__.py +++ b/core/apps/accounts/views/__init__.py @@ -1 +1,2 @@ from .auth import * # noqa +from .user import * # noqa \ No newline at end of file diff --git a/core/apps/accounts/views/user.py b/core/apps/accounts/views/user.py new file mode 100644 index 0000000..bc6e70e --- /dev/null +++ b/core/apps/accounts/views/user.py @@ -0,0 +1,28 @@ +from django.contrib.auth import get_user_model + +from rest_framework import generics, filters +from rest_framework.permissions import IsAuthenticated + +from drf_spectacular.utils import extend_schema + +from core.apps.accounts.serializers.user import UserSerializer +from core.apps.accounts.choices.user import RoleChoice + +User = get_user_model() + +@extend_schema(tags=['User']) +class UserListApiView(generics.ListAPIView): + queryset = User.objects.filter(role=RoleChoice.USER) + serializer_class = UserSerializer + permission_classes = [IsAuthenticated] + filter_backends = [filters.SearchFilter] + search_fields = ['phone', 'first_name', 'last_name'] + + +@extend_schema(tags=['User']) +class AdminUserListApiView(generics.ListAPIView): + queryset = User.objects.exclude(role=RoleChoice.USER) + serializer_class = UserSerializer + permission_classes = [IsAuthenticated] + filter_backends = [filters.SearchFilter] + search_fields = ['phone', 'first_name', 'last_name'] diff --git a/core/apps/evaluation/migrations/0028_autoevaluationmodel_appraisers.py b/core/apps/evaluation/migrations/0028_autoevaluationmodel_appraisers.py new file mode 100644 index 0000000..c50bef4 --- /dev/null +++ b/core/apps/evaluation/migrations/0028_autoevaluationmodel_appraisers.py @@ -0,0 +1,20 @@ +# Generated by Django 5.2.7 on 2026-04-03 11:51 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('evaluation', '0027_documentcategorymodel_documentmodel'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='autoevaluationmodel', + name='appraisers', + field=models.ManyToManyField(blank=True, null=True, related_name='auto_evaluations', to=settings.AUTH_USER_MODEL, verbose_name='appraisers'), + ), + ] diff --git a/core/apps/evaluation/models/auto.py b/core/apps/evaluation/models/auto.py index d3c85b0..ccabdff 100644 --- a/core/apps/evaluation/models/auto.py +++ b/core/apps/evaluation/models/auto.py @@ -38,6 +38,13 @@ class AutoEvaluationModel(AbstractBaseModel): null=True, blank=True, ) + appraisers = models.ManyToManyField( + "accounts.User", + verbose_name=_("appraisers"), + related_name="auto_evaluations", + blank=True, + null=True, + ) # ── Step 1 — Umumiy ma'lumotlar ────────────────────────────────── registration_number = models.CharField( diff --git a/core/apps/evaluation/serializers/auto/AutoEvaluation.py b/core/apps/evaluation/serializers/auto/AutoEvaluation.py index f406575..9550a81 100644 --- a/core/apps/evaluation/serializers/auto/AutoEvaluation.py +++ b/core/apps/evaluation/serializers/auto/AutoEvaluation.py @@ -1,10 +1,12 @@ import re +from django.contrib.auth import get_user_model from rest_framework import serializers from core.apps.evaluation.models import AutoEvaluationModel,ReferenceitemModel from core.apps.evaluation.serializers.reference import ListReferenceitemSerializer +User = get_user_model() class BaseAutoevaluationSerializer(serializers.ModelSerializer): status_display = serializers.CharField(source="get_status_display", read_only=True) @@ -14,7 +16,7 @@ class BaseAutoevaluationSerializer(serializers.ModelSerializer): value_determined = ListReferenceitemSerializer(read_only=True) property_rights = ListReferenceitemSerializer(read_only=True) form_ownership = ListReferenceitemSerializer(read_only=True) - + class Meta: model = AutoEvaluationModel fields = [ @@ -212,3 +214,16 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer): ) return attrs + + +class AutoEvaluationAppraisersSerializer(serializers.Serializer): + ids = serializers.ListField(child=serializers.IntegerField()) + + def validate(self, data): + if not data.get("ids"): + raise serializers.ValidationError("Appraisers IDs are required.") + users = User.objects.filter(id__in=data["ids"]) + if not users: + raise serializers.ValidationError("Invalid appraisers IDs.") + data['users'] = users + return data diff --git a/core/apps/evaluation/urls.py b/core/apps/evaluation/urls.py index 64b5f11..014fc69 100644 --- a/core/apps/evaluation/urls.py +++ b/core/apps/evaluation/urls.py @@ -23,6 +23,10 @@ from .views import ( ValuationDocumentView, ValuationView, VehicleView, + AutoEvaluationListAppraisersView, + AutoEvaluationSetAppraisersView, + AutoEvaluationRemoveAppraisersView, + ) router = DefaultRouter() @@ -47,4 +51,13 @@ router.register("vehicle", VehicleView, basename="vehicle") router.register("valuation", ValuationView, basename="valuation") router.register("property-owner", PropertyOwnerView, basename="property-owner") router.register("customer", CustomerView, basename="customer") -urlpatterns = [path("", include(router.urls))] +urlpatterns = [ + path("", include(router.urls)), + path("auto-evaluation/appraisers/", include( + [ + path("/list/", AutoEvaluationListAppraisersView.as_view(), name="auto-evaluation-list-appraisers"), + path("/set/", AutoEvaluationSetAppraisersView.as_view(), name="auto-evaluation-set-appraisers"), + path("/remove/", AutoEvaluationRemoveAppraisersView.as_view(), name="auto-evaluation-remove-appraisers"), + ] + )), +] diff --git a/core/apps/evaluation/views/auto.py b/core/apps/evaluation/views/auto.py index 7ed8eba..e4b2598 100644 --- a/core/apps/evaluation/views/auto.py +++ b/core/apps/evaluation/views/auto.py @@ -1,16 +1,22 @@ +from django.db.models import Q from django_core.mixins import BaseViewSetMixin +from django.shortcuts import get_object_or_404 from django_filters.rest_framework import DjangoFilterBackend -from drf_spectacular.utils import extend_schema +from drf_spectacular.utils import extend_schema, OpenApiParameter from rest_framework.filters import OrderingFilter, SearchFilter -from rest_framework.permissions import AllowAny +from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.viewsets import ModelViewSet +from rest_framework.generics import GenericAPIView +from rest_framework.response import Response +from core.apps.accounts.serializers.user import UserSerializer from core.apps.evaluation.filters.auto import AutoevaluationFilter from core.apps.evaluation.models import AutoEvaluationModel from core.apps.evaluation.serializers.auto import ( CreateAutoevaluationSerializer, ListAutoevaluationSerializer, RetrieveAutoevaluationSerializer, + AutoEvaluationAppraisersSerializer ) @@ -67,3 +73,75 @@ class AutoEvaluationView(BaseViewSetMixin, ModelViewSet): "retrieve": RetrieveAutoevaluationSerializer, "create": CreateAutoevaluationSerializer, } + + +@extend_schema(tags=["AutoEvaluation"]) +class AutoEvaluationSetAppraisersView(GenericAPIView): + permission_classes = [IsAuthenticated] + queryset = AutoEvaluationModel.objects.all() + serializer_class = AutoEvaluationAppraisersSerializer + + def post(self, request, id): + try: + auto_evaluation = get_object_or_404(AutoEvaluationModel, id=id) + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + users = serializer.validated_data.get("users") + auto_evaluation.appraisers.set(users) + auto_evaluation.save() + return Response({"success": True, "data": "Foydalanuvchilar muvaffaqiyatli qo'shildi"}) + except Exception as e: + return Response({"error": str(e)}, status=500) + + +@extend_schema(tags=["AutoEvaluation"]) +class AutoEvaluationRemoveAppraisersView(GenericAPIView): + permission_classes = [IsAuthenticated] + queryset = AutoEvaluationModel.objects.all() + serializer_class = AutoEvaluationAppraisersSerializer + + def post(self, request, id): + try: + auto_evaluation = get_object_or_404(AutoEvaluationModel, id=id) + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + users = serializer.validated_data.get("users") + auto_evaluation.appraisers.remove(*users) + auto_evaluation.save() + return Response({"success": True, "data": "Foydalanuvchilar muvaffaqiyatli o'chirildi"}) + except Exception as e: + return Response({"error": str(e)}, status=500) + + +@extend_schema(tags=["AutoEvaluation"]) +class AutoEvaluationListAppraisersView(GenericAPIView): + permission_classes = [IsAuthenticated] + queryset = AutoEvaluationModel.objects.all() + serializer_class = UserSerializer + + @extend_schema( + parameters=[ + OpenApiParameter( + name="search", + type=str, + description="Search query", + required=False, + ) + ] + ) + def get(self, request, id): + try: + search_query = request.query_params.get("search", "") + auto_evaluation = get_object_or_404(AutoEvaluationModel, id=id) + query = auto_evaluation.appraisers.all() + if search_query: + query = query.filter( + Q(phone__icontains=search_query) | + Q(first_name__icontains=search_query) | + Q(last_name__icontains=search_query) + ) + page = self.paginate_queryset(query) + serializer = self.serializer_class(page, many=True) + return self.get_paginated_response(serializer.data) + except Exception as e: + return Response({"error": str(e)}, status=500) diff --git a/core/apps/evaluation/views/document.py b/core/apps/evaluation/views/document.py index 062836a..620eb75 100644 --- a/core/apps/evaluation/views/document.py +++ b/core/apps/evaluation/views/document.py @@ -49,7 +49,7 @@ class DocumentView(BaseViewSetMixin, ModelViewSet): } @extend_schema( - summary="Auto evaluation documents.", + summary="Auto evaluation documents.", description="get auto evaluation documents.", parameters=[ OpenApiParameter( -- 2.49.1