GEN-3c: unknown allele displays as '-' (storage stays '?'); parser accepts '-' and skips fully-unknown tokens; de.ts hints

This commit is contained in:
2026-06-06 10:45:15 +02:00
parent a57b481a7e
commit 2e20df624f
2 changed files with 674 additions and 667 deletions

View File

@@ -79,12 +79,14 @@ export function wildType(): Genotype {
* The Sls locus is OMITTED when wild-type (sl/sl) so legacy 8-locus strings and * The Sls locus is OMITTED when wild-type (sl/sl) so legacy 8-locus strings and
* the colour catalog stay byte-identical; it only appears for WP/Sls carriers * the colour catalog stay byte-identical; it only appears for WP/Sls carriers
* (e.g. "… spsp rere Slsl"). Round-trips: a missing Sls re-parses to sl/sl. * (e.g. "… spsp rere Slsl"). Round-trips: a missing Sls re-parses to sl/sl.
* GEN-3c: unknown alleles are STORED as '?' but DISPLAYED as '-' (breeder
* convention) — e.g. ['C','?'] renders "C-".
*/ */
export function toDisplayString(g: Genotype): string { export function toDisplayString(g: Genotype): string {
return LOCUS_ORDER.filter( return LOCUS_ORDER.filter(
(locus) => locus !== 'Sls' || !(g.Sls[0] === 'sl' && g.Sls[1] === 'sl'), (locus) => locus !== 'Sls' || !(g.Sls[0] === 'sl' && g.Sls[1] === 'sl'),
) )
.map((locus) => g[locus][0] + g[locus][1]) .map((locus) => (g[locus][0] + g[locus][1]).replace(/\?/g, '-'))
.join(' ') .join(' ')
} }
@@ -144,6 +146,9 @@ function normalizeToken(tok: string): string | null {
if (t === 'WP') t = 'Slsl' if (t === 'WP') t = 'Slsl'
t = t.replace(/S\(l\)/g, 'Sl').replace(/s\(l\)/g, 'sl') t = t.replace(/S\(l\)/g, 'Sl').replace(/s\(l\)/g, 'sl')
t = t.replace(/Uw/g, 'G').replace(/uw/g, 'g') t = t.replace(/Uw/g, 'G').replace(/uw/g, 'g')
// GEN-3c: '-' is the breeder's UNKNOWN marker on input; store internally as '?'
// (the frozen storage contract keeps '?'; only DISPLAY renders '-').
t = t.replace(/-/g, '?')
return t return t
} }
@@ -193,7 +198,9 @@ export function fromDisplayString(input: string): Genotype {
const [a, b] = splitToken(token) const [a, b] = splitToken(token)
const refAllele = a === WILDCARD ? b : a const refAllele = a === WILDCARD ? b : a
if (refAllele === WILDCARD) { if (refAllele === WILDCARD) {
throw new Error(`Token "${token}" is fully unknown; cannot infer its locus`) // Fully-unknown token ("--"/"??", e.g. a positional placeholder): the locus
// can't be inferred — skip it (defaults to wild-type), don't throw.
continue
} }
const locus = ALLELE_TO_LOCUS[refAllele] const locus = ALLELE_TO_LOCUS[refAllele]
if (!locus) throw new Error(`Unknown allele "${refAllele}" in token "${token}"`) if (!locus) throw new Error(`Unknown allele "${refAllele}" in token "${token}"`)

View File

@@ -105,7 +105,7 @@ export const de = {
editTitle: 'Rennmaus bearbeiten', editTitle: 'Rennmaus bearbeiten',
none: '— keine Angabe —', none: '— keine Angabe —',
genotypeHint: genotypeHint:
'Optional. Format z. B. „Aa CC Dd EE GG Pp Spsp rere“. Unbekannte Allele als „?“.', 'Optional. Format z. B. „Aa CC Dd EE GG Pp Spsp rere“. Unbekannte Allele als „-“.',
save: 'Speichern', save: 'Speichern',
cancel: 'Abbrechen', cancel: 'Abbrechen',
saving: 'Speichern …', saving: 'Speichern …',
@@ -217,7 +217,7 @@ export const de = {
pickAnimalNoGenotype: 'Für dieses Tier ist kein Genotyp hinterlegt bitte unten eingeben.', pickAnimalNoGenotype: 'Für dieses Tier ist kein Genotyp hinterlegt bitte unten eingeben.',
clearAnimal: 'Auswahl entfernen', clearAnimal: 'Auswahl entfernen',
genotypeLabel: 'Genotyp', genotypeLabel: 'Genotyp',
genotypeHint: 'Format z. B. „Aa CC Dd EE GG Pp Spsp rere“. Unbekannte Allele als „?“.', genotypeHint: 'Format z. B. „Aa CC Dd EE GG Pp Spsp rere“. Unbekannte Allele als „-“.',
genotypeInvalid: 'Der Genotyp ist ungültig.', genotypeInvalid: 'Der Genotyp ist ungültig.',
genotypePreview: 'Farbschlag', genotypePreview: 'Farbschlag',
run: 'Probeverpaarung berechnen', run: 'Probeverpaarung berechnen',