from django.db import transaction from rest_framework import serializers from apps.operations.models import ( AIInsight, ChickenCounting, ChickenWeight, FeedSacks, IotPanel, KPI, ManualInput, ) from apps.accounts.serializers import PkAsIdMixin from apps.operations.services.manual_input import recompute_manual_totals class CycleContextMixin(serializers.Serializer): kandang = serializers.IntegerField(source="cycle.kandang_id", read_only=True) flock_id = serializers.SerializerMethodField() def get_flock_id(self, obj): flock = obj.cycle.kandang.flocks.order_by("flock_id").first() return flock.pk if flock is not None else None class KPISerializer(PkAsIdMixin, CycleContextMixin, serializers.ModelSerializer): class Meta: model = KPI fields = "__all__" read_only_fields = ["id", "created_at", "updated_at"] class ManualInputSerializer(PkAsIdMixin, CycleContextMixin, serializers.ModelSerializer): class Meta: model = ManualInput fields = "__all__" read_only_fields = [ "id", "created_at", "updated_at", "mortality_manual_total", "feed_in_manual_total", "feed_use_manual_total", "feed_out_manual_total", "harvest_manual_total", "harvest_weight_manual_total", "average_harvest_day_manual", "average_harvest_weight_manual", "fcr_manual", "eef_manual", ] validators = [] def create(self, validated_data): return self._upsert(validated_data) def update(self, instance, validated_data): validated_data.setdefault("cycle", instance.cycle) validated_data.setdefault("date", instance.date) return self._upsert(validated_data, instance=instance) def _upsert(self, validated_data, instance=None): cycle = validated_data.get("cycle") or (instance.cycle if instance else None) row_date = validated_data.get("date") or (instance.date if instance else None) defaults = { "age_manual": validated_data.get("age_manual", instance.age_manual if instance else 0), "mortality_manual": validated_data.get( "mortality_manual", instance.mortality_manual if instance else 0 ), "feed_in_manual": validated_data.get( "feed_in_manual", instance.feed_in_manual if instance else 0 ), "feed_use_manual": validated_data.get( "feed_use_manual", instance.feed_use_manual if instance else 0 ), "feed_out_manual": validated_data.get( "feed_out_manual", instance.feed_out_manual if instance else 0 ), "harvest_manual": validated_data.get( "harvest_manual", instance.harvest_manual if instance else 0 ), "harvest_weight_manual": validated_data.get( "harvest_weight_manual", instance.harvest_weight_manual if instance else 0 ), "manual_weight": validated_data.get( "manual_weight", instance.manual_weight if instance else 0.0 ), } with transaction.atomic(): row, _created = ManualInput.objects.update_or_create( cycle=cycle, date=row_date, defaults=defaults, ) recompute_manual_totals(cycle) row.refresh_from_db() return row class ChickenCountingSerializer(PkAsIdMixin, CycleContextMixin, serializers.ModelSerializer): class Meta: model = ChickenCounting fields = "__all__" read_only_fields = ["id", "created_at", "updated_at"] class ChickenWeightSerializer(PkAsIdMixin, CycleContextMixin, serializers.ModelSerializer): class Meta: model = ChickenWeight fields = "__all__" read_only_fields = ["id", "created_at", "updated_at"] class FeedSacksSerializer(PkAsIdMixin, CycleContextMixin, serializers.ModelSerializer): class Meta: model = FeedSacks fields = "__all__" read_only_fields = ["id", "created_at", "updated_at"] class IotPanelSerializer(PkAsIdMixin, serializers.ModelSerializer): kandang = serializers.SerializerMethodField() flock_id = serializers.IntegerField(read_only=True) class Meta: model = IotPanel fields = "__all__" read_only_fields = ["id", "created_at", "updated_at"] def get_kandang(self, obj): return obj.flock.kandang_id class AIInsightSerializer(PkAsIdMixin, CycleContextMixin, serializers.ModelSerializer): class Meta: model = AIInsight fields = "__all__" read_only_fields = ["id", "created_at", "updated_at"] def to_representation(self, instance): import json data = super().to_representation(instance) raw_citations = getattr(instance, "citations", "") if isinstance(raw_citations, list): data["citations"] = raw_citations elif isinstance(raw_citations, str) and raw_citations.strip(): try: data["citations"] = json.loads(raw_citations) except Exception: data["citations"] = [] else: data["citations"] = [] return data