/**
* 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) The Minnesota Department of Health. All Rights Reserved.
*
* Contributor(s): CIRG, University of Washington, Seattle WA.
*/
package us.mn.state.health.lims.common.provider.validation;
import org.apache.commons.validator.GenericValidator;
import us.mn.state.health.lims.common.action.IActionConstants;
import us.mn.state.health.lims.common.services.StatusService;
import us.mn.state.health.lims.common.services.StatusService.RecordStatus;
import us.mn.state.health.lims.common.services.StatusSet;
import us.mn.state.health.lims.common.util.StringUtil;
import us.mn.state.health.lims.observationhistory.dao.ObservationHistoryDAO;
import us.mn.state.health.lims.observationhistory.daoimpl.ObservationHistoryDAOImpl;
import us.mn.state.health.lims.observationhistory.valueholder.ObservationHistory;
import us.mn.state.health.lims.observationhistorytype.ObservationHistoryTypeMap;
import us.mn.state.health.lims.patient.valueholder.Patient;
import us.mn.state.health.lims.project.dao.ProjectDAO;
import us.mn.state.health.lims.project.daoimpl.ProjectDAOImpl;
import us.mn.state.health.lims.project.valueholder.Project;
import us.mn.state.health.lims.sample.dao.SampleDAO;
import us.mn.state.health.lims.sample.daoimpl.SampleDAOImpl;
import us.mn.state.health.lims.sample.util.AccessionNumberUtil;
import us.mn.state.health.lims.sample.valueholder.Sample;
import java.util.List;
import static us.mn.state.health.lims.common.provider.validation.IAccessionNumberValidator.ValidationResults.*;
public class ProgramAccessionValidator implements IAccessionNumberValidator {
private static final String INCREMENT_STARTING_VALUE = "00001";
private static final int UPPER_INC_RANGE = 99999;
private static final int INCREMENT_START = 4;
private static final int PROGRAM_START = 0;
private static final int PROGRAM_END = 4;
private static final int LENGTH = 9;
private static final boolean NEED_PROGRAM_CODE = true;
private static ProjectDAO projectDAO;
public boolean needProgramCode() {
return NEED_PROGRAM_CODE;
}
public String createFirstAccessionNumber(String programCode) {
return programCode + INCREMENT_STARTING_VALUE;
}
public String incrementAccessionNumber(String currentHighAccessionNumber) {
int increment = Integer.parseInt(currentHighAccessionNumber.substring(INCREMENT_START));
String incrementAsString = INCREMENT_STARTING_VALUE;
if( increment < UPPER_INC_RANGE){
increment++;
incrementAsString = String.format("%05d", increment);
}else{
throw new IllegalArgumentException("AccessionNumber has no next value");
}
StringBuilder builder = new StringBuilder( currentHighAccessionNumber.substring(PROGRAM_START, PROGRAM_END).toUpperCase());
builder.append(incrementAsString);
return builder.toString();
}
public ValidationResults validFormat(String accessionNumber, boolean checkDate) {
// The rule is 4 digit program code and 4 incremented numbers
if (accessionNumber.length() != LENGTH) {
return ValidationResults.LENGTH_FAIL;
}
String programCode = accessionNumber.substring(PROGRAM_START, PROGRAM_END).toUpperCase();
//check program code validity
ProjectDAO projectDAO = getProjectDAO();
List<Project> programCodes = projectDAO.getAllProjects();
boolean found = false;
for ( Project code: programCodes ){
if ( programCode.equals(code.getProgramCode())){
found = true;
break;
}
}
if ( !found ) {
return ValidationResults.PROGRAM_FAIL;
}
try {
Integer.parseInt(accessionNumber.substring(INCREMENT_START));
} catch (NumberFormatException e) {
return ValidationResults.FORMAT_FAIL;
}
return ValidationResults.SUCCESS;
}
public String getInvalidMessage(ValidationResults results){
switch(results){
case LENGTH_FAIL: return StringUtil.getMessageForKey("sample.entry.invalid.accession.number.length");
case USED_FAIL: return StringUtil.getMessageForKey("sample.entry.invalid.accession.number.used");
case PROGRAM_FAIL: return StringUtil.getMessageForKey("sample.entry.invalid.accession.number.program");
case FORMAT_FAIL: return StringUtil.getMessageForKey("sample.entry.invalid.accession.number.format");
case REQUIRED_FAIL: return StringUtil.getMessageForKey("sample.entry.invalid.accession.number.required");
case PATIENT_STATUS_FAIL: return StringUtil.getMessageForKey("sample.entry.invalid.accession.number.patientRecordStatus");
case SAMPLE_STATUS_FAIL: return StringUtil.getMessageForKey("sample.entry.invalid.accession.number.sampleRecordStatus");
default: return StringUtil.getMessageForKey("sample.entry.invalid.accession.number");
}
}
public String getInvalidFormatMessage( ValidationResults results ){
return StringUtil.getMessageForKey("sample.entry.invalid.accession.number.format");
}
public String getNextAvailableAccessionNumber(String prefix){
String nextAccessionNumber = null;
SampleDAO sampleDAO = new SampleDAOImpl();
String curLargestAccessionNumber = sampleDAO.getLargestAccessionNumberWithPrefix(prefix);
if( curLargestAccessionNumber == null){
nextAccessionNumber = createFirstAccessionNumber(prefix);
}else{
nextAccessionNumber = incrementAccessionNumber(curLargestAccessionNumber);
}
return nextAccessionNumber;
}
public boolean accessionNumberIsUsed(String accessionNumber, String recordType) {
boolean accessionNumberUsed = new SampleDAOImpl().getSampleByAccessionNumber(accessionNumber) != null;
if( recordType == null){
return accessionNumberUsed;
}
StatusSet statusSet = StatusService.getInstance().getStatusSetForAccessionNumber(accessionNumber);
String recordStatus = new String();
boolean isSampleEntry = recordType.contains("Sample");
boolean isPatientEntry = recordType.contains("Patient");
boolean isInitial = recordType.contains("initial");
boolean isDouble = recordType.contains("double");
if (accessionNumberUsed) {
// sample entry, get SampleRecordStatus
if (isSampleEntry){
recordStatus = statusSet.getSampleRecordStatus().toString();
}
// patient entry, get PatientRecordStatus
else if (isPatientEntry) {
recordStatus = statusSet.getPatientRecordStatus().toString();
}
// initial entry, the status must be NotRegistered
String notRegistered = RecordStatus.NotRegistered.toString();
String initialReg = RecordStatus.InitialRegistration.toString();
if (isInitial){
if(!notRegistered.equals(recordStatus) ){
return true;
}
}
// double entry, the status must be InitialRegistration
else if (isDouble) {
if ( !initialReg.equals(recordStatus) ) {
return false;
}
else {
return true;
}
}
}
return false;
}
public int getMaxAccessionLength() {
return LENGTH;
}
/**
* There are many possible samples with various status, only some of which are valid during certain entry steps.
* This method provides validation results identifying whether a given sample is appropriate given all the information.
* @param accessionNumber the number for the sample
* @param recordType initialPatient, initialSample, doublePatient (double entry for patient), doubleSample
* @param isRequired the step being done expects the sample to exist. This is used generate appropriate results, either
* REQUIRED_FAIL vs SAMPLE_NOT_FOUND
* @param studyFormName - an additional
* @return
*/
public ValidationResults checkAccessionNumberValidity(String accessionNumber, String recordType, String isRequired,
String studyFormName) {
ValidationResults results = validFormat(accessionNumber, true);
SampleDAO sampleDAO = new SampleDAOImpl();
boolean accessionUsed = (sampleDAO.getSampleByAccessionNumber(accessionNumber) != null);
if (results == ValidationResults.SUCCESS) {
if (IActionConstants.TRUE.equals(isRequired) && !accessionUsed){
results = ValidationResults.REQUIRED_FAIL;
return results;
} else {
if (recordType == null) {
results = ValidationResults.USED_FAIL;
}
// record Type specified, so work out the detailed response to report
if (accessionUsed) {
if ( recordType.contains("initial")) {
if ( recordType.contains("Patient")) {
results = AccessionNumberUtil.isPatientStatusValid(accessionNumber, RecordStatus.NotRegistered);
if (results != PATIENT_STATUS_FAIL) {
results = matchExistingStudyFormName(accessionNumber, studyFormName, false);
}
} else if ( recordType.contains("Sample")) {
results = AccessionNumberUtil.isSampleStatusValid(accessionNumber, RecordStatus.NotRegistered);
if (results != SAMPLE_STATUS_FAIL) {
results = matchExistingStudyFormName(accessionNumber, studyFormName, false);
}
}
} else {
if (recordType.contains("double")) {
if ( recordType.contains("Patient")) {
results = AccessionNumberUtil.isPatientStatusValid(accessionNumber, RecordStatus.InitialRegistration);
if (results != PATIENT_STATUS_FAIL) {
results = matchExistingStudyFormName(accessionNumber, studyFormName, true);
}
} else if ( recordType.contains("Sample")) {
results = AccessionNumberUtil.isSampleStatusValid(accessionNumber, RecordStatus.InitialRegistration);
if (results != SAMPLE_STATUS_FAIL) {
results = matchExistingStudyFormName(accessionNumber, studyFormName, true);
}
}
}
}
} else {
if ( recordType.contains("initial")) {
results = ValidationResults.SAMPLE_NOT_FOUND; // initial entry not used is good
} else if ( recordType.contains("double") ) {
results = ValidationResults.REQUIRED_FAIL; // double entry not existing is a problem
}
}
}
}
return results;
}
/**
* Can the existing accession number be used in the given form?
* This method is useful when we have an existing accessionNumber and want to ask the question.
* @param accessionNumber
* @param existingRequired true => it is required that there is an existing studyFormName?
* @return
*/
private static ValidationResults matchExistingStudyFormName(String accessionNumber, String studyFormName, boolean existingRequired) {
if (GenericValidator.isBlankOrNull(studyFormName)) {
return SAMPLE_FOUND;
}
String existingName = findStudyFormName(accessionNumber);
if (existingName.equals(studyFormName) || (!existingRequired && GenericValidator.isBlankOrNull(existingName))) {
return SAMPLE_FOUND;
}
return SAMPLE_STATUS_FAIL; // the sample was entered on a different form!
}
private static String findStudyFormName(String accessionNumber) {
ObservationHistoryDAO ohDAO = new ObservationHistoryDAOImpl();
StatusSet statusSet = StatusService.getInstance().getStatusSetForAccessionNumber(accessionNumber);
Patient p = new Patient();
p.setId(statusSet.getPatientId());
Sample s = new Sample();
s.setId(statusSet.getSampleId());
List<ObservationHistory> all = ohDAO.getAll(p, s, ObservationHistoryTypeMap.getInstance().getIDForType("projectFormName"));
String existingName = "";
if (all.size() > 0) {
existingName = all.get(0).getValue();
}
return existingName;
}
@Override
public int getInvarientLength() {
return PROGRAM_END;
}
@Override
public int getChangeableLength() {
return getMaxAccessionLength() - getInvarientLength();
}
@Override
public String getPrefix(){
return null; //no single prefix
}
private static ProjectDAO getProjectDAO() {
if( projectDAO == null){
projectDAO = new ProjectDAOImpl();
}
return projectDAO;
}
public static void setProjectDAO(ProjectDAO projectDAO) {
ProgramAccessionValidator.projectDAO = projectDAO;
}
}