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