| 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; |
| } |
|
|
|
|
|
|