OrderUtil.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.order;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Concept;
import org.openmrs.ConceptSet;
import org.openmrs.Drug;
import org.openmrs.DrugOrder;
import org.openmrs.Patient;
import org.openmrs.api.APIException;
import org.openmrs.api.OrderService;
import org.openmrs.api.context.Context;
import org.openmrs.api.context.ContextAuthenticationException;
import org.openmrs.util.OpenmrsUtil;

/**
 * Contains convenience methods for working with Orders.
 * 
 * @deprecated get rid of this before we merge back to trunk
 */
@Deprecated
public class OrderUtil {
	
	private static final Log log = LogFactory.getLog(OrderUtil.class);
	
	/**
	 * Discontinues all current orders for the given <code>patient</code>
	 * 
	 * @param patient
	 * @param discontinueReason
	 * @param discontinueDate
	 * @see OrderService#discontinueOrder(org.openmrs.Order, Concept, Date)
	 * @should discontinue all orders for the given patient if none are yet discontinued
	 * @should not affect orders that were already discontinued on the specified date
	 * @should not affect orders that end before the specified date
	 * @should not affect orders that start after the specified date
	 */
	public static void discontinueAllOrders(Patient patient, Concept discontinueReason, Date discontinueDate) {
		if (log.isDebugEnabled())
			log.debug("In discontinueAll with patient " + patient + " and concept " + discontinueReason + " and date "
			        + discontinueDate);
		
		OrderService orderService = Context.getOrderService();
		//Shouldn't the type parameter value be Order
		List<DrugOrder> drugOrders = orderService.getDrugOrdersByPatient(patient);
		
		// loop over all of this patient's drug orders to discontinue each
		if (drugOrders != null) {
			for (DrugOrder drugOrder : drugOrders) {
				if (drugOrder.getDiscontinued())
					continue;
				
				if (log.isDebugEnabled())
					log.debug("discontinuing order: " + drugOrder);
				// do the stuff to the database
				orderService.discontinueOrder(drugOrder, discontinueReason.getName().getName(), null, discontinueDate);
			}
		}
	}
	
	/**
	 * Void all DrugOrders for drugs whose concepts are in the given set, and that have the given
	 * status. An end-user would think of this method as "delete all drug orders of the given type".
	 * 
	 * @param patient
	 * @param drugSetId
	 * @param voidReason
	 * @should void all drug orders of the given type when status is null
	 * @should void drug orders of the given type for status of CURRENT
	 * @should void drug orders of the given type for status of COMPLETE
	 * @should not affect drug orders that are already voided
	 */
	public static void voidDrugSet(Patient patient, String drugSetId, String voidReason) {
		if (log.isDebugEnabled())
			log.debug("Voiding drug sets for patient: " + patient + " drugSetId: " + drugSetId + " reason: " + voidReason);
		
		// do some null pointer checks
		
		if (Context.isAuthenticated() == false)
			throw new ContextAuthenticationException("Unable to void drugs when no one is logged in");
		
		if (patient == null)
			throw new APIException("Unable to void drugs without a patient being given");
		
		if (drugSetId == null)
			throw new APIException("Unable to void drugs without a drugSetId being given");
		
		OrderService orderService = Context.getOrderService();
		
		List<DrugOrder> currentOrders = orderService.getDrugOrdersByPatient(patient);
		
		Map<String, List<DrugOrder>> ordersBySetId = getDrugSetsByDrugSetIdList(currentOrders, drugSetId, ",");
		
		// loop over the orders and mark each as void in the database
		if (ordersBySetId != null) {
			List<DrugOrder> ordersToVoid = ordersBySetId.get(drugSetId);
			if (ordersToVoid != null) {
				for (DrugOrder order : ordersToVoid) {
					orderService.voidOrder(order, voidReason);
				}
			}
		}
	}
	
