@openhealth/oht-custom-parser-lib 0.1.11 → 0.1.12
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.d.ts +11 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/service/auxiliaryFunctions.service.d.ts +19 -0
- package/dist/service/auxiliaryFunctions.service.js +64 -0
- package/dist/service/auxiliaryFunctions.service.js.map +1 -0
- package/dist/service/ohtMeasurementsExtractor.service.d.ts +31 -0
- package/dist/service/ohtMeasurementsExtractor.service.js +403 -0
- package/dist/service/ohtMeasurementsExtractor.service.js.map +1 -0
- package/dist/service/reportCreator.service.d.ts +8 -0
- package/dist/service/reportCreator.service.js +129 -0
- package/dist/service/reportCreator.service.js.map +1 -0
- package/dist/service/slackMessages.service.d.ts +9 -0
- package/dist/service/slackMessages.service.js +92 -0
- package/dist/service/slackMessages.service.js.map +1 -0
- package/dist/types/custom-parser.types.d.ts +56 -0
- package/dist/types/custom-parser.types.js +28 -0
- package/dist/types/custom-parser.types.js.map +1 -0
- package/dist/types/oht.types.d.ts +300 -0
- package/dist/types/oht.types.js +90 -0
- package/dist/types/oht.types.js.map +1 -0
- package/dist/types/slackMessages.types.d.ts +5 -0
- package/dist/types/slackMessages.types.js +3 -0
- package/dist/types/slackMessages.types.js.map +1 -0
- package/dist/util-ts/dataUtils.d.ts +3 -0
- package/dist/util-ts/dataUtils.js +19 -0
- package/dist/util-ts/dataUtils.js.map +1 -0
- package/dist/util-ts/googleStorageUtils.d.ts +3 -0
- package/dist/util-ts/googleStorageUtils.js +47 -0
- package/dist/util-ts/googleStorageUtils.js.map +1 -0
- package/dist/util-ts/pinoLogger.d.ts +12 -0
- package/dist/util-ts/pinoLogger.js +31 -0
- package/dist/util-ts/pinoLogger.js.map +1 -0
- package/dist/util-ts/regexUtils.d.ts +2 -0
- package/dist/util-ts/regexUtils.js +20 -0
- package/dist/util-ts/regexUtils.js.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { postDataImportFileUploadToCore, makeApiCallWithRetry, parseErrorObj } from './service/reportCreator.service';
|
|
2
|
+
export { parseExternalPatientId, parseGender, isPatientAgeValid, handleRejection, queryFileUploadsWithFilters, postFileUploadSetStatus } from './service/auxiliaryFunctions.service';
|
|
3
|
+
export { ohtMeasurementsExtractor, checkValueForPlausibleValues, extractReferenceRanges, extractReferenceAnnotation, parseExamValue, getRangeFromPositionAndRegex, extractUnit, extractValueFromGreaterLowerThan } from './service/ohtMeasurementsExtractor.service';
|
|
4
|
+
export { getSlackFileIssueNotificationMessage, sendMessageToSlack, aggregateMessages } from './service/slackMessages.service';
|
|
5
|
+
export { LabToOhtContract, LabToOhtMapper, UnknownMeasurementExtraction, ReferenceAsAnnotation, UnitExtraction, RangeExtraction, RangeExtractionResponse, ValueExtraction, unitSynonyms, } from './types/custom-parser.types';
|
|
6
|
+
export { AcfBiomarkerRangeAnnotationCheck, AcfBiomarkerGenericDisclaimerLogic, ManualCheck, BiomarkerCompatibility, ReferenceRangeType, AcfReferenceRange, VisualRange, NameAlias, AlternativeUnit, BiomarkerAcf, Biomarker, PipelineStep, DigitizationStatus, MeasurementValueComparator, BiomarkerValueType, DataImportFileUploadStatus, Sex, Digitization, PatientInfo, UnknownMeasurement, Measurement, DataImportFileUpload, MessagePayload, SignedUpload, ReportStyleConfig, LanguageCode, BiomarkerCustomisation, PartnerCustomPanel, ReportDisplaySettings, ReportCustomisation, OHTPartner, } from './types/oht.types';
|
|
7
|
+
export { SlackMessage } from './types/slackMessages.types';
|
|
8
|
+
export { extractValue, isBiomarkerValueNumerical, isBiomarkerExternalNoVisualRange } from './util-ts/dataUtils';
|
|
9
|
+
export { readFileFromBucket, uploadFileBufferToGCS } from './util-ts/googleStorageUtils';
|
|
10
|
+
export { default as pinoLogger } from './util-ts/pinoLogger';
|
|
11
|
+
export { getGreaterThanPatterns, getLowerThanPatterns } from './util-ts/regexUtils';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getLowerThanPatterns = exports.getGreaterThanPatterns = exports.pinoLogger = exports.uploadFileBufferToGCS = exports.readFileFromBucket = exports.isBiomarkerExternalNoVisualRange = exports.isBiomarkerValueNumerical = exports.extractValue = exports.Sex = exports.DataImportFileUploadStatus = exports.BiomarkerValueType = exports.MeasurementValueComparator = exports.DigitizationStatus = exports.PipelineStep = exports.ReferenceRangeType = exports.BiomarkerCompatibility = exports.ManualCheck = exports.AcfBiomarkerGenericDisclaimerLogic = exports.AcfBiomarkerRangeAnnotationCheck = exports.unitSynonyms = exports.aggregateMessages = exports.sendMessageToSlack = exports.getSlackFileIssueNotificationMessage = exports.extractValueFromGreaterLowerThan = exports.extractUnit = exports.getRangeFromPositionAndRegex = exports.parseExamValue = exports.extractReferenceAnnotation = exports.extractReferenceRanges = exports.checkValueForPlausibleValues = exports.ohtMeasurementsExtractor = exports.postFileUploadSetStatus = exports.queryFileUploadsWithFilters = exports.handleRejection = exports.isPatientAgeValid = exports.parseGender = exports.parseExternalPatientId = exports.parseErrorObj = exports.makeApiCallWithRetry = exports.postDataImportFileUploadToCore = void 0;
|
|
7
|
+
// ### SERVICES ###
|
|
8
|
+
var reportCreator_service_1 = require("./service/reportCreator.service");
|
|
9
|
+
Object.defineProperty(exports, "postDataImportFileUploadToCore", { enumerable: true, get: function () { return reportCreator_service_1.postDataImportFileUploadToCore; } });
|
|
10
|
+
Object.defineProperty(exports, "makeApiCallWithRetry", { enumerable: true, get: function () { return reportCreator_service_1.makeApiCallWithRetry; } });
|
|
11
|
+
Object.defineProperty(exports, "parseErrorObj", { enumerable: true, get: function () { return reportCreator_service_1.parseErrorObj; } });
|
|
12
|
+
var auxiliaryFunctions_service_1 = require("./service/auxiliaryFunctions.service");
|
|
13
|
+
Object.defineProperty(exports, "parseExternalPatientId", { enumerable: true, get: function () { return auxiliaryFunctions_service_1.parseExternalPatientId; } });
|
|
14
|
+
Object.defineProperty(exports, "parseGender", { enumerable: true, get: function () { return auxiliaryFunctions_service_1.parseGender; } });
|
|
15
|
+
Object.defineProperty(exports, "isPatientAgeValid", { enumerable: true, get: function () { return auxiliaryFunctions_service_1.isPatientAgeValid; } });
|
|
16
|
+
Object.defineProperty(exports, "handleRejection", { enumerable: true, get: function () { return auxiliaryFunctions_service_1.handleRejection; } });
|
|
17
|
+
Object.defineProperty(exports, "queryFileUploadsWithFilters", { enumerable: true, get: function () { return auxiliaryFunctions_service_1.queryFileUploadsWithFilters; } });
|
|
18
|
+
Object.defineProperty(exports, "postFileUploadSetStatus", { enumerable: true, get: function () { return auxiliaryFunctions_service_1.postFileUploadSetStatus; } });
|
|
19
|
+
var ohtMeasurementsExtractor_service_1 = require("./service/ohtMeasurementsExtractor.service");
|
|
20
|
+
Object.defineProperty(exports, "ohtMeasurementsExtractor", { enumerable: true, get: function () { return ohtMeasurementsExtractor_service_1.ohtMeasurementsExtractor; } });
|
|
21
|
+
Object.defineProperty(exports, "checkValueForPlausibleValues", { enumerable: true, get: function () { return ohtMeasurementsExtractor_service_1.checkValueForPlausibleValues; } });
|
|
22
|
+
Object.defineProperty(exports, "extractReferenceRanges", { enumerable: true, get: function () { return ohtMeasurementsExtractor_service_1.extractReferenceRanges; } });
|
|
23
|
+
Object.defineProperty(exports, "extractReferenceAnnotation", { enumerable: true, get: function () { return ohtMeasurementsExtractor_service_1.extractReferenceAnnotation; } });
|
|
24
|
+
Object.defineProperty(exports, "parseExamValue", { enumerable: true, get: function () { return ohtMeasurementsExtractor_service_1.parseExamValue; } });
|
|
25
|
+
Object.defineProperty(exports, "getRangeFromPositionAndRegex", { enumerable: true, get: function () { return ohtMeasurementsExtractor_service_1.getRangeFromPositionAndRegex; } });
|
|
26
|
+
Object.defineProperty(exports, "extractUnit", { enumerable: true, get: function () { return ohtMeasurementsExtractor_service_1.extractUnit; } });
|
|
27
|
+
Object.defineProperty(exports, "extractValueFromGreaterLowerThan", { enumerable: true, get: function () { return ohtMeasurementsExtractor_service_1.extractValueFromGreaterLowerThan; } });
|
|
28
|
+
var slackMessages_service_1 = require("./service/slackMessages.service");
|
|
29
|
+
Object.defineProperty(exports, "getSlackFileIssueNotificationMessage", { enumerable: true, get: function () { return slackMessages_service_1.getSlackFileIssueNotificationMessage; } });
|
|
30
|
+
Object.defineProperty(exports, "sendMessageToSlack", { enumerable: true, get: function () { return slackMessages_service_1.sendMessageToSlack; } });
|
|
31
|
+
Object.defineProperty(exports, "aggregateMessages", { enumerable: true, get: function () { return slackMessages_service_1.aggregateMessages; } });
|
|
32
|
+
// ### TYPES ###
|
|
33
|
+
var custom_parser_types_1 = require("./types/custom-parser.types");
|
|
34
|
+
Object.defineProperty(exports, "unitSynonyms", { enumerable: true, get: function () { return custom_parser_types_1.unitSynonyms; } });
|
|
35
|
+
var oht_types_1 = require("./types/oht.types");
|
|
36
|
+
Object.defineProperty(exports, "AcfBiomarkerRangeAnnotationCheck", { enumerable: true, get: function () { return oht_types_1.AcfBiomarkerRangeAnnotationCheck; } });
|
|
37
|
+
Object.defineProperty(exports, "AcfBiomarkerGenericDisclaimerLogic", { enumerable: true, get: function () { return oht_types_1.AcfBiomarkerGenericDisclaimerLogic; } });
|
|
38
|
+
Object.defineProperty(exports, "ManualCheck", { enumerable: true, get: function () { return oht_types_1.ManualCheck; } });
|
|
39
|
+
Object.defineProperty(exports, "BiomarkerCompatibility", { enumerable: true, get: function () { return oht_types_1.BiomarkerCompatibility; } });
|
|
40
|
+
Object.defineProperty(exports, "ReferenceRangeType", { enumerable: true, get: function () { return oht_types_1.ReferenceRangeType; } });
|
|
41
|
+
Object.defineProperty(exports, "PipelineStep", { enumerable: true, get: function () { return oht_types_1.PipelineStep; } });
|
|
42
|
+
Object.defineProperty(exports, "DigitizationStatus", { enumerable: true, get: function () { return oht_types_1.DigitizationStatus; } });
|
|
43
|
+
Object.defineProperty(exports, "MeasurementValueComparator", { enumerable: true, get: function () { return oht_types_1.MeasurementValueComparator; } });
|
|
44
|
+
Object.defineProperty(exports, "BiomarkerValueType", { enumerable: true, get: function () { return oht_types_1.BiomarkerValueType; } });
|
|
45
|
+
Object.defineProperty(exports, "DataImportFileUploadStatus", { enumerable: true, get: function () { return oht_types_1.DataImportFileUploadStatus; } });
|
|
46
|
+
Object.defineProperty(exports, "Sex", { enumerable: true, get: function () { return oht_types_1.Sex; } });
|
|
47
|
+
// ### UTIL-TS ###
|
|
48
|
+
var dataUtils_1 = require("./util-ts/dataUtils");
|
|
49
|
+
Object.defineProperty(exports, "extractValue", { enumerable: true, get: function () { return dataUtils_1.extractValue; } });
|
|
50
|
+
Object.defineProperty(exports, "isBiomarkerValueNumerical", { enumerable: true, get: function () { return dataUtils_1.isBiomarkerValueNumerical; } });
|
|
51
|
+
Object.defineProperty(exports, "isBiomarkerExternalNoVisualRange", { enumerable: true, get: function () { return dataUtils_1.isBiomarkerExternalNoVisualRange; } });
|
|
52
|
+
var googleStorageUtils_1 = require("./util-ts/googleStorageUtils");
|
|
53
|
+
Object.defineProperty(exports, "readFileFromBucket", { enumerable: true, get: function () { return googleStorageUtils_1.readFileFromBucket; } });
|
|
54
|
+
Object.defineProperty(exports, "uploadFileBufferToGCS", { enumerable: true, get: function () { return googleStorageUtils_1.uploadFileBufferToGCS; } });
|
|
55
|
+
var pinoLogger_1 = require("./util-ts/pinoLogger");
|
|
56
|
+
Object.defineProperty(exports, "pinoLogger", { enumerable: true, get: function () { return __importDefault(pinoLogger_1).default; } });
|
|
57
|
+
var regexUtils_1 = require("./util-ts/regexUtils");
|
|
58
|
+
Object.defineProperty(exports, "getGreaterThanPatterns", { enumerable: true, get: function () { return regexUtils_1.getGreaterThanPatterns; } });
|
|
59
|
+
Object.defineProperty(exports, "getLowerThanPatterns", { enumerable: true, get: function () { return regexUtils_1.getLowerThanPatterns; } });
|
|
60
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;AAAA,mBAAmB;AACnB,yEAEyC;AADvC,uIAAA,8BAA8B,OAAA;AAAE,6HAAA,oBAAoB,OAAA;AAAE,sHAAA,aAAa,OAAA;AAErE,mFAO8C;AAN5C,oIAAA,sBAAsB,OAAA;AACtB,yHAAA,WAAW,OAAA;AACX,+HAAA,iBAAiB,OAAA;AACjB,6HAAA,eAAe,OAAA;AACf,yIAAA,2BAA2B,OAAA;AAC3B,qIAAA,uBAAuB,OAAA;AAEzB,+FASoD;AARlD,4IAAA,wBAAwB,OAAA;AACxB,gJAAA,4BAA4B,OAAA;AAC5B,0IAAA,sBAAsB,OAAA;AACtB,8IAAA,0BAA0B,OAAA;AAC1B,kIAAA,cAAc,OAAA;AACd,gJAAA,4BAA4B,OAAA;AAC5B,+HAAA,WAAW,OAAA;AACX,oJAAA,gCAAgC,OAAA;AAElC,yEAEyC;AADvC,6IAAA,oCAAoC,OAAA;AAAE,2HAAA,kBAAkB,OAAA;AAAE,0HAAA,iBAAiB,OAAA;AAG7E,gBAAgB;AAChB,mEAUqC;AADnC,mHAAA,YAAY,OAAA;AAEd,+CAgC2B;AA/BzB,6HAAA,gCAAgC,OAAA;AAChC,+HAAA,kCAAkC,OAAA;AAClC,wGAAA,WAAW,OAAA;AACX,mHAAA,sBAAsB,OAAA;AACtB,+GAAA,kBAAkB,OAAA;AAOlB,yGAAA,YAAY,OAAA;AACZ,+GAAA,kBAAkB,OAAA;AAClB,uHAAA,0BAA0B,OAAA;AAC1B,+GAAA,kBAAkB,OAAA;AAClB,uHAAA,0BAA0B,OAAA;AAC1B,gGAAA,GAAG,OAAA;AAkBL,kBAAkB;AAClB,iDAA8G;AAAtG,yGAAA,YAAY,OAAA;AAAE,sHAAA,yBAAyB,OAAA;AAAE,6HAAA,gCAAgC,OAAA;AACjF,mEAAyF;AAAhF,wHAAA,kBAAkB,OAAA;AAAE,2HAAA,qBAAqB,OAAA;AAClD,mDAA2D;AAAnD,yHAAA,OAAO,OAAc;AAC7B,mDAAkF;AAA1E,oHAAA,sBAAsB,OAAA;AAAE,kHAAA,oBAAoB,OAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SlackMessage } from '../types/slackMessages.types';
|
|
2
|
+
import { DataImportFileUpload, DataImportFileUploadStatus, Sex } from "../types/oht.types";
|
|
3
|
+
declare function isPatientAgeValid(documentDate?: Date, patientBirthdate?: Date): boolean;
|
|
4
|
+
declare function parseExternalPatientId(patientExternalId?: string): boolean;
|
|
5
|
+
declare function parseGender(patientGender?: string): {
|
|
6
|
+
sex: Sex;
|
|
7
|
+
isGenderCorrectlyParsed: boolean;
|
|
8
|
+
};
|
|
9
|
+
declare function handleRejection(parseType: string, slackMessages: SlackMessage[], type?: string): {
|
|
10
|
+
rejectedStatus: string;
|
|
11
|
+
rejectReason: string;
|
|
12
|
+
};
|
|
13
|
+
export interface FileUploadFilter {
|
|
14
|
+
field: string;
|
|
15
|
+
search: string;
|
|
16
|
+
}
|
|
17
|
+
declare function queryFileUploadsWithFilters(filters: FileUploadFilter[], ohtCoreApiKey: string): Promise<DataImportFileUpload[]>;
|
|
18
|
+
declare function postFileUploadSetStatus(status: DataImportFileUploadStatus, fileUploadId: string, filename: string, ohtCoreApiKey: string): Promise<DataImportFileUpload>;
|
|
19
|
+
export { parseExternalPatientId, parseGender, isPatientAgeValid, handleRejection, queryFileUploadsWithFilters, postFileUploadSetStatus };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseExternalPatientId = parseExternalPatientId;
|
|
7
|
+
exports.parseGender = parseGender;
|
|
8
|
+
exports.isPatientAgeValid = isPatientAgeValid;
|
|
9
|
+
exports.handleRejection = handleRejection;
|
|
10
|
+
exports.queryFileUploadsWithFilters = queryFileUploadsWithFilters;
|
|
11
|
+
exports.postFileUploadSetStatus = postFileUploadSetStatus;
|
|
12
|
+
const moment_1 = __importDefault(require("moment/moment"));
|
|
13
|
+
const reportCreator_service_1 = require("./reportCreator.service");
|
|
14
|
+
const pinoLogger_1 = __importDefault(require("../util-ts/pinoLogger"));
|
|
15
|
+
const oht_types_1 = require("../types/oht.types");
|
|
16
|
+
const logger = (0, pinoLogger_1.default)();
|
|
17
|
+
function isPatientAgeValid(documentDate, patientBirthdate) {
|
|
18
|
+
if (!patientBirthdate || !documentDate)
|
|
19
|
+
return false;
|
|
20
|
+
const patientAge = (0, moment_1.default)(documentDate).diff(patientBirthdate, 'years');
|
|
21
|
+
return patientAge >= 18 && patientAge <= 130;
|
|
22
|
+
}
|
|
23
|
+
function parseExternalPatientId(patientExternalId) {
|
|
24
|
+
return patientExternalId !== undefined && patientExternalId.length > 0;
|
|
25
|
+
}
|
|
26
|
+
function parseGender(patientGender) {
|
|
27
|
+
const gender = patientGender?.toLowerCase();
|
|
28
|
+
switch (gender) {
|
|
29
|
+
case 'masculino':
|
|
30
|
+
case 'male':
|
|
31
|
+
return { sex: oht_types_1.Sex.MALE, isGenderCorrectlyParsed: true };
|
|
32
|
+
case 'feminino':
|
|
33
|
+
case 'female':
|
|
34
|
+
return { sex: oht_types_1.Sex.FEMALE, isGenderCorrectlyParsed: true };
|
|
35
|
+
default:
|
|
36
|
+
return { sex: oht_types_1.Sex.OTHER, isGenderCorrectlyParsed: false };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function handleRejection(parseType, slackMessages, type = 'ERROR') {
|
|
40
|
+
let rejectedStatus = 'REJECTED';
|
|
41
|
+
let rejectReason = `Could not parse ${parseType}`;
|
|
42
|
+
slackMessages.push({
|
|
43
|
+
type: type,
|
|
44
|
+
message: `File REJECTED, ${parseType} not parsed correctly:`,
|
|
45
|
+
});
|
|
46
|
+
return { rejectedStatus, rejectReason };
|
|
47
|
+
}
|
|
48
|
+
async function queryFileUploadsWithFilters(filters, ohtCoreApiKey) {
|
|
49
|
+
const OHT_CORE_URL = process.env.OHT_CORE_URL;
|
|
50
|
+
const ohtCoreUrl = `${OHT_CORE_URL}/v1/file-uploads/query`;
|
|
51
|
+
const queryData = {
|
|
52
|
+
"filters": [...filters]
|
|
53
|
+
};
|
|
54
|
+
const queryResponse = await (0, reportCreator_service_1.makeApiCallWithRetry)('post', ohtCoreUrl, queryData, ohtCoreApiKey);
|
|
55
|
+
return queryResponse?.data?.items;
|
|
56
|
+
}
|
|
57
|
+
async function postFileUploadSetStatus(status, fileUploadId, filename, ohtCoreApiKey) {
|
|
58
|
+
const OHT_CORE_URL = process.env.OHT_CORE_URL;
|
|
59
|
+
const ohtCoreFileUploadSetStatusUrl = `${OHT_CORE_URL}/v1/file-uploads/status/set/${fileUploadId}`;
|
|
60
|
+
const processingResponse = await (0, reportCreator_service_1.makeApiCallWithRetry)('post', ohtCoreFileUploadSetStatusUrl, { status }, ohtCoreApiKey);
|
|
61
|
+
logger.info({ resp: processingResponse?.data }, `Resp: POST status: ${status} /file-uploads/${fileUploadId}, filename: ${filename}`);
|
|
62
|
+
return processingResponse?.data;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=auxiliaryFunctions.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auxiliaryFunctions.service.js","sourceRoot":"","sources":["../../service/auxiliaryFunctions.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { LabToOhtContract, LabToOhtMapper, RangeExtraction, RangeExtractionResponse, UnitExtraction, ValueExtraction } from "../types/custom-parser.types";
|
|
2
|
+
import { Biomarker, BiomarkerValueType, Measurement, MeasurementValueComparator, PatientInfo, UnknownMeasurement } from "../types/oht.types";
|
|
3
|
+
declare function extractUnit(unitExtraction: UnitExtraction | undefined, rawData: any, ohtBiomarker: Biomarker): {
|
|
4
|
+
unit: string;
|
|
5
|
+
isUnitParsedCorrectly: boolean;
|
|
6
|
+
};
|
|
7
|
+
declare function extractReferenceAnnotation(labToOhtMapper: LabToOhtMapper, rawExam: any, patientInfo: PatientInfo, documentDate?: Date): any;
|
|
8
|
+
declare const getRangeFromPositionAndRegex: (rangeExtraction: RangeExtraction, rawData: any) => {
|
|
9
|
+
from: number | undefined;
|
|
10
|
+
to: number | undefined;
|
|
11
|
+
};
|
|
12
|
+
declare function extractReferenceRanges(labToOhtMapper: LabToOhtMapper, rawData: any, patientInfo: PatientInfo, documentDate?: Date): RangeExtractionResponse;
|
|
13
|
+
declare function extractValueFromGreaterLowerThan(value: string): {
|
|
14
|
+
extractedValue: number | null;
|
|
15
|
+
extractedValueComparator: MeasurementValueComparator | null;
|
|
16
|
+
valueParsedCorrectly: boolean;
|
|
17
|
+
};
|
|
18
|
+
declare function parseExamValue(rawExamObj: any, valueExtraction: ValueExtraction | undefined, ohtBiomarker: Biomarker): {
|
|
19
|
+
valueType: BiomarkerValueType;
|
|
20
|
+
alphanumericalValue: string | null;
|
|
21
|
+
numberValue: number | null;
|
|
22
|
+
valueComparator: MeasurementValueComparator;
|
|
23
|
+
isValueParsedCorrectly: boolean;
|
|
24
|
+
};
|
|
25
|
+
declare function checkValueForPlausibleValues(labToOhtMapper: LabToOhtMapper, ohtBiomarker: Biomarker, unit: string, value: number, isUnitParsedCorrectly: boolean): boolean;
|
|
26
|
+
declare function ohtMeasurementsExtractor(labToOhtContract: LabToOhtContract, ohtCoreApiKey: string): Promise<{
|
|
27
|
+
measurements: Measurement[];
|
|
28
|
+
unknownMeasurements: UnknownMeasurement[];
|
|
29
|
+
isReportCorrectlyParsed: boolean;
|
|
30
|
+
}>;
|
|
31
|
+
export { ohtMeasurementsExtractor, checkValueForPlausibleValues, extractReferenceRanges, extractReferenceAnnotation, parseExamValue, getRangeFromPositionAndRegex, extractUnit, extractValueFromGreaterLowerThan };
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getRangeFromPositionAndRegex = void 0;
|
|
7
|
+
exports.ohtMeasurementsExtractor = ohtMeasurementsExtractor;
|
|
8
|
+
exports.checkValueForPlausibleValues = checkValueForPlausibleValues;
|
|
9
|
+
exports.extractReferenceRanges = extractReferenceRanges;
|
|
10
|
+
exports.extractReferenceAnnotation = extractReferenceAnnotation;
|
|
11
|
+
exports.parseExamValue = parseExamValue;
|
|
12
|
+
exports.extractUnit = extractUnit;
|
|
13
|
+
exports.extractValueFromGreaterLowerThan = extractValueFromGreaterLowerThan;
|
|
14
|
+
const custom_parser_types_1 = require("../types/custom-parser.types");
|
|
15
|
+
const dataUtils_1 = require("../util-ts/dataUtils");
|
|
16
|
+
const regexUtils_1 = require("../util-ts/regexUtils");
|
|
17
|
+
const pinoLogger_1 = __importDefault(require("../util-ts/pinoLogger"));
|
|
18
|
+
const reportCreator_service_1 = require("./reportCreator.service");
|
|
19
|
+
const oht_types_1 = require("../types/oht.types");
|
|
20
|
+
const logger = (0, pinoLogger_1.default)();
|
|
21
|
+
const OHT_CORE_URL = process.env.OHT_CORE_URL;
|
|
22
|
+
let filename = '';
|
|
23
|
+
function extractUnit(unitExtraction, rawData, ohtBiomarker) {
|
|
24
|
+
if (!(0, dataUtils_1.isBiomarkerValueNumerical)(ohtBiomarker)) {
|
|
25
|
+
return { unit: "None", isUnitParsedCorrectly: true };
|
|
26
|
+
}
|
|
27
|
+
// If there's a function to extract the unit, use it
|
|
28
|
+
if (unitExtraction && unitExtraction?.extractionFunc) {
|
|
29
|
+
const extractedUnitExtraction = unitExtraction.extractionFunc(rawData);
|
|
30
|
+
return { unit: extractedUnitExtraction ?? '', isUnitParsedCorrectly: !!extractedUnitExtraction };
|
|
31
|
+
}
|
|
32
|
+
const unit = (0, dataUtils_1.extractValue)(rawData, unitExtraction?.unitPosition);
|
|
33
|
+
let unitLowerCase = unit?.toLowerCase();
|
|
34
|
+
unitLowerCase = unitLowerCase?.replaceAll(/ /g, '') ?? '';
|
|
35
|
+
const standardUnit = ohtBiomarker.acf.standardUnit;
|
|
36
|
+
const allBmOhtUnits = [
|
|
37
|
+
standardUnit?.toLowerCase(),
|
|
38
|
+
...ohtBiomarker.acf?.alternativeUnits
|
|
39
|
+
?.map((bmAlternative) => bmAlternative.name.toLowerCase()) ?? []
|
|
40
|
+
];
|
|
41
|
+
// First test if the biomarker has the unit explicit in the 'unity' field
|
|
42
|
+
const allUnitContainsJsonUnit = allBmOhtUnits.includes(unitLowerCase);
|
|
43
|
+
if (unitLowerCase?.length > 0 && allUnitContainsJsonUnit) {
|
|
44
|
+
return {
|
|
45
|
+
unit: unit?.replaceAll(/ /g, ''),
|
|
46
|
+
isUnitParsedCorrectly: true
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Check if there are any Synonym configured
|
|
50
|
+
const allUnitContainsJsonUnitSynonyms = allBmOhtUnits.includes(custom_parser_types_1.unitSynonyms[unitLowerCase]?.toLowerCase());
|
|
51
|
+
if (unitLowerCase?.length > 0 && allUnitContainsJsonUnitSynonyms) {
|
|
52
|
+
return { unit: custom_parser_types_1.unitSynonyms[unitLowerCase], isUnitParsedCorrectly: true };
|
|
53
|
+
}
|
|
54
|
+
if (unitExtraction?.predefinedUnit) {
|
|
55
|
+
const allUnitContainsUnitFromOhtMap = allBmOhtUnits.includes(unitExtraction.predefinedUnit.toLowerCase());
|
|
56
|
+
// If the 'unity' field is not present, try to get the unit from the biomarker map
|
|
57
|
+
return { unit: unitExtraction?.predefinedUnit, isUnitParsedCorrectly: allUnitContainsUnitFromOhtMap };
|
|
58
|
+
}
|
|
59
|
+
if (unitExtraction?.extractionRegex) {
|
|
60
|
+
const regex = new RegExp(unitExtraction.extractionRegex);
|
|
61
|
+
const match = rawData.match(regex);
|
|
62
|
+
const allUnitContainsUnitMatch = allBmOhtUnits.includes(match?.[1]?.toLowerCase());
|
|
63
|
+
if (match?.[1] && allUnitContainsUnitMatch) {
|
|
64
|
+
return { unit: match?.[1], isUnitParsedCorrectly: true };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { unit: '', isUnitParsedCorrectly: false };
|
|
68
|
+
}
|
|
69
|
+
function extractReferenceAnnotation(labToOhtMapper, rawExam, patientInfo, documentDate) {
|
|
70
|
+
const annotationExtractionFunc = labToOhtMapper?.referenceAsAnnotation?.extractionFunc;
|
|
71
|
+
if (annotationExtractionFunc) {
|
|
72
|
+
return annotationExtractionFunc(rawExam, labToOhtMapper, patientInfo, documentDate);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const extractedReference = (0, dataUtils_1.extractValue)(rawExam, labToOhtMapper?.referenceAsAnnotation?.position);
|
|
76
|
+
const reference = extractedReference?.valor?.trim();
|
|
77
|
+
return reference?.length > 0 ? reference : extractedReference?.val?.trim();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const getRangeFromPositionAndRegex = (rangeExtraction, rawData) => {
|
|
81
|
+
const extractionRegex = rangeExtraction.extractionRegex;
|
|
82
|
+
const rangePosition = rangeExtraction.rangePosition;
|
|
83
|
+
const regex = typeof extractionRegex === 'string' ? new RegExp(extractionRegex) : extractionRegex;
|
|
84
|
+
const value = (0, dataUtils_1.extractValue)(rawData, rangePosition);
|
|
85
|
+
if (regex) {
|
|
86
|
+
let match = regex.exec(value);
|
|
87
|
+
if (match) {
|
|
88
|
+
// Check length of match
|
|
89
|
+
// If length is 2, it means that the range is a single value
|
|
90
|
+
// If length is 3, it means that the range has two values
|
|
91
|
+
const inferior = parseFloat(match[1].replaceAll('.', '').replaceAll(',', '.'));
|
|
92
|
+
const superior = parseFloat(match[match.length - 1].replaceAll('.', '').replaceAll(',', '.'));
|
|
93
|
+
return {
|
|
94
|
+
from: inferior,
|
|
95
|
+
to: superior,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
from: undefined,
|
|
101
|
+
to: undefined,
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
exports.getRangeFromPositionAndRegex = getRangeFromPositionAndRegex;
|
|
105
|
+
function extractReferenceRanges(labToOhtMapper, rawData, patientInfo, documentDate) {
|
|
106
|
+
// WIP
|
|
107
|
+
const nullRangeNotParsedCorrectly = { from: null, to: null, isRangeParsedCorrectly: false };
|
|
108
|
+
const nullRangeParsedCorrectly = { from: null, to: null, isRangeParsedCorrectly: true };
|
|
109
|
+
// if internalRange return null, but is parsed correctly
|
|
110
|
+
if (labToOhtMapper?.rangeExtraction?.useInternalRange) {
|
|
111
|
+
//logger.info(`Using internalRange for biomarker ${ohtBiomarkerMap.ohtBmId}, key: ${ohtBiomarkerMap.labiKey}, filename: ${filename}`)
|
|
112
|
+
return nullRangeParsedCorrectly;
|
|
113
|
+
} // if internalRange return null
|
|
114
|
+
// Each lab has a "default" range structure.
|
|
115
|
+
// We need a default extraction function for each lab
|
|
116
|
+
// Here we receive from the custom parser a function inside the RangeExtraction object
|
|
117
|
+
let rangeExtractionResult = labToOhtMapper?.rangeExtraction?.defaultRangeExtractionFunc
|
|
118
|
+
? labToOhtMapper.rangeExtraction.defaultRangeExtractionFunc(labToOhtMapper, rawData)
|
|
119
|
+
: nullRangeNotParsedCorrectly;
|
|
120
|
+
if (rangeExtractionResult.isRangeParsedCorrectly) {
|
|
121
|
+
return rangeExtractionResult;
|
|
122
|
+
}
|
|
123
|
+
// if there's a function to extract the range, use it
|
|
124
|
+
if (labToOhtMapper.rangeExtraction?.extractionFunc) {
|
|
125
|
+
rangeExtractionResult = labToOhtMapper.rangeExtraction.extractionFunc(rawData, labToOhtMapper, patientInfo, documentDate);
|
|
126
|
+
if (rangeExtractionResult.isRangeParsedCorrectly) {
|
|
127
|
+
return rangeExtractionResult;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// if there's a position and regex to extract the range, use it
|
|
131
|
+
if (labToOhtMapper.rangeExtraction?.extractionRegex) {
|
|
132
|
+
let { from, to, } = getRangeFromPositionAndRegex(labToOhtMapper.rangeExtraction, rawData);
|
|
133
|
+
if (labToOhtMapper.rangeExtraction?.rangePosition
|
|
134
|
+
&& (labToOhtMapper.rangeExtraction?.extractionRegex)
|
|
135
|
+
&& from != null // if from is 0 is valid, but fails the if condition because it's falsy
|
|
136
|
+
&& to != null // same as from
|
|
137
|
+
) {
|
|
138
|
+
const isRangeParsedCorrectly = !Number.isNaN(from) && !Number.isNaN(to);
|
|
139
|
+
return { from, to, isRangeParsedCorrectly };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return nullRangeNotParsedCorrectly;
|
|
143
|
+
}
|
|
144
|
+
function extractValueFromGreaterLowerThan(value) {
|
|
145
|
+
// Checks first if any greater than pattern is present
|
|
146
|
+
const greaterThanPatterns = (0, regexUtils_1.getGreaterThanPatterns)();
|
|
147
|
+
const lowerThanPatterns = (0, regexUtils_1.getLowerThanPatterns)();
|
|
148
|
+
value = value.toLowerCase();
|
|
149
|
+
for (const pattern of greaterThanPatterns) {
|
|
150
|
+
const match = pattern.exec(value);
|
|
151
|
+
if (match) {
|
|
152
|
+
const value = parseFloat(match?.[1].replaceAll(',', '.'));
|
|
153
|
+
const valueComparator = oht_types_1.MeasurementValueComparator.GREATER_THAN;
|
|
154
|
+
return {
|
|
155
|
+
extractedValue: value,
|
|
156
|
+
extractedValueComparator: valueComparator,
|
|
157
|
+
valueParsedCorrectly: !Number.isNaN(value)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Checks if any lower than pattern is present
|
|
162
|
+
for (const pattern of lowerThanPatterns) {
|
|
163
|
+
const match = pattern.exec(value);
|
|
164
|
+
if (match) {
|
|
165
|
+
const value = parseFloat(match?.[1].replaceAll(',', '.'));
|
|
166
|
+
const valueComparator = oht_types_1.MeasurementValueComparator.LESS_THAN;
|
|
167
|
+
return {
|
|
168
|
+
extractedValue: value,
|
|
169
|
+
extractedValueComparator: valueComparator,
|
|
170
|
+
valueParsedCorrectly: !Number.isNaN(value)
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// If no pattern is found, return the value as is and the comparator as EQUAL and isValueParsedCorrectly as false
|
|
175
|
+
return { extractedValue: null, extractedValueComparator: null, valueParsedCorrectly: false };
|
|
176
|
+
}
|
|
177
|
+
function parseExamValue(rawExamObj, valueExtraction, ohtBiomarker) {
|
|
178
|
+
let value = (0, dataUtils_1.extractValue)(rawExamObj, valueExtraction?.valuePosition);
|
|
179
|
+
value = valueExtraction.defaultValueExtractionFunc ? valueExtraction.defaultValueExtractionFunc(value) : value;
|
|
180
|
+
// If is parse float nan, the value is alphanumerical
|
|
181
|
+
const valueType = (0, dataUtils_1.isBiomarkerValueNumerical)(ohtBiomarker)
|
|
182
|
+
? oht_types_1.BiomarkerValueType.NUMERICAL
|
|
183
|
+
: oht_types_1.BiomarkerValueType.ALPHANUMERICAL;
|
|
184
|
+
let alphanumericalValue = Number.isNaN(parseFloat(value))
|
|
185
|
+
? value
|
|
186
|
+
: null;
|
|
187
|
+
if (valueType === oht_types_1.BiomarkerValueType.ALPHANUMERICAL
|
|
188
|
+
&& !Number.isNaN(parseFloat(value))) {
|
|
189
|
+
// convert numberValue to string
|
|
190
|
+
alphanumericalValue = value.toString();
|
|
191
|
+
}
|
|
192
|
+
if (valueType === oht_types_1.BiomarkerValueType.NUMERICAL
|
|
193
|
+
&& Number.isNaN(parseFloat(value))) {
|
|
194
|
+
// convert numberValue to string
|
|
195
|
+
const { extractedValue, extractedValueComparator, valueParsedCorrectly } = extractValueFromGreaterLowerThan(value);
|
|
196
|
+
return {
|
|
197
|
+
valueType,
|
|
198
|
+
alphanumericalValue,
|
|
199
|
+
numberValue: extractedValue,
|
|
200
|
+
valueComparator: extractedValueComparator ?? oht_types_1.MeasurementValueComparator.EQUAL,
|
|
201
|
+
isValueParsedCorrectly: valueParsedCorrectly
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (valueType === 'NUMERICAL') {
|
|
205
|
+
return {
|
|
206
|
+
valueType,
|
|
207
|
+
alphanumericalValue,
|
|
208
|
+
numberValue: parseFloat(value),
|
|
209
|
+
valueComparator: oht_types_1.MeasurementValueComparator.EQUAL,
|
|
210
|
+
isValueParsedCorrectly: !Number.isNaN(parseFloat(value))
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
if (valueType === 'ALPHANUMERICAL') {
|
|
214
|
+
return {
|
|
215
|
+
valueType,
|
|
216
|
+
alphanumericalValue,
|
|
217
|
+
numberValue: parseFloat(value),
|
|
218
|
+
valueComparator: oht_types_1.MeasurementValueComparator.EQUAL,
|
|
219
|
+
isValueParsedCorrectly: alphanumericalValue !== null
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
valueType,
|
|
224
|
+
alphanumericalValue,
|
|
225
|
+
numberValue: null,
|
|
226
|
+
valueComparator: oht_types_1.MeasurementValueComparator.EQUAL,
|
|
227
|
+
isValueParsedCorrectly: false
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function checkValueForPlausibleValues(labToOhtMapper, ohtBiomarker, unit, value, isUnitParsedCorrectly) {
|
|
231
|
+
if (!(0, dataUtils_1.isBiomarkerValueNumerical)(ohtBiomarker) && isUnitParsedCorrectly) {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
const standardUnit = ohtBiomarker.acf.standardUnit;
|
|
235
|
+
const plausibleLowerValue = ohtBiomarker.acf.plausibilityRangeCheck;
|
|
236
|
+
const plausibleUpperValue = ohtBiomarker.acf.plausibilityRangeCheckHigh;
|
|
237
|
+
const biomarkerStdValueDecimals = ohtBiomarker.acf?.displayDecimals ?? 2;
|
|
238
|
+
if (unit !== standardUnit) {
|
|
239
|
+
const alternativeUnit = (Array.isArray(ohtBiomarker.acf?.alternativeUnits)
|
|
240
|
+
? ohtBiomarker.acf?.alternativeUnits
|
|
241
|
+
: [])?.find((altUnit) => altUnit.name === unit);
|
|
242
|
+
if (alternativeUnit) {
|
|
243
|
+
const conversionFactor = alternativeUnit.conversionFactor;
|
|
244
|
+
value = +(value * conversionFactor).toFixed(biomarkerStdValueDecimals);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const isPlausibleValue = value >= plausibleLowerValue && value <= plausibleUpperValue;
|
|
248
|
+
// Log if the value is not plausible
|
|
249
|
+
if (!isPlausibleValue) {
|
|
250
|
+
logger.warn({ labToOhtMapper, value, standardUnit, plausibleLowerValue, plausibleUpperValue, filename }, `Value "${value} ${standardUnit}" not plausible "${plausibleLowerValue} to ${plausibleUpperValue}" for "${labToOhtMapper.ohtBmId}" | "${labToOhtMapper.labKey}", filename: ${filename}`);
|
|
251
|
+
}
|
|
252
|
+
return isPlausibleValue;
|
|
253
|
+
}
|
|
254
|
+
async function ohtMeasurementsExtractor(labToOhtContract, ohtCoreApiKey) {
|
|
255
|
+
filename = labToOhtContract.sourceFilename;
|
|
256
|
+
const measurements = [];
|
|
257
|
+
const unknownMeasurements = [];
|
|
258
|
+
let isReportCorrectlyParsed = true;
|
|
259
|
+
const allPossibleOHTBmIds = [];
|
|
260
|
+
[
|
|
261
|
+
...Object.values(labToOhtContract.labToOhtList)
|
|
262
|
+
].forEach(labToOhtMapper => {
|
|
263
|
+
if (labToOhtMapper.ohtBmId) {
|
|
264
|
+
allPossibleOHTBmIds.push(labToOhtMapper.ohtBmId);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
// data to request api
|
|
268
|
+
const requestData = {
|
|
269
|
+
"biomarkerIds": allPossibleOHTBmIds,
|
|
270
|
+
"page": 1,
|
|
271
|
+
"pageSize": allPossibleOHTBmIds.length
|
|
272
|
+
};
|
|
273
|
+
// api call to ohtCore to find biomarkers.
|
|
274
|
+
const biomarkers = await (0, reportCreator_service_1.makeApiCallWithRetry)('post', `${OHT_CORE_URL}/v1/biomarker/biomarkers`, requestData, ohtCoreApiKey);
|
|
275
|
+
for (const labToOhtMapper of labToOhtContract.labToOhtList) {
|
|
276
|
+
if (!labToOhtMapper.rawExam) {
|
|
277
|
+
labToOhtContract.slackMessages.push({
|
|
278
|
+
type: 'ERROR',
|
|
279
|
+
message: `Missing rawExam for labKey: ${labToOhtMapper.labKey} filename: ${filename}`,
|
|
280
|
+
});
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const rawExamObj = JSON.parse(labToOhtMapper.rawExam);
|
|
284
|
+
if (labToOhtMapper?.ohtBmId) {
|
|
285
|
+
// if BM is already in measurements, continue to the next one
|
|
286
|
+
if (measurements.some(measurement => measurement.biomarkerId === labToOhtMapper.ohtBmId)) {
|
|
287
|
+
logger.warn({ labToOhtMapper }, `Biomarker already in measurements array, should investigate, filename: ${filename}`);
|
|
288
|
+
labToOhtContract.slackMessages.push({
|
|
289
|
+
type: 'ERROR',
|
|
290
|
+
message: `Biomarkers Duplicated keys: [PLACEHOLDER]`,
|
|
291
|
+
item: `bmId:${labToOhtMapper.ohtBmId}, labKey:${labToOhtMapper.labKey}`
|
|
292
|
+
});
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const ohtBiomarker = biomarkers?.data?.items.find((bm) => bm.id === labToOhtMapper.ohtBmId);
|
|
296
|
+
const { unit, isUnitParsedCorrectly } = extractUnit(labToOhtMapper.unitExtraction, rawExamObj, ohtBiomarker);
|
|
297
|
+
if (!isUnitParsedCorrectly) {
|
|
298
|
+
logger.warn({ labToOhtMapper }, `Unit not extracted correctly for "${labToOhtMapper.ohtBmId}|${labToOhtMapper.labKey}", filename: ${filename}`);
|
|
299
|
+
labToOhtContract.slackMessages.push({
|
|
300
|
+
type: 'ERROR',
|
|
301
|
+
message: `Unit not extracted correctly for keys: [PLACEHOLDER]`,
|
|
302
|
+
item: `bmId:${labToOhtMapper.ohtBmId}, labKey:${labToOhtMapper.labKey}`
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
isReportCorrectlyParsed = isReportCorrectlyParsed && isUnitParsedCorrectly;
|
|
306
|
+
let { valueType, alphanumericalValue, numberValue, valueComparator, isValueParsedCorrectly } = parseExamValue(rawExamObj, labToOhtMapper.valueExtraction, ohtBiomarker);
|
|
307
|
+
if (!isValueParsedCorrectly) {
|
|
308
|
+
logger.warn({ labToOhtMapper }, `Value not extracted correctly for "${labToOhtMapper.ohtBmId}|${labToOhtMapper.labKey}", filename: ${filename}`);
|
|
309
|
+
labToOhtContract.slackMessages.push({
|
|
310
|
+
type: 'ERROR',
|
|
311
|
+
message: `Value not extracted correctly for keys: [PLACEHOLDER]`,
|
|
312
|
+
item: `bmId:${labToOhtMapper.ohtBmId}, labKey:${labToOhtMapper.labKey}`
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (valueType === 'NUMERICAL') {
|
|
316
|
+
const isPlausibleValue = numberValue !== null && checkValueForPlausibleValues(labToOhtMapper, ohtBiomarker, unit, numberValue, isUnitParsedCorrectly);
|
|
317
|
+
if (!isPlausibleValue) {
|
|
318
|
+
logger.warn({ labToOhtMapper }, `Value [${numberValue} ${unit}] is not plausible for "${labToOhtMapper.ohtBmId}|${labToOhtMapper.labKey}", filename: ${filename}`);
|
|
319
|
+
labToOhtContract.slackMessages.push({
|
|
320
|
+
type: 'ERROR',
|
|
321
|
+
message: `Value is not plausible for: [PLACEHOLDER]`,
|
|
322
|
+
item: `bmId:${labToOhtMapper.ohtBmId}, labKey:${labToOhtMapper.labKey}, value: ${numberValue}${unit}`
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
isReportCorrectlyParsed = isReportCorrectlyParsed && isPlausibleValue;
|
|
326
|
+
}
|
|
327
|
+
isReportCorrectlyParsed = isReportCorrectlyParsed && isValueParsedCorrectly;
|
|
328
|
+
let from = null, to = null;
|
|
329
|
+
let isRangeParsedCorrectly = true;
|
|
330
|
+
if ((0, dataUtils_1.isBiomarkerValueNumerical)(ohtBiomarker)) {
|
|
331
|
+
const extractedRange = extractReferenceRanges(labToOhtMapper, rawExamObj, labToOhtContract.patientInfo, labToOhtContract.documentDate);
|
|
332
|
+
from = extractedRange.from ?? null;
|
|
333
|
+
to = extractedRange.to ?? null;
|
|
334
|
+
isRangeParsedCorrectly = extractedRange.isRangeParsedCorrectly;
|
|
335
|
+
if (!isRangeParsedCorrectly) {
|
|
336
|
+
logger.warn({ labToOhtMapper }, `Range not extracted correctly for "${labToOhtMapper.ohtBmId}|${labToOhtMapper.labKey}", filename: ${filename}`);
|
|
337
|
+
labToOhtContract.slackMessages.push({
|
|
338
|
+
type: 'ERROR',
|
|
339
|
+
message: `Range not extracted correctly for keys: [PLACEHOLDER]`,
|
|
340
|
+
item: `bmId:${labToOhtMapper.ohtBmId}, labKey:${labToOhtMapper.labKey}`
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
isReportCorrectlyParsed = isReportCorrectlyParsed && isRangeParsedCorrectly;
|
|
344
|
+
}
|
|
345
|
+
let extractedAnnotation;
|
|
346
|
+
if (labToOhtMapper?.referenceAsAnnotation?.use) {
|
|
347
|
+
extractedAnnotation = extractReferenceAnnotation(labToOhtMapper, rawExamObj, labToOhtContract.patientInfo, labToOhtContract.documentDate);
|
|
348
|
+
}
|
|
349
|
+
const material = (0, dataUtils_1.extractValue)(rawExamObj, labToOhtMapper.material);
|
|
350
|
+
const method = (0, dataUtils_1.extractValue)(rawExamObj, labToOhtMapper.method);
|
|
351
|
+
let alphaValue = !(0, dataUtils_1.isBiomarkerValueNumerical)(ohtBiomarker) && alphanumericalValue
|
|
352
|
+
? alphanumericalValue.trim()
|
|
353
|
+
: null;
|
|
354
|
+
// adding unit for alphanumerical values as number.
|
|
355
|
+
if (!Number.isNaN(parseFloat(alphaValue))) {
|
|
356
|
+
let originalUnit = (0, dataUtils_1.extractValue)(rawExamObj, labToOhtMapper?.unitExtraction?.unitPosition);
|
|
357
|
+
originalUnit = originalUnit?.replaceAll(/ /g, '') ?? '';
|
|
358
|
+
alphaValue = alphaValue && unit?.length > 0 ? `${alphaValue} ${originalUnit}`.trim() : alphaValue;
|
|
359
|
+
}
|
|
360
|
+
let measurement = {
|
|
361
|
+
biomarkerId: ohtBiomarker.id,
|
|
362
|
+
valueType,
|
|
363
|
+
value: (0, dataUtils_1.isBiomarkerValueNumerical)(ohtBiomarker) ? numberValue : null,
|
|
364
|
+
alphanumericValue: alphaValue,
|
|
365
|
+
rangeAnnotation: labToOhtMapper?.referenceAsAnnotation?.use && extractedAnnotation ? extractedAnnotation : null,
|
|
366
|
+
showVisualisationIfAnnotated: !!labToOhtMapper?.referenceAsAnnotation?.use,
|
|
367
|
+
comparator: valueComparator ?? oht_types_1.MeasurementValueComparator.EQUAL,
|
|
368
|
+
minNormalValue: from,
|
|
369
|
+
maxNormalValue: to,
|
|
370
|
+
unit,
|
|
371
|
+
material,
|
|
372
|
+
method,
|
|
373
|
+
isDigitalizationApproved: true,
|
|
374
|
+
};
|
|
375
|
+
measurements.push(measurement);
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
logger.warn(`Created an ungroupedBiomarker for id: ${labToOhtMapper.labKey}, filename: ${filename}`);
|
|
379
|
+
labToOhtContract.slackMessages.push({
|
|
380
|
+
type: 'ERROR',
|
|
381
|
+
message: `Created ungroupedBiomarkers for: [PLACEHOLDER]`,
|
|
382
|
+
item: `labKey: ${labToOhtMapper.labKey}`
|
|
383
|
+
});
|
|
384
|
+
const biomarkerName = labToOhtMapper?.unknownMeasurement?.biomarkerName ?? '';
|
|
385
|
+
const value = labToOhtMapper?.unknownMeasurement?.value ?? '';
|
|
386
|
+
const unit = labToOhtMapper?.unknownMeasurement?.unit ?? '';
|
|
387
|
+
const annotation = labToOhtMapper?.unknownMeasurement?.annotation ?? '';
|
|
388
|
+
const unknownMeasurement = {
|
|
389
|
+
biomarkerName: biomarkerName,
|
|
390
|
+
value: value,
|
|
391
|
+
unit: unit,
|
|
392
|
+
annotation: annotation
|
|
393
|
+
};
|
|
394
|
+
unknownMeasurements.push(unknownMeasurement);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
measurements,
|
|
399
|
+
unknownMeasurements,
|
|
400
|
+
isReportCorrectlyParsed
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
//# sourceMappingURL=ohtMeasurementsExtractor.service.js.map
|