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 vgadiyak on 5/30/12
16   */
17  package org.kuali.student.enrollment.class2.courseoffering.service.impl;
18  
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.log4j.Logger;
21  import org.kuali.rice.core.api.util.ConcreteKeyValue;
22  import org.kuali.rice.kim.api.identity.Person;
23  import org.kuali.rice.krad.exception.AuthorizationException;
24  import org.kuali.rice.krad.maintenance.Maintainable;
25  import org.kuali.rice.krad.maintenance.MaintenanceDocument;
26  import org.kuali.rice.krad.uif.container.CollectionGroup;
27  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
28  import org.kuali.rice.krad.uif.view.View;
29  import org.kuali.rice.krad.util.GlobalVariables;
30  import org.kuali.rice.krad.util.KRADConstants;
31  import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
32  import org.kuali.student.enrollment.class2.courseoffering.dto.CourseOfferingContextBar;
33  import org.kuali.student.enrollment.class2.courseoffering.dto.CourseOfferingEditWrapper;
34  import org.kuali.student.enrollment.class2.courseoffering.dto.FormatOfferingWrapper;
35  import org.kuali.student.enrollment.class2.courseoffering.dto.OfferingInstructorWrapper;
36  import org.kuali.student.enrollment.class2.courseoffering.dto.OrganizationInfoWrapper;
37  import org.kuali.student.enrollment.class2.courseoffering.util.ActivityOfferingConstants;
38  import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingConstants;
39  import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingManagementUtil;
40  import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingViewHelperUtil;
41  import org.kuali.student.enrollment.courseoffering.dto.CourseOfferingCrossListingInfo;
42  import org.kuali.student.enrollment.courseoffering.dto.CourseOfferingInfo;
43  import org.kuali.student.enrollment.courseoffering.dto.CreditOptionInfo;
44  import org.kuali.student.enrollment.courseoffering.dto.FinalExam;
45  import org.kuali.student.enrollment.courseoffering.dto.FormatOfferingInfo;
46  import org.kuali.student.enrollment.courseoffering.dto.OfferingInstructorInfo;
47  import org.kuali.student.enrollment.courseofferingset.dto.SocInfo;
48  import org.kuali.student.r2.common.dto.AttributeInfo;
49  import org.kuali.student.r2.common.dto.ContextInfo;
50  import org.kuali.student.r2.common.exceptions.DoesNotExistException;
51  import org.kuali.student.r2.common.exceptions.InvalidParameterException;
52  import org.kuali.student.r2.common.exceptions.MissingParameterException;
53  import org.kuali.student.r2.common.exceptions.OperationFailedException;
54  import org.kuali.student.r2.common.exceptions.PermissionDeniedException;
55  import org.kuali.student.r2.common.util.ContextUtils;
56  import org.kuali.student.r2.common.util.constants.CourseOfferingServiceConstants;
57  import org.kuali.student.r2.common.util.constants.CourseOfferingSetServiceConstants;
58  import org.kuali.student.r2.common.util.constants.LprServiceConstants;
59  import org.kuali.student.r2.common.util.constants.LuServiceConstants;
60  import org.kuali.student.r2.common.util.constants.LuiServiceConstants;
61  import org.kuali.student.r2.common.util.date.DateFormatters;
62  import org.kuali.student.r2.core.acal.dto.TermInfo;
63  import org.kuali.student.r2.core.class1.search.ActivityOfferingSearchServiceImpl;
64  import org.kuali.student.r2.core.class1.search.CourseOfferingManagementSearchImpl;
65  import org.kuali.student.r2.core.organization.dto.OrgInfo;
66  import org.kuali.student.r2.core.search.dto.SearchRequestInfo;
67  import org.kuali.student.r2.core.search.dto.SearchResultCellInfo;
68  import org.kuali.student.r2.core.search.dto.SearchResultInfo;
69  import org.kuali.student.r2.core.search.dto.SearchResultRowInfo;
70  import org.kuali.student.r2.lum.course.dto.ActivityInfo;
71  import org.kuali.student.r2.lum.course.dto.CourseInfo;
72  import org.kuali.student.r2.lum.course.dto.FormatInfo;
73  import org.kuali.student.r2.lum.course.service.assembler.CourseAssemblerConstants;
74  import org.kuali.student.r2.lum.lrc.dto.ResultValueInfo;
75  import org.kuali.student.r2.lum.lrc.dto.ResultValuesGroupInfo;
76  import org.kuali.student.r2.lum.util.constants.LrcServiceConstants;
77  
78  import java.util.ArrayList;
79  import java.util.Arrays;
80  import java.util.Collection;
81  import java.util.Collections;
82  import java.util.Comparator;
83  import java.util.HashSet;
84  import java.util.List;
85  import java.util.Map;
86  import java.util.Set;
87  
88  /**
89   * This class provides Maintainable logic for Course Offerings in the Course Offering Edit ui
90   *
91   * @author Kuali Student Team
92   */
93  public class CourseOfferingEditMaintainableImpl extends CourseOfferingMaintainableImpl implements Maintainable {
94      private static final long serialVersionUID = 1L;
95      private final static Logger LOG = Logger.getLogger(CourseOfferingEditMaintainableImpl.class);
96  
97      //TODO : implement the functionality for Personnel section and its been delayed now since the backend implementation is not yet ready (06/06/2012). KSENROLL-1375
98  
99      @Override
100     public void saveDataObject() {
101         if (getMaintenanceAction().equals(KRADConstants.MAINTENANCE_EDIT_ACTION)) {
102             CourseOfferingEditWrapper coEditWrapper = (CourseOfferingEditWrapper) getDataObject();
103             updateCourseOffering(coEditWrapper);
104         } else if (getMaintenanceAction().equals(KRADConstants.MAINTENANCE_NEW_ACTION)) {
105             CourseOfferingEditWrapper coCreateWrapper = (CourseOfferingEditWrapper) getDataObject();
106             CourseOfferingInfo createdCOInfo = createCourseOfferingInfo(coCreateWrapper);
107             coCreateWrapper.setCourseOfferingInfo(createdCOInfo);
108         } else { //for copy action, report error
109             LOG.error(">>>Do not support!");
110         }
111 
112     }
113 
114     private void updateCourseOffering(CourseOfferingEditWrapper coEditWrapper) {
115         try {
116             // persist format offerings
117             updateFormatOfferings(coEditWrapper);
118 
119             //persist unitDeploymentOrgIds
120             List<String> unitDeploymentOrgIds = new ArrayList<String>();
121             for (OrganizationInfoWrapper orgWrapper : coEditWrapper.getOrganizationNames()) {
122                 if( StringUtils.isNotBlank(orgWrapper.getId())) {
123                     unitDeploymentOrgIds.add(orgWrapper.getId());
124                 }
125             }
126 
127             CourseOfferingInfo coInfo = coEditWrapper.getCourseOfferingInfo();
128             coInfo.setUnitsDeploymentOrgIds(unitDeploymentOrgIds);
129 
130             ContextInfo contextInfo = ContextUtils.createDefaultContextInfo();
131 
132             // Credit Options (also creates extra-line)
133             if (coEditWrapper.getCreditOption().getTypeKey().equals(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_FIXED) &&
134                     !coEditWrapper.getCreditOption().getFixedCredit().isEmpty()) {
135                 ResultValuesGroupInfo rvgInfo = CourseOfferingManagementUtil.getLrcService().getCreateFixedCreditResultValuesGroup(coEditWrapper.getCreditOption().getFixedCredit(),
136                         LrcServiceConstants.RESULT_SCALE_KEY_CREDIT_DEGREE, contextInfo);
137                 coInfo.setCreditOptionId(rvgInfo.getKey());
138             } else if (coEditWrapper.getCreditOption().getTypeKey().equals(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_RANGE) &&
139                     !coEditWrapper.getCreditOption().getMinCredits().isEmpty() && !coEditWrapper.getCreditOption().getMaxCredits().isEmpty()) {
140                 ResultValuesGroupInfo rvgInfo = CourseOfferingManagementUtil.getLrcService().getCreateRangeCreditResultValuesGroup(coEditWrapper.getCreditOption().getMinCredits(),
141                         coEditWrapper.getCreditOption().getMaxCredits(), calculateIncrement(coEditWrapper.getCreditOption().getAllowedCredits()), LrcServiceConstants.RESULT_SCALE_KEY_CREDIT_DEGREE, contextInfo);
142                 coInfo.setCreditOptionId(rvgInfo.getKey());
143             } else if (coEditWrapper.getCreditOption().getTypeKey().equals(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_MULTIPLE) &&
144                     !coEditWrapper.getCreditOption().getCredits().isEmpty()) {
145                 ResultValuesGroupInfo rvgInfo = CourseOfferingManagementUtil.getLrcService().getCreateMultipleCreditResultValuesGroup(coEditWrapper.getCreditOption().getCredits(),
146                         LrcServiceConstants.RESULT_SCALE_KEY_CREDIT_DEGREE, contextInfo);
147                 coInfo.setCreditOptionId(rvgInfo.getKey());
148             }
149 
150             // CO code
151             String courseOfferingCode = coEditWrapper.getCourse().getCode();
152             coInfo.setCourseNumberSuffix(StringUtils.upperCase(coInfo.getCourseNumberSuffix()));
153             if (!StringUtils.isEmpty(coInfo.getCourseNumberSuffix())) {
154                 courseOfferingCode += coInfo.getCourseNumberSuffix();
155             }
156             coInfo.setCourseOfferingCode(courseOfferingCode);
157 
158             // Waitlist
159             if(coInfo.getHasWaitlist() && !coEditWrapper.getHasWaitlist()) {
160                 //activate the waitlist
161                 CourseOfferingManagementUtil.getCourseWaitListServiceFacade().activateActivityOfferingWaitlistsByCourseOffering(coInfo.getId(), coInfo.getTermId(), contextInfo);
162             } else if (!coInfo.getHasWaitlist() && coEditWrapper.getHasWaitlist()) {
163                 //deactivate the waitlist
164                 CourseOfferingManagementUtil.getCourseWaitListServiceFacade().deactivateActivityOfferingWaitlistsByCourseOffering(coInfo.getId(), contextInfo);
165             }
166 
167             //TODO REMOVE THIS AFTER KRAD CHECKLISTS ARE FIXED for student registration options
168             //determine if audit reg options and pass/fail reg options should be added/removed to/from coInfo
169             if (coEditWrapper.getAuditStudentRegOpts() &&
170                     !coInfo.getStudentRegistrationGradingOptions().contains(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_AUDIT)) {
171                 coInfo.getStudentRegistrationGradingOptions().add(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_AUDIT);
172             } else if (!coEditWrapper.getAuditStudentRegOpts() &&
173                     coInfo.getStudentRegistrationGradingOptions().contains(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_AUDIT)) {
174                 coInfo.getStudentRegistrationGradingOptions().remove(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_AUDIT);
175             }
176 
177             if (coEditWrapper.getPassFailStudentRegOpts() &&
178                     !coInfo.getStudentRegistrationGradingOptions().contains(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_PASSFAIL)) {
179                 coInfo.getStudentRegistrationGradingOptions().add(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_PASSFAIL);
180             } else if (!coEditWrapper.getPassFailStudentRegOpts() &&
181                     coInfo.getStudentRegistrationGradingOptions().contains(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_PASSFAIL)) {
182                 coInfo.getStudentRegistrationGradingOptions().remove(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_PASSFAIL);
183             }
184 
185             updateInstructors(coEditWrapper, coInfo);
186 
187             //Save cross lists
188             loadCrossListedCOs(coEditWrapper, coInfo);
189 
190             // update Final Exam Driver
191             setFinalExamDriverAttr(coInfo, coEditWrapper);
192 
193             // update Use Final Exam Matrix
194             setUseFinalExamMatrix(coInfo, coEditWrapper);
195 
196             CourseOfferingManagementUtil.getCourseOfferingService().updateCourseOffering(coInfo.getId(), coInfo, contextInfo);
197 
198             // generate exam offerings if exam period exists
199             if (!StringUtils.isEmpty(coEditWrapper.getExamPeriodId())) {
200                 CourseOfferingManagementUtil.getExamOfferingServiceFacade().generateFinalExamOffering(coInfo,
201                         coInfo.getTermId(), coEditWrapper.getExamPeriodId(), new ArrayList<String>(), contextInfo);
202             }
203 
204             // check for changes to states in CO and related FOs (may happen in the case of deleted FOs)
205 //            CourseOfferingViewHelperUtil.updateCourseOfferingStateFromActivityOfferingStateChange(coInfo, contextInfo);
206 
207         } catch (Exception ex) {
208             throw new RuntimeException(ex);
209         }
210 
211     }
212 
213     protected CourseOfferingInfo createCourseOfferingInfo(CourseOfferingEditWrapper coCreateWrapper) {
214 
215         try {
216             //persist unitDeploymentOrgIds
217             List<String> unitDeploymentOrgIds = new ArrayList<String>();
218             for (OrganizationInfoWrapper orgWrapper : coCreateWrapper.getOrganizationNames()) {
219                 unitDeploymentOrgIds.add(orgWrapper.getId());
220             }
221 
222             CourseOfferingInfo coInfo = coCreateWrapper.getCourseOfferingInfo();
223             coInfo.setUnitsDeploymentOrgIds(unitDeploymentOrgIds);
224 
225             ContextInfo contextInfo = ContextUtils.createDefaultContextInfo();
226 
227             // Credit Options (also creates extra-line)
228             if (coCreateWrapper.getCreditOption().getTypeKey().equals(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_FIXED) &&
229                     !coCreateWrapper.getCreditOption().getFixedCredit().isEmpty()) {
230                 ResultValuesGroupInfo rvgInfo = CourseOfferingManagementUtil.getLrcService().getCreateFixedCreditResultValuesGroup(coCreateWrapper.getCreditOption().getFixedCredit(),
231                         LrcServiceConstants.RESULT_SCALE_KEY_CREDIT_DEGREE, contextInfo);
232                 coInfo.setCreditOptionId(rvgInfo.getKey());
233             } else if (coCreateWrapper.getCreditOption().getTypeKey().equals(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_RANGE) &&
234                     !coCreateWrapper.getCreditOption().getMinCredits().isEmpty() && !coCreateWrapper.getCreditOption().getMaxCredits().isEmpty()) {
235                 ResultValuesGroupInfo rvgInfo = CourseOfferingManagementUtil.getLrcService().getCreateRangeCreditResultValuesGroup(coCreateWrapper.getCreditOption().getMinCredits(),
236                         coCreateWrapper.getCreditOption().getMaxCredits(), calculateIncrement(coCreateWrapper.getCreditOption().getAllowedCredits()), LrcServiceConstants.RESULT_SCALE_KEY_CREDIT_DEGREE, contextInfo);
237                 coInfo.setCreditOptionId(rvgInfo.getKey());
238             } else if (coCreateWrapper.getCreditOption().getTypeKey().equals(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_MULTIPLE) &&
239                     !coCreateWrapper.getCreditOption().getCredits().isEmpty()) {
240                 ResultValuesGroupInfo rvgInfo = CourseOfferingManagementUtil.getLrcService().getCreateMultipleCreditResultValuesGroup(coCreateWrapper.getCreditOption().getCredits(),
241                         LrcServiceConstants.RESULT_SCALE_KEY_CREDIT_DEGREE, contextInfo);
242                 coInfo.setCreditOptionId(rvgInfo.getKey());
243             }
244 
245             // CO code
246             List<String> optionKeys = CourseOfferingManagementUtil.getDefaultOptionKeysService().getDefaultOptionKeysForCreateCourseOfferingFromCanonical();
247             String courseOfferingCode = coCreateWrapper.getCourse().getCode();
248             coInfo.setCourseNumberSuffix(StringUtils.upperCase(coInfo.getCourseNumberSuffix()));
249             if (!StringUtils.isEmpty(coInfo.getCourseNumberSuffix())) {
250                 courseOfferingCode += coInfo.getCourseNumberSuffix();
251                 optionKeys.add(CourseOfferingServiceConstants.APPEND_COURSE_OFFERING_CODE_SUFFIX_OPTION_KEY);
252             }
253             coInfo.setCourseOfferingCode(courseOfferingCode);
254 
255             // Waitlist
256             if (!coInfo.getHasWaitlist()) {
257                 coInfo.setWaitlistTypeKey(null);
258                 coInfo.setWaitlistLevelTypeKey(null);
259             }
260 
261             //TODO REMOVE THIS AFTER KRAD CHECKLISTS ARE FIXED for student registration options
262             //determine if audit reg options and pass/fail reg options should be added/removed to/from coInfo
263             if (coCreateWrapper.getAuditStudentRegOpts() &&
264                     !coInfo.getStudentRegistrationGradingOptions().contains(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_AUDIT)) {
265                 coInfo.getStudentRegistrationGradingOptions().add(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_AUDIT);
266             } else if (!coCreateWrapper.getAuditStudentRegOpts() &&
267                     coInfo.getStudentRegistrationGradingOptions().contains(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_AUDIT)) {
268                 coInfo.getStudentRegistrationGradingOptions().remove(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_AUDIT);
269             }
270 
271             if (coCreateWrapper.getPassFailStudentRegOpts() &&
272                     !coInfo.getStudentRegistrationGradingOptions().contains(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_PASSFAIL)) {
273                 coInfo.getStudentRegistrationGradingOptions().add(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_PASSFAIL);
274             } else if (!coCreateWrapper.getPassFailStudentRegOpts() &&
275                     coInfo.getStudentRegistrationGradingOptions().contains(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_PASSFAIL)) {
276                 coInfo.getStudentRegistrationGradingOptions().remove(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_PASSFAIL);
277             }
278 
279             updateInstructors(coCreateWrapper, coInfo);
280 
281             //Save cross lists
282             loadCrossListedCOs(coCreateWrapper, coInfo);
283 
284             // set Final Exam Driver
285             setFinalExamDriverAttr(coInfo, coCreateWrapper);
286 
287             //set Use Final Exam Matrix
288             setUseFinalExamMatrix(coInfo, coCreateWrapper);
289 
290             CourseOfferingInfo info = CourseOfferingManagementUtil.getCourseOfferingService().createCourseOffering(coInfo.getCourseId(), coInfo.getTermId(), LuiServiceConstants.COURSE_OFFERING_TYPE_KEY, coInfo, optionKeys, contextInfo);
291 
292             coCreateWrapper.setCourseOfferingInfo(info);
293 
294             this.updateFormatOfferings(coCreateWrapper);
295             // generate exam offerings if exam period exists
296             if (!StringUtils.isEmpty(coCreateWrapper.getExamPeriodId())) {
297                 CourseOfferingManagementUtil.getExamOfferingServiceFacade().generateFinalExamOffering(info, info.getTermId(), coCreateWrapper.getExamPeriodId(), new ArrayList<String>(), contextInfo);
298             }
299 
300             return info;
301 
302         } catch (Exception ex) {
303             throw new RuntimeException(ex);
304         }
305     }
306 
307     private void updateInstructors(CourseOfferingEditWrapper coEditWrapper, CourseOfferingInfo coInfo) {
308         List<OfferingInstructorWrapper> instructors = coEditWrapper.getInstructors();
309         List<OfferingInstructorInfo> coInstructors = coInfo.getInstructors();
310         coInstructors.clear();
311         int firstPerson = 0;
312 
313         for (OfferingInstructorWrapper instructorWrapper : instructors) {
314             if (instructorWrapper != null) {
315                 OfferingInstructorInfo info = instructorWrapper.getOfferingInstructorInfo();
316                 if ((info != null) && (info.getPersonId() != null) && !info.getPersonId().isEmpty()) {
317                     if (info.getStateKey() == null) {
318                         info.setStateKey(LprServiceConstants.TENTATIVE_STATE_KEY);
319                     }
320                     if (info.getPersonName() == null) {
321                         List<Person> personList = CourseOfferingViewHelperUtil.getInstructorByPersonId(info.getPersonId());
322                         if (personList.size() == 1) {
323                             info.setPersonName(personList.get(firstPerson).getName());
324                         }
325 
326                     }
327                     coInstructors.add(info);
328                 }
329             }
330         }
331 
332     }
333 
334     private String calculateIncrement(List<String> credits) {
335         //Sort the list of credits options by the float value.
336         Collections.sort(credits, new Comparator<String>() {
337             public int compare(String o1, String o2) {
338                 return Float.valueOf(o1).compareTo(Float.valueOf(o2));
339             }
340         });
341         // JIRA Fix : KSENROLL-8726. - Validate credit list and throw exception.
342         if (credits == null || credits.size() < 2) {
343             throw new RuntimeException("Not enough credits to calculate credit increment");
344         }
345         int firstValue = 0;
346         int secondValue = 1;
347         //Find the difference between the first two values to get the increment
348         return String.valueOf(Float.parseFloat(credits.get(secondValue)) - Float.parseFloat(credits.get(firstValue)));
349     }
350 
351     private void updateFormatOfferings(CourseOfferingEditWrapper coEditWrapper) {
352         try {
353             List<FormatOfferingWrapper> formatOfferingList = coEditWrapper.getFormatOfferingList();
354             CourseOfferingInfo coInfo = coEditWrapper.getCourseOfferingInfo();
355             List<String> currentFOIds = getExistingFormatOfferingIds(coInfo.getId());
356             ContextInfo contextInfo = ContextUtils.createDefaultContextInfo();
357             if (formatOfferingList != null && !formatOfferingList.isEmpty()) {
358                 FormatOfferingInfo updatedFormatOffering;
359                 for (FormatOfferingWrapper foWrapper : formatOfferingList) {
360                     FormatOfferingInfo formatOfferingInfo = foWrapper.getFormatOfferingInfo();
361                     if (StringUtils.isNotBlank(formatOfferingInfo.getFormatId())) {
362                         if (formatOfferingInfo.getId() != null &&
363                                 !formatOfferingInfo.getId().isEmpty() &&
364                                 currentFOIds.contains(formatOfferingInfo.getId())) {
365                             //update FO
366                             if (coInfo.getFinalExamType() != null && !coInfo.getFinalExamType().equals(CourseOfferingConstants.COURSEOFFERING_FINAL_EXAM_TYPE_STANDARD)) {
367                                 formatOfferingInfo.setFinalExamLevelTypeKey(null);
368                             }
369                             // Populate AO types (all FOs should "require" this (less important here since it should
370                             // already exist)
371                             CourseOfferingViewHelperUtil.addActivityOfferingTypesToFormatOffering(formatOfferingInfo, coEditWrapper.getCourse(), CourseOfferingManagementUtil.getTypeService(), contextInfo);
372                             updatedFormatOffering = CourseOfferingManagementUtil.getCourseOfferingService().updateFormatOffering(formatOfferingInfo.getId(), formatOfferingInfo, contextInfo);
373                             currentFOIds.remove(formatOfferingInfo.getId());
374                         } else {
375                             //create a new FO
376                             formatOfferingInfo.setStateKey(LuiServiceConstants.LUI_FO_STATE_DRAFT_KEY);
377                             formatOfferingInfo.setTypeKey(LuiServiceConstants.FORMAT_OFFERING_TYPE_KEY);
378                             formatOfferingInfo.setDescr(null);
379                             formatOfferingInfo.setTermId(coInfo.getTermId());
380                             formatOfferingInfo.setCourseOfferingId(coInfo.getId());
381                             //We need to set the name to maintain ordinality from CLU. (If we're not setting the name here, service will set the name based on priority order which is not the expected behavior)
382                             if (StringUtils.isBlank(formatOfferingInfo.getName())) {
383                                 String[] foNames = getFormatShortAndLongNames(foWrapper, coEditWrapper.getCourse());
384                                 formatOfferingInfo.setName(foNames[0]);
385                                 formatOfferingInfo.setShortName(foNames[1]);
386 
387                             }
388                             if (coInfo.getFinalExamType() != null && !coInfo.getFinalExamType().equals(CourseOfferingConstants.COURSEOFFERING_FINAL_EXAM_TYPE_STANDARD)) {
389                                 formatOfferingInfo.setFinalExamLevelTypeKey(null);
390                             }
391                             // Populate AO types (all FOs should "require" this
392                             CourseOfferingViewHelperUtil.addActivityOfferingTypesToFormatOffering(formatOfferingInfo, coEditWrapper.getCourse(), CourseOfferingManagementUtil.getTypeService(), contextInfo);
393                             updatedFormatOffering = CourseOfferingManagementUtil.getCourseOfferingService().createFormatOffering(coInfo.getId(), formatOfferingInfo.getFormatId(), formatOfferingInfo.getTypeKey(), formatOfferingInfo, contextInfo);
394                         }
395                         foWrapper.setFormatOfferingInfo(updatedFormatOffering);
396                     }
397                 }
398             }
399             //delete FormatOfferings that have been removed by the user
400             if (currentFOIds != null && currentFOIds.size() > 0) {
401                 for (String formatOfferingId : currentFOIds) {
402                     //delete all AOs associated with this FO, then delete FO
403                     //Note by bonnie deleteAO invoked in deleteFormatOfferingCascaded seems not completely correct.
404                     //I didn't see the code if removing FO-AO relations before deleting AOs....
405                     CourseOfferingManagementUtil.getCourseOfferingService().deleteFormatOfferingCascaded(formatOfferingId, contextInfo);
406                 }
407             }
408         } catch (Exception ex) {
409             throw new RuntimeException(ex);
410         }
411     }
412 
413     private List<String> getExistingFormatOfferingIds(String courseOfferingId) throws Exception {
414         List<String> formatOfferingIds = new ArrayList<String>();
415 
416         SearchRequestInfo sr = new SearchRequestInfo(ActivityOfferingSearchServiceImpl.FO_IDS_BY_CO_ID_SEARCH_KEY);
417         sr.addParam(ActivityOfferingSearchServiceImpl.SearchParameters.CO_ID, courseOfferingId);
418         SearchResultInfo searchResult = CourseOfferingManagementUtil.getSearchService().search(sr, null);
419         List<SearchResultRowInfo> rows = searchResult.getRows();
420         if (!rows.isEmpty()) {
421             for (SearchResultRowInfo row: rows) {
422                 List<SearchResultCellInfo> cells = row.getCells();
423                 for (SearchResultCellInfo cell: cells) {
424                     if(ActivityOfferingSearchServiceImpl.SearchResultColumns.FO_ID.equals(cell.getKey())){
425                         formatOfferingIds.add(cell.getValue());
426                     }
427                 }
428             }
429         }
430 
431         return formatOfferingIds;
432     }
433 
434     @Override
435     protected boolean performAddLineValidation(View view, CollectionGroup collectionGroup, Object model, Object addLine) {
436         if (addLine instanceof OfferingInstructorInfo) {
437             OfferingInstructorInfo instructorInfo = (OfferingInstructorInfo) addLine;
438 
439             //check duplication
440             MaintenanceDocumentForm form = (MaintenanceDocumentForm) model;
441             CourseOfferingEditWrapper coEditWrapper = (CourseOfferingEditWrapper) form.getDocument().getNewMaintainableObject().getDataObject();
442             List<OfferingInstructorInfo> instructors = coEditWrapper.getCourseOfferingInfo().getInstructors();
443             if (instructors != null && !instructors.isEmpty()) {
444                 for (OfferingInstructorInfo thisInst : instructors) {
445                     if (instructorInfo.getPersonId().equals(thisInst.getPersonId())) {
446                         GlobalVariables.getMessageMap().putErrorForSectionId("KS-CourseOfferingEdit-PersonnelTableSection", ActivityOfferingConstants.MSG_ERROR_INSTRUCTOR_DUPLICATE, instructorInfo.getPersonId());
447                         return false;
448                     }
449                 }
450             }
451 
452             //validate ID
453             List<Person> lstPerson = CourseOfferingViewHelperUtil.getInstructorByPersonId(instructorInfo.getPersonId());
454             if (lstPerson == null || lstPerson.isEmpty()) {
455                 GlobalVariables.getMessageMap().putErrorForSectionId("KS-CourseOfferingEdit-PersonnelSection", ActivityOfferingConstants.MSG_ERROR_INSTRUCTOR_NOTFOUND, instructorInfo.getPersonId());
456                 return false;
457             }
458         } else if (addLine instanceof OrganizationInfoWrapper) {
459             OrganizationInfoWrapper org = (OrganizationInfoWrapper) addLine;
460             if (StringUtils.isEmpty(org.getId())) {
461                 GlobalVariables.getMessageMap().putErrorForSectionId(collectionGroup.getId(), ActivityOfferingConstants.MSG_ERROR_ORGANIZATION_ID_REQUIRED);
462                 return false;
463             }
464         }
465 
466         return super.performAddLineValidation(view, collectionGroup, model, addLine);
467     }
468 
469     @Override
470     protected void processBeforeAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine) {
471         if (addLine instanceof FormatOfferingInfo) {
472             FormatOfferingInfo newLine = (FormatOfferingInfo) addLine;
473             String formatId = newLine.getFormatId();
474             MaintenanceDocumentForm form = (MaintenanceDocumentForm) model;
475             CourseOfferingEditWrapper coEditWrapper = (CourseOfferingEditWrapper) form.getDocument().getNewMaintainableObject().getDataObject();
476             FormatInfo formatInfo = getFormatInfo(coEditWrapper, formatId);
477             // TODO: fix R2 Format to include name and short name
478             StringBuilder sb = new StringBuilder();
479             for (ActivityInfo activityInfo : formatInfo.getActivities()) {
480                 String activityTypeKey = activityInfo.getTypeKey();
481                 String activityName = "";
482                 try {
483                     if (!activityTypeKey.isEmpty()) {
484                         activityName = CourseOfferingManagementUtil.getTypeService().getType(activityTypeKey, ContextUtils.createDefaultContextInfo()).getName();
485                     }
486                 } catch (Exception e) {
487                     throw new RuntimeException(e);
488                 }
489 
490                 sb.append(activityName);
491                 sb.append("/");
492             }
493             String tempName = sb.toString().substring(0, sb.toString().length() - 1);
494             newLine.setName(tempName);
495             newLine.setShortName(tempName);
496         }
497     }
498 
499     @Override
500     protected void processAfterAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine, boolean isValidLine) {
501         if (addLine instanceof OfferingInstructorInfo) {
502             // set the person name if it's null, in the case of user-input personell id
503             OfferingInstructorInfo instructorInfo = (OfferingInstructorInfo) addLine;
504             if (instructorInfo.getPersonName() == null && instructorInfo.getPersonId() != null) {
505                 List<Person> personList = CourseOfferingViewHelperUtil.getInstructorByPersonId(instructorInfo.getPersonId());
506                 if (personList.size() == 1) {
507                     int firstperson = 0;
508                     instructorInfo.setPersonName(personList.get(firstperson).getName());
509                 }
510             }
511 
512             // make sure state is not null
513             if (instructorInfo.getStateKey() == null) {
514                 instructorInfo.setStateKey(LprServiceConstants.TENTATIVE_STATE_KEY);
515             }
516         }
517     }
518 
519     @Override
520     protected void processAfterDeleteLine(View view, CollectionGroup collectionGroup, Object model, int lineIndex) {
521 
522         MaintenanceDocumentForm maintenanceForm = (MaintenanceDocumentForm) model;
523         MaintenanceDocument document = maintenanceForm.getDocument();
524 
525         if (collectionGroup.getPropertyName().endsWith("instructors")) {
526             if (model instanceof MaintenanceDocumentForm) {
527                 // get the old object's collection
528                 Collection<Object> oldCollection = ObjectPropertyUtils.getPropertyValue(document.getOldMaintainableObject().getDataObject(),
529                         collectionGroup.getPropertyName());
530                 if (oldCollection.size() - 1 >= lineIndex) {
531                     super.processAfterDeleteLine(view, collectionGroup, model, lineIndex);
532                 }
533             }
534         } else if (collectionGroup.getPropertyName().endsWith("formatOfferingList")) {
535             CourseOfferingEditWrapper coWrapper = (CourseOfferingEditWrapper) document.getNewMaintainableObject().getDataObject();
536             populateFormatNames(coWrapper);
537         } else {
538             super.processAfterDeleteLine(view, collectionGroup, model, lineIndex);
539         }
540     }
541 
542     @Override
543     public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) {
544         try {
545             ContextInfo contextInfo = ContextUtils.createDefaultContextInfo();
546 
547             if (getDataObject() instanceof CourseOfferingEditWrapper) {
548 
549                 //0.1 get credit count from CourseInfo
550                 CourseOfferingInfo coInfo = CourseOfferingManagementUtil.getCourseOfferingService().getCourseOffering(dataObjectKeys.get("courseOfferingInfo.id"), contextInfo);
551                 CourseInfo courseInfo = CourseOfferingManagementUtil.getCourseService().getCourse(coInfo.getCourseId(), contextInfo);
552 
553                 //1. set CourseOfferingInfo
554                 CourseOfferingEditWrapper formObject = new CourseOfferingEditWrapper(coInfo);
555                 formObject.setHasWaitlist(coInfo.getHasWaitlist());
556 
557                 //2. set CourseInfo
558                 formObject.setCourse(courseInfo);
559 
560                 //3. set formatOfferingList
561                 List<FormatOfferingInfo> formatOfferingList = CourseOfferingManagementUtil.getCourseOfferingService().getFormatOfferingsByCourseOffering(coInfo.getId(), contextInfo);
562                 List<FormatOfferingWrapper> foList = new ArrayList<FormatOfferingWrapper>();
563                 for (FormatOfferingInfo fo : formatOfferingList) {
564                     FormatOfferingWrapper wrapper = new FormatOfferingWrapper();
565                     wrapper.setFormatOfferingInfo(fo);
566                     wrapper.setCourseOfferingWrapper(formObject);
567 
568                     //set the reader friendly Grade Roster Level
569                     if (!StringUtils.isEmpty(fo.getGradeRosterLevelTypeKey())) {
570                         wrapper.setGradeRosterUI(CourseOfferingManagementUtil.getTypeService().getType(fo.getGradeRosterLevelTypeKey(), contextInfo).getName());
571                     }
572                     //set the reader friendly Final Exam Driver Activity
573                     if (!StringUtils.isEmpty(fo.getFinalExamLevelTypeKey()) && StringUtils.equals(formObject.getFinalExamDriver(), LuServiceConstants.LU_EXAM_DRIVER_AO_KEY)) {
574                         wrapper.setFinalExamUI(CourseOfferingManagementUtil.getTypeService().getType(fo.getFinalExamLevelTypeKey(), contextInfo).getName());
575                     } else {
576                         wrapper.setFinalExamUI(" ");
577                     }
578 
579 
580 
581                     foList.add(wrapper);
582                 }
583                 formObject.setFormatOfferingList(foList);
584 
585                 if (foList.isEmpty()) {
586                     FormatOfferingWrapper defaultFO = new FormatOfferingWrapper();
587                     defaultFO.getRenderHelper().setNewRow(true);
588                     defaultFO.setCourseOfferingWrapper(formObject);
589                     formObject.getFormatOfferingList().add(defaultFO);
590                 }
591 
592                 //4. Checking if Grading Options should be disabled or not and assign default (if no value)
593                 //5. Checking if there are any student registration options from CLU for screen display
594                 List<String> studentRegOptions = new ArrayList<String>();
595                 List<String> crsGradingOptions = new ArrayList<String>();
596                 if (coInfo.getCourseId() != null && courseInfo != null) {
597                     List<String> gradingOptions = courseInfo.getGradingOptions();
598                     Set<String> regOpts = new HashSet<String>(Arrays.asList(CourseOfferingServiceConstants.ALL_STUDENT_REGISTRATION_OPTION_TYPE_KEYS));
599                     for (String gradingOption : gradingOptions) {
600                         if (regOpts.contains(gradingOption)) {
601                             studentRegOptions.add(gradingOption);
602                         } else {
603                             crsGradingOptions.add(gradingOption);
604                         }
605                     }
606                     //Audit is pulled out into a dynamic attribute on course so map it back
607                     if ("true".equals(courseInfo.getAttributeValue(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_AUDIT))) {
608                         studentRegOptions.add(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_AUDIT);
609                     }
610                 }
611 
612                 //TODO REMOVE THIS WHEN KRAD IS FIXED
613                 if (coInfo.getStudentRegistrationGradingOptions().contains(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_AUDIT)) {
614                     formObject.setAuditStudentRegOpts(true);
615                 } else {
616                     formObject.setAuditStudentRegOpts(false);
617                 }
618                 if (coInfo.getStudentRegistrationGradingOptions().contains(LrcServiceConstants.RESULT_GROUP_KEY_GRADE_PASSFAIL)) {
619                     formObject.setPassFailStudentRegOpts(true);
620                 } else {
621                     formObject.setPassFailStudentRegOpts(false);
622                 }
623 
624                 formObject.setStudentRegOptions(studentRegOptions);
625                 formObject.setCrsGradingOptions(crsGradingOptions);
626 
627                 //6. Defining Credit Option and if CLU is fixed (then it's disabled)
628                 boolean creditOptionFixed = false;
629                 String creditOptionId = coInfo.getCreditOptionId();
630 
631                 CreditOptionInfo creditOption = new CreditOptionInfo();
632 
633                 //Grab the Course's credit constraints
634                 //FindBugs: getCreditOptions() null check is in CourseInfo
635                 List<ResultValuesGroupInfo> courseCreditOptions = courseInfo.getCreditOptions();
636                 int firstValue = 0;
637 
638                 //Lookup the related course's credit constraints and set them on the creditOption
639                 if (coInfo.getCourseId() != null && !courseCreditOptions.isEmpty()) {
640                     ResultValuesGroupInfo resultValuesGroupInfo = courseCreditOptions.get(firstValue);
641                     //Check for fixed
642                     if (resultValuesGroupInfo.getTypeKey().equalsIgnoreCase(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_FIXED)) {
643                         if (!resultValuesGroupInfo.getResultValueKeys().isEmpty()) {
644                             creditOption.setCourseFixedCredits(CourseOfferingManagementUtil.getLrcService().getResultValue(resultValuesGroupInfo.getResultValueKeys().get(firstValue), contextInfo).getValue());
645                         }
646                         //Set the flag
647                         creditOptionFixed = true;
648 
649                         //Default the value
650                         creditOption.setTypeKey(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_FIXED);
651                         creditOption.setFixedCredit(creditOption.getCourseFixedCredits());
652                         creditOption.getAllowedCredits().add(creditOption.getCourseFixedCredits());
653                     } else {
654                         //This is either range or multiple
655 
656                         //Copy all the allowed credits and sort so that the multiple checkboxes can be properly displayed
657                         List<ResultValueInfo> resultValueInfos = CourseOfferingManagementUtil.getLrcService().getResultValuesForResultValuesGroup(resultValuesGroupInfo.getKey(), contextInfo);
658                         for (ResultValueInfo rVI : resultValueInfos) {
659                             creditOption.getAllowedCredits().add(rVI.getValue());
660                         }
661                         Collections.sort(creditOption.getAllowedCredits());
662 
663                         if (resultValuesGroupInfo.getTypeKey().equalsIgnoreCase(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_RANGE)) {
664                             creditOption.setCourseMinCredits(resultValuesGroupInfo.getResultValueRange().getMinValue());
665                             creditOption.setCourseMaxCredits(resultValuesGroupInfo.getResultValueRange().getMaxValue());
666 
667                             //Default the value
668                             creditOption.setTypeKey(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_RANGE);
669                             creditOption.setMinCredits(creditOption.getCourseMinCredits());
670                             creditOption.setMaxCredits(creditOption.getCourseMaxCredits());
671                         } else if (resultValuesGroupInfo.getTypeKey().equalsIgnoreCase(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_MULTIPLE)) {
672                             //Default the value
673                             creditOption.setTypeKey(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_MULTIPLE);
674                             creditOption.getCredits().addAll(creditOption.getAllowedCredits());
675                         }
676                     }
677                 }
678 
679                 //Lookup the selected credit option and set from persisted values
680                 if (creditOptionId != null) {
681                     //Lookup the resultValueGroup Information
682                     ResultValuesGroupInfo resultValuesGroupInfo = CourseOfferingManagementUtil.getLrcService().getResultValuesGroup(creditOptionId, contextInfo);
683                     String typeKey = resultValuesGroupInfo.getTypeKey();
684 
685                     //Get the actual values
686                     List<ResultValueInfo> resultValueInfos = CourseOfferingManagementUtil.getLrcService().getResultValuesByKeys(resultValuesGroupInfo.getResultValueKeys(), contextInfo);
687 
688                     if (typeKey.equals(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_FIXED)) {
689                         creditOption.setTypeKey(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_FIXED);
690                         if (!resultValueInfos.isEmpty()) {
691                             creditOption.setFixedCredit(resultValueInfos.get(firstValue).getValue());
692                         }
693                     } else if (typeKey.equals(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_RANGE)) {
694                         creditOption.setTypeKey(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_RANGE);
695                         creditOption.setMinCredits(resultValuesGroupInfo.getResultValueRange().getMinValue());
696                         creditOption.setMaxCredits(resultValuesGroupInfo.getResultValueRange().getMaxValue());
697                     } else if (typeKey.equals(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_MULTIPLE)) {
698                         creditOption.setTypeKey(LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_MULTIPLE);
699                         if (!resultValueInfos.isEmpty()) {
700                             List<String> credits = new ArrayList<String>();
701                             for (ResultValueInfo resultValueInfo : resultValueInfos) {
702                                 credits.add(resultValueInfo.getValue());
703                             }
704                             creditOption.setCredits(credits);
705                         }
706                     }
707                 }
708 
709                 formObject.setCreditOption(creditOption);
710                 formObject.setCreditOptionFixed(creditOptionFixed);
711 
712                 formObject.setOrganizationNames(new ArrayList<OrganizationInfoWrapper>());
713 
714                 ArrayList<OrganizationInfoWrapper> orgList = new ArrayList<OrganizationInfoWrapper>();
715 
716                 if (coInfo.getUnitsDeploymentOrgIds() != null) {
717                     for (String orgId : coInfo.getUnitsDeploymentOrgIds()) {
718                         OrgInfo orgInfo = CourseOfferingManagementUtil.getOrganizationService().getOrg(orgId, contextInfo);
719                         orgList.add(new OrganizationInfoWrapper(orgInfo));
720                     }
721                 }
722                 formObject.setOrganizationNames(orgList);
723 
724                 List<String> socIds = CourseOfferingManagementUtil.getCourseOfferingSetService().getSocIdsByTerm(coInfo.getTermId(), ContextUtils.createDefaultContextInfo());
725                 if (socIds != null && !socIds.isEmpty()) {
726                     List<SocInfo> targetSocs = CourseOfferingManagementUtil.getCourseOfferingSetService().getSocsByIds(socIds, ContextUtils.createDefaultContextInfo());
727                     for (SocInfo soc : targetSocs) {
728                         if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.MAIN_SOC_TYPE_KEY)) {
729                             formObject.setSocInfo(soc);
730                         }
731                     }
732                 }
733 
734                 setTermPropertiesOnFormObject(formObject, coInfo, contextInfo);
735                 formObject.setContextBar(CourseOfferingContextBar.NEW_INSTANCE(formObject.getTerm(), formObject.getSocInfo(),
736                         CourseOfferingManagementUtil.getStateService(), CourseOfferingManagementUtil.getAcademicCalendarService(), contextInfo));
737 
738                 document.getNewMaintainableObject().setDataObject(formObject);
739                 document.getOldMaintainableObject().setDataObject(formObject);
740                 document.getDocumentHeader().setDocumentDescription("Edit CO - " + coInfo.getCourseOfferingCode());
741 
742                 Person user = GlobalVariables.getUserSession().getPerson();
743 
744                 boolean canOpenView = this.getDocumentDictionaryService().getDocumentAuthorizer(document).canOpen(document, user);
745 
746                 // Work around, should be fixed with KULRICE-8049
747                 if (!canOpenView) {
748                     throw new AuthorizationException(user.getPrincipalName(), "open", null,
749                             "User '" + user.getPrincipalName() + "' is not authorized to open view", null);
750                 }
751 
752 
753                 //Cross listing
754                 for (CourseOfferingCrossListingInfo crossListingInfo : coInfo.getCrossListings()) {
755                     formObject.getAlternateCOCodes().add(crossListingInfo.getCode());
756                     formObject.getAlternateCourseCodesSuffixStripped().add(crossListingInfo.getCode());
757                 }
758 
759                 loadNavigationDetails(formObject);
760 
761                 //check if the final exam status is the same as the one of CM
762                 String finalExamTypeCM = "";
763                 if (!courseInfo.getAttributes().isEmpty()) {
764                     for (AttributeInfo info : courseInfo.getAttributes()) {
765                         if (info.getKey().equals(CourseOfferingConstants.COURSEOFFERING_FINAL_EXAM_TYPE_KEY)) {
766                             finalExamTypeCM = info.getValue();
767                         }
768                     }
769                 }
770                 if (!finalExamTypeCM.isEmpty() && !StringUtils.equals(convertCourseFinalExamTypeToCourseOfferingFinalExamType(finalExamTypeCM), coInfo.getFinalExamType())) {
771                     GlobalVariables.getMessageMap().putWarningForSectionId("delivery_and_assessment", CourseOfferingConstants.COURSEOFFERING_MSG_WARNING_FINALEXAMTYPE_DIFF_CM);
772                 }
773 
774                 // set the display text for use final exam matrix in read-only view
775                 if (formObject.isUseFinalExamMatrix()) {
776                     formObject.setUseFinalExamMatrixUI(CourseOfferingConstants.COURSEOFFERING_TEXT_USE_FINAL_EXAM_MATRIX);
777                 } else {
778                     formObject.setUseFinalExamMatrixUI(CourseOfferingConstants.COURSEOFFERING_TEXT_NOT_USE_FINAL_EXAM_MATRIX);
779                 }
780 
781                 // retrieve the exam period id
782                 try {
783                     String examPeriodId = CourseOfferingManagementUtil.getExamOfferingServiceFacade().getExamPeriodId(coInfo.getTermId(), contextInfo);
784                     if (!StringUtils.isEmpty(examPeriodId)) {
785                         formObject.setExamPeriodId(examPeriodId);
786                     }
787                 } catch (DoesNotExistException e) {
788                     LOG.warn("The Term " + formObject.getTermName() + " that the course offering " + formObject.getCourseOfferingCode() + " is attached to doesn't have an exam period to create exam offerings.");
789                 }
790 
791                 return formObject;
792             }
793         } catch (AuthorizationException ae) {
794             throw new AuthorizationException(ae.getUserId(), "open", null,
795                     "User '" + ae.getUserId() + "' is not authorized to open view", null);
796         } catch (Exception e) {
797             throw new RuntimeException(e);
798         }
799         return null;
800     }
801 
802     protected static String convertCourseFinalExamTypeToCourseOfferingFinalExamType(String courseFinalExamType) {
803         String sRet;
804         if ("STD".equals(courseFinalExamType)) {
805             sRet = CourseOfferingConstants.COURSEOFFERING_FINAL_EXAM_TYPE_STANDARD;
806         } else if ("ALT".equals(courseFinalExamType)) {
807             sRet = CourseOfferingConstants.COURSEOFFERING_FINAL_EXAM_TYPE_ALTERNATE;
808         } else {
809             sRet = CourseOfferingConstants.COURSEOFFERING_FINAL_EXAM_TYPE_NONE;
810         }
811         return sRet;
812     }
813 
814     private void loadNavigationDetails(CourseOfferingEditWrapper wrapper) throws InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
815         List<CourseOfferingInfo> relatedCOs = searchForRelatedCOs(wrapper);
816         int indexOfCurrentCo = buildListOfRelatedCOsAndReturnIndexOfCurrentCO(wrapper, relatedCOs);
817         wrapper.getRenderHelper().setSelectedCoCode(wrapper.getId());
818         setPreviousToCurrentCO(wrapper, indexOfCurrentCo, relatedCOs);
819         setNextToCurrentCO(wrapper, indexOfCurrentCo, relatedCOs);
820     }
821 
822     private List<CourseOfferingInfo> searchForRelatedCOs(CourseOfferingEditWrapper wrapper) throws InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
823         List<CourseOfferingInfo> result = new ArrayList<CourseOfferingInfo>();
824 
825         SearchRequestInfo searchRequest = new SearchRequestInfo(CourseOfferingManagementSearchImpl.CO_MANAGEMENT_SEARCH.getKey());
826         searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.SUBJECT_AREA, wrapper.getCourse().getSubjectArea());
827         searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.ATP_ID, wrapper.getTerm().getId());
828         List<CourseOfferingInfo> relatedCOs = CourseOfferingViewHelperUtil.loadCourseOfferings(searchRequest);
829         if (relatedCOs != null) {
830             result.addAll(relatedCOs);
831         }
832 
833         return result;
834     }
835 
836     private int buildListOfRelatedCOsAndReturnIndexOfCurrentCO(CourseOfferingEditWrapper currentCO, List<CourseOfferingInfo> relatedCOs) {
837 
838         int indexOfCurrentCo = -1;
839         for (CourseOfferingInfo coInfo : relatedCOs) {
840 
841             // store index of current item if it's the one the user is looking at
842             if (currentCO.getId().equals(coInfo.getId())) {
843                 indexOfCurrentCo = relatedCOs.indexOf(coInfo);
844             }
845 
846             // store current item in list of related-COs
847             ConcreteKeyValue keyValue = new ConcreteKeyValue();
848             keyValue.setKey(coInfo.getId());
849             keyValue.setValue(coInfo.getCourseOfferingCode());
850             currentCO.getRenderHelper().getRelatedCOs().add(keyValue);
851         }
852 
853         return indexOfCurrentCo;
854     }
855 
856     private void setPreviousToCurrentCO(CourseOfferingEditWrapper currentCO, int indexOfCurrentCO, List<CourseOfferingInfo> relatedCOs) {
857         CourseOfferingInfo previousCoInfo = new CourseOfferingInfo();
858         if (indexOfCurrentCO > 0) {
859             previousCoInfo = relatedCOs.get(indexOfCurrentCO - 1);
860         }
861         currentCO.getRenderHelper().setPrevCO(previousCoInfo);
862     }
863 
864     private void setNextToCurrentCO(CourseOfferingEditWrapper currentCO, int indexOfCurrentCO, List<CourseOfferingInfo> relatedCOs) {
865         CourseOfferingInfo nextCoInfo = new CourseOfferingInfo();
866         if (indexOfCurrentCO < relatedCOs.size() - 1) {
867             nextCoInfo = relatedCOs.get(indexOfCurrentCO + 1);
868         }
869         currentCO.getRenderHelper().setNextCO(nextCoInfo);
870     }
871 
872     private void setTermPropertiesOnFormObject(CourseOfferingEditWrapper formObject, CourseOfferingInfo coInfo, ContextInfo contextInfo) throws Exception {
873 
874         TermInfo termInfo = CourseOfferingManagementUtil.getAcademicCalendarService().getTerm(coInfo.getTermId(), contextInfo);
875         formObject.setTerm(termInfo);
876         formObject.setTermName(termInfo.getName());
877 
878         // Setting term string: Fall 2012 (09/28/2012 to 12/15/2012)
879         String termStartDate = new String(DateFormatters.MONTH_DAY_YEAR_DATE_FORMATTER.format(termInfo.getStartDate()));
880         String termEndDate = new String(DateFormatters.MONTH_DAY_YEAR_DATE_FORMATTER.format(termInfo.getEndDate()));
881         StringBuilder termStartEnd = new StringBuilder();
882         termStartEnd.append(termInfo.getName());
883         termStartEnd.append(" (");
884         termStartEnd.append(termStartDate);
885         termStartEnd.append(" to ");
886         termStartEnd.append(termEndDate);
887         termStartEnd.append(")");
888         formObject.setTermStartEnd(termStartEnd.toString());
889     }
890 
891     private FormatInfo getFormatInfo(CourseOfferingEditWrapper courseOfferingEditWrapper, String coFormId) {
892         List<FormatInfo> formatInfoList = courseOfferingEditWrapper.getCourse().getFormats();
893         for (FormatInfo formatInfo : formatInfoList) {
894             if (coFormId.equals(formatInfo.getId())) {
895                 return formatInfo;
896             }
897         }
898         return null;
899     }
900 
901     private void setFinalExamDriverAttr(CourseOfferingInfo coInfo, CourseOfferingEditWrapper coEditWrapper) {
902         AttributeInfo attributeInfo = this.getAttributeForKey(coInfo, CourseOfferingServiceConstants.FINAL_EXAM_DRIVER_ATTR);
903         if (coInfo.getFinalExamType().equals(FinalExam.STANDARD.toString())) {  //driver is only for 'STANDARD'
904             if (attributeInfo != null) {
905                 attributeInfo.setValue(coEditWrapper.getFinalExamDriver());
906             } else {
907                 attributeInfo = createAttribute(CourseOfferingServiceConstants.FINAL_EXAM_DRIVER_ATTR, coEditWrapper.getFinalExamDriver());
908                 coInfo.getAttributes().add(attributeInfo);
909             }
910         } else {
911             if (attributeInfo != null) {
912                 attributeInfo.setValue("na");
913             }
914         }
915     }
916 
917     private void setUseFinalExamMatrix(CourseOfferingInfo coInfo, CourseOfferingEditWrapper coEditWrapper) {
918         AttributeInfo attributeInfo = this.getAttributeForKey(coInfo, CourseOfferingServiceConstants.FINAL_EXAM_USE_MATRIX);
919         if (coInfo.getFinalExamType().equals(FinalExam.STANDARD.toString())) {  //driver is only for 'STANDARD'
920             if (attributeInfo != null) {
921                 attributeInfo.setValue(String.valueOf(coEditWrapper.isUseFinalExamMatrix()));
922             } else {
923                 attributeInfo = createAttribute(CourseOfferingServiceConstants.FINAL_EXAM_USE_MATRIX, String.valueOf(coEditWrapper.isUseFinalExamMatrix()));
924                 coInfo.getAttributes().add(attributeInfo);
925             }
926         } else {
927             if (attributeInfo != null) {
928                 attributeInfo.setValue("false");
929             }
930         }
931     }
932 
933     private AttributeInfo getAttributeForKey(CourseOfferingInfo coInfo, String key) {
934         for (AttributeInfo info : coInfo.getAttributes()) {
935             if (info.getKey().equals(key)) {
936                 return info;
937             }
938         }
939         return null;
940     }
941 
942     private AttributeInfo createAttribute(String key, String value) {
943         AttributeInfo newAttr = new AttributeInfo();
944         newAttr.setKey(key);
945         newAttr.setValue(value);
946         return newAttr;
947     }
948 }