	/**
	 * Discontinue orders for the given patient with the given drug sets ...
	 * 
	 * @param patient
	 * @param drugSetId
	 * @param discontinueReason
	 * @param discontinueDate
	 * @should discontinue all orders of the given type for the given patient if none are yet
	 *         discontinued
	 * @should not affect orders that were already discontinued on the specified date
	 * @should not affect orders that end before the specified date
	 * @should not affect orders that start after the specified date
	 */
	public static void discontinueDrugSet(Patient patient, String drugSetId, Concept discontinueReason, Date discontinueDate) {
		if (log.isDebugEnabled()) {
			log.debug("Discontinuing drug sets. patient: " + patient + " drugSetId: " + drugSetId + " reason: "
			        + discontinueReason + " date: " + discontinueDate);
		}
		
		// do some null pointer checks
		
		if (Context.isAuthenticated() == false)
			throw new ContextAuthenticationException("Unable to discontinue drugs when no one is logged in");
		
		if (patient == null)
			throw new APIException("Unable to discontinue drugs without a patient being given");
		
		if (drugSetId == null)
			throw new APIException("Unable to discontinue drugs without a drugSetId being given");
		
		OrderService orderService = Context.getOrderService();
		
		List<DrugOrder> currentOrders = orderService.getDrugOrdersByPatient(patient);
		Map<String, List<DrugOrder>> ordersBySetId = getDrugSetsByDrugSetIdList(currentOrders, drugSetId, ",");
		
		// loop over all of the orders and discontinue each of them
		if (ordersBySetId != null) {
			List<DrugOrder> ordersToDiscontinue = ordersBySetId.get(drugSetId);
			if (ordersToDiscontinue != null) {
				for (DrugOrder order : ordersToDiscontinue) {
					orderService.discontinueOrder(order, discontinueReason.getName().getName(), null, discontinueDate);
				}
			} else {
				log.debug("no orders to discontinue");
			}
		} else {
			log.debug("no ordersBySetId returned for " + drugSetId);
		}
		
	}
	
	/**
	 * Associates the concept id of a drug set to a name of the drug set in the current locale
	 * 
	 * @param drugSetIds a comma separated list with the concept id of the drug sets
	 * @return <code>Map<String, String></code> of the drug headers for the given drugSetIds
	 * @should get map from concept id as string to concept name
	 */
	public static Map<String, String> getDrugSetHeadersByDrugSetIdList(String drugSetIds) {
		Map<String, String> ret = null;
		
		if (drugSetIds == null)
			throw new APIException("Unable to get drug headers without drugSetIds being given");
		
		Map<String, Concept> concepts = OpenmrsUtil.delimitedStringToConceptMap(drugSetIds, ",");
		if (concepts != null) {
			for (Map.Entry<String, Concept> e : concepts.entrySet()) {
				String id = e.getKey();
				Concept concept = e.getValue();
				if (ret == null)
					ret = new HashMap<String, String>();
				ret.put(id, concept.getName(Context.getLocale()).getName());
			}
		}
		
		return ret;
	}
	
	/**
	 * Gets a map of DrugOrders that belong to a DrugSet concept ID
	 * 
	 * @param orderList the Drug Order list
	 * @param drugSetIdList a 'delimiter' separated list of drug sets
	 * @param delimiter the delimiter of drug sets (defaults to a comma if set to null)
	 * @return Map<String, List<DrugOrder>> of DrugOrders that belong to a DrugSet concept_id
	 * @should get a map from concept id as string to drug orders that belong to that drug set
	 * @should treat an asterisk as all other drugs
	 */
	public static Map<String, List<DrugOrder>> getDrugSetsByDrugSetIdList(List<DrugOrder> orderList, String drugSetIdList,
	        String delimiter) {
		if (log.isDebugEnabled()) {
			log.debug("in getdrugsetsbydrugsetidlist. orderlist: " + orderList + " drugsetidlist: " + drugSetIdList
			        + " delimiter: " + delimiter);
		}
		
		if (delimiter == null)
			delimiter = ",";
		
		Map<String, List<DrugOrder>> ret = null;
		
		if (drugSetIdList != null && orderList != null) {
			List<Concept> drugSetConcepts = new ArrayList<Concept>();
			boolean addOthers = false;
			Map<Concept, String> idToConceptMappings = new HashMap<Concept, String>();
			
			String[] drugSetIds = drugSetIdList.split(delimiter);
			log.debug("starting with " + drugSetIds.length + " items in comma-delimited list, and " + orderList.size()
			        + " orders that are " + orderList);
			for (String drugSetId : drugSetIds) {
				// go through and get all concepts for these drugSetIds - then we can call another method to get Map
				
				if ("*".equals(drugSetId)) {
					// add "other"
					addOthers = true;
				} else {
					Concept drugSetConcept = OpenmrsUtil.getConceptByIdOrName(drugSetId);
					
					if (drugSetConcept != null) {
						drugSetConcepts.add(drugSetConcept);
						idToConceptMappings.put(drugSetConcept, drugSetId);
						log.debug("added concept " + drugSetConcept.getName(Context.getLocale()) + ", and mapping to id "
						        + drugSetId);
					}
				}
			}
			
			// now we know what drugSet concepts to separate the orderList into
			
			// first, let's create a list of "others", starting with a full list that we remove from
			List<DrugOrder> otherOrders = null;
			if (addOthers)
				otherOrders = orderList;
			
			Map<Concept, List<DrugOrder>> ordersByConcepts = getDrugSetsByConcepts(orderList, drugSetConcepts);
			if (ordersByConcepts != null) {
				log.debug("obc is size " + ordersByConcepts.size());
				for (Map.Entry<Concept, List<DrugOrder>> e : ordersByConcepts.entrySet()) {
					Concept c = e.getKey();
					List<DrugOrder> orders = e.getValue();
					log.debug("found concept " + c.getName(Context.getLocale()) + ", and list is size " + orders.size()
					        + " and list is " + orders);
					if (addOthers && otherOrders != null) {
						otherOrders.removeAll(orders);
					}
					if (ret == null)
						ret = new HashMap<String, List<DrugOrder>>();
					log.debug("putting list of size " + orders.size() + " in string " + idToConceptMappings.get(c));
					ret.put(idToConceptMappings.get(c), orders);
				}
			}
			
			// add the "others" list to the Map
			if (addOthers && otherOrders != null) {
				if (ret == null)
					ret = new HashMap<String, List<DrugOrder>>();
				ret.put("*", otherOrders);
			}
		}
		
		return ret;
	}
	
	/**
	 * Splits the drug orders into sublists based on which drug set the order's drug belongs to
	 * 
	 * @param drugOrders List of drugOrders
	 * @param drugSets List of drugSets concept
	 * @return Map<Concept, List<DrugOrder>> of a sublist of drug orders mapped by the drug set
	 *         concept that they belong
	 * @throws APIException
	 * @should get a map from concept to drugs orders in that drug set
	 */
	public static Map<Concept, List<DrugOrder>> getDrugSetsByConcepts(List<DrugOrder> drugOrders, List<Concept> drugSets)
	        throws APIException {
		if (log.isDebugEnabled()) {
			log.debug("in getdrugsetsbyconcepts. drugOrders: " + drugOrders + " drugSets: " + drugSets);
		}
		
		Map<Concept, List<DrugOrder>> hmRet = null;
		
		if (drugSets != null && drugOrders != null) {
			log.debug("drugSets is size " + drugSets.size());
			for (Concept c : drugSets) {
				List<DrugOrder> ordersForConcept = new ArrayList<DrugOrder>();
				
				Collection<ConceptSet> relatedConcepts = c.getConceptSets();
				log.debug("Concept is " + c.getName(Context.getLocale()) + " and has " + relatedConcepts.size()
				        + " related concepts");
				
				// now we have as a list, let's iterate
				if (relatedConcepts != null) {
					for (ConceptSet cs : relatedConcepts) {
						Concept csConcept = cs.getConcept();
						for (DrugOrder currOrder : drugOrders) {
							Drug currDrug = currOrder.getDrug();
							if (currDrug != null) {
								Concept currConcept = currDrug.getConcept(); // must not be null - ordained by data model
								if (currConcept.equals(csConcept)) {
									ordersForConcept.add(currOrder);
									log.debug("just added an order for " + currDrug.getName() + " to list of "
									        + c.getName(Context.getLocale()));
								}
							}
						}
					}
				}
				
				if (ordersForConcept.size() > 0) {
					if (hmRet == null)
						hmRet = new HashMap<Concept, List<DrugOrder>>();
					hmRet.put(c, ordersForConcept);
					log.debug("Concept " + c.getName(Context.getLocale()) + " was put to the map with a list of size "
					        + ordersForConcept.size());
				}
			}
		} else
			log.debug("drugSets is null");
		
		return hmRet;
	}
	
}