Package web.rechelper

Source Code of web.rechelper.ReceptionHelper


package web.rechelper;

import web.reception.*;
import dao.DAO;
import domain.shedule.Prorumble;
import domain.shedule.SheduleException;
import domain.shedule.SheduleHoliday;
import domain.shedule.SheduleIndividualWork;
import domain.shedule.SheduleReception;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Property;
import utils.Converter;
import utils.Day;
import utils.WeekIndex;

/*
* приоритет расписаний
* - шаблон расписания врача (дата)
* - праздничные дни год/месяц
* - исключительные дни год/месяц/год
* - расписание врача (дата)
* - прогулы/отгулы/отпуск врача год/месяц/год
*
*  Контроллер отображает недельное расписание врача. Входные данные
*   - пациент
*   - тип приема (работы врача)
*   - ЛПУ
*   - Врач
*   - Неделя
*
*  Логика выборки
*   I. Вычисление расписания
*   0. Получаем текущую неделю
*   1. Получаем список прогулов врача за текущую неделю
*   2. Для каждого дня недели, если в этот день есть прогул отмечаем день как закрытый, иначе:
*   3. Получаем расписание врача за текущую неделю (только работы указанного типа),
*          если оно еще не получено
*   4. Для выбранного дня недели, если есть работы, переходим к пункту II,
*          если работ нет, значит врач работает по шаблону
*   5. Получаем исключения работы поликлинники за текущую неделю.
*          если исключения есть, и они указывают что день нерабочий, отмечаем день как закрытый
*   6. Если исключений нет, получаем праздники за указанную неделю
*          если в этот день праздник, отмечаем день как закрытый
*   7. Врач работает по шаблону. Если еще не получены, получаем:
*          - весь шаблон расписания для данного врача
*          - чередование недель в расписании врача
*          - индекс текущей недели
*   10. Используем работы из шаблона врача
*
*  II. Подготовка работ к отображению в вебе
*    1. Получить список записей на прием к данному врачу на всю неделю
*    2. Для каждой работы создаеть список "талонов", временных промежутков
*      длительностью равной дефолтовой длительности приема по данной работе.
*    3. Отметить талоны, которые пересекаются по времени с существующими записями
*      на прием как занятые
*    4. собрать модель [dayofweek][Ticket]
*/

public class ReceptionHelper {

    protected static final Logger log = Logger.getLogger(IndexController.class);

    private final DAO dao;
    private final ReceptionDTO dto;
    private final Date begin;
    private final Date end;


    private HashMap<Day, List<SheduleIndividualWork>> exWorks;
    private HashMap<Integer, List<SheduleIndividualWork>> plWorks;
    private Set<Day> prorumbles;
    private HashMap<Day, List<SheduleReception>> receptions;
    private HashMap<Day, List<SheduleException>> exceptions;
    private HashMap<Integer, List<SheduleHoliday>> holidays;

    public ReceptionHelper(DAO dao, ReceptionDTO dto, Date begin, Date end) {
        this.dao = dao;
        this.dto = dto;
        this.begin = begin;
        this.end = end;
    }

