# Conflicts:
#	core/apps/accounts/serializers/user.py
#	core/apps/accounts/urls.py
#	core/apps/accounts/views/user.py
#	core/apps/evaluation/urls.py
#	core/apps/evaluation/views/request.py
This commit is contained in:
Shaxobff
2026-04-24 16:34:02 +05:00
29 changed files with 456 additions and 53 deletions

View File

@@ -153,7 +153,7 @@ jobs:
update_env \ update_env \
"DB_HOST=postgres" \ "DB_HOST=postgres" \
"DB_NAME=sifatbahodb" \ "DB_NAME=sifatbahodb" \
"DB_PORT=5432" "DB_PORT=5432" \
"DIDOX_TOKEN=${{ secrets.DIDOX_TOKEN }}"
export PORT=8085 export PORT=8085
docker stack deploy -c stack.yaml ${{ env.PROJECT_NAME }} --with-registry-auth docker stack deploy -c stack.yaml ${{ env.PROJECT_NAME }} --with-registry-auth

View File

@@ -13,7 +13,7 @@ from config.env import env
def home(request): def home(request):
return HttpResponse("OK: #46c96214578b78c1fe731f9a2ceb4429b81d4da9") return HttpResponse("OK: #88dedd85c79ccf732b2adac03616bd14e67a1579")
urlpatterns = [ urlpatterns = [

View File

@@ -30,6 +30,22 @@ class UserUpdateSerializer(serializers.ModelSerializer):
"last_name", "last_name",
"avatar" "avatar"
] ]
class AdminUserSerializer(serializers.ModelSerializer):
avatar = serializers.SerializerMethodField(method_name='get_avatar')
class Meta:
model = get_user_model()
fields = "__all__"
def get_avatar(self, obj):
request = self.context.get('request')
if obj.avatar:
return request.build_absolute_uri(obj.avatar.url)
return None
class UserCreateSerializer(serializers.ModelSerializer): class UserCreateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = get_user_model() model = get_user_model()
@@ -38,5 +54,4 @@ class UserCreateSerializer(serializers.ModelSerializer):
"first_name", "first_name",
"last_name", "last_name",
"password", "password",
"role" "role"]
]

View File

