import type { Well } from '../types' import { getTemplateData } from './templateEnvelopes' import { validateQpcrPlateData } from './templateValidators' import type { QpcrColumnsAdapterResult, QpcrDataFrameAdapterResult, QpcrPlateTemplate, QpcrPlateTemplateData, QpcrRowsAdapterResult, QpcrWellMapAdapterResult, } from './types' /** @deprecated Scheduled for removal from the core SDK in MINT 1.4; the biology templates move to a dedicated package or the owning plugins. */ export function toQpcrRows( template: QpcrPlateTemplate | QpcrPlateTemplateData ): QpcrRowsAdapterResult { const data = getTemplateData(template, 'qpcr-plate') validateQpcrPlateData(data) const samples = new Map(data.samples.map(sample => [sample.sampleId, sample])) const targets = new Map(data.targets.map(target => [target.id, target])) return data.reactions.map(reaction => { const sample = reaction.sampleId ? samples.get(reaction.sampleId) : undefined const target = targets.get(reaction.targetId) return { id: reaction.id, wellId: reaction.wellId, sampleId: reaction.sampleId, sample: sample?.name ?? reaction.sampleId ?? reaction.controlKind, group: sample?.group, targetId: reaction.targetId, target: target?.name ?? reaction.targetId, replicate: reaction.replicate, controlKind: reaction.controlKind, cq: reaction.cq ?? null, quantity: reaction.quantity ?? null, flags: reaction.flags.join(', '), instrument: data.instrument, chemistry: data.chemistry, ...(reaction.metadata ?? {}), } }) } /** @deprecated Scheduled for removal from the core SDK in MINT 1.4; the biology templates move to a dedicated package or the owning plugins. */ export function toQpcrColumns(): QpcrColumnsAdapterResult { return [ { key: 'wellId', label: 'Well', sortable: true }, { key: 'sample', label: 'Sample', sortable: true }, { key: 'group', label: 'Group', sortable: true }, { key: 'target', label: 'Target', sortable: true }, { key: 'replicate', label: 'Replicate', sortable: true, align: 'right' }, { key: 'controlKind', label: 'Control', sortable: true }, { key: 'cq', label: 'Cq', sortable: true, align: 'right' }, { key: 'quantity', label: 'Quantity', sortable: true, align: 'right' }, { key: 'flags', label: 'Flags' }, ] } /** @deprecated Scheduled for removal from the core SDK in MINT 1.4; the biology templates move to a dedicated package or the owning plugins. */ export function toQpcrDataFrame( template: QpcrPlateTemplate | QpcrPlateTemplateData ): QpcrDataFrameAdapterResult { return { data: toQpcrRows(template), columns: toQpcrColumns(), rowKey: 'id', } } /** @deprecated Scheduled for removal from the core SDK in MINT 1.4; the biology templates move to a dedicated package or the owning plugins. */ export function toQpcrWellPlateWells( template: QpcrPlateTemplate | QpcrPlateTemplateData ): QpcrWellMapAdapterResult { const data = getTemplateData(template, 'qpcr-plate') validateQpcrPlateData(data) return Object.fromEntries(data.reactions.map(reaction => { const [row, col] = wellPositionFromId(reaction.wellId) return [ reaction.wellId, { id: reaction.wellId, row, col, state: 'filled' as const, sampleType: reaction.sampleId ?? reaction.controlKind, value: reaction.cq ?? undefined, metadata: { reactionId: reaction.id, sampleId: reaction.sampleId, targetId: reaction.targetId, replicate: reaction.replicate, controlKind: reaction.controlKind, }, } satisfies Well, ] })) } function wellPositionFromId(wellId: string): [number, number] { const match = /^([A-Z]+)([1-9][0-9]*)$/.exec(wellId.toUpperCase()) if (!match) { throw new Error(`Invalid well id '${wellId}'.`) } const row = [...match[1]].reduce((value, char) => value * 26 + char.charCodeAt(0) - 64, 0) - 1 const col = Number(match[2]) - 1 return [row, col] }