View Javadoc
1   /*
2    * Copyright (c) 2002-2022, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.appointment.service.export;
35  
36  import java.io.IOException;
37  import java.io.OutputStream;
38  import java.nio.file.Files;
39  import java.nio.file.Path;
40  import java.sql.Date;
41  import java.util.ArrayList;
42  import java.util.Arrays;
43  import java.util.HashMap;
44  import java.util.List;
45  import java.util.Locale;
46  import java.util.Map;
47  import java.util.stream.Collectors;
48  
49  import org.apache.commons.collections.CollectionUtils;
50  import org.apache.commons.lang3.StringUtils;
51  import org.apache.poi.ss.usermodel.Cell;
52  import org.apache.poi.ss.usermodel.Row;
53  import org.apache.poi.xssf.usermodel.XSSFSheet;
54  import org.apache.poi.xssf.usermodel.XSSFWorkbook;
55  
56  import fr.paris.lutece.plugins.appointment.business.appointment.Appointment;
57  import fr.paris.lutece.plugins.appointment.business.category.Category;
58  import fr.paris.lutece.plugins.appointment.business.category.CategoryHome;
59  import fr.paris.lutece.plugins.appointment.business.form.Form;
60  import fr.paris.lutece.plugins.appointment.business.form.FormHome;
61  import fr.paris.lutece.plugins.appointment.service.AppointmentResponseService;
62  import fr.paris.lutece.plugins.appointment.service.Utilities;
63  import fr.paris.lutece.plugins.appointment.service.entrytype.EntryTypeGroup;
64  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentDTO;
65  import fr.paris.lutece.plugins.genericattributes.business.Entry;
66  import fr.paris.lutece.plugins.genericattributes.business.EntryFilter;
67  import fr.paris.lutece.plugins.genericattributes.business.EntryHome;
68  import fr.paris.lutece.plugins.genericattributes.business.Field;
69  import fr.paris.lutece.plugins.genericattributes.business.FieldHome;
70  import fr.paris.lutece.plugins.genericattributes.business.Response;
71  import fr.paris.lutece.plugins.genericattributes.business.ResponseHome;
72  import fr.paris.lutece.plugins.genericattributes.service.entrytype.EntryTypeServiceManager;
73  import fr.paris.lutece.plugins.workflowcore.business.state.State;
74  import fr.paris.lutece.plugins.workflowcore.service.state.StateService;
75  import fr.paris.lutece.portal.service.i18n.I18nService;
76  import fr.paris.lutece.portal.service.spring.SpringContextService;
77  import fr.paris.lutece.portal.service.util.AppLogService;
78  import fr.paris.lutece.portal.service.workflow.WorkflowService;
79  import fr.paris.lutece.util.ReferenceList;
80  
81  public final class AppointmentExportService
82  {
83  
84      private static final String KEY_RESOURCE_TYPE = "appointment.appointment.name";
85      private static final String KEY_COLUMN_FORM_CATEGORY = "appointment.manageAppointments.columnFormCategory";
86      private static final String KEY_COLUMN_FORM_TITLE = "appointment.manageAppointments.columnFormTitle";
87      private static final String KEY_COLUMN_LAST_NAME = "appointment.manageAppointments.columnLastName";
88      private static final String KEY_COLUMN_FIRST_NAME = "appointment.manageAppointments.columnFirstName";
89      private static final String KEY_COLUMN_EMAIL = "appointment.manageAppointments.columnEmail";
90      private static final String KEY_COLUMN_DATE_APPOINTMENT = "appointment.dateAppointment.title";
91      private static final String KEY_TIME_START = "appointment.model.entity.appointmentform.attribute.timeStart";
92      private static final String KEY_TIME_END = "appointment.model.entity.appointmentform.attribute.timeEnd";
93      private static final String KEY_COLUMN_ADMIN = "appointment.manageAppointments.columnAdmin";
94      private static final String KEY_COLUMN_STATUS = "appointment.labelStatus";
95      private static final String KEY_COLUMN_STATE = "appointment.manageAppointments.columnState";
96      private static final String KEY_COLUMN_NB_BOOKED_SEATS = "appointment.manageAppointments.columnNumberOfBookedseatsPerAppointment";
97      private static final String KEY_DATE_APPOINT_TAKEN = "appointment.model.entity.appointmentform.attribute.dateTaken";
98      private static final String KEY_HOUR_APPOINT_TAKEN = "appointment.model.entity.appointmentform.attribute.hourTaken";
99  
100     private static final String CONSTANT_COMMA = ",";
101 
102     private static final List<String> DEFAULT_COLUMN_LIST = Arrays.asList( KEY_COLUMN_FORM_CATEGORY, KEY_COLUMN_FORM_TITLE, KEY_COLUMN_LAST_NAME,
103             KEY_COLUMN_FIRST_NAME, KEY_COLUMN_EMAIL, KEY_COLUMN_DATE_APPOINTMENT, KEY_TIME_START, KEY_TIME_END, KEY_COLUMN_ADMIN, KEY_COLUMN_STATUS,
104             KEY_COLUMN_STATE, KEY_COLUMN_NB_BOOKED_SEATS, KEY_DATE_APPOINT_TAKEN, KEY_HOUR_APPOINT_TAKEN );
105 
106     private AppointmentExportService( )
107     {
108     }
109 
110     /**
111      * Build the excel fil of the list of the appointments found in the manage appointment viw by filter
112      * 
113      * @param excelFile
114      *            the excel file to write
115      * @param defaultColumnList
116      *            the default columns to export
117      * @param entryList
118      *            the entries to export
119      * @param locale
120      *            the local
121      * @param listAppointmentsDTO
122      *            the list of the appointments to input in the excel file
123      */
124     public static void buildExcelFileWithAppointments( List<String> defaultColumnList, List<Integer> entryList, Path excelFile, Locale locale,
125             List<AppointmentDTO> listAppointmentsDTO )
126     {
127         List<List<Object>> linesValues = new ArrayList<>( );
128         EntryFilter entryFilter = new EntryFilter( );
129         List<Entry> listEntry = EntryHome.getEntryList( entryFilter ).stream( ).filter( e -> entryList.contains( e.getIdEntry( ) ) ).map( Entry::getIdEntry )
130                 .map( EntryHome::findByPrimaryKey ).collect( Collectors.toList( ) );
131 
132         linesValues.add( createHeaderContent( defaultColumnList, listEntry, locale ) );
133 
134         if ( listAppointmentsDTO != null )
135         {
136             StateService stateService = null;
137             if ( WorkflowService.getInstance( ).isAvailable( ) )
138             {
139                 stateService = SpringContextService.getBean( StateService.BEAN_SERVICE );
140             }
141             Map<Integer, Form> formMap = new HashMap<>( );
142             for ( AppointmentDTO appointmentDTO : listAppointmentsDTO )
143             {
144                 Form form = formMap.computeIfAbsent( appointmentDTO.getIdForm( ), FormHome::findByPrimaryKey );
145                 linesValues.add( createLineContent( appointmentDTO, form, defaultColumnList, listEntry, stateService, locale ) );
146             }
147         }
148         writeWorkbook( linesValues, excelFile, locale );
149     }
150 
151     private static final void writeWorkbook( List<List<Object>> linesValues, Path excelFile, Locale locale )
152     {
153         int nRownum = 0;
154         try ( XSSFWorkbook workbook = new XSSFWorkbook( ) ; OutputStream os = Files.newOutputStream( excelFile ) )
155         {
156             XSSFSheet sheet = workbook.createSheet( I18nService.getLocalizedString( KEY_RESOURCE_TYPE, locale ) );
157             for ( List<Object> line : linesValues )
158             {
159                 Row row = sheet.createRow( nRownum++ );
160                 int nCellnum = 0;
161                 for ( Object cellValue : line )
162                 {
163                     Cell cell = row.createCell( nCellnum++ );
164                     if ( cellValue instanceof String )
165                     {
166                         cell.setCellValue( (String) cellValue );
167                     }
168                     else
169                         if ( cellValue instanceof Boolean )
170                         {
171                             cell.setCellValue( (Boolean) cellValue );
172                         }
173                         else
174                             if ( cellValue instanceof Date )
175                             {
176                                 cell.setCellValue( (Date) cellValue );
177                             }
178                             else
179                                 if ( cellValue instanceof Double )
180                                 {
181                                     cell.setCellValue( (Double) cellValue );
182                                 }
183                 }
184             }
185             workbook.write( os );
186         }
187         catch( IOException e )
188         {
189             AppLogService.error( e );
190         }
191     }
192 
193     private static final List<Object> createHeaderContent( List<String> defaultColumnList, List<Entry> listEntry, Locale locale )
194     {
195         List<Object> strInfos = new ArrayList<>( );
196         for ( String key : defaultColumnList )
197         {
198             strInfos.add( I18nService.getLocalizedString( key, locale ) );
199         }
200 
201         if ( CollectionUtils.isNotEmpty( listEntry ) )
202         {
203             for ( Entry e : listEntry )
204             {
205                 strInfos.add( e.getTitle( ) );
206             }
207         }
208         return strInfos;
209     }
210 
211     private static final List<Object> createLineContent( AppointmentDTO appointmentDTO, Form form, List<String> defaultColumnList, List<Entry> listEntry,
212             StateService stateService, Locale locale )
213     {
214         List<Object> strWriter = new ArrayList<>( );
215         addDefaultColumnValues( appointmentDTO, form, defaultColumnList, strWriter, stateService, locale );
216 
217         List<Integer> listIdResponse = AppointmentResponseService.findListIdResponse( appointmentDTO.getIdAppointment( ) );
218         List<Response> listResponses = new ArrayList<>( );
219         for ( int nIdResponse : listIdResponse )
220         {
221             Response resp = ResponseHome.findByPrimaryKey( nIdResponse );
222             if ( resp != null )
223             {
224                 listResponses.add( resp );
225             }
226         }
227         for ( Entry e : listEntry )
228         {
229             String value = getEntryValue( e, listResponses, locale );
230             strWriter.add( value );
231         }
232         return strWriter;
233     }
234 
235     private static final void addDefaultColumnValues( AppointmentDTO appointmentDTO, Form form, List<String> defaultColumnList, List<Object> strWriter,
236             StateService stateService, Locale locale )
237     {
238         if ( defaultColumnList.contains( KEY_COLUMN_FORM_CATEGORY ) )
239         {
240             Category category = CategoryHome.findByPrimaryKey( form.getIdCategory( ) );
241             String catStr = "";
242             if ( category != null )
243             {
244                 catStr = category.getLabel( );
245             }
246             strWriter.add( catStr );
247         }
248         if ( defaultColumnList.contains( KEY_COLUMN_FORM_TITLE ) )
249         {
250             strWriter.add( form.getTitle( ) );
251         }
252         if ( defaultColumnList.contains( KEY_COLUMN_LAST_NAME ) )
253         {
254             strWriter.add( appointmentDTO.getLastName( ) );
255         }
256         if ( defaultColumnList.contains( KEY_COLUMN_FIRST_NAME ) )
257         {
258             strWriter.add( appointmentDTO.getFirstName( ) );
259         }
260         if ( defaultColumnList.contains( KEY_COLUMN_EMAIL ) )
261         {
262             strWriter.add( appointmentDTO.getEmail( ) );
263         }
264         if ( defaultColumnList.contains( KEY_COLUMN_DATE_APPOINTMENT ) )
265         {
266             strWriter.add( appointmentDTO.getDateOfTheAppointment( ) );
267         }
268         if ( defaultColumnList.contains( KEY_TIME_START ) )
269         {
270             strWriter.add( appointmentDTO.getStartingTime( ).toString( ) );
271         }
272         if ( defaultColumnList.contains( KEY_TIME_END ) )
273         {
274             strWriter.add( appointmentDTO.getEndingTime( ).toString( ) );
275         }
276         if ( defaultColumnList.contains( KEY_COLUMN_ADMIN ) )
277         {
278             strWriter.add( appointmentDTO.getAdminUser( ) );
279         }
280         if ( defaultColumnList.contains( KEY_COLUMN_STATUS ) )
281         {
282             strWriter.add( getStatusValue( appointmentDTO, locale ) );
283         }
284         if ( defaultColumnList.contains( KEY_COLUMN_STATE ) )
285         {
286             strWriter.add( getStateValue( appointmentDTO, form.getIdWorkflow( ), stateService ) );
287         }
288         if ( defaultColumnList.contains( KEY_COLUMN_NB_BOOKED_SEATS ) )
289         {
290             strWriter.add( Integer.toString( appointmentDTO.getNbBookedSeats( ) ) );
291         }
292         if ( defaultColumnList.contains( KEY_DATE_APPOINT_TAKEN ) )
293         {
294             strWriter.add( appointmentDTO.getDateAppointmentTaken( ).toLocalDate( ).format( Utilities.getFormatter( ) ) );
295         }
296         if ( defaultColumnList.contains( KEY_HOUR_APPOINT_TAKEN ) )
297         {
298             strWriter.add( appointmentDTO.getDateAppointmentTaken( ).toLocalTime( ).withSecond( 0 ).toString( ) );
299         }
300     }
301 
302     private static String getStatusValue( AppointmentDTO appointmentDTO, Locale locale )
303     {
304         String status = I18nService.getLocalizedString( AppointmentDTO.PROPERTY_APPOINTMENT_STATUS_RESERVED, locale );
305         if ( appointmentDTO.getIsCancelled( ) )
306         {
307             status = I18nService.getLocalizedString( AppointmentDTO.PROPERTY_APPOINTMENT_STATUS_UNRESERVED, locale );
308         }
309         return status;
310     }
311 
312     private static String getStateValue( AppointmentDTO appointmentDTO, int idWorkflow, StateService stateService )
313     {
314         String strState = StringUtils.EMPTY;
315         if ( stateService != null )
316         {
317             State stateAppointment = stateService.findByResource( appointmentDTO.getIdAppointment( ), Appointment.APPOINTMENT_RESOURCE_TYPE, idWorkflow );
318             if ( stateAppointment != null )
319             {
320                 appointmentDTO.setState( stateAppointment );
321                 strState = stateAppointment.getName( );
322             }
323         }
324         return strState;
325     }
326 
327     private static final String getEntryValue( Entry e, List<Response> listResponses, Locale locale )
328     {
329         Integer key = e.getIdEntry( );
330         StringBuilder strValue = new StringBuilder( );
331         String strPrefix = StringUtils.EMPTY;
332 
333         List<Response> listResponsesForEntry = listResponses.stream( ).filter( resp -> key.equals( resp.getEntry( ).getIdEntry( ) ) )
334                 .filter( resp -> StringUtils.isNotEmpty( resp.getResponseValue( ) ) ).collect( Collectors.toList( ) );
335 
336         for ( Response resp : listResponsesForEntry )
337         {
338             Field f = resp.getField( );
339             if ( f != null )
340             {
341                 resp.setField( FieldHome.findByPrimaryKey( f.getIdField( ) ) );
342             }
343 
344             String valueExport = EntryTypeServiceManager.getEntryTypeService( e ).getResponseValueForExport( e, null, resp, locale );
345             if ( StringUtils.isNotEmpty( valueExport ) )
346             {
347                 strValue.append( strPrefix + valueExport );
348                 strPrefix = CONSTANT_COMMA;
349             }
350         }
351         return strValue.toString( );
352     }
353 
354     public static ReferenceList getDefaultColumnList( Locale locale )
355     {
356         ReferenceList refList = new ReferenceList( );
357         for ( String key : DEFAULT_COLUMN_LIST )
358         {
359             refList.addItem( key, I18nService.getLocalizedString( key, locale ) );
360         }
361         return refList;
362     }
363 
364     public static ReferenceList getCustomColumnList( String strIdForm )
365     {
366         EntryFilter entryFilter = new EntryFilter( );
367         entryFilter.setIdResource( Integer.valueOf( strIdForm ) );
368         List<Entry> listEntry = EntryHome.getEntryList( entryFilter );
369 
370         ReferenceList refList = new ReferenceList( );
371         for ( Entry entry : listEntry )
372         {
373             if ( !( EntryTypeServiceManager.getEntryTypeService( entry ) instanceof EntryTypeGroup ) )
374             {
375                 refList.addItem( String.valueOf( entry.getIdEntry( ) ), entry.getTitle( ) );
376             }
377         }
378         return refList;
379     }
380 }