@@ -5,7 +5,7 @@ Accounts app urls
from django.urls import path, include from django.urls import path, include
from rest_framework_simplejwt import views as jwt_views from rest_framework_simplejwt import views as jwt_views
from .views import RegisterView, ResetPasswordView, MeView, ChangePasswordView, UserListApiView, AdminUserListApiView, \ from .views import RegisterView, ResetPasswordView, MeView, ChangePasswordView, UserListApiView, AdminUserListApiView, \
AdminUpdateAPIView, AdminCreateAPIView AdminUserView, AdminCreateAPIView, AdminUpdateAPIView
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
router = DefaultRouter() router = DefaultRouter()
@@ -13,6 +13,7 @@ router.register("auth", RegisterView, basename="auth")
router.register("auth", ResetPasswordView, basename="reset-password") router.register("auth", ResetPasswordView, basename="reset-password")
router.register("auth", MeView, basename="me") router.register("auth", MeView, basename="me")
router.register("auth", ChangePasswordView, basename="change-password") router.register("auth", ChangePasswordView, basename="change-password")
router.register("user", AdminUserView, basename="user-crud")
urlpatterns = [ urlpatterns = [

View File

@@ -1,13 +1,15 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django_core.mixins import BaseViewSetMixin
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
from rest_framework import generics, filters from rest_framework import generics, filters
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from core.apps.accounts.choices.user import RoleChoice from core.apps.accounts.choices.user import RoleChoice
from core.apps.accounts.serializers.user import UserSerializer, UserCreateSerializer from core.apps.accounts.serializers.user import UserSerializer, AdminUserSerializer, UserCreateSerializer
User = get_user_model() User = get_user_model()
@@ -33,6 +35,18 @@ class AdminUserListApiView(generics.ListAPIView):
search_fields = ['phone', 'first_name', 'last_name'] search_fields = ['phone', 'first_name', 'last_name']
@extend_schema(tags=["User"], request=AdminUserSerializer)
class AdminUserView(BaseViewSetMixin, ModelViewSet):
queryset = User.objects.filter(role=RoleChoice.USER)
serializer_class = AdminUserSerializer
permission_classes = [IsAuthenticated]
filter_backends = [filters.SearchFilter]
search_fields = ['phone', 'first_name', 'last_name']
def serializer_context(self):
return self.serializer_class(context={"request": self.request})
@extend_schema(tags=['User'], @extend_schema(tags=['User'],
responses={200: UserSerializer}, responses={200: UserSerializer},
request=UserCreateSerializer) request=UserCreateSerializer)
@@ -49,10 +63,10 @@ class AdminCreateAPIView(APIView):
return Response(serializer.data, status=201) return Response(serializer.data, status=201)
@extend_schema(tags=['User'], @extend_schema(tags=['User'],
responses={200: UserSerializer}, responses={200: UserSerializer},
request=UserCreateSerializer) request=UserCreateSerializer)
class AdminUpdateAPIView(APIView): class AdminUpdateAPIView(APIView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
@@ -66,5 +80,3 @@ class AdminUpdateAPIView(APIView):
serializer.save() serializer.save()
return Response(serializer.data, status=200) return Response(serializer.data, status=200)

View File

@@ -0,0 +1,28 @@
# Generated by Django 6.0.4 on 2026-04-23 11:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0031_remove_autoevaluationmodel_object_location_city_and_more'),
]
operations = [
migrations.CreateModel(
name='CertificateModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('title', models.CharField(max_length=255, verbose_name='title')),
('file_url', models.URLField(max_length=255, verbose_name='file url')),
],
options={
'verbose_name': 'Certificate',
'verbose_name_plural': 'Certificates',
'db_table': 'certificate',
},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 6.0.4 on 2026-04-23 07:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0031_remove_autoevaluationmodel_object_location_city_and_more'),
]
operations = [
migrations.AddField(
model_name='evaluationrequestmodel',
name='is_archive',
field=models.BooleanField(default=False, verbose_name='is archive'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 6.0.4 on 2026-04-23 07:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0031_remove_autoevaluationmodel_object_location_city_and_more'),
]
operations = [
migrations.AddField(
model_name='quickevaluationmodel',
name='is_archive',
field=models.BooleanField(default=False, verbose_name='is archive'),
),
]

View File

@@ -0,0 +1,15 @@
# Generated by Django 6.0.4 on 2026-04-23 11:22
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0032_certificatemodel'),
('evaluation', '0032_evaluationrequestmodel_is_archive'),
('evaluation', '0032_quickevaluationmodel_is_archive'),
]
operations = [
]

View File

@@ -0,0 +1,22 @@
# Generated by Django 6.0.4 on 2026-04-23 13:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0033_merge_20260423_1622'),
]
operations = [
migrations.RemoveField(
model_name='certificatemodel',
name='file_url',
),
migrations.AddField(
model_name='certificatemodel',
name='file',
field=models.FileField(blank=True, null=True, upload_to='certificates/', verbose_name='file'),
),
]

View File

@@ -11,3 +11,4 @@ from .report import * # noqa
from .request import * # noqa from .request import * # noqa
from .valuation import * # noqa from .valuation import * # noqa
from .vehicle import * # noqa from .vehicle import * # noqa
from .certificate import * # noqa

View File

@@ -0,0 +1,30 @@
from django.db import models
from django_core.models import AbstractBaseModel
from django.utils.translation import gettext_lazy as _
from model_bakery import baker
class CertificateModel(AbstractBaseModel):
title = models.CharField(
verbose_name=_("title"),
max_length=255
)
file = models.FileField(
verbose_name=_("file"),
upload_to="certificates/",
blank=True,
null=True
)
def __str__(self):
return self.title
@classmethod
def _baker(cls):
return baker.make(cls)
class Meta:
db_table = "certificate"
verbose_name = _("Certificate")
verbose_name_plural = _("Certificates")

View File

@@ -132,6 +132,11 @@ class QuickEvaluationModel(AbstractBaseModel):
default=QuickEvaluationStatus.CREATED, default=QuickEvaluationStatus.CREATED,
) )
is_archive = models.BooleanField(
verbose_name=_("is archive"),
default=False,
)
def __str__(self): def __str__(self):
return f"Quick Evaluation {self.pk} by {self.created_by}" return f"Quick Evaluation {self.pk} by {self.created_by}"

View File

@@ -118,6 +118,10 @@ class EvaluationrequestModel(AbstractBaseModel):
choices=RequestStatus.choices, choices=RequestStatus.choices,
default=RequestStatus.PENDING, default=RequestStatus.PENDING,
) )
is_archive = models.BooleanField(
verbose_name=_("is archive"),
default=False,
)
def __str__(self): def __str__(self):
return f"Requests #{self.pk}{self.get_rate_type_display()}" return f"Requests #{self.pk}{self.get_rate_type_display()}"

