70 lines
2.3 KiB
TypeScript
70 lines
2.3 KiB
TypeScript
import type { DashboardReportEefRow, DashboardReportFcrRow } from '../types/dashboardReport.ts';
|
|
|
|
type ScalesKpiRow = {
|
|
food_total?: number;
|
|
iot_weight?: number;
|
|
chicken_life?: number;
|
|
harvest_weight_total?: number;
|
|
iot_fcr?: number;
|
|
chicken_life_percentage?: number;
|
|
iot_average_harvest_bw?: number;
|
|
iot_average_day_weight?: number;
|
|
iot_eef?: number;
|
|
};
|
|
|
|
const toValidRatio = (value: number | undefined): number | null =>
|
|
value !== undefined && value > 0 ? value : null;
|
|
|
|
const toNullableNumber = (value: number | undefined | null): number | null =>
|
|
value !== undefined && value !== null ? value : null;
|
|
|
|
const isKpiMap = (value: unknown): value is Record<string, ScalesKpiRow> =>
|
|
!!value && typeof value === 'object' && !Array.isArray(value);
|
|
|
|
const kpiDaysInRange = (
|
|
kpi: unknown,
|
|
upToDay: number
|
|
): Array<[number, ScalesKpiRow]> => {
|
|
if (!isKpiMap(kpi)) return [];
|
|
return Object.entries(kpi)
|
|
.map(([dayKey, row]) => [Number(dayKey), row || {}] as [number, ScalesKpiRow])
|
|
.filter(([day]) => Number.isFinite(day) && day >= 0 && day <= upToDay)
|
|
.sort((a, b) => a[0] - b[0]);
|
|
};
|
|
|
|
export function mapKpiToFcrRows(kpi: unknown, upToDay: number): DashboardReportFcrRow[] {
|
|
return kpiDaysInRange(kpi, upToDay).map(([day, row]) => ({
|
|
day,
|
|
feedTotal: row.food_total ?? 0,
|
|
iotWeight: toNullableNumber(row.iot_weight),
|
|
chickenLife: row.chicken_life ?? 0,
|
|
harvestWeightTotal: row.harvest_weight_total ?? 0,
|
|
iotFcr: toValidRatio(row.iot_fcr),
|
|
}));
|
|
}
|
|
|
|
export function mapKpiToEefRows(kpi: unknown, upToDay: number): DashboardReportEefRow[] {
|
|
return kpiDaysInRange(kpi, upToDay).map(([day, row]) => ({
|
|
day,
|
|
chickenLifePercentage: toNullableNumber(row.chicken_life_percentage),
|
|
iotAverageHarvestBw: toNullableNumber(row.iot_average_harvest_bw),
|
|
iotAverageDayWeight: toNullableNumber(row.iot_average_day_weight),
|
|
iotFcr: toValidRatio(row.iot_fcr),
|
|
iotEef: toValidRatio(row.iot_eef),
|
|
}));
|
|
}
|
|
|
|
export function latestValidRatio<T>(
|
|
rows: T[] | null | undefined,
|
|
key: keyof T
|
|
): number | null {
|
|
if (!rows?.length) return null;
|
|
for (let i = rows.length - 1; i >= 0; i--) {
|
|
const value = rows[i]?.[key];
|
|
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
return value;
|
|
}
|
|
}
|
|
return null;
|
|
}
|