View Javadoc

1   /**
2    * Copyright 2012 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   *
15   * Created by Charles on 5/7/12
16   */
17  package org.kuali.student.enrollment.class2.courseoffering.controller;
18  
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.commons.lang.UnhandledException;
21  import org.apache.log4j.Logger;
22  import org.kuali.rice.krad.util.GlobalVariables;
23  import org.kuali.rice.krad.util.KRADConstants;
24  import org.kuali.rice.krad.web.controller.UifControllerBase;
25  import org.kuali.rice.krad.web.form.UifFormBase;
26  import org.kuali.student.common.uif.util.GrowlIcon;
27  import org.kuali.student.common.uif.util.KSUifUtils;
28  import org.kuali.student.enrollment.class2.courseoffering.dto.SocRolloverResultItemWrapper;
29  import org.kuali.student.enrollment.class2.courseoffering.form.CourseOfferingRolloverManagementForm;
30  import org.kuali.student.enrollment.class2.courseoffering.service.CourseOfferingViewHelperService;
31  import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingConstants;
32  import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingManagementUtil;
33  import org.kuali.student.enrollment.courseoffering.dto.CourseOfferingInfo;
34  import org.kuali.student.enrollment.courseofferingset.dto.SocInfo;
35  import org.kuali.student.enrollment.courseofferingset.dto.SocRolloverResultInfo;
36  import org.kuali.student.enrollment.courseofferingset.dto.SocRolloverResultItemInfo;
37  import org.kuali.student.r2.common.dto.ContextInfo;
38  import org.kuali.student.r2.common.exceptions.DoesNotExistException;
39  import org.kuali.student.r2.common.exceptions.InvalidParameterException;
40  import org.kuali.student.r2.common.exceptions.MissingParameterException;
41  import org.kuali.student.r2.common.exceptions.OperationFailedException;
42  import org.kuali.student.r2.common.exceptions.PermissionDeniedException;
43  import org.kuali.student.r2.common.util.ContextUtils;
44  import org.kuali.student.r2.common.util.constants.CourseOfferingSetServiceConstants;
45  import org.kuali.student.r2.common.util.date.DateFormatters;
46  import org.kuali.student.r2.core.acal.dto.TermInfo;
47  import org.kuali.student.r2.core.class1.state.dto.StateInfo;
48  import org.kuali.student.r2.core.constants.AtpServiceConstants;
49  import org.springframework.stereotype.Controller;
50  import org.springframework.validation.BindingResult;
51  import org.springframework.web.bind.annotation.ModelAttribute;
52  import org.springframework.web.bind.annotation.RequestMapping;
53  import org.springframework.web.bind.annotation.RequestMethod;
54  import org.springframework.web.servlet.ModelAndView;
55  
56  import javax.servlet.http.HttpServletRequest;
57  import javax.servlet.http.HttpServletResponse;
58  import javax.xml.namespace.QName;
59  import java.util.Date;
60  import java.util.Formatter;
61  import java.util.List;
62  import java.util.Locale;
63  import java.util.Map;
64  import java.util.concurrent.CopyOnWriteArrayList;
65  
66  /**
67   * The controller for Perform Rollover, Rollover Details, and Release to Depts page (all within the same view).
68   *
69   * @author Kuali Student Team
70   */
71  @Controller
72  @RequestMapping(value = "/courseOfferingRollover")
73  public class CourseOfferingRolloverController extends UifControllerBase {
74  
75      private static final Logger LOGGER = Logger.getLogger(CourseOfferingRolloverController.class);
76      public static final String ROLLOVER_DETAILS_PAGEID = "selectTermForRolloverDetails";
77  
78      @Override
79      protected UifFormBase createInitialForm(@SuppressWarnings("unused") HttpServletRequest request) {
80          return new CourseOfferingRolloverManagementForm();
81      }
82  
83      @Override
84      @RequestMapping(method = RequestMethod.GET, params = "methodToCall=start")
85      public ModelAndView start(@ModelAttribute("KualiForm") UifFormBase form, @SuppressWarnings("unused") BindingResult result,
86                                @SuppressWarnings("unused") HttpServletRequest request, @SuppressWarnings("unused") HttpServletResponse response) {
87          if (!(form instanceof CourseOfferingRolloverManagementForm)) {
88              throw new RuntimeException("Form object passed into start method was not of expected type CourseOfferingRolloverManagementForm. Got " + form.getClass().getSimpleName());
89          }
90          CourseOfferingRolloverManagementForm theForm = (CourseOfferingRolloverManagementForm) form;
91  
92          // check view authorization
93          // TODO: this needs to be invoked for each request
94          if (form.getView() != null) {
95              String methodToCall = request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER);
96              checkViewAuthorization(theForm, methodToCall);
97          }
98  
99          Map paramMap = request.getParameterMap();
100         if (paramMap.containsKey("pageId")) {
101             String pageId = ((String[]) paramMap.get("pageId"))[0];
102             if (pageId.equals("selectTermsForRollover")) {
103                 return _startPerformRollover(form, result, request, response);
104             } else if (pageId.equals("releaseToDepts")) {
105                 return _startReleaseToDepts(theForm, result, request, response);
106             } else if (pageId.equals("selectTermForRolloverDetails")) {
107                 return _startRolloverDetails(form, result, request, response);
108             }
109         }
110         return getUIFModelAndView(theForm);
111     }
112 
113     private ModelAndView _startPerformRollover(@ModelAttribute("KualiForm") UifFormBase form, @SuppressWarnings("unused") BindingResult result,
114                                                @SuppressWarnings("unused") HttpServletRequest request, @SuppressWarnings("unused") HttpServletResponse response) {
115         CourseOfferingRolloverManagementForm theForm = (CourseOfferingRolloverManagementForm) form;
116         LOGGER.info("startPerformRollover");
117         return getUIFModelAndView(theForm);
118     }
119 
120     private ModelAndView _startRolloverDetails(@ModelAttribute("KualiForm") UifFormBase form, @SuppressWarnings("unused") BindingResult result,
121                                                @SuppressWarnings("unused") HttpServletRequest request, @SuppressWarnings("unused") HttpServletResponse response) {
122         CourseOfferingRolloverManagementForm theForm = (CourseOfferingRolloverManagementForm) form;
123         LOGGER.info("startRolloverDetails");
124         String rolloverTerm = theForm.getRolloverTargetTermCode();
125 
126         try {
127             if (rolloverTerm != null && !"".equals(rolloverTerm)) {
128                 return showRolloverResults(theForm, result, request, response);
129             }
130         } catch (Exception ex) {
131             return getUIFModelAndView(theForm);
132         }
133 
134         return getUIFModelAndView(theForm);
135     }
136 
137     private ModelAndView _startReleaseToDepts(@ModelAttribute("KualiForm") CourseOfferingRolloverManagementForm form, @SuppressWarnings("unused") BindingResult result,
138                                               @SuppressWarnings("unused") HttpServletRequest request, @SuppressWarnings("unused") HttpServletResponse response) {
139         LOGGER.info("startReleaseToDepts");
140         form.computeReleaseToDeptsDisabled();
141         return getUIFModelAndView(form);
142     }
143 
144     @RequestMapping(params = "methodToCall=goTargetTerm")
145     public ModelAndView goTargetTerm(@ModelAttribute("KualiForm") CourseOfferingRolloverManagementForm form, @SuppressWarnings("unused") BindingResult result,
146                                      @SuppressWarnings("unused") HttpServletRequest request, @SuppressWarnings("unused") HttpServletResponse response) throws Exception {
147         CourseOfferingViewHelperService helper = CourseOfferingManagementUtil.getCoViewHelperService(form);
148         // validation to check for like terms and target term year comes before source term year.
149         String targetTermCd = form.getTargetTermCode();
150         String sourceTermCd = form.getSourceTermCode();
151         List<TermInfo> targetTermsByCode = helper.findTermByTermCode(targetTermCd);
152         List<TermInfo> sourceTermsByCode = helper.findTermByTermCode(sourceTermCd);
153 
154         //Check that the source and target terms exist in the db
155         if (sourceTermsByCode.isEmpty()) {
156             GlobalVariables.getMessageMap().putError("sourceTermCode", "error.courseoffering.sourceTerm.inValid");
157             form.setIsRolloverButtonDisabled(true);
158             return getUIFModelAndView(form);
159         }
160         if (targetTermsByCode.isEmpty()) {
161             GlobalVariables.getMessageMap().putError("targetTermCode", "error.courseoffering.targetTerm.inValid");
162             form.setIsRolloverButtonDisabled(true);
163             return getUIFModelAndView(form);
164         }
165 
166         TermInfo targetTerm = helper.findTermByTermCode(targetTermCd).get(0);
167         TermInfo sourceTerm = helper.findTermByTermCode(sourceTermCd).get(0);
168         boolean likeTerms = sourceTerm.getTypeKey().equals(targetTerm.getTypeKey());
169         boolean sourcePrecedesTarget = sourceTerm.getStartDate().before(targetTerm.getStartDate());
170         if (!likeTerms) {
171             GlobalVariables.getMessageMap().putError("targetTermCode", "error.likeTerms.validation");
172             form.setIsRolloverButtonDisabled(true);
173             return getUIFModelAndView(form);
174         } else if (!sourcePrecedesTarget) {
175             GlobalVariables.getMessageMap().putError("targetTermCode", "error.years.validation");
176             form.setIsRolloverButtonDisabled(true);
177             return getUIFModelAndView(form);
178         }
179 
180         List<TermInfo> termList = helper.findTermByTermCode(form.getTargetTermCode());
181         int firstTerm = 0;
182         if (termList != null && termList.size() == 1) {
183             //validation to check if already rollover target term exists..
184             List<String> coIds = CourseOfferingManagementUtil.getCourseOfferingService().getCourseOfferingIdsByTerm(termList.get(firstTerm).getId(), true, new ContextInfo());
185             if (!coIds.isEmpty()) {
186                 // Print error message if there are course offerings in the target term
187                 GlobalVariables.getMessageMap().putError("targetTermCode", "error.courseoffering.rollover.targetTermExists");
188                 return getUIFModelAndView(form);
189             }
190             // Get first term
191             TermInfo matchingTerm = termList.get(firstTerm);
192             String targetTermCode = matchingTerm.getCode();
193             form.setDisplayedTargetTermCode(targetTermCode);
194             // Set the start date
195             Date startDate = matchingTerm.getStartDate();
196             String startDateStr = DateFormatters.COURSE_OFFERING_VIEW_HELPER_DATE_FORMATTER.format(startDate);
197             form.setTargetTermStartDate(startDateStr);
198             // Set the end date
199             Date endDate = matchingTerm.getEndDate();
200             String endDateStr = DateFormatters.COURSE_OFFERING_VIEW_HELPER_DATE_FORMATTER.format(endDate);
201             form.setTargetTermEndDate(endDateStr);
202             form.setTargetTerm(matchingTerm);
203             form.setIsRolloverButtonDisabled(false); // Enable the button
204         } else {
205             form.setTargetTerm(null);
206             form.resetForm();
207             GlobalVariables.getMessageMap().putError("targetTermCode", "error.courseoffering.targetTerm.inValid");
208         }
209         return getUIFModelAndView(form);
210     }
211 
212     @RequestMapping(params = "methodToCall=goSourceTerm")
213     public ModelAndView goSourceTerm(@ModelAttribute("KualiForm") CourseOfferingRolloverManagementForm form, @SuppressWarnings("unused") BindingResult result,
214                                      @SuppressWarnings("unused") HttpServletRequest request, @SuppressWarnings("unused") HttpServletResponse response) throws Exception {
215         if (form.getSourceTermCode().isEmpty()) {
216             GlobalVariables.getMessageMap().putError("sourceTermCode", "error.courseoffering.sourceTerm.inValid");
217             form.setIsRolloverButtonDisabled(true);
218             return getUIFModelAndView(form);
219         }
220         CourseOfferingViewHelperService helper = CourseOfferingManagementUtil.getCoViewHelperService(form);
221         List<TermInfo> termList = helper.findTermByTermCode(form.getSourceTermCode());
222         if (termList != null && termList.size() == 1) {
223             int firstTerm = 0;
224             // Get first term
225             TermInfo matchingTerm = termList.get(firstTerm);
226             String sourceTermCode = matchingTerm.getCode();
227             //Check SOC
228             boolean sourceTermHasSoc = helper.termHasSoc(matchingTerm.getId(), form);
229             if (!sourceTermHasSoc) {
230                 GlobalVariables.getMessageMap().putError("sourceTermCode", "error.rollover.sourceTerm.noSoc");
231                 form.setIsRolloverButtonDisabled(true);
232                 return getUIFModelAndView(form);
233             }
234             form.setDisplayedSourceTermCode(sourceTermCode);
235             // Set the start date
236             Date startDate = matchingTerm.getStartDate();
237             String startDateStr = DateFormatters.COURSE_OFFERING_VIEW_HELPER_DATE_FORMATTER.format(startDate);
238             form.setSourceTermStartDate(startDateStr);
239             // Set the end date
240             Date endDate = matchingTerm.getEndDate();
241             String endDateStr = DateFormatters.COURSE_OFFERING_VIEW_HELPER_DATE_FORMATTER.format(endDate);
242             form.setSourceTermEndDate(endDateStr);
243             form.setSourceTerm(matchingTerm);
244             form.setIsGoSourceButtonDisabled(false); // Make go button for target enabled
245         } else {
246             form.setTargetTerm(null);
247             form.resetForm();
248             GlobalVariables.getMessageMap().putError("soucrceTermCode", "error.courseoffering.sourceTerm.inValid");
249         }
250         return getUIFModelAndView(form);
251     }
252 
253     private boolean validateSourceTargetTerms(@ModelAttribute("KualiForm") CourseOfferingRolloverManagementForm form) throws Exception {
254         String targetTermCd = form.getTargetTermCode();
255         String sourceTermCd = form.getSourceTermCode();
256 
257         if (sourceTermCd==null || sourceTermCd.isEmpty()) {
258             GlobalVariables.getMessageMap().putError("sourceTermCode", "error.courseoffering.sourceTerm.inValid");
259             return false;
260         }
261         if (targetTermCd==null || targetTermCd.isEmpty()) {
262             GlobalVariables.getMessageMap().putError("targetTermCode", "error.courseoffering.sourceTerm.inValid");
263             return false;
264         }
265 
266         CourseOfferingViewHelperService helper = CourseOfferingManagementUtil.getCoViewHelperService(form);
267         List<TermInfo> targetTermsByCode = helper.findTermByTermCode(targetTermCd);
268         List<TermInfo> sourceTermsByCode = helper.findTermByTermCode(sourceTermCd);
269 
270         if (sourceTermsByCode==null || sourceTermsByCode.isEmpty()) {
271             GlobalVariables.getMessageMap().putError("sourceTermCode", "error.courseoffering.sourceTerm.inValid");
272             return false;
273         }
274         if (targetTermsByCode==null || targetTermsByCode.isEmpty()) {
275             GlobalVariables.getMessageMap().putError("targetTermCode", "error.courseoffering.targetTerm.inValid");
276             return false;
277         }
278 
279         //collect sub-terms if any
280         int firstTerm = 0;
281         List<TermInfo> targetSubTermsByCode = CourseOfferingManagementUtil.getAcademicCalendarService().getIncludedTermsInTerm(targetTermsByCode.get(firstTerm).getId(), new ContextInfo());
282         List<TermInfo> sourceSubTermsByCode = CourseOfferingManagementUtil.getAcademicCalendarService().getIncludedTermsInTerm(sourceTermsByCode.get(firstTerm).getId(), new ContextInfo());
283         //validate target sub-terms
284         if (targetSubTermsByCode != null && targetSubTermsByCode.size() > 0) {
285             for (TermInfo targetSubTerm : targetSubTermsByCode) {
286                 if(!StringUtils.equals(AtpServiceConstants.ATP_OFFICIAL_STATE_KEY, targetSubTerm.getStateKey())) {
287                     GlobalVariables.getMessageMap().putError("targetTermCode", "error.rollover.targetTerm.notOfficial");
288                     form.setSourceTermInfoDisplay(getTermDisplayString(targetTermsByCode.get(firstTerm).getId(), targetTermsByCode.get(firstTerm)));
289                     form.setTargetTermInfoDisplay(getTermDisplayString(sourceTermsByCode.get(firstTerm).getId(), sourceTermsByCode.get(firstTerm)));
290                     return false;
291                 }
292             }
293         }
294         //validate source sub-terms
295         if (sourceSubTermsByCode != null && sourceSubTermsByCode.size() > 0) {
296             for (TermInfo sourceSubTerm : sourceSubTermsByCode) {
297                 if(!StringUtils.equals(AtpServiceConstants.ATP_OFFICIAL_STATE_KEY, sourceSubTerm.getStateKey())) {
298                     GlobalVariables.getMessageMap().putError("sourceTermCode", "error.rollover.sourceTerm.notOfficial");
299                     form.setSourceTermInfoDisplay(getTermDisplayString(targetTermsByCode.get(firstTerm).getId(), targetTermsByCode.get(firstTerm)));
300                     form.setTargetTermInfoDisplay(getTermDisplayString(sourceTermsByCode.get(firstTerm).getId(), sourceTermsByCode.get(firstTerm)));
301                     return false;
302                 }
303             }
304         }
305 
306         boolean sourceTermValid = sourceTermsByCode.size() == 1;
307         boolean targetTermValid = targetTermsByCode.size() == 1;
308 
309         if (sourceTermValid && targetTermValid) {
310             TermInfo targetTerm = targetTermsByCode.get(firstTerm);
311             TermInfo sourceTerm = sourceTermsByCode.get(firstTerm);
312 
313             //Check source term SOC
314             boolean sourceTermHasSoc = helper.termHasSoc(sourceTerm.getId(), form);
315             if (!sourceTermHasSoc) {
316                 GlobalVariables.getMessageMap().putError("sourceTermCode", "error.rollover.sourceTerm.noSoc");
317                 form.setSourceTermInfoDisplay(getTermDisplayString(sourceTerm.getId(), sourceTerm));
318                 form.setTargetTermInfoDisplay(getTermDisplayString(targetTerm.getId(), targetTerm));
319                 return false;
320             }
321             form.setSourceTerm(sourceTerm);
322 
323             //target term needs to be in official state
324             if(!StringUtils.equals(AtpServiceConstants.ATP_OFFICIAL_STATE_KEY, targetTerm.getStateKey())) {
325                 GlobalVariables.getMessageMap().putError("targetTermCode", "error.rollover.targetTerm.notOfficial");
326                 form.setSourceTermInfoDisplay(getTermDisplayString(sourceTerm.getId(), sourceTerm));
327                 form.setTargetTermInfoDisplay(getTermDisplayString(targetTerm.getId(), targetTerm));
328                 return false;
329             }
330 
331             //source and target term need to be alike terms and source term need to precede target term
332             boolean likeTerms = sourceTerm.getTypeKey().equals(targetTerm.getTypeKey());
333             boolean sourcePrecedesTarget = sourceTerm.getStartDate().before(targetTerm.getStartDate());
334             if (!likeTerms) {
335                 GlobalVariables.getMessageMap().putError("targetTermCode", "error.likeTerms.validation");
336                 form.setSourceTermInfoDisplay(getTermDisplayString(sourceTerm.getId(), sourceTerm));
337                 form.setTargetTermInfoDisplay(getTermDisplayString(targetTerm.getId(), targetTerm));
338                 return false;
339             } else if (!sourcePrecedesTarget) {
340                 GlobalVariables.getMessageMap().putError("targetTermCode", "error.years.validation");
341                 form.setSourceTermInfoDisplay(getTermDisplayString(sourceTerm.getId(), sourceTerm));
342                 form.setTargetTermInfoDisplay(getTermDisplayString(targetTerm.getId(), targetTerm));
343                 return false;
344             }
345 
346             //validation to check if already rollover target term exists..
347             List<String> coIds = CourseOfferingManagementUtil.getCourseOfferingService().getCourseOfferingIdsByTerm(targetTermsByCode.get(firstTerm).getId(), true, new ContextInfo());
348             if (!coIds.isEmpty()) {
349                 // Print error message if there are course offerings in the target term
350                 GlobalVariables.getMessageMap().putError("targetTermCode", "error.courseoffering.rollover.targetTermExists");
351                 form.setSourceTermInfoDisplay(getTermDisplayString(sourceTerm.getId(), sourceTerm));
352                 form.setTargetTermInfoDisplay(getTermDisplayString(targetTerm.getId(), targetTerm));
353                 return false;
354             }
355             form.setTargetTerm(targetTerm);
356         } else {
357             form.setTargetTerm(null);
358             form.setSourceTerm(null);
359             form.resetForm();
360 
361             if (!sourceTermValid && !targetTermValid) {
362                 GlobalVariables.getMessageMap().putError("sourceTermCode", "error.courseoffering.sourceTerm.inValid");
363                 GlobalVariables.getMessageMap().putError("targetTermCode", "error.courseoffering.targetTerm.inValid");
364             } else if (sourceTermValid && !targetTermValid) {
365                 TermInfo sourceTerm = sourceTermsByCode.get(firstTerm);
366                 GlobalVariables.getMessageMap().putError("targetTermCode", "error.courseoffering.targetTerm.inValid");
367                 form.setSourceTermInfoDisplay(getTermDisplayString(sourceTerm.getId(), sourceTerm));
368             } else if (!sourceTermValid && targetTermValid) {
369                 TermInfo targetTerm = targetTermsByCode.get(firstTerm);
370                 GlobalVariables.getMessageMap().putError("sourceTermCode", "error.courseoffering.sourceTerm.inValid");
371                 form.setTargetTermInfoDisplay(getTermDisplayString(targetTerm.getId(), targetTerm));
372             }
373         }
374 
375         return true;
376 
377     }
378     @RequestMapping(params = "methodToCall=performRollover")
379     public ModelAndView performRollover(@ModelAttribute("KualiForm") CourseOfferingRolloverManagementForm form, @SuppressWarnings("unused") BindingResult result,
380                                         @SuppressWarnings("unused") HttpServletRequest request, @SuppressWarnings("unused") HttpServletResponse response) throws Exception {
381         if (!validateSourceTargetTerms(form)) {
382             form.getDialogManager().removeDialog(CourseOfferingSetServiceConstants.NO_EXAM_PERIOD_WARNING_DIALOG);
383             return getUIFModelAndView(form);
384         }
385 
386         CourseOfferingViewHelperService helper = CourseOfferingManagementUtil.getCoViewHelperService(form);
387 
388         if (form.getSourceTerm() == null || form.getTargetTerm() == null) {
389             form.setStatusField("(setUp) Source/target term objects appear to be missing");
390             return getUIFModelAndView(form);
391         }
392         form.setStatusField("");
393         String sourceTermId = form.getSourceTerm().getId();
394         String targetTermId = form.getTargetTerm().getId();
395 
396         if (!helper.termHasExamPeriod(targetTermId)) {
397             if (!hasDialogBeenAnswered(CourseOfferingSetServiceConstants.NO_EXAM_PERIOD_WARNING_DIALOG, form)) {
398                 //display dialog
399                 form.setLightboxScript("openLightboxOnLoad('" + CourseOfferingSetServiceConstants.NO_EXAM_PERIOD_WARNING_DIALOG + "');");
400                 form.getDialogManager().addDialog(CourseOfferingSetServiceConstants.NO_EXAM_PERIOD_WARNING_DIALOG, form.getMethodToCall());
401                 return getUIFModelAndView(form);
402             } else {
403                 boolean continueWithoutExams = getBooleanDialogResponse(CourseOfferingSetServiceConstants.NO_EXAM_PERIOD_WARNING_DIALOG, form, request, response);
404                 form.getDialogManager().removeDialog(CourseOfferingSetServiceConstants.NO_EXAM_PERIOD_WARNING_DIALOG);
405                 if (!continueWithoutExams) {
406                     return getUIFModelAndView(form);
407                 }
408             }
409         }
410 
411         boolean success = helper.performRollover(sourceTermId, targetTermId, form);
412         if (success) {
413             form.setRolloverTargetTermCode(form.getTargetTermCode());
414             showRolloverResults(form, result, request, response); // TODO: Factor out a common method?
415             // Switch to rollover details page
416             return start(form, result, request, response);
417         } else {
418             // Had problems, stay in the same screen
419             return getUIFModelAndView(form);
420         }
421     }
422 
423     @RequestMapping(params = "methodToCall=performReverseRollover")
424     public ModelAndView performReverseRollover(@ModelAttribute("KualiForm") CourseOfferingRolloverManagementForm form, @SuppressWarnings("unused") BindingResult result,
425                                                @SuppressWarnings("unused") HttpServletRequest request, @SuppressWarnings("unused") HttpServletResponse response) throws Exception {
426         CourseOfferingViewHelperService helper = CourseOfferingManagementUtil.getCoViewHelperService(form);
427         if (form.getSourceTerm() == null || form.getTargetTerm() == null) {
428             form.setStatusField("(cleanUp) Source/target term objects appear to be missing");
429             return getUIFModelAndView(form);
430         }
431         form.setStatusField("");
432 
433         String sourceTermId = form.getSourceTerm().getId();
434         String targetTermId = form.getTargetTerm().getId();
435         SocRolloverResultInfo info = helper.performReverseRollover(sourceTermId, targetTermId, form);
436         if (info != null) {
437             form.setStatusField("Num items processed: " + info.getItemsProcessed());
438         }
439         return getUIFModelAndView(form);
440     }
441 
442     private void _disableReleaseToDeptsIfNeeded(CourseOfferingViewHelperService helper, String targetTermId,
443                                                 CourseOfferingRolloverManagementForm form) {
444         SocInfo socInfo = helper.getMainSoc(targetTermId);
445         if (socInfo == null) {
446             // Disable if no term found
447             form.setReleaseToDeptsInvalidTerm(true);
448         } else {
449             String stateKey = socInfo.getStateKey();
450             if (!CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY.equals(stateKey)) {
451                 // Assume it's been released if no longer in draft state (TODO: may not be true--revisit)
452                 form.setSocReleasedToDepts(true);
453             } else { // In draft state
454                 form.setSocReleasedToDepts(false);
455             }
456         }
457     }
458 
459     private String _computeRolloverDuration(Date dateInitiated, Date dateCompleted) {
460         long diffInMillis = dateCompleted.getTime() - dateInitiated.getTime();
461         long diffInSeconds = diffInMillis / 1000;
462         int minutes = (int) (diffInSeconds / 60);
463         int seconds = (int) (diffInSeconds % 60);
464         int hours = minutes / 60;
465         minutes = minutes % 60;
466         String result = seconds + "s";
467         if (minutes > 0 || hours > 0) {
468             result = minutes + "m " + result;
469         }
470         if (hours > 0) {
471             result = hours + "h " + result;
472         }
473         return result;
474     }
475 
476     private String _createPlural(int count) {
477         return count == 1 ? "" : "s";
478     }
479 
480     private String _createStatusString(SocRolloverResultInfo socRolloverResultInfo) {
481         String status = "";
482         String stateKey = socRolloverResultInfo.getStateKey();
483         if (CourseOfferingSetServiceConstants.SUBMITTED_RESULT_STATE_KEY.equals(stateKey) ||
484                 CourseOfferingSetServiceConstants.RUNNING_RESULT_STATE_KEY.equals(stateKey)) {
485             status = " (in progress)";
486         } else if (CourseOfferingSetServiceConstants.ABORTED_RESULT_STATE_KEY.equals(stateKey)) {
487             status = " (aborted)";
488         }
489         return status;
490     }
491 
492     private void _setStatus(String stateKey, CourseOfferingRolloverManagementForm form) {
493         if (CourseOfferingSetServiceConstants.FINISHED_RESULT_STATE_KEY.equals(stateKey)) {
494             form.setStatusField("Finished");
495             form.setRolloverCompleted(true);
496         } else if (CourseOfferingSetServiceConstants.RUNNING_RESULT_STATE_KEY.equals(stateKey) ||
497                 CourseOfferingSetServiceConstants.SUBMITTED_RESULT_STATE_KEY.equals(stateKey)) {
498             form.setStatusField("In Progress");
499             form.setRolloverCompleted(false);
500         } else if (CourseOfferingSetServiceConstants.ABORTED_RESULT_STATE_KEY.equals(stateKey)) {
501             form.setRolloverCompleted(true);
502         }
503     }
504 
505     private void _displayRolloverInfo(SocInfo socInfo, SocRolloverResultInfo socRolloverResultInfo,
506                                       CourseOfferingRolloverManagementForm form, CourseOfferingViewHelperService helper,
507                                       String stateKey, String targetTermId) {
508         if (socInfo != null) {
509             // Set some display fields that show friendly, human-readable term data
510             String friendlySourceTermDesc = helper.getTermDesc(socInfo.getTermId());
511             form.setRolloverSourceTermDesc(friendlySourceTermDesc);
512             String friendlyTargetTermDesc = helper.getTermDesc(targetTermId);
513             form.setRolloverTargetTermDesc(friendlyTargetTermDesc);
514         }
515         Date dateInitiated = socRolloverResultInfo.getDateInitiated();
516         String startDateStr = helper.formatDateAndTime(dateInitiated);
517         form.setDateInitiated(startDateStr);
518         // if items skipped is null, then below condition passes and items skipped is calculated
519         if (socRolloverResultInfo.getCourseOfferingsCreated() == null || socRolloverResultInfo.getCourseOfferingsCreated().toString().length() < 1) {
520             Integer temp = socRolloverResultInfo.getItemsExpected() - socRolloverResultInfo.getItemsProcessed();
521             String plural = _createPlural(temp);
522             form.setCourseOfferingsAllowed(socRolloverResultInfo.getItemsProcessed() + " transitioned with " + temp + " exception" + plural);
523         } else {
524             // This is the official way to compute this
525             String plural = _createPlural(socRolloverResultInfo.getCourseOfferingsSkipped());
526             form.setCourseOfferingsAllowed(socRolloverResultInfo.getCourseOfferingsCreated() + " transitioned with " +
527                     socRolloverResultInfo.getCourseOfferingsSkipped() + " exception" + plural);
528         }
529         String plural = _createPlural(socRolloverResultInfo.getActivityOfferingsSkipped());
530         form.setActivityOfferingsAllowed(socRolloverResultInfo.getActivityOfferingsCreated() + " transitioned with " +
531                 socRolloverResultInfo.getActivityOfferingsSkipped() + " exception" + plural);
532         Date dateCompleted = socRolloverResultInfo.getDateCompleted();
533         String updatedDateStr = helper.formatDateAndTime(dateCompleted);
534         // The status displays whether the time is in progress or aborted or nothing if it's completed.
535         String status = _createStatusString(socRolloverResultInfo);
536         if ((CourseOfferingSetServiceConstants.SUBMITTED_RESULT_STATE_KEY.equals(stateKey) ||
537                 CourseOfferingSetServiceConstants.RUNNING_RESULT_STATE_KEY.equals(stateKey))) {
538             form.setDateCompleted("Rollover in progress");  // DanS doesn't want a date completed if still in progress
539         } else {
540             form.setDateCompleted(updatedDateStr + status);
541         }
542         // Set value on how long rollover has been running
543         String rolloverDuration = _computeRolloverDuration(dateInitiated, dateCompleted);
544         form.setRolloverDuration(rolloverDuration + status);
545     }
546 
547     private void _displayRolloverItems(CourseOfferingRolloverManagementForm form,
548                                        List<SocRolloverResultItemInfo> socRolloverResultItemInfos,
549                                        List<SocRolloverResultItemInfo> socRolloverResultItemInfosCopy)
550             throws InvalidParameterException, MissingParameterException, DoesNotExistException, PermissionDeniedException,
551             OperationFailedException {
552         // Clear out the existing list of result items
553         form.getSocRolloverResultItems().clear();
554 
555         for (SocRolloverResultItemInfo socRolloverResultItemInfo : socRolloverResultItemInfosCopy) {
556             if (CourseOfferingSetServiceConstants.SUCCESSFUL_RESULT_ITEM_STATES.contains(socRolloverResultItemInfo.getStateKey())) {
557                 socRolloverResultItemInfos.remove(socRolloverResultItemInfo);
558             } else {
559                 String courseOfferingId = socRolloverResultItemInfo.getTargetCourseOfferingId();
560                 if (courseOfferingId == null || courseOfferingId.isEmpty()) {
561                     courseOfferingId = socRolloverResultItemInfo.getSourceCourseOfferingId();
562                 }
563 
564                 CourseOfferingInfo courseOfferingInfo = CourseOfferingManagementUtil.getCourseOfferingService().getCourseOffering(courseOfferingId, new ContextInfo());
565                 SocRolloverResultItemWrapper socRolloverResultItemWrapper = new SocRolloverResultItemWrapper();
566                 socRolloverResultItemWrapper.setCourse(courseOfferingInfo.getCourseOfferingCode());
567                 if (socRolloverResultItemInfo.getMessage() != null) {
568                     socRolloverResultItemWrapper.setMessage(socRolloverResultItemInfo.getMessage().getPlain());
569                 }
570                 socRolloverResultItemWrapper.setState(socRolloverResultItemInfo.getStateKey());
571 
572                 try {
573                     StateInfo stateInfo = CourseOfferingManagementUtil.getStateService().getState(socRolloverResultItemInfo.getStateKey(), ContextUtils.getContextInfo());
574                     if (stateInfo != null) {
575                         socRolloverResultItemWrapper.setStateName((stateInfo.getName() != null) ? stateInfo.getName() : socRolloverResultItemInfo.getStateKey());
576                     }
577                 } catch (DoesNotExistException ex) {
578                     socRolloverResultItemWrapper.setStateName(socRolloverResultItemInfo.getStateKey());
579                 }
580                 form.getSocRolloverResultItems().add(socRolloverResultItemWrapper);
581             }
582         }
583     }
584 
585     // This method displays rollover result Infos for specific target term.
586     @RequestMapping(params = "methodToCall=showRolloverResults")
587     public ModelAndView showRolloverResults(@ModelAttribute("KualiForm") CourseOfferingRolloverManagementForm form, @SuppressWarnings("unused") BindingResult result,
588                                             @SuppressWarnings("unused") HttpServletRequest request, @SuppressWarnings("unused") HttpServletResponse response) throws Exception {
589         //helper class for courseOfferingSetService
590         CourseOfferingViewHelperService helper = CourseOfferingManagementUtil.getCoViewHelperService(form);
591         //To fetch Term by code which is desirable.
592         String targetTermCode = form.getRolloverTargetTermCode();
593         List<TermInfo> termList = helper.findTermByTermCode(targetTermCode);
594         if (termList.isEmpty()) {
595             GlobalVariables.getMessageMap().putError("rolloverTargetTermCode", "error.rollover.targetTerm.noResults", targetTermCode);
596             form.resetForm(); // TODO: Does this make sense?  I don't think so. cclin
597             return getUIFModelAndView(form);
598         } else {
599             int firstValue = 0;
600             TermInfo targetTerm = termList.get(firstValue);
601             form.setTargetTerm(targetTerm);
602             form.setTargetTermCode(targetTermCode);
603             String targetTermId = targetTerm.getId();
604             // Get rollover result info for target term
605             List<SocRolloverResultInfo> socRolloverResultInfos = helper.findRolloverByTerm(targetTermId);
606             if (socRolloverResultInfos == null || socRolloverResultInfos.isEmpty()) {
607                 GlobalVariables.getMessageMap().putError("rolloverTargetTermCode", "error.rollover.targetTerm.noResults", targetTermCode);
608                 form.resetForm(); // TODO: Does this make sense?  I don't think so. cclin
609                 return getUIFModelAndView(form);
610             } else {
611                 if (socRolloverResultInfos.size() > 1) {
612                     LOGGER.warn("Multiple Soc Rollover Results Found");
613                 }
614                 _disableReleaseToDeptsIfNeeded(helper, targetTermId, form);
615                 SocRolloverResultInfo socRolloverResultInfo = socRolloverResultInfos.get(firstValue);
616                 String stateKey = socRolloverResultInfo.getStateKey();
617                 _setStatus(stateKey, form);
618                 // SocInfo service to get Source Term Id
619                 SocInfo socInfo = CourseOfferingManagementUtil.getSocService().getSoc(socRolloverResultInfo.getSourceSocId(), new ContextInfo());
620                 // Put info in the display fields on the left hand side
621                 _displayRolloverInfo(socInfo, socRolloverResultInfo, form, helper, stateKey, targetTermId);
622 
623                 // CourseOfferingSet service to get Soc Rollover ResultItems by socResultItemInfo id
624                 try {
625                     List<SocRolloverResultItemInfo> socRolloverResultItemInfos =
626                             CourseOfferingManagementUtil.getSocService().getSocRolloverResultItemsByResultId(socRolloverResultInfo.getId(), new ContextInfo());
627                     List<SocRolloverResultItemInfo> socRolloverResultItemInfosCopy =
628                             new CopyOnWriteArrayList<SocRolloverResultItemInfo>(socRolloverResultItemInfos);
629 
630                     _displayRolloverItems(form, socRolloverResultItemInfos, socRolloverResultItemInfosCopy);
631                 } catch (UnhandledException ue) {
632                     throw new RuntimeException(ue);
633                 } catch (DoesNotExistException dne) {
634                     throw new RuntimeException(dne);
635                 }
636             }
637         }
638         return getUIFModelAndView(form, ROLLOVER_DETAILS_PAGEID);
639     }
640 
641     /**
642      * This is used in the release to depts page
643      */
644     public CourseOfferingRolloverManagementForm releaseToDepts(CourseOfferingRolloverManagementForm form, BindingResult result,
645                                       HttpServletRequest request, HttpServletResponse response) throws Exception {
646         LOGGER.info("releaseToDepts");
647         CourseOfferingViewHelperService helper = CourseOfferingManagementUtil.getCoViewHelperService(form);
648         TermInfo targetTerm = form.getTargetTerm();
649         if (targetTerm == null) {
650             // Didn't get term info from Rollover Results page
651             GlobalVariables.getMessageMap().putError("approveCheckbox", "error.rollover.invalidTerm");
652         } else {
653             // We're good!
654             LOGGER.info("Ready to release to depts");
655             SocInfo socInfo = helper.getMainSoc(targetTerm.getId());
656             if (!socInfo.getStateKey().equals(CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY)) {
657                 // If it's not draft, then set variable to disable release to depts in the UI
658                 form.setSocReleasedToDepts(true);
659             } else {
660                 // It's draft, so change to state to open
661                 CourseOfferingManagementUtil.getSocService().changeSocState(socInfo.getId(), CourseOfferingSetServiceConstants.OPEN_SOC_STATE_KEY, new ContextInfo());
662                 form.setSocReleasedToDepts(true);
663             }
664             // Do a refresh of the data on rollover details
665             showRolloverResults(form, result, request, response);
666             KSUifUtils.addGrowlMessageIcon(GrowlIcon.SUCCESS, CourseOfferingConstants.COURSEOFFERING_ROLLOVER_RELEASE_TO_DEPTS_SUCCESSFULLY);
667         }
668         return form;
669     }
670 
671     @RequestMapping(params = "methodToCall=checkApproval")
672     public ModelAndView checkApproval(@ModelAttribute("KualiForm") CourseOfferingRolloverManagementForm form, @SuppressWarnings("unused") BindingResult result,
673                                       @SuppressWarnings("unused") HttpServletRequest request, @SuppressWarnings("unused") HttpServletResponse response) throws Exception {
674         LOGGER.info("checkApproval " + form.getAcceptIndicator());
675         return getUIFModelAndView(form);
676     }
677 
678     @RequestMapping(params = "methodToCall=redoRollover")
679     public ModelAndView redoRollover(@ModelAttribute("KualiForm") CourseOfferingRolloverManagementForm form, @SuppressWarnings("unused") BindingResult result,
680                                      @SuppressWarnings("unused") HttpServletRequest request, @SuppressWarnings("unused") HttpServletResponse response) throws Exception {
681         LOGGER.info("redoRollover ");
682         return getUIFModelAndView(form);
683     }
684 
685     @RequestMapping(params = "methodToCall=confirmReleaseToDepts")
686     public ModelAndView confirmReleaseToDepts(@ModelAttribute("KualiForm") CourseOfferingRolloverManagementForm form, @SuppressWarnings("unused") BindingResult result,
687                                               @SuppressWarnings("unused") HttpServletRequest request, @SuppressWarnings("unused") HttpServletResponse response) throws Exception {
688         LOGGER.info("confirmReleaseToDepts ");
689         if(form.getActionParamaterValue("confirm") == null || form.getActionParamaterValue("confirm").equals("")){
690             // redirect back to client to display lightbox
691             return showDialog("releaseToDepts", form, request, response);
692         } else if (form.getActionParamaterValue("confirm").equals("do") ){
693             form = releaseToDepts(form, result, request, response);
694             form.getDialogManager().removeAllDialogs();
695             form.setLightboxScript("closeLightbox('releaseToDepts');");
696         }
697         return getUIFModelAndView(form);
698     }
699 
700     private String getTermDisplayString(String termId, TermInfo term) {
701         // Return Term as String display like 'FALL 2020 (9/26/2020-12/26/2020)'
702         StringBuilder stringBuilder = new StringBuilder();
703         Formatter formatter = new Formatter(stringBuilder, Locale.US);
704         String displayString = termId; // use termId as a default.
705         if (term != null) {
706             String startDate = DateFormatters.MONTH_DAY_YEAR_DATE_FORMATTER.format(term.getStartDate());
707             String endDate = DateFormatters.MONTH_DAY_YEAR_DATE_FORMATTER.format(term.getEndDate());
708             String termType = term.getName();
709             formatter.format("%s (%s to %s)", termType, startDate, endDate);
710             displayString = stringBuilder.toString();
711         }
712         return displayString;
713     }
714 }