001/** 002 * Copyright 2013 The Kuali Foundation Licensed under the 003 * Educational Community License, Version 2.0 (the "License"); you may 004 * not use this file except in compliance with the License. You may 005 * obtain a copy of the License at 006 * 007 * http://www.osedu.org/licenses/ECL-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, 010 * software distributed under the License is distributed on an "AS IS" 011 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 012 * or implied. See the License for the specific language governing 013 * permissions and limitations under the License. 014 * 015 */ 016package org.kuali.student.enrollment.class2.autogen.service.impl; 017 018import org.apache.commons.lang.BooleanUtils; 019import org.apache.commons.lang.StringUtils; 020import org.kuali.rice.core.api.criteria.PredicateFactory; 021import org.kuali.rice.core.api.criteria.QueryByCriteria; 022import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 023import org.kuali.rice.kim.api.KimConstants; 024import org.kuali.rice.kim.api.identity.IdentityService; 025import org.kuali.rice.kim.api.identity.entity.EntityDefault; 026import org.kuali.rice.kim.api.identity.entity.EntityDefaultQueryResults; 027import org.kuali.rice.kim.api.identity.principal.Principal; 028import org.kuali.rice.kim.api.permission.PermissionService; 029import org.kuali.rice.kim.api.services.KimApiServiceLocator; 030import org.kuali.rice.krad.util.GlobalVariables; 031import org.kuali.rice.krad.util.KRADConstants; 032import org.kuali.student.common.uif.util.GrowlIcon; 033import org.kuali.student.common.uif.util.KSUifUtils; 034import org.kuali.student.enrollment.class2.autogen.controller.ARGUtil; 035import org.kuali.student.enrollment.class2.autogen.dto.ScheduleCalcContainer; 036import org.kuali.student.enrollment.class2.autogen.dto.ScheduleRequestCalcContainer; 037import org.kuali.student.enrollment.class2.autogen.form.ARGCourseOfferingManagementForm; 038import org.kuali.student.enrollment.class2.autogen.service.ARGCourseOfferingManagementViewHelperService; 039import org.kuali.student.enrollment.class2.autogen.util.ARGToolbarUtil; 040import org.kuali.student.enrollment.class2.courseoffering.dto.ActivityOfferingClusterWrapper; 041import org.kuali.student.enrollment.class2.courseoffering.dto.ActivityOfferingWrapper; 042import org.kuali.student.enrollment.class2.courseoffering.dto.CourseOfferingContextBar; 043import org.kuali.student.enrollment.class2.courseoffering.dto.CourseOfferingListSectionWrapper; 044import org.kuali.student.enrollment.class2.courseoffering.dto.CourseOfferingWrapper; 045import org.kuali.student.enrollment.class2.courseoffering.dto.RegistrationGroupWrapper; 046import org.kuali.student.enrollment.class2.courseoffering.service.impl.CO_AO_RG_ViewHelperServiceImpl; 047import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingConstants; 048import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingResourceLoader; 049import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingViewHelperUtil; 050import org.kuali.student.enrollment.class2.courseoffering.util.ManageSocConstants; 051import org.kuali.student.enrollment.class2.courseoffering.util.RegistrationGroupConstants; 052import org.kuali.student.enrollment.class2.scheduleofclasses.dto.ActivityOfferingDisplayWrapper; 053import org.kuali.student.enrollment.class2.scheduleofclasses.util.ScheduleOfClassesConstants; 054import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingClusterInfo; 055import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingDisplayInfo; 056import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingInfo; 057import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingSetInfo; 058import org.kuali.student.enrollment.courseoffering.dto.CourseOfferingInfo; 059import org.kuali.student.enrollment.courseoffering.dto.FormatOfferingInfo; 060import org.kuali.student.enrollment.courseoffering.dto.RegistrationGroupInfo; 061import org.kuali.student.enrollment.courseoffering.service.CourseOfferingService; 062import org.kuali.student.enrollment.courseofferingset.dto.SocInfo; 063import org.kuali.student.enrollment.courseofferingset.service.CourseOfferingSetService; 064import org.kuali.student.enrollment.lpr.dto.LprInfo; 065import org.kuali.student.enrollment.lpr.service.LprService; 066import org.kuali.student.r2.common.constants.CommonServiceConstants; 067import org.kuali.student.r2.common.dto.ContextInfo; 068import org.kuali.student.r2.common.dto.RichTextInfo; 069import org.kuali.student.r2.common.dto.StatusInfo; 070import org.kuali.student.r2.common.dto.TimeOfDayInfo; 071import org.kuali.student.r2.common.dto.ValidationResultInfo; 072import org.kuali.student.r2.common.exceptions.DoesNotExistException; 073import org.kuali.student.r2.common.exceptions.InvalidParameterException; 074import org.kuali.student.r2.common.exceptions.MissingParameterException; 075import org.kuali.student.r2.common.exceptions.OperationFailedException; 076import org.kuali.student.r2.common.exceptions.PermissionDeniedException; 077import org.kuali.student.r2.common.infc.ValidationResult; 078import org.kuali.student.r2.common.permutation.PermutationUtils; 079import org.kuali.student.r2.common.util.ContextUtils; 080import org.kuali.student.r2.common.util.constants.CourseOfferingServiceConstants; 081import org.kuali.student.r2.common.util.constants.CourseOfferingSetServiceConstants; 082import org.kuali.student.r2.common.util.constants.LprServiceConstants; 083import org.kuali.student.r2.common.util.constants.LuiServiceConstants; 084import org.kuali.student.r2.common.util.date.DateFormatters; 085import org.kuali.student.r2.core.acal.dto.KeyDateInfo; 086import org.kuali.student.r2.core.acal.dto.TermInfo; 087import org.kuali.student.r2.core.acal.service.AcademicCalendarService; 088import org.kuali.student.r2.core.atp.dto.AtpInfo; 089import org.kuali.student.r2.core.atp.service.AtpService; 090import org.kuali.student.r2.core.class1.search.ActivityOfferingSearchServiceImpl; 091import org.kuali.student.r2.core.class1.search.CoreSearchServiceImpl; 092import org.kuali.student.r2.core.class1.search.CourseOfferingManagementSearchImpl; 093import org.kuali.student.r2.core.class1.state.dto.StateInfo; 094import org.kuali.student.r2.core.class1.type.dto.TypeInfo; 095import org.kuali.student.r2.core.class1.type.dto.TypeTypeRelationInfo; 096import org.kuali.student.r2.core.constants.AcademicCalendarServiceConstants; 097import org.kuali.student.r2.core.constants.AtpServiceConstants; 098import org.kuali.student.r2.core.constants.TypeServiceConstants; 099import org.kuali.student.r2.core.room.dto.BuildingInfo; 100import org.kuali.student.r2.core.room.dto.RoomInfo; 101import org.kuali.student.r2.core.scheduling.constants.SchedulingServiceConstants; 102import org.kuali.student.r2.core.scheduling.dto.ScheduleRequestComponentInfo; 103import org.kuali.student.r2.core.scheduling.dto.ScheduleRequestInfo; 104import org.kuali.student.r2.core.scheduling.dto.TimeSlotInfo; 105import org.kuali.student.r2.core.scheduling.infc.ScheduleComponentDisplay; 106import org.kuali.student.r2.core.scheduling.util.SchedulingServiceUtil; 107import org.kuali.student.r2.core.search.dto.SearchRequestInfo; 108import org.kuali.student.r2.core.search.dto.SearchResultCellInfo; 109import org.kuali.student.r2.core.search.dto.SearchResultInfo; 110import org.kuali.student.r2.core.search.dto.SearchResultRowInfo; 111import org.kuali.student.r2.core.search.service.SearchService; 112import org.kuali.student.r2.lum.course.dto.ActivityInfo; 113import org.kuali.student.r2.lum.course.dto.CourseInfo; 114import org.kuali.student.r2.lum.course.dto.CourseJointInfo; 115import org.kuali.student.r2.lum.course.dto.FormatInfo; 116import org.kuali.student.r2.lum.course.service.CourseService; 117import org.kuali.student.r2.lum.lrc.service.LRCService; 118 119import javax.xml.namespace.QName; 120import java.util.ArrayList; 121import java.util.Arrays; 122import java.util.Calendar; 123import java.util.Collection; 124import java.util.Collections; 125import java.util.Comparator; 126import java.util.Date; 127import java.util.Formatter; 128import java.util.HashMap; 129import java.util.HashSet; 130import java.util.List; 131import java.util.Locale; 132import java.util.Map; 133import java.util.Set; 134 135import static java.util.Arrays.asList; 136 137/** 138 * This class implements Course offering management functionality using auto-generated registration groups 139 * 140 * @author Kuali Student Team 141 */ 142public class ARGCourseOfferingManagementViewHelperServiceImpl extends CO_AO_RG_ViewHelperServiceImpl implements ARGCourseOfferingManagementViewHelperService { 143 144 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ARGCourseOfferingManagementViewHelperServiceImpl.class); 145 146 private AcademicCalendarService acalService = null; 147 private CourseOfferingService coService = null; 148 private SearchService searchService = null; 149 150 private CourseService courseService; 151 private LRCService lrcService; 152 private AtpService atpService; 153 private CourseOfferingSetService socService; 154 155 private static LprService lprService; 156 private static PermissionService permissionService; 157 private static IdentityService identityService; 158 159 /** 160 * This method fetches the <code>TermInfo</code> and validate for exact match 161 * 162 * @param form input ARGCourseOfferingManagementForm 163 * @throws Exception 164 */ 165 public void populateTerm(ARGCourseOfferingManagementForm form) throws Exception { 166 167 String termCode = form.getTermCode(); 168 169 form.getCourseOfferingResultList().clear(); 170 171 QueryByCriteria.Builder qbcBuilder = QueryByCriteria.Builder.create(); 172 qbcBuilder.setPredicates(PredicateFactory.equal("atpCode", termCode)); 173 174 QueryByCriteria criteria = qbcBuilder.build(); 175 176 List<TermInfo> terms = getAcalService().searchForTerms(criteria, createContextInfo()); 177 178 if (terms.isEmpty()) { 179 GlobalVariables.getMessageMap().putError("termCode", CourseOfferingConstants.COURSEOFFERING_MSG_ERROR_NO_TERM_IS_FOUND, termCode); 180 } else if (terms.size() > 1) { 181 GlobalVariables.getMessageMap().putError("termCode", CourseOfferingConstants.COURSEOFFERING_MSG_ERROR_FOUND_MORE_THAN_ONE_TERM, termCode); 182 } else { 183 form.setTermInfo(terms.get(0)); 184 185 //Checking soc 186 List<String> socIds; 187 try { 188 socIds = getSocService().getSocIdsByTerm(form.getTermInfo().getId(), createContextInfo()); 189 } catch (Exception e) { 190 throw convertServiceExceptionsToUI(e); 191 } 192 193 if (socIds.isEmpty()) { 194 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, ManageSocConstants.MessageKeys.ERROR_SOC_NOT_EXISTS); 195 } else { 196 setSocStateKeys(form, socIds); 197 198 blockUserIfSocStateIs( form, CourseOfferingSetServiceConstants.SOC_SCHEDULING_STATE_IN_PROGRESS, ManageSocConstants.MessageKeys.ERROR_CANNOT_ACCESS_COURSE_OFFERING_WHILE_SOC_IS_IN_PROGRESS ); 199 blockUserIfSocStateIs( form, CourseOfferingSetServiceConstants.PUBLISHING_SOC_STATE_KEY, ManageSocConstants.MessageKeys.ERROR_CANNOT_ACCESS_COURSE_OFFERING_WHILE_SOC_IS_PUBLISHING); 200 201 // setting term first day of classes 202 List<KeyDateInfo> keyDateInfoList = getAcalService().getKeyDatesForTerm(form.getTermInfo().getId(), createContextInfo()); 203 Date termClassStartDate = null; 204 for (KeyDateInfo keyDateInfo : keyDateInfoList) { 205 if (keyDateInfo.getTypeKey().equalsIgnoreCase(AtpServiceConstants.MILESTONE_SEATPOOL_FIRST_DAY_OF_CLASSES_TYPE_KEY) && keyDateInfo.getStartDate() != null) { 206 termClassStartDate = keyDateInfo.getStartDate(); 207 break; 208 } 209 } 210 for (KeyDateInfo keyDateInfo : keyDateInfoList) { 211 212 if (keyDateInfo.getTypeKey().equalsIgnoreCase(AtpServiceConstants.MILESTONE_INSTRUCTIONAL_PERIOD_TYPE_KEY) && keyDateInfo.getStartDate() != null && keyDateInfo.getEndDate() != null) { 213 termClassStartDate = keyDateInfo.getStartDate(); 214 break; 215 } 216 } 217 form.setTermClassStartDate(termClassStartDate); 218 } 219 } 220 221 } 222 223 private void blockUserIfSocStateIs( ARGCourseOfferingManagementForm form, String socStateKeyToBlockUserOn, String errorMessageKey ) { 224 errorMessageKey = StringUtils.defaultIfEmpty( errorMessageKey, ManageSocConstants.MessageKeys.ERROR_CANNOT_ACCESS_COURSE_OFFERING_WHILE_SOC_INVALID_STATE_DEFAULT); 225 226 if( StringUtils.equalsIgnoreCase( form.getSocStateKey(), socStateKeyToBlockUserOn ) ) { 227 GlobalVariables.getMessageMap().putError( KRADConstants.GLOBAL_ERRORS, errorMessageKey ); 228 } 229 } 230 231 /** 232 * This method loads all the course offerings for a term and course code. 233 * 234 * @param termId Term Id 235 * @param courseCode Course Code 236 * @param form Input Form 237 * @throws Exception 238 */ 239 public void loadCourseOfferingsByTermAndCourseCode(String termId, String courseCode, ARGCourseOfferingManagementForm form) throws Exception { 240 241 SearchRequestInfo searchRequest = new SearchRequestInfo(CourseOfferingManagementSearchImpl.CO_MANAGEMENT_SEARCH.getKey()); 242 searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.COURSE_CODE, courseCode); 243 searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.ATP_ID, termId); 244 searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.CROSS_LIST_SEARCH_ENABLED, BooleanUtils.toStringTrueFalse(true)); 245 246 // Check to see if the "exact co code match" param has been set. If so, add a query param. 247 boolean isExactMatchSearch = BooleanUtils.toBoolean(form.getViewRequestParameters().get(CourseOfferingManagementSearchImpl.SearchParameters.IS_EXACT_MATCH_CO_CODE_SEARCH)); 248 if (isExactMatchSearch) { 249 searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.IS_EXACT_MATCH_CO_CODE_SEARCH, BooleanUtils.toStringTrueFalse(true)); 250 } 251 252 loadCourseOfferings(searchRequest, form); 253 254 if (form.getCourseOfferingResultList().isEmpty()) { 255 LOG.error("Error: Can't find any Course Offering for a Course Code: " + courseCode + " in term: " + termId); 256 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, CourseOfferingConstants.COURSEOFFERING_MSG_ERROR_NO_COURSE_OFFERING_IS_FOUND, "Course Code", courseCode, termId); 257 } 258 259 form.setContextBar( CourseOfferingContextBar.NEW_INSTANCE(form.getTermInfo(), form.getSocStateKey(), 260 getStateService(), getAcalService(), createContextInfo()) ); 261 } 262 263 /** 264 * This method loads all the course offerings for a term and subject area/code. 265 * 266 * @param termId term id 267 * @param subjectCode subject area 268 * @param form course offering management form 269 * @throws Exception 270 */ 271 public void loadCourseOfferingsByTermAndSubjectCode(String termId, String subjectCode, ARGCourseOfferingManagementForm form) throws Exception { 272 273 SearchRequestInfo searchRequest = new SearchRequestInfo(CourseOfferingManagementSearchImpl.CO_MANAGEMENT_SEARCH.getKey()); 274 searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.SUBJECT_AREA, subjectCode); 275 searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.ATP_ID, termId); 276 searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.CROSS_LIST_SEARCH_ENABLED, BooleanUtils.toStringTrueFalse(true)); 277 278 loadCourseOfferings(searchRequest, form); 279 280 if (form.getCourseOfferingResultList().isEmpty()) { 281 LOG.error("Error: Can't find any Course Offering for a Subject Code: " + subjectCode + " in term: " + termId); 282 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, CourseOfferingConstants.COURSEOFFERING_MSG_ERROR_NO_COURSE_OFFERING_IS_FOUND, "Subject Code", subjectCode, termId); 283 } 284 285 } 286 287 /** 288 * This method fetches, prepares and sets 289 * activityWrapperList for View All Activities tab, 290 * clusterResultList for View byClusters 291 * rgResultList for View Registration Groupsall the course offerings for a term and course/subject code. 292 * 293 * @param form input form 294 * @param theCourseOffering course offering 295 * @throws Exception 296 */ 297 public void build_AOs_RGs_AOCs_Lists_For_TheCourseOffering(ARGCourseOfferingManagementForm form, CourseOfferingWrapper theCourseOffering) throws Exception { 298 //KSENROLL-6102 performance improvements, delete this code when performance work is complete 299 300 //New Search Stuff! 301 String coId = form.getCurrentCourseOfferingWrapper().getCourseOfferingId(); 302 303 //First search for AOs and Cluster information 304 SearchRequestInfo sr = new SearchRequestInfo(ActivityOfferingSearchServiceImpl.AOS_AND_CLUSTERS_BY_CO_ID_SEARCH_KEY); 305 sr.addParam(ActivityOfferingSearchServiceImpl.SearchParameters.CO_ID, coId); 306 SearchResultInfo results = searchService.search(sr, null); 307 308 Map<String, List<ActivityOfferingWrapper>> sch2aoMap = new HashMap<String, List<ActivityOfferingWrapper>>(); 309 List<String> aoIdsWithoutSch = new ArrayList<String>(); 310 Map<String, ActivityOfferingClusterWrapper> clusterMap = new HashMap<String, ActivityOfferingClusterWrapper>(); 311 Map<String, ActivityOfferingWrapper> aoMap = new HashMap<String, ActivityOfferingWrapper>(); 312 Map<String, FormatOfferingInfo> foIds = new HashMap<String, FormatOfferingInfo>(); 313 Map<String, List<ScheduleCalcContainer>> ao2sch = new HashMap<String, List<ScheduleCalcContainer>>(); 314 Map<String, List<ScheduleRequestCalcContainer>> ao2schReq = new HashMap<String, List<ScheduleRequestCalcContainer>>(); 315 ContextInfo contextInfo = ContextUtils.createDefaultContextInfo(); 316 317 //Parse the search results 318 List<ActivityOfferingWrapper> wrappers = processAoClusterData(results, sch2aoMap, clusterMap, aoMap, foIds, aoIdsWithoutSch, contextInfo); 319 320 processAosData(coId, clusterMap); 321 322 //Sort Activity Wrappers and Clusters 323 ArrayList<ActivityOfferingClusterWrapper> clusterWrapperList = new ArrayList<ActivityOfferingClusterWrapper>(clusterMap.values()); 324 if(clusterWrapperList.size() > 1){ 325 Collections.sort(clusterWrapperList, new Comparator<ActivityOfferingClusterWrapper>() { 326 @Override 327 public int compare(ActivityOfferingClusterWrapper o1, ActivityOfferingClusterWrapper o2) { 328 int nameComparison = o1.getAoCluster().getPrivateName().compareToIgnoreCase(o2.getAoCluster().getPrivateName()); 329 int formatComparison = o1.getFormatNameForDisplay().compareToIgnoreCase(o2.getFormatNameForDisplay()); 330 331 if(formatComparison==0){ 332 return nameComparison; 333 } else { 334 return formatComparison; 335 } 336 } 337 }); 338 } 339 340 if(wrappers.size() >1){ 341 Collections.sort(wrappers, new Comparator<ActivityOfferingWrapper>() { 342 343 @Override 344 public int compare(ActivityOfferingWrapper o1, ActivityOfferingWrapper o2) { 345 int typeComparison = (o1.getTypeName().compareTo(o2.getTypeName())) * -1; 346 int nameComparison = o1.getActivityCode().compareTo(o2.getActivityCode()); 347 if(typeComparison==0){ 348 return nameComparison; 349 } else { 350 return typeComparison; 351 } 352 } 353 }); 354 } 355 356 //Set the items in the form 357 form.setActivityWrapperList(wrappers); 358 form.getClusterResultList().clear(); 359 form.getClusterResultList().addAll(clusterWrapperList); 360 361 //fix for KSENROLL-7886: foIds was populated in processAoClusterData method. 362 //However if one FO does not have a cluster and AO, that FO won't be added to foIds, which causes NPE error later. 363 foIds = new HashMap<String, FormatOfferingInfo>(); 364 List<FormatOfferingInfo> foList = getCourseOfferingService().getFormatOfferingsByCourseOffering(coId,ContextUtils.createDefaultContextInfo()); 365 for(FormatOfferingInfo fo:foList){ 366 foIds.put(fo.getId(), fo); 367 } 368 //Get the mapping of formatids to AO types 369 //by Bonnie: why do we need this method? It causes the dropdown list 370 //because of this method, in manage the CO page, when click Add Activity button from toolbar 371 //in the popover form, Activity Type dropdown list would display every AO type twice. I comment this method out for now 372// processRelatedTypeKeysForFos(coId, foIds, contextInfo); 373 374 form.setFoId2aoTypeMap(foIds); 375 376 if (!aoMap.keySet().isEmpty()) { 377 //Process Colocated 378 sr = new SearchRequestInfo(ActivityOfferingSearchServiceImpl.COLOCATED_AOS_BY_AO_IDS_SEARCH_KEY); 379 sr.addParam(ActivityOfferingSearchServiceImpl.SearchParameters.AO_IDS, new ArrayList<String>(aoMap.keySet())); 380 results = searchService.search(sr, null); 381 382 processColocated(results, aoMap); 383 384 385 //Addin LPR data 386 processInstructors(aoMap, ContextUtils.createDefaultContextInfo()); 387 388 //Search for schedule information 389 if (!sch2aoMap.isEmpty()){ 390 sr = new SearchRequestInfo(CoreSearchServiceImpl.SCH_AND_ROOM_SEARH_BY_ID_SEARCH_KEY); 391 sr.addParam(CoreSearchServiceImpl.SearchParameters.SCHEDULE_IDS, new ArrayList<String>(sch2aoMap.keySet())); 392 results = searchService.search(sr, null); 393 394 //processSchData(results, sch2aoMap, aoIdsWithoutSch, aoMap, ContextUtils.createDefaultContextInfo()); 395 396 // the next two methods pull scheduling data from the DB and put them into the ao2sch map 397 processScheduleInfo(results, sch2aoMap, ao2sch); 398 } 399 processScheduleRequestsForAos(aoIdsWithoutSch, ao2schReq, contextInfo); 400 401 // this takes the scheduling data and puts it into the screen form 402 processScheduleData(aoMap, ao2sch, ao2schReq); 403 404 //Search for registration group information 405 sr = new SearchRequestInfo(ActivityOfferingSearchServiceImpl.REG_GROUPS_BY_CO_ID_SEARCH_KEY); 406 sr.addParam(ActivityOfferingSearchServiceImpl.SearchParameters.CO_ID, form.getCurrentCourseOfferingWrapper().getCourseOfferingId()); 407 results = searchService.search(sr, null); 408 409 List<RegistrationGroupWrapper> rgWrappers = processRgData(results, clusterMap, aoMap); 410 411 //Sort rgWrappers 412 if(rgWrappers.size()>1){ 413 Collections.sort(rgWrappers, new Comparator<RegistrationGroupWrapper>() { 414 @Override 415 public int compare(RegistrationGroupWrapper o1, RegistrationGroupWrapper o2) { 416 return o1.getRgInfo().getName().compareTo(o2.getRgInfo().getName()); 417 } 418 }); 419 } 420 421 form.setRgResultList(rgWrappers); 422 423 form.setHasMoreThanOneCluster(clusterMap.size() > 1); 424 425 426 //Validate Reg Groups 427 Date startOfValidation = new Date(); 428 int i = 0; 429 for (ActivityOfferingClusterWrapper cluster : clusterWrapperList) { 430 List<RegistrationGroupInfo> rgInfos = new ArrayList<RegistrationGroupInfo>(); 431 for (RegistrationGroupWrapper rgWrapper : cluster.getRgWrapperList()) { 432 rgInfos.add(rgWrapper.getRgInfo()); 433 } 434 List<ActivityOfferingInfo> aoInfos = new ArrayList<ActivityOfferingInfo>(); 435 for (ActivityOfferingWrapper aoWrapper : cluster.getAoWrapperList()) { 436 aoInfos.add(aoWrapper.getAoInfo()); 437 } 438 _validateRegistrationGroupsPerCluster(rgInfos, aoInfos, cluster, form, i, ao2sch, ao2schReq, aoMap); 439 440 // Test the Cluster for Multiple AO types and Term types 441 _validateMulitpleTermsPerCluster(form.getFoId2aoTypeMap().get(cluster.getFormatOfferingId()).getActivityOfferingTypeKeys(),cluster, i); 442 443 i++; 444 } 445 Date endOfValidation = new Date(); 446 LOG.info("Time of RG Validation:" + (endOfValidation.getTime() - startOfValidation.getTime()) + "ms"); 447 } 448 // Normally we would use the KeyValue finder for this, but since we HAVE all the data, why waste sql calls 449 // replaces : ARGActivitiesForCreateAOKeyValues.java 450 //List<KeyValue> 451 452 453 } 454 455 private void _validateMulitpleTermsPerCluster(List<String> aoTypeKeys,ActivityOfferingClusterWrapper cluster, int clusterIndex ){ 456 // Test the Cluster for Multiple AO types and Term types 457 if(aoTypeKeys.size()>1){ 458 List<String> termIds = new ArrayList<String>(); 459 460 // Tests for multiple subTerms 461 for(ActivityOfferingWrapper aoWrapper : cluster.getAoWrapperList()){ 462 if(termIds.size()==0){ 463 termIds.add(aoWrapper.getSubTermId()); 464 continue; 465 } 466 boolean newTerm = false; 467 for(String id : termIds){ 468 if(id == null) continue; 469 if(aoWrapper.getSubTermId().compareTo(id)!=0){ 470 newTerm=true; 471 } 472 } 473 if(newTerm) termIds.add(aoWrapper.getSubTermId()); 474 } 475 if(termIds.size()>1){ 476 GlobalVariables.getMessageMap().putWarningForSectionId("activityOfferingsPerCluster_line" + clusterIndex, RegistrationGroupConstants.MSG_ERROR_CLUSTER_MULTIPLE_TERMS, cluster.getAoCluster().getPrivateName()); 477 } 478 479 // Test for multiple Terms 480 termIds = new ArrayList<String>(); 481 for(ActivityOfferingWrapper aoWrapper : cluster.getAoWrapperList()){ 482 if(termIds.size()==0){ 483 termIds.add(aoWrapper.getTermId()); 484 continue; 485 } 486 boolean newTerm = false; 487 for(String id : termIds){ 488 if(id == null) continue; 489 if(aoWrapper.getTermId().compareTo(id)!=0){ 490 newTerm=true; 491 } 492 } 493 if(newTerm) termIds.add(aoWrapper.getTermId()); 494 } 495 if(termIds.size()>1){ 496 GlobalVariables.getMessageMap().putWarningForSectionId("activityOfferingsPerCluster_line" + clusterIndex, RegistrationGroupConstants.MSG_ERROR_CLUSTER_MULTIPLE_TERMS, cluster.getAoCluster().getPrivateName()); 497 } 498 } 499 } 500 501 private void processColocated(SearchResultInfo searchResults, Map<String, ActivityOfferingWrapper> aoMap) { 502 503 for (SearchResultRowInfo row : searchResults.getRows()) { 504 String aoId = null; 505 String aoCode = null; 506 String coCode = null; 507 508 for (SearchResultCellInfo cell : row.getCells()) { 509 if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_ID.equals(cell.getKey())) { 510 aoId = cell.getValue(); 511 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_CODE.equals(cell.getKey())) { 512 aoCode = cell.getValue(); 513 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.CO_CODE.equals(cell.getKey())) { 514 coCode = cell.getValue(); 515 } 516 } 517 ActivityOfferingWrapper aoWrapper = aoMap.get(aoId); 518 aoWrapper.setColocatedAO(true); 519 if (aoWrapper.getColocatedAoInfo() == null || aoWrapper.getColocatedAoInfo().isEmpty()) { 520 aoWrapper.setColocatedAoInfo(coCode + " " + aoCode); 521 } else { 522 aoWrapper.setColocatedAoInfo(aoWrapper.getColocatedAoInfo() + "<br/>" + coCode + " " + aoCode); 523 } 524 525 } 526 } 527 528 private void processInstructors(Map<String, ActivityOfferingWrapper> aoMap, ContextInfo contextInfo) throws InvalidParameterException, MissingParameterException, DoesNotExistException, PermissionDeniedException, OperationFailedException { 529 List<LprInfo> lprInfos = getLprService().getLprsByLuis(new ArrayList<String>(aoMap.keySet()), contextInfo); 530 if (lprInfos != null) { 531 Map<String, Set<String>> principalId2aoIdMap = new HashMap<String, Set<String>>(); 532 for (LprInfo lprInfo : lprInfos) { 533 // Only include the main instructor. 534 if ( ! StringUtils.equals(lprInfo.getTypeKey(), LprServiceConstants.INSTRUCTOR_MAIN_TYPE_KEY)) { 535 continue; 536 } 537 Set<String> aoIds = principalId2aoIdMap.get(lprInfo.getPersonId()); 538 if (aoIds == null) { 539 aoIds = new HashSet<String>(); 540 principalId2aoIdMap.put(lprInfo.getPersonId(), aoIds); 541 } 542 aoIds.add(lprInfo.getLuiId()); 543 } 544 if (!principalId2aoIdMap.keySet().isEmpty()) { 545 EntityDefaultQueryResults results = getInstructorsInfoFromKim(new ArrayList<String>(principalId2aoIdMap.keySet())); 546 547 for (EntityDefault entity : results.getResults()) { 548 for (Principal principal : entity.getPrincipals()) { 549 Set<String> aoIds = principalId2aoIdMap.get(principal.getPrincipalId()); 550 if (aoIds != null) { 551 for (String aoId : aoIds) { 552 ActivityOfferingWrapper activityOfferingWrapper = aoMap.get(aoId); 553 if(entity.getName() != null) { 554 activityOfferingWrapper.setInstructorDisplayNames(entity.getName().getCompositeName(), true); 555 } else { 556 activityOfferingWrapper.setInstructorDisplayNames(principal.getPrincipalId()); 557 } 558 } 559 } 560 } 561 } 562 563 } 564 } 565 } 566 567 /** 568 * Add scheduling information to the map if there are no "actual" schedules already in place for a particular AO. 569 * 570 * 571 * @param aoIdsWithoutSch list of aoIds without schedule information 572 * @param ao2sch map of aos to schedules 573 *@param contextInfo @throws Exception 574 */ 575 protected void processScheduleRequestsForAos(Collection<String> aoIdsWithoutSch, Map<String, List<ScheduleRequestCalcContainer>> ao2sch, ContextInfo contextInfo) throws Exception { 576 577 if (!aoIdsWithoutSch.isEmpty()) { 578 Set<String> buildingIds = new HashSet<String>(); 579 Set<String> roomIds = new HashSet<String>(); 580 Set<String> timeslotIds = new HashSet<String>(); 581 582 Map<String, BuildingInfo> buildingIdMap = new HashMap<String, BuildingInfo>(); 583 Map<String, RoomInfo> roomIdMap = new HashMap<String, RoomInfo>(); 584 Map<String, TimeSlotInfo> timeslotIdMap = new HashMap<String, TimeSlotInfo>(); 585 586 Map<String, List<ScheduleRequestInfo>> ao2srMap = new HashMap<String, List<ScheduleRequestInfo>>(); 587 HashSet<String> aoIdsSet = new HashSet<String>(aoIdsWithoutSch); 588 for (String aoId : aoIdsSet) { 589 ao2srMap.put(aoId, new ArrayList<ScheduleRequestInfo>()); 590 List<ScheduleRequestInfo> srList = getSchedulingService().getScheduleRequestsByRefObjects(CourseOfferingServiceConstants.REF_OBJECT_URI_ACTIVITY_OFFERING, new ArrayList<String>(asList(aoId)), contextInfo); 591 ao2srMap.get(aoId).addAll(srList); 592 for (ScheduleRequestInfo sr : srList) { 593 for (ScheduleRequestComponentInfo schRequestCom : sr.getScheduleRequestComponents()) { 594 buildingIds.addAll(schRequestCom.getBuildingIds()); 595 roomIds.addAll(schRequestCom.getRoomIds()); 596 timeslotIds.addAll(schRequestCom.getTimeSlotIds()); 597 } 598 } 599 600 } 601 602 //Lookup the information using bulk service calls and store in maps 603 List<TimeSlotInfo> timeSlotInfos = getSchedulingService().getTimeSlotsByIds(new ArrayList<String>(timeslotIds), contextInfo); 604 for (TimeSlotInfo timeSlotInfo : timeSlotInfos) { 605 timeslotIdMap.put(timeSlotInfo.getId(), timeSlotInfo); 606 } 607 List<RoomInfo> roomInfos = getRoomService().getRoomsByIds(new ArrayList<String>(roomIds), contextInfo); 608 for (RoomInfo roomInfo : roomInfos) { 609 roomIdMap.put(roomInfo.getId(), roomInfo); 610 buildingIds.add(roomInfo.getBuildingId()); 611 } 612 List<BuildingInfo> buildingInfos = getRoomService().getBuildingsByIds(new ArrayList<String>(buildingIds), contextInfo); 613 for (BuildingInfo buildingInfo : buildingInfos) { 614 buildingIdMap.put(buildingInfo.getId(), buildingInfo); 615 } 616 617 for (String aoId : aoIdsSet) { 618 List<ScheduleRequestCalcContainer> srccList = new ArrayList<ScheduleRequestCalcContainer>(); 619 ao2sch.put(aoId, srccList); 620 621 for (ScheduleRequestInfo sr : ao2srMap.get(aoId)) { 622 for (ScheduleRequestComponentInfo src : sr.getScheduleRequestComponents()) { 623 624 List<RoomInfo> rooms = new ArrayList<RoomInfo>(); 625 List<BuildingInfo> buildings = new ArrayList<BuildingInfo>(); 626 List<TimeSlotInfo> timeSlots = new ArrayList<TimeSlotInfo>(); 627 628 for (String roomId : src.getRoomIds()) { 629 rooms.add(roomIdMap.get(roomId)); 630 buildings.add(buildingIdMap.get(roomIdMap.get(roomId).getBuildingId())); 631 } 632 for (String timeSlotId : src.getTimeSlotIds()) { 633 TimeSlotInfo timeSlotInfo = timeslotIdMap.get(timeSlotId); 634 timeSlots.add(timeSlotInfo); 635 636 } 637 ScheduleRequestCalcContainer srcc = new ScheduleRequestCalcContainer(aoId, sr.getId(), 638 CourseOfferingServiceConstants.REF_OBJECT_URI_ACTIVITY_OFFERING, 639 timeSlots, 640 rooms, 641 buildings, 642 src.getIsTBA()); 643 srccList.add(srcc); 644 } 645 } 646 } 647 } 648 } 649 650 private void processScheduleInfo(SearchResultInfo searchResults, Map<String, List<ActivityOfferingWrapper>> sch2aoMap, Map<String, List<ScheduleCalcContainer>> ao2sch) throws InvalidParameterException, MissingParameterException, PermissionDeniedException, OperationFailedException, DoesNotExistException { 651 //Process the search results 652 653 for (SearchResultRowInfo row : searchResults.getRows()) { 654 String schId = null; 655 String startTime = null; 656 String endTime = null; 657 Boolean tbaInd = null; 658 String roomCode = null; 659 String buildingName = null; 660 String weekdays = null; 661 for (SearchResultCellInfo cell : row.getCells()) { 662 if (CoreSearchServiceImpl.SearchResultColumns.SCH_ID.equals(cell.getKey())) { 663 schId = cell.getValue(); 664 } else if (CoreSearchServiceImpl.SearchResultColumns.START_TIME.equals(cell.getKey())) { 665 startTime = cell.getValue(); 666 } else if (CoreSearchServiceImpl.SearchResultColumns.END_TIME.equals(cell.getKey())) { 667 endTime = cell.getValue(); 668 } else if (CoreSearchServiceImpl.SearchResultColumns.TBA_IND.equals(cell.getKey())) { 669 tbaInd = Boolean.parseBoolean(cell.getValue()); 670 } else if (CoreSearchServiceImpl.SearchResultColumns.ROOM_CODE.equals(cell.getKey())) { 671 roomCode = cell.getValue(); 672 } else if (CoreSearchServiceImpl.SearchResultColumns.BLDG_NAME.equals(cell.getKey())) { 673 buildingName = cell.getValue(); 674 } else if (CoreSearchServiceImpl.SearchResultColumns.WEEKDAYS.equals(cell.getKey())) { 675 weekdays = cell.getValue(); 676 } 677 } 678 for(ActivityOfferingWrapper aoWrapper:sch2aoMap.get(schId)){ 679 680 ScheduleCalcContainer scheduleCalcContainer = new ScheduleCalcContainer(aoWrapper.getId(), schId, SchedulingServiceConstants.SCHEDULE_TYPE_SCHEDULE, startTime, endTime, weekdays, roomCode, buildingName, tbaInd); 681 if (ao2sch.containsKey(aoWrapper.getId())) { 682 ao2sch.get(aoWrapper.getId()).add(scheduleCalcContainer); 683 } else { 684 List<ScheduleCalcContainer> schList = new ArrayList<ScheduleCalcContainer>(); 685 schList.add(scheduleCalcContainer); 686 ao2sch.put(aoWrapper.getId(), schList); 687 } 688 } 689 } 690 } 691 692 private void processScheduleData(Map<String, ActivityOfferingWrapper> aoMap, Map<String, List<ScheduleCalcContainer>> ao2sch, Map<String, List<ScheduleRequestCalcContainer>> ao2schReq) throws Exception { 693 for (Map.Entry<String, ActivityOfferingWrapper> aoEntry : aoMap.entrySet()) { 694 ActivityOfferingWrapper aoWrapper = aoEntry.getValue(); 695 String aoId = aoEntry.getKey(); 696 697 if (ao2sch.containsKey(aoId)) { 698 List<ScheduleCalcContainer> schedList = ao2sch.get(aoId); 699 boolean newRow = false; 700 for (ScheduleCalcContainer sched : schedList) { 701// aoWrapper.setScheduleInfo(new ScheduleInfo()); 702 aoWrapper.setStartTimeDisplay(sched.getStart().isEmpty() ? sched.getStart() : DateFormatters.HOUR_MINUTE_AM_PM_TIME_FORMATTER.format(new Date(Long.parseLong(sched.getStart()))), newRow); 703 aoWrapper.setEndTimeDisplay(sched.getEnd().isEmpty() ? sched.getEnd() : DateFormatters.HOUR_MINUTE_AM_PM_TIME_FORMATTER.format(new Date(Long.parseLong(sched.getEnd()))), newRow); 704 aoWrapper.setBuildingName(sched.getBldgName(), newRow); 705 aoWrapper.setRoomName(sched.getRoomCode(), newRow); 706 aoWrapper.setDaysDisplayName(sched.getWeekdays(), newRow); 707 aoWrapper.setTbaDisplayName(sched.getTbaInd(), newRow); 708 newRow = true; 709 } 710 711 } else if (ao2schReq.containsKey((aoId))) { 712 List<ScheduleRequestCalcContainer> schedList = ao2schReq.get(aoId); 713 for (ScheduleRequestCalcContainer sched : schedList) { 714 boolean newLine = aoWrapper.getTbaDisplayName() != null && !aoWrapper.getTbaDisplayName().isEmpty(); 715 for (RoomInfo room : sched.getRooms()) { 716 aoWrapper.setRoomName(room.getRoomCode(), newLine, "uif-scheduled-dl"); 717 } 718 for (BuildingInfo bldg : sched.getBldgs()) { 719 aoWrapper.setBuildingName(bldg.getName(), newLine, "uif-scheduled-dl"); 720 } 721 for (TimeSlotInfo timeSlotInfo : sched.getTimeSlots()) { 722 if (timeSlotInfo.getStartTime() != null && timeSlotInfo.getStartTime().getMilliSeconds() != null) { 723 aoWrapper.setStartTimeDisplay(DateFormatters.HOUR_MINUTE_AM_PM_TIME_FORMATTER.format(new Date(timeSlotInfo.getStartTime().getMilliSeconds())), newLine, "uif-scheduled-dl"); 724 } 725 if (timeSlotInfo.getEndTime() != null && timeSlotInfo.getEndTime().getMilliSeconds() != null) { 726 aoWrapper.setEndTimeDisplay(DateFormatters.HOUR_MINUTE_AM_PM_TIME_FORMATTER.format(new Date(timeSlotInfo.getEndTime().getMilliSeconds())), newLine, "uif-scheduled-dl"); 727 } 728 if (timeSlotInfo.getWeekdays() != null && !timeSlotInfo.getWeekdays().isEmpty()) { 729 aoWrapper.setDaysDisplayName(SchedulingServiceUtil.weekdaysList2WeekdaysString(timeSlotInfo.getWeekdays()), newLine, "uif-scheduled-dl"); 730 } 731 } 732 aoWrapper.setTbaDisplayName(sched.getTbaInd(), true); 733 } 734 735 } 736 } 737 } 738 739 private List<RegistrationGroupWrapper> processRgData(SearchResultInfo searchResults, Map<String, ActivityOfferingClusterWrapper> clusterMap, Map<String, ActivityOfferingWrapper> aoMap) throws InvalidParameterException, MissingParameterException, DoesNotExistException, PermissionDeniedException, OperationFailedException { 740 Map<String, RegistrationGroupWrapper> rgMap = new HashMap<String, RegistrationGroupWrapper>(); 741 Map<String, List<ActivityOfferingWrapper>> storedAOs = new HashMap<String, List<ActivityOfferingWrapper>>(); 742 743 for (SearchResultRowInfo row : searchResults.getRows()) { 744 745 String aoId = null; 746 String rgId = null; 747 String rgName = null; 748 String rgState = null; 749 for (SearchResultCellInfo cell : row.getCells()) { 750 if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_ID.equals(cell.getKey())) { 751 aoId = cell.getValue(); 752 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.RG_ID.equals(cell.getKey())) { 753 rgId = cell.getValue(); 754 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.RG_NAME.equals(cell.getKey())) { 755 rgName = cell.getValue(); 756 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.RG_STATE.equals(cell.getKey())) { 757 rgState = cell.getValue(); 758 } 759 } 760 ActivityOfferingWrapper aoWrapper = aoMap.get(aoId); 761 RegistrationGroupWrapper rgWrapper = rgMap.get(rgId); 762 763 if (rgWrapper == null) { 764 765 storedAOs.put(rgId, new ArrayList<ActivityOfferingWrapper>()); 766 767 ActivityOfferingClusterWrapper clusterWrapper = clusterMap.get(aoWrapper.getAoClusterID()); 768 769 rgWrapper = new RegistrationGroupWrapper(); 770 rgWrapper.setAoCluster(clusterWrapper.getAoCluster()); 771 rgWrapper.setAoClusterName(clusterWrapper.getClusterNameForDisplay()); 772 rgWrapper.setStateKey(rgState, getStateService().getState(rgState, ContextUtils.createDefaultContextInfo()).getName()); 773 rgWrapper.getRgInfo().setName(rgName); 774 rgWrapper.getRgInfo().setId(rgId); 775 776 rgWrapper.setAoMaxEnrText(""); 777 rgWrapper.setAoStateNameText(""); 778 rgWrapper.setAoActivityCodeText(""); 779 rgWrapper.setAoTypeNameText(""); 780 rgWrapper.setStartTimeDisplay(""); 781 rgWrapper.setEndTimeDisplay(""); 782 rgWrapper.setDaysDisplayName(""); 783 rgWrapper.setRoomName(""); 784 rgWrapper.setBuildingName(""); 785 rgWrapper.setAoInstructorText(""); 786 787 rgMap.put(rgId, rgWrapper); 788 clusterWrapper.getRgWrapperList().add(rgWrapper); 789 } 790 storedAOs.get(rgId).add(aoWrapper); 791 } 792 793 List<String> keyList = new ArrayList<String>(storedAOs.keySet()); 794 for(int i=0;i<keyList.size();i++){ 795 796 RegistrationGroupWrapper rgWrapper = rgMap.get(keyList.get(i)); 797 List<ActivityOfferingWrapper> aoList = storedAOs.get(keyList.get(i)); 798 799 //Sort aoList 800 Collections.sort(aoList,new Comparator<ActivityOfferingWrapper>() { 801 @Override 802 public int compare(ActivityOfferingWrapper ao1, ActivityOfferingWrapper ao2) { 803 if(ao1.getTypeName().compareTo("Lecture")==0){ 804 return -1; 805 } 806 if(ao2.getTypeName().compareTo("Lecture")==0){ 807 return 1; 808 } 809 return ao1.getTypeName().compareTo(ao2.getTypeName()); 810 } 811 }); 812 813 for(int j=0;j<aoList.size();j++){ 814 boolean newLine = true; 815 if(j==0){ 816 newLine=false; 817 } 818 ActivityOfferingWrapper aoWrapper = aoList.get(j); 819 820 rgWrapper.getRgInfo().getActivityOfferingIds().add(aoWrapper.getAoInfo().getId()); 821 822 //if there are more than one instructors re-arrange the rows 823 StringBuilder lineBreaksInstructorsSB = new StringBuilder(); 824 if (aoWrapper.getInstructorDisplayNames() != null && StringUtils.contains(aoWrapper.getInstructorDisplayNames(),("<br>"))) { //more than one instructor 825 String s = aoWrapper.getInstructorDisplayNames(); 826 for( int k=0; k<s.length(); k++ ) { //add lines according to number of instructors 827 if( s.contains("<br>")) { 828 lineBreaksInstructorsSB.append("<br/>"); 829 s = s.substring(s.indexOf("<br>")+4); 830 } else { 831 break; 832 } 833 } 834 } 835 String lineBreaksInstructors = lineBreaksInstructorsSB.toString(); 836 837 //if there are more than one delivery logistics re-arrange the rows 838 StringBuilder lineBreaksDeliveriesSB = new StringBuilder(); 839 if (aoWrapper.getDaysDisplayName() != null && StringUtils.contains(aoWrapper.getDaysDisplayName(),"<br>")) { //more than one delivery logistics 840 String s = aoWrapper.getDaysDisplayName(); 841 for( int k=0; k<s.length(); k++ ) { //add lines according to number of delivery logistics 842 if( s.contains("<br>")) { 843 lineBreaksDeliveriesSB.append("<br/>"); 844 s = s.substring(s.indexOf("<br>")+4); 845 } else { 846 break; 847 } 848 } 849 } 850 String lineBreaksDeliveries = lineBreaksDeliveriesSB.toString(); 851 852 String lineBreaks; 853 //Set different line breaks according to number of instructors and number of delivery logistics (can it be simpler?) 854 if (lineBreaksInstructors.length() < lineBreaksDeliveries.length()) { 855 lineBreaks = lineBreaksDeliveries; 856 lineBreaksDeliveries = lineBreaksDeliveries.substring(0, lineBreaksDeliveries.length() - lineBreaksInstructors.length()); 857 lineBreaksInstructors = ""; 858 } else { 859 lineBreaks = lineBreaksInstructors; 860 lineBreaksInstructors = lineBreaksInstructors.substring(0, lineBreaksInstructors.length() - lineBreaksDeliveries.length()); 861 lineBreaksDeliveries = ""; 862 } 863 864 //Set the wrapper 865 rgWrapper.setAoMaxEnrText(rgWrapper.getAoMaxEnrText() + (newLine ? "<br/>" : "") + (aoWrapper.getAoInfo().getMaximumEnrollment() == null ? "" : aoWrapper.getAoInfo().getMaximumEnrollment()) + lineBreaks); 866 rgWrapper.setAoStateNameText(rgWrapper.getAoStateNameText() + (newLine ? "<br/>" : "") + aoWrapper.getStateName() + lineBreaks); 867 //sub-term icon and tooltip setup 868 if(!aoWrapper.getSubTermName().equals("None")){ //sub-term? > icon + name and dates 869 StringBuilder sb = new StringBuilder(); 870 sb.append(rgWrapper.getAoActivityCodeText()); 871 sb.append(newLine ? "<br/>" : ""); 872 sb.append(aoWrapper.getAoInfo().getActivityCode()); 873 sb.append(" <img src=\"../ks-enroll/images/subterm_icon.png\" title=\"This activity is in "); 874 sb.append(aoWrapper.getSubTermName()); 875 sb.append(" -\n"); 876 sb.append(aoWrapper.getTermStartEndDate()); 877 sb.append("\">"); 878 sb.append(lineBreaks); 879 rgWrapper.setAoActivityCodeText(sb.toString()); 880// rgWrapper.setAoActivityCodeText(rgWrapper.getAoActivityCodeText() + (newLine ? "<br/>" : "") + aoWrapper.getAoInfo().getActivityCode() 881// + " <img src=\"../ks-enroll/images/subterm_icon.png\" title=\"This activity is in "+aoWrapper.getSubTermName()+" -\n"+aoWrapper.getTermStartEndDate()+"\">" + lineBreaks); 882 } else { 883 rgWrapper.setAoActivityCodeText(rgWrapper.getAoActivityCodeText() + (newLine ? "<br/>" : "") + aoWrapper.getAoInfo().getActivityCode() + lineBreaks); 884 } 885 rgWrapper.setAoTypeNameText(rgWrapper.getAoTypeNameText() + (newLine ? "<br/>" : "") + aoWrapper.getTypeName() + lineBreaks); 886 rgWrapper.setStartTimeDisplay(rgWrapper.getStartTimeDisplay() + (newLine ? "<br/>" : "") + aoWrapper.getStartTimeDisplay() + lineBreaksInstructors); 887 rgWrapper.setEndTimeDisplay(rgWrapper.getEndTimeDisplay() + (newLine ? "<br/>" : "") + aoWrapper.getEndTimeDisplay() + lineBreaksInstructors); 888 rgWrapper.setDaysDisplayName(rgWrapper.getDaysDisplayName() + (newLine ? "<br/>" : "") + aoWrapper.getDaysDisplayName() + lineBreaksInstructors); 889 rgWrapper.setRoomName(rgWrapper.getRoomName() + (newLine ? "<br/>" : "") + aoWrapper.getRoomName() + lineBreaksInstructors); 890 rgWrapper.setBuildingName(rgWrapper.getBuildingName() + (newLine ? "<br/>" : "") + aoWrapper.getBuildingName() + lineBreaksInstructors); 891 rgWrapper.setAoInstructorText(rgWrapper.getAoInstructorText() + (newLine ? "<br/>" : "") + (aoWrapper.getInstructorDisplayNames() == null ? "" : aoWrapper.getInstructorDisplayNames()) + lineBreaksDeliveries); 892 893 } 894 } 895 896 897 898 return new ArrayList<RegistrationGroupWrapper>(rgMap.values()); 899 } 900 901 /** 902 * After the performance improvements, the clusterMap does not contain set information. So, lets add it here. 903 * This method will use the dto to grab a full AOC. 904 * 905 * @param coId Course Offering Id 906 * @param clusterMap map of AoIds to AO cluster wrappers 907 * @throws InvalidParameterException 908 * @throws MissingParameterException 909 * @throws DoesNotExistException 910 * @throws PermissionDeniedException 911 * @throws OperationFailedException 912 */ 913 protected void processAosData(String coId, Map<String, ActivityOfferingClusterWrapper> clusterMap) 914 throws InvalidParameterException, MissingParameterException, DoesNotExistException, PermissionDeniedException, OperationFailedException { 915 916 List<ActivityOfferingClusterInfo> aoClusters = ARGUtil.getArgServiceAdapter().getActivityOfferingClusterByCourseOffering(coId); 917 918 //A fix for KSENROLL-8097: clusterMap was populated in processAoClusterData method. 919 //However if one cluster does not have an AO, that cluster sometimes won't be added to clusterMap, which causes NPE error later. 920 //The walkaround solution here is to add the missing cluster to clusterMap 921 for (ActivityOfferingClusterInfo aoc : aoClusters) { 922 if (clusterMap.get(aoc.getId()) != null){ 923 clusterMap.get(aoc.getId()).setAoCluster(aoc); 924 } 925 else{ 926 ActivityOfferingClusterWrapper aoClusterWrapper = new ActivityOfferingClusterWrapper(); 927 aoClusterWrapper.setAoCluster(aoc); 928 aoClusterWrapper.setActivityOfferingClusterId(aoc.getId()); 929 aoClusterWrapper.setClusterNameForDisplay(aoc.getName()); 930 String formatName = getCourseOfferingService().getFormatOffering(aoc.getFormatOfferingId(),ContextUtils.createDefaultContextInfo()).getName(); 931 aoClusterWrapper.setFormatNameForDisplay(formatName); 932 aoClusterWrapper.setFormatOfferingId(aoc.getFormatOfferingId()); 933 clusterMap.put(aoc.getId(), aoClusterWrapper); 934 } 935 } 936 937 } 938 939 private List<ActivityOfferingWrapper> processAoClusterData(SearchResultInfo searchResults, 940 Map<String, List<ActivityOfferingWrapper>> sch2aoMap, 941 Map<String, ActivityOfferingClusterWrapper> clusterMap, 942 Map<String, ActivityOfferingWrapper> aoMap, 943 Map<String, FormatOfferingInfo> foIds, 944 List<String> aoIdsWithoutSch, 945 ContextInfo contextInfo) 946 throws InvalidParameterException, MissingParameterException, DoesNotExistException, PermissionDeniedException, OperationFailedException { 947 948 List<ActivityOfferingWrapper> activityOfferingWrappers = new ArrayList<ActivityOfferingWrapper>(); 949 ActivityOfferingWrapper aoWrapperStored = new ActivityOfferingWrapper(); //storage for sub-term only to compare 950 951 for (SearchResultRowInfo row : searchResults.getRows()) { 952 953 ActivityOfferingWrapper aoWrapper = new ActivityOfferingWrapper(); 954 String aocPrivateName = null; 955 String foId = null; 956 String formatId = null; 957 String foName = null; 958 List<String> scheduleIds = new ArrayList<String>(); 959 960 for (SearchResultCellInfo cell : row.getCells()) { 961 962 if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_ID.equals(cell.getKey())) { 963 aoWrapper.getAoInfo().setId(cell.getValue()); 964 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_CODE.equals(cell.getKey())) { 965 aoWrapper.setActivityCode(cell.getValue()); 966 aoWrapper.getAoInfo().setActivityCode(cell.getValue()); 967 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_MAX_SEATS.equals(cell.getKey())) { 968 aoWrapper.getAoInfo().setMaximumEnrollment(cell.getValue() == null ? null : Integer.parseInt(cell.getValue())); 969 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_STATE.equals(cell.getKey())) { 970 aoWrapper.getAoInfo().setStateKey(cell.getValue()); 971 if (cell.getValue() != null) { 972 StateInfo stateInfo = getStateService().getState(cell.getValue(), contextInfo); 973 aoWrapper.setStateName(stateInfo.getName()); 974 } 975 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_TYPE.equals(cell.getKey())) { 976 aoWrapper.getAoInfo().setTypeKey(cell.getValue()); 977 if (cell.getValue() != null) { 978 TypeInfo typeInfo = getTypeService().getType(cell.getValue(), contextInfo); 979 aoWrapper.setTypeKey(cell.getValue()); 980 aoWrapper.setTypeName(typeInfo.getName()); 981 } 982 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.SCHEDULE_ID.equals(cell.getKey())) { 983 984 if(cell.getValue()!=null){ 985 scheduleIds.add(cell.getValue()); 986 987 } 988 989 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.FO_ID.equals(cell.getKey())) { 990 aoWrapper.getAoInfo().setFormatOfferingId(cell.getValue()); 991 foId = cell.getValue(); 992 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.FO_NAME.equals(cell.getKey())) { 993 aoWrapper.setFormatOfferingName(cell.getValue()); 994 aoWrapper.getAoInfo().setFormatOfferingName(cell.getValue()); 995 foName = cell.getValue(); 996 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.FORMAT_ID.equals(cell.getKey())) { 997 formatId = cell.getValue(); 998 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AOC_ID.equals(cell.getKey())) { 999 aoWrapper.setAoClusterID(cell.getValue()); 1000 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AOC_NAME.equals(cell.getKey())) { 1001 aoWrapper.setAoClusterName(cell.getValue()); 1002 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AOC_PRIVATE_NAME.equals(cell.getKey())) { 1003 aocPrivateName = cell.getValue(); 1004 } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.ATP_ID.equals(cell.getKey())) { 1005 aoWrapper.getAoInfo().setTermId(cell.getValue()); 1006 } 1007 } 1008 1009 aoWrapper.getAoInfo().setScheduleIds(scheduleIds); 1010 for(String scheduleId : scheduleIds){ 1011 List<ActivityOfferingWrapper> list = sch2aoMap.get(scheduleId); 1012 if(list == null){ 1013 list = new ArrayList<ActivityOfferingWrapper>(); 1014 sch2aoMap.put(scheduleId, list); 1015 } 1016 list.add(aoWrapper);//Add to schedule map 1017 } 1018 if (aoWrapper.getAoClusterID() != null) { 1019 ActivityOfferingClusterWrapper aoClusterWrapper = clusterMap.get(aoWrapper.getAoClusterID()); 1020 if (aoClusterWrapper == null) { 1021 aoClusterWrapper = new ActivityOfferingClusterWrapper(); 1022 ActivityOfferingClusterInfo activityOfferingClusterInfo = new ActivityOfferingClusterInfo(); 1023 activityOfferingClusterInfo.setFormatOfferingId(aoWrapper.getAoInfo().getFormatOfferingId()); 1024 activityOfferingClusterInfo.setId(aoWrapper.getAoClusterID()); 1025 activityOfferingClusterInfo.setName(aoWrapper.getAoClusterName()); 1026 activityOfferingClusterInfo.setPrivateName(aocPrivateName); 1027 activityOfferingClusterInfo.setTypeKey(CourseOfferingServiceConstants.AOC_ROOT_TYPE_KEY); 1028 activityOfferingClusterInfo.setStateKey(CourseOfferingServiceConstants.AOC_ACTIVE_STATE_KEY); 1029 aoClusterWrapper.setAoCluster(activityOfferingClusterInfo); 1030 aoClusterWrapper.setActivityOfferingClusterId(aoWrapper.getAoClusterID()); 1031 aoClusterWrapper.setClusterNameForDisplay(aoWrapper.getAoClusterName()); 1032 aoClusterWrapper.setFormatNameForDisplay(aoWrapper.getAoInfo().getFormatOfferingName()); 1033 aoClusterWrapper.setFormatOfferingId(aoWrapper.getAoInfo().getFormatOfferingId()); 1034 clusterMap.put(aoWrapper.getAoClusterID(), aoClusterWrapper); 1035 } 1036 if (aoWrapper.getId() != null) { 1037 aoClusterWrapper.getAoWrapperList().add(aoWrapper); 1038 1039 aoMap.put(aoWrapper.getAoInfo().getId(), aoWrapper); 1040 1041 //Check if there is a schedule id, if not add it to the list to get RDLs 1042 if (aoWrapper.getAoInfo().getScheduleIds() == null || aoWrapper.getAoInfo().getScheduleIds().isEmpty()) { 1043 aoIdsWithoutSch.add(aoWrapper.getAoInfo().getId()); 1044 } 1045 1046 //Check for sub-term or term and populate accordingly 1047 //If no change of AO.getTermId() > avoid service calls and populate sub-term info as it has been stored in aoWrapperStored 1048 if(aoWrapper.getAoInfo().getTermId().equals(aoWrapperStored.getAoInfo().getTermId())){ 1049 aoWrapper.setHasSubTerms(aoWrapperStored.getHasSubTerms()); 1050 aoWrapper.setSubTermId(aoWrapperStored.getSubTermId()); 1051 aoWrapper.setSubTermName(aoWrapperStored.getSubTermName()); 1052 aoWrapper.setTerm(aoWrapperStored.getTerm()); 1053 aoWrapper.setTermName(aoWrapperStored.getTermName()); 1054 aoWrapper.setTermDisplayString(aoWrapperStored.getTermDisplayString()); 1055 aoWrapper.setTermStartEndDate(aoWrapperStored.getTermStartEndDate()); 1056 } else { 1057 TermInfo term; 1058 TermInfo subTerm; 1059 aoWrapper.setHasSubTerms(false); 1060 aoWrapper.setSubTermName("None"); 1061 aoWrapper.setSubTermId(""); 1062 List<TermInfo> terms = getAcalService().getContainingTerms(aoWrapper.getAoInfo().getTermId(), contextInfo); 1063 if (terms == null || terms.isEmpty()) { 1064 term = getAcalService().getTerm(aoWrapper.getAoInfo().getTermId(), contextInfo); 1065 // checking if we can have sub-terms for giving term 1066 List<TermInfo> subTerms = getAcalService().getIncludedTermsInTerm(aoWrapper.getAoInfo().getTermId(), contextInfo); 1067 if(!subTerms.isEmpty()) { 1068 aoWrapper.setHasSubTerms(true); 1069 } 1070 } else { 1071 subTerm = getAcalService().getTerm(aoWrapper.getAoInfo().getTermId(), contextInfo); 1072 term = terms.get(0); 1073 aoWrapper.setHasSubTerms(true); 1074 aoWrapper.setSubTermId(subTerm.getId()); 1075 TypeInfo subTermType = getTypeService().getType(subTerm.getTypeKey(), contextInfo); 1076 aoWrapper.setSubTermName(subTermType.getName()); 1077 aoWrapper.setTermStartEndDate(ARGUtil.getTermStartEndDate(subTerm.getId(), subTerm)); 1078 } 1079 aoWrapper.setTerm(term); 1080 if (term != null) { 1081 aoWrapper.setTermName(term.getName()); 1082 } 1083 aoWrapper.setTermDisplayString(ARGUtil.getTermDisplayString(aoWrapper.getAoInfo().getTermId(), term)); 1084 aoWrapperStored = aoWrapper; //update for next comparison 1085 } 1086 // end sub-terms 1087 activityOfferingWrappers.add(aoWrapper); 1088 } 1089 } 1090 1091 FormatOfferingInfo fo = foIds.get(foId); 1092 if (fo == null) { 1093 fo = new FormatOfferingInfo(); 1094 fo.setId(foId); 1095 fo.setName(foName); 1096 fo.setFormatId(formatId); 1097 foIds.put(foId, fo); 1098 } 1099 1100 } 1101 1102 return activityOfferingWrappers; 1103 } 1104 1105 protected EntityDefaultQueryResults getInstructorsInfoFromKim(List<String> principalIds) { 1106 QueryByCriteria.Builder qbcBuilder = QueryByCriteria.Builder.create(); 1107 qbcBuilder.setPredicates( 1108 PredicateFactory.in("principals.principalId", principalIds.toArray()) 1109 ); 1110 1111 QueryByCriteria criteria = qbcBuilder.build(); 1112 1113 EntityDefaultQueryResults entityResults = getIdentityService().findEntityDefaults(criteria); 1114 1115 return entityResults; 1116 } 1117 1118 /** 1119 * This method will indicate to the user if the cluster canot be generated because the AO Set does not contain 1120 * enough activities that meet the requirements of the FormatOffering 1121 * 1122 * @param aoTypeKeys list of AO Type Keys 1123 * @param aoList List Of Activity Offerings 1124 * @param aoClusterWrapper AO Cluster 1125 * @param clusterIndex Used to tack the warning message onto a particular part of the screen 1126 * @param contextInfo context of the call 1127 * @throws Exception 1128 */ 1129 protected void _performAOCompletePerClusterValidation(List<String> aoTypeKeys, List<ActivityOfferingInfo> aoList, 1130 ActivityOfferingClusterWrapper aoClusterWrapper, int clusterIndex, ContextInfo contextInfo) throws Exception { 1131 Map<String, Boolean> completeAoSet = new HashMap<String, Boolean>(); // using a map to store what's required 1132 1133 for (String aoType : aoTypeKeys) { 1134 completeAoSet.put(aoType, false); 1135 } 1136 1137 for (ActivityOfferingInfo aoInfo : aoList) { 1138 completeAoSet.put(aoInfo.getTypeKey(), true); // This is used to determine if the AO Set has all FO types 1139 } 1140 1141 StringBuilder aoCompleteWarningMessageSB = new StringBuilder(); 1142 String delim = ""; 1143 for (Map.Entry<String,Boolean> completeAoEntry : completeAoSet.entrySet()) { 1144 if (!completeAoEntry.getValue()) { 1145 // type service should be cached so this shouldn't be slow 1146 aoCompleteWarningMessageSB.append(delim + getTypeService().getType(completeAoEntry.getKey(), contextInfo).getName()); 1147 delim = ", "; 1148 } 1149 } 1150 if (!(aoCompleteWarningMessageSB.length()==0)) { 1151 aoClusterWrapper.setRgStatus(RegistrationGroupConstants.RGSTATUS_NO_RG_GENERATED); 1152 aoClusterWrapper.setRgMessageStyle(ActivityOfferingClusterWrapper.RG_MESSAGE_ALL); 1153 aoClusterWrapper.setHasAllRegGroups(true); 1154 String clusterName = aoClusterWrapper.getClusterNameForDisplay(); 1155 String formatName = aoClusterWrapper.getFormatNameForDisplay(); 1156 GlobalVariables.getMessageMap().putWarningForSectionId("activityOfferingsPerCluster_line" + clusterIndex, CourseOfferingConstants.REGISTRATIONGROUP_INCOMPLETE_AOSET, clusterName, formatName, aoCompleteWarningMessageSB.toString()); 1157 } 1158 1159 } 1160 1161 /* 1162 * Perform several validations: 1163 * 1. check if All RGs have been generated given AOs in a cluster 1164 * if not, set rgStatus="Only Some Registration Groups Generated" and 1165 * set hasAllRegGroups=false, therefore "Generate Registration Group" action link will show up 1166 * if yes, set rgStatus="All Registration Groups Generated" and 1167 * set hasAllRegGroups=true, therefore "View Registration Group" action link will show up 1168 * 2. when #1 validation result is yes, need to perform max enrollment validation. 1169 * 3. when #1 validation result is yes, need to perform time conflict validation 1170 * 1171 */ 1172 private void _validateRegistrationGroupsPerCluster(List<RegistrationGroupInfo> rgInfos, List<ActivityOfferingInfo> aoList, 1173 ActivityOfferingClusterWrapper aoClusterWrapper, 1174 ARGCourseOfferingManagementForm theForm, int clusterIndex, Map<String, List<ScheduleCalcContainer>> ao2sch, Map<String, List<ScheduleRequestCalcContainer>> ao2schReq, Map<String, ActivityOfferingWrapper> aoMap) throws Exception { 1175 1176 Map<String, List<String>> activityOfferingTypeToAvailableActivityOfferingMap = 1177 _constructActivityOfferingTypeToAvailableActivityOfferingMap(aoList); 1178 1179 List<List<String>> generatedPermutations = new ArrayList<List<String>>(); 1180 List<List<String>> foundList = new ArrayList<List<String>>(); 1181 1182 PermutationUtils.generatePermutations(new ArrayList<String>( 1183 activityOfferingTypeToAvailableActivityOfferingMap.keySet()), 1184 new ArrayList<String>(), 1185 activityOfferingTypeToAvailableActivityOfferingMap, 1186 generatedPermutations); 1187 1188 List<RegistrationGroupInfo> rgInfosCopy = new ArrayList<RegistrationGroupInfo>(rgInfos.size()); 1189 for (RegistrationGroupInfo rgInfo : rgInfos) { 1190 rgInfosCopy.add(rgInfo); 1191 } 1192 1193 for (List<String> activityOfferingPermutation : generatedPermutations) { 1194 for (RegistrationGroupInfo rgInfo : rgInfosCopy) { 1195 if (_hasGeneratedRegGroup(activityOfferingPermutation, rgInfo)) { 1196 rgInfosCopy.remove(rgInfo); 1197 foundList.add(activityOfferingPermutation); 1198 break; 1199 } 1200 } 1201 } 1202 if (generatedPermutations.size() != foundList.size()) { 1203 aoClusterWrapper.setRgStatus(RegistrationGroupConstants.RGSTATUS_SOME_RG_GENERATED); 1204 aoClusterWrapper.setRgMessageStyle(ActivityOfferingClusterWrapper.RG_MESSAGE_PARTIAL); 1205 aoClusterWrapper.setHasAllRegGroups(false); 1206 } else { 1207 aoClusterWrapper.setRgStatus(RegistrationGroupConstants.RGSTATUS_ALL_RG_GENERATED); 1208 aoClusterWrapper.setRgMessageStyle(ActivityOfferingClusterWrapper.RG_MESSAGE_ALL); 1209 aoClusterWrapper.setHasAllRegGroups(true); 1210 // perform max enrollment validation 1211 _performMaxEnrollmentValidation(aoMap, aoClusterWrapper.getAoCluster(), clusterIndex); 1212 //validate AO time conflict in RG 1213 _performRGTimeConflictValidation(aoClusterWrapper.getAoCluster(), rgInfos, clusterIndex, ao2sch, ao2schReq); 1214 } 1215 1216 _performAOCompletePerClusterValidation(theForm.getFoId2aoTypeMap().get(aoClusterWrapper.getFormatOfferingId()).getActivityOfferingTypeKeys(), aoList, aoClusterWrapper, clusterIndex, ContextUtils.createDefaultContextInfo()); 1217 1218 if (!rgInfosCopy.isEmpty()) { 1219 GlobalVariables.getMessageMap().putWarningForSectionId("registrationGroupsPerFormatSection", CourseOfferingConstants.REGISTRATIONGROUP_INVALID_REGGROUPS); 1220 } 1221 } 1222 1223 private Map<String, List<String>> _constructActivityOfferingTypeToAvailableActivityOfferingMap(List<ActivityOfferingInfo> aoList) { 1224 Map<String, List<String>> activityOfferingTypeToAvailableActivityOfferingMap = new HashMap<String, List<String>>(); 1225 1226 for (ActivityOfferingInfo info : aoList) { 1227 String activityType = info.getTypeKey(); 1228 List<String> activityList = activityOfferingTypeToAvailableActivityOfferingMap 1229 .get(activityType); 1230 1231 if (activityList == null) { 1232 activityList = new ArrayList<String>(); 1233 activityOfferingTypeToAvailableActivityOfferingMap.put( 1234 activityType, activityList); 1235 } 1236 1237 activityList.add(info.getId()); 1238 1239 } 1240 return activityOfferingTypeToAvailableActivityOfferingMap; 1241 } 1242 1243 private boolean _hasGeneratedRegGroup(List<String> activityOfferingPermutation, RegistrationGroupInfo rgInfo) { 1244 boolean isMatched = true; 1245 List<String> aoIds = rgInfo.getActivityOfferingIds(); 1246 List<String> aoIdsCopy = new ArrayList<String>(aoIds.size()); 1247 for (String aoId : aoIds) { 1248 aoIdsCopy.add(aoId); 1249 } 1250 List<String> foundList = new ArrayList<String>(); 1251 for (String activityOfferingPermutationItem : activityOfferingPermutation) { 1252 for (String aoId : aoIdsCopy) { 1253 if (activityOfferingPermutationItem.equals(aoId)) { 1254 aoIdsCopy.remove(aoId); 1255 foundList.add(activityOfferingPermutationItem); 1256 break; 1257 } 1258 } 1259 } 1260 if (activityOfferingPermutation.size() != foundList.size() || !aoIdsCopy.isEmpty()) { 1261 isMatched = false; 1262 } 1263 return isMatched; 1264 } 1265 1266 1267 private void _performMaxEnrollmentValidation(Map<String, ActivityOfferingWrapper> aoMap, ActivityOfferingClusterInfo aoCluster, int clusterIndex) throws Exception { 1268 1269 int aoSetMaxEnrollNumber = 0; 1270 int currentAoSetMaxEnrollNumber = 0; 1271 int listIndex = 0; 1272 ValidationResultInfo validationResultInfo = new ValidationResultInfo(); 1273 1274 //The max enrollment numbers of all the aoSets in the given AOC are the same. The validation passes. 1275 validationResultInfo.setLevel(ValidationResult.ErrorLevel.OK); 1276 validationResultInfo.setMessage("Sum of enrollment for each AO type is equal"); 1277 1278 for (ActivityOfferingSetInfo aos : aoCluster.getActivityOfferingSets()) { 1279 for (String aoId : aos.getActivityOfferingIds()) { 1280 ActivityOfferingWrapper aoWrapper = aoMap.get(aoId); 1281 ActivityOfferingInfo aoInfo = aoWrapper.getAoInfo(); 1282 if (aoInfo != null && aoInfo.getMaximumEnrollment() != null) { 1283 aoSetMaxEnrollNumber += aoInfo.getMaximumEnrollment(); 1284 } 1285 } 1286 1287 if (listIndex == 0) { 1288 currentAoSetMaxEnrollNumber = aoSetMaxEnrollNumber; 1289 } else { 1290 if (aoSetMaxEnrollNumber != currentAoSetMaxEnrollNumber) { 1291 1292 validationResultInfo.setLevel(ValidationResult.ErrorLevel.WARN); 1293 validationResultInfo.setMessage("Sum of enrollment for each AO type is not equal"); 1294 break; 1295 } 1296 } 1297 aoSetMaxEnrollNumber = 0; 1298 listIndex++; 1299 } 1300 1301 if (validationResultInfo.isWarn()) { 1302 GlobalVariables.getMessageMap().putWarningForSectionId("activityOfferingsPerCluster_line" + clusterIndex, RegistrationGroupConstants.MSG_WARNING_MAX_ENROLLMENT, aoCluster.getPrivateName()); 1303 } 1304 } 1305 1306 private List<Integer> _performRGTimeConflictValidation(ActivityOfferingClusterInfo aoCluster, List<RegistrationGroupInfo> registrationGroupInfos, int clusterIndex, Map<String, List<ScheduleCalcContainer>> ao2sch, Map<String, List<ScheduleRequestCalcContainer>> ao2schReq) throws Exception { 1307 List<Integer> rgIndexList = new ArrayList<Integer>(); 1308 rgIndexList.clear(); 1309 1310 if (aoCluster != null && registrationGroupInfos != null && !registrationGroupInfos.isEmpty()) { 1311 int rgIndex = 0; 1312 for (RegistrationGroupInfo registrationGroupInfo : registrationGroupInfos) { 1313 1314 List<ValidationResultInfo> validationResultInfoList = new ArrayList<ValidationResultInfo>(); 1315 // Lets build a list of all schedules that need to be compared for this registration group. 1316 List<TimeSlotInfo> shed2Check = new ArrayList<TimeSlotInfo>(); 1317 for (String aoId : registrationGroupInfo.getActivityOfferingIds()) { 1318 if (ao2sch.get(aoId) != null) { 1319 for (ScheduleCalcContainer sched : ao2sch.get(aoId)) { 1320 shed2Check.add(toTimeSlotInfo(sched)); 1321 } 1322 } 1323 if (ao2schReq.get(aoId) != null) { 1324 for (ScheduleRequestCalcContainer sched : ao2schReq.get(aoId)) { 1325 shed2Check.addAll(sched.getTimeSlots()); 1326 } 1327 } 1328 } 1329 1330 for (TimeSlotInfo outerEntry : shed2Check) { 1331 for (TimeSlotInfo innerEntry : shed2Check) { 1332 if (outerEntry.equals(innerEntry)) { 1333 break; 1334 } 1335 if (SchedulingServiceUtil.areTimeSlotsInConflict(outerEntry, innerEntry)) { 1336 ValidationResultInfo validationResultInfo = new ValidationResultInfo(); 1337 validationResultInfo.setLevel(ValidationResult.ErrorLevel.WARN); 1338 validationResultInfo.setMessage("time conflict between AO: " + outerEntry.getId() + " and AO: " + innerEntry.getId()); 1339 validationResultInfoList.add(validationResultInfo); 1340 } 1341 } 1342 } 1343 1344 if (!validationResultInfoList.isEmpty() && validationResultInfoList.get(0).isWarn()) { 1345 //Why are we alter the RG state? Commenting out for now. 1346 //getCourseOfferingService().changeRegistrationGroupState(registrationGroupInfo.getId(), LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY, ContextUtils.createDefaultContextInfo()); 1347 rgIndexList.add(rgIndex); 1348 } 1349 1350 rgIndex++; 1351 } 1352 1353 if (!rgIndexList.isEmpty()) { 1354 GlobalVariables.getMessageMap().putWarningForSectionId("activityOfferingsPerCluster_line" + clusterIndex, RegistrationGroupConstants.MSG_WARNING_AO_TIMECONFLICT, aoCluster.getPrivateName()); 1355 } 1356 } 1357 1358 return rgIndexList; 1359 } 1360 1361 public TimeSlotInfo toTimeSlotInfo(ScheduleCalcContainer sched) { 1362 TimeSlotInfo info = new TimeSlotInfo(); 1363 1364 1365 info.setId(null); 1366 info.setTypeKey(null); 1367 info.setStateKey(null); 1368 info.setName(null); 1369 info.setWeekdays(SchedulingServiceUtil.weekdaysString2WeekdaysList(sched.getWeekdays())); 1370 1371 info.setStartTime(new TimeOfDayInfo()); 1372 info.getStartTime().setMilliSeconds(((sched.getStart() != null && !"".equals(sched.getStart())) ? new Long(sched.getStart()) : null)); 1373 1374 info.setEndTime(new TimeOfDayInfo()); 1375 info.getEndTime().setMilliSeconds(((sched.getEnd() != null && !"".equals(sched.getEnd())) ? new Long(sched.getEnd()) : null)); 1376 1377 info.setDescr(new RichTextInfo()); 1378 info.getDescr().setFormatted(null); 1379 info.getDescr().setPlain(null); 1380 1381 info.setMeta(null); 1382 1383 return info; 1384 } 1385 1386 /** 1387 * This method fetches all the course offerings for a term and course/subject code. 1388 * 1389 * @param searchRequest search request 1390 * @param form input form 1391 * @throws Exception 1392 * @see CourseOfferingManagementSearchImpl Actual CO search happens here 1393 */ 1394 protected void loadCourseOfferings(SearchRequestInfo searchRequest, ARGCourseOfferingManagementForm form) throws Exception { 1395 1396 ContextInfo contextInfo = createContextInfo(); 1397 1398 SearchResultInfo searchResult = getSearchService().search(searchRequest, contextInfo); 1399 1400 form.getCourseOfferingResultList().clear(); 1401 1402 for (SearchResultRowInfo row : searchResult.getRows()) { 1403 CourseOfferingListSectionWrapper coListWrapper = new CourseOfferingListSectionWrapper(); 1404 1405 for (SearchResultCellInfo cellInfo : row.getCells()) { 1406 1407 String value = StringUtils.EMPTY; 1408 if (cellInfo.getValue() != null) { 1409 value = cellInfo.getValue(); 1410 } 1411 1412 if (CourseOfferingManagementSearchImpl.SearchResultColumns.CODE.equals(cellInfo.getKey())) { 1413 coListWrapper.setCourseOfferingCode(value); 1414 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.DESC.equals(cellInfo.getKey())) { 1415 coListWrapper.setCourseOfferingDesc(value); 1416 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.STATE.equals(cellInfo.getKey())) { 1417 coListWrapper.setCourseOfferingStateKey(value); 1418 coListWrapper.setCourseOfferingStateDisplay(getStateInfo(value).getName()); 1419 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.CREDIT_OPTION.equals(cellInfo.getKey())) { 1420 coListWrapper.setCourseOfferingCreditOptionKey(value); 1421 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.GRADING_OPTION.equals(cellInfo.getKey())) { 1422 coListWrapper.setCourseOfferingGradingOptionKey(value); 1423 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.GRADING_OPTION_NAME.equals(cellInfo.getKey())) { 1424 coListWrapper.setCourseOfferingGradingOptionDisplay(cellInfo.getValue()); 1425 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.CREDIT_OPTION_NAME.equals(cellInfo.getKey())) { 1426 coListWrapper.setCourseOfferingCreditOptionDisplay(cellInfo.getValue()); 1427 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.DEPLOYMENT_ORG_ID.equals(cellInfo.getKey())) { 1428 coListWrapper.setAdminOrg(cellInfo.getValue()); 1429 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.CO_ID.equals(cellInfo.getKey())) { 1430 coListWrapper.setCourseOfferingId(value); 1431 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.SUBJECT_AREA.equals(cellInfo.getKey())) { 1432 coListWrapper.setSubjectArea(value); 1433 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.IS_CROSS_LISTED.equals(cellInfo.getKey())) { 1434 coListWrapper.setCrossListed(BooleanUtils.toBoolean(value)); 1435 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.CROSS_LISTED_COURSES.equals(cellInfo.getKey())) { 1436 coListWrapper.setAlternateCOCodes(Arrays.asList(StringUtils.split(value, ","))); 1437 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.OWNER_CODE.equals(cellInfo.getKey())) { 1438 coListWrapper.setOwnerCode(value); 1439 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.OWNER_ALIASES.equals(cellInfo.getKey())) { 1440 coListWrapper.setOwnerAliases(Arrays.asList(StringUtils.split(value, ","))); 1441 } 1442 1443 } 1444 form.getCourseOfferingResultList().add(coListWrapper); 1445 } 1446 } 1447 1448 1449 /** 1450 * This method loads the previous and next course offerings for navigation purpose. 1451 * 1452 * @param form input form 1453 */ 1454 public void loadPreviousAndNextCourseOffering(ARGCourseOfferingManagementForm form) { 1455 try { 1456 ContextInfo contextInfo = createContextInfo(); 1457 1458 /** 1459 * Get all the course offerings for a term and subject area. 1460 */ 1461 SearchRequestInfo searchRequest = new SearchRequestInfo(CourseOfferingManagementSearchImpl.CO_MANAGEMENT_SEARCH.getKey()); 1462 searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.SUBJECT_AREA, form.getSubjectCode()); 1463 searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.ATP_ID, form.getTermInfo().getId()); 1464 searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.CROSS_LIST_SEARCH_ENABLED, BooleanUtils.toStringTrueFalse(true)); 1465 1466 SearchResultInfo searchResult = getSearchService().search(searchRequest, contextInfo); 1467 List<CourseOfferingWrapper> availableCOs = new ArrayList<CourseOfferingWrapper>(); 1468 1469 for (SearchResultRowInfo row : searchResult.getRows()) { 1470 1471 String courseOfferingCode = ""; 1472 String courseOfferingId = ""; 1473 String courseOfferingDesc = ""; 1474 boolean isCrossListed = false; 1475 List<String> alternateCodes = null; 1476 1477 for (SearchResultCellInfo cellInfo : row.getCells()) { 1478 1479 String value = StringUtils.EMPTY; 1480 if (cellInfo.getValue() != null) { 1481 value = cellInfo.getValue(); 1482 } 1483 1484 if (CourseOfferingManagementSearchImpl.SearchResultColumns.CODE.equals(cellInfo.getKey())) { 1485 courseOfferingCode = value; 1486 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.CO_ID.equals(cellInfo.getKey())) { 1487 courseOfferingId = value; 1488 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.DESC.equals(cellInfo.getKey())) { 1489 courseOfferingDesc = value; 1490 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.IS_CROSS_LISTED.equals(cellInfo.getKey())) { 1491 isCrossListed = BooleanUtils.toBoolean(value); 1492 } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.CROSS_LISTED_COURSES.equals(cellInfo.getKey())) { 1493 alternateCodes = Arrays.asList(StringUtils.split(value, ",")); 1494 } 1495 1496 } 1497 1498 CourseOfferingWrapper coWrapper = new CourseOfferingWrapper(isCrossListed, courseOfferingCode, courseOfferingDesc, alternateCodes, courseOfferingId); 1499 availableCOs.add(coWrapper); 1500 } 1501 1502 /** 1503 * Find the current course offering index and set the previous and next course offerings if exists. 1504 */ 1505 for (CourseOfferingWrapper coWrapper : availableCOs) { 1506 if (StringUtils.equals(coWrapper.getCourseOfferingCode(), form.getCurrentCourseOfferingWrapper().getCourseOfferingCode())) { 1507 int currentIndex = availableCOs.indexOf(coWrapper); 1508 form.setInputCode(coWrapper.getCourseOfferingCode()); 1509 if (currentIndex > 0) { 1510 form.setPreviousCourseOfferingWrapper(availableCOs.get(currentIndex - 1)); 1511 } else { 1512 form.setPreviousCourseOfferingWrapper(null); 1513 } 1514 if (currentIndex < availableCOs.size() - 1) { 1515 form.setNextCourseOfferingWrapper(availableCOs.get(currentIndex + 1)); 1516 } else { 1517 form.setNextCourseOfferingWrapper(null); 1518 } 1519 break; 1520 } 1521 } 1522 1523 1524 } catch (Exception e) { 1525 throw new RuntimeException(e); 1526 } 1527 } 1528 1529 public void createActivityOfferings(String formatOfferingId, String activityId, int noOfActivityOfferings, ARGCourseOfferingManagementForm form) { 1530 String termcode; 1531 FormatOfferingInfo formatOfferingInfo; 1532 TypeInfo activityOfferingType = null; 1533 CourseInfo course; 1534 String clusterId = form.getClusterIdForNewAO(); 1535 ContextInfo contextInfo = createContextInfo(); 1536 List<ActivityOfferingClusterInfo> clusters; 1537 ActivityOfferingClusterInfo defaultCluster; 1538 1539 //create default cluster if there is no cluster for the FO yet 1540 try { 1541 clusters = getCourseOfferingService().getActivityOfferingClustersByFormatOffering(formatOfferingId, contextInfo); 1542 if (clusters == null || clusters.size() <= 0) { 1543 defaultCluster = ARGUtil.getArgServiceAdapter().createDefaultCluster(formatOfferingId, contextInfo); 1544 if (defaultCluster != null) { 1545 clusterId = defaultCluster.getId(); 1546 } 1547 } 1548 } catch (Exception e) { 1549 throw new RuntimeException(e); 1550 } 1551 1552 //the AO clusters associated with the given FO 1553 CourseOfferingInfo courseOffering = form.getCurrentCourseOfferingWrapper().getCourseOfferingInfo(); 1554 1555 // find the Activity object that matches the activity id selected 1556 ActivityInfo activity = null; 1557 1558 // Get the format object for the id selected 1559 try { 1560 formatOfferingInfo = getCourseOfferingService().getFormatOffering(formatOfferingId, contextInfo); 1561 course = getCourseService().getCourse(courseOffering.getCourseId(), contextInfo); 1562 for (FormatInfo f : course.getFormats()) { 1563 if (f.getId().equals(formatOfferingInfo.getFormatId())) { 1564 for (ActivityInfo info : f.getActivities()) { 1565 if (StringUtils.equals(activityId, info.getId())) { 1566 activity = info; 1567 } 1568 } 1569 break; 1570 } 1571 } 1572 } catch (Exception e) { 1573 throw new RuntimeException(e); 1574 } 1575 1576 // Get the matching activity offering type for the selected activity 1577 try { 1578 for (String aoTypeKey : formatOfferingInfo.getActivityOfferingTypeKeys()) { 1579 List<TypeTypeRelationInfo> typeTypeRelationInfos = getTypeService().getTypeTypeRelationsByRelatedTypeAndType(aoTypeKey, TypeServiceConstants.TYPE_TYPE_RELATION_ALLOWED_TYPE_KEY, ContextUtils.getContextInfo()); 1580 if (typeTypeRelationInfos != null && typeTypeRelationInfos.size() > 0) { 1581 if (StringUtils.equals(activity.getTypeKey(), typeTypeRelationInfos.get(0).getOwnerTypeKey())) { 1582 activityOfferingType = getTypeService().getType(aoTypeKey, contextInfo); 1583 } 1584 1585 } 1586 } 1587 } catch (Exception e) { 1588 throw new RuntimeException(e); 1589 } 1590 1591 try { 1592 AtpInfo termAtp = getAtpService().getAtp(courseOffering.getTermId(), contextInfo); 1593 termcode = termAtp.getCode(); 1594 } catch (Exception e) { 1595 throw new RuntimeException(e); 1596 } 1597 1598 for (int i = 0; i < noOfActivityOfferings; i++) { 1599 ActivityOfferingInfo aoInfo = new ActivityOfferingInfo(); 1600 aoInfo.setActivityId(activityId); 1601 aoInfo.setFormatOfferingId(formatOfferingInfo.getId()); 1602 aoInfo.setTypeKey(activityOfferingType.getKey()); 1603 aoInfo.setCourseOfferingId(courseOffering.getId()); 1604 aoInfo.setStateKey(LuiServiceConstants.LUI_AO_STATE_DRAFT_KEY); 1605 aoInfo.setTermId(courseOffering.getTermId()); 1606 aoInfo.setTermCode(termcode); 1607 aoInfo.setFormatOfferingName(formatOfferingInfo.getName()); 1608 aoInfo.setCourseOfferingTitle(courseOffering.getCourseOfferingTitle()); 1609 aoInfo.setCourseOfferingCode(courseOffering.getCourseOfferingCode()); 1610 1611 ActivityOfferingInfo activityOfferingInfo; 1612 try { 1613 //Temp solution here: if there is cluster(s) assocaited with the given FO, call createAO method in adapter 1614 //It will associate created AOs with the first cluster of the given FO 1615 if (clusterId != null && !clusterId.isEmpty()) { 1616 activityOfferingInfo = ARGUtil.getArgServiceAdapter().createActivityOffering(aoInfo, clusterId, contextInfo).getCreatedActivityOffering(); 1617 } else { 1618 activityOfferingInfo = _getCourseOfferingService().createActivityOffering(formatOfferingInfo.getId(), activityId, activityOfferingType.getKey(), aoInfo, contextInfo); 1619 } 1620 ActivityOfferingWrapper wrapper = new ActivityOfferingWrapper(activityOfferingInfo); 1621 StateInfo state = getStateService().getState(wrapper.getAoInfo().getStateKey(), contextInfo); 1622 wrapper.setStateName(state.getName()); 1623 TypeInfo typeInfo = getTypeInfo(wrapper.getAoInfo().getTypeKey()); 1624 wrapper.setTypeName(typeInfo.getName()); 1625 form.getActivityWrapperList().add(wrapper); 1626 } catch (Exception e) { 1627 throw new RuntimeException(e); 1628 } 1629 } 1630 1631 ARGToolbarUtil.processAoToolbarForUser(form.getActivityWrapperList(), form); 1632 if (noOfActivityOfferings == 1) { 1633 KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_ADD_1_SUCCESS); 1634 } else { 1635 KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_ADD_N_SUCCESS); 1636 } 1637 } 1638 1639 public void loadActivityOfferingsByCourseOffering(CourseOfferingInfo theCourseOfferingInfo, ARGCourseOfferingManagementForm form) throws Exception { 1640 String courseOfferingId = theCourseOfferingInfo.getId(); 1641 List<ActivityOfferingInfo> activityOfferingInfoList; 1642 List<ActivityOfferingWrapper> activityOfferingWrapperList; 1643 1644 try { 1645 activityOfferingInfoList = _getCourseOfferingService().getActivityOfferingsByCourseOffering(courseOfferingId, createContextInfo()); 1646 activityOfferingWrapperList = new ArrayList<ActivityOfferingWrapper>(activityOfferingInfoList.size()); 1647 1648 for (ActivityOfferingInfo info : activityOfferingInfoList) { 1649 ActivityOfferingWrapper aoWrapper = convertAOInfoToWrapper(info); 1650 activityOfferingWrapperList.add(aoWrapper); 1651 } 1652 } catch (Exception e) { 1653 throw new RuntimeException(String.format("Could not load AOs for course offering [%s].", courseOfferingId), e); 1654 } 1655 form.setActivityWrapperList(activityOfferingWrapperList); 1656 } 1657 1658 public List<ActivityOfferingWrapper> getActivityOfferingsByCourseOfferingId(String courseOfferingId, ARGCourseOfferingManagementForm form) throws Exception { 1659 List<ActivityOfferingInfo> activityOfferingInfoList; 1660 List<ActivityOfferingWrapper> activityOfferingWrapperList; 1661 1662 try { 1663 activityOfferingInfoList = _getCourseOfferingService().getActivityOfferingsByCourseOffering(courseOfferingId, createContextInfo()); 1664 activityOfferingWrapperList = new ArrayList<ActivityOfferingWrapper>(activityOfferingInfoList.size()); 1665 1666 for (ActivityOfferingInfo info : activityOfferingInfoList) { 1667 ActivityOfferingWrapper aoWrapper = new ActivityOfferingWrapper(info); 1668 activityOfferingWrapperList.add(aoWrapper); 1669 } 1670 } catch (Exception e) { 1671 throw new RuntimeException(String.format("Could not load AOs for course offering [%s].", courseOfferingId), e); 1672 } 1673 return activityOfferingWrapperList; 1674 } 1675 1676 public void approveCourseOfferings(ARGCourseOfferingManagementForm form) throws Exception { 1677 List<CourseOfferingListSectionWrapper> coList = form.getCourseOfferingResultList(); 1678 ContextInfo contextInfo = createContextInfo(); 1679 int checked = 0; 1680 int enabled = 0; 1681 for (CourseOfferingListSectionWrapper co : coList) { 1682 if (co.getIsChecked()) { 1683 checked++; 1684 if (co.isEnableApproveButton()) { 1685 enabled++; 1686 List<ActivityOfferingWrapper> aos = getActivityOfferingsByCourseOfferingId(co.getCourseOfferingId(), form); 1687 if (aos != null && !aos.isEmpty()) { 1688 ARGToolbarUtil.processAoToolbarForUser(aos, form); 1689 for (ActivityOfferingWrapper ao : aos) { 1690 if (ao.isEnableApproveButton()) { 1691 getCourseOfferingService().changeActivityOfferingState(ao.getAoInfo().getId(), LuiServiceConstants.LUI_AO_STATE_APPROVED_KEY, contextInfo); 1692 } 1693 } 1694 } 1695 } 1696 } 1697 } 1698 1699 if (checked > enabled) { 1700 KSUifUtils.addGrowlMessageIcon(GrowlIcon.WARNING, CourseOfferingConstants.COURSEOFFERING_TOOLBAR_APPROVED); 1701 } else { 1702 if (enabled == 1) { 1703 KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.COURSEOFFERING_TOOLBAR_APPROVED_1_SUCCESS); 1704 } else { 1705 KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.COURSEOFFERING_TOOLBAR_APPROVED_N_SUCCESS); 1706 } 1707 } 1708 } 1709 1710 public void deleteCourseOfferings(ARGCourseOfferingManagementForm form) throws Exception { 1711 List<CourseOfferingListSectionWrapper> coList = form.getCourseOfferingResultList(); 1712 int totalCosToDelete = 0; 1713 int totalColocatedCos = 0; 1714 int totalColocatedAos = 0; 1715 boolean iscolocated; 1716 int totalCrossListedCosToDelete = 0; 1717 int totalJointDefinedCosToDelete = 0; 1718 1719 form.setNumOfColocatedCosToDelete(0); 1720 form.setNumOfColocatedAosToDelete(0); 1721 form.setNumOfCrossListedCosToDelete(0); 1722 form.setNumOfJointDefinedCosToDelete(0); 1723 1724 List<CourseOfferingListSectionWrapper> qualifiedToDeleteList = form.getSelectedCoToDeleteList(); 1725 qualifiedToDeleteList.clear(); 1726 ContextInfo contextInfo = ContextUtils.createDefaultContextInfo(); 1727 1728 int totalAos = 0; 1729 form.setCrossListedCO(false); 1730 form.setColocatedCO(false); 1731 for (CourseOfferingListSectionWrapper co : coList) { 1732 if (co.getIsChecked()) { 1733 totalCosToDelete++; 1734 iscolocated = false; 1735 1736 List<ActivityOfferingDisplayInfo> aoDisplayInfoList = getCourseOfferingService().getActivityOfferingDisplaysForCourseOffering(co.getCourseOfferingId(), contextInfo); 1737 List<ActivityOfferingInfo> aoInfoList = getCourseOfferingService().getActivityOfferingsByCourseOffering(co.getCourseOfferingId(), contextInfo); 1738 1739 co.setCoHasAoToDelete(false); 1740 1741 co.setColocated(false); 1742 1743 if (aoDisplayInfoList != null && !aoDisplayInfoList.isEmpty()) { 1744 co.setCoHasAoToDelete(true); 1745 for (ActivityOfferingDisplayInfo aoDisplayInfo : aoDisplayInfoList) { 1746 1747 ActivityOfferingDisplayWrapper aoDisplayWrapper = new ActivityOfferingDisplayWrapper(); 1748 aoDisplayWrapper.setAoDisplayInfo(aoDisplayInfo); 1749 aoDisplayWrapper.setActivityOfferingCode(aoDisplayInfo.getActivityOfferingCode()); 1750 // Adding Information (icons) 1751 String information = ""; 1752 if (aoDisplayInfo.getIsHonorsOffering() != null && aoDisplayInfo.getIsHonorsOffering()) { 1753 information = "<img src=" + ScheduleOfClassesConstants.SOC_RESULT_PAGE_HONORS_COURSE_IMG + " title=\"" + ScheduleOfClassesConstants.SOC_RESULT_PAGE_HELP_HONORS_ACTIVITY + "\"> "; 1754 } 1755 aoDisplayWrapper.setInformation(information); 1756 1757 if (aoDisplayInfo.getScheduleDisplay() != null && !aoDisplayInfo.getScheduleDisplay().getScheduleComponentDisplays().isEmpty()) { 1758 //TODO handle TBA state 1759 List<? extends ScheduleComponentDisplay> scheduleComponentDisplays = aoDisplayInfo.getScheduleDisplay().getScheduleComponentDisplays(); 1760 for (ScheduleComponentDisplay scheduleComponentDisplay : scheduleComponentDisplays) { 1761 if (scheduleComponentDisplay.getBuilding() != null) { 1762 aoDisplayWrapper.setBuildingName(scheduleComponentDisplay.getBuilding().getBuildingCode(), true); 1763 } 1764 if (scheduleComponentDisplay.getRoom() != null) { 1765 aoDisplayWrapper.setRoomName(scheduleComponentDisplay.getRoom().getRoomCode(), true); 1766 } 1767 if (!scheduleComponentDisplay.getTimeSlots().isEmpty()) { 1768 if (scheduleComponentDisplay.getTimeSlots().get(0).getStartTime() != null) { 1769 aoDisplayWrapper.setStartTimeDisplay(millisToTime(scheduleComponentDisplay.getTimeSlots().get(0).getStartTime().getMilliSeconds()), true); 1770 } 1771 if (scheduleComponentDisplay.getTimeSlots().get(0).getEndTime() != null) { 1772 aoDisplayWrapper.setEndTimeDisplay(millisToTime(scheduleComponentDisplay.getTimeSlots().get(0).getEndTime().getMilliSeconds()), true); 1773 } 1774 aoDisplayWrapper.setDaysDisplayName(getDays(scheduleComponentDisplay.getTimeSlots().get(0).getWeekdays()), true); 1775 } 1776 } 1777 } 1778 // if ao is colocated AO add colocated info 1779 if (isColocatedAo(aoDisplayInfo.getActivityOfferingCode(), aoInfoList)) { 1780 String colocateInfo = CourseOfferingViewHelperUtil.createColocatedDisplayData(getAoInfo(aoDisplayInfo.getActivityOfferingCode(), aoInfoList), contextInfo); 1781 aoDisplayWrapper.setColocatedAoInfo(colocateInfo); 1782 co.setColocated(true); 1783 co.setColocatedCoCode(colocateInfo); 1784 form.setColocatedCO(true); 1785 totalColocatedAos++; 1786 iscolocated = true; 1787 } 1788 co.getAoToBeDeletedList().add(aoDisplayWrapper); 1789 } 1790 1791 totalAos = totalAos + co.getAoToBeDeletedList().size(); 1792 co.setCrossListed(false); 1793 if (co.getAlternateCOCodes() != null && co.getAlternateCOCodes().size() > 0) { 1794 co.setCrossListed(true); 1795 form.setCrossListedCO(true); 1796 totalCrossListedCosToDelete++; 1797 } 1798 } 1799 co.setJointDefined(false); 1800 1801 if (iscolocated) { 1802 totalColocatedCos++; 1803 } else { 1804 // verify whether this CO is joint-defined or not 1805 String jointDefinedCodes = getJointDefinedInfo(co); 1806 if (!jointDefinedCodes.isEmpty()) { 1807 co.setJointDefinedCoCode(jointDefinedCodes); 1808 co.setJointDefined(true); 1809 totalJointDefinedCosToDelete++; 1810 } 1811 } 1812 qualifiedToDeleteList.add(co); 1813 } 1814 } 1815 form.setNumOfCrossListedCosToDelete(totalCrossListedCosToDelete); 1816 form.setNumOfJointDefinedCosToDelete(totalJointDefinedCosToDelete); 1817 if (totalColocatedCos == totalCosToDelete) { 1818 form.setColocatedCoOnly(true); 1819 form.setNumOfColocatedCosToDelete(totalColocatedCos); 1820 } 1821 if (totalJointDefinedCosToDelete >= 1) { 1822 form.setJointDefinedCo(true); 1823 } 1824 form.setNumOfColocatedAosToDelete(totalColocatedAos); 1825 form.setTotalAOsToBeDeleted(totalAos); 1826 } 1827 1828 public void approveActivityOfferings(ARGCourseOfferingManagementForm form) throws Exception { 1829 List<ActivityOfferingWrapper> aoList = form.getActivityWrapperList(); 1830 ContextInfo contextInfo = createContextInfo(); 1831 int checked = 0; 1832 int enabled = 0; 1833 for (ActivityOfferingWrapper ao : aoList) { 1834 if ((!"0".equals(form.getSelectedTabId())) && ao.getIsChecked() || 1835 ("0".equals(form.getSelectedTabId())) && ao.getIsCheckedByCluster()) { 1836 checked++; 1837 if (ao.isEnableApproveButton()) { 1838 enabled++; 1839 getCourseOfferingService().changeActivityOfferingState(ao.getAoInfo().getId(), LuiServiceConstants.LUI_AO_STATE_APPROVED_KEY, contextInfo); 1840 } 1841 } 1842 } 1843 1844 if (checked > enabled) { 1845 KSUifUtils.addGrowlMessageIcon(GrowlIcon.WARNING, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_APPROVED); 1846 } else { 1847 if (enabled == 1) { 1848 KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_APPROVED_1_SUCCESS); 1849 } else { 1850 KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_APPROVED_N_SUCCESS); 1851 } 1852 } 1853 } 1854 1855 public void draftActivityOfferings(ARGCourseOfferingManagementForm form) throws Exception { 1856 List<ActivityOfferingWrapper> aoList = form.getActivityWrapperList(); 1857 ContextInfo contextInfo = createContextInfo(); 1858 int checked = 0; 1859 int enabled = 0; 1860 for (ActivityOfferingWrapper ao : aoList) { 1861 if ((!"0".equals(form.getSelectedTabId())) && ao.getIsChecked() || 1862 ("0".equals(form.getSelectedTabId())) && ao.getIsCheckedByCluster()) { 1863 checked++; 1864 if (ao.isEnableDraftButton()) { 1865 enabled++; 1866 getCourseOfferingService().changeActivityOfferingState(ao.getAoInfo().getId(), LuiServiceConstants.LUI_AO_STATE_DRAFT_KEY, contextInfo); 1867 } 1868 } 1869 } 1870 1871 if (checked > enabled) { 1872 KSUifUtils.addGrowlMessageIcon(GrowlIcon.WARNING, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_DRAFT); 1873 } else { 1874 if (enabled == 1) { 1875 KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_DRAFT_1_SUCCESS); 1876 } else { 1877 KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_DRAFT_N_SUCCESS); 1878 } 1879 } 1880 1881 } 1882 1883 /** 1884 * Notes by Bonnie: The following implementation tries to support "Approve for Scheduling" in two cases historically: 1885 * 1)when some COs are checked and approve for scheduling link or button is clicked 1886 * vs. 1887 * 2)when a user simply click "Approve Subject Code for Scheduling" link with/without selecting any CO 1888 * case 1) has been implemented by approveCourseOfferings method -- invoked by Approve button in toolbar 1889 * case 2) is the only use case when markCourseOfferingsForScheduling is invoked. Therefore the parameter 1890 * checkedOnly and related checking for each CO seem not needed anymore... 1891 * TODO: code cleanup?? 1892 * <p/> 1893 * Examines a List of CourseOffering wrappers and changes the state of each "checked" AO (meaning the 1894 * CO was selected on the UI) from "Draft" to "Approved". If the AO has a state other than "Draft" the AO is ignored. 1895 * Also, changes the state of the CourseOffering if appropriate. 1896 * 1897 * @param coWrappers, viewId, socStateKey provides list of CourseOfferings, viewId, socState. 1898 * @param checkedOnly True if the CO wrapper isChecked() flag should be respected. 1899 */ 1900 public void markCourseOfferingsForScheduling(List<CourseOfferingListSectionWrapper> coWrappers, 1901 String viewId, String socStateKey, boolean checkedOnly) throws Exception { 1902 boolean hasAOWarning = false, hasStateChangedAO = false, hasOrgWarning = false; 1903 ContextInfo contextInfo = createContextInfo(); 1904 for (CourseOfferingListSectionWrapper coWrapper : coWrappers) { 1905 if (coWrapper.getIsChecked() || !checkedOnly) { 1906 // Checking if the person is authorized to approve 1907 Map<String, String> permissionDetails = new HashMap<String, String>(); 1908 Map<String, String> roleQualifications = new HashMap<String, String>(); 1909 CourseOfferingInfo coInfo = getCourseOfferingService().getCourseOffering(coWrapper.getCourseOfferingId(), contextInfo); 1910 List<String> orgIds = coInfo.getUnitsDeploymentOrgIds(); 1911 StringBuilder orgIDs = new StringBuilder(); 1912 if (orgIds != null && !orgIds.isEmpty()) { 1913 for (String orgId : orgIds) { 1914 orgIDs.append(orgId + ","); 1915 } 1916 } 1917 boolean canApproveAOs = true; 1918 if (orgIDs.length() > 0) { 1919 String principalId = GlobalVariables.getUserSession().getPerson().getPrincipalId(); 1920 1921 roleQualifications.put("offeringAdminOrgId", orgIDs.substring(0, orgIDs.length() - 1)); 1922 1923 permissionDetails.put(KimConstants.AttributeConstants.VIEW_ID, viewId); 1924 permissionDetails.put(KimConstants.AttributeConstants.ACTION_EVENT, "approveSubj"); 1925 1926 String socState = socStateKey == null ? null : socStateKey.substring(socStateKey.lastIndexOf('.') + 1); 1927 permissionDetails.put("socState", socState); 1928 1929 canApproveAOs = getPermissionService().isAuthorizedByTemplate(principalId, "KS-ENR", KimConstants.PermissionTemplateNames.PERFORM_ACTION, permissionDetails, roleQualifications); 1930 } 1931 1932 if (!canApproveAOs) { // if can't approve AOs for all COs (because they are in a different org) 1933 if (!hasOrgWarning) hasOrgWarning = true; 1934 continue; 1935 } else { 1936 1937 List<ActivityOfferingInfo> activityOfferingInfos = getCourseOfferingService().getActivityOfferingsByCourseOffering(coWrapper.getCourseOfferingId(), contextInfo); 1938 if (activityOfferingInfos.size() == 0) { 1939 if (!hasAOWarning) hasAOWarning = true; 1940 continue; 1941 } 1942 // Iterate through the AOs and state change Draft -> Approved. 1943 for (ActivityOfferingInfo activityOfferingInfo : activityOfferingInfos) { 1944 boolean isAOStateDraft = StringUtils.equals(activityOfferingInfo.getStateKey(), LuiServiceConstants.LUI_AO_STATE_DRAFT_KEY); 1945 if (isAOStateDraft) { 1946 StatusInfo statusInfo = getCourseOfferingService().changeActivityOfferingState(activityOfferingInfo.getId(), LuiServiceConstants.LUI_AO_STATE_APPROVED_KEY, contextInfo); 1947 if (!statusInfo.getIsSuccess()) { 1948 GlobalVariables.getMessageMap().putError("manageCourseOfferingsPage", CourseOfferingConstants.COURSE_OFFERING_STATE_CHANGE_ERROR, coWrapper.getCourseOfferingCode(), statusInfo.getMessage()); 1949 } 1950 // Flag if any AOs can be state changed. This affects the error message whi. 1951 if (statusInfo.getIsSuccess()) { 1952 hasStateChangedAO = true; 1953 } 1954 } else { 1955 // Flag if any AOs are not in a valid state for approval. 1956 if (!hasAOWarning) hasAOWarning = true; 1957 } 1958 } 1959 } 1960 } 1961 } 1962 // Set feedback messages. 1963 if (!hasStateChangedAO) { 1964 GlobalVariables.getMessageMap().putError("manageCourseOfferingsPage", CourseOfferingConstants.COURSEOFFERING_NONE_APPROVED); 1965 } else { 1966 if (hasAOWarning) { 1967 GlobalVariables.getMessageMap().putWarning("manageCourseOfferingsPage", CourseOfferingConstants.COURSEOFFERING_WITH_AO_DRAFT_APPROVED_ONLY); 1968 } 1969 if (hasOrgWarning) { 1970 GlobalVariables.getMessageMap().putWarning("manageCourseOfferingsPage", CourseOfferingConstants.COURSEOFFERING_WITH_AO_ORG_APPROVED_ONLY); 1971 } 1972 } 1973 } 1974 1975 private void setSocStateKeys(ARGCourseOfferingManagementForm form, List<String> socIds) throws Exception { 1976 if (socIds != null && !socIds.isEmpty()) { 1977 List<SocInfo> targetSocs = this.getSocService().getSocsByIds(socIds, createContextInfo()); 1978 for (SocInfo soc : targetSocs) { 1979 if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.MAIN_SOC_TYPE_KEY)) { 1980 form.setSocStateKey(soc.getStateKey()); 1981 form.setSocSchedulingStateKey(soc.getSchedulingStateKey()); 1982 1983 //TODO: Set SOC State - temporary display, to be removed after testing is finished 1984 1985 if (StringUtils.isNotBlank(soc.getStateKey())) { 1986 String socState = getStateService().getState(soc.getStateKey(), createContextInfo()).getName(); 1987 socState = (socState.substring(0, 1)).toUpperCase() + socState.substring(1, socState.length()); 1988 form.setSocState(socState); 1989 } 1990 1991 return; 1992 } 1993 } 1994 } 1995 } 1996 1997 private boolean isColocatedAo(String aoCode, List<ActivityOfferingInfo> aoList) { 1998 for(ActivityOfferingInfo ao : aoList) { 1999 if(StringUtils.equals(aoCode, ao.getActivityCode())) { 2000 if(ao.getIsColocated()) { 2001 return true; 2002 } 2003 } 2004 } 2005 return false; 2006 } 2007 2008 private ActivityOfferingInfo getAoInfo(String aoCode, List<ActivityOfferingInfo> aoList) { 2009 for(ActivityOfferingInfo ao : aoList) { 2010 if(StringUtils.equals(aoCode, ao.getActivityCode())) { 2011 if(ao.getIsColocated()) { 2012 return ao; 2013 } 2014 } 2015 } 2016 return null; 2017 } 2018 2019 private String getJointDefinedInfo(CourseOfferingListSectionWrapper co) { 2020 if (co == null) return null; 2021 2022 List<CourseInfo> coInfoList = CourseOfferingViewHelperUtil.getMatchingCoursesFromClu(co.getCourseOfferingCode()); 2023 StringBuffer jointDefinedCodes = new StringBuffer(); 2024 2025 for (CourseInfo coInfo : coInfoList) { 2026 List<CourseJointInfo> jointList = coInfo.getJoints(); 2027 if (!jointList.isEmpty() && jointList.size() >= 1) { 2028 for (CourseJointInfo jointInfo : jointList) { 2029 jointDefinedCodes.append(jointInfo.getSubjectArea()); 2030 jointDefinedCodes.append(jointInfo.getCourseNumberSuffix()); 2031 jointDefinedCodes.append(" "); 2032 } 2033 2034 } 2035 } 2036 2037 return jointDefinedCodes.toString(); 2038 } 2039 2040 private CourseOfferingService _getCourseOfferingService() { 2041 if (coService == null) { 2042 coService = (CourseOfferingService) GlobalResourceLoader.getService(new QName(CourseOfferingServiceConstants.NAMESPACE, 2043 CourseOfferingServiceConstants.SERVICE_NAME_LOCAL_PART)); 2044 } 2045 return coService; 2046 } 2047 2048 private AcademicCalendarService getAcalService() { 2049 if (acalService == null) { 2050 acalService = (AcademicCalendarService) GlobalResourceLoader.getService(new QName(AcademicCalendarServiceConstants.NAMESPACE, 2051 AcademicCalendarServiceConstants.SERVICE_NAME_LOCAL_PART)); 2052 } 2053 return acalService; 2054 } 2055 2056 private String millisToTime(Long milliseconds) { 2057 if (milliseconds == null) { 2058 return null; 2059 } 2060 final Calendar cal = Calendar.getInstance(); 2061 cal.setTimeInMillis(milliseconds); 2062 return DateFormatters.HOUR_MINUTE_AM_PM_TIME_FORMATTER.format(cal.getTime()); 2063 2064 } 2065 2066 private String convertIntoDaysDisplay(int day) { 2067 String dayOfWeek; 2068 switch (day) { 2069 case 1: 2070 dayOfWeek = SchedulingServiceConstants.SUNDAY_TIMESLOT_DISPLAY_DAY_CODE; 2071 break; 2072 case 2: 2073 dayOfWeek = SchedulingServiceConstants.MONDAY_TIMESLOT_DISPLAY_DAY_CODE; 2074 break; 2075 case 3: 2076 dayOfWeek = SchedulingServiceConstants.TUESDAY_TIMESLOT_DISPLAY_DAY_CODE; 2077 break; 2078 case 4: 2079 dayOfWeek = SchedulingServiceConstants.WEDNESDAY_TIMESLOT_DISPLAY_DAY_CODE; 2080 break; 2081 case 5: 2082 dayOfWeek = SchedulingServiceConstants.THURSDAY_TIMESLOT_DISPLAY_DAY_CODE; 2083 break; 2084 case 6: 2085 dayOfWeek = SchedulingServiceConstants.FRIDAY_TIMESLOT_DISPLAY_DAY_CODE; 2086 break; 2087 case 7: 2088 dayOfWeek = SchedulingServiceConstants.SATURDAY_TIMESLOT_DISPLAY_DAY_CODE; 2089 break; 2090 default: 2091 dayOfWeek = StringUtils.EMPTY; 2092 } 2093 // TODO implement TBA when service stores it. 2094 return dayOfWeek; 2095 } 2096 2097 private String getDays(List<Integer> intList) { 2098 2099 StringBuilder sb = new StringBuilder(); 2100 if (intList == null) { 2101 return sb.toString(); 2102 } 2103 2104 for (Integer d : intList) { 2105 sb.append(convertIntoDaysDisplay(d)); 2106 } 2107 2108 return sb.toString(); 2109 } 2110 2111 public CourseOfferingService getCourseOfferingService() { 2112 return CourseOfferingResourceLoader.loadCourseOfferingService(); 2113 } 2114 2115 public CourseService getCourseService() { 2116 if (courseService == null) { 2117 courseService = CourseOfferingResourceLoader.loadCourseService(); 2118 } 2119 return courseService; 2120 } 2121 2122 2123 protected LRCService getLrcService() { 2124 if (lrcService == null) { 2125 lrcService = (LRCService) GlobalResourceLoader.getService(new QName("http://student.kuali.org/wsdl/lrc", "LrcService")); 2126 } 2127 return this.lrcService; 2128 } 2129 2130 public SearchService getSearchService() { 2131 if (searchService == null) { 2132 searchService = (SearchService) GlobalResourceLoader.getService(new QName(CommonServiceConstants.REF_OBJECT_URI_GLOBAL_PREFIX + "search", SearchService.class.getSimpleName())); 2133 } 2134 return searchService; 2135 } 2136 2137 public AtpService getAtpService() { 2138 if (atpService == null) { 2139 atpService = CourseOfferingResourceLoader.loadAtpService(); 2140 } 2141 return atpService; 2142 } 2143 2144 public CourseOfferingSetService getSocService() { 2145 // If it hasn't been set by Spring, then look it up by GlobalResourceLoader 2146 if (socService == null) { 2147 socService = (CourseOfferingSetService) GlobalResourceLoader.getService(new QName(CourseOfferingSetServiceConstants.NAMESPACE, 2148 CourseOfferingSetServiceConstants.SERVICE_NAME_LOCAL_PART)); 2149 } 2150 return socService; 2151 } 2152 2153 private static PermissionService getPermissionService() { 2154 if (permissionService == null) { 2155 permissionService = KimApiServiceLocator.getPermissionService(); 2156 } 2157 return permissionService; 2158 } 2159 2160 public static IdentityService getIdentityService() { 2161 if (identityService == null) { 2162 identityService = KimApiServiceLocator.getIdentityService(); 2163 } 2164 return identityService; 2165 } 2166 2167 public static LprService getLprService() { 2168 if (lprService == null) { 2169 lprService = (LprService) GlobalResourceLoader.getService(new QName(LprServiceConstants.NAMESPACE, 2170 LprServiceConstants.SERVICE_NAME_LOCAL_PART)); 2171 } 2172 return lprService; 2173 } 2174 2175 2176}