org.openmrs.module.emrapi.adt.AdtServiceComponentTest.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.emrapi.adt.AdtServiceComponentTest.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.adt;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.time.DateUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openmrs.Encounter;
import org.openmrs.EncounterRole;
import org.openmrs.Location;
import org.openmrs.LocationTag;
import org.openmrs.Obs;
import org.openmrs.Patient;
import org.openmrs.Provider;
import org.openmrs.Visit;
import org.openmrs.api.ConceptService;
import org.openmrs.api.EncounterService;
import org.openmrs.api.LocationService;
import org.openmrs.api.PatientService;
import org.openmrs.api.VisitService;
import org.openmrs.api.context.Context;
import org.openmrs.module.emrapi.EmrApiConstants;
import org.openmrs.module.emrapi.EmrApiProperties;
import org.openmrs.module.emrapi.concept.EmrConceptService;
import org.openmrs.module.emrapi.disposition.DispositionService;
import org.openmrs.module.emrapi.test.ContextSensitiveMetadataTestUtils;
import org.openmrs.module.emrapi.visit.VisitDomainWrapper;
import org.openmrs.test.BaseModuleContextSensitiveTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.openmrs.module.emrapi.TestUtils.hasProviders;
import static org.openmrs.module.emrapi.adt.AdtAction.Type.ADMISSION;
import static org.openmrs.module.emrapi.adt.AdtAction.Type.DISCHARGE;

public class AdtServiceComponentTest extends BaseModuleContextSensitiveTest {

    public static final Predicate NON_VOIDED = new Predicate() {
        @Override
        public boolean evaluate(Object o) {
            return !((Encounter) o).isVoided();
        }
    };

    @Autowired
    private AdtService service;

    @Autowired
    private EmrApiProperties emrApiProperties;

    @Autowired
    LocationService locationService;

    @Autowired
    PatientService patientService;

    @Autowired
    VisitService visitService;

    @Autowired
    ConceptService conceptService;

    @Autowired
    DispositionService dispositionService;

    @Autowired
    EncounterService encounterService;

    @Autowired
    EmrConceptService emrConceptService;

    @Before
    public void setUp() throws Exception {
        executeDataSet("baseTestDataset.xml");
    }

    @Test
    public void integrationTest_ADT_workflow() {

        ContextSensitiveMetadataTestUtils.setupSupportsVisitLocationTag(locationService);
        Date startOfTest = new Date();

        Provider provider = Context.getProviderService().getProvider(1);
        Patient patient = Context.getPatientService().getPatient(7);

        Location parentLocation = locationService.getLocation(2);
        parentLocation.addTag(emrApiProperties.getSupportsVisitsLocationTag());
        locationService.saveLocation(parentLocation);

        // add a child location where we'll do the actual check-in
        Location outpatientDepartment = new Location();
        outpatientDepartment.setName("Outpatient Clinic in Xanadu");
        outpatientDepartment.setParentLocation(parentLocation);
        locationService.saveLocation(outpatientDepartment);

        // add another child location for inpatient, that supports admissions
        LocationTag supportsAdmission = new LocationTag();
        supportsAdmission.setName(EmrApiConstants.LOCATION_TAG_SUPPORTS_ADMISSION);
        locationService.saveLocationTag(supportsAdmission);

        Location inpatientWard = new Location();
        inpatientWard.setName("Inpatient Ward in Xanadu");
        inpatientWard.setParentLocation(parentLocation);
        inpatientWard.addTag(supportsAdmission);
        locationService.saveLocation(inpatientWard);

        // step 1: check in the patient (which should create a visit and an encounter)
        Encounter checkInEncounter = service.checkInPatient(patient, outpatientDepartment, null, null, null, false);

        assertThat(checkInEncounter.getVisit(), notNullValue());
        assertThat(checkInEncounter.getPatient(), is(patient));
        assertThat(checkInEncounter.getEncounterDatetime(), onOrAfter(startOfTest));
        assertThat(checkInEncounter.getVisit().getPatient(), is(patient));
        assertThat(checkInEncounter.getVisit().getStartDatetime(), onOrAfter(startOfTest));
        assertThat(checkInEncounter.getAllObs().size(), is(0));

        Map<EncounterRole, Set<Provider>> providers = new HashMap<EncounterRole, Set<Provider>>();
        providers.put(Context.getEncounterService().getEncounterRole(1), Collections.singleton(provider));

        // step 2: admit the patient (which should create an encounter)
        Date admitDatetime = new Date();
        AdtAction admission = new AdtAction(checkInEncounter.getVisit(), inpatientWard, providers, ADMISSION);
        admission.setActionDatetime(admitDatetime);
        Encounter admitEncounter = service.createAdtEncounterFor(admission);

        assertThat(admitEncounter.getPatient(), is(patient));
        assertThat(admitEncounter.getVisit(), is(checkInEncounter.getVisit()));
        assertThat(admitEncounter.getEncounterDatetime(), is(admitDatetime));
        assertThat(admitEncounter.getLocation(), is(inpatientWard));
        assertThat(admitEncounter, hasProviders(providers));
        assertThat(admitEncounter.getAllObs().size(), is(0));
        assertTrue(new VisitDomainWrapper(admitEncounter.getVisit(), emrApiProperties).isAdmitted());

        // TODO transfer the patient within the hospital

        // step 3: discharge the patient (which should create an encounter)

        AdtAction discharge = new AdtAction(admitEncounter.getVisit(), inpatientWard, providers, DISCHARGE);
        Encounter dischargeEncounter = service.createAdtEncounterFor(discharge);

        assertThat(dischargeEncounter.getPatient(), is(patient));
        assertThat(dischargeEncounter.getVisit(), is(checkInEncounter.getVisit()));
        assertThat(dischargeEncounter.getEncounterDatetime(), onOrAfter(startOfTest));
        assertThat(dischargeEncounter.getLocation(), is(inpatientWard));
        assertThat(dischargeEncounter, hasProviders(providers));
        assertFalse(new VisitDomainWrapper(admitEncounter.getVisit(), emrApiProperties).isAdmitted());
    }

