org.openmrs.module.emrapi.encounter.EncounterDomainWrapper.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.emrapi.encounter.EncounterDomainWrapper.java

Source

/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * 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.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.module.emrapi.encounter;

import org.apache.commons.collections.Predicate;
import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.DateTimeComparator;
import org.openmrs.Encounter;
import org.openmrs.EncounterProvider;
import org.openmrs.EncounterRole;
import org.openmrs.Form;
import org.openmrs.Location;
import org.openmrs.Obs;
import org.openmrs.Provider;
import org.openmrs.User;
import org.openmrs.Visit;
import org.openmrs.module.emrapi.adt.exception.EncounterDateAfterVisitStopDateException;
import org.openmrs.module.emrapi.adt.exception.EncounterDateBeforeVisitStartDateException;
import org.openmrs.module.emrapi.domainwrapper.DomainWrapper;
import org.openmrs.module.emrapi.visit.VisitDomainWrapper;
import org.openmrs.util.OpenmrsUtil;
import org.springframework.transaction.annotation.Transactional;

import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class EncounterDomainWrapper implements DomainWrapper {

    public static final Predicate NON_VOIDED_PREDICATE = new Predicate() {
        @Override
        public boolean evaluate(Object o) {
            return !((Encounter) o).isVoided();
        }
    };
    public static final Comparator<Encounter> DATETIME_COMPARATOR = new Comparator<Encounter>() {
        @Override
        public int compare(Encounter encounter, Encounter encounter2) {
            return DateTimeComparator.getInstance().compare(encounter.getEncounterDatetime(),
                    encounter2.getEncounterDatetime());
        }
    };

    private Encounter encounter;

    public EncounterDomainWrapper() {
    }

    @Deprecated // use DomainWrapperFactory
    public EncounterDomainWrapper(Encounter encounter) {
        this.encounter = encounter;
    }

    public Encounter getEncounter() {
        return encounter;
    }

    public void setEncounter(Encounter encounter) {
        this.encounter = encounter;
    }

    /**
     * Convenience getters for underlying encounter properties
     */

    public Visit getVisit() {
        return encounter.getVisit();
    }

    public Location getLocation() {
        return encounter.getLocation();
    }

    public Date getEncounterDatetime() {
        return encounter.getEncounterDatetime();
    }

    public int getEncounterId() {
        return encounter.getEncounterId();
    }

    public Form getForm() {
        return encounter.getForm();
    }

    public Boolean getVoided() {
        return encounter.getVoided();
    }

    public Set<EncounterProvider> getEncounterProviders() {
        return encounter.getEncounterProviders();
    }

    // TODO not sure we are ever going to get this working properly. might want to deprecate?
    public Provider getPrimaryProvider() {
        // TODO for now we just return the first non-voided provider as the primary provider; we should improve this
        for (EncounterProvider provider : encounter.getEncounterProviders()) {
            if (!provider.isVoided()) {
                return provider.getProvider();
            }
        }
        return null;
    }

    /**
     * Verify if a user is the creator or one of the providers in the encounter
     *
     * @param currentUser
     * @return
     */
    public boolean participatedInEncounter(User currentUser) {

        if (verifyIfUserIsTheCreatorOfEncounter(currentUser)) {
            return true;
        } else if (verifyIfUserIsOneOfTheProviders(currentUser)) {
            return true;
        }

        return false;
    }

    private boolean verifyIfUserIsOneOfTheProviders(User currentUser) {
        for (EncounterProvider encounterProvider : encounter.getEncounterProviders()) {
            if (encounterProvider.getProvider().getPerson().equals(currentUser.getPerson())) {
                return true;
            }
        }
        return false;
    }

    private boolean verifyIfUserIsTheCreatorOfEncounter(User currentUser) {
        return encounter.getCreator().equals(currentUser);
    }

    public void closeVisit() {
        Visit visit = encounter.getVisit();
        if (visit == null) {
            throw new IllegalArgumentException("This encounter does not belong to a visit");
        }
        if (visit.getStopDatetime() == null) {
            visit.setStopDatetime(new Date());
        }
        // TODO save the visit via service
    }

    public Map<EncounterRole, Set<Provider>> getProviders() {
        return encounter.getProvidersByRoles();
    }

    /**
     * Associates the encounter with the specified visit
     * If the encounterDatetime has only a Date component, adds a time component (if necessary) based on our business logic:
     * if this is an open visit and encounter date = today, consider a real-tiome transaction and we stamp with the current time,
     * otherwise we add a time component (if necessary) to make sure the encounter falls within the specified visit
     *
     * @param visit
     * @throws EncounterDateBeforeVisitStartDateException
     *
     * @throws EncounterDateAfterVisitStopDateException
     *
     */
    @Transactional
    public void attachToVisit(VisitDomainWrapper visit)
            throws EncounterDateBeforeVisitStartDateException, EncounterDateAfterVisitStopDateException {

        // if time component already exists, for now we just verify that the encounter falls within the visit
        if (dateHasTimeComponent(encounter.getEncounterDatetime())) {

            if (encounter.getEncounterDatetime().before(visit.getStartDatetime())) {
                throw new EncounterDateBeforeVisitStartDateException();
            }

            if (visit.getStopDatetime() != null
                    && encounter.getEncounterDatetime().after(visit.getStopDatetime())) {
                throw new EncounterDateAfterVisitStopDateException();
            }

        }
        // otherwise, properly set the time component
        else {

            DateMidnight encounterDate = new DateMidnight(encounter.getEncounterDatetime());
            DateMidnight currentDate = new DateMidnight();
            DateMidnight visitStartDate = new DateMidnight(visit.getStartDatetime());
            DateMidnight visitStopDate = visit.getStopDatetime() != null ? new DateMidnight(visit.getStopDatetime())
                    : null;

            if (encounterDate.isBefore(visitStartDate)) {
                throw new EncounterDateBeforeVisitStartDateException();
            }

            if (visitStopDate != null && encounterDate.isAfter(visitStopDate)) {
                throw new EncounterDateAfterVisitStopDateException();
            }

            // if encounter date = today and open visit, consider this a real-time transaction and timestamp with current datetime
            if (encounterDate.equals(currentDate) && visit.isOpen()) {
                setEncounterDatetimeAndPropagateToObs(encounter, new Date());
            }

            // otherwise, consider a retrospective encounter, and so we want an encounter date to have no time component, EXCEPT
            // if this encounter is on the first day of the visit, the encounter datetime cannot be before the visit start time
            else if (encounterDate.isBefore(new DateTime(visit.getStartDatetime()))) {
                setEncounterDatetimeAndPropagateToObs(encounter, visit.getStartDatetime());
            }

            // otherwise, leave encounter date as is
        }

        // now associate with the visit
        encounter.setVisit(visit.getVisit());
    }

    /**
     * Sets encounter.encounterDatetime, and if any obs in encounter.allObs had an obsDatetime equal to the original
     * encounterDatetime, those obsDatetimes are also set. (I.e. this will change the obsDatetime of obs who
     * "inherit" datetime from the encounter, but not touch obs who have an different obsDatetime.
     * @param encounter
     * @param datetime
     */
    private void setEncounterDatetimeAndPropagateToObs(Encounter encounter, Date datetime) {
        Date original = encounter.getEncounterDatetime();
        encounter.setEncounterDatetime(datetime);

        for (Obs candidate : allObs(encounter)) {
            if (OpenmrsUtil.nullSafeEquals(original, candidate.getObsDatetime())) {
                candidate.setObsDatetime(datetime);
            }
        }
    }

    /**
     * If an encounter has not been persisted yet, we cannot be sure that Encounter.getAllObs() will actually return all
     * obs (because some obs group members may not yet have been attached to the encounter by the service), so we use
     * this helper method to ensure we recursively get all obs in an encounter
     * @param encounter
     * @return
     */
    private Set<Obs> allObs(Encounter encounter) {
        Set<Obs> allObs = new HashSet<Obs>();
        for (Obs o : encounter.getObsAtTopLevel(false)) {
            allObsRecursion(allObs, o);
        }
        return allObs;
    }

    private void allObsRecursion(Set<Obs> allObs, Obs obs) {
        if (!obs.isVoided()) {
            allObs.add(obs);
            if (obs.hasGroupMembers()) {
                for (Obs child : obs.getGroupMembers()) {
                    allObsRecursion(allObs, child);
                }
            }
        }
    }

    /**
     * Convenience method to allow one to call attachToVisit with an "unwrapped" visit
     *
     * @param visit
     * @throws EncounterDateBeforeVisitStartDateException
     *
     * @throws EncounterDateAfterVisitStopDateException
     *
     */
    @Transactional
    public void attachToVisit(Visit visit)
            throws EncounterDateBeforeVisitStartDateException, EncounterDateAfterVisitStopDateException {
        attachToVisit(new VisitDomainWrapper(visit));
    }

    private boolean dateHasTimeComponent(Date date) {
        return !new DateTime(date).equals(new DateMidnight(date));
    }
}