nuVistA/htdocs/reportparser.mjs

219 lines
9.6 KiB
JavaScript

function isEqualArray(a, b) {
if(a.length == b.length) {
for(var i = a.length - 1; i >= 0; --i) if(a[i] != b[i]) return false;
return true;
} else return false;
}
export function lab_parse(data) {
data = data.join('\n');
if(data == '\nNo Data Found') return [];
return data.split('\n===============================================================================\n \n').map(lab_parse1).filter(x => x);
}
export function lab_reparse_results(reports) {
var res = [], report, result;
for(var i = 0; i < reports.length; ++i) {
if((report = reports[i]).hasOwnProperty('results')) {
report = Object.assign({}, report);
var results = report.results;
delete report.results;
if(report.hasOwnProperty('comment')) delete report.comment;
for(var j = 0; j < results.length; ++j) res.push(result = Object.assign({}, report, results[j]));
}
}
return res;
}
function lab_parse1(data) {
if(data.startsWith('\n')) return lab_parse1default(data);
if(data.startsWith(' ---- MICROBIOLOGY ----\n')) return lab_parse1microbiology(data);
if(data.startsWith('Performing Lab Sites\n')) return null;
}
function lab_parse1default(data) {
var res = {}, m, x, line;
if(m = data.match(/^Report Released Date\/Time: (.*)/m)) res.time_released = new Date(m[1]); // 'Aug 24, 2022@07:15'
if(m = data.match(/^Provider: (.*)/m)) res.practitioner = m[1]; // 'BARGNES,VINCENT HARRY III'
if(m = data.match(/^ Specimen: (.*?)\.\s*(.*)/m)) {
res.specimen = m[1]; // 'SERUM'
res.accession = m[2]; // 'CH 0800 6706'
}
if(m = data.match(/^ Specimen Collection Date: (.*)/m)) res.time_collected = new Date(m[1]); // 'Aug 24, 2022'
data = data.split('\n Test name Result units Ref. range Site Code\n')[1].split('\n');
var results = res.results = [];
for(var i = 0; i < data.length; ++i) {
if((line = data[i]).startsWith('Comment: ')) {
res.comment = data.slice(i).join('\n').substring(9);
break;
} else if(line.startsWith(' Eval: ')) {
if(results.length > 0) {
x = results[results.length - 1];
if(x.comment) x.comment.push(line.substring(12));
else x.comment = [line.substring(12)];
} else console.log('DANGLING:', line);
} else if(m = line.match(/^\b(?<name>.*?)\s{2,}(?<value>.*?)(?: (?<flag>L\*|L|H\*|H))?\s+(?:(?<unit>.{10}) (?<range>.*?)(?: \[(?<site>\d+)\])?)?$/)) {
if(m.groups.name.startsWith('COVID-19 SCR (CEPHEID-RAPID)')) {
m = line.substring(28).match(/^(?<value>.*?)(?: (?<flag>L\*|L|H\*|H))?\s+(?:(?<unit>.{10}) (?<range>.*?)(?: \[(?<site>\d+)\])?)?$/);
m.groups.name = 'COVID-19 SCR (CEPHEID-RAPID)';
}
if((m.groups.range) && (m.groups.range.startsWith('Ref: '))) m.groups.range = m.groups.range.substring(5);
results.push(x = m.groups);
if((x.value === '') && (m = x.name.match(/^(?<name>.*?)(?<value>(?:[\d\.]+|positive|negative|reactive|not detected|collected - specimen in lab|test not performed))\s*$/i))) {
x.name = m.groups.name;
x.value = m.groups.value;
}
for(var k in x) if(x[k]) x[k] = x[k] ? x[k].replace(/^\s+|\s+$/g, '') : undefined;
} else if(m = line.match(/^\b(?<name>.*?)(?<value>(?:[\d\.]+|positive|negative|reactive|not detected|collected - specimen in lab|test not performed))\s*$/i)) {
results.push(x = m.groups);
for(var k in x) if(x[k]) x[k] = x[k] ? x[k].replace(/^\s+|\s+$/g, '') : undefined;
} else if(line.startsWith(' [')) {
if(results.length > 0) results[results.length - 1].site = line.split('[')[1].split(']')[0]
else console.log('DANGLING:', line);
} else if(line.startsWith(' ')) {
if(results.length > 0) {
x = results[results.length - 1];
if(line.endsWith(']')) {
x.range = line.split('[')[0].replace(/^\s+|\s+$/g, '');
x.site = line.split('[')[1].split(']')[0];
} else x.range = line.replace(/^\s+|\s+$/g, '');
} else console.log('DANGLING:', line);
} else if(m = line.match(/^\s{40}\b(?:(?<unit>.{10}) (?<range>.*?)(?: \[(?<site>\d+)\])?)?$/)) {
if(results.length > 0) {
x = results[results.length - 1];
if(m.groups.unit) x.unit = m.groups.unit.replace(/^\s+|\s+$/g, '');
if(m.groups.range) x.range = m.groups.range.replace(/^\s+|\s+$/g, '');
if(m.groups.site) x.site = m.groups.site.replace(/^\s+|\s+$/g, '');
} else console.log('DANGLING:', line, m.groups);
} else console.log('INVALID:', line);
}
for(var i = results.length - 1; i >= 0; --i) {
results[(x = results[i]).name] = x;
if(x.comment) x.comment = x.comment.join('\n');
}
if(res.accession.startsWith('HE ')) {
if((results.hasOwnProperty('SEGS')) || (results.hasOwnProperty('BANDS'))) {
results.push(results['NEUTROPHIL%'] = {
name: 'NEUTROPHIL%', unit: '%', range: '42.2 - 75.2',
value: x = (results.hasOwnProperty('SEGS') ? +results.SEGS.value : 0) + (results.hasOwnProperty('BANDS') ? +results.BANDS.value : 0),
flag: (x < 42.2 ? 'L' : x > 75.2 ? 'H' : undefined)
});
results.push(results['NEUTROPHIL#'] = {
name: 'NEUTROPHIL#', unit: 'K/cmm', range: '1.4 - 6.5',
value: +(x = 0.01*x*results.WBC.value).toFixed(3),
flag: (x < 1.4 ? 'L' : x > 6.5 ? 'H' : undefined)
});
}
if((results.hasOwnProperty('LYMPHS')) || (results.hasOwnProperty('ATYPICAL LYMPHOCYTES'))) {
results.push(results['LYMPHOCYTE%'] = {
name: 'LYMPHOCYTE%', unit: '%', range: '15.0 - 41.0',
value: x = (results.hasOwnProperty('LYMPHS') ? +results.LYMPHS.value : 0) + (results.hasOwnProperty('ATYPICAL LYMPHOCYTES') ? +results['ATYPICAL LYMPHOCYTES'].value : 0),
flag: (x < 15 ? 'L' : x > 41 ? 'H' : undefined)
});
results.push(results['LYMPHOCYTE#'] = {
name: 'LYMPHOCYTE#', unit: 'K/cmm', range: '1.2 - 3.4',
value: +(x = 0.01*x*results.WBC.value).toFixed(3),
flag: (x < 1.2 ? 'L' : x > 3.4 ? 'H' : undefined)
});
}
}
return res;
}
function lab_parse1microbiology(data) {
var res = {}, lines = data.split('\n'), line, m;
var idx_body = lines.indexOf(' ');
for(var i = 0; i < lines.length; ++i) {
line = lines[i];
if(line.startsWith('Accession [UID]: ')) {
if(m = line.match(/^Accession \[UID\]: (?<accession>.*?) \[(?<accession_uid>\d+)\]/)) { // 'BCUL 22 819 [3922000819]'
res.accession = m.groups.accession;
res.accession_uid = m.groups.accession_uid;
}
if(m = line.match(/Received: (.*)$/)) res.time_received = new Date(m[1]); // 'Aug 01, 2022@11:57'
} else if(line.startsWith('Collection sample: ')) {
res.sample = line.substring(0, 39).substring(19).replace(/^\s+|\s+$/g, '');
res.time_collected = new Date(line.substring(39).split('Collection date: ')[1].replace(/^\s+|\s+$/g, ''));
} else if(line.startsWith('Site/Specimen: ')) {
res.specimen = line.substring(15).replace(/^\s+|\s+$/g, '');
} else if(line.startsWith('Provider: ')) {
res.practitioner = line.substring(10).replace(/^\s+|\s+$/g, '');
} else if(line.startsWith('Comment on specimen:')) {
res.comment = lines.slice(i, idx_body).join('\n').substring(20).replace(/^\s+|\s+$/g, '');
break
}
}
var idx_footer = lines.indexOf('=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--')
if(idx_footer > idx_body) {
res.body = lines.slice(idx_body, idx_footer).join('\n').replace(/^\s+|\s+$/g, '');
res.footer = lines.slice(idx_footer + 1).join('\n').replace(/^\s+|\s+$/g, '');
} else res.body = lines.slice(idx_body).join('\n').replace(/^\s+|\s+$/g, '');
return res;
}
export function measurement_parse(data) {
var extras = [];
var res = data.map(function(row) {
if(row.charAt(0) != ' ') {
var res = {}, idx = 0, value, m;
res.measurement_ien = row.substring(0, idx = row.indexOf('^'));
if(res.measurement_ien == '0') return; // '0^NO VITALS/MEASUREMENTS ENTERED WITHIN THIS PERIOD'
res.datetime = new Date(row.substring(idx + 1, idx = row.indexOf(' ', idx)));
res.name = row.substring(idx + 3, idx = row.indexOf(': ', idx));
value = row.substring(idx + 4, idx = row.indexOf(' _', idx));
res.user = row.substring(idx + 2);
m = value.match(/^(?:(.*?)(?: (\S+))?)(\*)?(?: \((?:(.*?)(?: (\S+))?)\))?\s*$/);
res.value = m[4] ? m[4] : m[1];
res.unit = m[4] ? m[5] : m[2];
res.flag = m[3];
res.value_american = m[4] ? m[1] : m[4];
res.unit_american = m[4] ? m[2] : m[5];
if(res.value.charAt(res.value.length - 1) == '%') {
res.unit = '%';
res.value = res.value.substring(0, res.value.length - 1);
}
if(res.name == 'B/P') {
var bpsplit = res.value.split('/');
extras.push({...res, name: 'SBP', range: '90 - 120', unit: 'mmHg', value: bpsplit[0] });
extras.push({...res, name: 'DBP', range: '60 - 80', unit: 'mmHg', value: bpsplit[1] });
}
return res;
}
}).filter(x => x);
res.push(...extras);
return res;
}
export function order_parse(data) {
var res = [], item, line;
for(var i = 0; i < data.length; ++i) {
if((line = data[i]).startsWith('~')) {
res.push(item = line.slice(1).split('^'));
item.IFN = item[0];
item.Grp = item[1];
item.OrdTm = item[2];
item.StrtTm = item[3];
item.StopTm = item[4];
item.Sts = item[5];
item.Sig = item[6];
item.Nrs = item[7];
item.Clk = item[8];
item.PrvID = item[9];
item.PrvNam = item[10];
item.Act = item[11];
item.Flagged = item[12];
item.DCType = item[13];
item.ChartRev = item[14];
item.DEA = item[15];
item.DigSig = item[17];
item.LOC = item[18];
item.DCORIGINAL = item[19];
item.IsPendingDCorder = item[20];
item.IsDelayOrder = item[21];
item.text = [];
} else if((item) && (line.startsWith('t'))) item.text.push(line.slice(1));
else console.log('INVALID:', line);
}
return res;
}