diff --git a/analyzers/GeneXpert/.vscode/settings.json b/analyzers/GeneXpert/.vscode/settings.json new file mode 100644 index 0000000..890c247 --- /dev/null +++ b/analyzers/GeneXpert/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "java.project.sourcePaths": [ + "src/main/java" + ] +} \ No newline at end of file diff --git a/analyzers/GeneXpert/pom.xml b/analyzers/GeneXpert/pom.xml index 6ef1f51..f22725d 100644 --- a/analyzers/GeneXpert/pom.xml +++ b/analyzers/GeneXpert/pom.xml @@ -3,7 +3,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.openelisglobal.plugins - GeneXpert + GeneXpertAnalyzer org.openelisglobal openelisglobal-plugins @@ -11,7 +11,7 @@ ../../pom.xml - 0.3 + 0.4 UTF-8 2.3 @@ -25,14 +25,9 @@ provided - ca.uhn.hapi - hapi-hl7overhttp - ${hapi.version} - - - ca.uhn.hapi - hapi-structures-v25 - ${hapi.version} + commons-fileupload + commons-fileupload + 1.4 org.springframework diff --git a/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzer.java b/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzer.java index bc4e54d..4898cfb 100644 --- a/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzer.java +++ b/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzer.java @@ -5,7 +5,7 @@ * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * basis, WITHOUT WARRANTY OF ANY KIND, either eXNLress or implied. See the * License for the specific language governing rights and limitations under * the License. * @@ -20,39 +20,165 @@ import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerLineInserter; import org.openelisglobal.common.services.PluginAnalyzerService; import org.openelisglobal.plugin.AnalyzerImporterPlugin; +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerResponder; +import org.openelisglobal.common.log.LogEvent; public class GeneXpertAnalyzer implements AnalyzerImporterPlugin { + + public static final String ANALYZER_NAME = "GeneXpertAnalyzer"; - @Override - public boolean connect() { + @Override + public boolean connect() { List nameMapping = new ArrayList<>(); nameMapping.add( - new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.HBV, "HEPATITIS B VIRAL LOAD", - GeneXpertAnalyzerImplementation.HBV_LOINC)); + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_WBC, "White Blood Cells Count (WBC)", + GeneXpertAnalyzerImplementation.LOINC_WBC)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_RBC, "Red Blood Cells Count (RBC)", + GeneXpertAnalyzerImplementation.LOINC_RBC)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_HGB, "Hemoglobin", + GeneXpertAnalyzerImplementation.LOINC_HGB)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_HCT, "Hematocrit", + GeneXpertAnalyzerImplementation.LOINC_HCT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_MCV, "Medium corpuscular volum", + GeneXpertAnalyzerImplementation.LOINC_MCV)); nameMapping.add( - new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.HCV, "HEPATITIS C VIRAL LOAD", - GeneXpertAnalyzerImplementation.HCV_LOINC)); + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_MCH, "", + GeneXpertAnalyzerImplementation.LOINC_MCH)); + nameMapping + .add(new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_MCHC, "", + GeneXpertAnalyzerImplementation.LOINC_MCHC)); + nameMapping + .add(new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_RDWSD, "", + GeneXpertAnalyzerImplementation.LOINC_RDWSD)); nameMapping.add( - new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.HIV_QUAL, "​Xpert HIV-1 Qual", - GeneXpertAnalyzerImplementation.HIV_QUAL_LOINC)); + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_RDWCV, "", + GeneXpertAnalyzerImplementation.LOINC_RDWCV)); nameMapping - .add(new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.HIV_VIRAL, "HIV VIRAL LOAD", - GeneXpertAnalyzerImplementation.HIV_VIRAL_LOINC)); + .add(new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_PLT, "Platelets", + GeneXpertAnalyzerImplementation.LOINC_PLT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_MPV, "", + GeneXpertAnalyzerImplementation.LOINC_MPV)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_NEUT_COUNT, "Neutrophiles", + GeneXpertAnalyzerImplementation.LOINC_NEUT_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_NEUT_PERCENT, "Neutrophiles (%)", + GeneXpertAnalyzerImplementation.LOINC_NEUT_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_LYMPH_COUNT, "Lymphocytes (Abs)", + GeneXpertAnalyzerImplementation.LOINC_LYMPH_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_LYMPH_PERCENT, "Lymphocytes (%)", + GeneXpertAnalyzerImplementation.LOINC_LYMPH_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_MONO_COUNT, "Monocytes (Abs)", + GeneXpertAnalyzerImplementation.LOINC_MONO_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_MONO_PERCENT, "Monocytes (%)", + GeneXpertAnalyzerImplementation.LOINC_MONO_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_EO_COUNT, "Eosinophiles", + GeneXpertAnalyzerImplementation.LOINC_EO_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_EO_PERCENT, "Eosinophiles (%)", + GeneXpertAnalyzerImplementation.LOINC_EO_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_BASO_COUNT, "Basophiles", + GeneXpertAnalyzerImplementation.LOINC_BASO_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_BASO_PERCENT, "Basophiles (%)", + GeneXpertAnalyzerImplementation.LOINC_BASO_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_IG_COUNT, "", + GeneXpertAnalyzerImplementation.LOINC_IG_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_IG_PERCENT, "", + GeneXpertAnalyzerImplementation.LOINC_IG_PERCENT)); nameMapping.add( - new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.COV_2, "COVID-19 PCR", - GeneXpertAnalyzerImplementation.COV_2_LOINC)); + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_RET_COUNT, "", + GeneXpertAnalyzerImplementation.LOINC_RET_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_RET_PERCENT, "", + GeneXpertAnalyzerImplementation.LOINC_RET_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_IRF, "", + GeneXpertAnalyzerImplementation.LOINC_IRF)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_RETHE, "", + GeneXpertAnalyzerImplementation.LOINC_RETHE)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_WBCBF, "", + GeneXpertAnalyzerImplementation.LOINC_WBCBF)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_RBCBF, "", + GeneXpertAnalyzerImplementation.LOINC_RBCBF)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_MN_COUNT, "", + GeneXpertAnalyzerImplementation.LOINC_MN_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_MN_PERCENT, "", + GeneXpertAnalyzerImplementation.LOINC_MN_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_PMN_COUNT, "", + GeneXpertAnalyzerImplementation.LOINC_PMN_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_PMN_PERCENT, "", + GeneXpertAnalyzerImplementation.LOINC_PMN_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.ANALYZER_TEST_TCBF_COUNT, "", + GeneXpertAnalyzerImplementation.LOINC_TCBF_COUNT)); getInstance().addAnalyzerDatabaseParts("GeneXpertAnalyzer", "GeneXpertAnalyzer", nameMapping, true); getInstance().registerAnalyzer(this); return true; } @Override - // this plugin does not work for flat files, so we disable it in that workflow public boolean isTargetAnalyzer(List lines) { + for (String line : lines) { + if (line.startsWith(GeneXpertAnalyzerImplementation.HEADER_RECORD_IDENTIFIER)) { + String[] headerRecord = line.split(Pattern.quote(GeneXpertAnalyzerImplementation.FD)); + if (headerRecord.length < 5) { + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is not GeneXpert: header record not long enough"); + return false; + } + String[] senderNameFields = headerRecord[4].split(Pattern.quote(GeneXpertAnalyzerImplementation.CD)); + if (senderNameFields.length < 2) { + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is not GeneXpert: sender name field not long enough"); + return false; + } + String systemName = senderNameFields[1].trim(); + + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message analyzer name is " + systemName); + if (systemName.equalsIgnoreCase("GeneXpert")) { + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is GeneXpert "); + return true; + } + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is not GeneXpert: sender name doesn't match"); + return false; + } + } + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is not GeneXpert: no header line"); + return false; + } + + @Override + public boolean isAnalyzerResult(List lines) { + for (String line : lines) { + if (line.startsWith(GeneXpertAnalyzerImplementation.RESULT_RECORD_IDENTIFIER)) { + return true; + } + } + LogEvent.logDebug(this.getClass().getSimpleName(), "isAnalyzerResult", "no result recoord identifier located"); return false; } @@ -61,4 +187,9 @@ public AnalyzerLineInserter getAnalyzerLineInserter() { return new GeneXpertAnalyzerImplementation(); } + @Override + public AnalyzerResponder getAnalyzerResponder() { + return new GeneXpertAnalyzerImplementation(); + } + } diff --git a/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzerImplementation.java b/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzerImplementation.java index 504d316..dc1cc62 100644 --- a/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzerImplementation.java +++ b/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzerImplementation.java @@ -14,56 +14,211 @@ * Copyright (C) ITECH, University of Washington, Seattle WA. All Rights Reserved. */ -package oe.plugin.analyzer; + package oe.plugin.analyzer; import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; +import java.util.Optional; +import org.apache.commons.validator.GenericValidator; import org.openelisglobal.analyzer.service.AnalyzerService; import org.openelisglobal.analyzer.valueholder.Analyzer; import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerLineInserter; import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerReaderUtil; +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerResponder; +import org.openelisglobal.analyzerimport.util.AnalyzerTestNameCache; +import org.openelisglobal.analyzerimport.util.MappedTestName; import org.openelisglobal.analyzerresults.valueholder.AnalyzerResults; +import org.openelisglobal.common.services.PluginAnalyzerService; import org.openelisglobal.spring.util.SpringContext; import org.openelisglobal.test.service.TestService; +import org.openelisglobal.sample.service.SampleService; +import org.openelisglobal.samplehuman.service.SampleHumanService; +import org.openelisglobal.analysis.service.AnalysisService; import org.openelisglobal.test.valueholder.Test; +import org.openelisglobal.analysis.valueholder.Analysis; +import org.openelisglobal.person.valueholder.Person; +import org.openelisglobal.patient.valueholder.Patient; +import org.openelisglobal.sample.valueholder.Sample; +import org.openelisglobal.common.util.DateUtil; +import org.openelisglobal.common.log.LogEvent; + +public class GeneXpertAnalyzerImplementation extends AnalyzerLineInserter implements AnalyzerResponder { + + static final String ANALYZER_TEST_WBC = "WBC"; + static final String ANALYZER_TEST_RBC = "RBC"; + static final String ANALYZER_TEST_HGB = "HGB"; + static final String ANALYZER_TEST_HCT = "HCT"; + static final String ANALYZER_TEST_MCV = "MCV"; + static final String ANALYZER_TEST_MCH = "MCH"; + static final String ANALYZER_TEST_MCHC = "MCHC"; + static final String ANALYZER_TEST_RDWSD = "RDW-CD"; + static final String ANALYZER_TEST_RDWCV = "RDW-CV"; + static final String ANALYZER_TEST_PLT = "PLT"; + static final String ANALYZER_TEST_MPV = "MPV"; + static final String ANALYZER_TEST_IPF = "IPF"; + static final String ANALYZER_TEST_IPF_COUNT = "IPF#"; + static final String ANALYZER_TEST_NEUT_COUNT = "NEUT#"; + static final String ANALYZER_TEST_NEUT_PERCENT = "NEUT%"; + static final String ANALYZER_TEST_LYMPH_COUNT = "LYMPH#"; + static final String ANALYZER_TEST_LYMPH_PERCENT = "LYMPH%"; + static final String ANALYZER_TEST_MONO_COUNT = "MONO#"; + static final String ANALYZER_TEST_MONO_PERCENT = "MONO%"; + static final String ANALYZER_TEST_EO_COUNT = "EO#"; + static final String ANALYZER_TEST_EO_PERCENT = "EO%"; + static final String ANALYZER_TEST_BASO_COUNT = "BASO#"; + static final String ANALYZER_TEST_BASO_PERCENT = "BASO%"; + static final String ANALYZER_TEST_IG_COUNT = "IG#"; + static final String ANALYZER_TEST_IG_PERCENT = "IG%"; + static final String ANALYZER_TEST_PDW = "PDW"; // flagging only + static final String ANALYZER_TEST_PLCR = "P-LCR"; // flagging only + static final String ANALYZER_TEST_PCT = "PCT"; // flagging only + static final String ANALYZER_TEST_RET_PERCENT = "RET%"; + static final String ANALYZER_TEST_RET_COUNT = "RET#"; + static final String ANALYZER_TEST_IRF = "IRF"; + static final String ANALYZER_TEST_LFR = "LFR"; // flagging only + static final String ANALYZER_TEST_MFR = "MFR"; // flagging only + static final String ANALYZER_TEST_HFR = "HFR"; // flagging only + static final String ANALYZER_TEST_LWBC = "LWBC"; // flagging only + static final String ANALYZER_TEST_RETHE = "RET-HE"; + static final String ANALYZER_TEST_WBCBF = "WBC-BF"; + static final String ANALYZER_TEST_RBCBF = "RBC-BF"; + static final String ANALYZER_TEST_MN_COUNT = "MN#"; + static final String ANALYZER_TEST_MN_PERCENT = "MN%"; + static final String ANALYZER_TEST_PMN_COUNT = "PMN#"; + static final String ANALYZER_TEST_PMN_PERCENT = "PMN%"; + static final String ANALYZER_TEST_TCBF_COUNT = "TC-BF#"; + + static final String LOINC_WBC = "6690-2"; + static final String LOINC_RBC = "789-8"; + static final String LOINC_HGB = "718-7"; + static final String LOINC_HCT = "4544-3"; + static final String LOINC_MCV = "787-2"; + static final String LOINC_MCH = "785-6"; + static final String LOINC_MCHC = "786-4"; + static final String LOINC_RDWSD = "21000-5"; + static final String LOINC_RDWCV = "788-0"; + static final String LOINC_PLT = "777-3"; + static final String LOINC_MPV = "32623-1"; + static final String LOINC_NEUT_COUNT = "751-8"; + static final String LOINC_NEUT_PERCENT = "770-8"; + static final String LOINC_LYMPH_COUNT = "731-0"; + static final String LOINC_LYMPH_PERCENT = "736-9"; + static final String LOINC_MONO_COUNT = "742-7"; + static final String LOINC_MONO_PERCENT = "5905-5"; + static final String LOINC_EO_COUNT = "711-2"; + static final String LOINC_EO_PERCENT = "713-8"; + static final String LOINC_BASO_COUNT = "704-7"; + static final String LOINC_BASO_PERCENT = "706-2"; + static final String LOINC_IG_COUNT = "53115-2"; + static final String LOINC_IG_PERCENT = "71695-1"; + // static final String LOINC_PDW = ""; // flagging only + // static final String LOINC_PLCR = ""; // flagging only + // static final String LOINC_PCT = ""; // flagging only + static final String LOINC_RET_PERCENT = "17849-1"; + static final String LOINC_RET_COUNT = "60474-4"; + static final String LOINC_IRF = "33516-6"; + // static final String LOINC_LFR = ""; // flagging only + // static final String LOINC_MFR = ""; // flagging only + // static final String LOINC_HFR = ""; // flagging only + // static final String LOINC_LWBC = ""; // flagging only + static final String LOINC_RETHE = "71694-4"; + static final String LOINC_WBCBF = "57845-0"; + static final String LOINC_RBCBF = "23860-0"; + static final String LOINC_MN_COUNT = "71689-4"; + static final String LOINC_PMN_COUNT = "71698-5"; + static final String LOINC_MN_PERCENT = "71697-7"; + static final String LOINC_PMN_PERCENT = "71696-9"; + static final String LOINC_TCBF_COUNT = "71690-2"; + + protected static final String HEADER_RECORD_IDENTIFIER = "H"; + protected static final String QUERY_RECORD_IDENTIFIER = "Q"; + protected static final String PATIENT_RECORD_IDENTIFIER = "P"; + protected static final String ORDER_RECORD_IDENTIFIER = "O"; + protected static final String RESULT_RECORD_IDENTIFIER = "R"; + protected static final String END_RECORD_IDENTIFIER = "L"; + protected static final String FD = "|"; //DEFAULT_FIELD_DELIMITER + protected static final String RD = "@"; //DEFAULT_REPEATER_DELIMITER + protected static final String CD = "^"; //DEFAULT_COMPONENT_DELIMITER + protected static final String ED = "\\"; //DEFAULT_ESCAPE_DELIMITER + protected static final String TEST_COMMUNICATION_IDENTIFIER = "M|1|106"; -public class GeneXpertAnalyzerImplementation extends AnalyzerLineInserter { + private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); + private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyyMMddHHmmss"); + private TestService testService = SpringContext.getBean(TestService.class); + private SampleService sampleService = SpringContext.getBean(SampleService.class); + private SampleHumanService sampleHumanService = SpringContext.getBean(SampleHumanService.class); private AnalyzerService analyzerService = SpringContext.getBean(AnalyzerService.class); - - - static final String HBV = "Xpert HBV Viral Load"; - static final String HCV = "Xpert HCV Viral Load"; - static final String HIV_QUAL = "Xpert HIV-1 Qual"; - static final String HIV_VIRAL = "Xpert HIV-1 viral Load"; - static final String COV_2 = "Xpert Xpress SARS-CoV-2 assay"; - - static final String HBV_LOINC = "29615-2"; - static final String HCV_LOINC = "11011-4"; - static final String HIV_QUAL_LOINC = ""; - static final String HIV_VIRAL_LOINC = "10351-5"; - static final String COV_2_LOINC = "94500-6"; - + private AnalysisService analysisService = SpringContext.getBean(AnalysisService.class); + private String ANALYZER_ID; - private HashMap> testLoincMap = new HashMap<>(); - + private Map testToLoincMap = new HashMap<>(); + private Map loincToTestCodeMap = new HashMap<>(); + private Map> testCodeToTestsMap = new HashMap<>(); + private AnalyzerReaderUtil readerUtil = new AnalyzerReaderUtil(); - + public GeneXpertAnalyzerImplementation() { - testLoincMap.put(HBV_LOINC, testService.getTestsByLoincCode(HBV_LOINC)); - testLoincMap.put(HCV_LOINC, testService.getTestsByLoincCode(HCV_LOINC)); - testLoincMap.put(HIV_QUAL_LOINC, testService.getTestsByLoincCode(HIV_QUAL_LOINC)); - testLoincMap.put(HIV_VIRAL_LOINC, testService.getTestsByLoincCode(HIV_VIRAL_LOINC)); - testLoincMap.put(COV_2_LOINC, testService.getTestsByLoincCode(COV_2_LOINC)); + testToLoincMap.put(ANALYZER_TEST_WBC, LOINC_WBC); + testToLoincMap.put(ANALYZER_TEST_RBC, LOINC_RBC); + testToLoincMap.put(ANALYZER_TEST_HGB, LOINC_HGB); + testToLoincMap.put(ANALYZER_TEST_HCT, LOINC_HCT); + testToLoincMap.put(ANALYZER_TEST_MCV, LOINC_MCV); + testToLoincMap.put(ANALYZER_TEST_MCH, LOINC_MCH); + testToLoincMap.put(ANALYZER_TEST_MCHC, LOINC_MCHC); + testToLoincMap.put(ANALYZER_TEST_RDWSD, LOINC_RDWSD); + testToLoincMap.put(ANALYZER_TEST_RDWCV, LOINC_RDWCV); + testToLoincMap.put(ANALYZER_TEST_PLT, LOINC_PLT); + testToLoincMap.put(ANALYZER_TEST_MPV, LOINC_MPV); + testToLoincMap.put(ANALYZER_TEST_NEUT_COUNT, LOINC_NEUT_COUNT); + testToLoincMap.put(ANALYZER_TEST_NEUT_PERCENT, LOINC_NEUT_PERCENT); + testToLoincMap.put(ANALYZER_TEST_LYMPH_COUNT, LOINC_LYMPH_COUNT); + testToLoincMap.put(ANALYZER_TEST_LYMPH_PERCENT, LOINC_LYMPH_PERCENT); + testToLoincMap.put(ANALYZER_TEST_MONO_COUNT, LOINC_MONO_COUNT); + testToLoincMap.put(ANALYZER_TEST_MONO_PERCENT, LOINC_MONO_PERCENT); + testToLoincMap.put(ANALYZER_TEST_EO_COUNT, LOINC_EO_COUNT); + testToLoincMap.put(ANALYZER_TEST_EO_PERCENT, LOINC_EO_PERCENT); + testToLoincMap.put(ANALYZER_TEST_BASO_COUNT, LOINC_BASO_COUNT); + testToLoincMap.put(ANALYZER_TEST_BASO_PERCENT, LOINC_BASO_PERCENT); + testToLoincMap.put(ANALYZER_TEST_IG_COUNT, LOINC_IG_COUNT); + testToLoincMap.put(ANALYZER_TEST_IG_PERCENT, LOINC_IG_PERCENT); + // testToLoincMap.put(ANALYZER_TEST_PDW, LOINC_PDW); + // testToLoincMap.put(ANALYZER_TEST_PLCR, LOINC_PLCR); + // testToLoincMap.put(ANALYZER_TEST_PCT, LOINC_PCT); + testToLoincMap.put(ANALYZER_TEST_RET_COUNT, LOINC_RET_COUNT); + testToLoincMap.put(ANALYZER_TEST_RET_PERCENT, LOINC_RET_PERCENT); + testToLoincMap.put(ANALYZER_TEST_IRF, LOINC_IRF); + // testToLoincMap.put(ANALYZER_TEST_LFR, LOINC_LFR); + // testToLoincMap.put(ANALYZER_TEST_MFR, LOINC_MFR); + // testToLoincMap.put(ANALYZER_TEST_HFR, LOINC_HFR); + // testToLoincMap.put(ANALYZER_TEST_LWBC, LOINC_LWBC); + testToLoincMap.put(ANALYZER_TEST_RETHE, LOINC_RETHE); + testToLoincMap.put(ANALYZER_TEST_WBCBF, LOINC_WBCBF); + testToLoincMap.put(ANALYZER_TEST_RBCBF, LOINC_RBCBF); + testToLoincMap.put(ANALYZER_TEST_MN_COUNT, LOINC_MN_COUNT); + testToLoincMap.put(ANALYZER_TEST_PMN_COUNT, LOINC_PMN_COUNT); + testToLoincMap.put(ANALYZER_TEST_MN_PERCENT, LOINC_MN_PERCENT); + testToLoincMap.put(ANALYZER_TEST_TCBF_COUNT, LOINC_TCBF_COUNT); + for (Entry entry : testToLoincMap.entrySet()) { + loincToTestCodeMap.put(entry.getValue(), entry.getKey()); + testCodeToTestsMap.put(entry.getKey(), testService.getTestsByLoincCode(entry.getValue())); + } + Analyzer analyzer = analyzerService.getAnalyzerByName("GeneXpertAnalyzer"); ANALYZER_ID = analyzer.getId(); } - + + // example message: /* * (non-Javadoc) * @@ -73,40 +228,156 @@ public GeneXpertAnalyzerImplementation() { */ @Override public boolean insert(List lines, String currentUserId) { - return false; + + String patientRecord = null; + String orderRecord = null; + + List results = new ArrayList<>(); + for (String line : lines) { + if (line.startsWith(TEST_COMMUNICATION_IDENTIFIER)) { + LogEvent.logInfo(this.getClass().getName(), "insert", "this is a test communication record for GeneXpert"); + } + if (line.startsWith(PATIENT_RECORD_IDENTIFIER)) { + if (patientRecord != null) { + patientRecord = null; + orderRecord = null; + } + patientRecord = line; + } + if (line.startsWith(ORDER_RECORD_IDENTIFIER)) { + orderRecord = line; + } + if (line.startsWith(RESULT_RECORD_IDENTIFIER)) { + LogEvent.logDebug(this.getClass().getName(), "insert", "adding result"); + + addRecordsToResults(patientRecord, orderRecord, line, results, currentUserId); + } + if (line.startsWith(END_RECORD_IDENTIFIER)) { + LogEvent.logDebug(this.getClass().getName(), "insert", "end Aquios of record"); + break; + } + } + return persistImport(currentUserId, results); } - + @Override public String getError() { - return "GeneXpert analyzer unable to write to database"; + return "GeneXpertAnalyzer analyzer unable to write to database"; + } + + private Test findMatchingTest(Sample sample, String resultTestCode) { + if (sample != null) { + for (Analysis curAnalysis : analysisService.getAnalysesBySampleId(sample.getId())) { + List possibleTests = testCodeToTestsMap.get(resultTestCode); + // if ((possibleTests == null || possibleTests.size() == 0) && resultTestCode.contains("+")) { + // possibleTests = testCodeToTestsMap.get(resultTestCode); + // } + if (possibleTests != null) { + for (Test curTest : possibleTests) { + if (curTest.getLoinc() != null && curTest.getLoinc().equals(curAnalysis.getTest().getLoinc())) { + LogEvent.logDebug(this.getClass().getSimpleName(), "findMatchingTest", "found test in sample for code: " + resultTestCode); + return curAnalysis.getTest(); + } + } + } + } + } + return null; } - public void addResult(List resultList, List notMatchedResults, String resultType, + private void addRecordsToResults(String patientRecord, String orderRecord, String resultRecord, + List results, String currentUserId) { + String[] patientRecordFields = patientRecord.split(Pattern.quote(FD)); + String[] orderRecordFields = orderRecord.split(Pattern.quote(FD)); + String[] orderTestIdFields = orderRecordFields[4].split(Pattern.quote(RD)); + String[] orderIdFields = orderRecordFields[3].split(Pattern.quote(CD)); + String[] resultRecordFields = resultRecord.split(Pattern.quote(FD)); + String[] resultTestIdField = resultRecordFields[2].split(Pattern.quote(CD)); + String resultRecordAbnormalFlag = resultRecordFields[6]; + List orderTestIds = new ArrayList<>(); + for (String orderIdField : orderTestIdFields) { + String[] orderIds = orderIdField.split(Pattern.quote(CD)); + String orderTestId = orderIds.length >= 5 ? orderIds[4] : ""; + if (GenericValidator.isBlankOrNull(orderTestId)) { + LogEvent.logWarn(this.getClass().getSimpleName(), "addRecordsToResults", "order analysis parameter name is not present"); + } + orderTestIds.add(orderTestId); + } + if (orderTestIds.size() <= 0) { + LogEvent.logWarn(this.getClass().getSimpleName(), "addRecordsToResults", "order analysis has no tests specified"); + } + String resultTestId = resultTestIdField.length >= 4 ? resultTestIdField[3] : ""; + + String currentAccessionNumber = orderIdFields[2].trim(); + Sample sample = sampleService.getSampleByAccessionNumber(currentAccessionNumber); + Test test = findMatchingTest(sample, resultTestId); + + if (test == null) { + LogEvent.logError(this.getClass().getName(), "addRecordsToResults", + "can't import a result if order does not have that test ordered"); + return; + } + + switch (resultRecordAbnormalFlag) { + case "<": //below absolute low + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "below absolute low"); + break; + case ">": //above absolute high + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "above absolute high"); + break; + case "N": //normal + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "normal"); + break; + case "A": //abnormal + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "abnormal"); + break; + default: + LogEvent.logWarn(this.getClass().getSimpleName(), "addRecordsToResults", "abnormal flag not understood"); + } + AnalyzerResults analyzerResults = addResult(results, null, "N", resultRecordFields[3], + DateUtil.convertStringDateToTimestampWithPattern(resultRecordFields[12], "yyyyMMddHHmmss"), + currentAccessionNumber, false, resultRecordFields[4], test); + LogEvent.logDebug(this.getClass().getName(), "addResultLine", "***" + analyzerResults.getAccessionNumber() + " " + + analyzerResults.getCompleteDate() + " " + analyzerResults.getResult()); + } + + public AnalyzerResults addResult(List resultList, List notMatchedResults, String resultType, String resultValue, Date date, String accessionNumber, boolean isControl, String resultUnits, - String analyzerTestId) { + Test test) { + LogEvent.logDebug(this.getClass().getName(), "addResult", + "adding result for lab Number: " + accessionNumber); AnalyzerResults analyzerResults = createAnalyzerResult(resultType, resultValue, resultUnits, date, - accessionNumber, isControl, analyzerTestId); + accessionNumber, isControl, test); if (analyzerResults.getTestId() != null) { addValueToResults(resultList, analyzerResults); } else { + LogEvent.logWarn(this.getClass().getName(), "addResult", + "no matching result for " + accessionNumber); notMatchedResults.add(analyzerResults); } + return analyzerResults; } - + private void addValueToResults(List resultList, AnalyzerResults result) { resultList.add(result); - + LogEvent.logDebug(this.getClass().getName(), "addValueToResults", + "searching for matching analysis for " + result.getAccessionNumber()); AnalyzerResults resultFromDB = readerUtil.createAnalyzerResultFromDB(result); if (resultFromDB != null) { + LogEvent.logWarn(this.getClass().getName(), "addValueToResults", + "no resultFromDB for " + result.getAccessionNumber()); resultList.add(resultFromDB); } - + } - + private AnalyzerResults createAnalyzerResult(String resultType, String resultValue, String resultUnits, Date date, - String accessionNumber, boolean isControl, String analyzerTestId) { + String accessionNumber, boolean isControl, Test test) { + LogEvent.logDebug(this.getClass().getName(), "createAnalyzerResult", + "creating analyzer result for " + accessionNumber); + AnalyzerResults analyzerResults = new AnalyzerResults(); - + analyzerResults.setAnalyzerId(ANALYZER_ID); analyzerResults.setResult(resultValue); analyzerResults.setUnits(resultUnits); @@ -114,17 +385,99 @@ private AnalyzerResults createAnalyzerResult(String resultType, String resultVal analyzerResults.setCompleteDate(new Timestamp(date.getTime())); } analyzerResults.setAccessionNumber(accessionNumber); - analyzerResults.setTestId( - testLoincMap.get(analyzerTestId).size() > 0 ? testLoincMap.get(analyzerTestId).get(0).getId() : ""); + analyzerResults.setTestId(test.getId()); analyzerResults.setIsControl(isControl); - analyzerResults.setTestName(testLoincMap.get(analyzerTestId).size() > 0 - ? testLoincMap.get(analyzerTestId).get(0).getLocalizedTestName().getLocalizedValue() - : ""); + analyzerResults.setTestName(test.getLocalizedTestName().getLocalizedValue()); return analyzerResults; } - + public void persistImport(List resultList) { this.persistImport("1", resultList); } -} + public String buildResponse(List lines) { + + String queryRecord = null; + + for (String line : lines) { + if (line.startsWith(TEST_COMMUNICATION_IDENTIFIER)) { + LogEvent.logInfo(this.getClass().getName(), "buildResponse", "this is a test communication record for " + GeneXpertAnalyzer.ANALYZER_NAME); + } + if (line.startsWith(QUERY_RECORD_IDENTIFIER)) { + LogEvent.logDebug(this.getClass().getName(), "buildResponse", "request contains query record"); + String response = generateQueryResponse(line); + return response; + } + } + LogEvent.logWarn(this.getClass().getName(), "buildResponse", "no response could be created, no query identifier supplied"); + return ""; + } + + private String generateQueryResponse(String queryRecord) { + LogEvent.logDebug(this.getClass().getSimpleName(), "generateQueryResponse", "generating query response"); + String[] queryRecordFields = queryRecord.split(Pattern.quote(FD)); + + String[] startingRangeIdNumber = queryRecordFields[2].split(Pattern.quote(CD)); + String sampleIdNo = startingRangeIdNumber[1].trim(); + String sampleNoAttribute = startingRangeIdNumber[3]; + + StringBuilder msgBuilder = new StringBuilder(); + LogEvent.logDebug(this.getClass().getSimpleName(), "generateQueryResponse", "searching for sample with lab Number: " + sampleIdNo); + + Sample sample = sampleService.getSampleByAccessionNumber(sampleIdNo.trim()); + if (sample == null) { + LogEvent.logWarn(this.getClass().getSimpleName(), "generateQueryResponse", "no sample found with lab Number: " + sampleIdNo); + //return could not find sample messager + return msgBuilder.append("H|") + .append(RD) + .append(CD) + .append(ED) + .append("|\r\n") + .append("P|1|\r\n") + .append("O|1||||||||||||||||||||||||Y\r\n") //"Y" is no order exists marker + .append("L|1|N\r\n").toString(); + } + Patient patient = sampleHumanService.getPatientForSample(sample); + Person person = patient.getPerson(); + msgBuilder.append("H|") + .append(RD) + .append(CD) + .append(ED) + .append("|||||||||||1394-97\r\n"); + msgBuilder.append("P|1|||") + .append(patient.getNationalId()) //patient identifier + .append("|") + .append(CD) + .append(person.getFirstName()) + .append(CD) + .append(person.getLastName()) //names + .append("||").append(patient.getBirthDate() == null ? "": dateFormat.format(patient.getBirthDate())) //birthdate + .append("|").append(patient.getGender()) //gender M F U + .append("|||||") + .append("")//DR id + .append("||||||||||||\r\n"); + + + int i = 0; + + for (Analysis curAnalysis : analysisService.getAnalysesBySampleId(sample.getId())) { + Optional testCode = Optional.ofNullable(loincToTestCodeMap.get(curAnalysis.getTest().getLoinc())); + if (testCode.isPresent()) { + LogEvent.logDebug(this.getClass().getSimpleName(), "generateQueryResponse", "found supported test in sample with test code: " + testCode.get()); + msgBuilder.append("O|").append(++i).append("|") + .append(sampleIdNo).append("||") + .append(CD + CD + CD) + .append(testCode.get()); + msgBuilder.append("|R|") + .append(sample.getEnteredDate() == null ? "" : dateTimeFormat.format(sample.getEnteredDate())) + .append("|||||") + .append("A").append("||||ORH||||||||||Q\r\n"); + } + } + msgBuilder.append("L|1|F\r\n"); + return msgBuilder.toString(); + } + + + } + \ No newline at end of file diff --git a/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzerMenu.java b/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzerMenu.java new file mode 100644 index 0000000..b306dcd --- /dev/null +++ b/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzerMenu.java @@ -0,0 +1,57 @@ +/* + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations under + * the License. + * + * The Original Code is OpenELIS code. + * + * Copyright (C) ITECH, University of Washington, Seattle WA. All Rights Reserved. + */ + +package oe.plugin.analyzer; + +import java.util.Locale; + +import org.openelisglobal.common.services.PluginMenuService; +import org.openelisglobal.common.services.PluginMenuService.KnownMenu; +import org.openelisglobal.menu.valueholder.Menu; +import org.openelisglobal.plugin.MenuPlugin; + +public class GeneXpertAnalyzerMenu extends MenuPlugin { + + @Override + protected void insertMenu() { + PluginMenuService service = PluginMenuService.getInstance(); + Menu menu = new Menu(); + + menu.setParent(PluginMenuService.getInstance().getKnownMenu(KnownMenu.ANALYZER, "menu_results")); + // The order this analyzer will show on the menu relative to other analyzers + menu.setPresentationOrder(10); + // The id needs to be unique in the system + menu.setElementId(GeneXpertAnalyzer.ANALYZER_NAME + "_plugin"); + // This will always be "/AnalyzerResults?type=Analyzer->" + GeneXpertAnalyzer.ANALYZER_NAME); + Role role = service.getSystemRole( "Results" ); + return service.bindRoleToModule( role, module ); + } +} diff --git a/analyzers/GeneXpert/src/main/resources/GeneXpertAnalyzer.xml b/analyzers/GeneXpert/src/main/resources/GeneXpertAnalyzer.xml new file mode 100644 index 0000000..689d3fb --- /dev/null +++ b/analyzers/GeneXpert/src/main/resources/GeneXpertAnalyzer.xml @@ -0,0 +1,22 @@ + + + 1.0 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/analyzers/GeneXpertFile/src/oe/plugin/analyzer/GeneXpertAnalyzer.java b/analyzers/GeneXpertFile/src/oe/plugin/analyzer/GeneXpertAnalyzer.java index 74d28f1..292b8b6 100644 --- a/analyzers/GeneXpertFile/src/oe/plugin/analyzer/GeneXpertAnalyzer.java +++ b/analyzers/GeneXpertFile/src/oe/plugin/analyzer/GeneXpertAnalyzer.java @@ -27,7 +27,8 @@ public class GeneXpertAnalyzer implements AnalyzerImporterPlugin { - + + public static final String ANALYZER_NAME = "GeneXpertAnalyzer"; @Override public boolean connect(){ @@ -48,7 +49,7 @@ public boolean connect(){ nameMapping.add( new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.COV_2, "COVID-19 PCR", GeneXpertAnalyzerImplementation.COV_2_LOINC)); - getInstance().addAnalyzerDatabaseParts("GeneXpertAnalyzer", "Plugin for GeneXpertAnalyzer", nameMapping); + getInstance().addAnalyzerDatabaseParts(ANALYZER_NAME, "Plugin for " + ANALYZER_NAME, nameMapping); getInstance().registerAnalyzer(this); return true; } diff --git a/analyzers/GeneXpertFile/src/oe/plugin/analyzer/GeneXpertAnalyzerImplementation.java b/analyzers/GeneXpertFile/src/oe/plugin/analyzer/GeneXpertAnalyzerImplementation.java index 854ec6a..1055ded 100644 --- a/analyzers/GeneXpertFile/src/oe/plugin/analyzer/GeneXpertAnalyzerImplementation.java +++ b/analyzers/GeneXpertFile/src/oe/plugin/analyzer/GeneXpertAnalyzerImplementation.java @@ -106,7 +106,7 @@ public GeneXpertAnalyzerImplementation() { resultMap.put(COV_2_ANALYZER_POS.toLowerCase(), COV_2_DB_POS.toLowerCase()); resultMap.put(COV_2_ANALYZER_INV.toLowerCase(), COV_2_DB_INV.toLowerCase()); - Analyzer analyzer = analyzerService.getAnalyzerByName("GeneXpertAnalyzer"); + Analyzer analyzer = analyzerService.getAnalyzerByName(GeneXpertAnalyzer.ANALYZER_NAME); ANALYZER_ID = analyzer.getId(); } @@ -257,6 +257,6 @@ private AnalyzerResults createAnalyzerResult(String resultType, String resultVal @Override public String getError() { - return "GeneXpertAnalyzer unable to write to database"; + return GeneXpertAnalyzer.ANALYZER_NAME + " unable to write to database"; } } diff --git a/analyzers/GeneXpertFile/src/oe/plugin/analyzer/GeneXpertAnalyzerPermission.java b/analyzers/GeneXpertFile/src/oe/plugin/analyzer/GeneXpertAnalyzerPermission.java index f0c298a..1e6a2e4 100644 --- a/analyzers/GeneXpertFile/src/oe/plugin/analyzer/GeneXpertAnalyzerPermission.java +++ b/analyzers/GeneXpertFile/src/oe/plugin/analyzer/GeneXpertAnalyzerPermission.java @@ -27,8 +27,8 @@ public class GeneXpertAnalyzerPermission extends PermissionPlugin { @Override protected boolean insertPermission(){ PluginPermissionService service = new PluginPermissionService(); - SystemModule module = service.getOrCreateSystemModule("AnalyzerResults", "GeneXpertAnalyzer", - "Results->Analyzer->GeneXpertAnalyzer"); + SystemModule module = service.getOrCreateSystemModule("AnalyzerResults", GeneXpertAnalyzer.ANALYZER_NAME, + "Results->Analyzer->" + GeneXpertAnalyzer.ANALYZER_NAME); Role role = service.getSystemRole( "Results" ); return service.bindRoleToModule( role, module ); } diff --git a/analyzers/GeneXpert/ORU_R30.txt b/analyzers/GeneXpertHL7/ORU_R30.txt similarity index 100% rename from analyzers/GeneXpert/ORU_R30.txt rename to analyzers/GeneXpertHL7/ORU_R30.txt diff --git a/analyzers/GeneXpert/QBP_Z03.txt b/analyzers/GeneXpertHL7/QBP_Z03.txt similarity index 100% rename from analyzers/GeneXpert/QBP_Z03.txt rename to analyzers/GeneXpertHL7/QBP_Z03.txt diff --git a/analyzers/GeneXpert/QCN_J01.txt b/analyzers/GeneXpertHL7/QCN_J01.txt similarity index 100% rename from analyzers/GeneXpert/QCN_J01.txt rename to analyzers/GeneXpertHL7/QCN_J01.txt diff --git a/analyzers/GeneXpertHL7/pom.xml b/analyzers/GeneXpertHL7/pom.xml new file mode 100644 index 0000000..2ff7ac3 --- /dev/null +++ b/analyzers/GeneXpertHL7/pom.xml @@ -0,0 +1,94 @@ + + 4.0.0 + org.openelisglobal.plugins + GeneXpertHL7 + + org.openelisglobal + openelisglobal-plugins + 1.0 + ../../pom.xml + + + 0.3 + + UTF-8 + 2.3 + + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + ca.uhn.hapi + hapi-hl7overhttp + ${hapi.version} + + + ca.uhn.hapi + hapi-structures-v25 + ${hapi.version} + + + org.springframework + spring-webmvc + ${springframework.version} + + + + + src/main/java + + + src/main/filtered-resources + true + + + src/main/resources + + + + + maven-compiler-plugin + 3.8.0 + + 1.8 + 1.8 + + + + maven-resources-plugin + 2.4.3 + + + copy-resources + + install + + copy-resources + + + ${project.basedir}/../../plugins + + + + target/ + + ${project.build.finalName}.jar + + + false + + + + + + + + + \ No newline at end of file diff --git a/analyzers/GeneXpert/src/main/java/ca/uhn/hl7v2/custom/factory/PluginModelClassFactory.java b/analyzers/GeneXpertHL7/src/main/java/ca/uhn/hl7v2/custom/factory/PluginModelClassFactory.java similarity index 100% rename from analyzers/GeneXpert/src/main/java/ca/uhn/hl7v2/custom/factory/PluginModelClassFactory.java rename to analyzers/GeneXpertHL7/src/main/java/ca/uhn/hl7v2/custom/factory/PluginModelClassFactory.java diff --git a/analyzers/GeneXpert/src/main/java/ca/uhn/hl7v2/custom/model/v25/group/RSP_Z02_ORDER.java b/analyzers/GeneXpertHL7/src/main/java/ca/uhn/hl7v2/custom/model/v25/group/RSP_Z02_ORDER.java similarity index 100% rename from analyzers/GeneXpert/src/main/java/ca/uhn/hl7v2/custom/model/v25/group/RSP_Z02_ORDER.java rename to analyzers/GeneXpertHL7/src/main/java/ca/uhn/hl7v2/custom/model/v25/group/RSP_Z02_ORDER.java diff --git a/analyzers/GeneXpert/src/main/java/ca/uhn/hl7v2/custom/model/v25/group/RSP_Z02_PATIENT.java b/analyzers/GeneXpertHL7/src/main/java/ca/uhn/hl7v2/custom/model/v25/group/RSP_Z02_PATIENT.java similarity index 100% rename from analyzers/GeneXpert/src/main/java/ca/uhn/hl7v2/custom/model/v25/group/RSP_Z02_PATIENT.java rename to analyzers/GeneXpertHL7/src/main/java/ca/uhn/hl7v2/custom/model/v25/group/RSP_Z02_PATIENT.java diff --git a/analyzers/GeneXpert/src/main/java/ca/uhn/hl7v2/custom/model/v25/message/QBP_Z03.java b/analyzers/GeneXpertHL7/src/main/java/ca/uhn/hl7v2/custom/model/v25/message/QBP_Z03.java similarity index 100% rename from analyzers/GeneXpert/src/main/java/ca/uhn/hl7v2/custom/model/v25/message/QBP_Z03.java rename to analyzers/GeneXpertHL7/src/main/java/ca/uhn/hl7v2/custom/model/v25/message/QBP_Z03.java diff --git a/analyzers/GeneXpert/src/main/java/ca/uhn/hl7v2/custom/model/v25/message/RSP_Z02.java b/analyzers/GeneXpertHL7/src/main/java/ca/uhn/hl7v2/custom/model/v25/message/RSP_Z02.java similarity index 100% rename from analyzers/GeneXpert/src/main/java/ca/uhn/hl7v2/custom/model/v25/message/RSP_Z02.java rename to analyzers/GeneXpertHL7/src/main/java/ca/uhn/hl7v2/custom/model/v25/message/RSP_Z02.java diff --git a/analyzers/GeneXpertHL7/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzer.java b/analyzers/GeneXpertHL7/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzer.java new file mode 100644 index 0000000..7eaf3e9 --- /dev/null +++ b/analyzers/GeneXpertHL7/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzer.java @@ -0,0 +1,64 @@ +/* + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations under + * the License. + * + * The Original Code is OpenELIS code. + * + * Copyright (C) ITECH, University of Washington, Seattle WA. All Rights Reserved. + */ + +package oe.plugin.analyzer; + +import static org.openelisglobal.common.services.PluginAnalyzerService.getInstance; + +import java.util.ArrayList; +import java.util.List; + +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerLineInserter; +import org.openelisglobal.common.services.PluginAnalyzerService; +import org.openelisglobal.plugin.AnalyzerImporterPlugin; + +public class GeneXpertAnalyzer implements AnalyzerImporterPlugin { + + @Override + public boolean connect() { + List nameMapping = new ArrayList<>(); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.HBV, "HEPATITIS B VIRAL LOAD", + GeneXpertAnalyzerImplementation.HBV_LOINC)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.HCV, "HEPATITIS C VIRAL LOAD", + GeneXpertAnalyzerImplementation.HCV_LOINC)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.HIV_QUAL, "​Xpert HIV-1 Qual", + GeneXpertAnalyzerImplementation.HIV_QUAL_LOINC)); + nameMapping + .add(new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.HIV_VIRAL, "HIV VIRAL LOAD", + GeneXpertAnalyzerImplementation.HIV_VIRAL_LOINC)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(GeneXpertAnalyzerImplementation.COV_2, "COVID-19 PCR", + GeneXpertAnalyzerImplementation.COV_2_LOINC)); + getInstance().addAnalyzerDatabaseParts(GeneXpertAnalyzer.ANALYZER_NAME, GeneXpertAnalyzer.ANALYZER_NAME, nameMapping, true); + getInstance().registerAnalyzer(this); + return true; + } + + @Override + // this plugin does not work for flat files, so we disable it in that workflow + public boolean isTargetAnalyzer(List lines) { + return false; + } + + @Override + public AnalyzerLineInserter getAnalyzerLineInserter() { + return new GeneXpertAnalyzerImplementation(); + } + +} diff --git a/analyzers/GeneXpertHL7/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzerImplementation.java b/analyzers/GeneXpertHL7/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzerImplementation.java new file mode 100644 index 0000000..df6b351 --- /dev/null +++ b/analyzers/GeneXpertHL7/src/main/java/oe/plugin/analyzer/GeneXpertAnalyzerImplementation.java @@ -0,0 +1,130 @@ +/* + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations under + * the License. + * + * The Original Code is OpenELIS code. + * + * Copyright (C) ITECH, University of Washington, Seattle WA. All Rights Reserved. + */ + +package oe.plugin.analyzer; + +import java.sql.Timestamp; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +import org.openelisglobal.analyzer.service.AnalyzerService; +import org.openelisglobal.analyzer.valueholder.Analyzer; +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerLineInserter; +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerReaderUtil; +import org.openelisglobal.analyzerresults.valueholder.AnalyzerResults; +import org.openelisglobal.spring.util.SpringContext; +import org.openelisglobal.test.service.TestService; +import org.openelisglobal.test.valueholder.Test; + +public class GeneXpertAnalyzerImplementation extends AnalyzerLineInserter { + + private TestService testService = SpringContext.getBean(TestService.class); + private AnalyzerService analyzerService = SpringContext.getBean(AnalyzerService.class); + + + static final String HBV = "Xpert HBV Viral Load"; + static final String HCV = "Xpert HCV Viral Load"; + static final String HIV_QUAL = "Xpert HIV-1 Qual"; + static final String HIV_VIRAL = "Xpert HIV-1 viral Load"; + static final String COV_2 = "Xpert Xpress SARS-CoV-2 assay"; + + static final String HBV_LOINC = "29615-2"; + static final String HCV_LOINC = "11011-4"; + static final String HIV_QUAL_LOINC = ""; + static final String HIV_VIRAL_LOINC = "10351-5"; + static final String COV_2_LOINC = "94500-6"; + + private String ANALYZER_ID; + private HashMap> testLoincMap = new HashMap<>(); + + private AnalyzerReaderUtil readerUtil = new AnalyzerReaderUtil(); + + public GeneXpertAnalyzerImplementation() { + testLoincMap.put(HBV_LOINC, testService.getTestsByLoincCode(HBV_LOINC)); + testLoincMap.put(HCV_LOINC, testService.getTestsByLoincCode(HCV_LOINC)); + testLoincMap.put(HIV_QUAL_LOINC, testService.getTestsByLoincCode(HIV_QUAL_LOINC)); + testLoincMap.put(HIV_VIRAL_LOINC, testService.getTestsByLoincCode(HIV_VIRAL_LOINC)); + testLoincMap.put(COV_2_LOINC, testService.getTestsByLoincCode(COV_2_LOINC)); + + Analyzer analyzer = analyzerService.getAnalyzerByName(GeneXpertAnalyzer.ANALYZER_NAME); + ANALYZER_ID = analyzer.getId(); + } + + /* + * (non-Javadoc) + * + * @see + * org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerLineInserter#insert + * (java.util.List, java.lang.String) + */ + @Override + public boolean insert(List lines, String currentUserId) { + return false; + } + + @Override + public String getError() { + return "GeneXpert analyzer unable to write to database"; + } + + public void addResult(List resultList, List notMatchedResults, String resultType, + String resultValue, Date date, String accessionNumber, boolean isControl, String resultUnits, + String analyzerTestId) { + AnalyzerResults analyzerResults = createAnalyzerResult(resultType, resultValue, resultUnits, date, + accessionNumber, isControl, analyzerTestId); + if (analyzerResults.getTestId() != null) { + addValueToResults(resultList, analyzerResults); + } else { + notMatchedResults.add(analyzerResults); + } + } + + private void addValueToResults(List resultList, AnalyzerResults result) { + resultList.add(result); + + AnalyzerResults resultFromDB = readerUtil.createAnalyzerResultFromDB(result); + if (resultFromDB != null) { + resultList.add(resultFromDB); + } + + } + + private AnalyzerResults createAnalyzerResult(String resultType, String resultValue, String resultUnits, Date date, + String accessionNumber, boolean isControl, String analyzerTestId) { + AnalyzerResults analyzerResults = new AnalyzerResults(); + + analyzerResults.setAnalyzerId(ANALYZER_ID); + analyzerResults.setResult(resultValue); + analyzerResults.setUnits(resultUnits); + if (date != null) { + analyzerResults.setCompleteDate(new Timestamp(date.getTime())); + } + analyzerResults.setAccessionNumber(accessionNumber); + analyzerResults.setTestId( + testLoincMap.get(analyzerTestId).size() > 0 ? testLoincMap.get(analyzerTestId).get(0).getId() : ""); + analyzerResults.setIsControl(isControl); + analyzerResults.setTestName(testLoincMap.get(analyzerTestId).size() > 0 + ? testLoincMap.get(analyzerTestId).get(0).getLocalizedTestName().getLocalizedValue() + : ""); + return analyzerResults; + } + + public void persistImport(List resultList) { + this.persistImport("1", resultList); + } + +} diff --git a/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertMenu.java b/analyzers/GeneXpertHL7/src/main/java/oe/plugin/analyzer/GeneXpertMenu.java similarity index 80% rename from analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertMenu.java rename to analyzers/GeneXpertHL7/src/main/java/oe/plugin/analyzer/GeneXpertMenu.java index 10c6745..ab947d9 100644 --- a/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertMenu.java +++ b/analyzers/GeneXpertHL7/src/main/java/oe/plugin/analyzer/GeneXpertMenu.java @@ -34,22 +34,22 @@ protected void insertMenu() { // The order this analyzer will show on the menu relative to other analyzers menu.setPresentationOrder(10); // The id needs to be unique in the system - menu.setElementId("genexpert_analyzer_plugin"); + menu.setElementId(GeneXpertAnalyzer.ANALYZER_NAME + "_plugin"); // This will always be "/AnalyzerResults?type=Analyzer->GeneXpertAnalyzer"); + SystemModule module = service.getOrCreateSystemModule("AnalyzerResults", GeneXpertAnalyzer.ANALYZER_NAME, + "Results->Analyzer->" + GeneXpertAnalyzer.ANALYZER_NAME); Role role = service.getSystemRole( "Results" ); return service.bindRoleToModule( role, module ); } diff --git a/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertServlet.java b/analyzers/GeneXpertHL7/src/main/java/oe/plugin/analyzer/GeneXpertServlet.java similarity index 88% rename from analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertServlet.java rename to analyzers/GeneXpertHL7/src/main/java/oe/plugin/analyzer/GeneXpertServlet.java index 628a3b9..ea2ce38 100644 --- a/analyzers/GeneXpert/src/main/java/oe/plugin/analyzer/GeneXpertServlet.java +++ b/analyzers/GeneXpertHL7/src/main/java/oe/plugin/analyzer/GeneXpertServlet.java @@ -15,7 +15,9 @@ import org.openelisglobal.analysis.service.AnalysisService; import org.openelisglobal.analysis.valueholder.Analysis; import org.openelisglobal.analyzerresults.valueholder.AnalyzerResults; +import org.openelisglobal.common.log.LogEvent; import org.openelisglobal.patient.valueholder.Patient; +import org.openelisglobal.patient.service.PatientService; import org.openelisglobal.plugin.ServletPlugin; import org.openelisglobal.sample.service.SampleService; import org.openelisglobal.sample.valueholder.Sample; @@ -60,6 +62,7 @@ public class GeneXpertServlet extends HohServlet implements ServletPlugin { private static final String[] LOINC_CODES = { "29615-2", "11011-4", "10351-5", "94500-6" }; SampleService sampleService = SpringContext.getBean(SampleService.class); AnalysisService analysisService = SpringContext.getBean(AnalysisService.class); + PatientService patientService = SpringContext.getBean(PatientService.class); private GeneXpertAnalyzerImplementation inserter ; @Override @@ -86,6 +89,7 @@ private class GeneXpertApplication implements ReceivingApplication { @Override public Message processMessage(Message message, @SuppressWarnings("rawtypes") Map theMetadata) throws ReceivingApplicationException, HL7Exception { + LogEvent.logTrace(this.getClass().getSimpleName(), "processMessage", "received an HL7 message for GeneXpert analyzer"); MSH messageHeader = (MSH) message.get("MSH"); Message response = null; @@ -119,9 +123,8 @@ public Message processMessage(Message message, @SuppressWarnings("rawtypes") Map private RSP_Z02 instrumentSystemSendsHostQueryStart(QBP_Z03 message, @SuppressWarnings("rawtypes") Map theMetadata) throws HL7Exception, IOException { + LogEvent.logTrace(this.getClass().getSimpleName(), "instrumentSystemSendsHostQueryStart", "received an HL7 message for GeneXpert analyzer: Host query"); activeRequests.put(message.getMSH().getMessageControlID().getValue(), message); - - // TODO use these to get the right objects Optional patientId = message.getQPD().getField(3).length > 0 ? Optional.of(((Varies) message.getQPD().getField(3, 0)).getData()) : Optional.empty(); @@ -131,13 +134,19 @@ private RSP_Z02 instrumentSystemSendsHostQueryStart(QBP_Z03 message, : Optional.empty(); List patients = new ArrayList<>(); - patients.add(new Patient()); + if (patientId.isPresent()) { + LogEvent.logTrace(this.getClass().getSimpleName(), "instrumentSystemSendsHostQueryStart", "looking for patient with id: "+ patientId.get()); + } + + patientId.ifPresent(p -> patients.add(patientService.get(patientId.toString()))); RSP_Z02 response = instrumentSystemSendsHostQueryStartSuccess(message); response.getQAK().getQueryTag().setValue(message.getQPD().getQueryTag().getValue()); Sample sample = sampleService.getSampleByAccessionNumber(specimenId.toString()); if (sample != null) { + LogEvent.logTrace(this.getClass().getSimpleName(), "instrumentSystemSendsHostQueryStart", "found sample for: " + specimenId.toString()); + int pidSeq = 0; int orcSeq = 0; int obrSeq = 0; @@ -225,11 +234,16 @@ private ACK instrumentSystemCancelsHostQuerySuccessACK(QCN_J01 message) throws H private Message instrumentSystemUploadsTestResults(ORU_R01 message, @SuppressWarnings("rawtypes") Map theMetadata) throws HL7Exception, IOException { + LogEvent.logTrace(this.getClass().getSimpleName(), "instrumentSystemUploadsTestResults", "received HL7 result(s) for GeneXpert analyzer: parsing ORU_R01"); + List resultList = new ArrayList<>(); List notMatchedResults = new ArrayList<>(); for (ORU_R01_PATIENT_RESULT patientResult : message.getPATIENT_RESULTAll()) { + LogEvent.logTrace(this.getClass().getSimpleName(), "instrumentSystemUploadsTestResults", "parsing patient result"); PID pid = patientResult.getPATIENT().getPID(); for (ORU_R01_ORDER_OBSERVATION orderObservation : patientResult.getORDER_OBSERVATIONAll()) { + LogEvent.logTrace(this.getClass().getSimpleName(), "instrumentSystemUploadsTestResults", "parsing order observation"); + ORC orc = orderObservation.getORC(); OBR obr = orderObservation.getOBR(); Optional orderNTE = orderObservation.getNTEReps() <= 0 ? Optional.empty() @@ -242,6 +256,8 @@ private Message instrumentSystemUploadsTestResults(ORU_R01 message, String accessionNumber = spm.getSpecimenID().getPlacerAssignedIdentifier().getEntityIdentifier() .getValue(); for (ORU_R01_OBSERVATION observation : orderObservation.getOBSERVATIONAll()) { + LogEvent.logTrace(this.getClass().getSimpleName(), "instrumentSystemUploadsTestResults", "parsing observation"); + OBX obx = observation.getOBX(); Optional resultNTE = observation.getNTEReps() <= 0 ? Optional.empty() : Optional.of(observation.getNTE()); @@ -260,13 +276,15 @@ private Message instrumentSystemUploadsTestResults(ORU_R01 message, } } } - inserter.persistImport(resultList); - // TODO insert the results as analyzer results + LogEvent.logTrace(this.getClass().getSimpleName(), "instrumentSystemUploadsTestResults", "parsing order observation"); + inserter.persistImport(resultList); return instrumentSystemUploadsTestResultsSuccessACK(message); } private ACK instrumentSystemUploadsTestResultsSuccessACK(ORU_R01 message) throws HL7Exception, IOException { + LogEvent.logTrace(this.getClass().getSimpleName(), "instrumentSystemUploadsTestResultsSuccessACK", "Sending ACK"); + ACK response = (ACK) message.generateACK(AcknowledgmentCode.CA, null); MSH responseMSH = response.getMSH(); responseMSH.getAcceptAcknowledgmentType().setValue("NE"); diff --git a/analyzers/GeneXpert/src/main/resources/GeneXpert.xml b/analyzers/GeneXpertHL7/src/main/resources/GeneXpert.xml similarity index 100% rename from analyzers/GeneXpert/src/main/resources/GeneXpert.xml rename to analyzers/GeneXpertHL7/src/main/resources/GeneXpert.xml diff --git a/analyzers/SysmexXN-L/pom.xml b/analyzers/SysmexXN-L/pom.xml index 54b8366..fb3d288 100644 --- a/analyzers/SysmexXN-L/pom.xml +++ b/analyzers/SysmexXN-L/pom.xml @@ -11,7 +11,7 @@ ../../pom.xml - 0.3 + 0.5 UTF-8 2.3 diff --git a/analyzers/SysmexXN-L/src/main/java/oe/plugin/analyzer/SysmexXNLAnalyzer.java b/analyzers/SysmexXN-L/src/main/java/oe/plugin/analyzer/SysmexXNLAnalyzer.java index ef26799..a596603 100644 --- a/analyzers/SysmexXN-L/src/main/java/oe/plugin/analyzer/SysmexXNLAnalyzer.java +++ b/analyzers/SysmexXN-L/src/main/java/oe/plugin/analyzer/SysmexXNLAnalyzer.java @@ -20,26 +20,29 @@ import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerLineInserter; import org.openelisglobal.common.services.PluginAnalyzerService; import org.openelisglobal.plugin.AnalyzerImporterPlugin; - +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerResponder; import org.openelisglobal.common.log.LogEvent; public class SysmexXNLAnalyzer implements AnalyzerImporterPlugin { + + public static final String ANALYZER_NAME = "SysmexXNLAnalyzer"; @Override public boolean connect() { List nameMapping = new ArrayList<>(); nameMapping.add( - new PluginAnalyzerService.TestMapping(SysmexXNLAnalyzerImplementation.ANALYZER_TEST_WBC, "White Blood Cells Count", + new PluginAnalyzerService.TestMapping(SysmexXNLAnalyzerImplementation.ANALYZER_TEST_WBC, "White Blood Cells Count (WBC)", SysmexXNLAnalyzerImplementation.LOINC_WBC)); nameMapping.add( - new PluginAnalyzerService.TestMapping(SysmexXNLAnalyzerImplementation.ANALYZER_TEST_RBC, "Red Blood Cells Count", + new PluginAnalyzerService.TestMapping(SysmexXNLAnalyzerImplementation.ANALYZER_TEST_RBC, "Red Blood Cells Count (RBC)", SysmexXNLAnalyzerImplementation.LOINC_RBC)); nameMapping.add( - new PluginAnalyzerService.TestMapping(SysmexXNLAnalyzerImplementation.ANALYZER_TEST_HGB, "​Hemoglobin​", + new PluginAnalyzerService.TestMapping(SysmexXNLAnalyzerImplementation.ANALYZER_TEST_HGB, "Hemoglobin", SysmexXNLAnalyzerImplementation.LOINC_HGB)); nameMapping.add( new PluginAnalyzerService.TestMapping(SysmexXNLAnalyzerImplementation.ANALYZER_TEST_HCT, "Hematocrit", @@ -66,7 +69,7 @@ public boolean connect() { new PluginAnalyzerService.TestMapping(SysmexXNLAnalyzerImplementation.ANALYZER_TEST_MPV, "", SysmexXNLAnalyzerImplementation.LOINC_MPV)); nameMapping.add( - new PluginAnalyzerService.TestMapping(SysmexXNLAnalyzerImplementation.ANALYZER_TEST_NEUT_COUNT, "​Neutrophiles​", + new PluginAnalyzerService.TestMapping(SysmexXNLAnalyzerImplementation.ANALYZER_TEST_NEUT_COUNT, "Neutrophiles", SysmexXNLAnalyzerImplementation.LOINC_NEUT_COUNT)); nameMapping.add( new PluginAnalyzerService.TestMapping(SysmexXNLAnalyzerImplementation.ANALYZER_TEST_NEUT_PERCENT, "Neutrophiles (%)", @@ -134,7 +137,7 @@ public boolean connect() { nameMapping.add( new PluginAnalyzerService.TestMapping(SysmexXNLAnalyzerImplementation.ANALYZER_TEST_TCBF_COUNT, "", SysmexXNLAnalyzerImplementation.LOINC_TCBF_COUNT)); - getInstance().addAnalyzerDatabaseParts("SysmexXNLAnalyzer", "SysmexXNLAnalyzer", nameMapping, true); + getInstance().addAnalyzerDatabaseParts(ANALYZER_NAME, ANALYZER_NAME, nameMapping, true); getInstance().registerAnalyzer(this); return true; } @@ -143,39 +146,41 @@ public boolean connect() { public boolean isTargetAnalyzer(List lines) { for (String line : lines) { if (line.startsWith(SysmexXNLAnalyzerImplementation.HEADER_RECORD_IDENTIFIER)) { - String[] headerRecord = line.split(SysmexXNLAnalyzerImplementation.DEFAULT_FIELD_DELIMITER); + String[] headerRecord = line.split(Pattern.quote(SysmexXNLAnalyzerImplementation.FD)); if (headerRecord.length < 5) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is not SysmexXN: header record not long enough"); return false; } - String[] senderName = headerRecord[4].split(SysmexXNLAnalyzerImplementation.DEFAULT_SUBFIELD_DELIMITER); - if (senderName.length < 1) { + String[] senderNameFields = headerRecord[4].split(Pattern.quote(SysmexXNLAnalyzerImplementation.CD)); + if (senderNameFields.length < 1) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is not SysmexXN: sender name field not long enough"); return false; } - LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message analyzer name is " + senderName[0]); - if (senderName[0].equalsIgnoreCase("XN-550")) { + String senderName = senderNameFields[0].trim(); + + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message analyzer name is " + senderName); + if (senderName.equalsIgnoreCase("XN-550")) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is SysmexXN (XN-350)"); return true; - } else if (senderName[0].equalsIgnoreCase("XN-530")) { + } else if (senderName.equalsIgnoreCase("XN-530")) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is SysmexXN (XN-350)"); return true; - } else if (senderName[0].equalsIgnoreCase("XN-450")) { + } else if (senderName.equalsIgnoreCase("XN-450")) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is SysmexXN (XN-350)"); return true; - } else if (senderName[0].equalsIgnoreCase("XN-430")) { + } else if (senderName.equalsIgnoreCase("XN-430")) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is SysmexXN (XN-350)"); return true; - } else if (senderName[0].equalsIgnoreCase("XN-350")) { + } else if (senderName.equalsIgnoreCase("XN-350")) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is SysmexXN (XN-350)"); return true; - } else if (senderName[0].equalsIgnoreCase("XN-330")) { + } else if (senderName.equalsIgnoreCase("XN-330")) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is SysmexXN (XN-350)"); return true; - } else if (senderName[0].equalsIgnoreCase("XN-150")) { + } else if (senderName.equalsIgnoreCase("XN-150")) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is SysmexXN (XN-350)"); return true; - } else if (senderName[0].equalsIgnoreCase("XN-110")) { + } else if (senderName.equalsIgnoreCase("XN-110")) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is SysmexXN (XN-350)"); return true; } @@ -187,9 +192,25 @@ public boolean isTargetAnalyzer(List lines) { return false; } + @Override + public boolean isAnalyzerResult(List lines) { + for (String line : lines) { + if (line.startsWith(SysmexXNLAnalyzerImplementation.RESULT_RECORD_IDENTIFIER)) { + return true; + } + } + LogEvent.logDebug(this.getClass().getSimpleName(), "isAnalyzerResult", "no result recoord identifier located"); + return false; + } + @Override public AnalyzerLineInserter getAnalyzerLineInserter() { return new SysmexXNLAnalyzerImplementation(); } + @Override + public AnalyzerResponder getAnalyzerResponder() { + return new SysmexXNLAnalyzerImplementation(); + } + } diff --git a/analyzers/SysmexXN-L/src/main/java/oe/plugin/analyzer/SysmexXNLAnalyzerImplementation.java b/analyzers/SysmexXN-L/src/main/java/oe/plugin/analyzer/SysmexXNLAnalyzerImplementation.java index 3639233..bdf3bdb 100644 --- a/analyzers/SysmexXN-L/src/main/java/oe/plugin/analyzer/SysmexXNLAnalyzerImplementation.java +++ b/analyzers/SysmexXN-L/src/main/java/oe/plugin/analyzer/SysmexXNLAnalyzerImplementation.java @@ -17,16 +17,22 @@ package oe.plugin.analyzer; import java.sql.Timestamp; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; +import java.util.Optional; import org.apache.commons.validator.GenericValidator; import org.openelisglobal.analyzer.service.AnalyzerService; import org.openelisglobal.analyzer.valueholder.Analyzer; import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerLineInserter; import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerReaderUtil; +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerResponder; import org.openelisglobal.analyzerimport.util.AnalyzerTestNameCache; import org.openelisglobal.analyzerimport.util.MappedTestName; import org.openelisglobal.analyzerresults.valueholder.AnalyzerResults; @@ -34,14 +40,17 @@ import org.openelisglobal.spring.util.SpringContext; import org.openelisglobal.test.service.TestService; import org.openelisglobal.sample.service.SampleService; +import org.openelisglobal.samplehuman.service.SampleHumanService; import org.openelisglobal.analysis.service.AnalysisService; import org.openelisglobal.test.valueholder.Test; import org.openelisglobal.analysis.valueholder.Analysis; +import org.openelisglobal.person.valueholder.Person; +import org.openelisglobal.patient.valueholder.Patient; import org.openelisglobal.sample.valueholder.Sample; import org.openelisglobal.common.util.DateUtil; import org.openelisglobal.common.log.LogEvent; -public class SysmexXNLAnalyzerImplementation extends AnalyzerLineInserter { +public class SysmexXNLAnalyzerImplementation extends AnalyzerLineInserter implements AnalyzerResponder { static final String ANALYZER_TEST_WBC = "WBC"; static final String ANALYZER_TEST_RBC = "RBC"; @@ -130,69 +139,82 @@ public class SysmexXNLAnalyzerImplementation extends AnalyzerLineInserter { static final String LOINC_TCBF_COUNT = "71690-2"; protected static final String HEADER_RECORD_IDENTIFIER = "H"; + protected static final String QUERY_RECORD_IDENTIFIER = "Q"; protected static final String PATIENT_RECORD_IDENTIFIER = "P"; protected static final String ORDER_RECORD_IDENTIFIER = "O"; protected static final String RESULT_RECORD_IDENTIFIER = "R"; protected static final String END_RECORD_IDENTIFIER = "L"; - protected static final String DEFAULT_FIELD_DELIMITER = "\\|"; - protected static final String DEFAULT_SUBFIELD_DELIMITER = "\\^"; - protected static final String DEFAULT_REPEATER_DELIMITER = "\\\\"; + protected static final String FD = "|"; //DEFAULT_FIELD_DELIMITER + protected static final String CD = "^"; //DEFAULT_COMPONENT_DELIMITER + protected static final String RD = "\\"; //DEFAULT_REPEATER_DELIMITER + protected static final String ED = "&"; //DEFAULT_ESCAPE_DELIMITER protected static final String TEST_COMMUNICATION_IDENTIFIER = "M|1|106"; + + private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); + private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyyMMddHHmmss"); + private TestService testService = SpringContext.getBean(TestService.class); private SampleService sampleService = SpringContext.getBean(SampleService.class); + private SampleHumanService sampleHumanService = SpringContext.getBean(SampleHumanService.class); private AnalyzerService analyzerService = SpringContext.getBean(AnalyzerService.class); private AnalysisService analysisService = SpringContext.getBean(AnalysisService.class); private String ANALYZER_ID; - private HashMap> testLoincMap = new HashMap<>(); + private Map testToLoincMap = new HashMap<>(); + private Map loincToTestCodeMap = new HashMap<>(); + private Map> testCodeToTestsMap = new HashMap<>(); private AnalyzerReaderUtil readerUtil = new AnalyzerReaderUtil(); public SysmexXNLAnalyzerImplementation() { - testLoincMap.put(ANALYZER_TEST_WBC, testService.getTestsByLoincCode(LOINC_WBC)); - testLoincMap.put(ANALYZER_TEST_RBC, testService.getTestsByLoincCode(LOINC_RBC)); - testLoincMap.put(ANALYZER_TEST_HGB, testService.getTestsByLoincCode(LOINC_HGB)); - testLoincMap.put(ANALYZER_TEST_HCT, testService.getTestsByLoincCode(LOINC_HCT)); - testLoincMap.put(ANALYZER_TEST_MCV, testService.getTestsByLoincCode(LOINC_MCV)); - testLoincMap.put(ANALYZER_TEST_MCH, testService.getTestsByLoincCode(LOINC_MCH)); - testLoincMap.put(ANALYZER_TEST_MCHC, testService.getTestsByLoincCode(LOINC_MCHC)); - testLoincMap.put(ANALYZER_TEST_RDWSD, testService.getTestsByLoincCode(LOINC_RDWSD)); - testLoincMap.put(ANALYZER_TEST_RDWCV, testService.getTestsByLoincCode(LOINC_RDWCV)); - testLoincMap.put(ANALYZER_TEST_PLT, testService.getTestsByLoincCode(LOINC_PLT)); - testLoincMap.put(ANALYZER_TEST_MPV, testService.getTestsByLoincCode(LOINC_MPV)); - testLoincMap.put(ANALYZER_TEST_NEUT_COUNT, testService.getTestsByLoincCode(LOINC_NEUT_COUNT)); - testLoincMap.put(ANALYZER_TEST_NEUT_PERCENT, testService.getTestsByLoincCode(LOINC_NEUT_PERCENT)); - testLoincMap.put(ANALYZER_TEST_LYMPH_COUNT, testService.getTestsByLoincCode(LOINC_LYMPH_COUNT)); - testLoincMap.put(ANALYZER_TEST_LYMPH_PERCENT, testService.getTestsByLoincCode(LOINC_LYMPH_PERCENT)); - testLoincMap.put(ANALYZER_TEST_MONO_COUNT, testService.getTestsByLoincCode(LOINC_MONO_COUNT)); - testLoincMap.put(ANALYZER_TEST_MONO_PERCENT, testService.getTestsByLoincCode(LOINC_MONO_PERCENT)); - testLoincMap.put(ANALYZER_TEST_EO_COUNT, testService.getTestsByLoincCode(LOINC_EO_COUNT)); - testLoincMap.put(ANALYZER_TEST_EO_PERCENT, testService.getTestsByLoincCode(LOINC_EO_PERCENT)); - testLoincMap.put(ANALYZER_TEST_BASO_COUNT, testService.getTestsByLoincCode(LOINC_BASO_COUNT)); - testLoincMap.put(ANALYZER_TEST_BASO_PERCENT, testService.getTestsByLoincCode(LOINC_BASO_PERCENT)); - testLoincMap.put(ANALYZER_TEST_IG_COUNT, testService.getTestsByLoincCode(LOINC_IG_COUNT)); - testLoincMap.put(ANALYZER_TEST_IG_PERCENT, testService.getTestsByLoincCode(LOINC_IG_PERCENT)); - // testLoincMap.put(ANALYZER_TEST_PDW, testService.getTestsByLoincCode(LOINC_PDW)); - // testLoincMap.put(ANALYZER_TEST_PLCR, testService.getTestsByLoincCode(LOINC_PLCR)); - // testLoincMap.put(ANALYZER_TEST_PCT, testService.getTestsByLoincCode(LOINC_PCT)); - testLoincMap.put(ANALYZER_TEST_RET_COUNT, testService.getTestsByLoincCode(LOINC_RET_COUNT)); - testLoincMap.put(ANALYZER_TEST_RET_PERCENT, testService.getTestsByLoincCode(LOINC_RET_PERCENT)); - testLoincMap.put(ANALYZER_TEST_IRF, testService.getTestsByLoincCode(LOINC_IRF)); - // testLoincMap.put(ANALYZER_TEST_LFR, testService.getTestsByLoincCode(LOINC_LFR)); - // testLoincMap.put(ANALYZER_TEST_MFR, testService.getTestsByLoincCode(LOINC_MFR)); - // testLoincMap.put(ANALYZER_TEST_HFR, testService.getTestsByLoincCode(LOINC_HFR)); - // testLoincMap.put(ANALYZER_TEST_LWBC, testService.getTestsByLoincCode(LOINC_LWBC)); - testLoincMap.put(ANALYZER_TEST_RETHE, testService.getTestsByLoincCode(LOINC_RETHE)); - testLoincMap.put(ANALYZER_TEST_WBCBF, testService.getTestsByLoincCode(LOINC_WBCBF)); - testLoincMap.put(ANALYZER_TEST_RBCBF, testService.getTestsByLoincCode(LOINC_RBCBF)); - testLoincMap.put(ANALYZER_TEST_MN_COUNT, testService.getTestsByLoincCode(LOINC_MN_COUNT)); - testLoincMap.put(ANALYZER_TEST_PMN_COUNT, testService.getTestsByLoincCode(LOINC_PMN_COUNT)); - testLoincMap.put(ANALYZER_TEST_MN_PERCENT, testService.getTestsByLoincCode(LOINC_MN_PERCENT)); - testLoincMap.put(ANALYZER_TEST_TCBF_COUNT, testService.getTestsByLoincCode(LOINC_TCBF_COUNT)); + testToLoincMap.put(ANALYZER_TEST_WBC, LOINC_WBC); + testToLoincMap.put(ANALYZER_TEST_RBC, LOINC_RBC); + testToLoincMap.put(ANALYZER_TEST_HGB, LOINC_HGB); + testToLoincMap.put(ANALYZER_TEST_HCT, LOINC_HCT); + testToLoincMap.put(ANALYZER_TEST_MCV, LOINC_MCV); + testToLoincMap.put(ANALYZER_TEST_MCH, LOINC_MCH); + testToLoincMap.put(ANALYZER_TEST_MCHC, LOINC_MCHC); + testToLoincMap.put(ANALYZER_TEST_RDWSD, LOINC_RDWSD); + testToLoincMap.put(ANALYZER_TEST_RDWCV, LOINC_RDWCV); + testToLoincMap.put(ANALYZER_TEST_PLT, LOINC_PLT); + testToLoincMap.put(ANALYZER_TEST_MPV, LOINC_MPV); + testToLoincMap.put(ANALYZER_TEST_NEUT_COUNT, LOINC_NEUT_COUNT); + testToLoincMap.put(ANALYZER_TEST_NEUT_PERCENT, LOINC_NEUT_PERCENT); + testToLoincMap.put(ANALYZER_TEST_LYMPH_COUNT, LOINC_LYMPH_COUNT); + testToLoincMap.put(ANALYZER_TEST_LYMPH_PERCENT, LOINC_LYMPH_PERCENT); + testToLoincMap.put(ANALYZER_TEST_MONO_COUNT, LOINC_MONO_COUNT); + testToLoincMap.put(ANALYZER_TEST_MONO_PERCENT, LOINC_MONO_PERCENT); + testToLoincMap.put(ANALYZER_TEST_EO_COUNT, LOINC_EO_COUNT); + testToLoincMap.put(ANALYZER_TEST_EO_PERCENT, LOINC_EO_PERCENT); + testToLoincMap.put(ANALYZER_TEST_BASO_COUNT, LOINC_BASO_COUNT); + testToLoincMap.put(ANALYZER_TEST_BASO_PERCENT, LOINC_BASO_PERCENT); + testToLoincMap.put(ANALYZER_TEST_IG_COUNT, LOINC_IG_COUNT); + testToLoincMap.put(ANALYZER_TEST_IG_PERCENT, LOINC_IG_PERCENT); + // testToLoincMap.put(ANALYZER_TEST_PDW, LOINC_PDW); + // testToLoincMap.put(ANALYZER_TEST_PLCR, LOINC_PLCR); + // testToLoincMap.put(ANALYZER_TEST_PCT, LOINC_PCT); + testToLoincMap.put(ANALYZER_TEST_RET_COUNT, LOINC_RET_COUNT); + testToLoincMap.put(ANALYZER_TEST_RET_PERCENT, LOINC_RET_PERCENT); + testToLoincMap.put(ANALYZER_TEST_IRF, LOINC_IRF); + // testToLoincMap.put(ANALYZER_TEST_LFR, LOINC_LFR); + // testToLoincMap.put(ANALYZER_TEST_MFR, LOINC_MFR); + // testToLoincMap.put(ANALYZER_TEST_HFR, LOINC_HFR); + // testToLoincMap.put(ANALYZER_TEST_LWBC, LOINC_LWBC); + testToLoincMap.put(ANALYZER_TEST_RETHE, LOINC_RETHE); + testToLoincMap.put(ANALYZER_TEST_WBCBF, LOINC_WBCBF); + testToLoincMap.put(ANALYZER_TEST_RBCBF, LOINC_RBCBF); + testToLoincMap.put(ANALYZER_TEST_MN_COUNT, LOINC_MN_COUNT); + testToLoincMap.put(ANALYZER_TEST_PMN_COUNT, LOINC_PMN_COUNT); + testToLoincMap.put(ANALYZER_TEST_MN_PERCENT, LOINC_MN_PERCENT); + testToLoincMap.put(ANALYZER_TEST_TCBF_COUNT, LOINC_TCBF_COUNT); + for (Entry entry : testToLoincMap.entrySet()) { + loincToTestCodeMap.put(entry.getValue(), entry.getKey()); + testCodeToTestsMap.put(entry.getKey(), testService.getTestsByLoincCode(entry.getValue())); + } - Analyzer analyzer = analyzerService.getAnalyzerByName("SysmexXNLAnalyzer"); + Analyzer analyzer = analyzerService.getAnalyzerByName(SysmexXNLAnalyzer.ANALYZER_NAME); ANALYZER_ID = analyzer.getId(); } @@ -249,9 +271,29 @@ public boolean insert(List lines, String currentUserId) { @Override public String getError() { - return "SysmexXNLAnalyzer analyzer unable to write to database"; + return SysmexXNLAnalyzer.ANALYZER_NAME + " analyzer unable to write to database"; } + private Test findMatchingTest(Sample sample, String resultTestCode) { + if (sample != null) { + for (Analysis curAnalysis : analysisService.getAnalysesBySampleId(sample.getId())) { + List possibleTests = testCodeToTestsMap.get(resultTestCode); + // if ((possibleTests == null || possibleTests.size() == 0) && resultTestCode.contains("+")) { + // possibleTests = testCodeToTestsMap.get(resultTestCode); + // } + if (possibleTests != null) { + for (Test curTest : possibleTests) { + if (curTest.getLoinc() != null && curTest.getLoinc().equals(curAnalysis.getTest().getLoinc())) { + LogEvent.logDebug(this.getClass().getSimpleName(), "findMatchingTest", "found test in sample for code: " + resultTestCode); + return curAnalysis.getTest(); + } + } + } + } + } + return null; + } + //example patient record: // P|1|||123456|^Jim^Brown||20010820|M|||||^Dr.1||||||||||||^^^WEST // example order record: @@ -260,15 +302,16 @@ public String getError() { // R|1|^^^^WBC^1|7.80|10*3/uL||N||||||20011116101000 private void addRecordsToResults(String patientRecord, String orderRecord, String resultRecord, List results, String currentUserId) { - String[] patientRecordFields = patientRecord.split(DEFAULT_FIELD_DELIMITER); - String[] orderRecordFields = orderRecord.split(DEFAULT_FIELD_DELIMITER); - String[] orderTestIdFields = orderRecordFields[4].split(DEFAULT_REPEATER_DELIMITER); - String[] orderIdFields = orderRecordFields[3].split(DEFAULT_SUBFIELD_DELIMITER); - String[] resultRecordFields = resultRecord.split(DEFAULT_FIELD_DELIMITER); - String[] resultTestIdField = resultRecordFields[2].split(DEFAULT_SUBFIELD_DELIMITER); + String[] patientRecordFields = patientRecord.split(Pattern.quote(FD)); + String[] orderRecordFields = orderRecord.split(Pattern.quote(FD)); + String[] orderTestIdFields = orderRecordFields[4].split(Pattern.quote(RD)); + String[] orderIdFields = orderRecordFields[3].split(Pattern.quote(CD)); + String[] resultRecordFields = resultRecord.split(Pattern.quote(FD)); + String[] resultTestIdField = resultRecordFields[2].split(Pattern.quote(CD)); + String resultRecordAbnormalFlag = resultRecordFields[6]; List orderTestIds = new ArrayList<>(); for (String orderIdField : orderTestIdFields) { - String[] orderIds = orderIdField.split(DEFAULT_SUBFIELD_DELIMITER); + String[] orderIds = orderIdField.split(Pattern.quote(CD)); String orderTestId = orderIds.length >= 5 ? orderIds[4] : ""; if (GenericValidator.isBlankOrNull(orderTestId)) { LogEvent.logWarn(this.getClass().getSimpleName(), "addRecordsToResults", "order analysis parameter name is not present"); @@ -282,30 +325,42 @@ private void addRecordsToResults(String patientRecord, String orderRecord, Strin String currentAccessionNumber = orderIdFields[2].trim(); Sample sample = sampleService.getSampleByAccessionNumber(currentAccessionNumber); - Test test = null; - if (sample != null) { - for (Analysis curAnalysis : analysisService.getAnalysesBySampleId(sample.getId())) { - List possibleTests = testLoincMap.get(resultTestId); - if ((possibleTests == null || possibleTests.size() == 0) && resultTestId.contains("+")) { - possibleTests = testLoincMap.get(resultTestId); - } - if (possibleTests != null) { - for (Test curTest : possibleTests) { - if (curTest.getLoinc() != null && curTest.getLoinc().equals(curAnalysis.getTest().getLoinc())) { - test = curAnalysis.getTest(); - break; - } - } - } - } - } - + Test test = findMatchingTest(sample, resultTestId); + if (test == null) { LogEvent.logError(this.getClass().getName(), "addRecordsToResults", "can't import a result if order does not have that test ordered"); return; } - + + switch (resultRecordAbnormalFlag) { + case "L": //lower than reference interval + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "lower than interval result"); + break; + case "H": // higher than reference interval + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "higher than interval result"); + break; + case ">": //out of assured linearity + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "out of assured linearity result"); + break; + case "N": //normal + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "normal result"); + break; + case "A": //abnormal + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "abnormal result"); + break; + case "W": //low reliability + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "low reliability result"); + break; + case "LL": //less than panic value + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "less than panic value result"); + break; + case "HH": //higher than panic value + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "higher than panic value result"); + break; + default: + LogEvent.logWarn(this.getClass().getSimpleName(), "addRecordsToResults", "abnormal flag not understood"); + } AnalyzerResults analyzerResults = addResult(results, null, "N", resultRecordFields[3], DateUtil.convertStringDateToTimestampWithPattern(resultRecordFields[12], "yyyyMMddHHmmss"), currentAccessionNumber, false, resultRecordFields[4], test); @@ -366,6 +421,77 @@ private AnalyzerResults createAnalyzerResult(String resultType, String resultVal public void persistImport(List resultList) { this.persistImport("1", resultList); } + + public String buildResponse(List lines) { + + String queryRecord = null; + + for (String line : lines) { + if (line.startsWith(TEST_COMMUNICATION_IDENTIFIER)) { + LogEvent.logInfo(this.getClass().getName(), "buildResponse", "this is a test communication record for " + SysmexXNLAnalyzer.ANALYZER_NAME); + } + if (line.startsWith(QUERY_RECORD_IDENTIFIER)) { + LogEvent.logDebug(this.getClass().getName(), "buildResponse", "request contains query record"); + String response = generateQueryResponse(line); + return response; + } + } + LogEvent.logWarn(this.getClass().getName(), "buildResponse", "no response could be created, no query identifier supplied"); + return ""; + } + + private String generateQueryResponse(String queryRecord) { + LogEvent.logDebug(this.getClass().getSimpleName(), "generateQueryResponse", "generating query response"); + String[] queryRecordFields = queryRecord.split(Pattern.quote(FD)); + + String[] startingRangeIdNumber = queryRecordFields[2].split(Pattern.quote(CD)); + String sampleIdNo = startingRangeIdNumber[2].trim(); + String sampleNoAttribute = startingRangeIdNumber[3]; + + StringBuilder msgBuilder = new StringBuilder(); + LogEvent.logDebug(this.getClass().getSimpleName(), "generateQueryResponse", "searching for sample with lab Number: " + sampleIdNo); + + Sample sample = sampleService.getSampleByAccessionNumber(sampleIdNo.trim()); + if (sample == null) { + LogEvent.logWarn(this.getClass().getSimpleName(), "generateQueryResponse", "no sample found with lab Number: " + sampleIdNo); + //return could not find sample messager + return msgBuilder.append("H|\\^&|\r\n") + .append("P|1|\r\n") + .append("O|1||||||||||||||||||||||||Y\r\n") //"Y" is no order exists marker + .append("L|1|N\r\n").toString(); + } + Patient patient = sampleHumanService.getPatientForSample(sample); + Person person = patient.getPerson(); + msgBuilder.append("H|\\^&|||||||||||E1394-97\r\n"); + msgBuilder.append("P|1|||") + .append(patient.getNationalId()) //patient identifier + .append("|^").append(person.getFirstName()).append("^").append(person.getLastName()) //names + .append("||").append(patient.getBirthDate() == null ? "": dateFormat.format(patient.getBirthDate())) //birthdate + .append("|").append(patient.getGender()) //gender M F U + .append("|||||") + .append("")//DR id + .append("||||||||||||\r\n"); + + msgBuilder.append("O|1|").append(queryRecordFields[2]).append("||"); + + boolean first = true; + for (Analysis curAnalysis : analysisService.getAnalysesBySampleId(sample.getId())) { + Optional testCode = Optional.ofNullable(loincToTestCodeMap.get(curAnalysis.getTest().getLoinc())); + if (testCode.isPresent()) { + LogEvent.logDebug(this.getClass().getSimpleName(), "generateQueryResponse", "found supported test in sample with test code: " + testCode.get()); + if (!first) { + msgBuilder.append("\\"); + } + first = false; + msgBuilder.append("^^^^").append(testCode.get()); + } + } + msgBuilder.append("||") + .append(sample.getEnteredDate() == null ? "" : dateTimeFormat.format(sample.getEnteredDate())) + .append("|||||N||||||||||||||Q\r\n"); + msgBuilder.append("L|1|N\r\n"); + return msgBuilder.toString(); + } } \ No newline at end of file diff --git a/analyzers/SysmexXN-L/src/main/java/oe/plugin/analyzer/SysmexXNLAnalyzerMenu.java b/analyzers/SysmexXN-L/src/main/java/oe/plugin/analyzer/SysmexXNLAnalyzerMenu.java index 709ba09..2a93a6c 100644 --- a/analyzers/SysmexXN-L/src/main/java/oe/plugin/analyzer/SysmexXNLAnalyzerMenu.java +++ b/analyzers/SysmexXN-L/src/main/java/oe/plugin/analyzer/SysmexXNLAnalyzerMenu.java @@ -34,22 +34,22 @@ protected void insertMenu() { // The order this analyzer will show on the menu relative to other analyzers menu.setPresentationOrder(10); // The id needs to be unique in the system - menu.setElementId("httpanalyzer_plugin"); + menu.setElementId(SysmexXNLAnalyzer.ANALYZER_NAME + "_plugin"); // This will always be "/AnalyzerResults?type=Analyzer->SysmexXNLAnalyzer"); + SystemModule module = service.getOrCreateSystemModule("AnalyzerResults", SysmexXNLAnalyzer.ANALYZER_NAME, + "Results->Analyzer->" + SysmexXNLAnalyzer.ANALYZER_NAME); Role role = service.getSystemRole( "Results" ); return service.bindRoleToModule( role, module ); } diff --git a/analyzers/SysmexXP/pom.xml b/analyzers/SysmexXP/pom.xml index 98bfa07..bad561e 100644 --- a/analyzers/SysmexXP/pom.xml +++ b/analyzers/SysmexXP/pom.xml @@ -11,7 +11,7 @@ ../../pom.xml - 0.3 + 0.5 UTF-8 2.3 diff --git a/analyzers/SysmexXP/src/main/java/oe/plugin/analyzer/SysmexXPAnalyzer.java b/analyzers/SysmexXP/src/main/java/oe/plugin/analyzer/SysmexXPAnalyzer.java index 3d1591a..46aea54 100644 --- a/analyzers/SysmexXP/src/main/java/oe/plugin/analyzer/SysmexXPAnalyzer.java +++ b/analyzers/SysmexXP/src/main/java/oe/plugin/analyzer/SysmexXPAnalyzer.java @@ -20,25 +20,29 @@ import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerLineInserter; import org.openelisglobal.common.services.PluginAnalyzerService; import org.openelisglobal.plugin.AnalyzerImporterPlugin; +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerResponder; import org.openelisglobal.common.log.LogEvent; public class SysmexXPAnalyzer implements AnalyzerImporterPlugin { + public static final String ANALYZER_NAME = "SysmexXPAnalyzer"; + @Override public boolean connect() { List nameMapping = new ArrayList<>(); nameMapping.add( - new PluginAnalyzerService.TestMapping(SysmexXPAnalyzerImplementation.ANALYZER_TEST_WBC, "White Blood Cells Count", + new PluginAnalyzerService.TestMapping(SysmexXPAnalyzerImplementation.ANALYZER_TEST_WBC, "White Blood Cells Count (WBC)", SysmexXPAnalyzerImplementation.LOINC_WBC)); nameMapping.add( - new PluginAnalyzerService.TestMapping(SysmexXPAnalyzerImplementation.ANALYZER_TEST_RBC, "Red Blood Cells Count", + new PluginAnalyzerService.TestMapping(SysmexXPAnalyzerImplementation.ANALYZER_TEST_RBC, "Red Blood Cells Count (RBC)", SysmexXPAnalyzerImplementation.LOINC_RBC)); nameMapping.add( - new PluginAnalyzerService.TestMapping(SysmexXPAnalyzerImplementation.ANALYZER_TEST_HGB, "​Hemoglobin", + new PluginAnalyzerService.TestMapping(SysmexXPAnalyzerImplementation.ANALYZER_TEST_HGB, "Hemoglobin", SysmexXPAnalyzerImplementation.LOINC_HGB)); nameMapping.add( new PluginAnalyzerService.TestMapping(SysmexXPAnalyzerImplementation.ANALYZER_TEST_HCT, "Hematocrit", @@ -65,7 +69,7 @@ public boolean connect() { new PluginAnalyzerService.TestMapping(SysmexXPAnalyzerImplementation.ANALYZER_TEST_MPV, "", SysmexXPAnalyzerImplementation.LOINC_MPV)); nameMapping.add( - new PluginAnalyzerService.TestMapping(SysmexXPAnalyzerImplementation.ANALYZER_TEST_NEUT_COUNT, "Neutrophiles​", + new PluginAnalyzerService.TestMapping(SysmexXPAnalyzerImplementation.ANALYZER_TEST_NEUT_COUNT, "Neutrophiles", SysmexXPAnalyzerImplementation.LOINC_NEUT_COUNT)); nameMapping.add( new PluginAnalyzerService.TestMapping(SysmexXPAnalyzerImplementation.ANALYZER_TEST_NEUT_PERCENT, "Neutrophiles (%)", @@ -106,7 +110,7 @@ public boolean connect() { nameMapping.add( new PluginAnalyzerService.TestMapping(SysmexXPAnalyzerImplementation.ANALYZER_TEST_MXD_PERCENT, "", SysmexXPAnalyzerImplementation.LOINC_MXD_PERCENT)); - getInstance().addAnalyzerDatabaseParts("SysmexXPAnalyzer", "SysmexXPAnalyzer", nameMapping, true); + getInstance().addAnalyzerDatabaseParts(SysmexXPAnalyzer.ANALYZER_NAME, SysmexXPAnalyzer.ANALYZER_NAME, nameMapping, true); getInstance().registerAnalyzer(this); return true; } @@ -115,21 +119,22 @@ public boolean connect() { public boolean isTargetAnalyzer(List lines) { for (String line : lines) { if (line.startsWith(SysmexXPAnalyzerImplementation.HEADER_RECORD_IDENTIFIER)) { - String[] headerRecord = line.split(SysmexXPAnalyzerImplementation.DEFAULT_FIELD_DELIMITER); + String[] headerRecord = line.split(Pattern.quote(SysmexXPAnalyzerImplementation.FD)); if (headerRecord.length < 5) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is not SysmexXP: header record not long enough"); return false; } - String[] senderName = headerRecord[4].split(SysmexXPAnalyzerImplementation.DEFAULT_SUBFIELD_DELIMITER); - if (senderName.length < 1) { + String[] senderNameFields = headerRecord[4].split(Pattern.quote(SysmexXPAnalyzerImplementation.CD)); + if (senderNameFields.length < 1) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is not SysmexXP: sender name field not long enough"); return false; } - LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message analyzer name is " + senderName[0]); - if (senderName[0].equalsIgnoreCase("XP-100")) { + String senderName = senderNameFields[0].trim(); + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message analyzer name is " + senderName); + if (senderName.equalsIgnoreCase("XP-100")) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is SysmexXP (XP-100)"); return true; - } else if (senderName[0].equalsIgnoreCase("XP-300")) { + } else if (senderName.equalsIgnoreCase("XP-300")) { LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is SysmexXP (XP-300)"); return true; } @@ -141,9 +146,25 @@ public boolean isTargetAnalyzer(List lines) { return false; } + @Override + public boolean isAnalyzerResult(List lines) { + for (String line : lines) { + if (line.startsWith(SysmexXPAnalyzerImplementation.RESULT_RECORD_IDENTIFIER)) { + return true; + } + } + LogEvent.logDebug(this.getClass().getSimpleName(), "isAnalyzerResult", "no result recoord identifier located"); + return false; + } + @Override public AnalyzerLineInserter getAnalyzerLineInserter() { return new SysmexXPAnalyzerImplementation(); } + @Override + public AnalyzerResponder getAnalyzerResponder() { + return new SysmexXPAnalyzerImplementation(); + } + } diff --git a/analyzers/SysmexXP/src/main/java/oe/plugin/analyzer/SysmexXPAnalyzerImplementation.java b/analyzers/SysmexXP/src/main/java/oe/plugin/analyzer/SysmexXPAnalyzerImplementation.java index 395caf1..1dd8743 100644 --- a/analyzers/SysmexXP/src/main/java/oe/plugin/analyzer/SysmexXPAnalyzerImplementation.java +++ b/analyzers/SysmexXP/src/main/java/oe/plugin/analyzer/SysmexXPAnalyzerImplementation.java @@ -17,16 +17,22 @@ package oe.plugin.analyzer; import java.sql.Timestamp; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; +import java.util.Optional; import org.apache.commons.validator.GenericValidator; import org.openelisglobal.analyzer.service.AnalyzerService; import org.openelisglobal.analyzer.valueholder.Analyzer; import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerLineInserter; import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerReaderUtil; +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerResponder; import org.openelisglobal.analyzerimport.util.AnalyzerTestNameCache; import org.openelisglobal.analyzerimport.util.MappedTestName; import org.openelisglobal.analyzerresults.valueholder.AnalyzerResults; @@ -34,14 +40,17 @@ import org.openelisglobal.spring.util.SpringContext; import org.openelisglobal.test.service.TestService; import org.openelisglobal.sample.service.SampleService; +import org.openelisglobal.samplehuman.service.SampleHumanService; import org.openelisglobal.analysis.service.AnalysisService; import org.openelisglobal.test.valueholder.Test; import org.openelisglobal.analysis.valueholder.Analysis; +import org.openelisglobal.person.valueholder.Person; +import org.openelisglobal.patient.valueholder.Patient; import org.openelisglobal.sample.valueholder.Sample; import org.openelisglobal.common.util.DateUtil; import org.openelisglobal.common.log.LogEvent; -public class SysmexXPAnalyzerImplementation extends AnalyzerLineInserter { +public class SysmexXPAnalyzerImplementation extends AnalyzerLineInserter implements AnalyzerResponder { static final String ANALYZER_TEST_WBC = "WBC"; static final String ANALYZER_TEST_RBC = "RBC"; @@ -82,7 +91,7 @@ public class SysmexXPAnalyzerImplementation extends AnalyzerLineInserter { static final String LOINC_MPV = "32623-1"; static final String LOINC_NEUT_COUNT = "751-8"; static final String LOINC_NEUT_PERCENT = "770-8"; - static final String LOINC_LYMPH_COUNT = "731-0"; + static final String LOINC_LYMPH_COUNT = "731-0";; static final String LOINC_LYMPH_PERCENT = "736-9"; static final String LOINC_MONO_COUNT = "742-7"; static final String LOINC_MONO_PERCENT = "5905-5"; @@ -96,54 +105,67 @@ public class SysmexXPAnalyzerImplementation extends AnalyzerLineInserter { static final String LOINC_MXD_PERCENT = "32155-4"; protected static final String HEADER_RECORD_IDENTIFIER = "H"; + protected static final String QUERY_RECORD_IDENTIFIER = "Q"; protected static final String PATIENT_RECORD_IDENTIFIER = "P"; protected static final String ORDER_RECORD_IDENTIFIER = "O"; protected static final String RESULT_RECORD_IDENTIFIER = "R"; protected static final String END_RECORD_IDENTIFIER = "L"; - protected static final String DEFAULT_FIELD_DELIMITER = "\\|"; - protected static final String DEFAULT_SUBFIELD_DELIMITER = "\\^"; - protected static final String DEFAULT_REPEATER_DELIMITER = "\\\\"; + protected static final String FD = "|"; //DEFAULT_FIELD_DELIMITER + protected static final String CD = "^"; //DEFAULT_COMPONENT_DELIMITER + protected static final String RD = "\\"; //DEFAULT_REPEATER_DELIMITER + protected static final String ED = "&"; //DEFAULT_ESCAPE_DELIMITER protected static final String TEST_COMMUNICATION_IDENTIFIER = "M|1|106"; + private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); + private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyyMMddHHmmss"); + private TestService testService = SpringContext.getBean(TestService.class); private SampleService sampleService = SpringContext.getBean(SampleService.class); + private SampleHumanService sampleHumanService = SpringContext.getBean(SampleHumanService.class); private AnalyzerService analyzerService = SpringContext.getBean(AnalyzerService.class); private AnalysisService analysisService = SpringContext.getBean(AnalysisService.class); private String ANALYZER_ID; - private HashMap> testLoincMap = new HashMap<>(); + private Map testToLoincMap = new HashMap<>(); + private Map loincToTestCodeMap = new HashMap<>(); + private Map> testCodeToTestsMap = new HashMap<>(); private AnalyzerReaderUtil readerUtil = new AnalyzerReaderUtil(); public SysmexXPAnalyzerImplementation() { - testLoincMap.put(ANALYZER_TEST_WBC, testService.getTestsByLoincCode(LOINC_WBC)); - testLoincMap.put(ANALYZER_TEST_RBC, testService.getTestsByLoincCode(LOINC_RBC)); - testLoincMap.put(ANALYZER_TEST_HGB, testService.getTestsByLoincCode(LOINC_HGB)); - testLoincMap.put(ANALYZER_TEST_HCT, testService.getTestsByLoincCode(LOINC_HCT)); - testLoincMap.put(ANALYZER_TEST_MCV, testService.getTestsByLoincCode(LOINC_MCV)); - testLoincMap.put(ANALYZER_TEST_MCH, testService.getTestsByLoincCode(LOINC_MCH)); - testLoincMap.put(ANALYZER_TEST_MCHC, testService.getTestsByLoincCode(LOINC_MCHC)); - testLoincMap.put(ANALYZER_TEST_RDWSD, testService.getTestsByLoincCode(LOINC_RDWSD)); - testLoincMap.put(ANALYZER_TEST_RDWCV, testService.getTestsByLoincCode(LOINC_RDWCV)); - testLoincMap.put(ANALYZER_TEST_PLT, testService.getTestsByLoincCode(LOINC_PLT)); - testLoincMap.put(ANALYZER_TEST_MPV, testService.getTestsByLoincCode(LOINC_MPV)); - testLoincMap.put(ANALYZER_TEST_NEUT_COUNT, testService.getTestsByLoincCode(LOINC_NEUT_COUNT)); - testLoincMap.put(ANALYZER_TEST_NEUT_PERCENT, testService.getTestsByLoincCode(LOINC_NEUT_PERCENT)); - testLoincMap.put(ANALYZER_TEST_LYMPH_COUNT, testService.getTestsByLoincCode(LOINC_LYMPH_COUNT)); - testLoincMap.put(ANALYZER_TEST_LYMPH_PERCENT, testService.getTestsByLoincCode(LOINC_LYMPH_PERCENT)); - testLoincMap.put(ANALYZER_TEST_MONO_COUNT, testService.getTestsByLoincCode(LOINC_MONO_COUNT)); - testLoincMap.put(ANALYZER_TEST_MONO_PERCENT, testService.getTestsByLoincCode(LOINC_MONO_PERCENT)); - testLoincMap.put(ANALYZER_TEST_EO_COUNT, testService.getTestsByLoincCode(LOINC_EO_COUNT)); - testLoincMap.put(ANALYZER_TEST_EO_PERCENT, testService.getTestsByLoincCode(LOINC_EO_PERCENT)); - testLoincMap.put(ANALYZER_TEST_BASO_COUNT, testService.getTestsByLoincCode(LOINC_BASO_COUNT)); - testLoincMap.put(ANALYZER_TEST_BASO_PERCENT, testService.getTestsByLoincCode(LOINC_BASO_PERCENT)); - testLoincMap.put(ANALYZER_TEST_IG_COUNT, testService.getTestsByLoincCode(LOINC_IG_COUNT)); - testLoincMap.put(ANALYZER_TEST_IG_PERCENT, testService.getTestsByLoincCode(LOINC_IG_PERCENT)); - testLoincMap.put(ANALYZER_TEST_MXD_COUNT, testService.getTestsByLoincCode(LOINC_MXD_COUNT)); - testLoincMap.put(ANALYZER_TEST_MXD_PERCENT, testService.getTestsByLoincCode(LOINC_MXD_PERCENT)); - - Analyzer analyzer = analyzerService.getAnalyzerByName("SysmexXNLAnalyzer"); + testToLoincMap.put(ANALYZER_TEST_WBC, LOINC_WBC); + testToLoincMap.put(ANALYZER_TEST_RBC, LOINC_RBC); + testToLoincMap.put(ANALYZER_TEST_HGB, LOINC_HGB); + testToLoincMap.put(ANALYZER_TEST_HCT, LOINC_HCT); + testToLoincMap.put(ANALYZER_TEST_MCV, LOINC_MCV); + testToLoincMap.put(ANALYZER_TEST_MCH, LOINC_MCH); + testToLoincMap.put(ANALYZER_TEST_MCHC, LOINC_MCHC); + testToLoincMap.put(ANALYZER_TEST_RDWSD, LOINC_RDWSD); + testToLoincMap.put(ANALYZER_TEST_RDWCV, LOINC_RDWCV); + testToLoincMap.put(ANALYZER_TEST_PLT, LOINC_PLT); + testToLoincMap.put(ANALYZER_TEST_MPV, LOINC_MPV); + testToLoincMap.put(ANALYZER_TEST_NEUT_COUNT, LOINC_NEUT_COUNT); + testToLoincMap.put(ANALYZER_TEST_NEUT_PERCENT, LOINC_NEUT_PERCENT); + testToLoincMap.put(ANALYZER_TEST_LYMPH_COUNT, LOINC_LYMPH_COUNT); + testToLoincMap.put(ANALYZER_TEST_LYMPH_PERCENT, LOINC_LYMPH_PERCENT); + testToLoincMap.put(ANALYZER_TEST_MONO_COUNT, LOINC_MONO_COUNT); + testToLoincMap.put(ANALYZER_TEST_MONO_PERCENT, LOINC_MONO_PERCENT); + testToLoincMap.put(ANALYZER_TEST_EO_COUNT, LOINC_EO_COUNT); + testToLoincMap.put(ANALYZER_TEST_EO_PERCENT, LOINC_EO_PERCENT); + testToLoincMap.put(ANALYZER_TEST_BASO_COUNT, LOINC_BASO_COUNT); + testToLoincMap.put(ANALYZER_TEST_BASO_PERCENT, LOINC_BASO_PERCENT); + testToLoincMap.put(ANALYZER_TEST_IG_COUNT, LOINC_IG_COUNT); + testToLoincMap.put(ANALYZER_TEST_IG_PERCENT, LOINC_IG_PERCENT); + testToLoincMap.put(ANALYZER_TEST_MXD_COUNT, LOINC_MXD_COUNT); + testToLoincMap.put(ANALYZER_TEST_MXD_PERCENT, LOINC_MXD_PERCENT); + + for (Entry entry : testToLoincMap.entrySet()) { + loincToTestCodeMap.put(entry.getValue(), entry.getKey()); + testCodeToTestsMap.put(entry.getKey(), testService.getTestsByLoincCode(entry.getValue())); + } + + Analyzer analyzer = analyzerService.getAnalyzerByName(SysmexXPAnalyzer.ANALYZER_NAME); ANALYZER_ID = analyzer.getId(); } @@ -173,7 +195,7 @@ public boolean insert(List lines, String currentUserId) { List results = new ArrayList<>(); for (String line : lines) { if (line.startsWith(TEST_COMMUNICATION_IDENTIFIER)) { - LogEvent.logInfo(this.getClass().getName(), "insert", "this is a test communication record for Aquios"); + LogEvent.logInfo(this.getClass().getName(), "insert", "this is a test communication record for Sysmex XP"); } if (line.startsWith(PATIENT_RECORD_IDENTIFIER)) { if (patientRecord != null) { @@ -200,7 +222,27 @@ public boolean insert(List lines, String currentUserId) { @Override public String getError() { - return "SysmexXNLAnalyzer analyzer unable to write to database"; + return SysmexXPAnalyzer.ANALYZER_NAME + " analyzer unable to write to database"; + } + + private Test findMatchingTest(Sample sample, String resultTestCode) { + if (sample != null) { + for (Analysis curAnalysis : analysisService.getAnalysesBySampleId(sample.getId())) { + List possibleTests = testCodeToTestsMap.get(resultTestCode); + // if ((possibleTests == null || possibleTests.size() == 0) && resultTestCode.contains("+")) { + // possibleTests = testCodeToTestsMap.get(resultTestCode); + // } + if (possibleTests != null) { + for (Test curTest : possibleTests) { + if (curTest.getLoinc() != null && curTest.getLoinc().equals(curAnalysis.getTest().getLoinc())) { + LogEvent.logDebug(this.getClass().getSimpleName(), "findMatchingTest", "found test in sample for code: " + resultTestCode); + return curAnalysis.getTest(); + } + } + } + } + } + return null; } //example patient record: @@ -211,15 +253,16 @@ public String getError() { // R|1|^^^^WBC^26|78|10*2/uL||N||||123456789012345||20011221163530 private void addRecordsToResults(String patientRecord, String orderRecord, String resultRecord, List results, String currentUserId) { - String[] patientRecordFields = patientRecord.split(DEFAULT_FIELD_DELIMITER); - String[] orderRecordFields = orderRecord.split(DEFAULT_FIELD_DELIMITER); - String[] orderTestIdFields = orderRecordFields[4].split(DEFAULT_REPEATER_DELIMITER); - String[] orderIdFields = orderRecordFields[3].split(DEFAULT_SUBFIELD_DELIMITER); - String[] resultRecordFields = resultRecord.split(DEFAULT_FIELD_DELIMITER); - String[] resultTestIdField = resultRecordFields[2].split(DEFAULT_SUBFIELD_DELIMITER); + String[] patientRecordFields = patientRecord.split(Pattern.quote(FD)); + String[] orderRecordFields = orderRecord.split(Pattern.quote(FD)); + String[] orderTestIdFields = orderRecordFields[4].split(Pattern.quote(RD)); + String[] orderIdFields = orderRecordFields[3].split(Pattern.quote(CD)); + String[] resultRecordFields = resultRecord.split(Pattern.quote(FD)); + String[] resultTestIdField = resultRecordFields[2].split(Pattern.quote(CD)); + String resultRecordAbnormalFlag = resultRecordFields[6]; List orderTestIds = new ArrayList<>(); for (String orderIdField : orderTestIdFields) { - String[] orderIds = orderIdField.split(DEFAULT_SUBFIELD_DELIMITER); + String[] orderIds = orderIdField.split(Pattern.quote(CD)); String orderTestId = orderIds.length >= 5 ? orderIds[4] : ""; if (GenericValidator.isBlankOrNull(orderTestId)) { LogEvent.logWarn(this.getClass().getSimpleName(), "addRecordsToResults", "order analysis parameter name is not present"); @@ -233,23 +276,7 @@ private void addRecordsToResults(String patientRecord, String orderRecord, Strin String currentAccessionNumber = orderIdFields[2].trim(); Sample sample = sampleService.getSampleByAccessionNumber(currentAccessionNumber); - Test test = null; - if (sample != null) { - for (Analysis curAnalysis : analysisService.getAnalysesBySampleId(sample.getId())) { - List possibleTests = testLoincMap.get(resultTestId); - if ((possibleTests == null || possibleTests.size() == 0) && resultTestId.contains("+")) { - possibleTests = testLoincMap.get(resultTestId); - } - if (possibleTests != null) { - for (Test curTest : possibleTests) { - if (curTest.getLoinc() != null && curTest.getLoinc().equals(curAnalysis.getTest().getLoinc())) { - test = curAnalysis.getTest(); - break; - } - } - } - } - } + Test test = findMatchingTest(sample, resultTestId); if (test == null) { LogEvent.logError(this.getClass().getName(), "addRecordsToResults", @@ -257,6 +284,29 @@ private void addRecordsToResults(String patientRecord, String orderRecord, Strin return; } + switch (resultRecordAbnormalFlag) { + case "A": //masked data + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "masked data result"); + break; + case ">": //out of assured linearity + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "out of assured linearity result"); + break; + case "W": //low reliability + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "low reliability result"); + break; + case "H": // higher than reference interval + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "higher than interval result"); + break; + case "L": //lower than reference interval + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "lower than interval result"); + break; + case "N": //normal + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "normal result"); + break; + default: + LogEvent.logWarn(this.getClass().getSimpleName(), "addRecordsToResults", "abnormal flag not understood"); + } + AnalyzerResults analyzerResults = addResult(results, null, "N", resultRecordFields[3], DateUtil.convertStringDateToTimestampWithPattern(resultRecordFields[12], "yyyyMMddHHmmss"), currentAccessionNumber, false, resultRecordFields[4], test); @@ -316,6 +366,75 @@ private AnalyzerResults createAnalyzerResult(String resultType, String resultVal public void persistImport(List resultList) { this.persistImport("1", resultList); + }public String buildResponse(List lines) { + + String queryRecord = null; + + for (String line : lines) { + if (line.startsWith(TEST_COMMUNICATION_IDENTIFIER)) { + LogEvent.logInfo(this.getClass().getName(), "buildResponse", "this is a test communication record for " + SysmexXPAnalyzer.ANALYZER_NAME); + } + if (line.startsWith(QUERY_RECORD_IDENTIFIER)) { + LogEvent.logDebug(this.getClass().getName(), "buildResponse", "request contains query record"); + String response = generateQueryResponse(line); + return response; + } + } + LogEvent.logWarn(this.getClass().getName(), "buildResponse", "no response could be created, no query identifier supplied"); + return ""; + } + + private String generateQueryResponse(String queryRecord) { + LogEvent.logDebug(this.getClass().getSimpleName(), "generateQueryResponse", "generating query response"); + String[] queryRecordFields = queryRecord.split(Pattern.quote(FD)); + + String[] startingRangeIdNumber = queryRecordFields[2].split(Pattern.quote(CD)); + String sampleIdNo = startingRangeIdNumber[2].trim(); + String sampleNoAttribute = startingRangeIdNumber[3]; + + StringBuilder msgBuilder = new StringBuilder(); + LogEvent.logDebug(this.getClass().getSimpleName(), "generateQueryResponse", "searching for sample with lab Number: " + sampleIdNo); + + Sample sample = sampleService.getSampleByAccessionNumber(sampleIdNo.trim()); + if (sample == null) { + LogEvent.logWarn(this.getClass().getSimpleName(), "generateQueryResponse", "no sample found with lab Number: " + sampleIdNo); + //return could not find sample messager + return msgBuilder.append("H|\\^&|\r\n") + .append("P|1|\r\n") + .append("O|1||||||||||||||||||||||||Y\r\n") //"Y" is no order exists marker + .append("L|1|N\r\n").toString(); + } + Patient patient = sampleHumanService.getPatientForSample(sample); + Person person = patient.getPerson(); + msgBuilder.append("H|\\^&|||||||||||E1394-97\r\n"); + msgBuilder.append("P|1|||") + .append(patient.getNationalId()) //patient identifier + .append("|^").append(person.getFirstName()).append("^").append(person.getLastName()) //names + .append("||").append(patient.getBirthDate() == null ? "": dateFormat.format(patient.getBirthDate())) //birthdate + .append("|").append(patient.getGender()) //gender M F U + .append("|||||") + .append("")//DR id + .append("||||||||||||\r\n"); + + msgBuilder.append("O|1|").append(queryRecordFields[2]).append("||"); + + boolean first = true; + for (Analysis curAnalysis : analysisService.getAnalysesBySampleId(sample.getId())) { + Optional testCode = Optional.ofNullable(loincToTestCodeMap.get(curAnalysis.getTest().getLoinc())); + if (testCode.isPresent()) { + LogEvent.logDebug(this.getClass().getSimpleName(), "generateQueryResponse", "found supported test in sample with test code: " + testCode.get()); + if (!first) { + msgBuilder.append("\\"); + } + first = false; + msgBuilder.append("^^^^").append(testCode.get()); + } + } + msgBuilder.append("||") + .append(sample.getEnteredDate() == null ? "" : dateTimeFormat.format(sample.getEnteredDate())) + .append("|||||N||||||||||||||Q\r\n"); + msgBuilder.append("L|1|N\r\n"); + return msgBuilder.toString(); } } diff --git a/analyzers/SysmexXP/src/main/java/oe/plugin/analyzer/SysmexXPAnalyzerMenu.java b/analyzers/SysmexXP/src/main/java/oe/plugin/analyzer/SysmexXPAnalyzerMenu.java index fc07f0d..161f985 100644 --- a/analyzers/SysmexXP/src/main/java/oe/plugin/analyzer/SysmexXPAnalyzerMenu.java +++ b/analyzers/SysmexXP/src/main/java/oe/plugin/analyzer/SysmexXPAnalyzerMenu.java @@ -34,22 +34,22 @@ protected void insertMenu() { // The order this analyzer will show on the menu relative to other analyzers menu.setPresentationOrder(10); // The id needs to be unique in the system - menu.setElementId("httpanalyzer_plugin"); + menu.setElementId(SysmexXPAnalyzer.ANALYZER_NAME + "_plugin"); // This will always be "/AnalyzerResults?type=Analyzer->SysmexXPAnalyzer"); + SystemModule module = service.getOrCreateSystemModule("AnalyzerResults", SysmexXPAnalyzer.ANALYZER_NAME, + "Results->Analyzer->" + SysmexXPAnalyzer.ANALYZER_NAME); Role role = service.getSystemRole( "Results" ); return service.bindRoleToModule( role, module ); } diff --git a/analyzers/pocH-100i/.vscode/settings.json b/analyzers/pocH-100i/.vscode/settings.json new file mode 100644 index 0000000..890c247 --- /dev/null +++ b/analyzers/pocH-100i/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "java.project.sourcePaths": [ + "src/main/java" + ] +} \ No newline at end of file diff --git a/analyzers/pocH-100i/pom.xml b/analyzers/pocH-100i/pom.xml new file mode 100644 index 0000000..af30aac --- /dev/null +++ b/analyzers/pocH-100i/pom.xml @@ -0,0 +1,89 @@ + + 4.0.0 + org.openelisglobal.plugins + pocH-100iAnalyzer + + org.openelisglobal + openelisglobal-plugins + 1.0 + ../../pom.xml + + + 0.1 + + UTF-8 + 2.3 + + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + commons-fileupload + commons-fileupload + 1.4 + + + org.springframework + spring-webmvc + ${springframework.version} + + + + + src/main/java + + + src/main/filtered-resources + true + + + src/main/resources + + + + + maven-compiler-plugin + 3.8.0 + + 1.8 + 1.8 + + + + maven-resources-plugin + 2.4.3 + + + copy-resources + + install + + copy-resources + + + ${project.basedir}/../../plugins + + + + target/ + + ${project.build.finalName}.jar + + + false + + + + + + + + + \ No newline at end of file diff --git a/analyzers/pocH-100i/src/main/java/oe/plugin/analyzer/PocH100iAnalyzer.java b/analyzers/pocH-100i/src/main/java/oe/plugin/analyzer/PocH100iAnalyzer.java new file mode 100644 index 0000000..88d1658 --- /dev/null +++ b/analyzers/pocH-100i/src/main/java/oe/plugin/analyzer/PocH100iAnalyzer.java @@ -0,0 +1,167 @@ +/* + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations under + * the License. + * + * The Original Code is OpenELIS code. + * + * Copyright (C) ITECH, University of Washington, Seattle WA. All Rights Reserved. + */ + +package oe.plugin.analyzer; + +import static org.openelisglobal.common.services.PluginAnalyzerService.getInstance; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerLineInserter; +import org.openelisglobal.common.services.PluginAnalyzerService; +import org.openelisglobal.plugin.AnalyzerImporterPlugin; +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerResponder; +import org.openelisglobal.common.log.LogEvent; + +public class PocH100iAnalyzer implements AnalyzerImporterPlugin { + + public static final String ANALYZER_NAME = "PocH100iAnalyzer"; + + @Override + public boolean connect() { + List nameMapping = new ArrayList<>(); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_WBC, "White Blood Cells Count (WBC)", + PocH100iAnalyzerImplementation.LOINC_WBC)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_RBC, "Red Blood Cells Count (RBC)", + PocH100iAnalyzerImplementation.LOINC_RBC)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_HGB, "Hemoglobin", + PocH100iAnalyzerImplementation.LOINC_HGB)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_HCT, "Hematocrit", + PocH100iAnalyzerImplementation.LOINC_HCT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_MCV, "Medium corpuscular volum", + PocH100iAnalyzerImplementation.LOINC_MCV)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_MCH, "", + PocH100iAnalyzerImplementation.LOINC_MCH)); + nameMapping + .add(new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_MCHC, "", + PocH100iAnalyzerImplementation.LOINC_MCHC)); + nameMapping + .add(new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_RDWSD, "", + PocH100iAnalyzerImplementation.LOINC_RDWSD)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_RDWCV, "", + PocH100iAnalyzerImplementation.LOINC_RDWCV)); + nameMapping + .add(new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_PLT, "Platelets", + PocH100iAnalyzerImplementation.LOINC_PLT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_MPV, "", + PocH100iAnalyzerImplementation.LOINC_MPV)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_NEUT_COUNT, "Neutrophiles", + PocH100iAnalyzerImplementation.LOINC_NEUT_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_NEUT_PERCENT, "Neutrophiles (%)", + PocH100iAnalyzerImplementation.LOINC_NEUT_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_LYMPH_COUNT, "Lymphocytes (Abs)", + PocH100iAnalyzerImplementation.LOINC_LYMPH_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_LYMPH_PERCENT, "Lymphocytes (%)", + PocH100iAnalyzerImplementation.LOINC_LYMPH_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_MONO_COUNT, "Monocytes (Abs)", + PocH100iAnalyzerImplementation.LOINC_MONO_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_MONO_PERCENT, "Monocytes (%)", + PocH100iAnalyzerImplementation.LOINC_MONO_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_EO_COUNT, "Eosinophiles", + PocH100iAnalyzerImplementation.LOINC_EO_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_EO_PERCENT, "Eosinophiles (%)", + PocH100iAnalyzerImplementation.LOINC_EO_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_BASO_COUNT, "Basophiles", + PocH100iAnalyzerImplementation.LOINC_BASO_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_BASO_PERCENT, "Basophiles (%)", + PocH100iAnalyzerImplementation.LOINC_BASO_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_IG_COUNT, "", + PocH100iAnalyzerImplementation.LOINC_IG_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_IG_PERCENT, "", + PocH100iAnalyzerImplementation.LOINC_IG_PERCENT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_MXD_COUNT, "", + PocH100iAnalyzerImplementation.LOINC_MXD_COUNT)); + nameMapping.add( + new PluginAnalyzerService.TestMapping(PocH100iAnalyzerImplementation.ANALYZER_TEST_MXD_PERCENT, "", + PocH100iAnalyzerImplementation.LOINC_MXD_PERCENT)); + getInstance().addAnalyzerDatabaseParts(ANALYZER_NAME, ANALYZER_NAME, nameMapping, true); + getInstance().registerAnalyzer(this); + return true; + } + + @Override + public boolean isTargetAnalyzer(List lines) { + for (String line : lines) { + if (line.startsWith(PocH100iAnalyzerImplementation.HEADER_RECORD_IDENTIFIER)) { + String[] headerRecord = line.split(Pattern.quote(PocH100iAnalyzerImplementation.FD)); + if (headerRecord.length < 5) { + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is not PocH100i: header record not long enough"); + return false; + } + String[] senderNameFields = headerRecord[4].split(Pattern.quote(PocH100iAnalyzerImplementation.CD)); + if (senderNameFields.length < 1) { + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is not PocH100i: sender name field not long enough"); + return false; + } + String senderName = senderNameFields[0].trim(); + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message analyzer name is " + senderName); + if (senderName.equalsIgnoreCase("pocH-100i")) { + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is PocH100i"); + return true; + } + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is not PocH100i: sender name doesn't match"); + return false; + } + } + LogEvent.logTrace(this.getClass().getSimpleName(), "isTargetAnalyzer", "incoming message is not PocH100i: no header line"); + return false; + } + + @Override + public boolean isAnalyzerResult(List lines) { + for (String line : lines) { + if (line.startsWith(PocH100iAnalyzerImplementation.RESULT_RECORD_IDENTIFIER)) { + return true; + } + } + LogEvent.logDebug(this.getClass().getSimpleName(), "isAnalyzerResult", "no result recoord identifier located"); + return false; + } + + @Override + public AnalyzerLineInserter getAnalyzerLineInserter() { + return new PocH100iAnalyzerImplementation(); + } + + @Override + public AnalyzerResponder getAnalyzerResponder() { + return new PocH100iAnalyzerImplementation(); + } + +} diff --git a/analyzers/pocH-100i/src/main/java/oe/plugin/analyzer/PocH100iAnalyzerImplementation.java b/analyzers/pocH-100i/src/main/java/oe/plugin/analyzer/PocH100iAnalyzerImplementation.java new file mode 100644 index 0000000..f5427af --- /dev/null +++ b/analyzers/pocH-100i/src/main/java/oe/plugin/analyzer/PocH100iAnalyzerImplementation.java @@ -0,0 +1,441 @@ +/* + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations under + * the License. + * + * The Original Code is OpenELIS code. + * + * Copyright (C) ITECH, University of Washington, Seattle WA. All Rights Reserved. + */ + +package oe.plugin.analyzer; + +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; +import java.util.Optional; + +import org.apache.commons.validator.GenericValidator; +import org.openelisglobal.analyzer.service.AnalyzerService; +import org.openelisglobal.analyzer.valueholder.Analyzer; +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerLineInserter; +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerReaderUtil; +import org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerResponder; +import org.openelisglobal.analyzerimport.util.AnalyzerTestNameCache; +import org.openelisglobal.analyzerimport.util.MappedTestName; +import org.openelisglobal.analyzerresults.valueholder.AnalyzerResults; +import org.openelisglobal.common.services.PluginAnalyzerService; +import org.openelisglobal.spring.util.SpringContext; +import org.openelisglobal.test.service.TestService; +import org.openelisglobal.sample.service.SampleService; +import org.openelisglobal.samplehuman.service.SampleHumanService; +import org.openelisglobal.analysis.service.AnalysisService; +import org.openelisglobal.test.valueholder.Test; +import org.openelisglobal.analysis.valueholder.Analysis; +import org.openelisglobal.person.valueholder.Person; +import org.openelisglobal.patient.valueholder.Patient; +import org.openelisglobal.sample.valueholder.Sample; +import org.openelisglobal.common.util.DateUtil; +import org.openelisglobal.common.log.LogEvent; + +public class PocH100iAnalyzerImplementation extends AnalyzerLineInserter implements AnalyzerResponder { + + static final String ANALYZER_TEST_WBC = "WBC"; + static final String ANALYZER_TEST_RBC = "RBC"; + static final String ANALYZER_TEST_HGB = "HGB"; + static final String ANALYZER_TEST_HCT = "HCT"; + static final String ANALYZER_TEST_MCV = "MCV"; + static final String ANALYZER_TEST_MCH = "MCH"; + static final String ANALYZER_TEST_MCHC = "MCHC"; + static final String ANALYZER_TEST_RDWSD = "RDW-SD"; + static final String ANALYZER_TEST_RDWCV = "RDW-CV"; + static final String ANALYZER_TEST_PLT = "PLT"; + static final String ANALYZER_TEST_MPV = "MPV"; + static final String ANALYZER_TEST_NEUT_COUNT = "NEUT#"; + static final String ANALYZER_TEST_NEUT_PERCENT = "NEUT%"; + static final String ANALYZER_TEST_LYMPH_COUNT = "LYMPH#"; + static final String ANALYZER_TEST_LYMPH_PERCENT = "LYMPH%"; + static final String ANALYZER_TEST_MONO_COUNT = "MONO#"; + static final String ANALYZER_TEST_MONO_PERCENT = "MONO%"; + static final String ANALYZER_TEST_EO_COUNT = "EO#"; + static final String ANALYZER_TEST_EO_PERCENT = "EO%"; + static final String ANALYZER_TEST_BASO_COUNT = "BASO#"; + static final String ANALYZER_TEST_BASO_PERCENT = "BASO%"; + static final String ANALYZER_TEST_IG_COUNT = "IG#"; + static final String ANALYZER_TEST_IG_PERCENT = "IG%"; + static final String ANALYZER_TEST_MXD_COUNT = "MXD#"; + static final String ANALYZER_TEST_MXD_PERCENT = "MXD%"; + + static final String LOINC_WBC = "6690-2"; + static final String LOINC_RBC = "789-8"; + static final String LOINC_HGB = "718-7"; + static final String LOINC_HCT = "4544-3"; + static final String LOINC_MCV = "787-2"; + static final String LOINC_MCH = "785-6"; + static final String LOINC_MCHC = "786-4"; + static final String LOINC_RDWSD = "21000-5"; + static final String LOINC_RDWCV = "788-0"; + static final String LOINC_PLT = "777-3"; + static final String LOINC_MPV = "32623-1"; + static final String LOINC_NEUT_COUNT = "751-8"; + static final String LOINC_NEUT_PERCENT = "770-8"; + static final String LOINC_LYMPH_COUNT = "731-0";; + static final String LOINC_LYMPH_PERCENT = "736-9"; + static final String LOINC_MONO_COUNT = "742-7"; + static final String LOINC_MONO_PERCENT = "5905-5"; + static final String LOINC_EO_COUNT = "711-2"; + static final String LOINC_EO_PERCENT = "713-8"; + static final String LOINC_BASO_COUNT = "704-7"; + static final String LOINC_BASO_PERCENT = "706-2"; + static final String LOINC_IG_COUNT = "53115-2"; + static final String LOINC_IG_PERCENT = "71695-1"; + static final String LOINC_MXD_COUNT = "32154-7"; + static final String LOINC_MXD_PERCENT = "32155-4"; + + protected static final String HEADER_RECORD_IDENTIFIER = "H"; + protected static final String QUERY_RECORD_IDENTIFIER = "Q"; + protected static final String PATIENT_RECORD_IDENTIFIER = "P"; + protected static final String ORDER_RECORD_IDENTIFIER = "O"; + protected static final String RESULT_RECORD_IDENTIFIER = "R"; + protected static final String END_RECORD_IDENTIFIER = "L"; + protected static final String FD = "|"; //DEFAULT_FIELD_DELIMITER + protected static final String CD = "^"; //DEFAULT_COMPONENT_DELIMITER + protected static final String RD = "\\"; //DEFAULT_REPEATER_DELIMITER + protected static final String ED = "&"; //DEFAULT_ESCAPE_DELIMITER + protected static final String TEST_COMMUNICATION_IDENTIFIER = "M|1|106"; + private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); + private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyyMMddHHmmss"); + + + private TestService testService = SpringContext.getBean(TestService.class); + private SampleService sampleService = SpringContext.getBean(SampleService.class); + private SampleHumanService sampleHumanService = SpringContext.getBean(SampleHumanService.class); + private AnalyzerService analyzerService = SpringContext.getBean(AnalyzerService.class); + private AnalysisService analysisService = SpringContext.getBean(AnalysisService.class); + + private String ANALYZER_ID; + private Map testToLoincMap = new HashMap<>(); + private Map loincToTestCodeMap = new HashMap<>(); + private Map> testCodeToTestsMap = new HashMap<>(); + + private AnalyzerReaderUtil readerUtil = new AnalyzerReaderUtil(); + + public PocH100iAnalyzerImplementation() { + + testToLoincMap.put(ANALYZER_TEST_WBC, LOINC_WBC); + testToLoincMap.put(ANALYZER_TEST_RBC, LOINC_RBC); + testToLoincMap.put(ANALYZER_TEST_HGB, LOINC_HGB); + testToLoincMap.put(ANALYZER_TEST_HCT, LOINC_HCT); + testToLoincMap.put(ANALYZER_TEST_MCV, LOINC_MCV); + testToLoincMap.put(ANALYZER_TEST_MCH, LOINC_MCH); + testToLoincMap.put(ANALYZER_TEST_MCHC, LOINC_MCHC); + testToLoincMap.put(ANALYZER_TEST_RDWSD, LOINC_RDWSD); + testToLoincMap.put(ANALYZER_TEST_RDWCV, LOINC_RDWCV); + testToLoincMap.put(ANALYZER_TEST_PLT, LOINC_PLT); + testToLoincMap.put(ANALYZER_TEST_MPV, LOINC_MPV); + testToLoincMap.put(ANALYZER_TEST_NEUT_COUNT, LOINC_NEUT_COUNT); + testToLoincMap.put(ANALYZER_TEST_NEUT_PERCENT, LOINC_NEUT_PERCENT); + testToLoincMap.put(ANALYZER_TEST_LYMPH_COUNT, LOINC_LYMPH_COUNT); + testToLoincMap.put(ANALYZER_TEST_LYMPH_PERCENT, LOINC_LYMPH_PERCENT); + testToLoincMap.put(ANALYZER_TEST_MONO_COUNT, LOINC_MONO_COUNT); + testToLoincMap.put(ANALYZER_TEST_MONO_PERCENT, LOINC_MONO_PERCENT); + testToLoincMap.put(ANALYZER_TEST_EO_COUNT, LOINC_EO_COUNT); + testToLoincMap.put(ANALYZER_TEST_EO_PERCENT, LOINC_EO_PERCENT); + testToLoincMap.put(ANALYZER_TEST_BASO_COUNT, LOINC_BASO_COUNT); + testToLoincMap.put(ANALYZER_TEST_BASO_PERCENT, LOINC_BASO_PERCENT); + testToLoincMap.put(ANALYZER_TEST_IG_COUNT, LOINC_IG_COUNT); + testToLoincMap.put(ANALYZER_TEST_IG_PERCENT, LOINC_IG_PERCENT); + testToLoincMap.put(ANALYZER_TEST_MXD_COUNT, LOINC_MXD_COUNT); + testToLoincMap.put(ANALYZER_TEST_MXD_PERCENT, LOINC_MXD_PERCENT); + + for (Entry entry : testToLoincMap.entrySet()) { + loincToTestCodeMap.put(entry.getValue(), entry.getKey()); + testCodeToTestsMap.put(entry.getKey(), testService.getTestsByLoincCode(entry.getValue())); + } + + Analyzer analyzer = analyzerService.getAnalyzerByName(PocH100iAnalyzer.ANALYZER_NAME); + ANALYZER_ID = analyzer.getId(); + } + + // example message: + // H|\^&|||pocH-100i^01-29^^^^ ^02318732||||||||E1394-97 + // P|1 + // O|1||^^ 12345ABCDE^B|^^^^WBC\^^^^RBC\^^^^HGB\^^^^HCT\ ^^^^MCV\^^^^MCH\^^^^MCHC\^^^^PLT\^^^^W-SCR\^^^^W-MCR\^^^^W-LCR\^^^^W-SCC\^^^^W-MCC\^^^^W-LCC\^^^^RDW-SD\^^^^RDW-CV\^^^^PDW\^^^^MPV\^^^^P-LCR|||||||N||||||||||||||F + // R|1|^^^^WBC^26|78|10*2/uL||N||||123456789012345||20011221163530 + // R|2|^^^^RBC^26|350|10*4/uL||L||||123456789012345||20011221163530 + // R|3|^^^^HGB^26|***.*|g/dL||A||||123456789012345||20011221163530 + // …… + // R|19|^^^^P-LCR^26|50.0|%||H||||123456789012345||20011221163530 + // L|1|N + /* + * (non-Javadoc) + * + * @see + * org.openelisglobal.analyzerimport.analyzerreaders.AnalyzerLineInserter#insert + * (java.util.List, java.lang.String) + */ + @Override + public boolean insert(List lines, String currentUserId) { + + String patientRecord = null; + String orderRecord = null; + + List results = new ArrayList<>(); + for (String line : lines) { + if (line.startsWith(TEST_COMMUNICATION_IDENTIFIER)) { + LogEvent.logInfo(this.getClass().getName(), "insert", "this is a test communication record for PocH-100i"); + } + if (line.startsWith(PATIENT_RECORD_IDENTIFIER)) { + if (patientRecord != null) { + patientRecord = null; + orderRecord = null; + } + patientRecord = line; + } + if (line.startsWith(ORDER_RECORD_IDENTIFIER)) { + orderRecord = line; + } + if (line.startsWith(RESULT_RECORD_IDENTIFIER)) { + LogEvent.logDebug(this.getClass().getName(), "insert", "adding result"); + + addRecordsToResults(patientRecord, orderRecord, line, results, currentUserId); + } + if (line.startsWith(END_RECORD_IDENTIFIER)) { + LogEvent.logDebug(this.getClass().getName(), "insert", "end Aquios of record"); + break; + } + } + return persistImport(currentUserId, results); + } + + @Override + public String getError() { + return PocH100iAnalyzer.ANALYZER_NAME + " analyzer unable to write to database"; + } + + private Test findMatchingTest(Sample sample, String resultTestCode) { + if (sample != null) { + for (Analysis curAnalysis : analysisService.getAnalysesBySampleId(sample.getId())) { + List possibleTests = testCodeToTestsMap.get(resultTestCode); + // if ((possibleTests == null || possibleTests.size() == 0) && resultTestCode.contains("+")) { + // possibleTests = testCodeToTestsMap.get(resultTestCode); + // } + if (possibleTests != null) { + for (Test curTest : possibleTests) { + if (curTest.getLoinc() != null && curTest.getLoinc().equals(curAnalysis.getTest().getLoinc())) { + LogEvent.logDebug(this.getClass().getSimpleName(), "findMatchingTest", "found test in sample for code: " + resultTestCode); + return curAnalysis.getTest(); + } + } + } + } + } + return null; + } + + //example patient record: + // P|1 + // example order record: + // O|1||^^ 12345ABCDE^B|^^^^WBC\^^^^RBC\^^^^HGB\^^^^HCT\ ^^^^MCV\^^^^MCH\^^^^MCHC\^^^^PLT\^^^^W-SCR\^^^^W-MCR\^^^^W-LCR\^^^^W-SCC\^^^^W-MCC\^^^^W-LCC\^^^^RDW-SD\^^^^RDW-CV\^^^^PDW\^^^^MPV\^^^^P-LCR|||||||N||||||||||||||F + // example result records: + // R|1|^^^^WBC^26|78|10*2/uL||N||||123456789012345||20011221163530 + private void addRecordsToResults(String patientRecord, String orderRecord, String resultRecord, + List results, String currentUserId) { + String[] patientRecordFields = patientRecord.split(Pattern.quote(FD)); + String[] orderRecordFields = orderRecord.split(Pattern.quote(FD)); + String[] orderTestIdFields = orderRecordFields[4].split(Pattern.quote(RD)); + String[] orderIdFields = orderRecordFields[3].split(Pattern.quote(CD)); + String[] resultRecordFields = resultRecord.split(Pattern.quote(FD)); + String[] resultTestIdField = resultRecordFields[2].split(Pattern.quote(CD)); + String resultRecordAbnormalFlag = resultRecordFields[6]; + List orderTestIds = new ArrayList<>(); + for (String orderIdField : orderTestIdFields) { + String[] orderIds = orderIdField.split(Pattern.quote(CD)); + String orderTestId = orderIds.length >= 5 ? orderIds[4] : ""; + if (GenericValidator.isBlankOrNull(orderTestId)) { + LogEvent.logWarn(this.getClass().getSimpleName(), "addRecordsToResults", "order analysis parameter name is not present"); + } + orderTestIds.add(orderTestId); + } + if (orderTestIds.size() <= 0) { + LogEvent.logWarn(this.getClass().getSimpleName(), "addRecordsToResults", "order analysis has no tests specified"); + } + String resultTestId = resultTestIdField.length >= 5 ? resultTestIdField[4] : ""; + + String currentAccessionNumber = orderIdFields[2].trim(); + Sample sample = sampleService.getSampleByAccessionNumber(currentAccessionNumber); + Test test = findMatchingTest(sample, resultTestId); + + if (test == null) { + LogEvent.logError(this.getClass().getName(), "addRecordsToResults", + "can't import a result if order does not have that test ordered"); + LogEvent.logDebug(this.getClass().getSimpleName(), "", "resultTestId: " + resultTestId); + return; + } + + switch (resultRecordAbnormalFlag) { + case "A": //masked data + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "masked data result"); + break; + case ">": //out of assured linearity + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "out of assured linearity result"); + break; + case "W": //low reliability + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "low reliability result"); + break; + case "H": // higher than reference interval + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "higher than interval result"); + break; + case "L": //lower than reference interval + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "lower than interval result"); + break; + case "N": //normal + LogEvent.logDebug(this.getClass().getSimpleName(), "addRecordsToResults", "normal result"); + break; + default: + LogEvent.logWarn(this.getClass().getSimpleName(), "addRecordsToResults", "abnormal flag not understood"); + } + + AnalyzerResults analyzerResults = addResult(results, null, "N", resultRecordFields[3], + DateUtil.convertStringDateToTimestampWithPattern(resultRecordFields[12], "yyyyMMddHHmmss"), + currentAccessionNumber, false, resultRecordFields[4], test); + LogEvent.logDebug(this.getClass().getName(), "addResultLine", "***" + analyzerResults.getAccessionNumber() + " " + + analyzerResults.getCompleteDate() + " " + analyzerResults.getResult()); + } + + public AnalyzerResults addResult(List resultList, List notMatchedResults, String resultType, + String resultValue, Date date, String accessionNumber, boolean isControl, String resultUnits, + Test test) { + LogEvent.logDebug(this.getClass().getName(), "addResult", + "adding result for lab Number: " + accessionNumber); + AnalyzerResults analyzerResults = createAnalyzerResult(resultType, resultValue, resultUnits, date, + accessionNumber, isControl, test); + if (analyzerResults.getTestId() != null) { + addValueToResults(resultList, analyzerResults); + } else { + LogEvent.logWarn(this.getClass().getName(), "addResult", + "no matching result for " + accessionNumber); + notMatchedResults.add(analyzerResults); + } + return analyzerResults; + } + + private void addValueToResults(List resultList, AnalyzerResults result) { + resultList.add(result); + LogEvent.logDebug(this.getClass().getName(), "addValueToResults", + "searching for matching analysis for " + result.getAccessionNumber()); + AnalyzerResults resultFromDB = readerUtil.createAnalyzerResultFromDB(result); + if (resultFromDB != null) { + LogEvent.logWarn(this.getClass().getName(), "addValueToResults", + "no resultFromDB for " + result.getAccessionNumber()); + resultList.add(resultFromDB); + } + + } + + private AnalyzerResults createAnalyzerResult(String resultType, String resultValue, String resultUnits, Date date, + String accessionNumber, boolean isControl, Test test) { + LogEvent.logDebug(this.getClass().getName(), "createAnalyzerResult", + "creating analyzer result for " + accessionNumber); + + AnalyzerResults analyzerResults = new AnalyzerResults(); + + analyzerResults.setAnalyzerId(ANALYZER_ID); + analyzerResults.setResult(resultValue); + analyzerResults.setUnits(resultUnits); + if (date != null) { + analyzerResults.setCompleteDate(new Timestamp(date.getTime())); + } + analyzerResults.setAccessionNumber(accessionNumber); + analyzerResults.setTestId(test.getId()); + analyzerResults.setIsControl(isControl); + analyzerResults.setTestName(test.getLocalizedTestName().getLocalizedValue()); + return analyzerResults; + } + + public void persistImport(List resultList) { + this.persistImport("1", resultList); + }public String buildResponse(List lines) { + + String queryRecord = null; + + for (String line : lines) { + if (line.startsWith(TEST_COMMUNICATION_IDENTIFIER)) { + LogEvent.logInfo(this.getClass().getName(), "buildResponse", "this is a test communication record for " + PocH100iAnalyzer.ANALYZER_NAME); + } + if (line.startsWith(QUERY_RECORD_IDENTIFIER)) { + LogEvent.logDebug(this.getClass().getName(), "buildResponse", "request contains query record"); + String response = generateQueryResponse(line); + return response; + } + } + LogEvent.logWarn(this.getClass().getName(), "buildResponse", "no response could be created, no query identifier supplied"); + return ""; + } + + private String generateQueryResponse(String queryRecord) { + LogEvent.logDebug(this.getClass().getSimpleName(), "generateQueryResponse", "generating query response"); + String[] queryRecordFields = queryRecord.split(Pattern.quote(FD)); + + String[] startingRangeIdNumber = queryRecordFields[2].split(Pattern.quote(CD)); + String sampleIdNo = startingRangeIdNumber[2].trim(); + String sampleNoAttribute = startingRangeIdNumber[3]; + + StringBuilder msgBuilder = new StringBuilder(); + LogEvent.logDebug(this.getClass().getSimpleName(), "generateQueryResponse", "searching for sample with lab Number: " + sampleIdNo); + + Sample sample = sampleService.getSampleByAccessionNumber(sampleIdNo.trim()); + if (sample == null) { + LogEvent.logWarn(this.getClass().getSimpleName(), "generateQueryResponse", "no sample found with lab Number: " + sampleIdNo); + //return could not find sample messager + return msgBuilder.append("H|\\^&|\r\n") + .append("P|1|\r\n") + .append("O|1||||||||||||||||||||||||Y\r\n") //"Y" is no order exists marker + .append("L|1|N\r\n").toString(); + } + Patient patient = sampleHumanService.getPatientForSample(sample); + Person person = patient.getPerson(); + msgBuilder.append("H|\\^&|||||||||||E1394-97\r\n"); + msgBuilder.append("P|1|||") + .append(patient.getNationalId()) //patient identifier + .append("|^").append(person.getFirstName()).append("^").append(person.getLastName()) //names + .append("||").append(patient.getBirthDate() == null ? "": dateFormat.format(patient.getBirthDate())) //birthdate + .append("|").append(patient.getGender()) //gender M F U + .append("|||||") + .append("")//DR id + .append("||||||||||||\r\n"); + + msgBuilder.append("O|1|").append(queryRecordFields[2]).append("||"); + + boolean first = true; + for (Analysis curAnalysis : analysisService.getAnalysesBySampleId(sample.getId())) { + Optional testCode = Optional.ofNullable(loincToTestCodeMap.get(curAnalysis.getTest().getLoinc())); + if (testCode.isPresent()) { + LogEvent.logDebug(this.getClass().getSimpleName(), "generateQueryResponse", "found supported test in sample with test code: " + testCode.get()); + if (!first) { + msgBuilder.append("\\"); + } + first = false; + msgBuilder.append("^^^^").append(testCode.get()); + } + } + msgBuilder.append("||") + .append(sample.getEnteredDate() == null ? "" : dateTimeFormat.format(sample.getEnteredDate())) + .append("|||||N||||||||||||||Q\r\n"); + msgBuilder.append("L|1|N\r\n"); + return msgBuilder.toString(); + } + +} diff --git a/analyzers/pocH-100i/src/main/java/oe/plugin/analyzer/PocH100iAnalyzerMenu.java b/analyzers/pocH-100i/src/main/java/oe/plugin/analyzer/PocH100iAnalyzerMenu.java new file mode 100644 index 0000000..1b3aa33 --- /dev/null +++ b/analyzers/pocH-100i/src/main/java/oe/plugin/analyzer/PocH100iAnalyzerMenu.java @@ -0,0 +1,57 @@ +/* + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations under + * the License. + * + * The Original Code is OpenELIS code. + * + * Copyright (C) ITECH, University of Washington, Seattle WA. All Rights Reserved. + */ + +package oe.plugin.analyzer; + +import java.util.Locale; + +import org.openelisglobal.common.services.PluginMenuService; +import org.openelisglobal.common.services.PluginMenuService.KnownMenu; +import org.openelisglobal.menu.valueholder.Menu; +import org.openelisglobal.plugin.MenuPlugin; + +public class PocH100iAnalyzerMenu extends MenuPlugin { + + @Override + protected void insertMenu() { + PluginMenuService service = PluginMenuService.getInstance(); + Menu menu = new Menu(); + + menu.setParent(PluginMenuService.getInstance().getKnownMenu(KnownMenu.ANALYZER, "menu_results")); + // The order this analyzer will show on the menu relative to other analyzers + menu.setPresentationOrder(10); + // The id needs to be unique in the system + menu.setElementId(PocH100iAnalyzer.ANALYZER_NAME + "_plugin"); + // This will always be "/AnalyzerResults?type=Analyzer->" + PocH100iAnalyzer.ANALYZER_NAME); + Role role = service.getSystemRole( "Results" ); + return service.bindRoleToModule( role, module ); + } +} diff --git a/analyzers/pocH-100i/src/main/resources/PocH-100iAnalyzer.xml b/analyzers/pocH-100i/src/main/resources/PocH-100iAnalyzer.xml new file mode 100644 index 0000000..e41e7d5 --- /dev/null +++ b/analyzers/pocH-100i/src/main/resources/PocH-100iAnalyzer.xml @@ -0,0 +1,22 @@ + + + 1.0 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/openelisglobal-2.5.0.0.jar b/lib/openelisglobal-2.7.3.8.jar similarity index 95% rename from lib/openelisglobal-2.5.0.0.jar rename to lib/openelisglobal-2.7.3.8.jar index f90db77..0d1ec9c 100644 Binary files a/lib/openelisglobal-2.5.0.0.jar and b/lib/openelisglobal-2.7.3.8.jar differ diff --git a/pom.xml b/pom.xml index d942918..5c4dd93 100644 --- a/pom.xml +++ b/pom.xml @@ -25,19 +25,23 @@ ./analyzers/GeneXpertFile ./analyzers/SysmexXN-L ./analyzers/SysmexXP + ./analyzers/pocH-100i - + 5.3.14 + 11 + 11 + 11 org.openelisglobal openelisglobal - 2.5.0.0 + 2.7.3.8 system - /home/runner/work/openelisglobal-plugins/openelisglobal-plugins/lib/openelisglobal-2.5.0.0.jar + /home/runner/work/openelisglobal-plugins/openelisglobal-plugins/lib/openelisglobal-2.7.3.8.jar commons-validator