    @Test
    public void integrationTest_ADT_workflow_duplicate_visits() throws Exception {
        final Integer numberOfThreads = 5;
        final CyclicBarrier threadsBarrier = new CyclicBarrier(numberOfThreads);

        Callable<Integer> checkInCall = new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                Context.openSession();
                authenticate();
                try {
                    LocationService locationService = Context.getLocationService();

                    Patient patient = Context.getPatientService().getPatient(7);

                    // parent location should support visits
                    LocationTag supportsVisits = new LocationTag();
                    supportsVisits.setName(EmrApiConstants.LOCATION_TAG_SUPPORTS_VISITS);
                    locationService.saveLocationTag(supportsVisits);

                    Location parentLocation = locationService.getLocation(2);
                    parentLocation.addTag(supportsVisits);
                    locationService.saveLocation(parentLocation);

                    threadsBarrier.await();

                    Encounter checkInEncounter = service.checkInPatient(patient, parentLocation, null, null, null,
                            false);

                    return checkInEncounter.getVisit().getVisitId();
                } finally {
                    Context.closeSession();
                }
            }
        };

        List<Callable<Integer>> checkInCalls = new ArrayList<Callable<Integer>>();
        for (int i = 0; i < numberOfThreads; i++) {
            checkInCalls.add(checkInCall);
        }

        ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);

        List<Future<Integer>> checkIns = executorService.invokeAll(checkInCalls);

        Integer visitId = null;
        for (Future<Integer> checkIn : checkIns) {
            Integer nextVisitId = checkIn.get();
            if (visitId != null) {
                assertThat(nextVisitId, is(visitId));
            } else {
                visitId = nextVisitId;
            }
        }
    }

    @Test
    public void integrationTest_createRetrospectiveVisit() throws Exception {

        ContextSensitiveMetadataTestUtils.setupSupportsVisitLocationTag(locationService);

        Location parentLocation = locationService.getLocation(2);
        parentLocation.addTag(emrApiProperties.getSupportsVisitsLocationTag());
        locationService.saveLocation(parentLocation);

        // add a child location where we'll enter the actual visit
        Location outpatientDepartment = new Location();
        outpatientDepartment.setName("Outpatient Clinic in Xanadu");
        outpatientDepartment.setParentLocation(parentLocation);
        locationService.saveLocation(outpatientDepartment);

        Patient patient = Context.getPatientService().getPatient(7);

        // create a retrospective visit
        Date startDate = new DateTime(2012, 1, 1, 0, 0, 0).toDate();
        Date stopDate = new DateTime(2012, 1, 3, 0, 0, 0).toDate();

        VisitDomainWrapper visit = service.createRetrospectiveVisit(patient, outpatientDepartment, startDate,
                stopDate);

        // test that the visit was successfully created
        assertNotNull(visit);
        assertThat(visit.getVisit().getPatient(), is(patient));
        assertThat(visit.getStartDatetime(), is(startDate));
        assertThat(visit.getStopDatetime(), is(stopDate));
        assertThat(visit.getVisit().getLocation(), is(parentLocation));
        assertThat(visit.getVisit().getVisitType(), is(emrApiProperties.getAtFacilityVisitType()));
    }

    @Test
    public void test_getVisitsAndHasVisitDuring() throws Exception {

        ContextSensitiveMetadataTestUtils.setupSupportsVisitLocationTag(locationService);

        Location parentLocation = locationService.getLocation(2);
        parentLocation.addTag(emrApiProperties.getSupportsVisitsLocationTag());
        locationService.saveLocation(parentLocation);

        // add a child location where we'll do the actual visit
        Location outpatientDepartment = new Location();
        outpatientDepartment.setName("Outpatient Clinic in Xanadu");
        outpatientDepartment.setParentLocation(parentLocation);
        locationService.saveLocation(outpatientDepartment);

        Patient patient = Context.getPatientService().getPatient(7);

        // create a retrospective visit
        Date startDate = new DateTime(2012, 1, 1, 0, 0, 0).toDate();
        Date stopDate = new DateTime(2012, 1, 3, 0, 0, 0).toDate();

        VisitDomainWrapper visit = service.createRetrospectiveVisit(patient, outpatientDepartment, startDate,
                stopDate);

        // start date falls within existing visit
        startDate = new DateTime(2012, 1, 2, 0, 0, 0).toDate();
        stopDate = new DateTime(2012, 1, 4, 0, 0, 0).toDate();
        assertTrue(service.hasVisitDuring(patient, outpatientDepartment, startDate, stopDate));

        // end date falls within existing visit
        startDate = new DateTime(2011, 12, 29, 0, 0, 0).toDate();
        stopDate = new DateTime(2012, 1, 2, 0, 0, 0).toDate();
        assertTrue(service.hasVisitDuring(patient, outpatientDepartment, startDate, stopDate));

        // range falls within existing visit
        startDate = new DateTime(2012, 1, 1, 12, 0, 0).toDate();
        stopDate = new DateTime(2012, 1, 2, 0, 0, 0).toDate();
        assertTrue(service.hasVisitDuring(patient, outpatientDepartment, startDate, stopDate));

        // range encompasses existing visit
        startDate = new DateTime(2011, 12, 29, 0, 0, 0).toDate();
        stopDate = new DateTime(2012, 1, 5, 0, 0, 0).toDate();
        assertTrue(service.hasVisitDuring(patient, outpatientDepartment, startDate, stopDate));

        // no stopDate specified
        startDate = new DateTime(2011, 12, 29, 0, 0, 0).toDate();
        assertTrue(service.hasVisitDuring(patient, outpatientDepartment, startDate, null));

        // range is before existing visit
        startDate = new DateTime(2011, 12, 29, 0, 0, 0).toDate();
        stopDate = new DateTime(2011, 12, 30, 0, 0, 0).toDate();
        assertFalse(service.hasVisitDuring(patient, outpatientDepartment, startDate, stopDate));

        // range is after existing visit
        startDate = new DateTime(2012, 1, 4, 0, 0, 0).toDate();
        stopDate = new DateTime(2012, 1, 5, 0, 0, 0).toDate();
        assertFalse(service.hasVisitDuring(patient, outpatientDepartment, startDate, stopDate));

        // now lets create an active visit to make sure that hasVisitDuring properly handles visits with no stopDate
        Date now = new Date();
        Date futureDate = new DateTime(3000, 12, 30, 0, 0, 0).toDate(); //  this test will start to fail after the year 3000! :)

        service.ensureActiveVisit(patient, outpatientDepartment);
        assertTrue(service.hasVisitDuring(patient, outpatientDepartment, now, futureDate));
        assertFalse(service.hasVisitDuring(patient, outpatientDepartment, stopDate, now));

        // now lets just add another retrospective visit to do a quick test of the getVisits method
        startDate = new DateTime(2012, 1, 5, 0, 0, 0).toDate();
        stopDate = new DateTime(2012, 1, 7, 0, 0, 0).toDate();

        VisitDomainWrapper anotherVisit = service.createRetrospectiveVisit(patient, outpatientDepartment, startDate,
                stopDate);

        startDate = new DateTime(2012, 1, 2, 0, 0, 0).toDate();
        stopDate = new DateTime(2012, 1, 6, 0, 0, 0).toDate();
        List<VisitDomainWrapper> visitDomainWrappers = service.getVisits(patient, outpatientDepartment, startDate,
                stopDate);

        assertThat(visitDomainWrappers.size(), is(2));

        List<Visit> visits = new ArrayList<Visit>();
        for (VisitDomainWrapper visitDomainWrapper : visitDomainWrappers) {
            visits.add(visitDomainWrapper.getVisit());
        }

        assertTrue(visits.contains(visit.getVisit()));
        assertTrue(visits.contains(anotherVisit.getVisit()));

    }

    @Test
    public void test_MergePatientsWithOverlappingVisits() throws Exception {

        Patient preferred = patientService.getPatient(7);
        Patient notPreferred = patientService.getPatient(8);

        service.mergePatients(preferred, notPreferred);

        List<Visit> visits = visitService.getVisitsByPatient(preferred);

        // sanity check
        assertThat(visits.size(), is(1));

        Set<Encounter> encounters = visits.get(0).getEncounters();
        assertThat(CollectionUtils.select(encounters, NON_VOIDED).size(), is(2));
    }

    @Test
    public void test_shouldCloseActiveVisits() throws Exception {

        ContextSensitiveMetadataTestUtils.setupDispositionDescriptor(conceptService, dispositionService);
        ContextSensitiveMetadataTestUtils.setupAdmissionDecisionConcept(conceptService, emrApiProperties);
        ContextSensitiveMetadataTestUtils.setupSupportsVisitLocationTag(locationService);

        Location location = locationService.getLocation(1);
        location.addTag(emrApiProperties.getSupportsVisitsLocationTag());
        locationService.saveLocation(location);

        // sanity check--visits in the standard test data set that are open
        assertThat(visitService.getVisit(1).getStopDatetime(), nullValue());
        assertThat(visitService.getVisit(2).getStopDatetime(), nullValue());
        assertThat(visitService.getVisit(3).getStopDatetime(), nullValue());
        assertThat(visitService.getVisit(4).getStopDatetime(), nullValue());
        assertThat(visitService.getVisit(5).getStopDatetime(), nullValue());
        assertThat(visitService.getVisit(6).getStopDatetime(), nullValue());

        service.closeInactiveVisits();

        // all these visits should now have stop datetime
        assertThat(visitService.getVisit(1).getStopDatetime(), notNullValue());
        assertThat(visitService.getVisit(2).getStopDatetime(), notNullValue());
        assertThat(visitService.getVisit(3).getStopDatetime(), notNullValue());
        assertThat(visitService.getVisit(4).getStopDatetime(), notNullValue());
        assertThat(visitService.getVisit(5).getStopDatetime(), notNullValue());

        // should ignore voided visits
        assertThat(visitService.getVisit(6).getStopDatetime(), nullValue());
    }

    @Test
    public void test_shouldNotCloseVisitIfMostRecentDispositionKeepsVisitOpen() throws Exception {

        ContextSensitiveMetadataTestUtils.setupDispositionDescriptor(conceptService, dispositionService);
        ContextSensitiveMetadataTestUtils.setupAdmissionDecisionConcept(conceptService, emrApiProperties);
        ContextSensitiveMetadataTestUtils.setupSupportsVisitLocationTag(locationService);

        Patient patient = patientService.getPatient(7); // patient already has one visit in test dataset

        // need o tag the unknown location so we don't run into an error when testing against the existing visits in the test dataset
        Location unknownLocation = locationService.getLocation(1);
        unknownLocation.addTag(emrApiProperties.getSupportsVisitsLocationTag());
        locationService.saveLocation(unknownLocation);

        Location location = locationService.getLocation(2);
        location.addTag(emrApiProperties.getSupportsVisitsLocationTag());
        locationService.saveLocation(location);

        Visit visit = new Visit();
        visit.setStartDatetime(DateUtils.addHours(new Date(), -14));
        visit.setPatient(patient);
        visit.setLocation(location);
        visit.setVisitType(emrApiProperties.getAtFacilityVisitType());

        // create an encounter with a disposition obs
        Encounter encounter = new Encounter();
        encounter.setPatient(patient);
        encounter.setEncounterType(encounterService.getEncounterType(1));
        encounter.setEncounterDatetime(visit.getStartDatetime());

        Obs dispositionObsGroup = new Obs();
        dispositionObsGroup.setConcept(dispositionService.getDispositionDescriptor().getDispositionSetConcept());
        Obs dispositionObs = new Obs();
        dispositionObs.setConcept(dispositionService.getDispositionDescriptor().getDispositionConcept());
        dispositionObs.setValueCoded(
                emrConceptService.getConcept(EmrApiConstants.EMR_CONCEPT_SOURCE_NAME + ":Admit to hospital")); // this fake code is set in ContextSensitiveMetadataTestUtils
        dispositionObsGroup.addGroupMember(dispositionObs);

        encounter.addObs(dispositionObsGroup);
        encounterService.saveEncounter(encounter);

        visit.addEncounter(encounter);
        visitService.saveVisit(visit);

        VisitDomainWrapper activeVisit = service.getActiveVisit(patient, location);

        // sanity check
        assertNotNull(activeVisit);

        service.closeInactiveVisits();

        activeVisit = service.getActiveVisit(patient, location);
        assertNotNull(activeVisit);
    }

    /**
     * I'm sure there's a standard matcher for this, but sometimes we run into bugs comparing a Date to a java.sql.Timestamp
     * @param expected
     * @return a matcher that checks whether a given date is on or after 'expected'
     */
    private Matcher<? super Date> onOrAfter(final Date expected) {
        return new BaseMatcher<Date>() {
            @Override
            public boolean matches(Object item) {
                Date actual = (Date) item;
                return actual.getTime() >= expected.getTime();
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("on or after " + expected);
            }
        };
    }

}