Java tutorial
/** * 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.appointmentscheduling.api.impl; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.openmrs.module.appointmentscheduling.StudentT; import org.apache.commons.chain.web.MapEntry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openmrs.Location; import org.openmrs.Patient; import org.openmrs.PatientIdentifier; import org.openmrs.Provider; import org.openmrs.Visit; import org.openmrs.api.APIException; import org.openmrs.api.context.Context; import org.openmrs.api.impl.BaseOpenmrsService; import org.openmrs.module.appointmentscheduling.Appointment; import org.openmrs.module.appointmentscheduling.Appointment.AppointmentStatus; import org.openmrs.module.appointmentscheduling.AppointmentBlock; import org.openmrs.module.appointmentscheduling.AppointmentStatusHistory; import org.openmrs.module.appointmentscheduling.AppointmentType; import org.openmrs.module.appointmentscheduling.TimeSlot; import org.openmrs.module.appointmentscheduling.api.AppointmentService; import org.openmrs.module.appointmentscheduling.api.db.AppointmentBlockDAO; import org.openmrs.module.appointmentscheduling.api.db.AppointmentDAO; import org.openmrs.module.appointmentscheduling.api.db.AppointmentStatusHistoryDAO; import org.openmrs.module.appointmentscheduling.api.db.AppointmentTypeDAO; import org.openmrs.module.appointmentscheduling.api.db.TimeSlotDAO; import org.openmrs.validator.ValidateUtil; import org.springframework.transaction.annotation.Transactional; /** * It is a default implementation of {@link AppointmentService}. */ public class AppointmentServiceImpl extends BaseOpenmrsService implements AppointmentService { protected final Log log = LogFactory.getLog(this.getClass()); private AppointmentTypeDAO appointmentTypeDAO; private AppointmentBlockDAO appointmentBlockDAO; private AppointmentDAO appointmentDAO; private TimeSlotDAO timeSlotDAO; private AppointmentStatusHistoryDAO appointmentStatusHistoryDAO; /** * @param dao the appointment type dao to set */ public void setAppointmentTypeDAO(AppointmentTypeDAO appointmentTypeDAO) { this.appointmentTypeDAO = appointmentTypeDAO; } /** * @return the appointment type dao */ public AppointmentTypeDAO getAppointmentTypeDAO() { return appointmentTypeDAO; } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAllAppointmentTypes() */ @Transactional(readOnly = true) public Set<AppointmentType> getAllAppointmentTypes() { HashSet set = new HashSet(); set.addAll(getAppointmentTypeDAO().getAll()); return set; } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAllAppointmentTypes(boolean) */ @Override @Transactional(readOnly = true) public List<AppointmentType> getAllAppointmentTypes(boolean includeRetired) { return getAppointmentTypeDAO().getAll(includeRetired); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAppointmentType(java.lang.Integer) */ @Transactional(readOnly = true) public AppointmentType getAppointmentType(Integer appointmentTypeId) { return (AppointmentType) getAppointmentTypeDAO().getById(appointmentTypeId); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAppointmentTypeByUuid(java.lang.String) */ @Transactional(readOnly = true) public AppointmentType getAppointmentTypeByUuid(String uuid) { return (AppointmentType) getAppointmentTypeDAO().getByUuid(uuid); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAppointmentTypes(java.lang.String) */ @Transactional(readOnly = true) public List<AppointmentType> getAppointmentTypes(String fuzzySearchPhrase) { return getAppointmentTypeDAO().getAll(fuzzySearchPhrase); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#saveAppointmentType(org.openmrs.AppointmentType) */ public AppointmentType saveAppointmentType(AppointmentType appointmentType) throws APIException { ValidateUtil.validate(appointmentType); return (AppointmentType) getAppointmentTypeDAO().saveOrUpdate(appointmentType); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#retireAppointmentType(org.openmrs.AppointmentType, * java.lang.String) */ public AppointmentType retireAppointmentType(AppointmentType appointmentType, String reason) { return saveAppointmentType(appointmentType); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#unretireAppointmentType(org.openmrs.AppointmentType) */ public AppointmentType unretireAppointmentType(AppointmentType appointmentType) { return saveAppointmentType(appointmentType); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#purgeAppointmentType(org.openmrs.AppointmentType) */ public void purgeAppointmentType(AppointmentType appointmentType) { getAppointmentTypeDAO().delete(appointmentType); } //Appointment Block /** * @param dao the appointment block dao to set */ public void setAppointmentBlockDAO(AppointmentBlockDAO appointmentBlockDAO) { this.appointmentBlockDAO = appointmentBlockDAO; } /** * @return the appointment block dao */ public AppointmentBlockDAO getAppointmentBlockDAO() { return appointmentBlockDAO; } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAllAppointmentBlocks() */ @Transactional(readOnly = true) public List<AppointmentBlock> getAllAppointmentBlocks() { return getAppointmentBlockDAO().getAll(); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAllAppointmentBlocks(boolean) */ @Override @Transactional(readOnly = true) public List<AppointmentBlock> getAllAppointmentBlocks(boolean includeVoided) { return getAppointmentBlockDAO().getAllData(includeVoided); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAppointmentBlock(java.lang.Integer) */ @Transactional(readOnly = true) public AppointmentBlock getAppointmentBlock(Integer appointmentBlockId) { return (AppointmentBlock) getAppointmentBlockDAO().getById(appointmentBlockId); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAppointmentBlockByUuid(java.lang.String) */ @Transactional(readOnly = true) public AppointmentBlock getAppointmentBlockByUuid(String uuid) { return (AppointmentBlock) getAppointmentBlockDAO().getByUuid(uuid); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#saveAppointmentBlock(org.openmrs.AppointmentBlock) */ public AppointmentBlock saveAppointmentBlock(AppointmentBlock appointmentBlock) throws APIException { ValidateUtil.validate(appointmentBlock); return (AppointmentBlock) getAppointmentBlockDAO().saveOrUpdate(appointmentBlock); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#voidAppointmentBlock(org.openmrs.AppointmentBlock, * java.lang.String) */ public AppointmentBlock voidAppointmentBlock(AppointmentBlock appointmentBlock, String reason) { return saveAppointmentBlock(appointmentBlock); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#unvoidAppointmentBlock(org.openmrs.AppointmentBlock) */ public AppointmentBlock unvoidAppointmentBlock(AppointmentBlock appointmentBlock) { return saveAppointmentBlock(appointmentBlock); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#purgeAppointmentBlock(org.openmrs.AppointmentBlock) */ public void purgeAppointmentBlock(AppointmentBlock appointmentBlock) { getAppointmentBlockDAO().delete(appointmentBlock); } public void setAppointmentDAO(AppointmentDAO appointmentDAO) { this.appointmentDAO = appointmentDAO; } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAppointmentBlocks(java.util.Date,java.util.Date,java.util.String,org.openmrs.Provider,org.openmrs.AppointmentType) * ) */ @Transactional(readOnly = true) public List<AppointmentBlock> getAppointmentBlocks(Date fromDate, Date toDate, String locations, Provider provider, AppointmentType appointmentType) { return getAppointmentBlockDAO().getAppointmentBlocks(fromDate, toDate, locations, provider, appointmentType); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getOverlappingAppointmentBlocks(org.openmrs.AppointmentBlock) * ) */ @Transactional(readOnly = true) public List<AppointmentBlock> getOverlappingAppointmentBlocks(AppointmentBlock appointmentBlock) { return getAppointmentBlockDAO().getOverlappingAppointmentBlocks(appointmentBlock); } //Appointment /** * @return the appointment dao */ public AppointmentDAO getAppointmentDAO() { return appointmentDAO; } @Override @Transactional(readOnly = true) public List<Appointment> getAllAppointments() { return getAppointmentDAO().getAll(); } @Override @Transactional(readOnly = true) public List<Appointment> getAllAppointments(boolean includeVoided) { return getAppointmentDAO().getAllData(includeVoided); } @Override @Transactional(readOnly = true) public Appointment getAppointment(Integer appointmentId) { return (Appointment) getAppointmentDAO().getById(appointmentId); } @Override @Transactional(readOnly = true) public Appointment getAppointmentByUuid(String uuid) { return (Appointment) getAppointmentDAO().getByUuid(uuid); } @Override public Appointment saveAppointment(Appointment appointment) throws APIException { ValidateUtil.validate(appointment); return (Appointment) getAppointmentDAO().saveOrUpdate(appointment); } @Override public Appointment voidAppointment(Appointment appointment, String reason) { return saveAppointment(appointment); } @Override public Appointment unvoidAppointment(Appointment appointment) { return saveAppointment(appointment); } @Override public void purgeAppointment(Appointment appointment) { getAppointmentDAO().delete(appointment); } @Override @Transactional(readOnly = true) public List<Appointment> getAppointmentsOfPatient(Patient patient) { return getAppointmentDAO().getAppointmentsByPatient(patient); } @Override @Transactional(readOnly = true) public Appointment getAppointmentByVisit(Visit visit) { return getAppointmentDAO().getAppointmentByVisit(visit); } //TimeSlot /** * @param dao the time slot dao to set */ public void setTimeSlotDAO(TimeSlotDAO timeSlotDAO) { this.timeSlotDAO = timeSlotDAO; } /** * @return the time slot dao */ public TimeSlotDAO getTimeSlotDAO() { return timeSlotDAO; } @Override public TimeSlot saveTimeSlot(TimeSlot timeSlot) throws APIException { ValidateUtil.validate(timeSlot); return (TimeSlot) getTimeSlotDAO().saveOrUpdate(timeSlot); } @Override @Transactional(readOnly = true) public List<TimeSlot> getAllTimeSlots() { return getTimeSlotDAO().getAll(); } @Override @Transactional(readOnly = true) public List<TimeSlot> getAllTimeSlots(boolean includeVoided) { return getTimeSlotDAO().getAllData(includeVoided); } @Override @Transactional(readOnly = true) public TimeSlot getTimeSlot(Integer timeSlotId) { return (TimeSlot) getTimeSlotDAO().getById(timeSlotId); } @Override @Transactional(readOnly = true) public TimeSlot getTimeSlotByUuid(String uuid) { return (TimeSlot) getTimeSlotDAO().getByUuid(uuid); } @Override public TimeSlot voidTimeSlot(TimeSlot timeSlot, String reason) { return saveTimeSlot(timeSlot); } @Override public TimeSlot unvoidTimeSlot(TimeSlot timeSlot) { return saveTimeSlot(timeSlot); } @Override public void purgeTimeSlot(TimeSlot timeSlot) { getTimeSlotDAO().delete(timeSlot); } @Override public List<Appointment> getAppointmentsInTimeSlot(TimeSlot timeSlot) { return getTimeSlotDAO().getAppointmentsInTimeSlot(timeSlot); } @Override @Transactional(readOnly = true) public List<TimeSlot> getTimeSlotsInAppointmentBlock(AppointmentBlock appointmentBlock) { return getTimeSlotDAO().getTimeSlotsByAppointmentBlock(appointmentBlock); } //AppointmentStatusHistory /** * @param dao the appointment status history dao to set */ public void setAppointmentStatusHistoryDAO(AppointmentStatusHistoryDAO appointmentStatusHistoryDAO) { this.appointmentStatusHistoryDAO = appointmentStatusHistoryDAO; } /** * @return the appointment status dao */ public AppointmentStatusHistoryDAO getAppointmentStatusHistoryDAO() { return appointmentStatusHistoryDAO; } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAllAppointmentStatusHistories() */ @Transactional(readOnly = true) public List<AppointmentStatusHistory> getAllAppointmentStatusHistories() { return getAppointmentStatusHistoryDAO().getAll(); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAppointmentStatusHistory(java.lang.Integer) */ @Transactional(readOnly = true) public AppointmentStatusHistory getAppointmentStatusHistory(Integer appointmentStatusHistoryId) { return (AppointmentStatusHistory) getAppointmentStatusHistoryDAO().getById(appointmentStatusHistoryId); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#getAppointmentStatusHistories(java.lang.String) */ @Transactional(readOnly = true) public List<AppointmentStatusHistory> getAppointmentStatusHistories(AppointmentStatus status) { return getAppointmentStatusHistoryDAO().getAll(status); } /** * @see org.openmrs.module.appointmentscheduling.api.AppointmentService#saveAppointmentStatusHistory(org.openmrs.AppointmentStatusHistory) */ public AppointmentStatusHistory saveAppointmentStatusHistory(AppointmentStatusHistory appointmentStatusHistory) throws APIException { ValidateUtil.validate(appointmentStatusHistory); return (AppointmentStatusHistory) getAppointmentStatusHistoryDAO().saveOrUpdate(appointmentStatusHistory); } @Override @Transactional(readOnly = true) public Appointment getLastAppointment(Patient patient) { return getAppointmentDAO().getLastAppointment(patient); } @Override @Transactional(readOnly = true) public List<TimeSlot> getTimeSlotsByConstraints(AppointmentType appointmentType, Date fromDate, Date toDate, Provider provider, Location location) throws APIException { List<TimeSlot> suitableTimeSlots = getTimeSlotsByConstraintsIncludingFull(appointmentType, fromDate, toDate, provider, location); List<TimeSlot> availableTimeSlots = new LinkedList<TimeSlot>(); Integer duration = appointmentType.getDuration(); for (TimeSlot slot : suitableTimeSlots) { //Filter by time left if (getTimeLeftInTimeSlot(slot) >= duration) availableTimeSlots.add(slot); } return availableTimeSlots; } @Override @Transactional(readOnly = true) public List<TimeSlot> getTimeSlotsByConstraintsIncludingFull(AppointmentType appointmentType, Date fromDate, Date toDate, Provider provider, Location location) throws APIException { List<TimeSlot> suitableTimeSlots = getTimeSlotDAO().getTimeSlotsByConstraints(appointmentType, fromDate, toDate, provider); List<TimeSlot> availableTimeSlots = new LinkedList<TimeSlot>(); // Used to update the session to the correct one if (location != null) location = Context.getLocationService().getLocation(location.getId()); Set<Location> relevantLocations = getAllLocationDescendants(location, null); relevantLocations.add(location); for (TimeSlot slot : suitableTimeSlots) { //Filter by location if (location != null) { if (relevantLocations.contains(slot.getAppointmentBlock().getLocation())) availableTimeSlots.add(slot); } else availableTimeSlots.add(slot); } return availableTimeSlots; } @Override @Transactional(readOnly = true) public List<String> getPatientIdentifiersRepresentation(Patient patient) { LinkedList<String> identifiers = new LinkedList<String>(); if (patient == null) return identifiers; for (PatientIdentifier identifier : patient.getIdentifiers()) { //Representation format: <identifier type name> : <identifier value> //for example: "OpenMRS Identification Number: 7532AM-1" String representation = identifier.getIdentifierType().getName() + ": " + identifier.getIdentifier(); //Put preferred identifier first. if (identifier.getPreferred()) identifiers.add(0, representation); //Insert to the end of the list else identifiers.add(identifiers.size(), representation); } return identifiers; } @Override @Transactional(readOnly = true) public Integer getTimeLeftInTimeSlot(TimeSlot timeSlot) { Integer timeLeft = null; if (timeSlot == null) return timeLeft; Date startDate = timeSlot.getStartDate(); Date endDate = timeSlot.getEndDate(); //Calculate total number of minutes in the time slot. timeLeft = (int) ((endDate.getTime() / 60000) - (startDate.getTime() / 60000)); //Subtract from time left the amounts of minutes already scheduled //Should not take into consideration cancelled or missed appointments List<Appointment> appointments = getAppointmentsInTimeSlot(timeSlot); for (Appointment appointment : appointments) { if (!appointment.isVoided() && appointment.getStatus() != AppointmentStatus.CANCELLED && appointment.getStatus() != AppointmentStatus.MISSED) timeLeft -= appointment.getAppointmentType().getDuration(); } return timeLeft; } @Override @Transactional(readOnly = true) public Set<Location> getAllLocationDescendants(Location location, Set<Location> descendants) { if (descendants == null) descendants = new HashSet<Location>(); if (location != null) { Set<Location> childLocations = location.getChildLocations(); for (Location childLocation : childLocations) { descendants.add(childLocation); getAllLocationDescendants(childLocation, descendants); } } return descendants; } @Override @Transactional(readOnly = true) public List<Appointment> getAppointmentsByConstraints(Date fromDate, Date toDate, Location location, Provider provider, AppointmentType type, AppointmentStatus status) throws APIException { List<Appointment> appointments = appointmentDAO.getAppointmentsByConstraints(fromDate, toDate, provider, type, status); List<Appointment> appointmentsInLocation = new LinkedList<Appointment>(); // Used to update the session to the correct one if (location != null) location = Context.getLocationService().getLocation(location.getId()); Set<Location> relevantLocations = getAllLocationDescendants(location, null); relevantLocations.add(location); for (Appointment appointment : appointments) { boolean satisfyingConstraints = true; //Filter by location if (location != null) { if (relevantLocations.contains(appointment.getTimeSlot().getAppointmentBlock().getLocation())) appointmentsInLocation.add(appointment); } else appointmentsInLocation.add(appointment); } return appointmentsInLocation; } @Override @Transactional(readOnly = true) public Date getAppointmentCurrentStatusStartDate(Appointment appointment) { return appointmentStatusHistoryDAO.getStartDateOfCurrentStatus(appointment); } @Override public void changeAppointmentStatus(Appointment appointment, AppointmentStatus newStatus) { if (appointment != null) { AppointmentStatusHistory history = new AppointmentStatusHistory(); history.setAppointment(appointment); history.setEndDate(new Date()); history.setStartDate(getAppointmentCurrentStatusStartDate(appointment)); history.setStatus(appointment.getStatus()); saveAppointmentStatusHistory(history); appointment.setStatus(newStatus); saveAppointment(appointment); if (newStatus == AppointmentStatus.COMPLETED || newStatus == AppointmentStatus.CANCELLED || newStatus == AppointmentStatus.MISSED) { Visit visit = appointment.getVisit(); if (visit != null && visit.getStopDatetime() != null) { visit.setStopDatetime(new Date()); Context.getVisitService().saveVisit(visit); } } } } @Override @Transactional(readOnly = true) public List<Provider> getAllProvidersSorted(boolean includeRetired) { List<Provider> providers = Context.getProviderService().getAllProviders(includeRetired); Collections.sort(providers, new Comparator<Provider>() { public int compare(Provider pr1, Provider pr2) { return pr1.getName().toLowerCase().compareTo(pr2.getName().toLowerCase()); } }); return providers; } @Override @Transactional(readOnly = true) public List<AppointmentType> getAllAppointmentTypesSorted(boolean includeRetired) { List<AppointmentType> appointmentTypes = Context.getService(AppointmentService.class) .getAllAppointmentTypes(includeRetired); Collections.sort(appointmentTypes, new Comparator<AppointmentType>() { public int compare(AppointmentType at1, AppointmentType at2) { return at1.getName().toLowerCase().compareTo(at2.getName().toLowerCase()); } }); return appointmentTypes; } @Override @Transactional(readOnly = true) public List<Appointment> getAppointmentsByStatus(List<AppointmentStatus> states) { return appointmentDAO.getAppointmentsByStates(states); } @Override @Transactional(readOnly = false) public List<Appointment> cleanOpenAppointments() { List<AppointmentStatus> states = new LinkedList<AppointmentStatus>(); states.add(AppointmentStatus.SCHEDULED); states.add(AppointmentStatus.WAITING); states.add(AppointmentStatus.WALKIN); states.add(AppointmentStatus.INCONSULTATION); List<Appointment> appointmentsInStates = appointmentDAO.getPastAppointmentsByStates(states); if (appointmentsInStates == null) return new LinkedList<Appointment>(); Iterator<Appointment> iter = appointmentsInStates.iterator(); Date now = Calendar.getInstance().getTime(); while (iter.hasNext()) { Appointment appointment = iter.next(); //Check if past appointment if (now.after(appointment.getTimeSlot().getEndDate())) { AppointmentStatus status = appointment.getStatus(); switch (status) { case SCHEDULED: case WAITING: case WALKIN: changeAppointmentStatus(appointment, AppointmentStatus.MISSED); break; case INCONSULTATION: changeAppointmentStatus(appointment, AppointmentStatus.COMPLETED); break; default: //No need to change appointmentsInStates.remove(appointment); break; } } else { appointmentsInStates.remove(appointment); } } return appointmentsInStates; } public Map<AppointmentType, Double> getAverageHistoryDurationByConditions(Date fromDate, Date endDate, AppointmentStatus status) { Map<AppointmentType, Double> averages = new HashMap<AppointmentType, Double>(); Map<AppointmentType, Integer> counters = new HashMap<AppointmentType, Integer>(); List<AppointmentStatusHistory> histories = appointmentStatusHistoryDAO.getHistoriesByInterval(fromDate, endDate, status); //Clean Not-Reasonable Durations Map<AppointmentStatusHistory, Double> durations = new HashMap<AppointmentStatusHistory, Double>(); // 60 seconds * 1000 milliseconds in 1 minute int minutesConversion = 60000; int minutesInADay = 1440; for (AppointmentStatusHistory history : histories) { Date startDate = history.getStartDate(); Date toDate = history.getEndDate(); Double duration = (double) ((toDate.getTime() / minutesConversion) - (startDate.getTime() / minutesConversion)); //Not reasonable to be more than a day if (duration < minutesInADay) durations.put(history, duration); } Double[] data = new Double[durations.size()]; int i = 0; for (Map.Entry<AppointmentStatusHistory, Double> entry : durations.entrySet()) { //Added Math.sqrt in order to lower the mean and variance data[i] = Math.sqrt(entry.getValue()); i++; } // Compute Intervals double[] boundaries = confidenceInterval(data); // //sum up the durations by type for (Map.Entry<AppointmentStatusHistory, Double> entry : durations.entrySet()) { AppointmentType type = entry.getKey().getAppointment().getAppointmentType(); Double duration = entry.getValue(); //Added Math.sqrt in order to lower the mean and variance if ((Math.sqrt(duration) <= boundaries[1])) { if (averages.containsKey(type)) { averages.put(type, averages.get(type) + duration); counters.put(type, counters.get(type) + 1); } else { averages.put(type, duration); counters.put(type, 1); } } } // Compute average for (Map.Entry<AppointmentType, Integer> counter : counters.entrySet()) averages.put(counter.getKey(), averages.get(counter.getKey()) / counter.getValue()); return averages; } @Transactional(readOnly = true) public Map<Provider, Double> getAverageHistoryDurationByConditionsPerProvider(Date fromDate, Date endDate, AppointmentStatus status) { Map<Provider, Double> averages = new HashMap<Provider, Double>(); Map<Provider, Integer> counters = new HashMap<Provider, Integer>(); List<AppointmentStatusHistory> histories = appointmentStatusHistoryDAO.getHistoriesByInterval(fromDate, endDate, status); //Clean Not-Reasonable Durations Map<AppointmentStatusHistory, Double> durations = new HashMap<AppointmentStatusHistory, Double>(); // 60 seconds * 1000 milliseconds in 1 minute int minutesConversion = 60000; int minutesInADay = 1440; for (AppointmentStatusHistory history : histories) { Date startDate = history.getStartDate(); Date toDate = history.getEndDate(); Double duration = (double) ((toDate.getTime() / minutesConversion) - (startDate.getTime() / minutesConversion)); //Not reasonable to be more than a day if (duration > 0 && duration < minutesInADay) durations.put(history, duration); } Double[] data = new Double[durations.size()]; int i = 0; for (Map.Entry<AppointmentStatusHistory, Double> entry : durations.entrySet()) { //Added Math.sqrt in order to lower the mean and variance data[i] = Math.sqrt(entry.getValue()); i++; } // Compute Intervals double[] boundaries = confidenceInterval(data); // //sum up the durations by type for (Map.Entry<AppointmentStatusHistory, Double> entry : durations.entrySet()) { Provider provider = entry.getKey().getAppointment().getTimeSlot().getAppointmentBlock().getProvider(); Double duration = entry.getValue(); //Added Math.sqrt in order to lower the mean and variance if ((Math.sqrt(duration) <= boundaries[1])) { if (averages.containsKey(provider)) { averages.put(provider, averages.get(provider) + duration); counters.put(provider, counters.get(provider) + 1); } else { averages.put(provider, duration); counters.put(provider, 1); } } } // Compute average for (Map.Entry<Provider, Integer> counter : counters.entrySet()) averages.put(counter.getKey(), averages.get(counter.getKey()) / counter.getValue()); return averages; } @Override @Transactional(readOnly = true) public Integer getHistoryCountByConditions(Date fromDate, Date endDate, AppointmentStatus status) { List<AppointmentStatusHistory> histories = appointmentStatusHistoryDAO.getHistoriesByInterval(fromDate, endDate, status); return histories.size(); } @Override @Transactional(readOnly = true) public Map<AppointmentType, Integer> getAppointmentTypeDistribution(Date fromDate, Date toDate) { List<AppointmentType> unretiredTypes = Context.getService(AppointmentService.class) .getAllAppointmentTypes(false); Map<AppointmentType, Integer> distribution = new HashMap<AppointmentType, Integer>(); for (AppointmentType type : unretiredTypes) { Integer countOfType = appointmentTypeDAO.getAppointmentTypeCount(fromDate, toDate, type); if (countOfType > 0) distribution.put(type, countOfType); } return distribution; } private double[] confidenceInterval(Double[] data) { //Empty Dataset if (data.length == 0) return new double[] { 0.0, 0.0 }; //Initialization double mean = 0; int count = data.length; int df = count - 1; //If Dataset consists of only one item if (df == 0) return new double[] { Double.MIN_VALUE, Double.MAX_VALUE }; double alpha = 0.05; double tStat = StudentT.tTable(df, alpha); //Compute Mean for (double val : data) mean += val; mean = mean / count; //Compute Variance double variance = 0; for (double val : data) variance += Math.pow((val - mean), 2); variance = variance / df; //If deviation is small - Suspected as "Clean of Noise" if (Math.sqrt(variance) <= 1) return new double[] { Double.MIN_VALUE, Double.MAX_VALUE }; //Compute Confidence Interval Bounds. double[] boundaries = new double[2]; double factor = tStat * (Math.sqrt(variance) / Math.sqrt(count)); boundaries[0] = mean - factor; boundaries[1] = mean + factor; return boundaries; } }