kpi configuration (still prototype some data still use manual need to update later)
This commit is contained in:
1 parent
e4dbfd0252
commit
ed84d989b7
12 files changed
+547
-89
No files matched your search
@@ -0,0 +1,118 @@
|
||||
"""Refresh daily KPI rows from ops sources and compute FCR / EEF."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
|
||||
from apps.farms.models import Cycle
|
||||
from apps.operations.models import ChickenCounting, ChickenWeight, FeedSacks, KPI, ManualInput
|
||||
from apps.operations.services.kpi_formulas import (
|
||||
compute_average_harvest_day,
|
||||
compute_average_harvest_weight,
|
||||
compute_chicken_life,
|
||||
compute_chicken_life_percentage,
|
||||
compute_eef,
|
||||
compute_fcr,
|
||||
compute_stock_akhir,
|
||||
)
|
||||
from apps.operations.services.visibility import visible_through_date
|
||||
|
||||
|
||||
def refresh_kpi_source_fields(cycle: Cycle, *, through: date | None = None) -> int:
|
||||
"""
|
||||
Upsert one KPI row per day that has cc + cw + fs + manual.
|
||||
|
||||
Returns the number of KPI rows written.
|
||||
"""
|
||||
cutoff = through if through is not None else visible_through_date()
|
||||
start = cycle.start_date
|
||||
if cycle.end_date and cycle.end_date < cutoff:
|
||||
cutoff = cycle.end_date
|
||||
|
||||
manuals = {
|
||||
row.date: row
|
||||
for row in ManualInput.objects.filter(cycle=cycle, date__gte=start, date__lte=cutoff).order_by(
|
||||
"date", "pk"
|
||||
)
|
||||
}
|
||||
countings = {
|
||||
row.date: row
|
||||
for row in ChickenCounting.objects.filter(cycle=cycle, date__gte=start, date__lte=cutoff)
|
||||
}
|
||||
weights = {
|
||||
row.date: row
|
||||
for row in ChickenWeight.objects.filter(cycle=cycle, date__gte=start, date__lte=cutoff)
|
||||
}
|
||||
feeds = {
|
||||
row.date: row
|
||||
for row in FeedSacks.objects.filter(cycle=cycle, date__gte=start, date__lte=cutoff)
|
||||
}
|
||||
|
||||
written = 0
|
||||
harvest_age_sum = 0.0
|
||||
|
||||
for day in sorted(set(manuals) | set(countings) | set(weights) | set(feeds)):
|
||||
if day < start or day > cutoff:
|
||||
continue
|
||||
manual = manuals.get(day)
|
||||
cc = countings.get(day)
|
||||
cw = weights.get(day)
|
||||
fs = feeds.get(day)
|
||||
if not all([manual, cc, cw, fs]):
|
||||
# Still accumulate harvest age sum from manual-only days so later
|
||||
# complete days keep a correct running Σ(age×harvest).
|
||||
if manual is not None:
|
||||
age = (day - start).days
|
||||
harvest_age_sum += age * (manual.harvest_manual or 0)
|
||||
continue
|
||||
|
||||
age = (day - start).days
|
||||
harvest_today = manual.harvest_manual or 0
|
||||
harvest_age_sum += age * harvest_today
|
||||
|
||||
mortality_total = manual.mortality_manual_total or 0
|
||||
harvest_total = manual.harvest_manual_total or 0
|
||||
harvest_weight_total_g = float(manual.harvest_weight_manual_total or 0.0)
|
||||
feed_total = fs.feed_use_total or 0
|
||||
iot_weight = float(cw.average_weight or 0.0)
|
||||
|
||||
chicken_life = compute_chicken_life(cycle.doc_in_count, mortality_total)
|
||||
life_pct = compute_chicken_life_percentage(cycle.doc_in_count, chicken_life)
|
||||
stock_akhir = compute_stock_akhir(chicken_life, harvest_total)
|
||||
|
||||
avg_bw = compute_average_harvest_weight(
|
||||
harvest_weight_total_g, stock_akhir, iot_weight, harvest_total
|
||||
)
|
||||
avg_day = compute_average_harvest_day(harvest_age_sum, age, stock_akhir, harvest_total)
|
||||
fcr = compute_fcr(feed_total, stock_akhir, iot_weight, harvest_weight_total_g)
|
||||
eef = compute_eef(life_pct, avg_bw, avg_day, fcr)
|
||||
|
||||
KPI.objects.update_or_create(
|
||||
cycle=cycle,
|
||||
date=day,
|
||||
defaults={
|
||||
"age": age,
|
||||
"mortality": manual.mortality_manual or 0,
|
||||
"mortality_total": mortality_total,
|
||||
"feed": fs.feed_use_today or 0,
|
||||
"feed_total": feed_total,
|
||||
"harvest": harvest_today,
|
||||
"harvest_total": harvest_total,
|
||||
"harvest_weight": float(manual.harvest_weight_manual or 0.0),
|
||||
"harvest_weight_total": harvest_weight_total_g,
|
||||
"chicken_life": chicken_life,
|
||||
"chicken_life_percentage": life_pct,
|
||||
"iot_weight": iot_weight,
|
||||
"average_harvest_day": avg_day,
|
||||
"average_harvest_weight": avg_bw,
|
||||
"fcr": fcr,
|
||||
"eef": eef,
|
||||
"cc": cc,
|
||||
"cw": cw,
|
||||
"fs": fs,
|
||||
"manual": manual,
|
||||
},
|
||||
)
|
||||
written += 1
|
||||
|
||||
return written
|
||||
Reference in new issue
Block a user