ai insight rework #2
This commit is contained in:
1 parent
9d76f54d33
commit
e1987d6999
39 files changed
+2311
-830
No files matched your search
@@ -50,7 +50,9 @@ function kpi(partial: Partial<KPI> & Pick<KPI, 'date' | 'chicken_life'>): KPI {
|
||||
};
|
||||
}
|
||||
|
||||
function counting(partial: Partial<ChickenCounting> & Pick<ChickenCounting, 'date' | 'total_count'>): ChickenCounting {
|
||||
function counting(
|
||||
partial: Partial<ChickenCounting> & Pick<ChickenCounting, 'date' | 'total_count'>
|
||||
): ChickenCounting {
|
||||
return {
|
||||
id: 1,
|
||||
cycle: 1,
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
buildEndCycleRollups,
|
||||
type DashboardInsightMetrics,
|
||||
} from '../insightContextBuilders.ts';
|
||||
import { buildEndCycleRollups, type DashboardInsightMetrics } from '../insightContextBuilders.ts';
|
||||
|
||||
const baseMetrics = (): DashboardInsightMetrics => ({
|
||||
hari_ke: 21,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { scopeScalarsToTimeframe, UNAVAILABLE } from '../insightScope.ts';
|
||||
import { scopeContextToTimeframe, scopeScalarsToTimeframe, UNAVAILABLE } from '../insightScope.ts';
|
||||
|
||||
/**
|
||||
* Real shape of the FCR page's 7-day history for this cycle: the IoT scale
|
||||
@@ -78,3 +78,100 @@ describe('harian periode label starts at day 0', () => {
|
||||
expect(out.fcr_out).toBe(1.3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('replaces writes to both new key and original key for backend', () => {
|
||||
it('writes to fcr_out AND fcr_terakhir when replaces is set', () => {
|
||||
const rows = [
|
||||
{ hari: 30, fcr_aktual: 1.7 },
|
||||
{ hari: 35, fcr_aktual: 1.72 },
|
||||
{ hari: 39, fcr_aktual: 1.68 },
|
||||
{ hari: 48, fcr_aktual: 1.72 },
|
||||
];
|
||||
// First slice the history to day 39, then compute scalars
|
||||
const sliced = scopeContextToTimeframe(
|
||||
{ tren_fcr_harian: rows, fcr_terakhir: 1.72 },
|
||||
'harian',
|
||||
39
|
||||
);
|
||||
const out = scopeScalarsToTimeframe(
|
||||
sliced as Record<string, unknown>,
|
||||
'harian',
|
||||
{
|
||||
historyKey: 'tren_fcr_harian',
|
||||
derive: {
|
||||
fcrTerakhir_rasio: { field: 'fcr_aktual', agg: 'lastValid', replaces: 'fcr_terakhir' },
|
||||
},
|
||||
},
|
||||
39
|
||||
);
|
||||
// New key with unit suffix
|
||||
expect(out.fcrTerakhir_rasio).toBe(1.68);
|
||||
// Original key for backend grading
|
||||
expect(out.fcr_terakhir).toBe(1.68);
|
||||
});
|
||||
|
||||
it('works for EEF page with multiple replaces', () => {
|
||||
const rows = [
|
||||
{ hari: 30, eef: 340, fcr: 1.55, persen_hidup: 96.5, bw_panen: 1800, umur_panen_hari: 30 },
|
||||
{ hari: 35, eef: 350, fcr: 1.52, persen_hidup: 96.2, bw_panen: 2100, umur_panen_hari: 35 },
|
||||
{ hari: 39, eef: 355, fcr: 1.5, persen_hidup: 95.8, bw_panen: 2250, umur_panen_hari: 39 },
|
||||
{ hari: 48, eef: 229, fcr: 1.72, persen_hidup: 90.1, bw_panen: 2380, umur_panen_hari: 48 },
|
||||
];
|
||||
// First slice the history to day 39, then compute scalars
|
||||
const sliced = scopeContextToTimeframe(
|
||||
{
|
||||
tren_eef_harian: rows,
|
||||
eef_terakhir: 229,
|
||||
fcr_terakhir: 1.72,
|
||||
persen_hidup: 90.1,
|
||||
bw_panen_gram: 2380,
|
||||
umur_panen_rata_hari: 48,
|
||||
},
|
||||
'harian',
|
||||
39
|
||||
);
|
||||
const out = scopeScalarsToTimeframe(
|
||||
sliced as Record<string, unknown>,
|
||||
'harian',
|
||||
{
|
||||
historyKey: 'tren_eef_harian',
|
||||
derive: {
|
||||
eefTerakhir_indeksTanpaSatuan: {
|
||||
field: 'eef',
|
||||
agg: 'lastValid',
|
||||
replaces: 'eef_terakhir',
|
||||
},
|
||||
fcrTerakhir_rasio: { field: 'fcr', agg: 'lastValid', replaces: 'fcr_terakhir' },
|
||||
persenHidupTerakhir_persen: {
|
||||
field: 'persen_hidup',
|
||||
agg: 'lastValid',
|
||||
replaces: 'persen_hidup',
|
||||
},
|
||||
bobotPanenTerakhir_gram: {
|
||||
field: 'bw_panen',
|
||||
agg: 'lastValid',
|
||||
replaces: 'bw_panen_gram',
|
||||
},
|
||||
umurPanenTerakhir_hari: {
|
||||
field: 'umur_panen_hari',
|
||||
agg: 'lastValid',
|
||||
replaces: 'umur_panen_rata_hari',
|
||||
},
|
||||
},
|
||||
},
|
||||
39
|
||||
);
|
||||
// Verify day 39 values are used, not day 48
|
||||
expect(out.eefTerakhir_indeksTanpaSatuan).toBe(355);
|
||||
expect(out.fcrTerakhir_rasio).toBe(1.5);
|
||||
expect(out.persenHidupTerakhir_persen).toBe(95.8);
|
||||
expect(out.bobotPanenTerakhir_gram).toBe(2250);
|
||||
expect(out.umurPanenTerakhir_hari).toBe(39);
|
||||
// Verify original keys are also updated for backend
|
||||
expect(out.eef_terakhir).toBe(355);
|
||||
expect(out.fcr_terakhir).toBe(1.5);
|
||||
expect(out.persen_hidup).toBe(95.8);
|
||||
expect(out.bw_panen_gram).toBe(2250);
|
||||
expect(out.umur_panen_rata_hari).toBe(39);
|
||||
});
|
||||
});
|
||||
@@ -30,12 +30,7 @@ describe('latestValidRatio', () => {
|
||||
it('returns the last finite value walking backward', () => {
|
||||
expect(
|
||||
latestValidRatio(
|
||||
[
|
||||
{ iotFcr: 1.1 },
|
||||
{ iotFcr: null },
|
||||
{ iotFcr: 1.4 },
|
||||
{ iotFcr: null },
|
||||
],
|
||||
[{ iotFcr: 1.1 }, { iotFcr: null }, { iotFcr: 1.4 }, { iotFcr: null }],
|
||||
'iotFcr'
|
||||
)
|
||||
).toBe(1.4);
|
||||
|
||||
Reference in new issue
Block a user