/** * 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.api; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; import org.openmrs.Cohort; import org.openmrs.Concept; import org.openmrs.ConceptName; import org.openmrs.Encounter; import org.openmrs.Location; import org.openmrs.MimeType; import org.openmrs.Obs; import org.openmrs.Person; import org.openmrs.annotation.Authorized; import org.openmrs.api.db.ObsDAO; import org.openmrs.obs.ComplexObsHandler; import org.openmrs.util.OpenmrsConstants; import org.openmrs.util.OpenmrsConstants.PERSON_TYPE; import org.openmrs.util.PrivilegeConstants; import org.springframework.transaction.annotation.Transactional; /** * The ObsService deals with saving and getting Obs to/from the database Usage: * * <pre> * ObsService obsService = Context.getObsService(); * * // get the obs for patient with internal identifier of 1235 * List<Obs> someObsList = obsService.getObservationsByPerson(new Patient(1235)); * </pre> * * There are also a number of convenience methods for extracting obs pertaining to certain Concepts, * people, or encounters * * @see org.openmrs.Obs * @see org.openmrs.ComplexObs * @see org.openmrs.MimeType * @see org.openmrs.api.context.Context */ @Transactional public interface ObsService extends OpenmrsService { /** * @see org.openmrs.util.OpenmrsConstants * @deprecated Use org.openmrs.util.OpenmrsConstants#PERSON_TYPE.PATIENT */ @Deprecated public static final Integer PERSON = 1; /** * @see org.openmrs.util.OpenmrsConstants * @deprecated Use OpenmrsConstants#PERSON_TYPE.PATIENT */ @Deprecated public static final Integer PATIENT = 2; /** * @see org.openmrs.util.OpenmrsConstants * @deprecated Use OpenmrsConstants.PERSON_TYPE.USER */ @Deprecated public static final Integer USER = 4; /** * Set the given <code>dao</code> on this obs service. The dao will act as the conduit through * with all obs calls get to the database * * @param dao specific ObsDAO to use for this service */ public void setObsDAO(ObsDAO dao); /** * Create an observation * * @param obs * @throws APIException * @deprecated use {@link #saveObs(Obs, String)} */ @Deprecated @Authorized(PrivilegeConstants.ADD_OBS) public void createObs(Obs obs) throws APIException; /** * Create a grouping of observations (observations linked by * {@link org.openmrs.Obs#getObsGroupId()} The proper use is: * * <pre> * Obs obsGroup = new Obs(); * for (Obs member : obs) { * obsGroup.addGroupMember(obs); * } * pass obsGroup to {@link #createObs(Obs)} * </pre> * * @param obs - array of observations to be grouped * @throws APIException * @deprecated This method should no longer need to be called on the api. This was meant as * temporary until we created a true ObsGroup pojo. Replaced by * {@link #saveObs(Obs, String)} */ @Deprecated @Authorized(PrivilegeConstants.ADD_OBS) public void createObsGroup(Obs[] obs) throws APIException; /** * Get an observation * * @param obsId integer obsId of observation desired * @return matching Obs * @throws APIException * @should get obs matching given obsId */ @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public Obs getObs(Integer obsId) throws APIException; /** * Get Obs by its UUID * * @param uuid * @return * @should find object given valid uuid * @should return null if no object found with given uuid */ @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public Obs getObsByUuid(String uuid) throws APIException; /** * Save changes to observation * * @param obs * @throws APIException * @deprecated use {@link #saveObs(Obs, String)} */ @Deprecated @Authorized(PrivilegeConstants.EDIT_OBS) public void updateObs(Obs obs) throws APIException; /** * Save the given obs to the database. This will move the contents of the given <code>obs</code> * to the database. This acts as both the initial save and an update kind of save. The returned * obs will be the same as the obs passed in. It is included for chaining. If this is an initial * save, the obsId on the given <code>obs</code> object will be updated to reflect the auto * numbering from the database. The obsId on the returned obs will also have this number. If * there is already an obsId on the given <code>obs</code> object, the given obs will be voided * and a new row in the database will be created that has a new obs id. * * @param obs the Obs to save to the database * @param changeMessage String explaining why <code>obs</code> is being changed. If * <code>obs</code> is a new obs, changeMessage is nullable, or if it is being * updated, it would be required * @return Obs that was saved to the database * @throws APIException * @should create new file from complex data for new obs * @should not overwrite file when updating a complex obs * @should void the given obs in the database * @should create very basic obs and add new obsId * @should allow changing of every property on obs * @should return a different object when updating an obs * @should set creator and dateCreated on new obs * @should cascade save to child obs groups * @should cascade update to new child obs groups */ @Authorized( { PrivilegeConstants.ADD_OBS, PrivilegeConstants.EDIT_OBS }) public Obs saveObs(Obs obs, String changeMessage) throws APIException; /** * Equivalent to deleting an observation * * @param obs Obs to void * @param reason String reason it's being voided * @throws APIException * @should set voided bit on given obs * @should fail if reason parameter is empty */ @Authorized(PrivilegeConstants.EDIT_OBS) public Obs voidObs(Obs obs, String reason) throws APIException; /** * Revive an observation (pull a Lazarus) * * @param obs Obs to unvoid * @throws APIException * @should unset voided bit on given obs * @should cascade unvoid to child grouped obs */ @Authorized(PrivilegeConstants.EDIT_OBS) public Obs unvoidObs(Obs obs) throws APIException; /** * This method shouldn't be used. Use either {@link #purgeObs(Obs)} or * {@link #voidObs(Obs,String)} * * @param obs * @throws APIException * @deprecated use #purgeObs(Obs) */ @Deprecated @Authorized(PrivilegeConstants.DELETE_OBS) public void deleteObs(Obs obs) throws APIException; /** * Completely remove an observation from the database. This should typically not be called * because we don't want to ever lose data. The data really <i>should</i> be voided and then it * is not seen in interface any longer (see #voidObs(Obs) for that one) If other things link to * this obs, an error will be thrown. * * @param obs * @throws APIException * @see #purgeObs(Obs, boolean) * @should delete the given obs from the database */ @Authorized(PrivilegeConstants.DELETE_OBS) public void purgeObs(Obs obs) throws APIException; /** * Completely remove an observation from the database. This should typically not be called * because we don't want to ever lose data. The data really <i>should</i> be voided and then it * is not seen in interface any longer (see #voidObs(Obs) for that one) * * @param obs the observation to remove from the database * @param cascade true/false whether or not to cascade down to other things that link to this * observation (like Orders and ObsGroups) * @throws APIException * @see #purgeObs(Obs, boolean) * @should throw APIException if given true cascade */ @Authorized(PrivilegeConstants.DELETE_OBS) public void purgeObs(Obs obs, boolean cascade) throws APIException; /** * @deprecated use {@link #getAllMimeTypes()} */ @Deprecated @Transactional(readOnly = true) @Authorized(OpenmrsConstants.PRIV_VIEW_MIME_TYPES) public List<MimeType> getMimeTypes() throws APIException; /** * Gets all mime types (including retired ones) * * @return list of MimeTypes in the system * @see #getAllMimeTypes(boolean) * @throws APIException * @deprecated MimeTypes are no longer used. See ConceptComplex and its use of handlers */ @Deprecated @Authorized(OpenmrsConstants.PRIV_VIEW_MIME_TYPES) public List<MimeType> getAllMimeTypes() throws APIException; /** * Gets all mime types and disregards the retired ones if <code>includeRetired</code> is true * * @param includeRetired true/false of whether to also return the retired ones * @return list of MimeTypes lll * @throws APIException * @deprecated MimeTypes are no longer used. See ConceptComplex and its use of handlers */ @Deprecated @Authorized(OpenmrsConstants.PRIV_VIEW_MIME_TYPES) public List<MimeType> getAllMimeTypes(boolean includeRetired) throws APIException; /** * Get mimeType by internal identifier * * @param mimeTypeId * @return mimeType with given internal identifier * @throws APIException * @deprecated MimeTypes are no longer used. See ConceptComplex and its use of handlers */ @Deprecated @Transactional(readOnly = true) @Authorized(OpenmrsConstants.PRIV_VIEW_MIME_TYPES) public MimeType getMimeType(Integer mimeTypeId) throws APIException; /** * Save the given <code>mimeType</code> to the database. If mimeType is not null, the mimeType * is updated in the database. If mimeType is null, a new mimeType is added to the database * * @param mimeType mimeType * @return mimeType that was saved/updated in the database * @throws APIException * @deprecated MimeTypes are no longer used. See ConceptComplex and its use of handlers */ @Deprecated @Authorized(OpenmrsConstants.PRIV_MANAGE_MIME_TYPES) public MimeType saveMimeType(MimeType mimeType) throws APIException; /** * This effectively removes the given mimeType from the system. Voided mimeTypes are still * linked to from complexObs, they just aren't shown in the list of available mimeTypes * * @param mimeType the MimeType to remove * @param reason the reason this mimeType is being voided * @return the voided MimeType * @throws APIException * @see #createObs(Obs) * @deprecated MimeTypes are no longer used. See ConceptComplex and its use of handlers */ @Deprecated @Authorized(OpenmrsConstants.PRIV_MANAGE_MIME_TYPES) public MimeType voidMimeType(MimeType mimeType, String reason) throws APIException; /** * This completely removes the given <code>MimeType</code> from the database. If data has been * stored already that points at this mimeType an exception is thrown * * @param mimeType the MimeType to remove * @throws APIException * @see #purgeMimeType(MimeType) * @deprecated MimeTypes are no longer used. See {@link org.openmrs.ConceptComplex} and its use * of handlers */ @Deprecated @Authorized(OpenmrsConstants.PRIV_PURGE_MIME_TYPES) public void purgeMimeType(MimeType mimeType) throws APIException; /** * @deprecated use {@link #getObservationsByPerson(Person)} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public Set<Obs> getObservations(Person who, boolean includeVoided); /** * Get all Observations for the given person, sorted by obsDatetime ascending. Does not return * voided observations. * * @param who the user to match on * @return a List<Obs> object containing all non-voided observations for the specified person * @see #getObservations(List, List, List, List, List, List, List, Integer, Integer, Date, Date, * boolean) * @should get all observations assigned to given person */ @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> getObservationsByPerson(Person who); /** * This method fetches observations according to the criteria in the given arguments. All * arguments are optional and nullable. If more than one argument is non-null, the result is * equivalent to an "and"ing of the arguments. (e.g. if both a <code>location</code> and a * <code>fromDate</code> are given, only Obs that are <u>both</u> at that Location and after the * fromDate are returned). <br/> * <br/> * Note: If <code>whom</code> has elements, <code>personType</code> is ignored <br/> * <br/> * Note: to get all observations on a certain date, use:<br/> * Date fromDate = "2009-08-15";<br/> * Date toDate = OpenmrsUtil.lastSecondOfDate(fromDate); List<Obs> obs = getObservations(...., * fromDate, toDate, ...); * * @param whom List<Person> to restrict obs to (optional) * @param encounters List<Encounter> to restrict obs to (optional) * @param questions List<Concept> to restrict the obs to (optional) * @param answers List<Concept> to restrict the valueCoded to (optional) * @param personTypes List<PERSON_TYPE> objects to restrict this to. Only used if * <code>whom</code> is an empty list (optional) * @param locations The org.openmrs.Location objects to restrict to (optional) * @param sort list of column names to sort on (obsId, obsDatetime, etc) if null, defaults to * obsDatetime (optional) * @param mostRecentN restrict the number of obs returned to this size (optional) * @param obsGroupId the Obs.getObsGroupId() to this integer (optional) * @param fromDate the earliest Obs date to get (optional) * @param toDate the latest Obs date to get (optional) * @param includeVoidedObs true/false whether to also include the voided obs (required) * @return list of Observations that match all of the criteria given in the arguments * @throws APIException * @should compare dates using lte and gte * @should get all obs assigned to given encounters * @should get all obs with question concept in given questions parameter * @should get all obs with answer concept in given answers parameter * @should return all obs whose person is a person only * @should return obs whose person is a patient only * @should return obs whose person is a user only * @should return obs with location in given locations parameter * @should sort returned obs by obsDatetime if sort is empty * @should sort returned obs by conceptId if sort is concept * @should limit number of obs returned to mostReturnN parameter * @should return obs whose groupId is given obsGroupId * @should not include voided obs * @should include voided obs if includeVoidedObs is true */ @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> getObservations(List<Person> whom, List<Encounter> encounters, List<Concept> questions, List<Concept> answers, List<PERSON_TYPE> personTypes, List<Location> locations, List<String> sort, Integer mostRecentN, Integer obsGroupId, Date fromDate, Date toDate, boolean includeVoidedObs) throws APIException; /** * This method fetches the count of observations according to the criteria in the given * arguments. All arguments are optional and nullable. If more than one argument is non-null, * the result is equivalent to an "and"ing of the arguments. (e.g. if both a * <code>location</code> and a <code>fromDate</code> are given, only Obs that are <u>both</u> at * that Location and after the fromDate are returned). <br/> * <br/> * Note: If <code>whom</code> has elements, <code>personType</code> is ignored <br/> * <br/> * Note: to get all observations count on a certain date, use:<br/> * Date fromDate = "2009-08-15";<br/> * Date toDate = OpenmrsUtil.lastSecondOfDate(fromDate); List<Obs> obs = getObservations(...., * fromDate, toDate, ...); * * @param whom List<Person> to restrict obs to (optional) * @param encounters List<Encounter> to restrict obs to (optional) * @param questions List<Concept> to restrict the obs to (optional) * @param answers List<Concept> to restrict the valueCoded to (optional) * @param personTypes List<PERSON_TYPE> objects to restrict this to. Only used if * <code>whom</code> is an empty list (optional) * @param locations The org.openmrs.Location objects to restrict to (optional) obsDatetime * (optional) * @param obsGroupId the Obs.getObsGroupId() to this integer (optional) * @param fromDate the earliest Obs date to get (optional) * @param toDate the latest Obs date to get (optional) * @param includeVoidedObs true/false whether to also include the voided obs (required) * @return list of Observations that match all of the criteria given in the arguments * @throws APIException * @should compare dates using lte and gte * @should get the count of all obs assigned to given encounters * @should get the count of all obs with question concept in given questions parameter * @should get the count of all obs with answer concept in given answers parameter * @should return the count of all obs whose person is a person only * @should return the count of all obs whose person is a patient only * @should return the count of obs whose person is a user only * @should return the count of obs with location in given locations parameter * @should return the count of obs whose groupId is given obsGroupId * @should not include count of voided obs * @should include count of voided obs if includeVoidedObs is true */ @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public Integer getObservationCount(List<Person> whom, List<Encounter> encounters, List<Concept> questions, List<Concept> answers, List<PERSON_TYPE> personTypes, List<Location> locations, Integer obsGroupId, Date fromDate, Date toDate, boolean includeVoidedObs) throws APIException; /** * This method searches the obs table based on the given <code>searchString</code>. * * @param searchString The string to search on * @return observations matching the given string * @throws APIException * @should get obs matching patient identifier in searchString * @should get obs matching encounterId in searchString * @should get obs matching obsId in searchString */ @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> getObservations(String searchString) throws APIException; /** * @deprecated use * {@link #getObservations(List, List, List, List, List, List, List, Integer, Integer, Date, Date, boolean)} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> getObservations(Concept c, Location loc, String sort, Integer personType, boolean includeVoided); /** * @deprecated use {@link #getObservationsByPersonAndConcept(Person, Concept)} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public Set<Obs> getObservations(Person who, Concept question, boolean includeVoided); /** * Get all nonvoided observations for the given patient with the given concept as the question * concept (conceptId) * * @param who person to match on * @param question conceptId to match on * @return list of all nonvoided observations matching these criteria * @throws APIException * @see #getObservations(List, List, List, List, List, List, List, Integer, Integer, Date, Date, * boolean) * @should get observations matching person and question * @should not fail with null person parameter */ @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> getObservationsByPersonAndConcept(Person who, Concept question) throws APIException; /** * @deprecated use * {@link #getObservations(List, List, List, List, List, List, List, Integer, Integer, Date, Date, boolean)} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> getLastNObservations(Integer n, Person who, Concept question, boolean includeVoided); /** * @deprecated use * {@link #getObservations(List, List, List, List, List, List, List, Integer, Integer, Date, Date, boolean)} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> getObservations(Concept question, String sort, Integer personType, boolean includeVoided); /** * @deprecated use * {@link #getObservations(List, List, List, List, List, List, List, Integer, Integer, Date, Date, boolean)} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> getObservationsAnsweredByConcept(Concept answer, Integer personType, boolean includeVoided); /** * Return all numeric answer values for the given concept ordered by value numeric low to high * personType should be one of PATIENT, PERSON, or USER; * * @param answer * @param sortByValue true/false if sorting by valueNumeric. If false, will sort by obsDatetime * @param personType * @deprecated use * {@link #getObservations(List, List, List, List, List, List, List, Integer, Integer, Date, Date, boolean)} * @return List<Object[]> [0]=<code>obsId</code>, [1]=<code>obsDatetime</code>, [2]= * <code>valueNumeric</code>s **/ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Object[]> getNumericAnswersForConcept(Concept answer, Boolean sortByValue, Integer personType, boolean includeVoided); /** * @deprecated use {@link org.openmrs#Encounter.getObservations()} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public Set<Obs> getObservations(Encounter whichEncounter); /** * @deprecated use * {@link #getObservations(List, List, List, List, List, List, List, Integer, Integer, Date, Date, boolean)} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> getVoidedObservations(); /** * @deprecated use {@link #getObservations(String)} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> findObservations(String search, boolean includeVoided, Integer personType); /** * @deprecated should use {@link Obs#getGroupMembers()} or * {@link #getObservations(List, List, List, List, List, List, List, Integer, Integer, Date, Date, boolean)} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> findObsByGroupId(Integer obsGroupId); /** * @deprecated use * {@link #getObservations(List, List, List, List, List, List, List, Integer, Integer, Date, Date, boolean)} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> getObservations(List<Concept> concepts, Date fromDate, Date toDate, boolean includeVoided); /** * @deprecated use * {@link #getObservations(List, List, List, List, List, List, List, Integer, Integer, Date, Date, boolean)} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> getObservations(List<Concept> concepts, Date fromDate, Date toDate); /** * @deprecated use * {@link #getObservations(List, List, List, List, List, List, List, Integer, Integer, Date, Date, boolean)} */ @Deprecated @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public List<Obs> getObservations(Cohort patients, List<Concept> concepts, Date fromDate, Date toDate); /** * Get a complex observation. If obs.isComplex() is true, then returns an Obs with its * ComplexData. Otherwise returns a simple Obs. TODO: Possibly remove this method. It is * confusing because you may have a complexObs, but not want the complexData attached at the * time. Nevertheless, you may think that this method is necessary to use, when you could just * call getObs(). This will attach the complexData onto the obs. * * @param obsId * @return Obs with a ComplexData * @since 1.5 * @should fill in complex data object for complex obs * @should return normal obs for non complex obs * @should not fail with null view */ @Transactional(readOnly = true) @Authorized( { PrivilegeConstants.VIEW_OBS }) public Obs getComplexObs(Integer obsId, String view) throws APIException; /** * Get the ComplexObsHandler that has been registered with the given key * * @param key that has been registered with a handler class * @return Object representing the handler for the given key * @since 1.5 * @should get handler with matching key * @should have default image and text handlers registered by spring */ @Transactional(readOnly = true) public ComplexObsHandler getHandler(String key) throws APIException; /** * <u>Add</u> the given map to this service's handlers. This method registers each * ComplexObsHandler to this service. If the given String key exists, that handler is * overwritten with the given handler For most situations, this map is set via spring, see the * applicationContext-service.xml file to add more handlers. * * @param handlers Map of class to handler object * @throws APIException * @since 1.5 * @should override handlers with same key * @should add new handlers with new keys */ public void setHandlers(Map<String, ComplexObsHandler> handlers) throws APIException; /** * Gets the handlers map registered * * @return map of keys to handlers * @since 1.5 * @throws APIException * @should never return null */ @Transactional(readOnly = true) public Map<String, ComplexObsHandler> getHandlers() throws APIException; /** * Registers the given handler with the given key If the given String key exists, that handler * is overwritten with the given handler * * @param key the key name to use for this handler * @param handler the class to register with this key * @throws APIException * @since 1.5 * @should register handler with the given key */ public void registerHandler(String key, ComplexObsHandler handler) throws APIException; /** * Convenience method for {@link #registerHandler(String, ComplexObsHandler)} * * @param key the key name to use for this handler * @param handlerClass the class to register with this key * @throws APIException * @since 1.5 * @should load handler and register key */ public void registerHandler(String key, String handlerClass) throws APIException; /** * Remove the handler associated with the key from list of available handlers * * @param key the key of the handler to unregister * @since 1.5 * @should remove handler with matching key * @should not fail with invalid key */ public void removeHandler(String key) throws APIException; /** * Gets the number of observations(including voided ones) that are using the specified * conceptNames as valueCodedName answers * * @param conceptNames the conceptNames to be searched against * @param includeVoided whether voided observation should be included * @return The number of observations using the specified conceptNames as valueCodedNames * @should return the count of all observations using the specified conceptNames as answers * @should include voided observations using the specified conceptNames as answers * @should return zero if no observation is using any of the concepNames in the list * @since Version 1.7 */ @Transactional(readOnly = true) @Authorized(PrivilegeConstants.VIEW_OBS) public Integer getObservationCount(List<ConceptName> conceptNames, boolean includeVoided); }