View File

@@ -12,3 +12,4 @@ from .request import * # noqa
from .valuation import * # noqa from .valuation import * # noqa
from .vehicle import * # noqa from .vehicle import * # noqa
from .tech_passport import * # noqa from .tech_passport import * # noqa
from .certificate import * # noqa

View File

@@ -0,0 +1 @@
from .certificate import * # noqa

View File

@@ -0,0 +1,13 @@
from rest_framework import serializers
from core.apps.evaluation.models import CertificateModel
class BaseCertificateSerializer(serializers.ModelSerializer):
class Meta:
model = CertificateModel
fields = [
"id",
"title",
"file",
]

View File

@@ -38,7 +38,8 @@ class BaseQuickevaluationSerializer(serializers.ModelSerializer):
"state_car_name", "state_car_name",
"created_at", "created_at",
"distance_covered", "distance_covered",
"tex_passport_serie_num" "tex_passport_serie_num",
"is_archive"
] ]
@@ -125,3 +126,8 @@ class CreateQuickevaluationSerializer(serializers.ModelSerializer):
if request and request.user and request.user.is_authenticated: if request and request.user and request.user.is_authenticated:
validated_data["created_by"] = request.user validated_data["created_by"] = request.user
return super().create(validated_data) return super().create(validated_data)
class ArchiveQuickevaluationSerializer(serializers.Serializer):
id = serializers.IntegerField(required=True)
is_archive = serializers.BooleanField(required=True)

View File

@@ -55,6 +55,7 @@ class BaseEvaluationrequestSerializer(serializers.ModelSerializer):
"user", "user",
"created_at", "created_at",
"updated_at", "updated_at",
"is_archive",
] ]
def get_location(self, obj): def get_location(self, obj):
@@ -183,3 +184,7 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
validated_data["location_name"] = str(location_name) validated_data["location_name"] = str(location_name)
validated_data["user"] = self.context["request"].user validated_data["user"] = self.context["request"].user
return super().create(validated_data) return super().create(validated_data)
class ArchiveEvaluationrequestSerializer(serializers.Serializer):
id = serializers.IntegerField(required=True)
is_archive = serializers.BooleanField(required=True)

View File

