Encounter.java
/**
* 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;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openmrs.annotation.AllowDirectAccess;
import org.openmrs.annotation.DisableHandlers;
import org.openmrs.api.context.Context;
import org.openmrs.api.handler.VoidHandler;
/**
* An Encounter represents one visit or interaction of a patient with a healthcare worker. Every
* encounter can have 0 to n Observations associated with it Every encounter can have 0 to n Orders
* associated with it The patientId attribute should be equal to patient.patientId and is only
* included this second time for performance increases on bulk calls.
*
* @see Obs
* @see Order
*/
public class Encounter extends BaseOpenmrsData implements java.io.Serializable {
public static final long serialVersionUID = 2L;
// Fields
private Integer encounterId;
private Date encounterDatetime;
private Patient patient;
private Integer patientId;
private Location location;
private Form form;
private EncounterType encounterType;
private Set<Order> orders;
@AllowDirectAccess
private Set<Obs> obs;
private Visit visit;
@DisableHandlers(handlerTypes = { VoidHandler.class })
private Set<EncounterProvider> encounterProviders = new LinkedHashSet<EncounterProvider>();
// Constructors
/** default constructor */
public Encounter() {
}
/**
* @param encounterId
* @should set encounter id
*/
public Encounter(Integer encounterId) {
this.encounterId = encounterId;
}
// Property accessors
/**
* @return Returns the encounterDatetime.
*/
public Date getEncounterDatetime() {
return encounterDatetime;
}
/**
* @param encounterDatetime The encounterDatetime to set.
*/
public void setEncounterDatetime(Date encounterDatetime) {
this.encounterDatetime = encounterDatetime;
}
/**
* @return Returns the encounterId.
*/
public Integer getEncounterId() {
return encounterId;
}
/**
* @param encounterId The encounterId to set.
*/
public void setEncounterId(Integer encounterId) {
this.encounterId = encounterId;
}
/**
* @return Returns the encounterType.
*/
public EncounterType getEncounterType() {
return encounterType;
}
/**
* @param encounterType The encounterType to set.
*/
public void setEncounterType(EncounterType encounterType) {
this.encounterType = encounterType;
}
/**
* @return Returns the location.
*/
public Location getLocation() {
return location;
}
/**
* @param location The location to set.
*/
public void setLocation(Location location) {
this.location = location;
}
/**
* @return Returns a Set<Obs> of all non-voided, non-obsGroup children Obs of this Encounter
* @should not return null with null obs set
* @should get obs
* @should not get voided obs
* @should only get child obs
* @should not get child obs if child also on encounter
* @should get both child and parent obs after removing child from parent grouping
* @should get obs with two levels of hierarchy
* @should get obs with three levels of hierarchy
* @should not get voided obs with three layers of hierarchy
*/
public Set<Obs> getObs() {
Set<Obs> ret = new HashSet<Obs>();
if (this.obs != null) {
for (Obs o : this.obs)
ret.addAll(getObsLeaves(o));
// this should be all thats needed unless the encounter has been built by hand
//if (o.isVoided() == false && o.isObsGrouping() == false)
// ret.add(o);
}
return ret;
}
/**
* Convenience method to recursively get all leaf obs of this encounter. This method goes down
* into each obs and adds all non-grouping obs to the return list
*
* @param obsParent current obs to loop over
* @return list of leaf obs
*/
private List<Obs> getObsLeaves(Obs obsParent) {
List<Obs> leaves = new ArrayList<Obs>();
if (obsParent.hasGroupMembers()) {
for (Obs child : obsParent.getGroupMembers()) {
if (child.isVoided() == false) {
if (child.isObsGrouping() == false)
leaves.add(child);
else
// recurse if this is a grouping obs
leaves.addAll(getObsLeaves(child));
}
}
} else if (obsParent.isVoided() == false) {
leaves.add(obsParent);
}
return leaves;
}
/**
* Returns all Obs where Obs.encounterId = Encounter.encounterId In practice, this method should
* not be used very often...
*
* @param includeVoided specifies whether or not to include voided Obs
* @return Returns the all Obs.
* @should not return null with null obs set
* @should get obs
* @should get both parent and child obs
* @should get both parent and child with child directly on encounter
* @should get both child and parent obs after removing child from parent grouping
*/
public Set<Obs> getAllObs(boolean includeVoided) {
if (includeVoided && obs != null)
return obs;
Set<Obs> ret = new HashSet<Obs>();
if (this.obs != null) {
for (Obs o : this.obs) {
if (includeVoided)
ret.add(o);
else if (!o.isVoided())
ret.add(o);
}
}
return ret;
}
/**
* Convenience method to call {@link #getAllObs(boolean)} with a false parameter
*
* @return all non-voided obs
* @should not get voided obs
*/
public Set<Obs> getAllObs() {
return getAllObs(false);
}
/**
* Returns a Set<Obs> of all root-level Obs of an Encounter, including obsGroups
*
* @param includeVoided specifies whether or not to include voided Obs
* @return Returns all obs at top level -- will not be null
* @should not return null with null obs set
* @should get obs
* @should not get voided obs
* @should only get parents obs
* @should only return the grouped top level obs
* @should get both child and parent obs after removing child from parent grouping
*/
public Set<Obs> getObsAtTopLevel(boolean includeVoided) {
Set<Obs> ret = new HashSet<Obs>();
for (Obs o : getAllObs(includeVoided)) {
if (o.getObsGroup() == null)
ret.add(o);
}
return ret;
}
/**
* @param obs The obs to set.
*/
public void setObs(Set<Obs> obs) {
this.obs = obs;
}
/**
* Add the given Obs to the list of obs for this Encounter.
*
* @param observation the Obs to add to this encounter
* @should add obs with null values
* @should not fail with null obs
* @should set encounter attribute on obs
* @should add obs to non null initial obs set
* @should add encounter attrs to obs if attributes are null
* @should add encounter attrs to obs groupMembers if attributes are null
*/
public void addObs(Obs observation) {
if (obs == null)
obs = new HashSet<Obs>();
if (observation != null) {
obs.add(observation);
//Propagate some attributes to the obs and any groupMembers
// a Deque is a two-ended queue, that lets us add to the end, and fetch from the beginning
Deque<Obs> obsToUpdate = new ArrayDeque<Obs>();
obsToUpdate.add(observation);
//prevent infinite recursion if an obs is its own group member
Set<Obs> seenIt = new HashSet<Obs>();
while (!obsToUpdate.isEmpty()) {
Obs o = obsToUpdate.removeFirst();
//has this obs already been processed?
if (o == null || seenIt.contains(o))
continue;
seenIt.add(o);
o.setEncounter(this);
//if the attribute was already set, preserve it
//if not, inherit the values sfrom the encounter
if (o.getObsDatetime() == null)
o.setObsDatetime(getEncounterDatetime());
if (o.getPerson() == null)
o.setPerson(getPatient());
if (o.getLocation() == null)
o.setLocation(getLocation());
//propagate attributes to all group members as well
if (o.getGroupMembers(true) != null) {
obsToUpdate.addAll(o.getGroupMembers());
}
}
}
}
/**
* Remove the given observation from the list of obs for this Encounter
*
* @param observation
* @should remove obs successfully
* @should not throw error when removing null obs from empty set
* @should not throw error when removing null obs from non empty set
*/
public void removeObs(Obs observation) {
if (obs != null)
obs.remove(observation);
}
/**
* @return Returns the orders
*/
public Set<Order> getOrders() {
if (orders == null) {
orders = new LinkedHashSet<Order>();
}
return orders;
}
/**
* @param orders The orders to set.
*/
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
/**
* Add the given Order to the list of orders for this Encounter
*
* @param order
* @should add order with null values
* @should not fail with null obs passed to add order
* @should set encounter attribute
* @should add order to non null initial order set
* @should add order to encounter when adding order to set returned from getOrders
*/
public void addOrder(Order order) {
if (order != null) {
order.setEncounter(this);
getOrders().add(order);
}
}
/**
* Remove the given observation from the list of orders for this Encounter
*
* @param order
* @should remove order from encounter
* @should not fail when removing null order
* @should not fail when removing non existent order
*/
public void removeOrder(Order order) {
if (orders != null)
orders.remove(order);
}
/**
* @return Returns the patient.
*/
public Patient getPatient() {
return patient;
}
/**
* @param patient The patient to set.
*/
public void setPatient(Patient patient) {
this.patient = patient;
this.patientId = patient.getPersonId();
}
/**
* @return the patientId
* @deprecated due to duplication. Use Encounter.Patient instead
*/
@Deprecated
public Integer getPatientId() {
return patientId;
}
/**
* @param patientId the patientId to set
* @deprecated due to duplication. Use Encounter.Patient instead
*/
@Deprecated
public void setPatientId(Integer patientId) {
this.patientId = patientId;
}
/**
* Basic property accessor for encounterProviders. The convenience methods getProvidersByRoles
* and getProvidersByRole are the preferred methods for getting providers. This getter is
* provided as a convenience for treating this like a DTO
*
* @return list of all existing providers on this encounter
* @see #getProvidersByRole(EncounterRole)
* @see #getProvidersByRoles()
* @since 1.9.1
*/
public Set<EncounterProvider> getEncounterProviders() {
return encounterProviders;
}
/**
* Basic property setter for encounterProviders. The convenience methods addProvider,
* removeProvider, and setProvider are the preferred methods for adding/removing providers. This
* setter is provided as a convenience for treating this like a DTO
*
* @param encounterProviders the list of EncounterProvider objects to set. Overwrites list as
* normal setter is inclined to do
* @see #addProvider(EncounterRole, Provider)
* @see #removeProvider(EncounterRole, Provider)
* @see #setProvider(EncounterRole, Provider)
* @since 1.9.1
*/
public void setEncounterProviders(Set<EncounterProvider> encounterProviders) {
this.encounterProviders = encounterProviders;
}
/**
* @return Returns the provider.
* @since 1.6 (used to return User)
* @deprecated since 1.9, use {@link #getProvidersByRole(EncounterRole)}
* @should return null if there is no providers
* @should return provider for person
* @should return null if there is no provider for person
* @should return same provider for person if called twice
* @should not return a voided provider
*/
public Person getProvider() {
if (encounterProviders == null || encounterProviders.isEmpty()) {
return null;
} else {
for (EncounterProvider encounterProvider : encounterProviders) {
// Return the first non-voided provider associated with a person in the list
if (!encounterProvider.isVoided() && encounterProvider.getProvider().getPerson() != null) {
return encounterProvider.getProvider().getPerson();
}
}
}
return null;
}
/**
* @param provider The provider to set.
* @deprecated use {@link #setProvider(Person)}
*/
public void setProvider(User provider) {
setProvider(provider.getPerson());
}
/**
* @param provider The provider to set.
* @deprecated since 1.9, use {@link #setProvider(EncounterRole, Provider)}
* @should set existing provider for unknown role
*/
public void setProvider(Person provider) {
EncounterRole unknownRole = Context.getEncounterService().getEncounterRoleByUuid(
EncounterRole.UNKNOWN_ENCOUNTER_ROLE_UUID);
if (unknownRole == null) {
throw new IllegalStateException("No 'Unknown' encounter role with uuid "
+ EncounterRole.UNKNOWN_ENCOUNTER_ROLE_UUID + ".");
}
Collection<Provider> providers = Context.getProviderService().getProvidersByPerson(provider);
if (providers == null || providers.isEmpty()) {
throw new IllegalArgumentException("No provider with personId " + provider.getPersonId());
}
setProvider(unknownRole, providers.iterator().next());
}
/**
* @return Returns the form.
*/
public Form getForm() {
return form;
}
/**
* @param form The form to set.
*/
public void setForm(Form form) {
this.form = form;
}
/**
* @see java.lang.Object#toString()
* @should not fail with empty object
*/
@Override
public String toString() {
String ret = "";
ret += encounterId == null ? "(no ID) " : encounterId.toString() + " ";
ret += this.getEncounterDatetime() == null ? "(no Date) " : this.getEncounterDatetime().toString() + " ";
ret += this.getEncounterType() == null ? "(no Type) " : this.getEncounterType().getName() + " ";
ret += this.getLocation() == null ? "(no Location) " : this.getLocation().getName() + " ";
ret += this.getPatient() == null ? "(no Patient) " : this.getPatient().getPatientId().toString() + " ";
ret += this.getForm() == null ? "(no Form) " : this.getForm().getName() + " ";
ret += this.getObsAtTopLevel(false) == null ? "(no Obss) " : "num Obs: " + this.getObsAtTopLevel(false) + " ";
ret += this.getOrders() == null ? "(no Orders) " : "num Orders: " + this.getOrders().size() + " ";
return "Encounter: [" + ret + "]";
}
/**
* @since 1.5
* @see org.openmrs.OpenmrsObject#getId()
*/
public Integer getId() {
return getEncounterId();
}
/**
* @since 1.5
* @see org.openmrs.OpenmrsObject#setId(java.lang.Integer)
*/
public void setId(Integer id) {
setEncounterId(id);
}
/**
* Gets the visit.
*
* @return the visit.
* @since 1.9
*/
public Visit getVisit() {
return visit;
}
/**
* Sets the visit
*
* @param visit the visit to set.
* @since 1.9
*/
public void setVisit(Visit visit) {
this.visit = visit;
}
/**
* Gets all unvoided providers, grouped by role.
*
* @return map of unvoided providers keyed by roles
* @since 1.9
* @should return empty map if no unvoided providers
* @should return all roles and unvoided providers
*/
public Map<EncounterRole, Set<Provider>> getProvidersByRoles() {
return getProvidersByRoles(false);
}
/**
* Gets all providers, grouped by role.
*
* @param includeVoided set to true to include voided providers, else set to false
* @return map of providers keyed by roles
* @since 1.9
* @should return empty map if no providers
* @should return all roles and providers
*/
public Map<EncounterRole, Set<Provider>> getProvidersByRoles(boolean includeVoided) {
Map<EncounterRole, Set<Provider>> providers = new HashMap<EncounterRole, Set<Provider>>();
for (EncounterProvider encounterProvider : encounterProviders) {
if (!includeVoided && encounterProvider.getVoided()) {
continue;
}
Set<Provider> list = providers.get(encounterProvider.getEncounterRole());
if (list == null) {
list = new LinkedHashSet<Provider>();
providers.put(encounterProvider.getEncounterRole(), list);
}
list.add(encounterProvider.getProvider());
}
return providers;
}
/**
* Gets unvoided providers who had the given role in this encounter.
*
* @param role
* @return unvoided providers or empty set if none was found
* @since 1.9
* @should return unvoided providers for role
* @should return empty set for no role
* @should return empty set for null role
*/
public Set<Provider> getProvidersByRole(EncounterRole role) {
return getProvidersByRole(role, false);
}
/**
* Gets providers who had the given role in this encounter.
*
* @param role
* @param includeVoided set to true to include voided providers, else set to false
* @return providers or empty set if none was found
* @since 1.9
* @should return providers for role
* @should return empty set for no role
* @should return empty set for null role
*/
public Set<Provider> getProvidersByRole(EncounterRole role, boolean includeVoided) {
Set<Provider> providers = new LinkedHashSet<Provider>();
for (EncounterProvider encounterProvider : encounterProviders) {
if (encounterProvider.getEncounterRole().equals(role)) {
if (!includeVoided && encounterProvider.getVoided()) {
continue;
}
providers.add(encounterProvider.getProvider());
}
}
return providers;
}
/**
* Adds a new provider for the encounter, with the given role.
*
* @param role
* @param provider
* @since 1.9
* @should add provider for new role
* @should add second provider for role
* @should not add same provider twice for role
*/
public void addProvider(EncounterRole role, Provider provider) {
// first, make sure the provider isn't already there
for (EncounterProvider ep : encounterProviders) {
if (ep.getEncounterRole().equals(role) && ep.getProvider().equals(provider) && !ep.isVoided())
return;
}
EncounterProvider encounterProvider = new EncounterProvider();
encounterProvider.setEncounter(this);
encounterProvider.setEncounterRole(role);
encounterProvider.setProvider(provider);
encounterProvider.setDateCreated(new Date());
encounterProvider.setCreator(Context.getAuthenticatedUser());
encounterProviders.add(encounterProvider);
}
/**
* Sets the provider for the given role.
* <p>
* If the encounter already had any providers for the given role, those are removed.
*
* @param role
* @param provider
* @since 1.9
* @should set provider for new role
* @should clear providers and set provider for role
* @should void existing EncounterProvider
*/
public void setProvider(EncounterRole role, Provider provider) {
boolean hasProvider = false;
for (Iterator<EncounterProvider> it = encounterProviders.iterator(); it.hasNext();) {
EncounterProvider encounterProvider = it.next();
if (encounterProvider.getEncounterRole().equals(role)) {
if (!encounterProvider.getProvider().equals(provider)) {
encounterProvider.setVoided(true);
encounterProvider.setDateVoided(new Date());
encounterProvider.setVoidedBy(Context.getAuthenticatedUser());
} else if (!encounterProvider.isVoided()) {
hasProvider = true;
}
}
}
if (!hasProvider) {
addProvider(role, provider);
}
}
/**
* Removes the provider for a given role.
*
* @param role the role.
* @param provider the provider.
* @since 1.9
* @should void existing EncounterProvider
*/
public void removeProvider(EncounterRole role, Provider provider) {
for (EncounterProvider encounterProvider : encounterProviders) {
if (encounterProvider.getEncounterRole().equals(role) && encounterProvider.getProvider().equals(provider)) {
encounterProvider.setVoided(true);
encounterProvider.setDateVoided(new Date());
encounterProvider.setVoidedBy(Context.getAuthenticatedUser());
return;
}
}
}
}