Files
dashboard/utils/reportAccuracy.ts
T

60 lines
2.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* IoT-vs-manual accuracy for the Dashboard daily report.
*
* Smaller/larger × 100%, matching Hitung Ayam (`DataManagementPage`) and
* Hitung Karung (`FeedDailySummaryTable`). Null means no accuracy / N/A.
*/
const round1 = (value: number): number => Math.round(value * 10) / 10;
const minMaxPercent = (a: number, b: number): number =>
round1((Math.min(a, b) / Math.max(a, b)) * 100);
const isPresentNumber = (value: number | null | undefined): value is number =>
typeof value === 'number' && Number.isFinite(value);
/**
* Counting: no IoT or after-count ≤ 0 → no accuracy.
* Otherwise min(IoT, after) / max(IoT, after) × 100, 1 decimal.
*/
export function countingAccuracy(
iotCount: number | null | undefined,
afterCount: number
): number | null {
if (!isPresentNumber(iotCount) || afterCount <= 0) return null;
return minMaxPercent(iotCount, afterCount);
}
/**
* Sacks: both 0 (missing counts treated as 0) → 100%.
* Only one side > 0 → N/A. Both > 0 → min/max × 100.
*/
export function sackAccuracy(
manual: number | null | undefined,
iot: number | null | undefined
): number | null {
const manualVal = isPresentNumber(manual) ? manual : 0;
const iotVal = isPresentNumber(iot) ? iot : 0;
if (manualVal === 0 && iotVal === 0) return 100;
if (manualVal === 0 || iotVal === 0) return null;
return minMaxPercent(manualVal, iotVal);
}
/**
* Weight: same smaller/larger rule on average IoT vs average manual.
* Missing or non-positive averages → no accuracy.
*/
export function weightAccuracy(
manualAvg: number | null | undefined,
iotAvg: number | null | undefined
): number | null {
if (!isPresentNumber(manualAvg) || !isPresentNumber(iotAvg)) return null;
if (manualAvg <= 0 || iotAvg <= 0) return null;
return minMaxPercent(manualAvg, iotAvg);
}
/** Mortality column is mati + afkir. Panen is ignored. */
export function combinedMortality(mati: number, afkir: number, _panen?: number): number {
return (mati || 0) + (afkir || 0);
}