Skip to content

CRI Work Flow

1.Getting Active Facilities List:

CRI Application is designed to process reports from multiple facilities at a time, for this taking active facilities list from database and the facility properties from .yml file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
    public List<FacilityModal> getActiveFacilities() {
        List<FacilityModal> activeFacilities = new ArrayList<FacilityModal>();
        String query = "select distinct HospitalId,HospitalAliasName,Active,HospitalId from  MST_Hospital where Active=1 and HospitalId in(1,130)";
        try {
            List<Map<String, Object>> list = masterTemplate.queryForList(query);
            for (Map row : list) {
                log.info("HOSPITAL NAME ----->{}", row.get("HospitalAliasName"));
                FacilityModal facility = new FacilityModal();               
                facility.setFacilityId((Integer) row.get("HospitalId"));
                facility.setFacilityName((String) row.get("HospitalAliasName"));
                facility.setStatus((Integer) row.get("Active"));
                facility.setSiteId((Integer) row.get("HospitalId"));
                activeFacilities.add(facility);
            }
            return activeFacilities;
        } catch (Exception e) {
            log.error("Exception During getting Active Hospitals :::---",e);
            return null;
        }

    }

Properties from application.yml file are :

1
2
3
4
5
6
7
   hrcm: 
     datasource:
      url: jdbc:sqlserver://192.168.50.68:1433;databaseName=hrcm
      username: sa
      password: tensorflow
      driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
      name: HRCM

It repeats for all Active Facilities

