File size: 3,764 Bytes
1a343cd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | import { germplasmRecords } from './lab';
import type { DeckSummary, GermplasmRecord, Rgb, SeedProfile } from './types';
function mean(values: unknown[]): number {
const numeric = values.filter((value): value is number => typeof value === 'number' && Number.isFinite(value));
return numeric.length ? Math.round((numeric.reduce((sum, value) => sum + value, 0) / numeric.length) * 10) / 10 : 0;
}
function increment(target: Record<string, number>, key: string | number): void {
const normalized = String(key);
target[normalized] = (target[normalized] ?? 0) + 1;
}
export function deckSummary(seeds: SeedProfile[]): DeckSummary {
const records = germplasmRecords(seeds);
const byStage: Record<string, number> = {};
const byType: Record<string, number> = {};
const byGenerationRaw: Record<string, number> = {};
const byBudFamily: Record<string, number> = {};
for (const record of records) {
increment(byStage, record.stage);
increment(byType, record.germplasmType);
increment(byGenerationRaw, record.generation);
increment(byBudFamily, record.budFamily);
}
const breedable = records.filter((record) => record.canAttempt && record.stage === 'MATURE');
const growing = records.filter((record) => record.stage !== 'MATURE');
const crosses = records.filter((record) => record.parents.every(Boolean));
const recentCrosses = crosses
.slice(-6)
.reverse()
.map((record) => ({
id: record.germplasmDbId,
name: record.germplasmName,
generation: record.generation,
pedigree: record.pedigree,
budColor: record.budColor as Rgb,
leafColor: record.leafColor as Rgb,
THC: record.THC,
}));
const generationEntries = Object.entries(byGenerationRaw)
.map(([generation, count]) => [`F${generation}`, count] as const)
.sort(([left], [right]) => left.localeCompare(right));
const topBloom = [...records]
.sort((left, right) => right.Bloom - left.Bloom)
.slice(0, 6)
.map((record) => ({
id: record.germplasmDbId,
name: record.germplasmName,
generation: record.generation,
pedigree: record.pedigree,
budColor: record.budColor,
leafColor: record.leafColor,
budPalette: record.budPalette,
Bloom: record.Bloom,
Volatility: record.Volatility,
budPattern: record.budPattern,
colorCount: record.budBloom.colorCount,
hueSpread: record.budBloom.hueSpread,
THC: record.THC,
}));
return {
accessions: records.length,
byStage,
byType: sortRecord(byType),
byGeneration: Object.fromEntries(generationEntries),
byBudFamily: sortRecord(byBudFamily),
maxGeneration: maxGeneration(records),
breedableCount: breedable.length,
growingCount: growing.length,
traitAverages: {
THC: mean(records.map((record) => record.THC)),
CBD: mean(records.map((record) => record.CBD)),
Yield: mean(records.map((record) => record.Yield)),
GrowTime: mean(records.map((record) => record.GrowTime)),
Stability: mean(records.map((record) => record.Stability)),
Volatility: mean(records.map((record) => record.Volatility)),
Bloom: mean(records.map((record) => record.Bloom)),
},
gamut: records.map((record) => record.budColor),
recentCrosses,
topBloom,
breedable: breedable.map((record) => ({
id: record.germplasmDbId,
name: record.germplasmName,
})),
};
}
function sortRecord(record: Record<string, number>): Record<string, number> {
return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
}
function maxGeneration(records: GermplasmRecord[]): number {
return records.length ? Math.max(...records.map((record) => record.generation)) : 0;
}
|