    public List<WeekDay> getModel() throws Exception {
        List<WeekDay> weekModel = new LinkedList<WeekDay>();
       
        Calendar today = GregorianCalendar.getInstance();
        today.set(Calendar.HOUR_OF_DAY, 0);
        today.set(Calendar.MINUTE, 0);
        today.set(Calendar.MILLISECOND, 0);

        //I. Вычисление расписания
        Calendar dayCal = GregorianCalendar.getInstance();
        dayCal.setTime(begin);

         //   2. Для каждого дня недели,
        while(dayCal.getTimeInMillis() <= end.getTime()) {
            WeekDay weekDay = new WeekDay(dayCal);
            weekModel.add(weekDay);

            dayCal.add(Calendar.DAY_OF_MONTH, 1);

//            log.debug("Analyze day " + weekDay);

            if(dayCal.before(today)) {
//                log.debug("Past detected for day " + weekDay);
                continue;
            }

            //если в этот день есть прогул отмечаем день как закрытый, иначе:
             if(isProrumbled(weekDay.getDay())) {
//                 log.debug("Prorumble detected for day " + weekDay);
                 continue;
             }
             //Получаем расписание врача за текущую неделю (только работы указанного типа)
             //если нет работ - значит врач работает по шаблону, идем дальше
             List<SheduleIndividualWork> dayExceptionalWorks = getExceptionalWorks(weekDay.getDay());
             if(dayExceptionalWorks != null) {
//                 log.debug("Exceptional works detected, use it for day " + weekDay);
                 //если есть работы используем их
                 weekDay.setTickets(getDayTickets(weekDay.getDay(), dayExceptionalWorks));
                 continue;
             }

             //5. Получаем исключения работы поликлинники за текущую неделю.
             //если исключения есть, и они указывают что день нерабочий, отмечаем день как закрытый
             List<SheduleException> clinicExceptions = getClinicExceptions(weekDay.getDay());
             if(isWeekend(clinicExceptions)) {
//                 log.debug("Weekend exceptional clinic day detected for day " + weekDay);
                 continue;
             }

             //6. Если исключений нет, получаем праздники за указанную неделю
             //если в этот день праздник, отмечаем день как закрытый
             if(clinicExceptions == null) {
                 List<SheduleHoliday> clinicHolidays = getClinicHolidays(weekDay.getDay());
                 if(clinicHolidays != null) {
//                     log.debug("Holiday clinic day detected for day " + weekDay);
                     continue;
                 }
             }

             //7. Врач работает по шаблону. получаем шаблон расписания для данного врача
             List<SheduleIndividualWork> dayPlanWorks = getPlanWorks(weekDay.getDay());
             if(dayPlanWorks != null) {
                 //10. Используем работы из шаблона врача в качестве индекса текущей недели
//                 log.debug("Planned works detected for day " + weekDay);
                 weekDay.setTickets(getDayTickets(weekDay.getDay(), dayPlanWorks));
             }
        }
//        log.debug("Analyze of day complete.");

        //II. Подготовка работ к отображению в вебе
        //Отметить талоны, которые пересекаются по времени с существующими записями как занятые
        Iterator<WeekDay> it = weekModel.iterator();
        while(it.hasNext()) {
            WeekDay wd = it.next();
            if(wd.getTickets() == null) {
                //если в этот день нет талонов, пропускаем
                continue;
            }
            updateStatus(wd, getReceptions(wd.getDay()));
        }
       
        return weekModel;
    }



    /**
     * Возвращает список записей на прием, осуществляемых в диапазоне дат
     * @param timeBegin
     * @param timeEnd
     * @return
     */
    private List<SheduleReception> getReceptions(Day day) {
        if(receptions == null) {
            receptions = getReceptions();
        }
        return receptions.get(day);
    }

    /**
     * Возвращает список записей на прием в текущую неделю к указанному врачу
     * @return
     */
    private HashMap<Day, List<SheduleReception>> getReceptions() {
        HashMap<Day, List<SheduleReception>> res = new HashMap<Day, List<SheduleReception>>();
        DetachedCriteria criteria = DetachedCriteria.forClass(SheduleReception.class)
                .add(Property.forName("collaborator").eq(dto.getCollaborator()))
                .add(Property.forName("begin").ge(begin))
                .add(Property.forName("begin").le(end));
        List<SheduleReception> list = dao.getList(criteria, null, null);
        for(SheduleReception sr: list) {
            Day day = new Day(sr.getBegin());
            List<SheduleReception> dayList = res.get(day);
            if(dayList == null) {
                dayList = new LinkedList<SheduleReception>();
                res.put(day, dayList);
            }
            dayList.add(sr);
        }
        return res;
    }

    /**
     * Возвращает булевое выражение, есть ли пропуски в указанном диапазоне дат
     * @param prorumbles
     * @param timeBegin
     * @param timeEnd
     * @return
     */
    private boolean isProrumbled(Day day) {
        return getProrumbles().contains(day);
    }

    private Set<Day> getProrumbles() {
        if(prorumbles == null) {
            prorumbles = new HashSet<Day>();
            DetachedCriteria criteria = DetachedCriteria.forClass(Prorumble.class)
                    .add(Property.forName("collaborator").eq(dto.getCollaborator()))
                    .add(Property.forName("day").ge(begin.getTime()))
                    .add(Property.forName("day").le(end.getTime()));
            for(Object o: dao.getList(criteria, null, null)) {
                Prorumble pro = (Prorumble) o;
                Day day = new Day(pro.getDay().getTime());
                prorumbles.add(day);
            }
        }
        return prorumbles;
    }
   
    /**
     * исключения работы поликлинники в указанном дне текущей недели
     * @param dayBegin
     * @param dayEnd
     * @return
     */
    private List<SheduleException> getClinicExceptions(Day day) {
        if(exceptions == null) {
            exceptions = getClinicExceptions();
        }
        return exceptions.get(day);
    }

    private HashMap<Day,List<SheduleException>> getClinicExceptions() {
        HashMap<Day, List<SheduleException>> res = new HashMap<Day, List<SheduleException>>();
        DetachedCriteria criteria = DetachedCriteria.forClass(SheduleException.class)
                .addOrder(Order.asc("day"))
                .add(Property.forName("lpu").ge(dto.getLpu()))
                .add(Property.forName("day").ge(begin))
                .add(Property.forName("day").le(end));

        for(Object o: dao.getList(criteria, null, null)) {
            SheduleException se = (SheduleException) o;
            Day day = new Day(se.getDay());
            List<SheduleException> exlist = res.get(day);
            if(exlist == null) {
                exlist = new LinkedList<SheduleException>();
                res.put(day, exlist);
            }
            exlist.add(se);
        }
        return res;
    }

    /**
     * плановые праздники в указанном диапазоне дат
     * @param dayBegin
     * @param dayEnd
     * @return
     */
    private List<SheduleHoliday> getClinicHolidays(Day day) {
        if(holidays == null) {
            holidays = getClinicHolidays();
        }
        return holidays.get((day.getMonth()+1)*100 + day.getDay());
    }

    private HashMap<Integer, List<SheduleHoliday>> getClinicHolidays() {
        HashMap<Integer, List<SheduleHoliday>> res = new HashMap<Integer, List<SheduleHoliday>>();
        List<SheduleHoliday> list = dao.getList(SheduleHoliday.class);
        for (SheduleHoliday se : list) {
            int hash = se.getMonth() * 100 + se.getDay();

            List<SheduleHoliday> holList = res.get(hash);
            if (holList == null) {
                holList = new LinkedList<SheduleHoliday>();
                res.put(hash, holList);
            }
            holList.add(se);
        }
        return res;
    }

    /**
     * возвращает отсортированные по времени начала работы
     * @param dow
     * @return
     */
    private List<SheduleIndividualWork> getPlanWorks(Day day) {
        if(plWorks == null) {
            plWorks = getPlanWorks();
        }
        WeekIndex weekIndex = new WeekIndex(day);
        int weekPeriod = dto.getCollaborator().getWeekPeriod();
        int planWeekIndex = weekIndex.getIndex() % (weekPeriod + 1);
        int dayOfWeek = day.getDayOfWeek().getEuropean();
        int hash = planWeekIndex*7 + dayOfWeek + 1;
        return plWorks.get(hash);
    }

    private HashMap<Integer, List<SheduleIndividualWork>> getPlanWorks() {
        HashMap<Integer, List<SheduleIndividualWork>> works =
                new HashMap<Integer, List<SheduleIndividualWork>>();

        DetachedCriteria criteria = DetachedCriteria.forClass(SheduleIndividualWork.class)
                .addOrder(Order.asc("timeBegin"))
                .add(Property.forName("collaborator").eq(dto.getCollaborator()));
        for(Object o: dao.getList(criteria, null, null)) {
            SheduleIndividualWork siw = (SheduleIndividualWork) o;
            Calendar cal = GregorianCalendar.getInstance();
            cal.setTime(siw.getTimeBegin());

            int day = cal.get(Calendar.DAY_OF_MONTH);
            List<SheduleIndividualWork> dayList = works.get(day);
            if(dayList == null) {
                dayList = new LinkedList<SheduleIndividualWork>();
                works.put(day, dayList);
            }
            dayList.add(siw);
        }
        return works;
    }

    private List<SheduleIndividualWork> getExceptionalWorks(Day day) {
        return getExceptionalWorks().get(day);
    }

    private HashMap<Day, List<SheduleIndividualWork>> getExceptionalWorks() {
        if(exWorks == null) {
            exWorks = new HashMap<Day, List<SheduleIndividualWork>>();
            DetachedCriteria criteria = DetachedCriteria.forClass(SheduleIndividualWork.class)
                    .add(Property.forName("collaborator").eq(dto.getCollaborator()))
                    .add(Property.forName("timeBegin").ge(begin))
                    .add(Property.forName("timeBegin").le(end));
            for(Object o: dao.getList(criteria, null, null)) {
                SheduleIndividualWork siw = (SheduleIndividualWork) o;
                Day day = new Day(siw.getTimeBegin());
                List<SheduleIndividualWork> dayList = exWorks.get(day);
                if(dayList == null) {
                    dayList = new LinkedList<SheduleIndividualWork>();
                    exWorks.put(day, dayList);
                }
                dayList.add(siw);
            }
        }
        return exWorks;
    }

    /**
     * Возвращает, являются ли один из ВСЕ элементы указанного списка - НЕ рабочим днем
     * @param clinicExceptions
     * @return
     */
    private static boolean isWeekend(List<SheduleException> clinicExceptions) {
        if(clinicExceptions == null) {
            return false;
        }
        for(SheduleException se: clinicExceptions) {
            if(se.getWorking()) {
                return false;
            }
        }
        return true;
    }

    private void optimizeWorks(final Day day, List<SheduleIndividualWork> exWorks) {
        //makeTime чтоб потом не мучаться
        for (SheduleIndividualWork s : exWorks) {
            s.setTimeBegin(makeTime(day, s.getTimeBegin()).getTime());
            s.setTimeEnd(makeTime(day, s.getTimeEnd()).getTime());
        }
        //Сортировка по началу работы
        Collections.sort(exWorks, new Comparator<SheduleIndividualWork>() {

            @Override
            public int compare(SheduleIndividualWork o1, SheduleIndividualWork o2) {
                //Calendar c1 = makeTime(day, o1.getTimeBegin());
                //Calendar c2 = makeTime(day, o2.getTimeBegin());
                return o1.getTimeBegin().compareTo(o2.getTimeBegin());
            }

        });
        //цикл до предпоследнего элемента - последний объединять не надо (вылетит outofbounds)
        for (int i = 0; i < exWorks.size() - 1; i++) {
            SheduleIndividualWork current = exWorks.get(i);
            if(current == null) {
                continue;
            }
            SheduleIndividualWork next = null;
            Integer nextIndex = null;
            for (int j = i+1; j < exWorks.size(); j++) {
                if (exWorks.get(j) != null) {
                    next = exWorks.get(j);
                    nextIndex = j;
                    break;
                }
            }
            if (next != null) {
                Date currentEnd = current.getTimeEnd();
                Date nextBegin = next.getTimeBegin();
                if (!nextBegin.after(currentEnd)) {
                    //пересекаются либо идут встык
                    if (current.getServiceDuration() == next.getServiceDuration()) {
                        //разбивка совпадает - объединяем
                        current.setTimeEnd(next.getTimeEnd());
                        exWorks.set(nextIndex, null); //убили
                    } else {
                        //разбивка не совпадает - обрезаем
                        current.setTimeEnd(nextBegin);
                    }
                }
            }
        }
        //удаляем убитые
        for (Iterator<SheduleIndividualWork> it = exWorks.iterator(); it.hasNext();) {
            SheduleIndividualWork work = it.next();
            if (work == null) {
                it.remove();
            }
        }
    }
    /**
     * Для каждой работы создаеть список "талонов", временных промежутков
     * длительностью равной дефолтовой длительности приема по данной работе.
     * @param exWorks
     * @return
     */
    private List<Ticket> getDayTickets(Day day, List<SheduleIndividualWork> exWorks) {
        //объединяем перекрывающиеся талоны, поскольку типы работ должны быть одинаковы,
        //объединяем только работы с одинаковым временем приема
        //todo потестить
        optimizeWorks(day, exWorks);

        List<Ticket> res = new LinkedList<Ticket>();
        for(SheduleIndividualWork siw: exWorks) {
            //гранулируем время начала и конца работы
            Calendar beginCal = makeTime(day, siw.getTimeBegin());
            Calendar endCal = makeTime(day, siw.getTimeEnd());

            int step = siw.getServiceDuration();
            if(step == 0) {
                step = 20;
            }
//            log.trace("Ticketing: " + begin.getTime() + "," + end.getTime() + " step " + step);
            do {
                Ticket ticket = new Ticket(beginCal, step);
                if (!res.contains(ticket)) {
                    res.add(ticket);
                }
                beginCal.add(Calendar.MINUTE, step);
            } while (endCal.after(beginCal));
        }
        Collections.sort(res);
        return res;
    }

    private Calendar makeTime(Day day, Date time) {
        Calendar cal = GregorianCalendar.getInstance();
        cal.setTime(time);
        int hours = cal.get(Calendar.HOUR_OF_DAY);
        int minutes = cal.get(Calendar.MINUTE);
        if((minutes % 5) > 0) {
            minutes = (int) (Math.round(minutes / 5.0) * 5);
            if(minutes == 60) {
                minutes = 0;
                hours++;
            }
        }
        if(hours >= 24) {
            hours = 23;
            minutes = 59;
        }
        Calendar res = day.getCalendar();
        res.set(Calendar.HOUR_OF_DAY, hours);
        res.set(Calendar.MINUTE, minutes);
        res.set(Calendar.SECOND, 0);
        res.set(Calendar.MILLISECOND, 0);
        return res;
    }

    /**
     * Обновляет статусы талонов в соответствии с существующими записями на прием
     * @param dayModel
     * @param dayReceptions acn be null
     */
    private void updateStatus(WeekDay wd, List<SheduleReception> dayReceptions) {
        List<Ticket> dayModel = wd.getTickets();
        //отмечаем сегодняшние просроченные талоны
        Calendar now = GregorianCalendar.getInstance();
        if(wd.getDay().getCalendar().before(now)) {
            for(Ticket ticket: dayModel) {
                if(ticket.getCalendar().before(now)) {
                    ticket.setBuzy(true);
                }
            }
        }

        //отмечаем зянятые
        if(dayReceptions != null) {
            for(SheduleReception reception: dayReceptions) {
                long recBeg = reception.getBegin().getTime();
                long recEnd = recBeg + reception.getDuration() *60*1000;
                for(Ticket ticket: dayModel) {
                    if(ticket.isBuzy()) {
                        continue;
                    }
                    long tickBeg = ticket.getTime();
                    long tickEnd = tickBeg + ticket.getDuration()*60*1000;
                    if ((recBeg - tickBeg) * (tickEnd - recBeg) > 0) {
                        ticket.setBuzy(true);
                    } else if ((recEnd - tickBeg) * (tickEnd - recEnd) > 0) {
                        ticket.setBuzy(true);
                    } else if ((tickBeg - recBeg) * (recEnd - tickBeg) > 0) {
                        ticket.setBuzy(true);
                    } else if ((tickEnd - recBeg) * (recEnd - tickEnd) > 0) {
                        ticket.setBuzy(true);
                    } else if ((tickBeg == recBeg) && (tickEnd == recEnd)) {
                        ticket.setBuzy(true);
                    }
                }
            }
        }
        //обновляем информацию о первом свободном
        for(Ticket ticket: dayModel) {
            if(!ticket.isBuzy()) {
                wd.setFirstFree(ticket);
                break;
            }
        }
    }
}
TOP

Related Classes of web.rechelper.ReceptionHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.