from django.core.management.base import BaseCommand from django.db.models import Sum from apps.farms.models import Cycle from apps.operations.models import ChickenCounting, FeedSacks, KPI from apps.operations.services.visibility import visible_through_date class Command(BaseCommand): help = "Recompute simple KPI rollups from ops tables for active cycles" def handle(self, *args, **options): cutoff = visible_through_date() count = 0 for cycle in Cycle.objects.filter(status=Cycle.STATUS_ACTIVE): def visible(model): return model.objects.filter(cycle=cycle, date__lte=cutoff) counting = visible(ChickenCounting).aggregate( mortality=Sum("mortality_count"), life=Sum("total_count"), ) feed_sack = visible(FeedSacks).order_by("-date", "-pk").first() latest_cc = visible(ChickenCounting).order_by("-date").first() latest_cw = None latest_fs = feed_sack latest_mi = None from apps.operations.models import ChickenWeight, ManualInput latest_cw = visible(ChickenWeight).order_by("-date").first() latest_mi = visible(ManualInput).order_by("-date").first() if not all([latest_cc, latest_cw, latest_fs, latest_mi]): self.stdout.write(f"cycle={cycle.pk} skipped (missing source records)") continue kpi, _ = KPI.objects.update_or_create( cycle=cycle, date=latest_cc.date, defaults={ "cc": latest_cc, "cw": latest_cw, "fs": latest_fs, "manual": latest_mi, "mortality_total": counting["mortality"] or 0, "chicken_life": counting["life"] or cycle.doc_in_count, "feed_total": feed_sack.feed_use_total if feed_sack else 0, "chicken_life_percentage": ( ((counting["life"] or cycle.doc_in_count) / cycle.doc_in_count * 100.0) if cycle.doc_in_count else 0.0 ), }, ) count += 1 self.stdout.write(f"KPI {kpi.pk} for cycle {cycle.pk}") self.stdout.write(self.style.SUCCESS(f"Recomputed {count} KPI row(s)"))