11 Commits

Author SHA1 Message Date
komoliddin
7a88e39b96 Enhance Certificate model and serializer to support file uploads and URL generation 2026-04-23 18:04:09 +05:00
github-actions[bot]
dc622ce305 🔄 Update image to 111 [CI SKIP] 2026-04-23 11:24:21 +00:00
komoliddin
6e0718c5db merge migrations
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m57s
2026-04-23 16:22:42 +05:00
komoliddin
32d3bea234 Merge branch 'certificate'
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 47s
2026-04-23 16:18:43 +05:00
komoliddin
129827b495 Merge branch 'main' of https://gitea.felixits.uz/sifatbaho/backend-v1
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 49s
2026-04-23 16:13:34 +05:00
komoliddin
50cc555783 Merge branch 'request-archive' 2026-04-23 16:10:00 +05:00
github-actions[bot]
207363dc6a 🔄 Update image to 108 [CI SKIP] 2026-04-23 10:50:59 +00:00
2a08ad9662 Merge pull request 'Add is_archive field to Quickevaluation model. Write apis for update is_archive and list archived quick evaluations' (#89) from tb-archive into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 3m58s
Reviewed-on: #89
2026-04-23 10:49:11 +00:00
komoliddin
5cf4b950fb Make migrations 2026-04-23 12:25:18 +05:00
komoliddin
3a08c81ff3 Add is_archive field to EvaluationRequest model. Write apis for update is_archive and list archived requests 2026-04-23 12:23:55 +05:00
komoliddin
4fee037467 Add is_archive field to Quickevaluation model. Write apis for update is_archive and list archived quick evaluations 2026-04-23 11:49:55 +05:00
17 changed files with 272 additions and 17 deletions

View File

@@ -13,7 +13,7 @@ from config.env import env
def home(request):
return HttpResponse("OK: #d4e6d80c86fcf4422f71238c6552dfa4b42f9737")
return HttpResponse("OK: #6e0718c5dbc0c00aac698272f4a2cfde074453fa")
urlpatterns = [

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

@@ -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

View File

@@ -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}"

View File

@@ -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()}"

View File

@@ -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"
]
"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

View File

@@ -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)

View File

@@ -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)

View File

@@ -30,6 +30,8 @@ from .views import (
TechPassportAPIView,
EvaluationStatusChange,
CertificateView,
ArchiveQuickEvaluationView,
ArchiveEvaluationrequestView,
)
router = DefaultRouter()
@@ -76,4 +78,6 @@ urlpatterns = [
),
path("evaluation-request/<int:pk>/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"),
]

View File

@@ -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"]

View File

@@ -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"],

View File

@@ -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
)

View File

@@ -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
})
})
@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 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"],

View File

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