001    package org.hackystat.projectbrowser.page.projectportfolio;
002    
003    import java.io.Serializable;
004    import java.util.ArrayList;
005    import java.util.Arrays;
006    import java.util.Date;
007    import java.util.List;
008    import java.util.logging.Logger;
009    import javax.xml.datatype.XMLGregorianCalendar;
010    import org.apache.wicket.PageParameters;
011    import org.hackystat.projectbrowser.ProjectBrowserApplication;
012    import org.hackystat.projectbrowser.ProjectBrowserSession;
013    import org.hackystat.projectbrowser.page.ProjectBrowserBasePage;
014    import org.hackystat.projectbrowser.page.projectportfolio.detailspanel.ProjectPortfolioDataModel;
015    import org.hackystat.sensorbase.resource.projects.jaxb.Project;
016    import org.hackystat.utilities.tstamp.Tstamp;
017    
018    /**
019     * Session to hold state for Project Portfolio.
020     * @author Shaoxuan Zhang
021     */
022    public class ProjectPortfolioSession implements Serializable {
023    
024      /** Support serialization. */
025      private static final long serialVersionUID = 1L;
026    
027      /** The parameter key of start date. */
028      public static final String START_DATE_KEY = "0";
029      /** The parameter key of end date. */
030      public static final String END_DATE_KEY = "1";
031      /** The parameter key of granularity. */
032      public static final String GRANULARITY_KEY = "2";
033      /** The parameter key of selectedProjects. */
034      public static final String SELECTED_PROJECTS_KEY = "3";
035      /** 
036       * The last parameter key that is required.
037       * The telemetry parameters key is optional because not all telemetries have parameter.
038       */
039      private static final String LAST_REQUIRED_KEY = "3";
040      /** The last parameter key. */
041      public static final String PARAMETER_ORDER_MESSAGE = "Correct parameter order is : " + 
042                            "/<startDate>/<endDate>/<granularity>/<projects> or " +
043                            "/last/<number>/<granularity>/<projects>/";
044    
045      
046      /** the feedback string. */
047      private String feedback = "";
048      
049      /** The data model to hold state for details panel. */
050      private ProjectPortfolioDataModel dataModel;
051      
052      /** The start date this user has selected. */
053      private long startDate = ProjectBrowserBasePage.getDateBefore(5).getTime();
054      /** The end date this user has selected. */
055      private long endDate = ProjectBrowserBasePage.getDateBefore(1).getTime();
056      /** The projects this user has selected. */
057      private List<Project> selectedProjects = new ArrayList<Project>();
058      /** the granularity this data model focus. */
059      private String granularity = "Day";
060      /** The available granularities. */
061      private final String[] granularities = {"Day", "Week", "Month"};
062      
063      /** Error message when parsing page paramters. */
064      private String paramErrorMessage = "";
065    
066      /**
067       * Update the data model.
068       */
069      public void updateDataModel() {
070    
071        
072        boolean backgroundProcessEnable = ((ProjectBrowserApplication)ProjectBrowserApplication.get()).
073          isBackgroundProcessEnable("projectportfolio");
074        this.dataModel.setModel(startDate, endDate, selectedProjects, granularity);
075    
076        ProjectBrowserSession.get().logUsage("PORTFOLIO: {invoked} " + 
077            ProjectBrowserSession.get().printPageParameters(this.getPageParameters()));
078        
079        if (backgroundProcessEnable) {
080          Thread thread = new Thread() {
081            @Override
082            public void run() {
083              dataModel.loadData();
084            }
085          };
086          thread.start();
087        }
088        else {
089          dataModel.loadData();
090        }
091      }
092      
093      /**
094       * @param selectedProjects the selectedProjects to set
095       */
096      public void setSelectedProjects(List<Project> selectedProjects) {
097        this.selectedProjects = selectedProjects;
098      }
099    
100      /**
101       * @return the selectedProjects
102       */
103      public List<Project> getSelectedProjects() {
104        return selectedProjects;
105      }
106    
107      /**
108       * @return the dataModel
109       */
110      public ProjectPortfolioDataModel getDataModel() {
111        return dataModel;
112      }
113      
114      /**
115       * @param feedback the feedback to set
116       */
117      public void setFeedback(String feedback) {
118        this.feedback = feedback;
119      }
120    
121      /**
122       * @return the feedback
123       */
124      public String getFeedback() {
125        String returnString = this.feedback;
126        this.feedback = "";
127        return returnString;
128      }
129    
130      /**
131       * @param startDate the startDate to set
132       */
133      public void setStartDate(Date startDate) {
134        this.startDate = startDate.getTime();
135      }
136    
137      /**
138       * @return the startDate
139       */
140      public Date getStartDate() {
141        return new Date(startDate);
142      }
143    
144      /**
145       * @param endDate the endDate to set
146       */
147      public void setEndDate(Date endDate) {
148        this.endDate = endDate.getTime();
149      }
150    
151      /**
152       * @return the endDate
153       */
154      public Date getEndDate() {
155        return new Date(endDate);
156      }
157      
158      /**
159       * @param granularity the granularity to set
160       */
161      public void setGranularity(String granularity) {
162        this.granularity = granularity;
163      }
164    
165      /**
166       * @return the granularity
167       */
168      public String getGranularity() {
169        return granularity;
170      }
171    
172      /**
173       * @return the granularities
174       */
175      public List<String> getGranularities() {
176        return Arrays.asList(this.granularities);
177      }
178    
179      /**
180       * Initialize the dataModel.
181       * Only initialize it when it is null.
182       * @return true if the dataModel is initialized, otherwise false.
183       */
184      public boolean initializeDataModel() {
185        if (this.dataModel == null) {
186          this.dataModel = new ProjectPortfolioDataModel(
187              ((ProjectBrowserApplication)ProjectBrowserApplication.get()).getTelemetryHost(),
188              ProjectBrowserSession.get().getEmail(), ProjectBrowserSession.get().getPassword());
189          return true;
190        }
191        return false;
192      }
193      
194      /**
195       * Returns a PageParameters instance that represents the content of the input form.
196       * @return a PageParameters instance.
197       */
198      public PageParameters getPageParameters() {
199        PageParameters parameters = new PageParameters();
200    
201        parameters.put(GRANULARITY_KEY, this.getGranularity());
202        parameters.put(START_DATE_KEY, ProjectBrowserSession.getFormattedDateString(this.startDate));
203        parameters.put(END_DATE_KEY, ProjectBrowserSession.getFormattedDateString(this.endDate));
204        parameters.put(SELECTED_PROJECTS_KEY, 
205            ProjectBrowserSession.convertProjectListToString(this.getSelectedProjects()));
206        
207        return parameters;
208      }
209    
210      /**
211       * Load data from URL parameters into this session.
212       * @param parameters the URL parameters
213       * @return true if all parameters are loaded correctly
214       */
215      public boolean loadPageParameters(PageParameters parameters) {
216        boolean isLoadSucceed = true;
217        Logger logger = ProjectBrowserSession.get().getLogger();
218        if (!parameters.containsKey(LAST_REQUIRED_KEY)) {
219          isLoadSucceed = false;
220          String error = "Some parameters are missing, should be " + LAST_REQUIRED_KEY + "\n" +
221              PARAMETER_ORDER_MESSAGE;
222          logger.warning(error);
223          this.paramErrorMessage = error + "\n";
224          return false;
225        }
226        StringBuffer errorMessage = new StringBuffer(1000);
227        
228        //load granularity
229        if (parameters.containsKey(GRANULARITY_KEY)) {
230          String granularityString = parameters.getString(GRANULARITY_KEY);
231          if (this.getGranularities().contains(granularityString)) {
232            this.setGranularity(granularityString);
233          }
234          else {
235            isLoadSucceed = false;
236            String error = "Granularity is not supported: " + granularityString;
237            logger.warning(error);
238            errorMessage.append(error);
239            errorMessage.append('\n');
240          }
241        }
242        else {
243          isLoadSucceed = false;
244          errorMessage.append("granularity is missing in URL parameters.\n");
245        }
246        //load dates
247        String startDateString = "";
248        if (parameters.containsKey(START_DATE_KEY)) {
249          startDateString = parameters.getString(START_DATE_KEY);
250        }
251        else {
252          isLoadSucceed = false;
253          errorMessage.append("startDate is missing in URL parameters.\n");
254        }
255        String endDateString = "";
256        if (parameters.containsKey(END_DATE_KEY)) {
257          endDateString = parameters.getString(END_DATE_KEY);
258        }
259        else {
260          isLoadSucceed = false;
261          errorMessage.append("endDate is missing in URL parameters.\n");
262        }
263        //check last X format first.
264        if ("last".equalsIgnoreCase(startDateString)) {
265          int time;
266          try {
267            time = Integer.valueOf(endDateString);
268            int interval = 1;
269            if ("Month".equalsIgnoreCase(this.granularity)) {
270              interval = 30;
271            }
272            else if ("Week".equalsIgnoreCase(this.granularity)) {
273              interval = 7;
274            }
275            time *= interval;
276            XMLGregorianCalendar today = Tstamp.makeTimestamp();
277            endDate = Tstamp.incrementDays(today, -interval).toGregorianCalendar().getTimeInMillis();
278            startDate = Tstamp.incrementDays(today, -time).toGregorianCalendar().getTimeInMillis();
279          }
280          catch (NumberFormatException e) {
281            String error = endDateString + " cannot be parsed as an integer.";
282            logger.warning(error + " > " + e.getMessage());
283            errorMessage.append(error);
284            errorMessage.append('\n');
285          }
286        }
287        else if (endDateString.length() > 0 && startDateString.length() > 0) {
288          try {
289            this.endDate = 
290              Tstamp.makeTimestamp(endDateString).toGregorianCalendar().getTimeInMillis();
291          }
292          catch (Exception e) {
293            isLoadSucceed = false;
294            String error = "Errors when parsing end date from URL parameter: " + endDateString;
295            logger.warning(error + " > " + e.getMessage());
296            errorMessage.append(error);
297            errorMessage.append('\n');
298          }
299          try {
300            this.startDate = 
301              Tstamp.makeTimestamp(startDateString).toGregorianCalendar().getTimeInMillis();
302          }
303          catch (Exception e) {
304            isLoadSucceed = false;
305            String error = 
306              "Errors when parsing start date from URL parameter: " + startDateString;
307            logger.warning(error + " > " + e.getMessage());
308            errorMessage.append(error);
309            errorMessage.append('\n');
310          }
311        }
312        
313        //load seletecd project
314        if (parameters.containsKey(SELECTED_PROJECTS_KEY)) {
315          String[] projectsStringArray = 
316            parameters.getString(SELECTED_PROJECTS_KEY).split(
317                ProjectBrowserSession.PARAMETER_VALUE_SEPARATOR);
318          List<Project> projectsList = new ArrayList<Project>();
319          for (String projectString : projectsStringArray) {
320            int index = projectString.lastIndexOf(ProjectBrowserSession.PROJECT_NAME_OWNER_SEPARATR);
321            String projectName = projectString;
322            String projectOwner = null;
323            if (index > 0 && index < projectString.length()) {
324              projectName = projectString.substring(0, index);
325              projectOwner = projectString.substring(
326                  index + ProjectBrowserSession.PROJECT_NAME_OWNER_SEPARATR.length());
327              /*
328              isLoadSucceed = false;
329              String error = "Error URL parameter: project: " + projectString + 
330                  " >> project name and owner are missing or not formatted correctly.";
331              logger.warning(error);
332              errorMessage.append(error);
333              errorMessage.append('\n');
334              continue;
335              */
336            }
337            Project project = ProjectBrowserSession.get().getProject(projectName, projectOwner);
338            if (project == null) {
339              isLoadSucceed = false;
340              String error = "Error URL parameter: project: " + projectString +
341                  " >> matching project not found under user: " + 
342                  ProjectBrowserSession.get().getEmail();
343              logger.warning(error);
344              errorMessage.append(error);
345              errorMessage.append('\n');
346            }
347            else {
348              projectsList.add(project);
349            }
350          }
351          if (!projectsList.isEmpty()) {
352            this.setSelectedProjects(projectsList);
353          }
354        }
355        else {
356          isLoadSucceed = false;
357          errorMessage.append("projects is missing in URL parameters.\n");
358        }
359        
360        if (errorMessage.length() > 0) {
361          this.paramErrorMessage = errorMessage.append(PARAMETER_ORDER_MESSAGE).toString();
362        }
363        return isLoadSucceed;
364      }
365    
366      
367      /**
368       * @return the paramErrorMessage
369       */
370      public String getParamErrorMessage() {
371        String temp = this.paramErrorMessage;
372        this.clearParamErrorMessage();
373        return temp;
374      }
375      
376      /**
377       * Clears the paramErrorMessage.
378       */
379      public void clearParamErrorMessage() {
380        this.paramErrorMessage = "";
381      }
382    
383    }