fix and adjust main dashboard ai insight

This commit is contained in:
Alberto-Audrix committed 2026-09-17 14:27:08 +07:00
1 parent 18790ab8f6
commit 68e0d1a0bb
27 files changed
+360 -107

No files matched your search

@@ -393,6 +393,9 @@ def analyze_mortality(mortality_rate_pct: Any, day_age: Any) -> dict[str, Any]:
return {
"status": "unknown",
"direction": "unknown",
"severity_label": None,
"actual_pct": None,
"dilarang_kata": [],
"stdMortality": std_mortality,
"message": "Data mortalitas tidak tersedia.",
}
@@ -400,29 +403,55 @@ def analyze_mortality(mortality_rate_pct: Any, day_age: Any) -> dict[str, Any]:
if rate > MORTALITY_THRESHOLDS["critical_pct"]:
status = "critical"
direction = "di_atas_standar"
severity_label = "SANGAT TINGGI"
dilarang = ["rendah", "normal", "baik", "aman", "terkendali", "sedikit"]
message = (
f"Mortalitas kumulatif {rate:.2f}% melebihi batas kritis CP 707 (>7%). "
"Lakukan nekropsi darurat dan perketat biosekuriti."
f"Mortalitas kumulatif {rate:.2f}% adalah SANGAT TINGGI — melebihi batas kritis "
f"CP 707 (>7%). Lakukan nekropsi darurat dan perketat biosekuriti."
)
elif rate >= MORTALITY_THRESHOLDS["warning_pct"]:
status = "warning"
direction = "di_atas_standar"
message = f"Mortalitas kumulatif {rate:.2f}% perlu diwaspadai. Standar CP 707 <5%."
severity_label = "TINGGI"
dilarang = ["rendah", "normal", "baik", "aman", "terkendali", "sedikit"]
message = (
f"Mortalitas kumulatif {rate:.2f}% adalah TINGGI (warning). "
"Standar CP 707 <5%."
)
elif rate >= MORTALITY_THRESHOLDS["normal_pct"]:
# 5% inclusive through just under warning threshold — still not "rendah"
status = "warning"
direction = "di_atas_standar"
severity_label = "TINGGI"
dilarang = ["rendah", "normal", "baik", "aman", "terkendali", "sedikit"]
message = (
f"Mortalitas kumulatif {rate:.2f}% adalah TINGGI. "
"Standar CP 707 <5%."
)
elif std_mortality is not None and rate > std_mortality * 1.5:
status = "warning"
direction = "di_atas_standar"
severity_label = "TINGGI"
dilarang = ["rendah", "normal", "baik", "aman", "terkendali"]
message = (
f"Mortalitas {rate:.2f}% melebihi standar kumulatif harian CP 707 "
f"Mortalitas kumulatif {rate:.2f}% adalah TINGGI relatif standar harian CP 707 "
f"({std_mortality:.2f}%) pada hari ke-{day_age}."
)
else:
status = "ok"
direction = "sesuai_standar" if (std_mortality is None or rate <= std_mortality) else "di_atas_standar"
message = f"Mortalitas {rate:.2f}% dalam batas normal CP 707 (<5%)."
severity_label = "normal/rendah"
dilarang = ["SANGAT TINGGI", "kritis"]
message = (
f"Mortalitas kumulatif {rate:.2f}% normal/rendah — dalam batas CP 707 (<5%)."
)
return {
"status": status,
"direction": direction,
"severity_label": severity_label,
"actual_pct": rate,
"dilarang_kata": dilarang,
"stdMortality": std_mortality,
"message": message,
}
@@ -615,6 +644,9 @@ ATURAN WAJIB saat membandingkan dengan standar:
tersedia di buku untuk hari ini" — jangan mengganti dengan angka lain.
- Semua angka aktual harus berasal dari [Data Halaman (JSON)]. Dilarang
menghitung sendiri atau mengarang angka yang tidak ada di sana.
- Angka di blok ini adalah STANDAR saja (bukan nilai aktual kandang).
Jika bobot/mortalitas aktual tidak ada di Data Halaman / GRADED FACTS,
tulis "data tidak tersedia" — JANGAN menyalin angka standar sebagai aktual.
═══════════════════════════════════════════════"""
@@ -653,6 +685,8 @@ def _resolve_day_age(context_pack: dict[str, Any]) -> int | None:
def _resolve_bw_grams(context_pack: dict[str, Any]) -> float | None:
"""Bobot dalam gram. Field `_kg` hanya untuk legacy payload (dikonversi ×1000)."""
grams = _first_number(
context_pack.get("bobot_avg_gram"),
context_pack.get("averageWeight"),
context_pack.get("bobot_rata_rata_gram"),
context_pack.get("bobot_iot_gram_terakhir"),
context_pack.get("bobot_iot_gram"),
@@ -673,14 +707,34 @@ def _resolve_bw_grams(context_pack: dict[str, Any]) -> float | None:
def _resolve_mortality(context_pack: dict[str, Any]) -> float | None:
return _first_number(
"""Cumulative mortality % from explicit fields, % hidup, or kematian/DOC."""
direct = _first_number(
context_pack.get("mortalitas_kumulatif_pct"),
context_pack.get("mortalitas_kumulatif_persen"),
context_pack.get("mortalitas_persen"),
context_pack.get("mortalityRate"),
context_pack.get("mortality_rate_pct"),
_nested_get(context_pack, "chickenCounting", "dashboardSummary", "mortalityRate"),
_nested_get(context_pack, "chickenCounting", "mortality", "mortalityCount"),
)
if direct is not None:
return direct
persen_hidup = _first_number(context_pack.get("persen_hidup"))
if persen_hidup is not None:
return round((100.0 - persen_hidup) * 100) / 100
death = _first_number(
context_pack.get("kematian_kumulatif_ekor"),
context_pack.get("mortalitas_kumulatif_ekor"),
)
doc = _first_number(
context_pack.get("doc_in_ekor"),
context_pack.get("doc_in"),
context_pack.get("initialPopulation"),
)
if death is not None and doc is not None and doc > 0:
return round((death / doc) * 10000) / 100
return None
def _resolve_iot(context_pack: dict[str, Any]) -> dict[str, Any]:
@@ -702,7 +756,9 @@ def analyze_with_cp707_standards(context_pack: dict[str, Any] | None) -> dict[st
result: dict[str, Any] = {"dayAge": day_age, "analyses": {}}
actual_bw = _resolve_bw_grams(pack)
if actual_bw is not None and day_age is not None:
if day_age is not None:
# Always emit a bw block (unknown if missing) so the model cannot treat
# day-standard BW as the farm's actual weight.
result["analyses"]["bw"] = analyze_bw(actual_bw, day_age)
actual_fcr = _first_number(
@@ -716,8 +772,9 @@ def analyze_with_cp707_standards(context_pack: dict[str, Any] | None) -> dict[st
result["analyses"]["fcr"] = analyze_fcr(actual_fcr, day_age)
mortality_rate = _resolve_mortality(pack)
if mortality_rate is not None:
result["analyses"]["mortality"] = analyze_mortality(mortality_rate, day_age)
# Always emit mortality (unknown if missing) — silent omission caused the LLM
# to narrate STANDAR CP 707 mortality as if it were the farm actual.
result["analyses"]["mortality"] = analyze_mortality(mortality_rate, day_age)
iot = _resolve_iot(pack)
temp = _first_number(