1
2
3
4
5
   public void getReport() {
        List<FacilityModal> activeList = activeFacility.getActiveFacilities();              
        for (FacilityModal fm : activeList) {
        YAMLConfig facilityProperty =   dservice.getHospitalProperties(fm.getFacilityName());
            Map<String, String> map = facilityProperty.getHospitalProperties();

2.Getting Report Paths to process reports:

Read "reportPath" property from the HospitalProperties map, and consider it as a FacilityPath where all reports are available.

Get files from this path and add to DirectoryStream to process.

1
2
3
4
    if (!map.isEmpty()) {
                String facilityPath = map.get("reportPath");                
                try (DirectoryStream<Path> directoryStream = Files
                        .newDirectoryStream(FileSystems.getDefault().getPath(facilityPath)))

The paths set to the properties in properties.java file like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
     @Override
    public Map<String, String> getHospitalProperties() {
        Map<String, String> mapProp = new HashMap<>();
        mapProp.put("reportPath", this.reportpath);
        mapProp.put("modifiedpath", this.modifiedpath);
        mapProp.put("newReportpath", this.newfilespath);
        mapProp.put("TextO", this.reportpath);
        mapProp.put("LabRe", this.labspath);
        mapProp.put("Vital", this.vitalspath);
        mapProp.put("Medic", this.medicationpath);
        mapProp.put("Nursi", this.nursingsummarypath);
        mapProp.put("ReImb", this.reimbpath);
        mapProp.put("Admit", this.admitorderspath);
        mapProp.put("Physi", this.pdocspath);

path properties from application.yml file are :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
       reportpath: /home/kpmd/Reports/HRCM/
      modifiedpath: /home/kpmd/Reports/HRCM/Modified
      labspath: /home/kpmd/Reports/HRCM/labs/
      medicationpath: /home/kpmd/Reports/HRCM/medications/
      vitalspath: /home/kpmd/Reports/HRCM/vitals/
      admitorderspath: /home/kpmd/Reports/HRCM/admitorders/
      nursingsummarypath: /home/kpmd/Reports/HRCM/nursingsummary/
      reimbpath: /home/kpmd/Reports/HRCM/reimbursement/
      pdocspath: /home/kpmd/Reports/HRCM/pdocs/
      newfilespath: /home/kpmd/Reports/HRCM/newfiles/

3.Process, Insert and Move Reports:

after reading paths from properties file, Process the files ends with ".txt" only. remaining files are saved in "newfiles" folder. for this using two methods known as processReport() and processNewReport().

1
2
3
4
5
6
7
8
    for (Path path : directoryStream) {
                        if (path.getFileName().toString().endsWith(".txt")) {
                            processReport(path, map, fm);
                        } else {
                            processNewReport(path, map, fm);

                        }
                    }
Process new files

Here new files known as files which are not ends with .txt and the file paths not configured in properties file. those are directly moves into the newfiles folder.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
    private void processNewReport(Path path, Map<String, String> map, FacilityModal fm) {
        String newFilePath = "newReportpath";
        if (!Files.isDirectory(path)) {
            Report report = facilityReport.getFacilityReport(newFilePath);
            Path reportPath = report.processReport(path, map.get(newFilePath), fm.getSiteId());
            try {               
                String bodyMessage="NewFile " + path.getFileName().toString() + " is there in Reports Folder";
                String subjectMessage="New File Alert";             
                //int response = restTemplate.getForObject(mailProperties.getMailServiceUrl()+"/"+mailProperties.getFromAddress()+"/"+mailProperties.getToAddress()+"/"+bodyMessage+"/"+subjectMessage, Integer.class);//
                log.info("New File Alert --->{}",path.getFileName());
            } catch (Exception e) {
                log.error("Exception During NewReport process :::---",e);
            }
            report.moveReport(reportPath, map.get(newFilePath), 3);

        }

    }

Note

Previously there is mail alert system for new files, now new file alerts are saving in log file and log table.

Report Interface

we have two abstract methods for Process and Insert reports, and one default method for Move reports.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    public Path processReport(Path reportPath, String modifiedPath, int siteId);
    public int insertReport(Path reportPath, int siteId);
    default void moveReport(Path sourcePath, String targetPath, int reportInsertionStatus) {
        try {
            boolean pathExists = Files.exists(Paths.get(targetPath), new LinkOption[] { LinkOption.NOFOLLOW_LINKS });

            if (!pathExists) {

                Files.createDirectories(Paths.get(targetPath));
            }          
//           Files.copy(sourcePath, Paths.get(targetPath + "/" + sourcePath.getFileName()),
//                  StandardCopyOption.REPLACE_EXISTING);

            Files.move(sourcePath, Paths.get(targetPath + "/" + sourcePath.getFileName()),
                    StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
             try {
                Files.delete(sourcePath);
            } catch (IOException e1) {              
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }
Processing Custom Reports and Physician Documents:-

All custom reports known as Labs, Medical, Vitals, Nurse notes and Reimbursement files. and Physician Documents are processing from a single method public void processReport(Path path, Map map, FacilityModal fm) in ClinicalReportInsertion.java class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    public void processReport(Path path, Map<String, String> map, FacilityModal fm) {
        String filePrefixName = path.getFileName().toString().substring(0, 5);
        if (!map.containsKey(filePrefixName)) {
            filePrefixName = "newReportpath";
        }
        Report report = facilityReport.getFacilityReport(filePrefixName);
        if (!path.getFileName().toString().startsWith("PhysicianNotes_20")) {
         try {
            Path reportPath = report.processReport(path, map.get(filePrefixName), fm.getSiteId());
            int reportInsertionStatus = report.insertReport(reportPath, fm.getSiteId());            
            String statusInfo = (reportInsertionStatus == 0) ? "Success"
                    : (reportInsertionStatus == 2) ? "" : "Failure";
            if (statusInfo == "Failure") {
                String bodyMessage=reportPath.getFileName().toString() + " is failed to process";
                String subjectMessage="Report Insertion is failed";
                //int response = restTemplate.getForObject(mailProperties.getMailServiceUrl()+"/"+mailProperties.getFromAddress()+"/"+mailProperties.getToAddress()+"/"+bodyMessage+"/"+subjectMessage, Integer.class);
                log.error("Report Insertion is Failed ---> {}",reportPath.getFileName());
            }
            report.moveReport(reportPath, map.get(filePrefixName) + "" + statusInfo, reportInsertionStatus);
         }catch(Exception e) {
             log.error("Exception During MovingReport :::---",e);
         }  
        } else {
            String destinationFile = map.get(filePrefixName) + "/OldPhysicianNotes/";

            report.moveReport(path, destinationFile, 0);
        }

    }
Custom reports work flow:
1
2
3
4
    @Override
    public Path processReport(Path reportPath, String modifiedPath, int siteId) {
        return reportPath;
    }

All custom report classes override the processReport() method to return reportPath.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    @Override
    public int insertReport(Path reportPath, int siteId) {
        log.info("Report Path --->{}",reportPath);
        String reportDate = getReportDate(reportPath);
        int insertStatus = 0;
        JdbcTemplate jdbcTemplate = dataSourceService.getJdbcTemplate(siteId);
        try {
            SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate)
                    .withProcedureName(queryProperties.getLabQuery());
            Map<String, Object> inParamMap = new HashMap<String, Object>();
            inParamMap.put("PathFileName", reportPath.toAbsolutePath().toString());
            inParamMap.put("ReportDate", reportDate);
            inParamMap.put("ErrorNumber", Types.INTEGER);
            SqlParameterSource source = new MapSqlParameterSource(inParamMap);
            Map<String, Object> simpleJdbcCallResult = simpleJdbcCall.execute(source);              
            //insertStatus = (int) simpleJdbcCallResult.get("#update-count-1");
            insertStatus = (int) simpleJdbcCallResult.get("ErrorNumber");   
            log.info("{} Report Insertion Status --->{}",reportPath,insertStatus);
        } catch (Exception e) {
            log.error("Exception During InsertReport :::---",e);
            insertStatus = 1;
        }
        return insertStatus;
    }

All custom report classes override the insertReport() method to return insertStatus of file data inserted in database or not.

StroedProcedures and Tables for Custom Reports :--

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
  Lab Reports --> SP -- CRI_InsertLab
                 Table- FTP_Lab

  Med Reports  --> SP -- CRI_InsertIPMedication
                  Table- FTP_IPMedication

 Vital Reports --> SP -- CRI_InsertVitalsign
                  Table- FTP_VitalSign

Nursing Reports --> SP -- CRI_InsertNurseNote
                   Table- FTP_NurseNote

Reimbursement Reports -->  SP --  CRI_InsertDRG
                         Tables- FTP_DRG, FTP_DXCode, FTP_PCSCode

once get the report path and report insertionStatus it moves the file in to Backup folders by using moveReport() method in Report Interface.

if report inserted successfully the file moves to the "Success" folder else moves to the "Failure" folder.

for Nurse reports first moved to modified folder and then to Success or Failure folders.

Reimbursement reports data saving in three table for DRG related Info, ICD Codes Info, Procedure Codes Info.

Physician Documents work flow:

Physician Documents Processing is differs from the Custom reports process.

  1. if the file starts with "PhysicianNotes_20", it is the old file format so it directly moves to the "OldPhysicianNotes" folder.
1
2
3
4
    if (reportPath.getFileName().toString().startsWith("PhysicianNotes_20")) {
                destinationFile = modifiedPath + "/OldPhysicianNotes/" + reportPath.getFileName();
                move(reportPath, Paths.get(destinationFile), "success", 1);
            }
  1. Split the Processing Document by using regular expressions to avoid characters out of the HexaDecimal range.

and append new lines, and split as string array to read XMl tags data.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    private String[] splitDocument(Path reportPath) {

        List<String> filteredLines = null;
        try {
            filteredLines = Files.lines(reportPath, StandardCharsets.ISO_8859_1).map(s -> s.trim())
                    .map(s -> s.replaceAll("[^\\x20-\\x7e\\x0A]", "")).collect(Collectors.toList());
        } catch (IOException e) {
            log.error("Exception During Split Document :::---",e);
        }

        StringBuilder sb = new StringBuilder();
        filteredLines.forEach(s -> sb.append(s).append("\n"));

        String fileAsString = sb.toString();
        String[] docArray = fileAsString.split("(?=<Value>(.*?)</Value><FieldName>phys_doc_sta_ds)");
        return docArray;
    }

3.Get the FieldName and Value tags Data in MAP format.

Physician Documents are in XML format so the required data is extracted by using FieldName and Value Tags.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    private Map<String, String> getFieldMap(String fileAsString1) {
        Map<String, String> fieldMap = new LinkedHashMap<String, String>();
        String key = "", value = "";
        i = i + 1;
        while (fileAsString1.contains("<Value")) {
            int valtagstart = fileAsString1.indexOf("<Value");
            int valtagend = fileAsString1.indexOf(">", valtagstart);
            int valspos = valtagend + 1;
            String valtag = fileAsString1.substring(valtagstart, valtagend);
            int valepos = fileAsString1.indexOf("</Value>");
            if (valtag.contains("/")) {
                value = "";
            } else {
                valepos = fileAsString1.indexOf("</Value>");
                value = fileAsString1.substring(valspos, valepos);
            }
            int kespos = fileAsString1.indexOf("<FieldName>") + 11;
            int keepos = fileAsString1.indexOf("</FieldName>", valspos + 7);
            key = fileAsString1.substring(kespos, keepos);
            fileAsString1 = fileAsString1.substring(keepos + 11, fileAsString1.length());
            if (value != null)
                fieldMap.put(key, value);

        }
        return fieldMap;
    }
  1. while reading map the document which is not have "MedicalRecordNumber", consider as a Invalid one and moves to the Failure folder.

and the valid ones are going to further process.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    String[] docArray = splitDocument(reportPath);
                for (String fileAsString1 : docArray) {
                    fieldMap = getFieldMap(fileAsString1);
                    if (fieldMap.get("Medical_Record_Number") != null) {
                        String finaldoc = getFinalDocument(fieldMap);
                        moveToModified(finaldoc, reportPath, modifiedPath, siteId);
                    } else if (docArray.length == i) {
                        destinationFile = modifiedPath + "Modified/Failure/" + reportPath.getFileName();
                        move(reportPath, Paths.get(destinationFile), "failure", 1);
                    }
                }
  1. get final document to insert into database by formatting document.

--> get the required values from the map

1
2
3
4
5
6
7
8
           MRN = fieldMap.get("Medical_Record_Number");
            AccountNo = fieldMap.get("Visit_ID");
            docdictCreated = fieldMap.get("cre_dt");
            attendingMD = fieldMap.get("Attending_Physician");
            attendingMD2 = fieldMap.get("ety_usr_nm");
            documentName = fieldMap.get("doc_nm");
            realName = fieldMap.get("doc_nm");
            documentType = fieldMap.get("phys_doc_sta_ds");

--> replace the empty spaces and some HTML tags.

--> parse the date formats and append the line separators.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
    Date d1 = (new SimpleDateFormat("MMddyyyyHHmm")).parse(docdate);
            String s2 = (new SimpleDateFormat("yyyyMMddHHmm")).format(d1);
            docdate = s2;
            // attendingMD = attendingMD != null ? attendingMD : "attendingMD";
            attendingMD = attendingMD2 != null ? attendingMD2 : attendingMD;
            documentName = documentName != null ? documentName.replace(" ", "").replace("_", "") : "documentName";
            docContent = new StringBuilder();
            Iterator<String> itr = null;
            itr = fieldMap.keySet().iterator();
            String fieldname = null;
            while (itr.hasNext()) {
                fieldname = itr.next().toString();
                docContent.append(fieldname).append(": ").append(fieldMap.get(fieldname))
                        .append(System.getProperty("line.separator"));
            }
            finalDocument = docContent.toString().replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&")
                    .replace("<;", "<");
            if (finalDocument.contains("@@<root")) {
                String doc1 = finalDocument.substring(finalDocument.indexOf("@@<root"),
                        finalDocument.indexOf("</root>") + 7);
                finalDocument = finalDocument.replace(doc1, "\n");
            }
  1. Insert the formatted Document into database

--> first move to modified folder and check for "documentType", if it is Invalidate then move to "WrongPdocs" folder.

1
2
3
4
5
6
    if ("INVALIDATED (NOTE REPLACED)".equals(documentType) || "INVALIDATED (WRONG PATIENT)".equals(documentType)
                || "INVALIDATED (INFORMATION ENTERED IN ERROR)".equals(documentType)
                || "INVALIDATED (OPTED NOT TO DOCUMENT AT THIS TIME)".equals(documentType)) {
            destinationFile = modifiedPath + "/WrongPdocs/" + AccountNo + "_" + documentName + "_" + docdate + ".txt";
            move(reportPath, Paths.get(destinationFile), finaldoc, 0);
        }

--> if it is ok with documentType then create the filename with AccountNumber,DcoumentName and Date.

--> and Insert Document details into Log table.

1
2
3
destinationFile = modifiedPath + "/Modified/" + AccountNo + "_" + documentName + "_" + docdate + ".txt";
        modifiedFile = destinationFile;
move(reportPath, Paths.get(destinationFile), finaldoc, 0);

Log Table --> LOG_FTP_PatientDocuments

--> the original file moves into the "PdocsBackup" folder.

--> Finally the modified file inserted into database with document details.

1
2
  StoredProcedure ---> FTP_InsertPatientDocuments
  Table           ---> ReportsDPTEpic (in HPMCHL7 Database)