@@ -29,7 +29,9 @@ from .views import (
DidoxCompanyInfoAPIView, DidoxCompanyInfoAPIView,
TechPassportAPIView, TechPassportAPIView,
EvaluationStatusChange, EvaluationStatusChange,
GetArchivedEvaluationListAPIView CertificateView,
ArchiveQuickEvaluationView,
ArchiveEvaluationrequestView, GetArchivedEvaluationListAPIView, ArchivedEvaluation,
) )
router = DefaultRouter() router = DefaultRouter()
@@ -54,14 +56,14 @@ router.register("vehicle", VehicleView, basename="vehicle")
router.register("valuation", ValuationView, basename="valuation") router.register("valuation", ValuationView, basename="valuation")
router.register("property-owner", PropertyOwnerView, basename="property-owner") router.register("property-owner", PropertyOwnerView, basename="property-owner")
router.register("customer", CustomerView, basename="customer") router.register("customer", CustomerView, basename="customer")
router.register("certificate", CertificateView, basename="certificate")
urlpatterns = [ urlpatterns = [
path("", include(router.urls)), path("", include(router.urls)),
path("auto-evaluation/appraisers/", include( path("auto-evaluation/appraisers/", include(
[ [
path("<int:id>/list/", AutoEvaluationListAppraisersView.as_view(), name="auto-evaluation-list-appraisers"), path("<int:id>/list/", AutoEvaluationListAppraisersView.as_view(), name="auto-evaluation-list-appraisers"),
path("<int:id>/set/", AutoEvaluationSetAppraisersView.as_view(), name="auto-evaluation-set-appraisers"), path("<int:id>/set/", AutoEvaluationSetAppraisersView.as_view(), name="auto-evaluation-set-appraisers"),
path("<int:id>/remove/", AutoEvaluationRemoveAppraisersView.as_view(), path("<int:id>/remove/", AutoEvaluationRemoveAppraisersView.as_view(), name="auto-evaluation-remove-appraisers"),
name="auto-evaluation-remove-appraisers"),
] ]
)), )),
path( path(
@@ -76,10 +78,13 @@ urlpatterns = [
), ),
path("evaluation-request/<int:pk>/change-status/", EvaluationStatusChange.as_view(), path("evaluation-request/<int:pk>/change-status/", EvaluationStatusChange.as_view(),
name="evaluation-change-status"), name="evaluation-change-status"),
path("archive/quick-evaluation/", ArchiveQuickEvaluationView.as_view(), name="quick-evaluation-archive"),
path("archive/evaluation-request/", ArchiveEvaluationrequestView.as_view(), name="evaluation-request-archive"),
path("archived-evaluvation/", GetArchivedEvaluationListAPIView.as_view(), path("archived-evaluvation/", GetArchivedEvaluationListAPIView.as_view(),
name="archived-evaluation"), name="archived-evaluation"),
path("auto-evaluvation-change-status/<int:pk>", GetArchivedEvaluationListAPIView.as_view(), path("auto-evaluvation-change-status/<int:pk>", ArchivedEvaluation.as_view(),
name="archived-evaluation"), name="archived-evaluation"),
] ]

View File

@@ -13,3 +13,4 @@ from .valuation import * # noqa
from .vehicle import * # noqa from .vehicle import * # noqa
from .didox import * # noqa from .didox import * # noqa
from .tech_passport import * # noqa from .tech_passport import * # noqa
from .certificate import * # noqa

View File

@@ -160,7 +160,6 @@ class AutoEvaluationListAppraisersView(GenericAPIView):
@extend_schema(tags=["AutoEvaluation"]) @extend_schema(tags=["AutoEvaluation"])
class GetArchivedEvaluationListAPIView(ListAPIView): class GetArchivedEvaluationListAPIView(ListAPIView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def get_queryset(self): def get_queryset(self):
return AutoEvaluationModel.objects.filter(is_archived=True) return AutoEvaluationModel.objects.filter(is_archived=True)

View File

@@ -0,0 +1,23 @@
from django_core.mixins import BaseViewSetMixin
from drf_spectacular.utils import extend_schema
from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import ModelViewSet
from core.apps.evaluation.models import CertificateModel
from core.apps.evaluation.serializers.certificate import BaseCertificateSerializer
from rest_framework.filters import SearchFilter
from rest_framework.parsers import MultiPartParser, FormParser
@extend_schema(tags=["Certificate"],request=BaseCertificateSerializer)
class CertificateView(BaseViewSetMixin, ModelViewSet):
queryset = CertificateModel.objects.all()
serializer_class = BaseCertificateSerializer
permission_classes = [IsAuthenticated]
parser_classes = [MultiPartParser, FormParser]
filter_backends = [SearchFilter]
search_fields = ["title"]
pagination_class = None
action_permission_classes = {}

View File

@@ -1,6 +1,6 @@
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from rest_framework.permissions import AllowAny from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import GenericAPIView from rest_framework.generics import GenericAPIView
@@ -11,7 +11,7 @@ from core.services.didox import DidoxService
class DidoxCompanyInfoAPIView(GenericAPIView): class DidoxCompanyInfoAPIView(GenericAPIView):
authentication_classes = [] authentication_classes = []
permission_classes = [AllowAny] permission_classes = [IsAuthenticated]
@extend_schema( @extend_schema(
tags=["Didox"], tags=["Didox"],
@@ -31,7 +31,6 @@ class DidoxCompanyInfoAPIView(GenericAPIView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
tin = kwargs.get("tin") tin = kwargs.get("tin")
# 🔥 TYPE CHECK
try: try:
tin = int(tin) tin = int(tin)
except (TypeError, ValueError): except (TypeError, ValueError):
@@ -48,4 +47,14 @@ class DidoxCompanyInfoAPIView(GenericAPIView):
status=status.HTTP_502_BAD_GATEWAY status=status.HTTP_502_BAD_GATEWAY
) )
# if both name and personalNum are null/empty -> 404
name = data.get("name")
personal_num = data.get("personalNum")
if not name and not personal_num:
return Response(
{"detail": "Company or person not found"},
status=status.HTTP_404_NOT_FOUND
)
return Response(data, status=status.HTTP_200_OK) return Response(data, status=status.HTTP_200_OK)

View File

@@ -1,10 +1,14 @@
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema, OpenApiResponse
from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.parsers import FormParser, MultiPartParser from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework import status
from django.shortcuts import get_object_or_404
from core.apps.evaluation.filters.quick import QuickevaluationFilter from core.apps.evaluation.filters.quick import QuickevaluationFilter
from core.apps.evaluation.models import QuickEvaluationModel from core.apps.evaluation.models import QuickEvaluationModel
@@ -12,6 +16,7 @@ from core.apps.evaluation.serializers.quick import (
CreateQuickevaluationSerializer, CreateQuickevaluationSerializer,
ListQuickevaluationSerializer, ListQuickevaluationSerializer,
RetrieveQuickevaluationSerializer, RetrieveQuickevaluationSerializer,
ArchiveQuickevaluationSerializer,
) )
@@ -20,7 +25,7 @@ class QuickEvaluationView(BaseViewSetMixin, ModelViewSet):
queryset = QuickEvaluationModel.objects.select_related( queryset = QuickEvaluationModel.objects.select_related(
"created_by", "brand", "marka", "color", "fuel_type", "created_by", "brand", "marka", "color", "fuel_type",
"body_type", "state_car", "car_position", "body_type", "state_car", "car_position",
).all() ).filter(is_archive=False)
serializer_class = ListQuickevaluationSerializer serializer_class = ListQuickevaluationSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
parser_classes = [MultiPartParser, FormParser] parser_classes = [MultiPartParser, FormParser]
@@ -50,3 +55,76 @@ class QuickEvaluationView(BaseViewSetMixin, ModelViewSet):
"retrieve": RetrieveQuickevaluationSerializer, "retrieve": RetrieveQuickevaluationSerializer,
"create": CreateQuickevaluationSerializer, "create": CreateQuickevaluationSerializer,
} }
@extend_schema(tags=["QuickEvaluation"])
class ArchiveQuickEvaluationView(GenericAPIView):
permission_classes = [IsAuthenticated]
def get_serializer_class(self):
if self.request.method == "GET":
return ListQuickevaluationSerializer
return ArchiveQuickevaluationSerializer
@extend_schema(
tags=["QuickEvaluation"],
summary="Get archived quick evaluations list",
description="""
Returns only archived quick evaluations.
This endpoint works like quick-evaluation/,
but only records with is_archive=True are returned.
""",
responses={200: ListQuickevaluationSerializer(many=True)},
)
def get(self, request, *args, **kwargs):
queryset = QuickEvaluationModel.objects.filter(
is_archive=True
).order_by("-created_at")
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
@extend_schema(
tags=["QuickEvaluation"],
summary="Archive or unarchive quick evaluation",
description="""
Update archive status for quick evaluation.
- is_archive=true → archive
- is_archive=false → remove from archive
""",
request=ArchiveQuickevaluationSerializer,
responses={
200: OpenApiResponse(
description="Archive status updated successfully"
),
400: OpenApiResponse(
description="Validation error"
),
404: OpenApiResponse(
description="Quick evaluation not found"
),
},
)
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
validated_data = serializer.validated_data
obj = get_object_or_404(
QuickEvaluationModel,
id=validated_data["id"]
)
obj.is_archive = validated_data["is_archive"]
obj.save(update_fields=["is_archive"])
return Response(
{
"success": True,
"message": "Archive status updated successfully"
},
status=status.HTTP_200_OK
)

