import datetime from django.db.models import Sum, Avg, Count, Q from rest_framework import serializers from app.models import Poultry, PoultryHatching, TransportingChickenDetail, Hatching, TransportingDetail, KillHouse, \ ApkInfo, TransportCarcassDetail, Driver, Guilds, InquiryCredentials, AllProductsTransport, EvacuationDetail, \ RasadyarAppInfo from authentication.models import Province, City from helpers import build_calculation class PoultrySerializer(serializers.ModelSerializer): hatching = serializers.SerializerMethodField('get_hatching') class Meta: model = Poultry fields = ['FirstName', 'LastName', 'Mobile', 'UnitName', 'EpidemiologicCode', 'SystemCode', 'TrackingCode', 'UnitIsActiveDescription', 'RegDateShamsi', 'LocationNameProvince', 'LocationNameCity', 'hatching'] def get_hatching(self, instance): hatching = PoultryHatching.objects.filter(poultry=instance) total_amount = build_calculation(queryset=hatching, column_name='BaseHatchingCount', aggregate_func=Sum) EvacuationCount = build_calculation(queryset=hatching, column_name='EvacuationCount', aggregate_func=Sum) LeftOver = build_calculation(queryset=hatching, column_name='LeftOver', aggregate_func=Sum) return { "totalAmount": total_amount, "losses": EvacuationCount, "LeftOver": LeftOver, } # class TransportingForHatchingSerializer(serializers.ModelSerializer): # class Meta: # model = TransportingChickenDetail # fields = '__all__' class PoultryHatchingSerializer(serializers.ModelSerializer): # bars = serializers.SerializerMethodField('get_bars') age = serializers.SerializerMethodField('get_age') class Meta: model = PoultryHatching fields = '__all__' # def get_bars(self, obj): # transports = TransportingChickenDetail.objects.filter(trash=False, hatching=obj) # serializer = TransportingForHatchingSerializer(transports, many=True) # return serializer.data def get_age(self, obj): age = (datetime.datetime.now().date() - obj.Date.date()).days + 1 return age class HatchingsSerializer(serializers.ModelSerializer): class Meta: model = Hatching fields = '__all__' class TransportingChickenDetailSerializer(serializers.ModelSerializer): hatching = PoultryHatchingSerializer(read_only=True) killing_age = serializers.SerializerMethodField('get_killing_age') class Meta: model = TransportingChickenDetail fields = '__all__' def get_killing_age(self, obj): age = (obj.reside_date.date() - obj.hatching.Date.date()).days + 1 return age class HatchingSerializer(serializers.ModelSerializer): class Meta: model = PoultryHatching fields = '__all__' class TransportingSerializer(serializers.ModelSerializer): class Meta: model = TransportingDetail fields = '__all__' class HatchingCalculationSerializer(serializers.ModelSerializer): class Meta: model = PoultryHatching fields = ['EvacuationCount', 'LocationNameCity', 'LocationNameProvince', 'poultry'] class PoultryInfoSerializer(serializers.ModelSerializer): info = serializers.SerializerMethodField('get_info') class Meta: model = Poultry fields = ['key', 'FirstName', 'LastName', 'Mobile', 'EpidemiologicCode', 'UnitId', 'SystemCode', 'UnitName', 'UnitIsActive', 'RegDateShamsi', 'info', 'LocationNameCity', 'LocationNameProvince', 'UserIsActiveDescription', 'Province', 'City'] def get_info(self, obj): hatchings = Hatching.objects.filter(poultry=obj, trash=False) total_hatching = hatchings.aggregate(total=Sum('ChickCountSum'))['total'] or 0 total_leftover = hatchings.aggregate(total=Sum('LeftOver'))['total'] or 0 total_evacuation = hatchings.aggregate(total=Sum('Evacuation'))['total'] or 0 transports = TransportingDetail.objects.filter(hatching__in=hatchings, trash=False) total_killing = transports.aggregate(total=Sum('GoodAmount'))['total'] or 0 total_left_over_percent = round((total_leftover / total_hatching) * 100, 2) if total_hatching > 0 else 0 total_evacuation_percent = round((total_evacuation / total_hatching) * 100, 2) if total_hatching > 0 else 0 total_killing_percent = round((total_killing / total_hatching) * 100, 2) if total_hatching > 0 else 0 return { "count_hatching": len(hatchings), "total_hatching": total_hatching, "total_leftover": total_leftover, "total_evacuation": total_evacuation, "total_left_over_percent": total_left_over_percent, "total_evacuation_percent": total_evacuation_percent, "total_killing_percent": total_killing_percent, "total_killing": total_killing, "cars_count": len(transports), "total_cars": total_killing, "capacity": hatchings.last().CapacityFemale if hatchings else 0, } class PoultryForHatchingDetailSerializer(serializers.ModelSerializer): class Meta: model = Poultry fields = ['City', 'Province', 'FirstName', 'LastName', 'UnitName'] class HatchingDetailSerializer(serializers.ModelSerializer): info = serializers.SerializerMethodField('get_info') poultry = PoultryForHatchingDetailSerializer(read_only=True) class Meta: model = Hatching fields = ['Date', 'Age', 'KillingAve', 'info', 'CertId', 'RequestCode', 'ChickCountSum', 'Period', 'CapacityFemale', 'LeftOver', 'Evacuation', 'poultry', 'PedigreeName'] def get_info(self, obj): capacity = obj.CapacityFemale if obj.CapacityFemale else 0 transports = TransportingDetail.objects.filter(hatching=obj, trash=False) average_slaughter_age = transports.aggregate(avg_age=Avg('Age'))['avg_age'] or 0 load_volume = transports.aggregate(total=Sum('GoodAmount'))['total'] or 0 percent_hatching_license = (capacity / obj.ChickCountSum) * 100 return { "average_slaughter_age": average_slaughter_age, "load_volume": load_volume, "number_loads": len(transports), "percent_hatching_license": round(percent_hatching_license, 2) } class HatchingForUpdateSerializer(serializers.ModelSerializer): poultry = PoultryInfoSerializer(read_only=True) EvacuationDetail = serializers.SerializerMethodField() class Meta: model = Hatching fields = '__all__' def get_EvacuationDetail(self, obj): queryset = EvacuationDetail.objects.filter(hatching=obj, trash=False) return EvacuationDetailSerializer(queryset, many=True).data class PoultryDetailSerializerForTransport(serializers.ModelSerializer): class Meta: model = Poultry fields = ['UnitId', 'PartIdCode', 'Province', 'City', 'UnitName', 'Mobile'] class HatchingDetailSerializerForTransport(serializers.ModelSerializer): poultry = PoultryDetailSerializerForTransport(read_only=True) class Meta: model = Hatching fields = ['PartIdCode', 'poultry', 'RequestCode', 'PedigreeName'] class TransportingDetailSerializer(serializers.ModelSerializer): hatching = HatchingDetailSerializerForTransport(read_only=True) location_kill_house = serializers.SerializerMethodField('get_location_kill_house') location_poultry = serializers.SerializerMethodField('get_location_poultry') class Meta: model = TransportingDetail fields = ['TrackingCode', 'hatching', 'ResideDatePersian', 'DesUnitName', 'DesPartIdCode', 'Province', 'City', 'GoodAmount', 'TrackingStatusDescription', 'SourceUnitName', 'Age', 'PedigreeName', 'Date', 'Out', 'location_kill_house', 'location_poultry'] def get_location_kill_house(self, obj): resul = {} province_kill_house = Province.objects.filter(name=obj.Province).first() city_kill_house = City.objects.filter(name=obj.City).first() if province_kill_house: resul.update({ "provinceLat": province_kill_house.Lat, "provinceLng": province_kill_house.Lng }) if city_kill_house: resul.update({ "cityLat": city_kill_house.Lat, "cityLng": city_kill_house.Lng }) return resul def get_location_poultry(self, obj): resul = {} if obj.hatching: province_kill_house = Province.objects.filter(name=obj.hatching.ProvinceName).first() city_kill_house = City.objects.filter(name=obj.hatching.CityName).first() if province_kill_house: resul.update({ "provinceLat": province_kill_house.Lat, "provinceLng": province_kill_house.Lng }) if city_kill_house: resul.update({ "cityLat": city_kill_house.Lat, "cityLng": city_kill_house.Lng }) return resul class KillHouseSerializer(serializers.ModelSerializer): info = serializers.SerializerMethodField('get_info') class Meta: model = KillHouse fields = '__all__' def get_info(self, obj): # Use pre-computed cache if available (optimization to avoid N+1 queries) info_cache = self.context.get('info_cache') if info_cache and obj.PartIdCode in info_cache: return info_cache[obj.PartIdCode] # Fallback to original query-based approach if cache not available request = self.context.get('request') date1 = request.GET.get('date1') or None date2 = request.GET.get('date2') or None bars = TransportingDetail.objects.filter(DesPartIdCode=obj.PartIdCode, trash=False).only('GoodAmount', 'Out', 'Date') if date1: date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date() date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date() bars = bars.filter(Date__date__gte=date1, Date__date__lte=date2) aggregation = bars.aggregate( total=Sum('GoodAmount'), input_total=Sum('GoodAmount', filter=Q(Out=False)), output_total=Sum('GoodAmount', filter=Q(Out=True)), input_count=Count('id', filter=Q(Out=False)), output_count=Count('id', filter=Q(Out=True)), total_count=Count('id') ) all_products = AllProductsTransport.objects.filter( jihadi_origin=obj.PartIdCode, trash=False ).only('quantity', 'out', 'date', 'unloading_date') if date1: all_products = all_products.filter( Q( date__gte=date1, date__lte=date2, date__isnull=False ) | Q( unloading_date__gte=date1, unloading_date__lte=date2, date__isnull=True ) ) all_products_aggregation = all_products.aggregate( total=Sum('quantity'), input_total=Sum('quantity', filter=Q(out=False)), output_total=Sum('quantity', filter=Q(out=True)), input_count=Count('id', filter=Q(out=False)), output_count=Count('id', filter=Q(out=True)), total_count=Count('id') ) aggregation['total'] = (aggregation.get('total') or 0) + (all_products_aggregation.get('total') or 0) aggregation['input_total'] = (aggregation.get('input_total') or 0) + (all_products_aggregation.get('input_total') or 0) aggregation['output_total'] = (aggregation.get('output_total') or 0) + (all_products_aggregation.get('output_total') or 0) aggregation['input_count'] = (aggregation.get('input_count') or 0) + (all_products_aggregation.get('input_count') or 0) aggregation['output_count'] = (aggregation.get('output_count') or 0) + (all_products_aggregation.get('output_count') or 0) aggregation['total_count'] = (aggregation.get('total_count') or 0) + (all_products_aggregation.get('total_count') or 0) total_count = aggregation['total_count'] or 0 total_bars_quantity = aggregation['total'] or 0 total_input_bars_quantity = aggregation['input_total'] or 0 total_output_bars_quantity = aggregation['output_total'] or 0 input_bars_count = aggregation['input_count'] or 0 output_bars_count = aggregation['output_count'] or 0 if total_count > 0: total_input_bars_percent = round((input_bars_count / total_count) * 100, 1) total_output_bars_percent = round((output_bars_count / total_count) * 100, 1) else: total_input_bars_percent = 0 total_output_bars_percent = 0 return { "bars": total_count, "total_bars_quantity": total_bars_quantity, "input_bars": input_bars_count, "total_input_bars_quantity": total_input_bars_quantity, "total_input_bars_percent": total_input_bars_percent, "output_bars": output_bars_count, "total_output_bars_quantity": total_output_bars_quantity, "total_output_bars_percent": total_output_bars_percent, } class TransportingDetailForUpdateSerializer(serializers.ModelSerializer): hatching = HatchingDetailSerializerForTransport(read_only=True) class Meta: model = TransportingDetail fields = '__all__' class HatchingAnalysisSerializer(serializers.ModelSerializer): class Meta: model = Hatching fields = ['CityName', 'ProvinceName', 'PedigreeName'] class HatchingAnalysisSerializerTwo(serializers.ModelSerializer): class Meta: model = Hatching fields = ['CityName', 'ProvinceName'] class TransportingReportDashboard(serializers.ModelSerializer): class Meta: model = TransportingDetail fields = ['age', 'Province', 'City'] class TransportingForClearanceCodeSerializer(serializers.ModelSerializer): hatching = HatchingDetailSerializerForTransport(read_only=True) class Meta: model = TransportingDetail fields = '__all__' class ApkInfoSerializer(serializers.ModelSerializer): class Meta: model = ApkInfo fields = ['key', 'info', 'download_link'] class TransportCarcassDetailSerializer(serializers.ModelSerializer): class Meta: model = TransportCarcassDetail fields = '__all__' class DriverSerializer(serializers.ModelSerializer): class Meta: model = Driver fields = '__all__' class KillHouseForTransportCarcassSerializer(serializers.ModelSerializer): info = serializers.SerializerMethodField('get_info') class Meta: model = KillHouse fields = '__all__' def get_info(self, obj): bars_dict = self.context.get('bars_dict', {}) buy_dict = self.context.get('buy_dict', {}) bars_data = bars_dict.get(obj.PartIdCode, {}) buy_data = buy_dict.get(obj.PartIdCode, {}) total_count = bars_data.get('total_count', 0) or 0 total_bars_quantity = bars_data.get('total', 0) or 0 total_input_bars_quantity = bars_data.get('input_total', 0) or 0 total_output_bars_quantity = bars_data.get('output_total', 0) or 0 input_bars_count = bars_data.get('input_count', 0) or 0 output_bars_count = bars_data.get('output_count', 0) or 0 buy_input_total = buy_data.get('input_total', 0) or 0 buy_output_total = buy_data.get('output_total', 0) or 0 buy_input_count = buy_data.get('input_count', 0) or 0 buy_output_count = buy_data.get('output_count', 0) or 0 if total_count > 0 and (total_input_bars_quantity + total_output_bars_quantity) > 0: total_input_bars_percent = round( (total_input_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1 ) total_output_bars_percent = round( (total_output_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1 ) else: total_input_bars_percent = 0 total_output_bars_percent = 0 return { "role": 'کشتارگاه', "bars": int(total_count), "total_bars_wight": int(total_bars_quantity), "input_bars": int(input_bars_count), "total_input_bars_wight": int(total_input_bars_quantity), "total_input_buy_bars_wight": int(((buy_input_total) * 2.5) * 0.75), "total_input_bars_percent": total_input_bars_percent, "output_bars": int(output_bars_count), "total_output_bars_wight": int(total_output_bars_quantity), "total_output_buy_bars_wight": int(((buy_output_total) * 2.5) * 0.75), "total_output_bars_percent": total_output_bars_percent, "total_ware_house": int(((buy_input_total * 2.5) * 0.75) + ((buy_output_total * 2.5) * 0.75)), "total_input_buy_bars_count": int(buy_input_count), "total_output_buy_bars_count": int(buy_output_count), } class StewardForTransportCarcassSerializer(serializers.ModelSerializer): info = serializers.SerializerMethodField() class Meta: model = Guilds fields = '__all__' def get_info(self, obj): bars_dict = self.context.get('bars_dict', {}) row = bars_dict.get(obj.jihadi_code, {}) total_count = row.get('total_count', 0) or 0 total_bars_quantity = row.get('total', 0) or 0 total_input_bars_quantity = row.get('input_total', 0) or 0 total_output_bars_quantity = row.get('output_total', 0) or 0 input_bars_count = row.get('input_count', 0) or 0 output_bars_count = row.get('output_count', 0) or 0 total_input_buy_bars_wight = row.get('total_input_buy_bars_wight', 0) or 0 total_output_buy_bars_wight = row.get('total_output_buy_bars_wight', 0) or 0 total_input_buy_bars_count = row.get('total_input_buy_bars_count', 0) or 0 total_output_buy_bars_count = row.get('total_output_buy_bars_count', 0) or 0 total_ware_house = total_input_buy_bars_wight + total_output_buy_bars_wight if total_count > 0: total_input_bars_percent = round( (total_input_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1 ) total_output_bars_percent = round( (total_output_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1 ) else: total_input_bars_percent = 0 total_output_bars_percent = 0 return { "role": 'مباشر', "bars": int(total_count), "total_bars_wight": int(total_bars_quantity), "input_bars": int(input_bars_count), "total_input_bars_wight": int(total_input_bars_quantity), "total_input_buy_bars_wight": int(total_input_buy_bars_wight), "total_input_bars_percent": total_input_bars_percent, "output_bars": int(output_bars_count), "total_output_bars_wight": int(total_output_bars_quantity), "total_output_buy_bars_wight": int(total_output_buy_bars_wight), "total_output_bars_percent": total_output_bars_percent, "total_ware_house": int(total_ware_house), "total_input_buy_bars_count": int(total_input_buy_bars_count), "total_output_buy_bars_count": int(total_output_buy_bars_count), } class GuildsForTransportCarcassSerializer(serializers.ModelSerializer): info = serializers.SerializerMethodField('get_info') class Meta: model = Guilds fields = '__all__' def get_info(self, obj): bars_dict = self.context.get('bars_dict', {}) data = bars_dict.get(obj.jihadi_code, None) if not data: return { "role": 'صنف', "total_input_buy_bars_wight": 0, "total_output_buy_bars_wight": 0, "total_ware_house": 0, "total_input_buy_bars_percent": 0, "total_output_buy_bars_percent": 0, "total_input_buy_bars_count": 0, "total_output_buy_bars_count": 0, } total_input = data['total_input_buy_bars_wight'] or 0 total_output = data['total_output_buy_bars_wight'] or 0 total_wh = data['total_ware_house'] or 0 total_count = data['total_count'] or 0 if total_count > 0 and total_wh > 0: total_input_percent = round((total_input / total_wh) * 100, 1) total_output_percent = round((total_output / total_wh) * 100, 1) else: total_input_percent = 0 total_output_percent = 0 return { "role": 'صنف', "total_input_buy_bars_wight": int(total_input), "total_output_buy_bars_wight": int(total_output), "total_ware_house": int(total_wh), "total_input_buy_bars_percent": total_input_percent, "total_output_buy_bars_percent": total_output_percent, "total_input_buy_bars_count": int(data['total_count_input_buy'] or 0), "total_output_buy_bars_count": int(data['total_count_output_buy'] or 0), } class KillHouseForTransportCarcassForRassadyaarSerializer(serializers.ModelSerializer): info = serializers.SerializerMethodField('get_info') class Meta: model = KillHouse fields = '__all__' def get_info(self, obj): bars_dict = self.context.get('bars_dict', {}) buy_dict = self.context.get('buy_dict', {}) bars_data = bars_dict.get(obj.PartIdCode, {}) buy_data = buy_dict.get(obj.PartIdCode, {}) total_count = bars_data.get('total_count', 0) or 0 total_bars_quantity = bars_data.get('total', 0) or 0 total_input_bars_quantity = bars_data.get('input_total', 0) or 0 total_output_bars_quantity = bars_data.get('output_total', 0) or 0 input_bars_count = bars_data.get('input_count', 0) or 0 output_bars_count = bars_data.get('output_count', 0) or 0 buy_input_total = buy_data.get('input_total', 0) or 0 buy_output_total = buy_data.get('output_total', 0) or 0 buy_input_count = buy_data.get('input_count', 0) or 0 buy_output_count = buy_data.get('output_count', 0) or 0 if total_count > 0 and (total_input_bars_quantity + total_output_bars_quantity) > 0: total_input_bars_percent = round( (total_input_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1 ) total_output_bars_percent = round( (total_output_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1 ) else: total_input_bars_percent = 0 total_output_bars_percent = 0 return { "bars": int(total_count), "total_bars_wight": int(total_bars_quantity), "input_bars": int(input_bars_count), "total_input_bars_wight": int(total_input_bars_quantity), "total_input_buy_bars_wight": int(buy_input_total), "total_input_bars_percent": total_input_bars_percent, "output_bars": int(output_bars_count), "total_output_bars_wight": int(total_output_bars_quantity), "total_output_buy_bars_wight": int(buy_output_total), "total_output_bars_percent": total_output_bars_percent, "total_ware_house": int(buy_input_total) + int(buy_output_total), "total_input_buy_bars_count": int(buy_input_count), "total_output_buy_bars_count": int(buy_output_count), } class InquiryCredentialsSerializer(serializers.ModelSerializer): class Meta: model = InquiryCredentials fields = '__all__' class AllProductsTransportSerializer(serializers.ModelSerializer): hatching = HatchingDetailSerializerForTransport(read_only=True) class Meta: model = AllProductsTransport fields = '__all__' class EvacuationDetailSerializer(serializers.ModelSerializer): class Meta: model = EvacuationDetail fields = '__all__' class RasadyarAppInfoSerializer(serializers.ModelSerializer): class Meta: model = RasadyarAppInfo fields = '__all__' class AllProductsTransportCustomSerializer(serializers.ModelSerializer): location_origin = serializers.SerializerMethodField('get_location_origin') location_destination = serializers.SerializerMethodField('get_location_destination') class Meta: model = AllProductsTransport fields = '__all__' def get_location_origin(self, obj): resul = {} # بهینه‌سازی: استفاده از context cache برای جلوگیری از N+1 queries location_cache = self.context.get('location_cache', {}) if obj.origin_province: cache_key = f"province_{obj.origin_province}" if cache_key not in location_cache: province_origin = Province.objects.filter(name=obj.origin_province).only('Lat', 'Lng').first() if province_origin: location_cache[cache_key] = { "provinceLat": province_origin.Lat, "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 def get_location_destination(self, obj): resul = {} # بهینه‌سازی: استفاده از context cache برای جلوگیری از N+1 queries location_cache = self.context.get('location_cache', {}) if obj.destination_province: cache_key = f"province_{obj.destination_province}" if cache_key not in location_cache: province_destination = Province.objects.filter(name=obj.destination_province).only('Lat', 'Lng').first() if province_destination: location_cache[cache_key] = { "provinceLat": province_destination.Lat, "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