fr.paris.lutece.plugins.appointment.service.AppointmentUtilities.java Source code

Java tutorial

Introduction

Here is the source code for fr.paris.lutece.plugins.appointment.service.AppointmentUtilities.java

Source

/*
 * Copyright (c) 2002-2018, Mairie de Paris
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice
 *     and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice
 *     and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * License 1.0
 */
package fr.paris.lutece.plugins.appointment.service;

import static java.lang.Math.toIntExact;

import java.io.IOException;
import java.io.OutputStream;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import fr.paris.lutece.plugins.appointment.business.appointment.Appointment;
import fr.paris.lutece.plugins.appointment.business.form.Form;
import fr.paris.lutece.plugins.appointment.business.planning.TimeSlot;
import fr.paris.lutece.plugins.appointment.business.planning.WeekDefinition;
import fr.paris.lutece.plugins.appointment.business.planning.WorkingDay;
import fr.paris.lutece.plugins.appointment.business.rule.ReservationRule;
import fr.paris.lutece.plugins.appointment.business.slot.Slot;
import fr.paris.lutece.plugins.appointment.business.user.User;
import fr.paris.lutece.plugins.appointment.service.lock.SlotEditTask;
import fr.paris.lutece.plugins.appointment.service.lock.TimerForLockOnSlot;
import fr.paris.lutece.plugins.appointment.web.dto.AppointmentDTO;
import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFilterDTO;
import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFormDTO;
import fr.paris.lutece.plugins.appointment.web.dto.ResponseRecapDTO;
import fr.paris.lutece.plugins.genericattributes.business.Entry;
import fr.paris.lutece.plugins.genericattributes.business.EntryFilter;
import fr.paris.lutece.plugins.genericattributes.business.EntryHome;
import fr.paris.lutece.plugins.genericattributes.business.Field;
import fr.paris.lutece.plugins.genericattributes.business.FieldHome;
import fr.paris.lutece.plugins.genericattributes.business.GenericAttributeError;
import fr.paris.lutece.plugins.genericattributes.business.Response;
import fr.paris.lutece.plugins.genericattributes.business.ResponseHome;
import fr.paris.lutece.plugins.genericattributes.service.entrytype.EntryTypeServiceManager;
import fr.paris.lutece.plugins.genericattributes.service.entrytype.IEntryTypeService;
import fr.paris.lutece.plugins.workflowcore.business.state.State;
import fr.paris.lutece.plugins.workflowcore.service.state.StateService;
import fr.paris.lutece.portal.business.user.AdminUser;
import fr.paris.lutece.portal.service.i18n.I18nService;
import fr.paris.lutece.portal.service.rbac.RBACService;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
import fr.paris.lutece.util.beanvalidation.BeanValidationUtil;

/**
 * Utility class for Appointment Mutualize methods between MVCApplication and MVCAdminJspBean
 * 
 * @author Laurent Payen
 *
 */
public final class AppointmentUtilities {

    private static final String ERROR_MESSAGE_EMPTY_CONFIRM_EMAIL = "appointment.validation.appointment.EmailConfirmation.email";
    private static final String ERROR_MESSAGE_CONFIRM_EMAIL = "appointment.message.error.confirmEmail";
    private static final String ERROR_MESSAGE_DATE_APPOINTMENT = "appointment.message.error.dateAppointment";
    private static final String ERROR_MESSAGE_EMPTY_EMAIL = "appointment.validation.appointment.Email.notEmpty";
    private static final String ERROR_MESSAGE_EMPTY_NB_BOOKED_SEAT = "appointment.validation.appointment.NbBookedSeat.notEmpty";
    private static final String ERROR_MESSAGE_FORMAT_NB_BOOKED_SEAT = "appointment.validation.appointment.NbBookedSeat.notNumberFormat";
    private static final String ERROR_MESSAGE_ERROR_NB_BOOKED_SEAT = "appointment.validation.appointment.NbBookedSeat.error";

    private static final String KEY_RESOURCE_TYPE = "appointment.appointment.name";
    private static final String KEY_COLUMN_LAST_NAME = "appointment.manageAppointments.columnLastName";
    private static final String KEY_COLUMN_FISRT_NAME = "appointment.manageAppointments.columnFirstName";
    private static final String KEY_COLUMN_EMAIL = "appointment.manageAppointments.columnEmail";
    private static final String KEY_COLUMN_DATE_APPOINTMENT = "appointment.dateAppointment.title";
    private static final String KEY_TIME_START = "appointment.model.entity.appointmentform.attribute.timeStart";
    private static final String KEY_TIME_END = "appointment.model.entity.appointmentform.attribute.timeEnd";
    private static final String KEY_COLUMN_ADMIN = "appointment.manageAppointments.columnAdmin";
    private static final String KEY_COLUMN_STATUS = "appointment.labelStatus";
    private static final String KEY_COLUMN_STATE = "appointment.manageAppointments.columnState";
    private static final String KEY_COLUMN_NB_BOOKED_SEATS = "appointment.manageAppointments.columnNumberOfBookedseatsPerAppointment";

    private static final String CONSTANT_COMMA = ",";
    private static final String EXCEL_FILE_EXTENSION = ".xlsx";
    private static final String EXCEL_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

    public static final String SESSION_TIMER_SLOT = "appointment.session.timer.slot";

    public static final String PROPERTY_DEFAULT_EXPIRED_TIME_EDIT_APPOINTMENT = "appointment.edit.expired.time";

    public static final int THIRTY_MINUTES = 30;

    /**
     * Private constructor - this class does not need to be instantiated
     */
    private AppointmentUtilities() {
    }