View File

@@ -1,23 +1,27 @@
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
from rest_framework import status
from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.permissions import IsAuthenticated from rest_framework.pagination import PageNumberPagination
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet from rest_framework import status
from core.apps.accounts.choices import RoleChoice
from core.apps.evaluation.choices.request import RequestStatus
from core.apps.evaluation.filters.request import EvaluationrequestFilter from core.apps.evaluation.filters.request import EvaluationrequestFilter
from core.apps.evaluation.models import EvaluationrequestModel from core.apps.evaluation.models import EvaluationrequestModel
from core.apps.evaluation.serializers.request import ( from core.apps.evaluation.serializers.request import (
CreateEvaluationrequestSerializer, CreateEvaluationrequestSerializer,
ListEvaluationrequestSerializer, ListEvaluationrequestSerializer,
RetrieveEvaluationrequestSerializer, RetrieveEvaluationrequestSerializer,
ArchiveEvaluationrequestSerializer,
) )
from core.apps.evaluation.choices.request import RequestStatus
from rest_framework.generics import GenericAPIView
from drf_spectacular.utils import OpenApiResponse
# class RequestPagination(PageNumberPagination): # class RequestPagination(PageNumberPagination):
@@ -171,3 +175,76 @@ class EvaluationStatusChange(APIView):
'status': evaluation.status, 'status': evaluation.status,
'id': evaluation.pk 'id': evaluation.pk
}) })
@extend_schema(tags=["EvaluationRequest"])
class ArchiveEvaluationrequestView(GenericAPIView):
permission_classes = [IsAuthenticated]
def get_serializer_class(self):
if self.request.method == "GET":
return ListEvaluationrequestSerializer
return ArchiveEvaluationrequestSerializer
@extend_schema(
tags=["EvaluationRequest"],
summary="Get archived evaluation requests list",
description="""
Returns only archived evaluation requests.
This endpoint works like evaluation-request/,
but only records with is_archive=True are returned.
""",
responses={200: ListEvaluationrequestSerializer(many=True)},
)
def get(self, request, *args, **kwargs):
queryset = EvaluationrequestModel.objects.filter(
is_archive=True
).order_by("-created_at")
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
@extend_schema(
tags=["EvaluationRequest"],
summary="Archive or unarchive evaluation request",
description="""
Update archive status for evaluation request.
- is_archive=true → archive
- is_archive=false → remove from archive
""",
request=ArchiveEvaluationrequestSerializer,
responses={
200: OpenApiResponse(
description="Archive status updated successfully"
),
400: OpenApiResponse(
description="Validation error"
),
404: OpenApiResponse(
description="Evaluation request not found"
),
},
)
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
validated_data = serializer.validated_data
obj = get_object_or_404(
EvaluationrequestModel,
id=validated_data["id"]
)
obj.is_archive = validated_data["is_archive"]
obj.save(update_fields=["is_archive"])
return Response(
{
"success": True,
"message": "Archive status updated successfully"
},
status=status.HTTP_200_OK
)

