@precisa-saude/fhir-ocr-utils 0.38.0 → 0.38.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -17,6 +17,9 @@ var UNAMBIGUOUS_SHORT_NAMES = /* @__PURE__ */ new Set([
17
17
  "alt",
18
18
  "ast",
19
19
  "bun",
20
+ // Creatina quinase: Fleury e a Quest imprimem só "CK", e sem a sigla o
21
+ // modelo ficava sem âncora e encaixava o valor em outro exame (PRE-486).
22
+ "ck",
20
23
  "wbc",
21
24
  "rbc",
22
25
  "mcv",
@@ -119,6 +122,37 @@ var QUALITATIVE_VALUE_TERMS = /* @__PURE__ */ new Set([
119
122
  "undetectable",
120
123
  "yellow"
121
124
  ]);
125
+ var GENETIC_CONTEXT_PATTERNS = [
126
+ /\b[nx][mrpc]_\d{6,}/,
127
+ // RefSeq: NM_000384.2, NP_, NR_, XM_
128
+ /\bens[gtp]\d{6,}/,
129
+ // Ensembl: ENSG00000084674
130
+ /\bp\.[a-z]{3}\d/,
131
+ // HGVS proteína: p.Trp448*
132
+ /\bc\.\d+[acgt]?[>_+-]/,
133
+ // HGVS codificante: c.1234A>G, c.76_78del
134
+ /\brs\d{4,}\b/,
135
+ // dbSNP
136
+ /\bgenes?\b/,
137
+ /\bvariante?s?\b/,
138
+ /\bexons?\b/,
139
+ /\bzygosity\b/,
140
+ /\bzigosidade\b/,
141
+ /\balleles?\b/,
142
+ /\balelos?\b/,
143
+ /\bmutations?\b/,
144
+ /\bmutac(ao|oes)\b/,
145
+ /\bpathogenic/,
146
+ /\bpatogenic/,
147
+ /\bheterozyg/,
148
+ /\bhomozyg/,
149
+ /\bheterozigot/,
150
+ /\bhomozigot/,
151
+ /\bsequence change\b/
152
+ ];
153
+ var SUBTYPE_AFTER = {
154
+ CK: /^[\s-]*mb\b/i
155
+ };
122
156
 
123
157
  // src/body-region.ts
124
158
  var WHOLE_BODY_COMPOSITION_CODES = /* @__PURE__ */ new Set([
@@ -267,19 +301,37 @@ function isSectionName(matched) {
267
301
  return URINALYSIS_SECTION_NAMES.has(matched) || URINALYSIS_SECTION_NAMES.has(matched.replace(/s$/, ""));
268
302
  }
269
303
  var MENTIONS_URINE = /(?<![\p{L}\p{N}])urin[ae](?![\p{L}\p{N}])/u;
304
+ var SEDIMENT_FIELD_UNIT = /\/(?:hpf|lpf)(?![\p{L}\p{N}])/u;
305
+ function hasUrineCue(line) {
306
+ return MENTIONS_URINE.test(line) || SEDIMENT_FIELD_UNIT.test(line);
307
+ }
308
+ function kindOf(line) {
309
+ const text = line.text.trim();
310
+ if (!text) return "blank";
311
+ if (URINALYSIS_HEADER.test(text) && !/\d/.test(text)) return "header";
312
+ if (line.foreign) return "foreign";
313
+ if (line.urine) return "urine";
314
+ return line.known || line.hasValue ? "neutral" : "unknown";
315
+ }
270
316
  function urinalysisLineIndexes(lines) {
317
+ const kinds = lines.map(kindOf);
318
+ const continuesAsUrine = (from) => {
319
+ const next = kinds.findIndex(
320
+ (kind, index) => index > from && (kind === "urine" || kind === "foreign" || kind === "header")
321
+ );
322
+ return next !== -1 && kinds[next] === "urine";
323
+ };
271
324
  const inside = /* @__PURE__ */ new Set();
272
325
  let open = false;
273
- lines.forEach((line, index) => {
274
- const text = line.text.trim();
275
- if (URINALYSIS_HEADER.test(text) && !/\d/.test(text)) {
326
+ kinds.forEach((kind, index) => {
327
+ if (kind === "header") {
276
328
  open = true;
277
329
  return;
278
330
  }
279
- if (!open || !text) {
331
+ if (!open || kind === "blank") {
280
332
  return;
281
333
  }
282
- if (line.foreign || !line.known && !line.hasValue && !MENTIONS_URINE.test(text)) {
334
+ if (kind === "foreign" || kind === "unknown" && !continuesAsUrine(index)) {
283
335
  open = false;
284
336
  return;
285
337
  }
@@ -323,7 +375,7 @@ function applyUrinalysisSection(normalizedText, candidates, deps) {
323
375
  const known = deps.patterns.some(
324
376
  ({ name, regex }) => !matchesIn(text, name, regex).next().done
325
377
  );
326
- return { foreign: false, hasValue: deps.hasValue(text), known, text };
378
+ return { foreign: false, hasValue: deps.hasValue(text), known, text, urine: hasUrineCue(text) };
327
379
  });
328
380
  for (const candidate of resolved) {
329
381
  let index = lineStarts.length - 1;
@@ -331,7 +383,9 @@ function applyUrinalysisSection(normalizedText, candidates, deps) {
331
383
  const line = lines[index];
332
384
  line.known = true;
333
385
  const matched = normalizedText.slice(candidate.start, candidate.end);
334
- if (!isSectionName(matched) && !candidate.entries.some((e) => deps.urineCodes.has(e.code))) {
386
+ if (candidate.entries.some((e) => deps.urineCodes.has(e.code))) {
387
+ line.urine = true;
388
+ } else if (!isSectionName(matched)) {
335
389
  line.foreign = true;
336
390
  }
337
391
  }
@@ -404,40 +458,13 @@ function foldCommas(text) {
404
458
  (comma, space, offset) => startsWithCatalogName(text.slice(offset + 1 + space.length)) ? comma : " "
405
459
  );
406
460
  }
407
- var GENETIC_CONTEXT_PATTERNS = [
408
- /\b[nx][mrpc]_\d{6,}/,
409
- // RefSeq: NM_000384.2, NP_, NR_, XM_
410
- /\bens[gtp]\d{6,}/,
411
- // Ensembl: ENSG00000084674
412
- /\bp\.[a-z]{3}\d/,
413
- // HGVS proteína: p.Trp448*
414
- /\bc\.\d+[acgt]?[>_+-]/,
415
- // HGVS codificante: c.1234A>G, c.76_78del
416
- /\brs\d{4,}\b/,
417
- // dbSNP
418
- /\bgenes?\b/,
419
- /\bvariante?s?\b/,
420
- /\bexons?\b/,
421
- /\bzygosity\b/,
422
- /\bzigosidade\b/,
423
- /\balleles?\b/,
424
- /\balelos?\b/,
425
- /\bmutations?\b/,
426
- /\bmutac(ao|oes)\b/,
427
- /\bpathogenic/,
428
- /\bpatogenic/,
429
- /\bheterozyg/,
430
- /\bhomozyg/,
431
- /\bheterozigot/,
432
- /\bhomozigot/,
433
- /\bsequence change\b/
434
- ];
435
461
  var DIGIT_PATTERN = /\d/;
436
462
  var cachedUnitTokens = null;
437
463
  function getUnitTokens() {
438
464
  if (!cachedUnitTokens) {
465
+ const isExamName = (t) => URINALYSIS_SECTION_NAMES.has(t) || getNamePatterns().has(t);
439
466
  cachedUnitTokens = new Set(
440
- Object.keys(UNIT_TO_UCUM).map((unit) => normalize(unit).trim()).filter(Boolean)
467
+ Object.keys(UNIT_TO_UCUM).map((unit) => normalize(unit).trim()).filter((unit) => unit && !isExamName(unit))
441
468
  );
442
469
  }
443
470
  return cachedUnitTokens;
@@ -644,6 +671,9 @@ function findBiomarkersInText(ocrText) {
644
671
  if (qualifiedByBodyRegion(entry.code, before) || followedByPercent(entry.code, after)) {
645
672
  continue;
646
673
  }
674
+ if (SUBTYPE_AFTER[entry.code]?.test(after)) {
675
+ continue;
676
+ }
647
677
  if (girth && SKINFOLD_SITE_CODES.has(entry.code)) {
648
678
  continue;
649
679
  }