    /**
     * Check that the email is correct and matches the confirm email
     * 
     * @param strEmail
     *            the email
     * @param strConfirmEmail
     *            the confirm email
     * @param form
     *            the form
     * @param locale
     *            the local
     * @param listFormErrors
     *            the list of errors that can be fill in with the errors found for the email
     */
    public static void checkEmail(String strEmail, String strConfirmEmail, AppointmentFormDTO form, Locale locale,
            List<GenericAttributeError> listFormErrors) {
        if (form.getEnableMandatoryEmail()) {
            if (StringUtils.isEmpty(strEmail)) {
                GenericAttributeError genAttError = new GenericAttributeError();
                genAttError.setErrorMessage(I18nService.getLocalizedString(ERROR_MESSAGE_EMPTY_EMAIL, locale));
                listFormErrors.add(genAttError);
            }
            if (StringUtils.isEmpty(strConfirmEmail)) {
                GenericAttributeError genAttError = new GenericAttributeError();
                genAttError
                        .setErrorMessage(I18nService.getLocalizedString(ERROR_MESSAGE_EMPTY_CONFIRM_EMAIL, locale));
                listFormErrors.add(genAttError);
            }
        }
        if (!StringUtils.equals(strEmail, strConfirmEmail)) {
            GenericAttributeError genAttError = new GenericAttributeError();
            genAttError.setErrorMessage(I18nService.getLocalizedString(ERROR_MESSAGE_CONFIRM_EMAIL, locale));
            listFormErrors.add(genAttError);
        }
    }

    /**
     * Check that the date of the appointment we try to take is not in the past
     * 
     * @param appointmentDTO
     *            the appointment
     * @param locale
     *            the local
     * @param listFormErrors
     *            the list of errors that can be fill in with the error found with the date
     */
    public static void checkDateOfTheAppointmentIsNotBeforeNow(AppointmentDTO appointmentDTO, Locale locale,
            List<GenericAttributeError> listFormErrors) {
        if (appointmentDTO.getSlot().getStartingDateTime().toLocalDate().isBefore(LocalDate.now())) {
            GenericAttributeError genAttError = new GenericAttributeError();
            genAttError.setErrorMessage(I18nService.getLocalizedString(ERROR_MESSAGE_DATE_APPOINTMENT, locale));
            listFormErrors.add(genAttError);
        }
    }

    /**
     * Check that the delay between two appointments for the same use has been respected
     * 
     * @param appointmentDTO
     *            the appointment
     * @param strEmail
     *            the email
     * @param form
     *            the form
     * @return false if the delay is not respected
     */
    public static boolean checkNbDaysBetweenTwoAppointments(AppointmentDTO appointmentDTO, String strFirstName,
            String strLastName, String strEmail, AppointmentFormDTO form) {
        boolean bCheckPassed = true;
        int nbDaysBetweenTwoAppointments = form.getNbDaysBeforeNewAppointment();
        if (nbDaysBetweenTwoAppointments != 0) {
            List<Slot> listSlots = getSlotsByEmail(strEmail, appointmentDTO.getIdAppointment());
            if (CollectionUtils.isNotEmpty(listSlots)) {
                // Get the last appointment date for this form
                listSlots = listSlots.stream().filter(s -> s.getIdForm() == form.getIdForm())
                        .collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(listSlots)) {
                    LocalDate dateOfTheLastAppointment = listSlots.stream().map(Slot::getStartingDateTime)
                            .max(LocalDateTime::compareTo).get().toLocalDate();
                    // Check the number of days between this appointment and
                    // the last appointment the user has taken
                    LocalDate dateOfTheAppointment = appointmentDTO.getSlot().getStartingDateTime().toLocalDate();
                    if (Math.abs(dateOfTheLastAppointment.until(dateOfTheAppointment,
                            ChronoUnit.DAYS)) <= nbDaysBetweenTwoAppointments) {
                        bCheckPassed = false;
                    }
                }
            }
        }
        return bCheckPassed;
    }

    /**
     * Check that the delay between two appointments for the same use has been respected
     * 
     * @param appointmentDTO
     *            the appointment
     * @param strEmail
     *            the email
     * @param form
     *            the form
     * @return false if the delay is not respected
     */
    public static boolean checkNbDaysBetweenTwoAppointmentsTaken(AppointmentDTO appointmentDTO, String strEmail,
            AppointmentFormDTO form) {
        boolean bCheckPassed = true;
        int nbDaysBetweenTwoAppointments = form.getNbDaysBeforeNewAppointment();
        if (nbDaysBetweenTwoAppointments != 0) {
            AppointmentFilterDTO filter = new AppointmentFilterDTO();
            filter.setEmail(strEmail);
            filter.setIdForm(form.getIdForm());
            List<Appointment> listAppointment = AppointmentService.findListAppointmentsByFilter(filter);
            // If we modify an appointment, we remove the
            // appointment that we currently edit
            if (appointmentDTO.getIdAppointment() != 0) {
                listAppointment = listAppointment.stream()
                        .filter(a -> a.getIdAppointment() != appointmentDTO.getIdAppointment())
                        .collect(Collectors.toList());
            }

            if (CollectionUtils.isNotEmpty(listAppointment)) {

                LocalDateTime dateOfTheLastAppointmentTaken = listAppointment.stream()
                        .map(Appointment::getDateAppointmentTaken).max(LocalDateTime::compareTo).get();

                if (Math.abs(dateOfTheLastAppointmentTaken.until(LocalDateTime.now(),
                        ChronoUnit.DAYS)) <= nbDaysBetweenTwoAppointments) {
                    bCheckPassed = false;
                }

            }
        }
        return bCheckPassed;
    }

    /**
     * Get the appointment of a user appointment
     * 
     * @param strEmail
     *            the user's email
     * @param idAppointment
     *            the id of the appointment
     * @return the list of appointment
     */
    private static List<Appointment> getAppointmentByEmail(String strEmail, int idAppointment) {
        List<Appointment> listAppointment = new ArrayList<>();
        if (StringUtils.isNotEmpty(strEmail)) {
            // Looking for existing users with this email
            List<User> listUsers = UserService.findUsersByEmail(strEmail);
            if (listUsers != null) {
                // For each User
                for (User user : listUsers) {
                    // looking for its appointment
                    listAppointment.addAll(AppointmentService.findListAppointmentByUserId(user.getIdUser()));
                }

                // If we modify an appointment, we remove the
                // appointment that we currently edit
                if (idAppointment != 0) {
                    listAppointment = listAppointment.stream().filter(a -> a.getIdAppointment() != idAppointment)
                            .collect(Collectors.toList());
                }

            }
        }
        return listAppointment;
    }

    /**
     * Get the slot of a user appointment
     * 
     * @param strEmail
     *            the user's email
     * @param idAppointment
     *            the id of the appointment
     * @return the list of slots
     */
    private static List<Slot> getSlotsByEmail(String strEmail, int idAppointment) {
        List<Slot> listSlots = new ArrayList<>();
        if (StringUtils.isNotEmpty(strEmail)) {
            List<Appointment> listAppointment = getAppointmentByEmail(strEmail, idAppointment);
            if (CollectionUtils.isNotEmpty(listAppointment)) {
                // I know we could have a join sql query, but I don't
                // want to join the appointment table with the slot
                // table, it's too big and not efficient

                for (Appointment appointment : listAppointment) {
                    if (!appointment.getIsCancelled()) {
                        listSlots.add(SlotService.findSlotById(appointment.getIdSlot()));
                    }
                }

            }

        }
        return listSlots;
    }

    /**
     * Check that the number of appointments on a defined period is not above the maximum authorized
     * 
     * @param appointmentDTO
     *            the appointment
     * @param strEmail
     *            the email of the user
     * @param form
     *            the form
     * @return false if the number of appointments is above the maximum authorized on the defined period
     */
    public static boolean checkNbMaxAppointmentsOnAGivenPeriod(AppointmentDTO appointmentDTO, String strEmail,
            AppointmentFormDTO form) {
        boolean bCheckPassed = true;
        int nbMaxAppointmentsPerUser = form.getNbMaxAppointmentsPerUser();
        int nbDaysForMaxAppointmentsPerUser = form.getNbDaysForMaxAppointmentsPerUser();
        if (nbMaxAppointmentsPerUser != 0) {
            List<Slot> listSlots = getSlotsByEmail(strEmail, appointmentDTO.getIdAppointment());
            if (CollectionUtils.isNotEmpty(listSlots)) {
                // Filter fot the good form
                listSlots = listSlots.stream().filter(s -> s.getIdForm() == form.getIdForm())
                        .collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(listSlots)) {
                    // Get the date of the future appointment
                    LocalDate dateOfTheAppointment = appointmentDTO.getSlot().getStartingDateTime().toLocalDate();
                    // Min starting date of the period
                    LocalDate minStartingDateOfThePeriod = dateOfTheAppointment
                            .minusDays(nbDaysForMaxAppointmentsPerUser);
                    // Max ending date of the period
                    LocalDate maxEndingDateOfThePeriod = dateOfTheAppointment
                            .plusDays(nbDaysForMaxAppointmentsPerUser);
                    // Keep only the slots that are in the min-max period
                    listSlots = listSlots.stream()
                            .filter(s -> s.getStartingDateTime().toLocalDate().isEqual(minStartingDateOfThePeriod)
                                    || s.getStartingDateTime().toLocalDate().isAfter(minStartingDateOfThePeriod))
                            .filter(s -> s.getStartingDateTime().toLocalDate().isEqual(maxEndingDateOfThePeriod)
                                    || s.getStartingDateTime().toLocalDate().isBefore(maxEndingDateOfThePeriod))
                            .collect(Collectors.toList());
                    LocalDate startingDateOfThePeriod = null;
                    LocalDate endingDateOfThePeriod = null;
                    // For each slot
                    for (Slot slot : listSlots) {
                        if (slot.getStartingDateTime().toLocalDate().isBefore(dateOfTheAppointment)) {
                            startingDateOfThePeriod = slot.getStartingDateTime().toLocalDate();
                            endingDateOfThePeriod = startingDateOfThePeriod
                                    .plusDays(nbDaysForMaxAppointmentsPerUser);
                        }
                        if (slot.getStartingDateTime().toLocalDate().isAfter(dateOfTheAppointment)) {
                            endingDateOfThePeriod = slot.getStartingDateTime().toLocalDate();
                            startingDateOfThePeriod = endingDateOfThePeriod
                                    .minusDays(nbDaysForMaxAppointmentsPerUser);
                        }
                        if (slot.getStartingDateTime().toLocalDate().isEqual(dateOfTheAppointment)) {
                            startingDateOfThePeriod = endingDateOfThePeriod = slot.getStartingDateTime()
                                    .toLocalDate();
                        }
                        // Check the number of slots on the period
                        final LocalDate startingDateOfPeriodToSearch = startingDateOfThePeriod;
                        final LocalDate endingDateOfPeriodToSearch = endingDateOfThePeriod;
                        int nbSlots = toIntExact(listSlots.stream().filter(s -> (s.getStartingDateTime()
                                .toLocalDate().equals(startingDateOfPeriodToSearch)
                                || s.getStartingDateTime().toLocalDate().isAfter(startingDateOfPeriodToSearch))
                                && (s.getStartingDateTime().toLocalDate().equals(endingDateOfPeriodToSearch) || s
                                        .getStartingDateTime().toLocalDate().isBefore(endingDateOfPeriodToSearch)))
                                .count());
                        if (nbSlots >= nbMaxAppointmentsPerUser) {
                            bCheckPassed = false;
                            break;
                        }
                    }
                }
            }
        }
        return bCheckPassed;
    }

    /**
     * Check and validate all the rules for the number of booked seats asked
     * 
     * @param strNbBookedSeats
     *            the number of booked seats
     * @param form
     *            the form
     * @param nbRemainingPlaces
     *            the number of remaining places on the slot asked
     * @param locale
     *            the locale
     * @param listFormErrors
     *            the list of errors that can be fill in with the errors found for the number of booked seats
     * @return
     */
    public static int checkAndReturnNbBookedSeats(String strNbBookedSeats, AppointmentFormDTO form,
            AppointmentDTO appointmentDTO, Locale locale, List<GenericAttributeError> listFormErrors) {
        int nbBookedSeats = 1;
        if (StringUtils.isEmpty(strNbBookedSeats) && form.getMaxPeoplePerAppointment() > 1) {
            GenericAttributeError genAttError = new GenericAttributeError();
            genAttError.setErrorMessage(I18nService.getLocalizedString(ERROR_MESSAGE_EMPTY_NB_BOOKED_SEAT, locale));
            listFormErrors.add(genAttError);
        }
        if (StringUtils.isNotEmpty(strNbBookedSeats)) {
            try {
                nbBookedSeats = Integer.parseInt(strNbBookedSeats);
            } catch (NumberFormatException | NullPointerException e) {
                GenericAttributeError genAttError = new GenericAttributeError();
                genAttError.setErrorMessage(
                        I18nService.getLocalizedString(ERROR_MESSAGE_FORMAT_NB_BOOKED_SEAT, locale));
                listFormErrors.add(genAttError);
            }
        }
        // if it's a new appointment, need to check if the number of booked
        // seats is under or equal to the number of remaining places
        // if it's a modification, need to check if the new number of booked
        // seats is under or equal to the number of the remaining places + the
        // previous number of booked seats of the appointment
        if (nbBookedSeats > appointmentDTO.getNbMaxPotentialBookedSeats()) {
            GenericAttributeError genAttError = new GenericAttributeError();
            genAttError.setErrorMessage(I18nService.getLocalizedString(ERROR_MESSAGE_ERROR_NB_BOOKED_SEAT, locale));
            listFormErrors.add(genAttError);
        }

        if (nbBookedSeats == 0) {
            GenericAttributeError genAttError = new GenericAttributeError();
            genAttError.setErrorMessage(I18nService.getLocalizedString(ERROR_MESSAGE_EMPTY_NB_BOOKED_SEAT, locale));
            listFormErrors.add(genAttError);
        }
        return nbBookedSeats;
    }

    /**
     * Fill the appoinmentFront DTO with the given parameters
     * 
     * @param appointmentDTO
     *            the appointmentFront DTO
     * @param nbBookedSeats
     *            the number of booked seats
     * @param strEmail
     *            the email of the user
     * @param strFirstName
     *            the first name of the user
     * @param strLastName
     *            the last name of the user
     */
    public static void fillAppointmentDTO(AppointmentDTO appointmentDTO, int nbBookedSeats, String strEmail,
            String strFirstName, String strLastName) {
        appointmentDTO.setDateOfTheAppointment(appointmentDTO.getSlot().getDate().format(Utilities.getFormatter()));
        appointmentDTO.setNbBookedSeats(nbBookedSeats);
        appointmentDTO.setEmail(strEmail);
        appointmentDTO.setFirstName(strFirstName);
        appointmentDTO.setLastName(strLastName);
    }

    /**
     * Validate the form and the additional entries of the form
     * 
     * @param appointmentDTO
     *            the appointmentFron DTo to validate
     * @param request
     *            the request
     * @param listFormErrors
     *            the list of errors that can be fill with the errors found at the validation
     */
    public static void validateFormAndEntries(AppointmentDTO appointmentDTO, HttpServletRequest request,
            List<GenericAttributeError> listFormErrors) {
        Set<ConstraintViolation<AppointmentDTO>> listErrors = BeanValidationUtil.validate(appointmentDTO);
        if (CollectionUtils.isNotEmpty(listErrors)) {
            for (ConstraintViolation<AppointmentDTO> constraintViolation : listErrors) {
                GenericAttributeError genAttError = new GenericAttributeError();
                genAttError.setErrorMessage(I18nService.getLocalizedString(constraintViolation.getMessageTemplate(),
                        request.getLocale()));
                listFormErrors.add(genAttError);
            }
        }
        List<Entry> listEntryFirstLevel = EntryHome
                .getEntryList(EntryService.buildEntryFilter(appointmentDTO.getIdForm()));
        for (Entry entry : listEntryFirstLevel) {
            listFormErrors.addAll(EntryService.getResponseEntry(request, entry.getIdEntry(), request.getLocale(),
                    appointmentDTO));
        }
    }

    public static void fillInListResponseWithMapResponse(AppointmentDTO appointmentDTO) {
        Map<Integer, List<Response>> mapResponses = appointmentDTO.getMapResponsesByIdEntry();
        if (mapResponses != null && !mapResponses.isEmpty()) {
            List<Response> listResponse = new ArrayList<Response>();
            for (List<Response> listResponseByEntry : mapResponses.values()) {
                listResponse.addAll(listResponseByEntry);
            }
            // appointmentDTO.clearMapResponsesByIdEntry();
            appointmentDTO.setListResponse(listResponse);
        }
    }

    /**
     * Build a list of response of the appointment
     * 
     * @param appointment
     *            the appointment
     * @param request
     *            the request
     * @param locale
     *            the local
     * @return a list of response
     */
    public static List<ResponseRecapDTO> buildListResponse(AppointmentDTO appointment, HttpServletRequest request,
            Locale locale) {
        List<ResponseRecapDTO> listResponseRecapDTO = new ArrayList<ResponseRecapDTO>();
        HashMap<Integer, List<ResponseRecapDTO>> mapResponse = new HashMap<>();
        if (CollectionUtils.isNotEmpty(appointment.getListResponse())) {
            listResponseRecapDTO = new ArrayList<ResponseRecapDTO>(appointment.getListResponse().size());
            for (Response response : appointment.getListResponse()) {
                int nIndex = response.getEntry().getPosition();
                IEntryTypeService entryTypeService = EntryTypeServiceManager
                        .getEntryTypeService(response.getEntry());
                ResponseRecapDTO responseRecapDTO = new ResponseRecapDTO(response,
                        entryTypeService.getResponseValueForRecap(response.getEntry(), request, response, locale));
                List<ResponseRecapDTO> listResponse = mapResponse.get(nIndex);
                if (listResponse == null) {
                    listResponse = new ArrayList<>();
                    mapResponse.put(nIndex, listResponse);
                }
                listResponse.add(responseRecapDTO);
            }
        }
        for (List<ResponseRecapDTO> listResponse : mapResponse.values()) {
            listResponseRecapDTO.addAll(listResponse);
        }
        return listResponseRecapDTO;
    }

    /**
     * Build the excel fil of the list of the appointments found in the manage appointment viw by filter
     * 
     * @param strIdForm
     *            the form id
     * @param response
     *            the response
     * @param locale
     *            the local
     * @param listAppointmentsDTO
     *            the list of the appointments to input in the excel file
     * @param stateService
     *            the state service
     */
    public static void buildExcelFileWithAppointments(String strIdForm, HttpServletResponse response, Locale locale,
            List<AppointmentDTO> listAppointmentsDTO, StateService stateService) {
        AppointmentFormDTO tmpForm = FormService.buildAppointmentFormLight(Integer.parseInt(strIdForm));
        XSSFWorkbook workbook = new XSSFWorkbook();
        XSSFSheet sheet = workbook.createSheet(I18nService.getLocalizedString(KEY_RESOURCE_TYPE, locale));
        List<Object[]> tmpObj = new ArrayList<Object[]>();
        EntryFilter entryFilter = new EntryFilter();
        entryFilter.setIdResource(Integer.valueOf(strIdForm));
        List<Entry> listEntry = EntryHome.getEntryList(entryFilter);
        Map<Integer, String> mapDefaultValueGenAttBackOffice = new HashMap<Integer, String>();
        for (Entry e : listEntry) {
            if (e.isOnlyDisplayInBack()) {
                e = EntryHome.findByPrimaryKey(e.getIdEntry());
                if (e.getFields() != null && e.getFields().size() == 1
                        && !StringUtils.isEmpty(e.getFields().get(0).getValue())) {
                    mapDefaultValueGenAttBackOffice.put(e.getIdEntry(), e.getFields().get(0).getValue());
                } else if (e.getFields() != null) {
                    for (Field field : e.getFields()) {
                        if (field.isDefaultValue()) {
                            mapDefaultValueGenAttBackOffice.put(e.getIdEntry(), field.getValue());
                        }
                    }
                }
            }
        }
        int nTaille = 10 + (listEntry.size() + 1);
        if (tmpForm != null) {
            int nIndex = 0;
            Object[] strWriter = new String[1];
            strWriter[0] = tmpForm.getTitle();
            tmpObj.add(strWriter);
            Object[] strInfos = new String[nTaille];
            strInfos[0] = I18nService.getLocalizedString(KEY_COLUMN_LAST_NAME, locale);
            strInfos[1] = I18nService.getLocalizedString(KEY_COLUMN_FISRT_NAME, locale);
            strInfos[2] = I18nService.getLocalizedString(KEY_COLUMN_EMAIL, locale);
            strInfos[3] = I18nService.getLocalizedString(KEY_COLUMN_DATE_APPOINTMENT, locale);
            strInfos[4] = I18nService.getLocalizedString(KEY_TIME_START, locale);
            strInfos[5] = I18nService.getLocalizedString(KEY_TIME_END, locale);
            strInfos[6] = I18nService.getLocalizedString(KEY_COLUMN_ADMIN, locale);
            strInfos[7] = I18nService.getLocalizedString(KEY_COLUMN_STATUS, locale);
            strInfos[8] = I18nService.getLocalizedString(KEY_COLUMN_STATE, locale);
            strInfos[9] = I18nService.getLocalizedString(KEY_COLUMN_NB_BOOKED_SEATS, locale);
            nIndex = 1;
            if (listEntry.size() > 0) {
                for (Entry e : listEntry) {
                    strInfos[10 + nIndex] = e.getTitle();
                    nIndex++;
                }
            }
            tmpObj.add(strInfos);
        }
        if (listAppointmentsDTO != null) {
            for (AppointmentDTO appointmentDTO : listAppointmentsDTO) {
                int nIndex = 0;
                Object[] strWriter = new String[nTaille];
                strWriter[0] = appointmentDTO.getLastName();
                strWriter[1] = appointmentDTO.getFirstName();
                strWriter[2] = appointmentDTO.getEmail();
                strWriter[3] = appointmentDTO.getDateOfTheAppointment();
                strWriter[4] = appointmentDTO.getStartingTime().toString();
                strWriter[5] = appointmentDTO.getEndingTime().toString();
                strWriter[6] = appointmentDTO.getAdminUser();
                String status = I18nService.getLocalizedString(AppointmentDTO.PROPERTY_APPOINTMENT_STATUS_RESERVED,
                        locale);
                if (appointmentDTO.getIsCancelled()) {
                    status = I18nService.getLocalizedString(AppointmentDTO.PROPERTY_APPOINTMENT_STATUS_UNRESERVED,
                            locale);
                }
                strWriter[7] = status;
                State stateAppointment = stateService.findByResource(appointmentDTO.getIdAppointment(),
                        Appointment.APPOINTMENT_RESOURCE_TYPE, tmpForm.getIdWorkflow());
                String strState = StringUtils.EMPTY;
                if (stateAppointment != null) {
                    appointmentDTO.setState(stateAppointment);
                    strState = stateAppointment.getName();
                }
                strWriter[8] = strState;
                nIndex = 1;
                strWriter[9] = Integer.toString(appointmentDTO.getNbBookedSeats());
                List<Integer> listIdResponse = AppointmentResponseService
                        .findListIdResponse(appointmentDTO.getIdAppointment());
                List<Response> listResponses = new ArrayList<Response>();
                for (int nIdResponse : listIdResponse) {
                    Response resp = ResponseHome.findByPrimaryKey(nIdResponse);
                    if (resp != null) {
                        listResponses.add(resp);
                    }
                }
                for (Entry e : listEntry) {
                    Integer key = e.getIdEntry();
                    StringBuffer strValue = new StringBuffer(StringUtils.EMPTY);
                    String strPrefix = StringUtils.EMPTY;
                    for (Response resp : listResponses) {
                        String strRes = StringUtils.EMPTY;
                        if (key.equals(resp.getEntry().getIdEntry())) {
                            Field f = resp.getField();
                            int nfield = 0;
                            if (f != null) {
                                nfield = f.getIdField();
                                Field field = FieldHome.findByPrimaryKey(nfield);
                                if (field != null) {
                                    strRes = field.getTitle();
                                }
                            } else {
                                strRes = resp.getResponseValue();
                            }
                        }
                        if ((strRes != null) && !strRes.isEmpty()) {
                            strValue.append(strPrefix + strRes);
                            strPrefix = CONSTANT_COMMA;
                        }
                    }
                    if (strValue.toString().isEmpty() && mapDefaultValueGenAttBackOffice.containsKey(key)) {
                        strValue.append(mapDefaultValueGenAttBackOffice.get(key));
                    }
                    if (!strValue.toString().isEmpty()) {
                        strWriter[10 + nIndex] = strValue.toString();
                    }
                    nIndex++;
                }
                tmpObj.add(strWriter);
            }
        }
        int nRownum = 0;
        for (Object[] myObj : tmpObj) {
            Row row = sheet.createRow(nRownum++);
            int nCellnum = 0;
            for (Object strLine : myObj) {
                Cell cell = row.createCell(nCellnum++);
                if (strLine instanceof String) {
                    cell.setCellValue((String) strLine);
                } else if (strLine instanceof Boolean) {
                    cell.setCellValue((Boolean) strLine);
                } else if (strLine instanceof Date) {
                    cell.setCellValue((Date) strLine);
                } else if (strLine instanceof Double) {
                    cell.setCellValue((Double) strLine);
                }
            }
        }
        try {
            String now = new SimpleDateFormat("yyyyMMdd-hhmm")
                    .format(GregorianCalendar.getInstance(locale).getTime()) + "_"
                    + I18nService.getLocalizedString(KEY_RESOURCE_TYPE, locale) + EXCEL_FILE_EXTENSION;
            response.setContentType(EXCEL_MIME_TYPE);
            response.setHeader("Content-Disposition", "attachment; filename=\"" + now + "\";");
            response.setHeader("Pragma", "public");
            response.setHeader("Expires", "0");
            response.setHeader("Cache-Control", "must-revalidate,post-check=0,pre-check=0");
            OutputStream os = response.getOutputStream();
            workbook.write(os);
            os.close();
            workbook.close();
        } catch (IOException e) {
            AppLogService.error(e);
        }
    }

    /**
     * Kill the lock timer on a slot
     * 
     * @param request
     *            the request
     */
    public static void killTimer(HttpServletRequest request) {
        Timer timer = (Timer) request.getSession().getAttribute(SESSION_TIMER_SLOT);
        if (timer != null) {
            timer.cancel();
            request.getSession().removeAttribute(SESSION_TIMER_SLOT);
        }
    }

    /**
     * Create a timer on a slot
     * 
     * @param slot
     *            the slot
     * @param appointmentDTO
     *            the appointment
     * @param maxPeoplePerAppointment
     *            the max people per appointment
     * @return the timer
     */
    public synchronized static Timer putTimerInSession(HttpServletRequest request, int nIdSlot,
            AppointmentDTO appointmentDTO, int maxPeoplePerAppointment) {
        Slot slot = SlotService.findSlotById(nIdSlot);

        int nbPotentialRemainingPlaces = slot.getNbPotentialRemainingPlaces();
        int nbPotentialPlacesTaken = Math.min(nbPotentialRemainingPlaces, maxPeoplePerAppointment);

        if (slot.getNbPotentialRemainingPlaces() > 0) {

            appointmentDTO.setNbMaxPotentialBookedSeats(nbPotentialPlacesTaken);
            slot.setNbPotentialRemainingPlaces(nbPotentialRemainingPlaces - nbPotentialPlacesTaken);
            SlotService.updateSlot(slot);
            SlotEditTask slotEditTask = new SlotEditTask();
            slotEditTask.setNbPlacesTaken(nbPotentialPlacesTaken);
            slotEditTask.setIdSlot(slot.getIdSlot());
            TimerForLockOnSlot timer = new TimerForLockOnSlot();
            long delay = TimeUnit.MINUTES.toMillis(
                    AppPropertiesService.getPropertyInt(PROPERTY_DEFAULT_EXPIRED_TIME_EDIT_APPOINTMENT, 1));
            timer.schedule(slotEditTask, delay);
            request.getSession().setAttribute(AppointmentUtilities.SESSION_TIMER_SLOT, timer);
            return timer;
        }
        appointmentDTO.setNbMaxPotentialBookedSeats(0);
        return null;
    }

    /**
     * Get Form Permissions
     * 
     * @param listForms
     * @param request
     * @return
     */
    public static String[][] getPermissions(List<AppointmentFormDTO> listForms, AdminUser user) {
        String[][] retour = new String[listForms.size()][6];
        int nI = 0;

        for (AppointmentFormDTO tmpForm : listForms) {
            String[] strRetour = new String[7];
            strRetour[0] = String.valueOf(
                    RBACService.isAuthorized(AppointmentFormDTO.RESOURCE_TYPE, String.valueOf(tmpForm.getIdForm()),
                            AppointmentResourceIdService.PERMISSION_VIEW_APPOINTMENT, user));
            strRetour[1] = String.valueOf(
                    RBACService.isAuthorized(AppointmentFormDTO.RESOURCE_TYPE, String.valueOf(tmpForm.getIdForm()),
                            AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM, user));
            strRetour[2] = String.valueOf(
                    RBACService.isAuthorized(AppointmentFormDTO.RESOURCE_TYPE, String.valueOf(tmpForm.getIdForm()),
                            AppointmentResourceIdService.PERMISSION_MODIFY_FORM, user));
            strRetour[3] = String.valueOf(
                    RBACService.isAuthorized(AppointmentFormDTO.RESOURCE_TYPE, String.valueOf(tmpForm.getIdForm()),
                            AppointmentResourceIdService.PERMISSION_MODIFY_FORM, user));
            strRetour[4] = String.valueOf(
                    RBACService.isAuthorized(AppointmentFormDTO.RESOURCE_TYPE, String.valueOf(tmpForm.getIdForm()),
                            AppointmentResourceIdService.PERMISSION_CHANGE_STATE, user));
            strRetour[5] = String.valueOf(
                    RBACService.isAuthorized(AppointmentFormDTO.RESOURCE_TYPE, String.valueOf(tmpForm.getIdForm()),
                            AppointmentResourceIdService.PERMISSION_DELETE_FORM, user));
            retour[nI++] = strRetour;
        }

        return retour;
    }

    /**
     * Return the min starting time to display
     * 
     * @param minStartingTime
     *            the min starting time
     * @return 00 if the minstarting time is under 30, 30 otherwise
     */
    public static LocalTime getMinTimeToDisplay(LocalTime minStartingTime) {
        LocalTime minStartingTimeToDisplay;
        if (minStartingTime.getMinute() < THIRTY_MINUTES) {
            minStartingTimeToDisplay = LocalTime.of(minStartingTime.getHour(), 0);
        } else {
            minStartingTimeToDisplay = LocalTime.of(minStartingTime.getHour(), THIRTY_MINUTES);
        }
        return minStartingTimeToDisplay;
    }

    /**
     * Return the max ending time to display
     * 
     * @param maxEndingTime
     *            the max ending time
     * @return 30 if the max ending time is under 30, otherwise the next hour
     */
    public static LocalTime getMaxTimeToDisplay(LocalTime maxEndingTime) {
        LocalTime maxEndingTimeToDisplay;
        if (maxEndingTime.getMinute() < THIRTY_MINUTES) {
            maxEndingTimeToDisplay = LocalTime.of(maxEndingTime.getHour(), THIRTY_MINUTES);
        } else {
            maxEndingTimeToDisplay = LocalTime.of(maxEndingTime.getHour() + 1, 0);
        }
        return maxEndingTimeToDisplay;
    }

    /**
     * Check if there are appointments impacted by the new week definition
     * 
     * @param listAppointment
     *            the list of appointments
     * @param nIdForm
     *            the form Id
     * @param dateOfModification
     *            the date of modification (date of apply of the new week definition)
     * @param appointmentForm
     *            the appointment form
     * @return true if there are appointments impacted
     */
    public static boolean checkNoAppointmentsImpacted(List<Appointment> listAppointment, int nIdForm,
            LocalDate dateOfModification, AppointmentFormDTO appointmentForm) {
        boolean bNoAppointmentsImpacted = true;
        // Find the previous WeekDefinition
        WeekDefinition previousWeekDefinition = WeekDefinitionService
                .findWeekDefinitionByIdFormAndClosestToDateOfApply(nIdForm, dateOfModification);
        // Find the previous reservation rule
        ReservationRule previousReservationRule = ReservationRuleService
                .findReservationRuleByIdFormAndClosestToDateOfApply(nIdForm, dateOfModification);
        // Build the previous appointment form with the previous week
        // definition and the previous reservation rule
        AppointmentFormDTO previousAppointmentForm = FormService.buildAppointmentForm(nIdForm,
                previousReservationRule.getIdReservationRule(), previousWeekDefinition.getIdWeekDefinition());
        // Need to check if the new definition week has more open days.
        List<DayOfWeek> previousOpenDays = WorkingDayService.getOpenDays(previousAppointmentForm);
        List<DayOfWeek> newOpenDays = WorkingDayService.getOpenDays(appointmentForm);
        // If new open days
        if (newOpenDays.containsAll(previousOpenDays)) {
            // Nothing to check
        } else {
            // Else we remove all the corresponding days
            previousOpenDays.removeAll(newOpenDays);
            // For the remaining days
            // for each appointment, need to check if the appointment is
            // not in the remaining open days
            boolean bAppointmentOnOpenDays = false;
            for (Appointment appointment : listAppointment) {
                Slot tempSlot = SlotService.findSlotById(appointment.getIdSlot());
                if (previousOpenDays.contains(tempSlot.getStartingDateTime().getDayOfWeek())) {
                    bAppointmentOnOpenDays = true;
                    break;
                }
            }
            bNoAppointmentsImpacted = !bAppointmentOnOpenDays;
        }
        LocalTime newStartingTime = LocalTime.parse(appointmentForm.getTimeStart());
        LocalTime newEndingTime = LocalTime.parse(appointmentForm.getTimeEnd());
        LocalTime oldStartingTime = LocalTime.parse(previousAppointmentForm.getTimeStart());
        LocalTime oldEndingTime = LocalTime.parse(previousAppointmentForm.getTimeEnd());
        // If we have changed the duration of an appointment
        if (appointmentForm.getDurationAppointments() != previousAppointmentForm.getDurationAppointments()) {
            bNoAppointmentsImpacted = false;
        }
        // We have change the open hours
        // if the time slot is reduced
        if (newStartingTime.isAfter(oldStartingTime) || newEndingTime.isBefore(oldEndingTime)) {
            bNoAppointmentsImpacted = false;
        }

        return bNoAppointmentsImpacted;
    }

    /**
     * Check that there is no validated appointments on a slot
     * 
     * @param slot
     *            the slot
     * @return true if there are no validated appointments on this slot, false otherwise
     */
    public static boolean checkNoValidatedAppointmentsOnThisSlot(Slot slot) {
        boolean bNoValidatedAppointmentsOnThisSlot = true;
        List<Appointment> listAppointmentsOnThisSlot = AppointmentService
                .findListAppointmentBySlot(slot.getIdSlot());
        if (CollectionUtils.isNotEmpty(listAppointmentsOnThisSlot)) {
            listAppointmentsOnThisSlot = listAppointmentsOnThisSlot.stream()
                    .filter(a -> a.getIsCancelled() == false).collect(Collectors.toList());
        }
        if (CollectionUtils.isNotEmpty(listAppointmentsOnThisSlot)) {
            bNoValidatedAppointmentsOnThisSlot = false;
        }
        return bNoValidatedAppointmentsOnThisSlot;
    }

    /**
     * Return the slots impacted by the modification of this time slot
     * 
     * @param timeSlot
     *            the time slot
     * @param nIdForm
     *            the form id
     * @param nIdWeekDefinition
     *            the week definition id
     * @param bShiftSlot
     *            the boolean value for the shift
     * @return the list of slots impacted
     */
    public static List<Slot> findSlotsImpactedByThisTimeSlot(TimeSlot timeSlot, int nIdForm, int nIdWeekDefinition,
            boolean bShiftSlot) {
        List<Slot> listSlotsImpacted = new ArrayList<>();
        LocalDate maxDate = null;
        // Get the weekDefinition that is currently modified
        WeekDefinition currentModifiedWeekDefinition = WeekDefinitionService
                .findWeekDefinitionById(nIdWeekDefinition);
        // Find the next weekDefinition, if exist, to have the max date to
        // search slots with appointments
        WeekDefinition nextWeekDefinition = WeekDefinitionService.findNextWeekDefinition(nIdForm,
                currentModifiedWeekDefinition.getDateOfApply());
        if (nextWeekDefinition != null) {
            maxDate = nextWeekDefinition.getDateOfApply();
        } else {
            // If there is no next weekDefinition
            // Get the ending validity date of the form
            Form form = FormService.findFormLightByPrimaryKey(nIdForm);
            if (form.getEndingValidityDate() != null) {
                maxDate = form.getEndingValidityDate();
            } else {
                // If there is no ending validity date
                // Find the slot with the max date
                Slot slotWithMaxDate = SlotService.findSlotWithMaxDate(nIdForm);
                if (slotWithMaxDate != null && slotWithMaxDate.getStartingDateTime() != null) {
                    maxDate = slotWithMaxDate.getStartingDateTime().toLocalDate();
                }
            }
        }
        if (maxDate != null) {
            // We have an upper bound to search with
            List<Slot> listSlots = SlotService.findSlotsByIdFormAndDateRange(nIdForm,
                    currentModifiedWeekDefinition.getDateOfApply().atStartOfDay(), maxDate.atTime(LocalTime.MAX));
            // Need to check if the modification of the time slot or the typical
            // week impacts these slots
            WorkingDay workingDay = WorkingDayService.findWorkingDayLightById(timeSlot.getIdWorkingDay());
            // Filter all the slots with the working day and the starting time
            // ending time of the time slot
            // The begin time of the slot can be before or after the begin time
            // of the time slot
            // and the ending time of the slot can be before or after the ending
            // time of the time slot (specific slot)

            // If shiftTimeSlot is checked, need to check all the slots impacted
            // until the end of the day
            if (bShiftSlot) {
                listSlotsImpacted = listSlots.stream()
                        .filter(slot -> ((slot.getStartingDateTime().getDayOfWeek() == DayOfWeek
                                .of(workingDay.getDayOfWeek()))
                                && (!slot.getStartingTime().isBefore(timeSlot.getStartingTime())
                                        || (slot.getStartingTime().isBefore(timeSlot.getStartingTime())
                                                && (slot.getEndingTime().isAfter(timeSlot.getStartingTime()))))))
                        .collect(Collectors.toList());
            } else {
                listSlotsImpacted = listSlots.stream()
                        .filter(slot -> (slot.getStartingDateTime().getDayOfWeek() == DayOfWeek
                                .of(workingDay.getDayOfWeek()))
                                && (slot.getStartingTime().equals(timeSlot.getStartingTime())
                                        || (slot.getStartingTime().isBefore(timeSlot.getStartingTime())
                                                && (slot.getEndingTime().isAfter(timeSlot.getStartingTime())))
                                        || (slot.getStartingTime().isAfter(timeSlot.getStartingTime())
                                                && (!slot.getEndingTime().isAfter(timeSlot.getEndingTime())))))
                        .collect(Collectors.toList());
            }
        }
        return listSlotsImpacted;
    }
}