12 Commits

Author SHA1 Message Date
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
github-actions[bot]
320f490d23 🔄 Update image to 107 [CI SKIP] 2026-04-22 10:03:44 +00:00
d4e6d80c86 Merge pull request 'deploy.yaml fixess' (#88) from fix/cicd-env into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m55s
Reviewed-on: #88
2026-04-22 10:02:12 +00:00
1f0e942be8 deploy.yaml fixess 2026-04-22 15:01:52 +05:00
github-actions[bot]
2af67333e2 🔄 Update image to 106 [CI SKIP] 2026-04-22 09:21:04 +00:00
81c689e7e9 Merge pull request 'feat: add 404 response for missing company or person in Didox API' (#86) from didox-api into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m56s
Reviewed-on: #86
2026-04-22 09:19:30 +00:00
github-actions[bot]
09d2e0954c 🔄 Update image to 105 [CI SKIP] 2026-04-22 09:17:53 +00:00
84a5afb2ee Merge pull request 'add request status change api' (#87) from shaxob into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m57s
Reviewed-on: #87
Reviewed-by: xoliqberdiyev <behruz@felixits.uz>
2026-04-22 09:16:19 +00:00
github-actions[bot]
cc8fc345d9 🔄 Update image to 104 [CI SKIP] 2026-04-22 09:11:31 +00:00
03e4387e4d Merge pull request 'fix: handle response errors and improve data return in TechPassportService' (#85) from tech-passport into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m16s
Reviewed-on: #85
Reviewed-by: xoliqberdiyev <behruz@felixits.uz>
2026-04-22 09:09:38 +00:00
komoliddin
01711b5927 feat: add 404 response for missing company or person in Didox API 2026-04-22 11:28:35 +05:00
komoliddin
33aa06f80b fix: handle response errors and improve data return in TechPassportService 2026-04-22 11:17:09 +05:00
11 changed files with 158 additions and 28 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: #d4e6d80c86fcf4422f71238c6552dfa4b42f9737")
urlpatterns = [ 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

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

@@ -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,6 +29,7 @@ from .views import (
DidoxCompanyInfoAPIView, DidoxCompanyInfoAPIView,
TechPassportAPIView, TechPassportAPIView,
EvaluationStatusChange, EvaluationStatusChange,
ArchiveEvaluationrequestView,
) )
router = DefaultRouter() router = DefaultRouter()
@@ -74,4 +75,5 @@ 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("evaluation-request/archive/", ArchiveEvaluationrequestView.as_view(), name="evaluation-request-archive"),
] ]

View File

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

@@ -17,8 +17,11 @@ from core.apps.evaluation.serializers.request import (
CreateEvaluationrequestSerializer, CreateEvaluationrequestSerializer,
ListEvaluationrequestSerializer, ListEvaluationrequestSerializer,
RetrieveEvaluationrequestSerializer, RetrieveEvaluationrequestSerializer,
ArchiveEvaluationrequestSerializer,
) )
from core.apps.evaluation.choices.request import RequestStatus 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,4 +174,77 @@ class EvaluationStatusChange(APIView):
'success': True, 'success': True,
'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

@@ -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}"
) )
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: 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:107
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:107
env_file: env_file:
- .env - .env
environment: environment: