This commit is contained in:
2026-02-01 08:26:08 +03:30
parent 6c74e43629
commit 521dd9da5c
81 changed files with 103 additions and 53 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -21,7 +21,8 @@ from app.serializers import TransportingDetailSerializer, HatchingDetailSerializ
StewardForTransportCarcassSerializer, KillHouseForTransportCarcassSerializer, TransportCarcassDetailSerializer, \ StewardForTransportCarcassSerializer, KillHouseForTransportCarcassSerializer, TransportCarcassDetailSerializer, \
GuildsForTransportCarcassSerializer, AllProductsTransportSerializer, AllProductsTransportCustomSerializer GuildsForTransportCarcassSerializer, AllProductsTransportSerializer, AllProductsTransportCustomSerializer
from helpers import build_query from helpers import build_query
from app.helper import get_hatching_permit_code from app.helper import get_hatching_permit_code, normalize_persian_arabic_text
def transporting_detail_excel(request): def transporting_detail_excel(request):
filterset_class = TransportingDetailFilterSet filterset_class = TransportingDetailFilterSet
@@ -2589,10 +2590,7 @@ def all_products_transport_excel(request):
filters['product'] = product_type filters['product'] = product_type
if destination_province and destination_province != 'undefined': if destination_province and destination_province != 'undefined':
if destination_province == 'مرکزی': filters['destination_province'] = normalize_persian_arabic_text(destination_province)
filters['destination_province'] = 'مركزي'
else:
filters['destination_province'] = destination_province
if date1 and date2 and date1 != 'undefined' and date2 != 'undefined': if date1 and date2 and date1 != 'undefined' and date2 != 'undefined':
try: try:

View File

@@ -146,6 +146,26 @@ def api_get_hatching_permit_code(request):
return Response({'detail': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response({'detail': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
def normalize_persian_arabic_text(text):
"""
نرمال‌سازی متن فارسی/عربی برای یکسان‌سازی کاراکترهای مشابه
تبدیل کاراکترهای فارسی به عربی برای سازگاری با دیتابیس
این تابع کاراکترهای 'ک' و 'ی' فارسی را به 'ك' و 'ي' عربی تبدیل می‌کند
تا با فرم استاندارد ذخیره شده در دیتابیس مطابقت داشته باشد
"""
if not text:
return text
# تبدیل کاراکترهای فارسی به عربی
# 'ک' (U+06A9 - Persian Kaf) -> 'ك' (U+0643 - Arabic Kaf)
# 'ی' (U+06CC - Persian Yeh) -> 'ي' (U+064A - Arabic Yeh)
text = str(text)
text = text.replace('ک', 'ك') # Persian Kaf to Arabic Kaf
text = text.replace('ی', 'ي') # Persian Yeh to Arabic Yeh
return text
def create_guild(**info): def create_guild(**info):
Guilds( Guilds(

View File

@@ -622,32 +622,66 @@ class AllProductsTransportCustomSerializer(serializers.ModelSerializer):
def get_location_origin(self, obj): def get_location_origin(self, obj):
resul = {} resul = {}
province_origin = Province.objects.filter(name=obj.origin_province).first() # بهینه‌سازی: استفاده از context cache برای جلوگیری از N+1 queries
city_origin = City.objects.filter(name=obj.origin_city).first() location_cache = self.context.get('location_cache', {})
if province_origin:
resul.update({ if obj.origin_province:
"provinceLat": province_origin.Lat, cache_key = f"province_{obj.origin_province}"
"provinceLng": province_origin.Lng if cache_key not in location_cache:
}) province_origin = Province.objects.filter(name=obj.origin_province).only('Lat', 'Lng').first()
if city_origin: if province_origin:
resul.update({ location_cache[cache_key] = {
"cityLat": city_origin.Lat, "provinceLat": province_origin.Lat,
"cityLng": city_origin.Lng "provinceLng": province_origin.Lng
}) }
else:
location_cache[cache_key] = {}
resul.update(location_cache.get(cache_key, {}))
if obj.origin_city:
cache_key = f"city_{obj.origin_city}"
if cache_key not in location_cache:
city_origin = City.objects.filter(name=obj.origin_city).only('Lat', 'Lng').first()
if city_origin:
location_cache[cache_key] = {
"cityLat": city_origin.Lat,
"cityLng": city_origin.Lng
}
else:
location_cache[cache_key] = {}
resul.update(location_cache.get(cache_key, {}))
return resul return resul
def get_location_destination(self, obj): def get_location_destination(self, obj):
resul = {} resul = {}
province_destination = Province.objects.filter(name=obj.destination_province).first() # بهینه‌سازی: استفاده از context cache برای جلوگیری از N+1 queries
city_destination = City.objects.filter(name=obj.destination_city).first() location_cache = self.context.get('location_cache', {})
if province_destination:
resul.update({ if obj.destination_province:
"provinceLat": province_destination.Lat, cache_key = f"province_{obj.destination_province}"
"provinceLng": province_destination.Lng if cache_key not in location_cache:
}) province_destination = Province.objects.filter(name=obj.destination_province).only('Lat', 'Lng').first()
if city_destination: if province_destination:
resul.update({ location_cache[cache_key] = {
"cityLat": city_destination.Lat, "provinceLat": province_destination.Lat,
"cityLng": city_destination.Lng "provinceLng": province_destination.Lng
}) }
else:
location_cache[cache_key] = {}
resul.update(location_cache.get(cache_key, {}))
if obj.destination_city:
cache_key = f"city_{obj.destination_city}"
if cache_key not in location_cache:
city_destination = City.objects.filter(name=obj.destination_city).only('Lat', 'Lng').first()
if city_destination:
location_cache[cache_key] = {
"cityLat": city_destination.Lat,
"cityLng": city_destination.Lng
}
else:
location_cache[cache_key] = {}
resul.update(location_cache.get(cache_key, {}))
return resul return resul

View File

@@ -26,7 +26,7 @@ from app.filtersets import PoultryFilterSet, PoultryHatchingFilterSet, Transport
PoultryInfoFilterSet, HatchingCalculationsFilterSet, HatchingsFilterSet, TransportingDetailFilterSet, \ PoultryInfoFilterSet, HatchingCalculationsFilterSet, HatchingsFilterSet, TransportingDetailFilterSet, \
KillHouseFilterSet, TransportingDetailCustomFilterSet, CustomHatchingsFilterSet, TransportCarcassDetailFilterSet, \ KillHouseFilterSet, TransportingDetailCustomFilterSet, CustomHatchingsFilterSet, TransportCarcassDetailFilterSet, \
DriverFilterSet, GuildsFilterSet, AllProductsTransportFilterSet DriverFilterSet, GuildsFilterSet, AllProductsTransportFilterSet
from app.helper import SSLAdapter, get_hatching_permit_code from app.helper import SSLAdapter, get_hatching_permit_code, normalize_persian_arabic_text
from app.models import Poultry, PoultryHatching, TransportingChickenDetail, Hatching, TransportingDetail, KillHouse, \ from app.models import Poultry, PoultryHatching, TransportingChickenDetail, Hatching, TransportingDetail, KillHouse, \
ApkInfo, TransportCarcassDetail, Guilds, Driver, InquiryCredentials, AllProductsTransport, EvacuationDetail, \ ApkInfo, TransportCarcassDetail, Guilds, Driver, InquiryCredentials, AllProductsTransport, EvacuationDetail, \
RasadyarAppInfo RasadyarAppInfo
@@ -4522,10 +4522,7 @@ class AllProductsTransportViewSet(viewsets.ModelViewSet):
filters['product'] = product_type filters['product'] = product_type
if destination_province and destination_province != 'undefined': if destination_province and destination_province != 'undefined':
if destination_province == 'مرکزی': filters['destination_province'] = normalize_persian_arabic_text(destination_province)
filters['destination_province'] = 'مركزي'
else:
filters['destination_province'] = destination_province
if date1 and date2 and date1 != 'undefined' and date2 != 'undefined': if date1 and date2 and date1 != 'undefined' and date2 != 'undefined':
try: try:
@@ -4536,23 +4533,33 @@ class AllProductsTransportViewSet(viewsets.ModelViewSet):
except ValueError: except ValueError:
pass pass
transports = AllProductsTransport.objects.filter(**filters).order_by('-date', '-create_date') # بهینه‌سازی: استفاده از select_related برای جلوگیری از N+1 queries
# و اعمال order_by فقط یک بار
transports = AllProductsTransport.objects.select_related('hatching').filter(**filters)
if search and search != 'undefined' and search.strip(): if search and search != 'undefined' and search.strip():
transports = transports.filter( transports = transports.filter(
build_query(self.filterset_class.Meta.fields, search) build_query(self.filterset_class.Meta.fields, search)
) )
# اعمال order_by فقط یک بار در آخر
transports = transports.order_by('-date', '-create_date')
page_size = request.query_params.get('page_size', None) page_size = request.query_params.get('page_size', None)
if page_size: if page_size:
self.pagination_class.page_size = int(page_size) self.pagination_class.page_size = int(page_size)
page = self.paginate_queryset(transports.order_by('-date', '-create_date')) page = self.paginate_queryset(transports)
# بهینه‌سازی: ایجاد cache برای Province و City در context برای جلوگیری از N+1 queries
location_cache = {}
context = self.get_serializer_context()
context['location_cache'] = location_cache
if page is not None: if page is not None:
serializer = self.get_serializer(page, many=True) serializer = self.get_serializer(page, many=True, context=context)
return self.get_paginated_response(serializer.data) return self.get_paginated_response(serializer.data)
serializer = self.serializer_class(transports.order_by('-date', '-create_date'), many=True) serializer = self.serializer_class(transports, many=True, context=context)
return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.data, status=status.HTTP_200_OK)
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
@@ -4755,10 +4762,7 @@ class AllProductsTransportDashboardView(APIView):
destination_province = request.GET.get('destination_province') destination_province = request.GET.get('destination_province')
kill_house_filterset_class = AllProductsTransportFilterSet kill_house_filterset_class = AllProductsTransportFilterSet
if destination_province and destination_province != 'undefined': if destination_province and destination_province != 'undefined':
if destination_province == 'مرکزی': filters['destination_province'] = normalize_persian_arabic_text(destination_province)
filters['destination_province'] = 'مركزي'
else:
filters['destination_province'] = destination_province
queryset = AllProductsTransport.objects.filter(**filters) queryset = AllProductsTransport.objects.filter(**filters)
if search and search != 'undefined' and search.strip(): if search and search != 'undefined' and search.strip():
@@ -4785,10 +4789,7 @@ class AllProductsTransportDashboardView(APIView):
# bars = queryset.filter(jihadi_origin__in=kill_house_codes) # bars = queryset.filter(jihadi_origin__in=kill_house_codes)
if destination_province and destination_province != 'undefined': if destination_province and destination_province != 'undefined':
if destination_province == 'مرکزی': filters_query['destination_province'] = normalize_persian_arabic_text(destination_province)
filters_query['destination_province'] = 'مركزي'
else:
filters_query['destination_province'] = destination_province
bars = queryset.filter(**filters_query) bars = queryset.filter(**filters_query)
aggregation = bars.aggregate( aggregation = bars.aggregate(
total=Sum('quantity'), total=Sum('quantity'),
@@ -4850,10 +4851,7 @@ class AllProductsTransportDashboardView(APIView):
).order_by('-modify_date') ).order_by('-modify_date')
if destination_province and destination_province != 'undefined': if destination_province and destination_province != 'undefined':
if destination_province == 'مرکزی': filters_query['destination_province'] = normalize_persian_arabic_text(destination_province)
filters_query['destination_province'] = 'مركزي'
else:
filters_query['destination_province'] = destination_province
bars = bars(**filters_query) bars = bars(**filters_query)
aggregation = bars.aggregate( aggregation = bars.aggregate(
total=Sum('quantity'), total=Sum('quantity'),