View File

@@ -1,6 +1,6 @@
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from rest_framework.permissions import AllowAny from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import GenericAPIView from rest_framework.generics import GenericAPIView
from drf_spectacular.utils import ( from drf_spectacular.utils import (
@@ -14,7 +14,7 @@ from ..serializers import TechPassportSerializer
class TechPassportAPIView(GenericAPIView): class TechPassportAPIView(GenericAPIView):
authentication_classes = [] authentication_classes = []
permission_classes = [AllowAny] permission_classes = [IsAuthenticated]
@extend_schema( @extend_schema(
tags=["Tech Passport"], tags=["Tech Passport"],
@@ -33,18 +33,27 @@ class TechPassportAPIView(GenericAPIView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
serializer = TechPassportSerializer(data=request.data) serializer = TechPassportSerializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
data = serializer.validated_data data = serializer.validated_data
try:
result = TechPassportService.get_auto_info( result = TechPassportService.get_auto_info(
autonumber=data["autonumber"], autonumber=data["autonumber"],
tech_pass_number=data["tech_pass_number"], tech_pass_number=data["tech_pass_number"],
tech_pass_series=data["tech_pass_series"], tech_pass_series=data["tech_pass_series"],
) )
return Response(result, status=status.HTTP_200_OK)
except Exception as e: response_data = result["data"]
status_code = result["status_code"]
# success bolsa faqat data ichidagi data qaytariladi
if status_code == 200:
return Response( return Response(
{"detail": str(e)}, response_data.get("data", {}),
status=status.HTTP_400_BAD_REQUEST status=status.HTTP_200_OK
)
# error bolsa original response qaytariladi
return Response(
response_data,
status=status_code
) )

View File

@@ -44,21 +44,28 @@ class TechPassportService:
verify=False verify=False
) )
response.raise_for_status()
logger.info( logger.info(
f"Tech passport info fetched successfully: {response.status_code}" f"Tech passport response status: {response.status_code}"
) )
try:
response_data = response.json() response_data = response.json()
except ValueError:
response_data = {
"detail": "Invalid response from external service"
}
return response_data.get("data", {}) return {
"status_code": response.status_code,
"data": response_data
}
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
logger.error( logger.error(str(e))
f"Error while fetching tech passport info: {str(e)}"
)
return { return {
"success": False, "status_code": 500,
"message": str(e) "data": {
"detail": str(e)
}
} }

View File

@@ -84,7 +84,7 @@ services:
max-file: "5" max-file: "5"
web: web:
image: husanjon/sifatbaho:103 image: husanjon/sifatbaho:115
env_file: env_file:
- .env - .env
environment: environment:
@@ -129,7 +129,7 @@ services:
max-file: "5" max-file: "5"
celery: celery:
image: husanjon/sifatbaho:103 image: husanjon/sifatbaho:115
env_file: env_file:
- .env - .env
environment: environment: