edu.duke.cabig.c3pr.domain.StudySite.java Source code

Java tutorial

Introduction

Here is the source code for edu.duke.cabig.c3pr.domain.StudySite.java

Source

/*******************************************************************************
 * Copyright Duke Comprehensive Cancer Center and SemanticBits
 * 
 * Distributed under the OSI-approved BSD 3-Clause License.
 * See http://ncip.github.com/c3pr/LICENSE.txt for details.
 ******************************************************************************/
package edu.duke.cabig.c3pr.domain;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Transient;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.Where;
import org.springframework.context.MessageSource;
import org.springframework.context.support.ResourceBundleMessageSource;

import edu.duke.cabig.c3pr.constants.APIName;
import edu.duke.cabig.c3pr.constants.AmendmentType;
import edu.duke.cabig.c3pr.constants.CoordinatingCenterStudyStatus;
import edu.duke.cabig.c3pr.constants.NotificationEmailSubstitutionVariablesEnum;
import edu.duke.cabig.c3pr.constants.RegistrationWorkFlowStatus;
import edu.duke.cabig.c3pr.constants.SiteStudyStatus;
import edu.duke.cabig.c3pr.constants.StatusType;
import edu.duke.cabig.c3pr.domain.factory.ParameterizedBiDirectionalInstantiateFactory;
import edu.duke.cabig.c3pr.exception.C3PRCodedRuntimeException;
import edu.duke.cabig.c3pr.exception.C3PRExceptionHelper;
import edu.duke.cabig.c3pr.utils.CommonUtils;
import edu.duke.cabig.c3pr.utils.DateUtil;
import gov.nih.nci.cabig.ctms.collections.LazyListHelper;

/**
 * The Class StudySite.
 *
 *
 * @author Ram Chilukuri, Priyatam
 * @author kherm, himanshu
 */
@Entity
@DiscriminatorValue(value = "SST")
public class StudySite extends StudyOrganization implements Comparable<StudySite> {

    /** The companion study association. */
    private CompanionStudyAssociation companionStudyAssociation;

    /** The target accrual number. */
    private Integer targetAccrualNumber;

    /** The c3pr exception helper. */
    private C3PRExceptionHelper c3PRExceptionHelper;

    /** The c3pr error messages. */
    private MessageSource c3prErrorMessages;

    /** The study site study version. */
    private StudySiteStudyVersion studySiteStudyVersion;

    /** The study site study versions. */
    private List<StudySiteStudyVersion> studySiteStudyVersions;

    /** The site study status. */
    private LazyListHelper lazyListHelper;

    private Integer studySubjectId;

    /**
     * Instantiates a new study site.
     */
    public StudySite() {
        lazyListHelper = new LazyListHelper();
        ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
        resourceBundleMessageSource.setBasename("error_messages_multisite");
        ResourceBundleMessageSource resourceBundleMessageSource1 = new ResourceBundleMessageSource();
        resourceBundleMessageSource1.setBasename("error_messages_c3pr");
        resourceBundleMessageSource1.setParentMessageSource(resourceBundleMessageSource);
        this.c3prErrorMessages = resourceBundleMessageSource1;
        this.c3PRExceptionHelper = new C3PRExceptionHelper(c3prErrorMessages);
        studySiteStudyVersions = new ArrayList<StudySiteStudyVersion>();
        lazyListHelper.add(SiteStatusHistory.class,
                new ParameterizedBiDirectionalInstantiateFactory<SiteStatusHistory>(SiteStatusHistory.class, this));
    }

    @Transient
    public Integer getStudySubjectId() {
        return studySubjectId;
    }

    private void setStudySubjectId(Integer studySubjectId) {
        this.studySubjectId = studySubjectId;
    }

    public StudySite(HealthcareSite hcs, Integer studySubjectId) {
        this.setHealthcareSite(hcs);
        this.setStudySubjectId(studySubjectId);
    }

    /**
     * Are there any assignments using this relationship?.
     *
     * @return true, if checks if is used
     */
    @Transient
    public boolean isUsed() {
        return getStudySubjects().size() > 0;
    }

    /**
     * Gets the study subjects.
     *
     * @return the study subjects
     */
    @Transient
    public List<StudySubject> getStudySubjects() {
        Set<StudySubject> h = new HashSet<StudySubject>();
        for (StudySiteStudyVersion studySiteStudyVersion : getStudySiteStudyVersions()) {
            for (StudySubjectStudyVersion studySubjectStudyVersion : studySiteStudyVersion
                    .getStudySubjectStudyVersions()) {
                h.add(studySubjectStudyVersion.getStudySubject());
            }
        }
        List<StudySubject> studySubjects = new ArrayList<StudySubject>();
        studySubjects.addAll(h);
        return studySubjects;
    }

    /**
     * Gets the target accrual number.
     *
     * @return the target accrual number
     */
    public Integer getTargetAccrualNumber() {
        return targetAccrualNumber;
    }

    /**
     * Sets the target accrual number.
     *
     * @param targetAccrualNumber the new target accrual number
     */
    public void setTargetAccrualNumber(Integer targetAccrualNumber) {
        this.targetAccrualNumber = targetAccrualNumber;
    }

    /**
     * Gets the current accrual count.
     *
     * @return the current accrual count
     */
    @Transient
    public int getCurrentAccrualCount() {
        int count = 0;
        for (StudySubject s : this.getStudySubjects()) {
            if (s.getRegWorkflowStatus() == RegistrationWorkFlowStatus.ON_STUDY
                    || s.getRegWorkflowStatus() == RegistrationWorkFlowStatus.OFF_STUDY
                    || s.getRegWorkflowStatus() == RegistrationWorkFlowStatus.RESERVED)
                count++;
        }
        return count;
    }

    /**
     * Activate.
     */
    public void activate(Date effectiveDate) {
        if (!(this.getSiteStudyStatus() == SiteStudyStatus.PENDING
                || this.getSiteStudyStatus() == SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL
                || this.getSiteStudyStatus() == SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL_AND_TREATMENT)) {
            throw getC3PRExceptionHelper().getRuntimeException(
                    getCode("C3PR.EXCEPTION.STUDYSITE.STATUS_CANNOT_SET_STATUS.CODE"),
                    new String[] { this.getSiteStudyStatus().getDisplayName() });
        }
        if (this.getStudy().getCoordinatingCenterStudyStatus() == CoordinatingCenterStudyStatus.OPEN) {
            if (this.getSiteStudyStatus(effectiveDate) == SiteStudyStatus.PENDING) {
                StudySiteStudyVersion effectiveStudySiteStudyVersion = getStudySiteStudyVersion(effectiveDate);
                if (effectiveStudySiteStudyVersion != null) {
                    effectiveStudySiteStudyVersion.apply(effectiveDate);
                } else {
                    throw getC3PRExceptionHelper().getRuntimeException(
                            getCode("C3PR.EXCEPTION.SITE.EFFECTIVE_DATE_NO_STUDY_SITE_STUDY_VERSION_FOUND.CODE"),
                            new String[] { CommonUtils.getDateString(effectiveDate) });
                }
            }
            handleStudySiteStatusChange(effectiveDate, SiteStudyStatus.ACTIVE);

            //          TODO companion study
            //            Study study = this.getStudy();
            //            if(!study.getCompanionIndicator()){
            //               for(CompanionStudyAssociation companionStudyAssociation : study.getStudyVersion().getCompanionStudyAssociations()){
            //                  for(StudySite studySite : companionStudyAssociation.getStudySites()){
            //                     if(studySite.getHealthcareSite().getPrimaryIdentifier() == this.getHealthcareSite().getPrimaryIdentifier()){
            //                        if(studySite.getSiteStudyStatus() != SiteStudyStatus.ACTIVE){
            //                           studySite.activate(effectiveDate);
            //                        }
            //                     }
            //                  }
            //               }
            //            }
        } else {
            throw getC3PRExceptionHelper().getRuntimeException(getCode(
                    "C3PR.EXCEPTION.SITE.STUDY.STATUS_CANNOT_BE_SET_WITH_CURRENT_COORDINATING_CENTER_STATUS.CODE"),
                    new String[] { SiteStudyStatus.ACTIVE.getDisplayName(),
                            this.getStudy().getCoordinatingCenterStudyStatus().getDisplayName() });
        }
    }

    public void applyStudyAmendment(String versionName, Date irbApprovalDate) {
        StudySiteStudyVersion previousStudySiteStudyVersion = getLatestStudySiteStudyVersion();
        if (previousStudySiteStudyVersion == null) {
            throw new RuntimeException();
        }
        StudySiteStudyVersion newStudySiteStudyVersion = new StudySiteStudyVersion();
        StudyVersion studyVersion = getStudy().getStudyVersion(versionName);
        newStudySiteStudyVersion.setStudyVersion(studyVersion);
        newStudySiteStudyVersion.setIrbApprovalDate(irbApprovalDate);
        this.addStudySiteStudyVersion(newStudySiteStudyVersion);
        newStudySiteStudyVersion.apply(irbApprovalDate);

        if (previousStudySiteStudyVersion.getEndDate() == null
                || previousStudySiteStudyVersion.getEndDate().after(irbApprovalDate)) {
            GregorianCalendar cal = new GregorianCalendar();
            cal.setTime(irbApprovalDate);
            cal.add(Calendar.DATE, -1);
            previousStudySiteStudyVersion.setEndDate(cal.getTime());
        }

    }

    public void closeToAccrual(Date effectiveDate) throws C3PRCodedRuntimeException {
        if (this.getSiteStudyStatus() == SiteStudyStatus.CLOSED_TO_ACCRUAL) {
            throw getC3PRExceptionHelper().getRuntimeException(
                    getCode("C3PR.EXCEPTION.STUDYSITE.STATUS_ALREADY_CLOSED_TO_ACCRUAL.CODE"),
                    new String[] { SiteStudyStatus.CLOSED_TO_ACCRUAL_AND_TREATMENT.getDisplayName() });
        }
        if (this.getSiteStudyStatus() == SiteStudyStatus.CLOSED_TO_ACCRUAL_AND_TREATMENT) {
            throw getC3PRExceptionHelper().getRuntimeException(
                    getCode("C3PR.EXCEPTION.SITE.STUDY.ALREADY_CLOSED.CODE"),
                    new String[] { SiteStudyStatus.CLOSED_TO_ACCRUAL_AND_TREATMENT.getDisplayName() });
        }
        if (this.getSiteStudyStatus() == SiteStudyStatus.PENDING) {
            throw getC3PRExceptionHelper().getRuntimeException(
                    getCode("C3PR.EXCEPTION.SITE.STUDY.STATUS_NEEDS_TO_BE_ACTIVE_FIRST.CODE"),
                    new String[] { SiteStudyStatus.CLOSED_TO_ACCRUAL.getDisplayName() });
        }
        handleStudySiteStatusChange(effectiveDate, SiteStudyStatus.CLOSED_TO_ACCRUAL);
    }

    public void closeToAccrualAndTreatment(Date effectiveDate) {
        if (this.getSiteStudyStatus() == SiteStudyStatus.CLOSED_TO_ACCRUAL_AND_TREATMENT) {
            throw getC3PRExceptionHelper().getRuntimeException(
                    getCode("C3PR.EXCEPTION.STUDYSITE.STATUS_ALREADY_CLOSED_TO_ACCRUAL_AND_TREATMENT.CODE"),
                    new String[] { SiteStudyStatus.CLOSED_TO_ACCRUAL_AND_TREATMENT.getDisplayName() });
        }
        if (this.getSiteStudyStatus() == SiteStudyStatus.PENDING) {
            throw getC3PRExceptionHelper().getRuntimeException(
                    getCode("C3PR.EXCEPTION.SITE.STUDY.STATUS_NEEDS_TO_BE_ACTIVE_FIRST.CODE"),
                    new String[] { SiteStudyStatus.CLOSED_TO_ACCRUAL_AND_TREATMENT.getDisplayName() });
        }
        handleStudySiteStatusChange(effectiveDate, SiteStudyStatus.CLOSED_TO_ACCRUAL_AND_TREATMENT);
    }

    public void temporarilyCloseToAccrualAndTreatment(Date effectiveDate) {
        if (this.getSiteStudyStatus() == SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL_AND_TREATMENT) {
            throw getC3PRExceptionHelper().getRuntimeException(getCode(
                    "C3PR.EXCEPTION.STUDYSITE.STATUS_ALREADY_TEMPORARY_CLOSED_TO_ACCRUAL_AND_TREATMENT.CODE"),
                    new String[] { SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL_AND_TREATMENT.getDisplayName() });
        }
        if (this.getSiteStudyStatus() == SiteStudyStatus.PENDING) {
            throw getC3PRExceptionHelper().getRuntimeException(
                    getCode("C3PR.EXCEPTION.SITE.STUDY.STATUS_NEEDS_TO_BE_ACTIVE_FIRST.CODE"),
                    new String[] { SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL_AND_TREATMENT.getDisplayName() });
        }
        if (this.getSiteStudyStatus() == SiteStudyStatus.CLOSED_TO_ACCRUAL
                || this.getSiteStudyStatus() == SiteStudyStatus.CLOSED_TO_ACCRUAL_AND_TREATMENT) {
            throw getC3PRExceptionHelper().getRuntimeException(
                    getCode("C3PR.EXCEPTION.SITE.STUDY.ALREADY_CLOSED.CODE"),
                    new String[] { SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL_AND_TREATMENT.getDisplayName() });
        }
        handleStudySiteStatusChange(effectiveDate, SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL_AND_TREATMENT);
    }

    public void temporarilyCloseToAccrual(Date effectiveDate) throws C3PRCodedRuntimeException {
        if (this.getSiteStudyStatus() == SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL) {
            throw getC3PRExceptionHelper().getRuntimeException(
                    getCode("C3PR.EXCEPTION.SITE.STUDY.ALREADY.TEMPORARILY_CLOSED_TO_ACCRUAL.CODE"),
                    new String[] { SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL.getDisplayName() });
        }
        if (this.getSiteStudyStatus() == SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL_AND_TREATMENT) {
            throw getC3PRExceptionHelper().getRuntimeException(getCode(
                    "C3PR.EXCEPTION.STUDYSITE.STATUS_ALREADY_TEMPORARY_CLOSED_TO_ACCRUAL_AND_TREATMENT.CODE"),
                    new String[] { SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL_AND_TREATMENT.getDisplayName() });
        }
        if (this.getSiteStudyStatus() == SiteStudyStatus.PENDING) {
            throw getC3PRExceptionHelper().getRuntimeException(
                    getCode("C3PR.EXCEPTION.SITE.STUDY.STATUS_NEEDS_TO_BE_ACTIVE_FIRST.CODE"),
                    new String[] { SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL.getDisplayName() });
        }
        if (this.getSiteStudyStatus() == SiteStudyStatus.CLOSED_TO_ACCRUAL
                || this.getSiteStudyStatus() == SiteStudyStatus.CLOSED_TO_ACCRUAL_AND_TREATMENT) {
            throw getC3PRExceptionHelper().getRuntimeException(
                    getCode("C3PR.EXCEPTION.SITE.STUDY.ALREADY_CLOSED.CODE"),
                    new String[] { SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL_AND_TREATMENT.getDisplayName() });
        }
        handleStudySiteStatusChange(effectiveDate, SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL);
    }

    /**
     * Gets the code.
     *
     * @param errortypeString the errortype string
     *
     * @return the code
     */
    @Transient
    public int getCode(String errortypeString) {
        return Integer.parseInt(this.c3prErrorMessages.getMessage(errortypeString, null, null));
    }

    @Transient
    public C3PRExceptionHelper getC3PRExceptionHelper() {
        return c3PRExceptionHelper;
    }

    public void setExceptionHelper(C3PRExceptionHelper c3PRExceptionHelper) {
        this.c3PRExceptionHelper = c3PRExceptionHelper;
    }

    @Transient
    public MessageSource getC3prErrorMessages() {
        return c3prErrorMessages;
    }

    public void setC3prErrorMessages(MessageSource errorMessages) {
        c3prErrorMessages = errorMessages;
    }

    @Transient
    /*
     * Used by the notifications use case to compose the email message by replacing the sub vars.
     */
    public Map<Object, Object> buildMapForNotification() {
        Map<Object, Object> map = new HashMap<Object, Object>();
        map.put(NotificationEmailSubstitutionVariablesEnum.STUDY_SITE_STATUS.toString(),
                getSiteStudyStatus().getDisplayName() == null ? "status" : getSiteStudyStatus().getDisplayName());
        map.put(NotificationEmailSubstitutionVariablesEnum.STUDY_ID.toString(),
                getHealthcareSite().getName() == null ? "site name" : getHealthcareSite().getName().toString());
        map.put(NotificationEmailSubstitutionVariablesEnum.STUDY_SHORT_TITLE.toString(),
                getStudy().getShortTitleText() == null ? "Short Title" : getStudy().getShortTitleText().toString());
        map.put(NotificationEmailSubstitutionVariablesEnum.STUDY_SITE_CURRENT_ACCRUAL.toString(),
                getStudy().getCurrentAccrualCount() == null ? "Study site current accrual"
                        : getStudy().getCurrentAccrualCount().toString());
        map.put(NotificationEmailSubstitutionVariablesEnum.STUDY_ACCRUAL_THRESHOLD.toString(),
                getStudy().getTargetAccrualNumber() == null ? "Study site accrual threshold"
                        : getStudy().getTargetAccrualNumber().toString());
        return map;
    }

    @Transient
    public List<APIName> getPossibleTransitions() {
        List<APIName> possibleActions = new ArrayList<APIName>();
        SiteStatusHistory siteHistory = getSiteStatusHistory(new Date());
        if (siteHistory != null && siteHistory.getEndDate() != null) {
            return possibleActions;
        }

        if (this.getStudy().getCoordinatingCenterStudyStatus() != CoordinatingCenterStudyStatus.OPEN) {
            return possibleActions;
        }
        if (this.getSiteStudyStatus() == SiteStudyStatus.PENDING) {
            possibleActions.add(APIName.ACTIVATE_STUDY_SITE);
            return possibleActions;
        } else if (this.getSiteStudyStatus() == SiteStudyStatus.ACTIVE) {
            possibleActions.add(APIName.CLOSE_STUDY_SITE_TO_ACCRUAL);
            possibleActions.add(APIName.CLOSE_STUDY_SITE_TO_ACCRUAL_AND_TREATMENT);
            possibleActions.add(APIName.TEMPORARILY_CLOSE_STUDY_SITE_TO_ACCRUAL);
            possibleActions.add(APIName.TEMPORARILY_CLOSE_STUDY_SITE_TO_ACCRUAL_AND_TREATMENT);
            return possibleActions;
        } else if (this.getSiteStudyStatus() == SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL) {
            possibleActions.add(APIName.ACTIVATE_STUDY_SITE);
            possibleActions.add(APIName.TEMPORARILY_CLOSE_STUDY_SITE_TO_ACCRUAL_AND_TREATMENT);
            possibleActions.add(APIName.CLOSE_STUDY_SITE_TO_ACCRUAL);
            possibleActions.add(APIName.CLOSE_STUDY_SITE_TO_ACCRUAL_AND_TREATMENT);
            return possibleActions;
        } else if (this.getSiteStudyStatus() == SiteStudyStatus.TEMPORARILY_CLOSED_TO_ACCRUAL_AND_TREATMENT) {
            possibleActions.add(APIName.ACTIVATE_STUDY_SITE);
            possibleActions.add(APIName.CLOSE_STUDY_SITE_TO_ACCRUAL);
            possibleActions.add(APIName.CLOSE_STUDY_SITE_TO_ACCRUAL_AND_TREATMENT);
            return possibleActions;
        } else if (this.getSiteStudyStatus() == SiteStudyStatus.CLOSED_TO_ACCRUAL) {
            possibleActions.add(APIName.CLOSE_STUDY_SITE_TO_ACCRUAL_AND_TREATMENT);
            return possibleActions;
        }
        return possibleActions;
    }

    @ManyToOne
    @Cascade({ CascadeType.LOCK })
    @JoinColumn(name = "comp_assoc_id", insertable = false, updatable = false)
    public CompanionStudyAssociation getCompanionStudyAssociation() {
        return companionStudyAssociation;
    }

    public void setCompanionStudyAssociation(CompanionStudyAssociation companionStudyAssociation) {
        this.companionStudyAssociation = companionStudyAssociation;
    }

    @Transient
    public StudySiteStudyVersion getStudySiteStudyVersion() {
        if (studySiteStudyVersion == null) {
            int size = getStudySiteStudyVersions().size();
            if (size == 0) {
                throw getC3PRExceptionHelper().getRuntimeException(
                        getCode("C3PR.EXCEPTION.STUDYSITE.CORRUPT.STATE.CODE"),
                        new String[] { this.getHealthcareSite().getName(),
                                this.getStudy().getCoordinatingCenterStudyStatus().getDisplayName() });
            }
            studySiteStudyVersion = getStudySiteStudyVersion(new Date());
        }
        return studySiteStudyVersion;
    }

    @Transient
    public StudySiteStudyVersion getStudySiteStudyVersionGivenStudyVersionName(String studyVersionName) {
        for (StudySiteStudyVersion studySiteStudyVersion : this.getStudySiteStudyVersions()) {
            if (studySiteStudyVersion.getStudyVersion().getName().equalsIgnoreCase(studyVersionName)) {
                return studySiteStudyVersion;
            }
        }
        return null;
    }

    public void setStudySiteStudyVersion(StudySiteStudyVersion studySiteStudyVersion) {
        this.studySiteStudyVersion = studySiteStudyVersion;
    }

    @Transient
    public StudySiteStudyVersion getLatestStudySiteStudyVersion() {
        TreeSet<StudySiteStudyVersion> studySiteStudyVersionSet = new TreeSet<StudySiteStudyVersion>();
        studySiteStudyVersionSet.addAll(getStudySiteStudyVersions());
        return studySiteStudyVersionSet.last();
    }

    /**
     * Gets the study site study version for a given date.
     * Due to amendments, participating sites can be on multiple version,
     * however at a given date the site should only be using a single study version.
     * Use this method to fetch the version applicable on a date.
     *
     * @param date the date
     *
     * @return the study site study version, null if no study version was active on the given date
     */
    @Transient
    public StudySiteStudyVersion getStudySiteStudyVersion(Date date) {
        for (StudySiteStudyVersion studySiteStudyVersion : getStudySiteStudyVersions()) {
            if (studySiteStudyVersion.isValid(date)) {
                return studySiteStudyVersion;
            }
        }
        return null;
    }

    public StudySiteStudyVersion getAccruingStudySiteStudyVersion(Date date) {
        SiteStudyStatus status = getSiteStudyStatus(date);
        if (status == SiteStudyStatus.ACTIVE) {
            return getStudySiteStudyVersion(date);
        }
        return null;
    }

    @Transient
    public StudySiteStudyVersion getLatestAccruingStudySiteStudyVersion() {
        return getAccruingStudySiteStudyVersion(new Date());
    }

    /**
     * Gets the study study version for a given date.
     * Due to amendments, participating sites can be on multiple version,
     * however at a given date the site should only be using a single study version.
     * Use this method to fetch the version applicable on a date.
     *
     * @param date the date
     *
     * @return the study version, null if no study version was active on the given date
     */
    @Transient
    public StudyVersion getActiveStudyVersion(Date date) {
        StudySiteStudyVersion studySiteStudyVersion = getStudySiteStudyVersion(date);
        if (studySiteStudyVersion != null) {
            SiteStudyStatus status = getSiteStudyStatus(date);
            if (status == SiteStudyStatus.ACTIVE) {
                return studySiteStudyVersion.getStudyVersion();
            }
        }
        return null;
    }

    /**
     * Checks if study version setup is valid.
     * This methods confirms if on a given date, the study version that the site is using
     * is same as the one the study deems as latest.
     * @param date the date
     *
     * throws RuntimeException:
     * -if no study version is available for a given date
     * -Code 347: if the study site does not have any study version while the study expects the
     *            site to get the IRB approval for a study version
     * -Code 348: if the study site study version does not match the study version of the study on the
     *            given date. However the version does grant a grace period.
     * -Code 349: if the study site study version does not match the study version of the study on the
     *            given date. However the version is an optional amendment.
     */
    public void isStudyVersionSetupValid(Date date) {
        StudyVersion coCenterStudyVersion = getStudy().getStudyVersion(date);
        StudySiteStudyVersion studySiteStudyVersion = getStudySiteStudyVersion(date);
        if (coCenterStudyVersion == null) {
            throw getC3PRExceptionHelper()
                    .getRuntimeException(getCode("C3PR.EXCEPTION.STUDYSITE.STUDYVERSION.NO_VERSION_FOUND.CODE"));
        }
        if (coCenterStudyVersion.getVersionStatus() == StatusType.IN) {
            throw getC3PRExceptionHelper()
                    .getRuntimeException(getCode("C3PR.EXCEPTION.STUDYSITE.STUDYVERSION.PENDING.CODE"));
        }
        if (studySiteStudyVersion == null) {
            throw getC3PRExceptionHelper()
                    .getRuntimeException(getCode("C3PR.EXCEPTION.STUDYSITE.STUDYVERSION.IMMEDIATE.CODE"));
        }
        if (coCenterStudyVersion == studySiteStudyVersion.getStudyVersion()) {
            return;
        }
        if (coCenterStudyVersion.getAmendmentType() == AmendmentType.IMMEDIATE_AFTER_GRACE_PERIOD) {
            long daysLeft = 0;
            if (studySiteStudyVersion.getEndDate() != null) {
                daysLeft = (studySiteStudyVersion.getEndDate().getTime() - new Date().getTime())
                        / (1000 * 60 * 60 * 24);
            }
            throw getC3PRExceptionHelper().getRuntimeException(
                    getCode("C3PR.EXCEPTION.STUDYSITE.STUDYVERSION.GRACE.CODE"),
                    new String[] { Long.toString(daysLeft) });
        }
        if (coCenterStudyVersion.getAmendmentType() == AmendmentType.OPTIONAL) {
            throw getC3PRExceptionHelper()
                    .getRuntimeException(getCode("C3PR.EXCEPTION.STUDYSITE.STUDYVERSION.OPTIONAL.CODE"));
        }
    }

    /**
     * Checks if the current study version setup is valid.
     */
    public void isStudyVersionSetupValid() {
        if (getSiteStudyStatus() != SiteStudyStatus.PENDING) {
            isStudyVersionSetupValid(new Date());
        }
    }

    /**
     * Checks if study site can accrue a subject
     * on a study version on a given date.
     *
     * @param studyVersion the study version
     * @param date the date
     *
     * @return true, if is registerable
     */
    public boolean canEnroll(StudyVersion studyVersion, Date date) {
        StudySiteStudyVersion studySiteStudyVersion = getStudySiteStudyVersion(date);
        return studySiteStudyVersion == null ? false : (studySiteStudyVersion.getStudyVersion() == studyVersion);
    }

    @OneToMany(mappedBy = "studySite", orphanRemoval = true)
    @Cascade(value = { CascadeType.ALL })
    public List<StudySiteStudyVersion> getStudySiteStudyVersions() {
        return studySiteStudyVersions;
    }

    @Transient
    public List<StudySiteStudyVersion> getSortedStudySiteStudyVersions() {
        List<StudySiteStudyVersion> sortedStudySiteStudyVersions = new ArrayList<StudySiteStudyVersion>();
        sortedStudySiteStudyVersions.addAll(getStudySiteStudyVersions());
        Collections.sort(getStudySiteStudyVersions());
        return sortedStudySiteStudyVersions;
    }

    public void setStudySiteStudyVersions(List<StudySiteStudyVersion> studySiteStudyVersions) {
        this.studySiteStudyVersions = studySiteStudyVersions;
    }

    public void addStudySiteStudyVersion(StudySiteStudyVersion studySiteStudyVersion) {
        this.getStudySiteStudyVersions().add(studySiteStudyVersion);
        studySiteStudyVersion.setStudySite(this);
    }

    public void setup(Study study) {
        // this is the method where we will setup the study site for the  first time.
        super.setStudy(study);
        // 1. initially there is no study site study version, so we are creating one and associating it to study site.
        if (getStudySiteStudyVersions().size() == 0) {
            studySiteStudyVersion = new StudySiteStudyVersion();
            // 2. If we have active study version available , we are associating that study versin to study site study version otherwise we will associate latest available.
            StudyVersion studyVersion = study.getLatestActiveStudyVersion();
            if (studyVersion != null) {
                studyVersion.addStudySiteStudyVersion(studySiteStudyVersion);
            } else {
                studyVersion = study.getStudyVersion();
                studyVersion.addStudySiteStudyVersion(studySiteStudyVersion);
            }
            //3. initializing startdate of study site study version to 100 years old so that for the first time, it is not invalid
            studySiteStudyVersion.setStartDate(studyVersion.getVersionDate());
            this.addStudySiteStudyVersion(studySiteStudyVersion);
        }
        if (getSiteStatusHistory().size() == 0) {
            // 4. add default pending status to the study site
            createDefaultStudyStatusHistory();
        }
    }

    public void setIrbApprovalDate(Date irbApprovalDate) {
        getStudySiteStudyVersion().setIrbApprovalDate(irbApprovalDate);
    }

    @Transient
    public Date getIrbApprovalDate() {
        return getStudySiteStudyVersion().getIrbApprovalDate();
    }

    public int compareTo(StudySite o) {
        if (this.equals(o))
            return 0;
        else
            return 1;
    }

    @Transient
    public String getIrbApprovalDateStr() {
        return CommonUtils.getDateString(getIrbApprovalDate());
    }

    public void handleStudySiteStatusChange(Date effectiveDate, SiteStudyStatus status) {
        SiteStatusHistory lastSiteStatusHistory = getLatestSiteStatusHistory();
        if (lastSiteStatusHistory != null) {
            if (lastSiteStatusHistory.getStartDate() == null) {
                throw getC3PRExceptionHelper().getRuntimeException(
                        getCode("C3PR.EXCEPTION.STUDY.STUDYSITE.STATUS_HISTORY.NO.START_DATE.CODE"),
                        new String[] { lastSiteStatusHistory.getSiteStudyStatus().getDisplayName() });
            } else if (lastSiteStatusHistory.getStartDate() != null
                    && !lastSiteStatusHistory.getStartDate().before(effectiveDate)) {
                throw getC3PRExceptionHelper().getRuntimeException(
                        getCode("C3PR.EXCEPTION.STUDY.STUDYSITE.STATUS_HISTORY.INVALID.EFFECTIVE_DATE.CODE"),
                        new String[] { CommonUtils.getDateString(lastSiteStatusHistory.getStartDate()) });
            }
            if (lastSiteStatusHistory.getEndDate() != null) {
                // last history object should not have end date
                throw getC3PRExceptionHelper().getRuntimeException(
                        getCode("C3PR.EXCEPTION.STUDY.STUDYSITE.STATUS_HISTORY.END_DATE_PRESENT.CODE"),
                        new String[] { lastSiteStatusHistory.getSiteStudyStatus().getDisplayName() });
            } else {
                Date suggestedEndDate = effectiveDate;
                GregorianCalendar calendar = new GregorianCalendar();
                calendar.setTime(suggestedEndDate);
                calendar.add(calendar.DATE, -1);
                lastSiteStatusHistory.setEndDate(calendar.getTime());
            }
        }
        createSiteStatusHistory(effectiveDate, status);
    }

    @Transient
    public SiteStudyStatus getSiteStudyStatus() {
        return getSiteStudyStatus(new Date());
    }

    @Transient
    public SiteStudyStatus getSiteStudyStatus(Date date) {
        SiteStatusHistory siteStatusHistory = getSiteStatusHistory(date);
        if (siteStatusHistory != null) {
            return siteStatusHistory.getSiteStudyStatus();
        } else {
            createDefaultStudyStatusHistory();
            return SiteStudyStatus.PENDING;
        }
    }

    @Transient
    public SiteStatusHistory getLatestSiteStatusHistory() {
        TreeSet<SiteStatusHistory> siteStatusHistorySet = new TreeSet<SiteStatusHistory>();
        siteStatusHistorySet.addAll(getSiteStatusHistory());
        if (siteStatusHistorySet.size() > 0) {
            return siteStatusHistorySet.last();
        }
        return null;
    }

    @Transient
    public SiteStatusHistory getSiteStatusHistory(Date date) {
        Date newDate = DateUtil.getUtilDateFromString(DateUtil.formatDate(date, "MM/dd/yyyy"), "MM/dd/yyyy");
        List<SiteStatusHistory> siteStatusHistoryList = new ArrayList<SiteStatusHistory>();
        siteStatusHistoryList.addAll(this.getSiteStatusHistory());
        Collections.sort(siteStatusHistoryList);
        for (SiteStatusHistory siteStatusHistory : siteStatusHistoryList) {
            Date startDate = siteStatusHistory.getStartDate();
            Date endDate = siteStatusHistory.getEndDate();
            if (!startDate.after(newDate) && (endDate == null ? true : !endDate.before(newDate))) {
                return siteStatusHistory;
            }
        }
        return null;
    }

    private void createDefaultStudyStatusHistory() {
        Date currentDate = new Date();
        GregorianCalendar calendar = new GregorianCalendar();
        calendar.setTime(currentDate);
        calendar.add(calendar.YEAR, -100);

        SiteStatusHistory siteStatusHistory = new SiteStatusHistory();
        siteStatusHistory.setStartDate(calendar.getTime());
        siteStatusHistory.setSiteStudyStatus(SiteStudyStatus.PENDING);
        this.addSiteStatusHistory(siteStatusHistory);
    }

    private void createSiteStatusHistory(Date startDate, SiteStudyStatus status) {
        SiteStatusHistory siteStatusHistory = new SiteStatusHistory();
        siteStatusHistory.setStartDate(startDate);
        siteStatusHistory.setSiteStudyStatus(status);
        this.addSiteStatusHistory(siteStatusHistory);
    }

    @OneToMany(mappedBy = "studySite", fetch = FetchType.LAZY, orphanRemoval = true)
    @Cascade(value = { CascadeType.ALL })
    @Where(clause = "retired_indicator  = 'false'")
    public List<SiteStatusHistory> getSiteStatusHistoryInternal() {
        return lazyListHelper.getInternalList(SiteStatusHistory.class);
    }

    public void setSiteStatusHistoryInternal(final List<SiteStatusHistory> siteStatusHistory) {
        lazyListHelper.setInternalList(SiteStatusHistory.class, siteStatusHistory);
    }

    @Transient
    public List<SiteStatusHistory> getSiteStatusHistory() {
        return lazyListHelper.getLazyList(SiteStatusHistory.class);
    }

    @Transient
    public List<SiteStatusHistory> getSortedSiteStatusHistory() {
        List<SiteStatusHistory> siteStatusHistoryList = new ArrayList<SiteStatusHistory>();
        siteStatusHistoryList.addAll(getSiteStatusHistory());
        Collections.sort(siteStatusHistoryList);
        return siteStatusHistoryList;
    }

    public void setSiteStatusHistory(List<SiteStatusHistory> siteStatusHistory) {
        setSiteStatusHistoryInternal(siteStatusHistory);
    }

    public void addSiteStatusHistory(SiteStatusHistory siteStatusHistory) {
        siteStatusHistory.setStudySite(this);
        getSiteStatusHistoryInternal().add(siteStatusHistory);
    }

    @Transient
    public SiteStatusHistory getNextPossibleSiteStatusHistory() {
        SiteStatusHistory siteHistory = getSiteStatusHistory(new Date());
        if (siteHistory.getEndDate() != null) {
            List<SiteStatusHistory> listSiteStatusHistories = new ArrayList<SiteStatusHistory>();
            listSiteStatusHistories.addAll(getSiteStatusHistory());
            Collections.sort(listSiteStatusHistories);
            int index = listSiteStatusHistories.indexOf(siteHistory);
            if (listSiteStatusHistories.size() > index) {
                return listSiteStatusHistories.get(index + 1);
            }
        }
        return null;
    }

    @Transient
    public StudySiteStudyVersion getCurrentStudySiteStudyVersion() {
        Date currentDate = new Date();
        StudySiteStudyVersion currentSiteStudyVersion = getStudySiteStudyVersion(currentDate);
        if (currentSiteStudyVersion == null) {
            List<StudySiteStudyVersion> listStudySiteStudyVersion = getSortedStudySiteStudyVersions();
            return listStudySiteStudyVersion.get(listStudySiteStudyVersion.size() - 1);
        }
        return currentSiteStudyVersion;
    }

    @Transient
    public int getCurrentStudySiteStudyVersionIndex() {
        Date currentDate = new Date();
        StudySiteStudyVersion currentSiteStudyVersion = getStudySiteStudyVersion(currentDate);
        if (currentSiteStudyVersion == null) {
            List<StudySiteStudyVersion> listStudySiteStudyVersion = getSortedStudySiteStudyVersions();
            return getStudySiteStudyVersions()
                    .indexOf(listStudySiteStudyVersion.get(listStudySiteStudyVersion.size() - 1));
        }
        return getStudySiteStudyVersions().indexOf(currentSiteStudyVersion);
    }

}