diff --git a/src/ocr-extract.js b/src/ocr-extract.js index 6a377ab..1572585 100644 --- a/src/ocr-extract.js +++ b/src/ocr-extract.js @@ -42,16 +42,33 @@ export function extractFields(rawText) { matchKnown(raw, KNOWN.formFactor), ); - // Teilenummer: der laengste Kandidat, der nicht der Seriennummer entspricht. + // Teilenummer: unter den Kandidaten mit Bindestrich der laengste; + // bei gleicher Laenge der zuerst vorkommende. Echte Hersteller- + // Teilenummern sind laenger als die kurzen Codes, die sonst auf + // Etiketten stehen. PART_NUMBER_PATTERN.lastIndex = 0; const candidates = [...text.matchAll(PART_NUMBER_PATTERN)].map((m) => m[1]); const withDash = candidates.filter((c) => c.includes('-')); - spec.partNumber = withDash[0] ?? null; + spec.partNumber = + withDash.length === 0 + ? null + : withDash.reduce((longest, current) => + current.length > longest.length ? current : longest, + ); - // Datumscode: vierstellige Zahl, die fuer sich allein steht. + // Datumscode: vierstellige Zahl der Form Jahr-Woche (JJWW), die fuer + // sich allein steht. Nur Kandidaten mit plausiblem Jahr (10-39) und + // gueltiger Kalenderwoche (01-53) kommen infrage; gibt es mehrere, + // gewinnt der zuletzt vorkommende, weil der Datumscode auf diesen + // Etiketten am Ende steht. DATE_CODE_PATTERN.lastIndex = 0; - const dateMatch = [...text.matchAll(DATE_CODE_PATTERN)].map((m) => m[1]); - spec.dateCode = dateMatch[0] ?? null; + const dateCandidates = [...text.matchAll(DATE_CODE_PATTERN)].map((m) => m[1]); + const plausibleDates = dateCandidates.filter((raw) => { + const year = Number(raw.slice(0, 2)); + const week = Number(raw.slice(2, 4)); + return year >= 10 && year <= 39 && week >= 1 && week <= 53; + }); + spec.dateCode = plausibleDates.length === 0 ? null : plausibleDates[plausibleDates.length - 1]; return spec; } diff --git a/test/ocr-extract.test.js b/test/ocr-extract.test.js index bf1d552..e50bb1b 100644 --- a/test/ocr-extract.test.js +++ b/test/ocr-extract.test.js @@ -52,3 +52,20 @@ test('leerer Text liefert ein leeres Spec', () => { assert.equal(spec.capacityGb, null); assert.equal(spec.partNumber, null); }); + +test('waehlt bei der Teilenummer den laengsten Bindestrich-Kandidaten, nicht den ersten', () => { + const spec = extractFields( + 'SAMSUNG\nAB1234SN-001\n64GB 4DRx4 PC4-2400\nM386A8K40BM1-CRC4Y\n1908', + ); + assert.equal(spec.partNumber, 'M386A8K40BM1-CRC4Y'); +}); + +test('verwirft den Datumscode, wenn mehrere vierstellige Bloecke vorkommen und nur einer plausibel ist', () => { + const spec = extractFields('SAMSUNG\n1234 5678\n64GB 2Rx4 PC4-2666\n9012\n1908'); + assert.equal(spec.dateCode, '1908'); +}); + +test('laesst den Datumscode null, wenn kein Kandidat als Jahr-Woche plausibel ist', () => { + const spec = extractFields('SAMSUNG\n9999 0060\n64GB 2Rx4 PC4-2666'); + assert.equal(spec.dateCode, null); +});