From 33aa06f80bfeb6c7cb386ce4abd6d8a4099668e7 Mon Sep 17 00:00:00 2001 From: komoliddin Date: Wed, 22 Apr 2026 11:17:09 +0500 Subject: [PATCH 01/22] fix: handle response errors and improve data return in TechPassportService --- core/apps/evaluation/views/tech_passport.py | 31 +++++++++++++-------- core/services/tech_passport.py | 27 +++++++++++------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/core/apps/evaluation/views/tech_passport.py b/core/apps/evaluation/views/tech_passport.py index 6d7171f..28ade15 100644 --- a/core/apps/evaluation/views/tech_passport.py +++ b/core/apps/evaluation/views/tech_passport.py @@ -33,18 +33,27 @@ class TechPassportAPIView(GenericAPIView): def post(self, request, *args, **kwargs): serializer = TechPassportSerializer(data=request.data) serializer.is_valid(raise_exception=True) + data = serializer.validated_data - try: - result = TechPassportService.get_auto_info( - autonumber=data["autonumber"], - tech_pass_number=data["tech_pass_number"], - tech_pass_series=data["tech_pass_series"], - ) - return Response(result, status=status.HTTP_200_OK) + result = TechPassportService.get_auto_info( + autonumber=data["autonumber"], + tech_pass_number=data["tech_pass_number"], + tech_pass_series=data["tech_pass_series"], + ) - except Exception as e: + response_data = result["data"] + status_code = result["status_code"] + + # success bo‘lsa faqat data ichidagi data qaytariladi + if status_code == 200: return Response( - {"detail": str(e)}, - status=status.HTTP_400_BAD_REQUEST - ) \ No newline at end of file + response_data.get("data", {}), + status=status.HTTP_200_OK + ) + + # error bo‘lsa original response qaytariladi + return Response( + response_data, + status=status_code + ) \ No newline at end of file diff --git a/core/services/tech_passport.py b/core/services/tech_passport.py index 0d50849..bd9e05b 100644 --- a/core/services/tech_passport.py +++ b/core/services/tech_passport.py @@ -44,21 +44,28 @@ class TechPassportService: verify=False ) - response.raise_for_status() - logger.info( - f"Tech passport info fetched successfully: {response.status_code}" + f"Tech passport response status: {response.status_code}" ) - response_data = response.json() + try: + 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: - logger.error( - f"Error while fetching tech passport info: {str(e)}" - ) + logger.error(str(e)) + return { - "success": False, - "message": str(e) + "status_code": 500, + "data": { + "detail": str(e) + } } \ No newline at end of file From 01711b59273c8a9b18e0ba2172eb3647cce39aa8 Mon Sep 17 00:00:00 2001 From: komoliddin Date: Wed, 22 Apr 2026 11:28:35 +0500 Subject: [PATCH 02/22] feat: add 404 response for missing company or person in Didox API --- core/apps/evaluation/views/didox.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/apps/evaluation/views/didox.py b/core/apps/evaluation/views/didox.py index f327e6d..cd4752f 100644 --- a/core/apps/evaluation/views/didox.py +++ b/core/apps/evaluation/views/didox.py @@ -31,7 +31,6 @@ class DidoxCompanyInfoAPIView(GenericAPIView): def get(self, request, *args, **kwargs): tin = kwargs.get("tin") - # 🔥 TYPE CHECK try: tin = int(tin) except (TypeError, ValueError): @@ -48,4 +47,14 @@ class DidoxCompanyInfoAPIView(GenericAPIView): 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) \ No newline at end of file From cc8fc345d95923230e72bd3d416913bdd42fbb5e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 22 Apr 2026 09:11:31 +0000 Subject: [PATCH 03/22] =?UTF-8?q?=F0=9F=94=84=20Update=20image=20to=20104?= =?UTF-8?q?=20[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/urls.py | 2 +- stack.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/urls.py b/config/urls.py index 1c27670..c4afb9d 100644 --- a/config/urls.py +++ b/config/urls.py @@ -13,7 +13,7 @@ from config.env import env def home(request): - return HttpResponse("OK: #46c96214578b78c1fe731f9a2ceb4429b81d4da9") + return HttpResponse("OK: #03e4387e4dd3b8adac2b76cc4b34f60d204624a2") urlpatterns = [ diff --git a/stack.yaml b/stack.yaml index cd59834..c8f6d2b 100644 --- a/stack.yaml +++ b/stack.yaml @@ -84,7 +84,7 @@ services: max-file: "5" web: - image: husanjon/sifatbaho:103 + image: husanjon/sifatbaho:104 env_file: - .env environment: @@ -129,7 +129,7 @@ services: max-file: "5" celery: - image: husanjon/sifatbaho:103 + image: husanjon/sifatbaho:104 env_file: - .env environment: From 09d2e0954c17dec0cac01e05d3a2a449ec42a136 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 22 Apr 2026 09:17:53 +0000 Subject: [PATCH 04/22] =?UTF-8?q?=F0=9F=94=84=20Update=20image=20to=20105?= =?UTF-8?q?=20[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/urls.py | 2 +- stack.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/urls.py b/config/urls.py index c4afb9d..ab7214f 100644 --- a/config/urls.py +++ b/config/urls.py @@ -13,7 +13,7 @@ from config.env import env def home(request): - return HttpResponse("OK: #03e4387e4dd3b8adac2b76cc4b34f60d204624a2") + return HttpResponse("OK: #84a5afb2eeb4563190e79944bb74e9fb7f212737") urlpatterns = [ diff --git a/stack.yaml b/stack.yaml index c8f6d2b..b2006e3 100644 --- a/stack.yaml +++ b/stack.yaml @@ -84,7 +84,7 @@ services: max-file: "5" web: - image: husanjon/sifatbaho:104 + image: husanjon/sifatbaho:105 env_file: - .env environment: @@ -129,7 +129,7 @@ services: max-file: "5" celery: - image: husanjon/sifatbaho:104 + image: husanjon/sifatbaho:105 env_file: - .env environment: From 2af67333e2e9e766d5e3bb1a3e46492c887f9e19 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 22 Apr 2026 09:21:04 +0000 Subject: [PATCH 05/22] =?UTF-8?q?=F0=9F=94=84=20Update=20image=20to=20106?= =?UTF-8?q?=20[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/urls.py | 2 +- stack.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/urls.py b/config/urls.py index ab7214f..0d46d3d 100644 --- a/config/urls.py +++ b/config/urls.py @@ -13,7 +13,7 @@ from config.env import env def home(request): - return HttpResponse("OK: #84a5afb2eeb4563190e79944bb74e9fb7f212737") + return HttpResponse("OK: #81c689e7e9fae2cd8570239964d9fe3aa642bfdc") urlpatterns = [ diff --git a/stack.yaml b/stack.yaml index b2006e3..9393f59 100644 --- a/stack.yaml +++ b/stack.yaml @@ -84,7 +84,7 @@ services: max-file: "5" web: - image: husanjon/sifatbaho:105 + image: husanjon/sifatbaho:106 env_file: - .env environment: @@ -129,7 +129,7 @@ services: max-file: "5" celery: - image: husanjon/sifatbaho:105 + image: husanjon/sifatbaho:106 env_file: - .env environment: From 1f0e942be87e9f4d736afae74de9320c4577156b Mon Sep 17 00:00:00 2001 From: husanjon Date: Wed, 22 Apr 2026 15:01:52 +0500 Subject: [PATCH 06/22] deploy.yaml fixess --- .github/workflows/deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 9361866..20b46d5 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -153,7 +153,7 @@ jobs: update_env \ "DB_HOST=postgres" \ "DB_NAME=sifatbahodb" \ - "DB_PORT=5432" - + "DB_PORT=5432" \ + "DIDOX_TOKEN=${{ secrets.DIDOX_TOKEN }}" export PORT=8085 docker stack deploy -c stack.yaml ${{ env.PROJECT_NAME }} --with-registry-auth From 320f490d23495917a267779d41263b887ae8dbc5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 22 Apr 2026 10:03:44 +0000 Subject: [PATCH 07/22] =?UTF-8?q?=F0=9F=94=84=20Update=20image=20to=20107?= =?UTF-8?q?=20[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/urls.py | 2 +- stack.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/urls.py b/config/urls.py index 0d46d3d..91eed4d 100644 --- a/config/urls.py +++ b/config/urls.py @@ -13,7 +13,7 @@ from config.env import env def home(request): - return HttpResponse("OK: #81c689e7e9fae2cd8570239964d9fe3aa642bfdc") + return HttpResponse("OK: #d4e6d80c86fcf4422f71238c6552dfa4b42f9737") urlpatterns = [ diff --git a/stack.yaml b/stack.yaml index 9393f59..aa35c2c 100644 --- a/stack.yaml +++ b/stack.yaml @@ -84,7 +84,7 @@ services: max-file: "5" web: - image: husanjon/sifatbaho:106 + image: husanjon/sifatbaho:107 env_file: - .env environment: @@ -129,7 +129,7 @@ services: max-file: "5" celery: - image: husanjon/sifatbaho:106 + image: husanjon/sifatbaho:107 env_file: - .env environment: From 4fee037467e3583af477b7345ad92a0095d743ed Mon Sep 17 00:00:00 2001 From: komoliddin Date: Thu, 23 Apr 2026 11:49:55 +0500 Subject: [PATCH 08/22] Add is_archive field to Quickevaluation model. Write apis for update is_archive and list archived quick evaluations --- core/apps/evaluation/models/quick.py | 5 ++ .../serializers/quick/QuickEvaluation.py | 8 +- core/apps/evaluation/urls.py | 2 + core/apps/evaluation/views/didox.py | 4 +- core/apps/evaluation/views/quick.py | 84 ++++++++++++++++++- core/apps/evaluation/views/tech_passport.py | 4 +- 6 files changed, 99 insertions(+), 8 deletions(-) diff --git a/core/apps/evaluation/models/quick.py b/core/apps/evaluation/models/quick.py index d501397..819e16c 100644 --- a/core/apps/evaluation/models/quick.py +++ b/core/apps/evaluation/models/quick.py @@ -132,6 +132,11 @@ class QuickEvaluationModel(AbstractBaseModel): default=QuickEvaluationStatus.CREATED, ) + is_archive = models.BooleanField( + verbose_name=_("is archive"), + default=False, + ) + def __str__(self): return f"Quick Evaluation {self.pk} by {self.created_by}" diff --git a/core/apps/evaluation/serializers/quick/QuickEvaluation.py b/core/apps/evaluation/serializers/quick/QuickEvaluation.py index 93348d7..23019f4 100644 --- a/core/apps/evaluation/serializers/quick/QuickEvaluation.py +++ b/core/apps/evaluation/serializers/quick/QuickEvaluation.py @@ -38,7 +38,8 @@ class BaseQuickevaluationSerializer(serializers.ModelSerializer): "state_car_name", "created_at", "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: validated_data["created_by"] = request.user return super().create(validated_data) + + +class ArchiveQuickevaluationSerializer(serializers.Serializer): + id = serializers.IntegerField(required=True) + is_archive = serializers.BooleanField(required=True) \ No newline at end of file diff --git a/core/apps/evaluation/urls.py b/core/apps/evaluation/urls.py index a530b3c..1c4a54d 100644 --- a/core/apps/evaluation/urls.py +++ b/core/apps/evaluation/urls.py @@ -29,6 +29,7 @@ from .views import ( DidoxCompanyInfoAPIView, TechPassportAPIView, EvaluationStatusChange, + ArchiveQuickEvaluationView, ) router = DefaultRouter() @@ -74,4 +75,5 @@ urlpatterns = [ ), path("evaluation-request//change-status/", EvaluationStatusChange.as_view(), name="evaluation-change-status"), + path("quick-evaluation/archive/", ArchiveQuickEvaluationView.as_view(), name="quick-evaluation-archive"), ] diff --git a/core/apps/evaluation/views/didox.py b/core/apps/evaluation/views/didox.py index cd4752f..273a658 100644 --- a/core/apps/evaluation/views/didox.py +++ b/core/apps/evaluation/views/didox.py @@ -1,6 +1,6 @@ from rest_framework.response import Response from rest_framework import status -from rest_framework.permissions import AllowAny +from rest_framework.permissions import IsAuthenticated from rest_framework.generics import GenericAPIView @@ -11,7 +11,7 @@ from core.services.didox import DidoxService class DidoxCompanyInfoAPIView(GenericAPIView): authentication_classes = [] - permission_classes = [AllowAny] + permission_classes = [IsAuthenticated] @extend_schema( tags=["Didox"], diff --git a/core/apps/evaluation/views/quick.py b/core/apps/evaluation/views/quick.py index 87a4e80..d0970ef 100644 --- a/core/apps/evaluation/views/quick.py +++ b/core/apps/evaluation/views/quick.py @@ -1,10 +1,14 @@ from django_core.mixins import BaseViewSetMixin 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.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.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.models import QuickEvaluationModel @@ -12,6 +16,7 @@ from core.apps.evaluation.serializers.quick import ( CreateQuickevaluationSerializer, ListQuickevaluationSerializer, RetrieveQuickevaluationSerializer, + ArchiveQuickevaluationSerializer, ) @@ -20,7 +25,7 @@ class QuickEvaluationView(BaseViewSetMixin, ModelViewSet): queryset = QuickEvaluationModel.objects.select_related( "created_by", "brand", "marka", "color", "fuel_type", "body_type", "state_car", "car_position", - ).all() + ).filter(is_archive=False) serializer_class = ListQuickevaluationSerializer permission_classes = [AllowAny] parser_classes = [MultiPartParser, FormParser] @@ -50,3 +55,76 @@ class QuickEvaluationView(BaseViewSetMixin, ModelViewSet): "retrieve": RetrieveQuickevaluationSerializer, "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 + ) \ No newline at end of file diff --git a/core/apps/evaluation/views/tech_passport.py b/core/apps/evaluation/views/tech_passport.py index 28ade15..b1af112 100644 --- a/core/apps/evaluation/views/tech_passport.py +++ b/core/apps/evaluation/views/tech_passport.py @@ -1,6 +1,6 @@ from rest_framework.response import Response from rest_framework import status -from rest_framework.permissions import AllowAny +from rest_framework.permissions import IsAuthenticated from rest_framework.generics import GenericAPIView from drf_spectacular.utils import ( @@ -14,7 +14,7 @@ from ..serializers import TechPassportSerializer class TechPassportAPIView(GenericAPIView): authentication_classes = [] - permission_classes = [AllowAny] + permission_classes = [IsAuthenticated] @extend_schema( tags=["Tech Passport"], From 3a08c81ff3e2ba87bceb403cdb90b31139b92df4 Mon Sep 17 00:00:00 2001 From: komoliddin Date: Thu, 23 Apr 2026 12:23:55 +0500 Subject: [PATCH 09/22] Add is_archive field to EvaluationRequest model. Write apis for update is_archive and list archived requests --- .../0032_evaluationrequestmodel_is_archive.py | 18 +++++ core/apps/evaluation/models/request.py | 4 + .../serializers/request/EvaluationRequest.py | 5 ++ core/apps/evaluation/urls.py | 2 + core/apps/evaluation/views/request.py | 78 ++++++++++++++++++- 5 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 core/apps/evaluation/migrations/0032_evaluationrequestmodel_is_archive.py diff --git a/core/apps/evaluation/migrations/0032_evaluationrequestmodel_is_archive.py b/core/apps/evaluation/migrations/0032_evaluationrequestmodel_is_archive.py new file mode 100644 index 0000000..edb3779 --- /dev/null +++ b/core/apps/evaluation/migrations/0032_evaluationrequestmodel_is_archive.py @@ -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'), + ), + ] diff --git a/core/apps/evaluation/models/request.py b/core/apps/evaluation/models/request.py index baf6c14..a26c7d0 100644 --- a/core/apps/evaluation/models/request.py +++ b/core/apps/evaluation/models/request.py @@ -118,6 +118,10 @@ class EvaluationrequestModel(AbstractBaseModel): choices=RequestStatus.choices, default=RequestStatus.PENDING, ) + is_archive = models.BooleanField( + verbose_name=_("is archive"), + default=False, + ) def __str__(self): return f"Requests #{self.pk} — {self.get_rate_type_display()}" diff --git a/core/apps/evaluation/serializers/request/EvaluationRequest.py b/core/apps/evaluation/serializers/request/EvaluationRequest.py index 368af46..3554f9b 100644 --- a/core/apps/evaluation/serializers/request/EvaluationRequest.py +++ b/core/apps/evaluation/serializers/request/EvaluationRequest.py @@ -55,6 +55,7 @@ class BaseEvaluationrequestSerializer(serializers.ModelSerializer): "user", "created_at", "updated_at", + "is_archive", ] def get_location(self, obj): @@ -183,3 +184,7 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer): validated_data["location_name"] = str(location_name) validated_data["user"] = self.context["request"].user return super().create(validated_data) + +class ArchiveEvaluationrequestSerializer(serializers.Serializer): + id = serializers.IntegerField(required=True) + is_archive = serializers.BooleanField(required=True) \ No newline at end of file diff --git a/core/apps/evaluation/urls.py b/core/apps/evaluation/urls.py index a530b3c..969ddde 100644 --- a/core/apps/evaluation/urls.py +++ b/core/apps/evaluation/urls.py @@ -29,6 +29,7 @@ from .views import ( DidoxCompanyInfoAPIView, TechPassportAPIView, EvaluationStatusChange, + ArchiveEvaluationrequestView, ) router = DefaultRouter() @@ -74,4 +75,5 @@ urlpatterns = [ ), path("evaluation-request//change-status/", EvaluationStatusChange.as_view(), name="evaluation-change-status"), + path("evaluation-request/archive/", ArchiveEvaluationrequestView.as_view(), name="evaluation-request-archive"), ] diff --git a/core/apps/evaluation/views/request.py b/core/apps/evaluation/views/request.py index 1548049..0675d6a 100644 --- a/core/apps/evaluation/views/request.py +++ b/core/apps/evaluation/views/request.py @@ -17,8 +17,11 @@ from core.apps.evaluation.serializers.request import ( CreateEvaluationrequestSerializer, ListEvaluationrequestSerializer, 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): @@ -171,4 +174,77 @@ class EvaluationStatusChange(APIView): 'success': True, 'status': evaluation.status, 'id': evaluation.pk - }) \ No newline at end of file + }) + +@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 + ) \ No newline at end of file From 5cf4b950fbf0e3fe5b9c57a2529f487128c1befe Mon Sep 17 00:00:00 2001 From: komoliddin Date: Thu, 23 Apr 2026 12:25:18 +0500 Subject: [PATCH 10/22] Make migrations --- .../0032_quickevaluationmodel_is_archive.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 core/apps/evaluation/migrations/0032_quickevaluationmodel_is_archive.py diff --git a/core/apps/evaluation/migrations/0032_quickevaluationmodel_is_archive.py b/core/apps/evaluation/migrations/0032_quickevaluationmodel_is_archive.py new file mode 100644 index 0000000..c66262c --- /dev/null +++ b/core/apps/evaluation/migrations/0032_quickevaluationmodel_is_archive.py @@ -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'), + ), + ] From 207363dc6a48c012bbdacafe47ff8262ec882387 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 23 Apr 2026 10:50:59 +0000 Subject: [PATCH 11/22] =?UTF-8?q?=F0=9F=94=84=20Update=20image=20to=20108?= =?UTF-8?q?=20[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/urls.py | 2 +- stack.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/urls.py b/config/urls.py index 91eed4d..6043631 100644 --- a/config/urls.py +++ b/config/urls.py @@ -13,7 +13,7 @@ from config.env import env def home(request): - return HttpResponse("OK: #d4e6d80c86fcf4422f71238c6552dfa4b42f9737") + return HttpResponse("OK: #2a08ad9662f957a3a0b75b8c8440d49b0b4e9ae4") urlpatterns = [ diff --git a/stack.yaml b/stack.yaml index aa35c2c..77f93e6 100644 --- a/stack.yaml +++ b/stack.yaml @@ -84,7 +84,7 @@ services: max-file: "5" web: - image: husanjon/sifatbaho:107 + image: husanjon/sifatbaho:108 env_file: - .env environment: @@ -129,7 +129,7 @@ services: max-file: "5" celery: - image: husanjon/sifatbaho:107 + image: husanjon/sifatbaho:108 env_file: - .env environment: From 76563b3ef08bc90ff3628257b539b2b052016143 Mon Sep 17 00:00:00 2001 From: komoliddin Date: Thu, 23 Apr 2026 16:07:37 +0500 Subject: [PATCH 12/22] Add Certificate model and write crud for it --- .../migrations/0032_certificatemodel.py | 28 +++++++++++++++++++ core/apps/evaluation/models/__init__.py | 1 + core/apps/evaluation/models/certificate.py | 20 +++++++++++++ core/apps/evaluation/serializers/__init__.py | 1 + .../serializers/certificate/__init__.py | 1 + .../serializers/certificate/certificate.py | 12 ++++++++ core/apps/evaluation/urls.py | 2 ++ core/apps/evaluation/views/__init__.py | 1 + core/apps/evaluation/views/certificate.py | 20 +++++++++++++ 9 files changed, 86 insertions(+) create mode 100644 core/apps/evaluation/migrations/0032_certificatemodel.py create mode 100644 core/apps/evaluation/models/certificate.py create mode 100644 core/apps/evaluation/serializers/certificate/__init__.py create mode 100644 core/apps/evaluation/serializers/certificate/certificate.py create mode 100644 core/apps/evaluation/views/certificate.py diff --git a/core/apps/evaluation/migrations/0032_certificatemodel.py b/core/apps/evaluation/migrations/0032_certificatemodel.py new file mode 100644 index 0000000..cbd26f6 --- /dev/null +++ b/core/apps/evaluation/migrations/0032_certificatemodel.py @@ -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', + }, + ), + ] diff --git a/core/apps/evaluation/models/__init__.py b/core/apps/evaluation/models/__init__.py index a625148..46a6a07 100644 --- a/core/apps/evaluation/models/__init__.py +++ b/core/apps/evaluation/models/__init__.py @@ -11,3 +11,4 @@ from .report import * # noqa from .request import * # noqa from .valuation import * # noqa from .vehicle import * # noqa +from .certificate import * # noqa diff --git a/core/apps/evaluation/models/certificate.py b/core/apps/evaluation/models/certificate.py new file mode 100644 index 0000000..5b8233c --- /dev/null +++ b/core/apps/evaluation/models/certificate.py @@ -0,0 +1,20 @@ +from django.db import models +from django_core.models import AbstractBaseModel +from model_bakery import baker +from django.utils.translation import gettext_lazy as _ + +class CertificateModel(AbstractBaseModel): + title = models.CharField(verbose_name=_("title"), max_length=255, blank=False, null=False) + file_url = models.URLField(verbose_name=_("file url"), max_length=255, blank=False, null=False) + + 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") \ No newline at end of file diff --git a/core/apps/evaluation/serializers/__init__.py b/core/apps/evaluation/serializers/__init__.py index 600d37c..d137a69 100644 --- a/core/apps/evaluation/serializers/__init__.py +++ b/core/apps/evaluation/serializers/__init__.py @@ -12,3 +12,4 @@ from .request import * # noqa from .valuation import * # noqa from .vehicle import * # noqa from .tech_passport import * # noqa +from .certificate import * # noqa diff --git a/core/apps/evaluation/serializers/certificate/__init__.py b/core/apps/evaluation/serializers/certificate/__init__.py new file mode 100644 index 0000000..d736920 --- /dev/null +++ b/core/apps/evaluation/serializers/certificate/__init__.py @@ -0,0 +1 @@ +from .certificate import * # noqa diff --git a/core/apps/evaluation/serializers/certificate/certificate.py b/core/apps/evaluation/serializers/certificate/certificate.py new file mode 100644 index 0000000..1a7bcdb --- /dev/null +++ b/core/apps/evaluation/serializers/certificate/certificate.py @@ -0,0 +1,12 @@ +from rest_framework import serializers +from core.apps.evaluation.models import CertificateModel + +class BaseCertificateSerializer(serializers.ModelSerializer): + + class Meta: + model = CertificateModel + fields = [ + "id", + "title", + "file_url" + ] \ No newline at end of file diff --git a/core/apps/evaluation/urls.py b/core/apps/evaluation/urls.py index a530b3c..67887f6 100644 --- a/core/apps/evaluation/urls.py +++ b/core/apps/evaluation/urls.py @@ -29,6 +29,7 @@ from .views import ( DidoxCompanyInfoAPIView, TechPassportAPIView, EvaluationStatusChange, + CertificateView, ) router = DefaultRouter() @@ -53,6 +54,7 @@ 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") +router.register("certificate", CertificateView, basename="certificate") urlpatterns = [ path("", include(router.urls)), path("auto-evaluation/appraisers/", include( diff --git a/core/apps/evaluation/views/__init__.py b/core/apps/evaluation/views/__init__.py index 9fd419f..a6f2bd5 100644 --- a/core/apps/evaluation/views/__init__.py +++ b/core/apps/evaluation/views/__init__.py @@ -13,3 +13,4 @@ from .valuation import * # noqa from .vehicle import * # noqa from .didox import * # noqa from .tech_passport import * # noqa +from .certificate import * # noqa diff --git a/core/apps/evaluation/views/certificate.py b/core/apps/evaluation/views/certificate.py new file mode 100644 index 0000000..a990840 --- /dev/null +++ b/core/apps/evaluation/views/certificate.py @@ -0,0 +1,20 @@ +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 + +@extend_schema(tags=["Certificate"]) +class CertificateView(BaseViewSetMixin, ModelViewSet): + queryset = CertificateModel.objects.all() + serializer_class = BaseCertificateSerializer + permission_classes = [IsAuthenticated] + + filter_backends = [SearchFilter] + search_fields = ["title"] + + pagination_class = None + + action_permission_classes = {} From 6e0718c5dbc0c00aac698272f4a2cfde074453fa Mon Sep 17 00:00:00 2001 From: komoliddin Date: Thu, 23 Apr 2026 16:22:42 +0500 Subject: [PATCH 13/22] merge migrations --- .../migrations/0033_merge_20260423_1622.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 core/apps/evaluation/migrations/0033_merge_20260423_1622.py diff --git a/core/apps/evaluation/migrations/0033_merge_20260423_1622.py b/core/apps/evaluation/migrations/0033_merge_20260423_1622.py new file mode 100644 index 0000000..addd7d4 --- /dev/null +++ b/core/apps/evaluation/migrations/0033_merge_20260423_1622.py @@ -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 = [ + ] From dc622ce305065cddb55d3bd9fcea8fbdf33227a2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 23 Apr 2026 11:24:21 +0000 Subject: [PATCH 14/22] =?UTF-8?q?=F0=9F=94=84=20Update=20image=20to=20111?= =?UTF-8?q?=20[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/urls.py | 2 +- stack.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/urls.py b/config/urls.py index 6043631..78d1fe5 100644 --- a/config/urls.py +++ b/config/urls.py @@ -13,7 +13,7 @@ from config.env import env def home(request): - return HttpResponse("OK: #2a08ad9662f957a3a0b75b8c8440d49b0b4e9ae4") + return HttpResponse("OK: #6e0718c5dbc0c00aac698272f4a2cfde074453fa") urlpatterns = [ diff --git a/stack.yaml b/stack.yaml index 77f93e6..7db9338 100644 --- a/stack.yaml +++ b/stack.yaml @@ -84,7 +84,7 @@ services: max-file: "5" web: - image: husanjon/sifatbaho:108 + image: husanjon/sifatbaho:111 env_file: - .env environment: @@ -129,7 +129,7 @@ services: max-file: "5" celery: - image: husanjon/sifatbaho:108 + image: husanjon/sifatbaho:111 env_file: - .env environment: From 7a88e39b96afb9ee4eaa1b3587ded165cd59e38c Mon Sep 17 00:00:00 2001 From: komoliddin Date: Thu, 23 Apr 2026 18:04:09 +0500 Subject: [PATCH 15/22] Enhance Certificate model and serializer to support file uploads and URL generation --- core/apps/evaluation/models/certificate.py | 16 +++++++++++++--- .../serializers/certificate/certificate.py | 17 +++++++++++++++-- core/apps/evaluation/views/certificate.py | 3 +++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/core/apps/evaluation/models/certificate.py b/core/apps/evaluation/models/certificate.py index 5b8233c..4089da4 100644 --- a/core/apps/evaluation/models/certificate.py +++ b/core/apps/evaluation/models/certificate.py @@ -1,11 +1,21 @@ from django.db import models from django_core.models import AbstractBaseModel -from model_bakery import baker 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, blank=False, null=False) - file_url = models.URLField(verbose_name=_("file url"), max_length=255, blank=False, null=False) + 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 diff --git a/core/apps/evaluation/serializers/certificate/certificate.py b/core/apps/evaluation/serializers/certificate/certificate.py index 1a7bcdb..c094e3f 100644 --- a/core/apps/evaluation/serializers/certificate/certificate.py +++ b/core/apps/evaluation/serializers/certificate/certificate.py @@ -1,12 +1,25 @@ from rest_framework import serializers from core.apps.evaluation.models import CertificateModel + class BaseCertificateSerializer(serializers.ModelSerializer): + file_url = serializers.SerializerMethodField() class Meta: model = CertificateModel fields = [ "id", "title", - "file_url" - ] \ No newline at end of file + "file", + "file_url", + ] + + def get_file_url(self, obj): + request = self.context.get("request") + + if obj.file: + if request: + return request.build_absolute_uri(obj.file.url) + return obj.file.url + + return None \ No newline at end of file diff --git a/core/apps/evaluation/views/certificate.py b/core/apps/evaluation/views/certificate.py index a990840..ef5392e 100644 --- a/core/apps/evaluation/views/certificate.py +++ b/core/apps/evaluation/views/certificate.py @@ -5,6 +5,7 @@ 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"]) class CertificateView(BaseViewSetMixin, ModelViewSet): @@ -12,6 +13,8 @@ class CertificateView(BaseViewSetMixin, ModelViewSet): serializer_class = BaseCertificateSerializer permission_classes = [IsAuthenticated] + parser_classes = [MultiPartParser, FormParser] + filter_backends = [SearchFilter] search_fields = ["title"] From 6eed2d998ec4aa8b33aec572175e50ffbb3f3b62 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 23 Apr 2026 13:07:08 +0000 Subject: [PATCH 16/22] =?UTF-8?q?=F0=9F=94=84=20Update=20image=20to=20112?= =?UTF-8?q?=20[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/urls.py | 2 +- stack.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/urls.py b/config/urls.py index 78d1fe5..c45dddd 100644 --- a/config/urls.py +++ b/config/urls.py @@ -13,7 +13,7 @@ from config.env import env def home(request): - return HttpResponse("OK: #6e0718c5dbc0c00aac698272f4a2cfde074453fa") + return HttpResponse("OK: #2c8269116651c45eba04f881a2959fddf9732a0c") urlpatterns = [ diff --git a/stack.yaml b/stack.yaml index 7db9338..27be01d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -84,7 +84,7 @@ services: max-file: "5" web: - image: husanjon/sifatbaho:111 + image: husanjon/sifatbaho:112 env_file: - .env environment: @@ -129,7 +129,7 @@ services: max-file: "5" celery: - image: husanjon/sifatbaho:111 + image: husanjon/sifatbaho:112 env_file: - .env environment: From 6456283f721ceadea9372984c8ae325cbde65815 Mon Sep 17 00:00:00 2001 From: komoliddin Date: Thu, 23 Apr 2026 18:59:06 +0500 Subject: [PATCH 17/22] makemigrations --- ...move_certificatemodel_file_url_and_more.py | 22 +++++++++++++++++++ core/apps/evaluation/views/certificate.py | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 core/apps/evaluation/migrations/0034_remove_certificatemodel_file_url_and_more.py diff --git a/core/apps/evaluation/migrations/0034_remove_certificatemodel_file_url_and_more.py b/core/apps/evaluation/migrations/0034_remove_certificatemodel_file_url_and_more.py new file mode 100644 index 0000000..21973b2 --- /dev/null +++ b/core/apps/evaluation/migrations/0034_remove_certificatemodel_file_url_and_more.py @@ -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'), + ), + ] diff --git a/core/apps/evaluation/views/certificate.py b/core/apps/evaluation/views/certificate.py index ef5392e..5df696a 100644 --- a/core/apps/evaluation/views/certificate.py +++ b/core/apps/evaluation/views/certificate.py @@ -7,7 +7,7 @@ from core.apps.evaluation.serializers.certificate import BaseCertificateSerializ from rest_framework.filters import SearchFilter from rest_framework.parsers import MultiPartParser, FormParser -@extend_schema(tags=["Certificate"]) +@extend_schema(tags=["Certificate"],request=BaseCertificateSerializer) class CertificateView(BaseViewSetMixin, ModelViewSet): queryset = CertificateModel.objects.all() serializer_class = BaseCertificateSerializer From 07f8d55966583cdc2108c806a05b2b7cb6769930 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 23 Apr 2026 14:02:58 +0000 Subject: [PATCH 18/22] =?UTF-8?q?=F0=9F=94=84=20Update=20image=20to=20113?= =?UTF-8?q?=20[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/urls.py | 2 +- stack.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/urls.py b/config/urls.py index c45dddd..078bd56 100644 --- a/config/urls.py +++ b/config/urls.py @@ -13,7 +13,7 @@ from config.env import env def home(request): - return HttpResponse("OK: #2c8269116651c45eba04f881a2959fddf9732a0c") + return HttpResponse("OK: #b0b4ccfeee7a581cff6bd5398595fff38f323607") urlpatterns = [ diff --git a/stack.yaml b/stack.yaml index 27be01d..840e660 100644 --- a/stack.yaml +++ b/stack.yaml @@ -84,7 +84,7 @@ services: max-file: "5" web: - image: husanjon/sifatbaho:112 + image: husanjon/sifatbaho:113 env_file: - .env environment: @@ -129,7 +129,7 @@ services: max-file: "5" celery: - image: husanjon/sifatbaho:112 + image: husanjon/sifatbaho:113 env_file: - .env environment: From 3b62c5a7bfefc132e1c80d9eda962a6fe28502ff Mon Sep 17 00:00:00 2001 From: komoliddin Date: Fri, 24 Apr 2026 10:14:01 +0500 Subject: [PATCH 19/22] Refactor URL patterns for evaluation archiving and remove unused file_url field from BaseCertificateSerializer --- .../serializers/certificate/certificate.py | 14 +------------- core/apps/evaluation/urls.py | 4 ++-- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/core/apps/evaluation/serializers/certificate/certificate.py b/core/apps/evaluation/serializers/certificate/certificate.py index c094e3f..b9afd95 100644 --- a/core/apps/evaluation/serializers/certificate/certificate.py +++ b/core/apps/evaluation/serializers/certificate/certificate.py @@ -3,7 +3,6 @@ from core.apps.evaluation.models import CertificateModel class BaseCertificateSerializer(serializers.ModelSerializer): - file_url = serializers.SerializerMethodField() class Meta: model = CertificateModel @@ -11,15 +10,4 @@ class BaseCertificateSerializer(serializers.ModelSerializer): "id", "title", "file", - "file_url", - ] - - def get_file_url(self, obj): - request = self.context.get("request") - - if obj.file: - if request: - return request.build_absolute_uri(obj.file.url) - return obj.file.url - - return None \ No newline at end of file + ] \ No newline at end of file diff --git a/core/apps/evaluation/urls.py b/core/apps/evaluation/urls.py index 8b25f9f..c27c9b0 100644 --- a/core/apps/evaluation/urls.py +++ b/core/apps/evaluation/urls.py @@ -78,6 +78,6 @@ urlpatterns = [ ), path("evaluation-request//change-status/", EvaluationStatusChange.as_view(), name="evaluation-change-status"), - path("quick-evaluation/archive/", ArchiveQuickEvaluationView.as_view(), name="quick-evaluation-archive"), - path("evaluation-request/archive/", ArchiveEvaluationrequestView.as_view(), name="evaluation-request-archive"), + path("archive/quick-evaluation/", ArchiveQuickEvaluationView.as_view(), name="quick-evaluation-archive"), + path("archive/evaluation-request/", ArchiveEvaluationrequestView.as_view(), name="evaluation-request-archive"), ] From 190480b6f730d11efac1c0a7f7ba2d92fab761fe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 24 Apr 2026 05:16:20 +0000 Subject: [PATCH 20/22] =?UTF-8?q?=F0=9F=94=84=20Update=20image=20to=20114?= =?UTF-8?q?=20[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/urls.py | 2 +- stack.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/urls.py b/config/urls.py index 078bd56..18874d4 100644 --- a/config/urls.py +++ b/config/urls.py @@ -13,7 +13,7 @@ from config.env import env def home(request): - return HttpResponse("OK: #b0b4ccfeee7a581cff6bd5398595fff38f323607") + return HttpResponse("OK: #1a985ffa4b785b63a71b9e0cdd78042c3fcda239") urlpatterns = [ diff --git a/stack.yaml b/stack.yaml index 840e660..41f18ce 100644 --- a/stack.yaml +++ b/stack.yaml @@ -84,7 +84,7 @@ services: max-file: "5" web: - image: husanjon/sifatbaho:113 + image: husanjon/sifatbaho:114 env_file: - .env environment: @@ -129,7 +129,7 @@ services: max-file: "5" celery: - image: husanjon/sifatbaho:113 + image: husanjon/sifatbaho:114 env_file: - .env environment: From fb275a091aed230313beab89c196c65ec656b797 Mon Sep 17 00:00:00 2001 From: komoliddin Date: Fri, 24 Apr 2026 11:21:01 +0500 Subject: [PATCH 21/22] Add view for crud user --- core/apps/accounts/serializers/user.py | 13 +++++++++++++ core/apps/accounts/urls.py | 3 ++- core/apps/accounts/views/user.py | 18 +++++++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/core/apps/accounts/serializers/user.py b/core/apps/accounts/serializers/user.py index 4f3276e..2a3d902 100644 --- a/core/apps/accounts/serializers/user.py +++ b/core/apps/accounts/serializers/user.py @@ -30,3 +30,16 @@ class UserUpdateSerializer(serializers.ModelSerializer): "last_name", "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 \ No newline at end of file diff --git a/core/apps/accounts/urls.py b/core/apps/accounts/urls.py index f7a1e2d..45ae8fd 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, UserListApiView, AdminUserListApiView +from .views import RegisterView, ResetPasswordView, MeView, ChangePasswordView, UserListApiView, AdminUserListApiView,AdminUserView from rest_framework.routers import DefaultRouter router = DefaultRouter() @@ -12,6 +12,7 @@ router.register("auth", RegisterView, basename="auth") router.register("auth", ResetPasswordView, basename="reset-password") router.register("auth", MeView, basename="me") router.register("auth", ChangePasswordView, basename="change-password") +router.register("user", AdminUserView, basename="user-crud") urlpatterns = [ diff --git a/core/apps/accounts/views/user.py b/core/apps/accounts/views/user.py index f9d4b9e..73a1b1b 100644 --- a/core/apps/accounts/views/user.py +++ b/core/apps/accounts/views/user.py @@ -5,8 +5,11 @@ 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.serializers.user import UserSerializer, AdminUserSerializer from core.apps.accounts.choices.user import RoleChoice +from django_core.mixins import BaseViewSetMixin +from rest_framework.viewsets import ModelViewSet + User = get_user_model() @@ -29,3 +32,16 @@ class AdminUserListApiView(generics.ListAPIView): permission_classes = [IsAuthenticated] filter_backends = [filters.SearchFilter] 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}) + From c89f2b32af0d7f5eec14facc99c5d3d0ef3f1455 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 24 Apr 2026 06:23:17 +0000 Subject: [PATCH 22/22] =?UTF-8?q?=F0=9F=94=84=20Update=20image=20to=20115?= =?UTF-8?q?=20[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/urls.py | 2 +- stack.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/urls.py b/config/urls.py index 18874d4..0e857fb 100644 --- a/config/urls.py +++ b/config/urls.py @@ -13,7 +13,7 @@ from config.env import env def home(request): - return HttpResponse("OK: #1a985ffa4b785b63a71b9e0cdd78042c3fcda239") + return HttpResponse("OK: #88dedd85c79ccf732b2adac03616bd14e67a1579") urlpatterns = [ diff --git a/stack.yaml b/stack.yaml index 41f18ce..1e72694 100644 --- a/stack.yaml +++ b/stack.yaml @@ -84,7 +84,7 @@ services: max-file: "5" web: - image: husanjon/sifatbaho:114 + image: husanjon/sifatbaho:115 env_file: - .env environment: @@ -129,7 +129,7 @@ services: max-file: "5" celery: - image: husanjon/sifatbaho:114 + image: husanjon/sifatbaho:115 env_file: - .env environment: