View Javadoc
1   /*
2    * Copyright (c) 2002-2020, 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.participatoryideation.service.campaign;
35  
36  import java.sql.Timestamp;
37  import java.time.LocalDateTime;
38  import java.util.Collection;
39  import java.util.Comparator;
40  import java.util.Date;
41  import java.util.HashMap;
42  import java.util.Map;
43  import java.util.Optional;
44  import java.util.stream.Stream;
45  
46  import org.apache.commons.lang.StringUtils;
47  
48  import fr.paris.lutece.plugins.participatoryideation.business.proposal.Proposal;
49  import fr.paris.lutece.plugins.participatoryideation.business.submitter.SubmitterType;
50  import fr.paris.lutece.plugins.participatoryideation.business.submitter.SubmitterTypeHome;
51  import fr.paris.lutece.plugins.participatoryideation.util.ParticipatoryIdeationConstants;
52  import fr.paris.lutece.portal.service.spring.SpringContextService;
53  import fr.paris.lutece.portal.service.util.AppLogService;
54  import fr.paris.lutece.portal.service.util.AppPropertiesService;
55  import fr.paris.lutece.util.ReferenceItem;
56  import fr.paris.lutece.util.ReferenceList;
57  
58  /**
59   * This class provides ideation campaign default data. Campaign phases are considered as opened.
60   *
61   * TODO : some data should be configurable in a simple way in BO (for example, string with list of values separated by comma...)
62   */
63  public class IdeationCampaignDataProvider implements IIdeationCampaignDataProvider
64  {
65  
66      private static String PROPERTY_PREFIX = "participatoryideation.campaign.";
67      private static String PROPERTY_SEP = ";";
68  
69      // Campaigns
70      private static ReferenceList campaigns = null;
71  
72      // Themes
73      // Map :
74      // Key = campaign code
75      // Value = Reference list with code and label
76      private static Map<String, ReferenceList> themeLabels = null;
77  
78      // Themes
79      // Map :
80      // Key = campaign code
81      // Value = Reference list with code and front color rgb
82      private static Map<String, ReferenceList> themeFrontRgbs = null;
83  
84      // Areas
85      // Map :
86      // Key = campaign code
87      // Value = Reference list with code and label
88      private static Map<String, ReferenceList> areaLabels = null;
89  
90      // Area types
91      // Map :
92      // Key = campaign code
93      // Value = Reference list with code and type (whole / localized)
94      private static Map<String, ReferenceList> areaTypes = null;
95  
96      // Phase date
97      // Map :
98      // Key = campaign code
99      // Value = Array with two values : begin datetime, end datetime
100     private static Map<String, Timestamp [ ]> dates = null;
101 
102     //
103     // Areas Map :
104     // Key = campaign code
105     // Value = Map :
106     // Key = field code
107     // Value = data
108     //
109     private static Map<String, Map<String, String [ ]>> fields = null;
110 
111     // *********************************************************************************************
112     // * SINGLETON SINGLETON SINGLETON SINGLETON SINGLETON SINGLETON SINGLETON SINGLETON SINGLETON *
113     // * SINGLETON SINGLETON SINGLETON SINGLETON SINGLETON SINGLETON SINGLETON SINGLETON SINGLETON *
114     // *********************************************************************************************
115 
116     private static final String BEAN_IDEATIONCAMPAIGN_DATA_PROVIDER = "participatoryideation.ideationCampaignDataProvider";
117 
118     private static IIdeationCampaignDataProvider _singleton;
119 
120     public static IIdeationCampaignDataProvider getInstance( )
121     {
122         if ( _singleton == null )
123         {
124             _singleton = SpringContextService.getBean( BEAN_IDEATIONCAMPAIGN_DATA_PROVIDER );
125             loadProperties( );
126         }
127         return _singleton;
128     }
129 
130     // *********************************************************************************************
131     // * CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN *
132     // * CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN CAMPAIGN *
133     // *********************************************************************************************
134 
135     @Override
136     public ReferenceList getCampaigns( )
137     {
138         return campaigns;
139     }
140 
141     @Override
142     public ReferenceItem getLastCampaign( )
143     {
144         return getCampaigns( ).stream( ).sorted( new Comparator<ReferenceItem>( )
145         {
146             @Override
147             public int compare( ReferenceItem c1, ReferenceItem c2 )
148             {
149                 return c2.getCode( ).compareTo( c1.getCode( ) );
150             }
151         } ).findFirst( ).get( );
152     }
153 
154     // *********************************************************************************************
155     // * PHASES PHASES PHASES PHASES PHASES PHASES PHASES PHASES PHASES PHASES PHASES PHASES PHASE *
156     // * PHASES PHASES PHASES PHASES PHASES PHASES PHASES PHASES PHASES PHASES PHASES PHASES PHASE *
157     // *********************************************************************************************
158 
159     @Override
160     public boolean isBeforeBeginning( String codeCampaign, String phase )
161     {
162         if ( ParticipatoryIdeationConstants.IDEATION.equals( phase ) )
163         {
164             return new Date( ).before( dates.get( codeCampaign ) [0] );
165         }
166         AppLogService.error( "Unexpected phase code '" + phase + "'." );
167         return false;
168     }
169 
170     @Override
171     public boolean isAfterBeginning( String codeCampaign, String phase )
172     {
173         if ( ParticipatoryIdeationConstants.IDEATION.equals( phase ) )
174         {
175             return new Date( ).after( dates.get( codeCampaign ) [0] );
176         }
177         AppLogService.error( "Unexpected phase code '" + phase + "'." );
178         return false;
179     }
180 
181     @Override
182     public boolean isDuring( String codeCampaign, String phase )
183     {
184         return isAfterBeginning( codeCampaign, phase ) && isBeforeEnd( codeCampaign, phase );
185     }
186 
187     @Override
188     public boolean isBeforeEnd( String codeCampaign, String phase )
189     {
190         if ( ParticipatoryIdeationConstants.IDEATION.equals( phase ) )
191         {
192             return new Date( ).before( dates.get( codeCampaign ) [1] );
193         }
194         AppLogService.error( "Unexpected phase code '" + phase + "'." );
195         return false;
196     }
197 
198     @Override
199     public boolean isAfterEnd( String codeCampaign, String phase )
200     {
201         if ( ParticipatoryIdeationConstants.IDEATION.equals( phase ) )
202         {
203             return new Date( ).after( dates.get( codeCampaign ) [1] );
204         }
205         AppLogService.error( "Unexpected phase code '" + phase + "'." );
206         return false;
207     }
208 
209     @Override
210     public boolean isBeforeBeginning( String phase )
211     {
212         return isBeforeBeginning( getLastCampaign( ).getCode( ), phase );
213     }
214 
215     @Override
216     public boolean isAfterBeginning( String phase )
217     {
218         return isAfterBeginning( getLastCampaign( ).getCode( ), phase );
219     }
220 
221     @Override
222     public boolean isDuring( String phase )
223     {
224         return isDuring( getLastCampaign( ).getCode( ), phase );
225     }
226 
227     @Override
228     public boolean isBeforeEnd( String phase )
229     {
230         return isBeforeEnd( getLastCampaign( ).getCode( ), phase );
231     }
232 
233     @Override
234     public boolean isAfterEnd( String phase )
235     {
236         return isAfterEnd( getLastCampaign( ).getCode( ), phase );
237     }
238 
239     // *********************************************************************************************
240     // * AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA *
241     // * AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA AREA *
242     // *********************************************************************************************
243 
244     @Override
245     public ReferenceItem getCampaignWholeAreaLabel( String codeCampaign )
246     {    	
247     	Stream< ReferenceItem > areaStream = areaLabels.get( codeCampaign ).stream( );
248     	
249     	// Assuming there should be only one whole-typed item
250     	Optional< ReferenceItem > opt = areaStream.filter( r -> Proposal.LOCATION_AREA_TYPE_WHOLE.contentEquals( r.getCode( ) ) ).findFirst( );
251     	
252         return opt.get( );
253     }
254 
255     @Override
256     public ReferenceList getCampaignLocalizedAreaLabels( String codeCampaign )
257     {
258         ReferenceList result = new ReferenceList( );
259         
260         for ( ReferenceItem item : areaLabels.get( codeCampaign ) )
261         {
262             if ( !Proposal.LOCATION_AREA_TYPE_WHOLE.equals( item.getCode( ) ) )
263             {
264                 result.add( item );
265             }
266         }
267         return result;
268     }
269 
270     @Override
271     public int getCampaignNumberLocalizedAreas( String codeCampaign )
272     {
273         return getCampaignLocalizedAreaLabels( codeCampaign ).size( );
274     }
275 
276     @Override
277     public ReferenceItem getLastCampaignWholeAreaLabel( )
278     {	
279         return getCampaignWholeAreaLabel( getLastCampaign( ).getCode( ) );
280     }
281 
282     @Override
283     public ReferenceList getLastCampaignLocalizedAreaLabels( )
284     {
285         return getCampaignLocalizedAreaLabels( getLastCampaign( ).getCode( ) );
286     }
287 
288     @Override
289     public int getLastCampaignNumberLocalizedAreas( )
290     {
291         return getLastCampaignLocalizedAreaLabels( ).size( );
292     }
293 
294     @Override
295     public ReferenceList getCampaignAllAreaLabels( String codeCampaign )
296     {
297         ReferenceList result = new ReferenceList( );
298         for ( ReferenceItem item : areaLabels.get( codeCampaign ) )
299         {
300             if ( Proposal.LOCATION_AREA_TYPE_LOCALIZED.equals( item.getName( ) ) )
301             {
302                 result.add( item );
303             }
304         }
305         return result;
306     }
307 
308     @Override
309     public ReferenceList getCampaignAllAreaTypes( String codeCampaign )
310     {
311         ReferenceList result = new ReferenceList( );
312         for ( ReferenceItem item : areaTypes.get( codeCampaign ) )
313         {
314             if ( Proposal.LOCATION_AREA_TYPE_LOCALIZED.equals( item.getName( ) ) )
315             {
316                 result.add( item );
317             }
318         }
319         return result;
320     }
321 
322     @Override
323     public ReferenceList getLastCampaignAllAreaLabels( )
324     {
325         return getCampaignAllAreaLabels( getLastCampaign( ).getCode( ) );
326     }
327 
328     @Override
329     public ReferenceList getLastCampaignAllAreaTypes( )
330     {
331         return getCampaignAllAreaTypes( getLastCampaign( ).getCode( ) );
332     }
333 
334     // *********************************************************************************************
335     // * SUBMITTER SUBMITTER SUBMITTER SUBMITTER SUBMITTER SUBMITTER SUBMITTER SUBMITTER SUBMITTER *
336     // * SUBMITTER SUBMITTER SUBMITTER SUBMITTER SUBMITTER SUBMITTER SUBMITTER SUBMITTER SUBMITTER *
337     // *********************************************************************************************
338 
339     @Override
340     public Collection<SubmitterType> getCampaignSubmitterTypes( String codeCampaign )
341     {
342         return SubmitterTypeHome.getSubmitterTypesListByCampaign( codeCampaign );
343     }
344 
345     // *********************************************************************************************
346     // * THEMES THEMES THEMES THEMES THEMES THEMES THEMES THEMES THEMES THEMES THEMES THEMES THEME *
347     // * THEMES THEMES THEMES THEMES THEMES THEMES THEMES THEMES THEMES THEMES THEMES THEMES THEME *
348     // *********************************************************************************************
349 
350     @Override
351     public ReferenceList getCampaignThemes( String codeCampaign )
352     {
353         return themeLabels.get( codeCampaign );
354     }
355 
356     @Override
357     public ReferenceList getLastCampaignThemes( )
358     {
359         return getCampaignThemes( getLastCampaign( ).getCode( ) );
360     }
361 
362     @Override
363     public ReferenceList getCampaignThemesFrontRgb( String codeCampaign )
364     {
365         return themeFrontRgbs.get( codeCampaign );
366     }
367 
368     // *********************************************************************************************
369     // * FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELD *
370     // * FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELDS FIELD *
371     // *********************************************************************************************
372 
373     @Override
374     public String [ ] getCampaignFieldData( String codeCampaign, String fieldCode )
375     {
376         return getCampaignFieldsData( codeCampaign ).get( fieldCode );
377     }
378 
379     @Override
380     public Map<String, String [ ]> getCampaignFieldsData( String codeCampaign )
381     {
382         return fields.get( codeCampaign );
383     }
384 
385     // *********************************************************************************************
386     // * LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD *
387     // * LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD LOAD *
388     // *********************************************************************************************
389 
390     private static void loadProperties( )
391     {
392         // Campaigns
393         campaigns = new ReferenceList( );
394         for ( int i = 1; true; i++ )
395         {
396             String propName = PROPERTY_PREFIX + i;
397             String propValue = AppPropertiesService.getProperty( propName );
398             if ( StringUtils.isBlank( propValue ) )
399             {
400                 break;
401             }
402 
403             String [ ] campaign = propValue.split( PROPERTY_SEP );
404             if ( campaign.length != 2 )
405             {
406                 AppLogService.error( "The property '" + propName + "' should be formated as 'code" + PROPERTY_SEP + "label' but is '" + propValue + "'." );
407                 break;
408             }
409 
410             campaigns.addItem( campaign [0], campaign [1] );
411         }
412 
413         // Themes for each campaign
414         themeLabels = new HashMap<>( );
415         themeFrontRgbs = new HashMap<>( );
416         for ( ReferenceItem campaign : campaigns )
417         {
418             ReferenceList campaignThemes = new ReferenceList( );
419             ReferenceList campaignThemeFrontRgbs = new ReferenceList( );
420             for ( int i = 1; true; i++ )
421             {
422                 String propName = PROPERTY_PREFIX + campaign.getCode( ) + ".theme." + i;
423                 String propValue = AppPropertiesService.getProperty( propName );
424                 if ( StringUtils.isBlank( propValue ) )
425                 {
426                     break;
427                 }
428 
429                 String [ ] theme = propValue.split( PROPERTY_SEP );
430                 if ( theme.length != 3 )
431                 {
432                     AppLogService.error( "The property '" + propName + "' should be formated as 'code" + PROPERTY_SEP + "label" + PROPERTY_SEP
433                             + "color' but is '" + propValue + "'." );
434                     break;
435                 }
436 
437                 campaignThemes.addItem( theme [0], theme [1] );
438                 campaignThemeFrontRgbs.addItem( theme [0], theme [2] );
439             }
440             themeLabels.put( campaign.getCode( ), campaignThemes );
441             themeFrontRgbs.put( campaign.getCode( ), campaignThemeFrontRgbs );
442         }
443 
444         // Areas for each campaign
445         areaLabels = new HashMap<>( );
446         areaTypes = new HashMap<>( );
447         for ( ReferenceItem campaign : campaigns )
448         {
449             ReferenceList campaignAreas = new ReferenceList( );
450             ReferenceList campaignTypes = new ReferenceList( );
451             for ( int i = 1; true; i++ )
452             {
453                 String propName = PROPERTY_PREFIX + campaign.getCode( ) + ".area." + i;
454                 String propValue = AppPropertiesService.getProperty( propName );
455                 if ( StringUtils.isBlank( propValue ) )
456                 {
457                     break;
458                 }
459 
460                 String [ ] area = propValue.split( PROPERTY_SEP );
461                 
462                 if ( area.length != 3 )
463                 {
464                     AppLogService.error( "The property '" + propName + "' should be formated as 'code" + PROPERTY_SEP + "label" + PROPERTY_SEP
465                             + "type' but is '" + propValue + "'." );
466                     break;
467                 }
468 
469                 campaignAreas.addItem( area [0], area [1] );
470                 campaignTypes.addItem( area [0], area [2] );
471             }
472             areaLabels.put( campaign.getCode( ), campaignAreas );
473             areaTypes.put( campaign.getCode( ), campaignTypes );
474         }
475 
476         // Phase dates for each campaign
477         dates = new HashMap<>( );
478         for ( ReferenceItem campaign : campaigns )
479         {
480             Timestamp [ ] beginEnd = new Timestamp [ 2];
481 
482             String propName = PROPERTY_PREFIX + campaign.getCode( ) + ".ideation";
483             String propValue = AppPropertiesService.getProperty( propName );
484             if ( StringUtils.isBlank( propValue ) )
485             {
486                 break;
487             }
488 
489             String [ ] beginEndStr = propValue.split( PROPERTY_SEP );
490             if ( beginEndStr.length != 2 )
491             {
492                 AppLogService
493                         .error( "The property '" + propName + "' should be formated as 'timestamp" + PROPERTY_SEP + "timestamp' but is '" + propValue + "'." );
494                 break;
495             }
496 
497             beginEnd [0] = Timestamp.valueOf( LocalDateTime.parse( beginEndStr [0] ) );
498             beginEnd [1] = Timestamp.valueOf( LocalDateTime.parse( beginEndStr [1] ) );
499 
500             dates.put( campaign.getCode( ), beginEnd );
501         }
502 
503         // Fields for each campaign
504         fields = new HashMap<>( );
505         for ( ReferenceItem campaign : campaigns )
506         {
507             Map<String, String [ ]> fieldData = new HashMap<>( );
508             for ( int i = 1; i < 5; i++ ) // TODO : add unlimited fields in the last form step
509             {
510                 String fieldName = "field" + i;
511                 String propName = PROPERTY_PREFIX + campaign.getCode( ) + "." + fieldName;
512 
513                 String propActiveValue = AppPropertiesService.getProperty( propName + ".active" );
514                 String propLabelValue = AppPropertiesService.getProperty( propName + ".label" );
515                 String propDescriptionValue = AppPropertiesService.getProperty( propName + ".description" );
516                 String propMandatoryValue = AppPropertiesService.getProperty( propName + ".mandatory" );
517 
518                 fieldData.put( fieldName, new String [ ] {
519                         propActiveValue, propLabelValue, propDescriptionValue, propMandatoryValue
520                 } );
521             }
522             fields.put( campaign.getCode( ), fieldData );
523         }
524 
525     }
526 
527 }