Skip to content

Commit

Permalink
Merge branch 'release/1.0.11'
Browse files Browse the repository at this point in the history
  • Loading branch information
Plow74 committed Jan 30, 2017
2 parents b2b1203 + e9c444c commit db24601
Show file tree
Hide file tree
Showing 15 changed files with 5,238 additions and 587 deletions.
873 changes: 440 additions & 433 deletions pom.xml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,28 +1,45 @@
package org.sitenv.referenceccda.services;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.BOMInputStream;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.log4j.Logger;
import org.sitenv.referenceccda.dto.ValidationResultsDto;
import org.sitenv.referenceccda.dto.ValidationResultsMetaData;
import org.sitenv.referenceccda.validators.RefCCDAValidationResult;
import org.sitenv.referenceccda.validators.content.ReferenceContentValidator;
import org.sitenv.referenceccda.validators.schema.CCDATypes;
import org.sitenv.referenceccda.validators.schema.ReferenceCCDAValidator;
import org.sitenv.referenceccda.validators.schema.ValidationObjectives;
import org.sitenv.referenceccda.validators.vocabulary.VocabularyCCDAValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.xml.sax.SAXException;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

@Service
public class ReferenceCCDAValidationService {
private static Logger logger = Logger.getLogger(ReferenceCCDAValidationService.class);

private ReferenceCCDAValidator referenceCCDAValidator;
private VocabularyCCDAValidator vocabularyCCDAValidator;
private ReferenceContentValidator goldMatchingValidator;

private static final String ERROR_GENERAL_PREFIX = "The service has encountered ";
private static final String ERROR_PARSING_PREFIX = ERROR_GENERAL_PREFIX + "an error parsing the document. ";
private static final String ERROR_FOLLOWING_ERROR_POSTFIX = "the following error: ";
private static final String ERROR_IO_EXCEPTION = ERROR_GENERAL_PREFIX + "the following input/output error: ";
private static final String ERROR_CLASS_CAST_EXCEPTION = ERROR_PARSING_PREFIX
+ "Please verify the document is valid against schema and "
+ "contains a v3 namespace definition: ";
private static final String ERROR_SAX_PARSE_EXCEPTION = ERROR_PARSING_PREFIX
+ "Please verify the document does not contain in-line XSL styling and/or address " + ERROR_FOLLOWING_ERROR_POSTFIX;
private static final String ERROR_GENERIC_EXCEPTION = ERROR_GENERAL_PREFIX + ERROR_FOLLOWING_ERROR_POSTFIX;

@Autowired
public ReferenceCCDAValidationService(ReferenceCCDAValidator referenceCCDAValidator, VocabularyCCDAValidator vocabularyCCDAValidator,
Expand All @@ -41,37 +58,79 @@ public ValidationResultsDto validateCCDA(String validationObjective, String refe
resultsMetaData = buildValidationMedata(validatorResults, validationObjective);
resultsMetaData.setCcdaFileName(ccdaFile.getName());
resultsMetaData.setCcdaFileContents(new String(ccdaFile.getBytes()));
} catch (SAXException | IOException e) {
resultsMetaData.setServiceError(true);
resultsMetaData.setServiceErrorMessage(e.getMessage());
resultsMetaData.setCcdaDocumentType(validationObjective);
}
} catch (IOException ioE) {
processValidateCCDAException(resultsMetaData,
ERROR_IO_EXCEPTION, validationObjective, ioE);
} catch (SAXException saxE) {
processValidateCCDAException(resultsMetaData,
ERROR_SAX_PARSE_EXCEPTION, validationObjective, saxE);
} catch (ClassCastException ccE) {
processValidateCCDAException(resultsMetaData,
ERROR_CLASS_CAST_EXCEPTION, validationObjective, ccE);
} catch (Exception catchAllE) {
processValidateCCDAException(resultsMetaData,
ERROR_GENERIC_EXCEPTION, validationObjective, catchAllE);
}
resultsDto.setResultsMetaData(resultsMetaData);
resultsDto.setCcdaValidationResults(validatorResults);
return resultsDto;
}

private static void processValidateCCDAException(ValidationResultsMetaData resultsMetaData,
String serviceErrorStart, String validationObjective, Exception exception) {
resultsMetaData.setServiceError(true);
if(exception.getMessage() != null) {
String fullError = serviceErrorStart + exception.getMessage();
logger.error(fullError);
resultsMetaData.setServiceErrorMessage(fullError);
} else {
String fullError = serviceErrorStart + ExceptionUtils.getStackTrace(exception);
logger.error(fullError);
resultsMetaData.setServiceErrorMessage(fullError);
}
resultsMetaData.setCcdaDocumentType(validationObjective);
}

