fix and adjust main dashboard ai insight
This commit is contained in:
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(
|
||||
|
||||
Reference in new issue
Block a user