private List<RefCCDAValidationResult> runValidators(String validationObjective, String referenceFileName,
MultipartFile ccdaFile) throws SAXException {
MultipartFile ccdaFile) throws SAXException, Exception {
List<RefCCDAValidationResult> validatorResults = new ArrayList<>();
InputStream ccdaFileInputStream = null;
try {
ccdaFileInputStream = ccdaFile.getInputStream();
String ccdaFileContents = IOUtils.toString(new BOMInputStream(ccdaFileInputStream));

List<RefCCDAValidationResult> mdhtResults = doMDHTValidation(validationObjective, referenceFileName, ccdaFileContents);
if(mdhtResults != null && !mdhtResults.isEmpty())
if(mdhtResults != null && !mdhtResults.isEmpty()) {
logger.info("Adding MDHT results");
validatorResults.addAll(mdhtResults);
}

if (!mdhtResultsHaveSchemaError(mdhtResults)) {
List<RefCCDAValidationResult> vocabResults = DoVocabularyValidation(validationObjective, referenceFileName, ccdaFileContents);
if(vocabResults != null && !vocabResults.isEmpty())
validatorResults.addAll(vocabResults);

List<RefCCDAValidationResult> contentResults = doContentValidation(validationObjective, referenceFileName, ccdaFileContents);
if(contentResults != null && !contentResults.isEmpty()) {
validatorResults.addAll(contentResults);
boolean isSchemaErrorInMdhtResults = mdhtResultsHaveSchemaError(mdhtResults);
boolean isObjectiveAllowingVocabularyValidation = objectiveAllowsVocabularyValidation(validationObjective);
if (!isSchemaErrorInMdhtResults && isObjectiveAllowingVocabularyValidation) {
List<RefCCDAValidationResult> vocabResults = doVocabularyValidation(validationObjective, referenceFileName, ccdaFileContents);
if(vocabResults != null && !vocabResults.isEmpty()) {
logger.info("Adding Vocabulary results");
validatorResults.addAll(vocabResults);
}

if(objectiveAllowsContentValidation(validationObjective)) {
List<RefCCDAValidationResult> contentResults = doContentValidation(validationObjective, referenceFileName, ccdaFileContents);
if(contentResults != null && !contentResults.isEmpty()) {
logger.info("Adding Content results");
validatorResults.addAll(contentResults);
}
} else {
logger.info("Skipping Content validation due to: "
+ "validationObjective (" + (validationObjective != null ? validationObjective : "null objective")
+ ") is not relevant or valid for Content validation");
}
} else {
String separator = !isObjectiveAllowingVocabularyValidation && isSchemaErrorInMdhtResults ? " and " : "";
logger.info("Skipping Vocabulary (and thus Content) validation due to: "
+ (isObjectiveAllowingVocabularyValidation ? "" : "validationObjective POSTed: "
+ (validationObjective != null ? validationObjective : "null objective") + separator)
+ (isSchemaErrorInMdhtResults ? "C-CDA Schema error(s) found" : ""));
}
} catch (IOException e) {
throw new RuntimeException("Error getting CCDA contents from provided file", e);
Expand All @@ -80,25 +139,39 @@ private List<RefCCDAValidationResult> runValidators(String validationObjective,
}
return validatorResults;
}

private boolean mdhtResultsHaveSchemaError(List<RefCCDAValidationResult> mdhtResults) {
for(RefCCDAValidationResult result : mdhtResults){
if(result.isSchemaError()){
return true;
}
}
return false;
}

private ArrayList<RefCCDAValidationResult> DoVocabularyValidation(String validationObjective, String referenceFileName, String ccdaFileContents) throws SAXException {
return vocabularyCCDAValidator.validateFile(validationObjective, referenceFileName, ccdaFileContents);
}

private boolean objectiveAllowsVocabularyValidation(String validationObjective) {
return !validationObjective.equalsIgnoreCase(ValidationObjectives.Sender.C_CDA_IG_ONLY)
&& !referenceCCDAValidator.isValidationObjectiveMu2Type()
&& !validationObjective.equalsIgnoreCase(CCDATypes.NON_SPECIFIC_CCDA);
}

private boolean objectiveAllowsContentValidation(String validationObjective) {
return ReferenceCCDAValidator.isValidationObjectiveACertainType(validationObjective,
ValidationObjectives.ALL_UNIQUE_CONTENT_ONLY);
}

private List<RefCCDAValidationResult> doMDHTValidation(String validationObjective, String referenceFileName, String ccdaFileContents) throws SAXException {
private List<RefCCDAValidationResult> doMDHTValidation(String validationObjective, String referenceFileName, String ccdaFileContents) throws SAXException, Exception {
logger.info("Attempting MDHT validation...");
return referenceCCDAValidator.validateFile(validationObjective, referenceFileName, ccdaFileContents);
}

private ArrayList<RefCCDAValidationResult> doVocabularyValidation(String validationObjective, String referenceFileName, String ccdaFileContents) throws SAXException {
logger.info("Attempting Vocabulary validation...");
return vocabularyCCDAValidator.validateFile(validationObjective, referenceFileName, ccdaFileContents);
}

private List<RefCCDAValidationResult> doContentValidation(String validationObjective, String referenceFileName, String ccdaFileContents) throws SAXException {
logger.info("Attempting Content validation...");
return goldMatchingValidator.validateFile(validationObjective, referenceFileName, ccdaFileContents);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
* Created by Brian on 10/20/2015.
*/
public interface CCDAValidator {
ArrayList<RefCCDAValidationResult> validateFile(String validationObjective, String referenceFileName, String ccdaFile) throws SAXException;
ArrayList<RefCCDAValidationResult> validateFile(String validationObjective,
String referenceFileName, String ccdaFile) throws SAXException, Exception;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.sitenv.referenceccda.validators;

import org.sitenv.referenceccda.validators.enums.ValidationResultType;
import org.sitenv.referenceccda.validators.schema.MDHTResultDetails;

public class RefCCDAValidationResult {

Expand All @@ -10,7 +11,9 @@ public class RefCCDAValidationResult {
private final String xPath;
private final String validatorConfiguredXpath;
private final String documentLineNumber;
private final boolean isSchemaError, isDataTypeSchemaError;

// Only required for MDHT
private final MDHTResultDetails mdhtResultDetails;

// Only valid for Vocabulary testing
private String actualCode;
Expand All @@ -28,8 +31,11 @@ private RefCCDAValidationResult(RefCCDAValidationResultBuilder builder){
this.actualCodeSystem = builder.actualCodeSystem;
this.actualCodeSystemName = builder.actualCodeSystemName;
this.actualDisplayName = builder.actualDisplayName;
this.isSchemaError = builder.isSchemaError;
this.isDataTypeSchemaError = builder.isDataTypeSchemaError;
if(builder.mdhtResultDetails != null) {
this.mdhtResultDetails = builder.mdhtResultDetails;
} else {
this.mdhtResultDetails = new MDHTResultDetails(false, false, false, false);
}
}

public String getDescription() {
Expand All @@ -53,12 +59,20 @@ public String getDocumentLineNumber() {
}

public boolean isSchemaError() {
return isSchemaError;
return mdhtResultDetails.isSchemaError();
}

public boolean isDataTypeSchemaError() {
return isDataTypeSchemaError;
return mdhtResultDetails.isDataTypeSchemaError();
}

public boolean isIGIssue() {
return mdhtResultDetails.isIGIssue();
}

public boolean isMUIssue() {
return mdhtResultDetails.isMUIssue();
}

public String getActualCodeSystem() {
return actualCodeSystem;
Expand All @@ -84,7 +98,9 @@ public static class RefCCDAValidationResultBuilder{
private final String xPath;
private final String validatorConfiguredXpath;
private final String documentLineNumber;
private final boolean isSchemaError, isDataTypeSchemaError;

// Only required for MDHT
private MDHTResultDetails mdhtResultDetails;

// Only valid for Vocabulary testing
private String actualCodeSystem;
Expand All @@ -93,16 +109,17 @@ public static class RefCCDAValidationResultBuilder{
private String actualCodeSystemName;

public RefCCDAValidationResultBuilder(String description, String xPath,
String validatorConfiguredXpath, ValidationResultType type,
String documentLineNumber, boolean isSchemaError,
boolean isDataTypeSchemaError) {
String validatorConfiguredXpath, ValidationResultType type, String documentLineNumber) {
this.description = description;
this.validatorConfiguredXpath = validatorConfiguredXpath;
this.type = type;
this.xPath = xPath;
this.documentLineNumber = documentLineNumber;
this.isSchemaError = isSchemaError;
this.isDataTypeSchemaError = isDataTypeSchemaError;
}

public RefCCDAValidationResultBuilder mdhtResultDetails(MDHTResultDetails mdhtResultDetails) {
this.mdhtResultDetails = mdhtResultDetails;
return this;
}

public RefCCDAValidationResultBuilder actualCodeSystem(String actualCodeSystem) {
Expand All @@ -125,7 +142,7 @@ public RefCCDAValidationResultBuilder actualCodeSystemName(String actualCodeSyst
return this;
}

public RefCCDAValidationResult build(){
public RefCCDAValidationResult build() {
return new RefCCDAValidationResult(this);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.sitenv.referenceccda.validators.content;

import java.util.ArrayList;
import java.util.List;

import org.sitenv.contentvalidator.dto.ContentValidationResult;
import org.sitenv.contentvalidator.service.ContentValidatorService;
import org.sitenv.referenceccda.validators.BaseCCDAValidator;
Expand All @@ -10,9 +13,6 @@
import org.springframework.stereotype.Component;
import org.xml.sax.SAXException;

import java.util.ArrayList;
import java.util.List;


/**
* Created by Brian on 8/15/2016.
Expand Down Expand Up @@ -54,8 +54,8 @@ private RefCCDAValidationResult createValidationResult(ContentValidationResult r
default: type = ValidationResultType.REF_CCDA_INFO;
break;
}

return new RefCCDAValidationResult.RefCCDAValidationResultBuilder(result.getMessage(), null, null, type, "0", false, false)
return new RefCCDAValidationResult.RefCCDAValidationResultBuilder(result.getMessage(), null, null, type, "0")
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.sitenv.referenceccda.validators.schema;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public final class CCDATypes {
// higher level validation versions - not an objective
public static final String CCDAR21_OR_CCDAR11 = "C-CDA R2.1 or R1.1 Document";
public static final String CCDAR11_MU2 = "C-CDA R1.1 MU2 Document";

/// all below here are an actual validationObjective
// generic CCDA base level
public static final String NON_SPECIFIC_CCDA = "NonSpecificCCDA";
public static final String NON_SPECIFIC_CCDAR2 = "NonSpecificCCDAR2";
public static final List<String> NON_SPECIFIC_CCDA_TYPES = new ArrayList<String>(
Arrays.asList(NON_SPECIFIC_CCDA, NON_SPECIFIC_CCDAR2));

// most common mu2
public static final String TRANSITIONS_OF_CARE_AMBULATORY_SUMMARY = "TransitionsOfCareAmbulatorySummary";
// other mu2
public static final String CLINICAL_OFFICE_VISIT_SUMMARY = "ClinicalOfficeVisitSummary";
public static final String TRANSITIONS_OF_CARE_INPATIENT_SUMMARY = "TransitionsOfCareInpatientSummary";
public static final String VDT_AMBULATORY_SUMMARY = "VDTAmbulatorySummary";
public static final String VDT_INPATIENT_SUMMARY = "VDTInpatientSummary";
public static final List<String> MU2_TYPES = new ArrayList<String>(
Arrays.asList(TRANSITIONS_OF_CARE_AMBULATORY_SUMMARY,
CLINICAL_OFFICE_VISIT_SUMMARY,
TRANSITIONS_OF_CARE_INPATIENT_SUMMARY,
VDT_AMBULATORY_SUMMARY, VDT_INPATIENT_SUMMARY));

// CCDA document level (non-mu2)
public static final String CONSULTATION_NOTE = "ConsultationNote";
public static final String CONTINUITY_OF_CARE_DOCUMENT = "ContinuityOfCareDocument";
public static final String DIAGNOSTIC_IMAGING_REPORT = "DiagnosticImagingReport";
public static final String DISCHARGE_SUMMARY = "DischargeSummary";
public static final String HISTORY_AND_PHYSICAL_NOTE = "HistoryAndPhysicalNote";
public static final String OPERATIVE_NOTE = "OperativeNote";
public static final String PROCEDURE_NOTE = "ProcedureNote";
public static final String PROGRESS_NOTE = "ProgressNote";
public static final String UNSTRUCTURED_DOCUMENT = "UnstructuredDocument";

public static String getTypes() {
StringBuffer sb = new StringBuffer();
ValidationObjectives.appendObjectivesData(NON_SPECIFIC_CCDA_TYPES, "LEGACY (same result as 'C-CDA_IG_Plus_Vocab')", sb);
sb.append(" ");
ValidationObjectives.appendObjectivesData(MU2_TYPES, "C-CDA R1.1 WITH MU2", sb);
return sb.toString();
}

public static void main(String[] args) {
System.out.println(CCDATypes.getTypes());
}
}
Loading

0 comments on commit db24601

Please sign in to comment.