1
2
3
4
5 package org.kuali.student.enrollment.class2.courseofferingset.service.impl;
6
7 import org.apache.commons.lang.StringUtils;
8 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
9 import org.kuali.student.enrollment.class2.courseofferingset.dao.SocDao;
10 import org.kuali.student.enrollment.class2.courseofferingset.service.facade.RolloverAssist;
11 import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingInfo;
12 import org.kuali.student.enrollment.courseoffering.dto.CourseOfferingInfo;
13 import org.kuali.student.enrollment.courseoffering.service.CourseOfferingService;
14 import org.kuali.student.enrollment.courseofferingset.dto.SocInfo;
15 import org.kuali.student.enrollment.courseofferingset.dto.SocRolloverResultInfo;
16 import org.kuali.student.enrollment.courseofferingset.service.CourseOfferingSetService;
17 import org.kuali.student.enrollment.courseofferingset.service.CourseOfferingSetServiceBusinessLogic;
18 import org.kuali.student.r2.common.dto.ContextInfo;
19 import org.kuali.student.r2.common.dto.StatusInfo;
20 import org.kuali.student.r2.common.exceptions.DataValidationErrorException;
21 import org.kuali.student.r2.common.exceptions.DoesNotExistException;
22 import org.kuali.student.r2.common.exceptions.InvalidParameterException;
23 import org.kuali.student.r2.common.exceptions.MissingParameterException;
24 import org.kuali.student.r2.common.exceptions.OperationFailedException;
25 import org.kuali.student.r2.common.exceptions.PermissionDeniedException;
26 import org.kuali.student.r2.common.exceptions.ReadOnlyException;
27 import org.kuali.student.r2.common.exceptions.VersionMismatchException;
28 import org.kuali.student.r2.common.util.constants.CourseOfferingSetServiceConstants;
29 import org.kuali.student.r2.common.util.constants.LuiServiceConstants;
30 import org.kuali.student.r2.core.acal.dto.TermInfo;
31 import org.kuali.student.r2.core.acal.service.AcademicCalendarService;
32 import org.kuali.student.r2.core.constants.AtpServiceConstants;
33 import org.kuali.student.r2.core.scheduling.service.SchedulingService;
34 import org.kuali.student.r2.lum.course.service.CourseService;
35
36 import javax.xml.namespace.QName;
37 import java.util.ArrayList;
38 import java.util.Date;
39 import java.util.List;
40
41 public class CourseOfferingSetServiceBusinessLogicImpl implements CourseOfferingSetServiceBusinessLogic {
42
43 private CourseOfferingService coService;
44 private CourseService courseService;
45 private AcademicCalendarService acalService;
46 private CourseOfferingSetService socService;
47 private RolloverAssist rolloverAssist;
48 private SocDao socDao;
49
50 private SchedulingService schedulingService;
51
52 public SocDao getSocDao() {
53 return socDao;
54 }
55
56 public void setSocDao(SocDao socDao) {
57 this.socDao = socDao;
58 }
59
60 public CourseOfferingSetService getSocService() {
61 return socService;
62 }
63
64 public void setSocService(CourseOfferingSetService socService) {
65 this.socService = socService;
66 }
67
68 public CourseOfferingService getCoService() {
69 return coService;
70 }
71
72 public void setCoService(CourseOfferingService coService) {
73 this.coService = coService;
74 }
75
76 public CourseService getCourseService() {
77 return courseService;
78 }
79
80 public void setCourseService(CourseService courseService) {
81 this.courseService = courseService;
82 }
83
84 public AcademicCalendarService getAcalService() {
85 return acalService;
86 }
87
88 public void setAcalService(AcademicCalendarService acalService) {
89 this.acalService = acalService;
90 }
91
92 public RolloverAssist getRolloverAssist() {
93 return rolloverAssist;
94 }
95
96 public void setRolloverAssist(RolloverAssist rolloverAssist) {
97 this.rolloverAssist = rolloverAssist;
98 }
99
100 private CourseOfferingSetService _getSocService() {
101
102 if (socService == null) {
103 socService = (CourseOfferingSetService) GlobalResourceLoader.getService(new QName(CourseOfferingSetServiceConstants.NAMESPACE,
104 CourseOfferingSetServiceConstants.SERVICE_NAME_LOCAL_PART));
105 }
106 return socService;
107 }
108
109 public SchedulingService getSchedulingService() {
110 return schedulingService;
111 }
112
113 public void setSchedulingService(SchedulingService schedulingService) {
114 this.schedulingService = schedulingService;
115 }
116
117 private SocInfo _findTargetSoc(String targetTermId) {
118 try {
119 List<String> socIds = this._getSocService().getSocIdsByTerm(targetTermId, new ContextInfo());
120 if (socIds != null) {
121 if (socIds.isEmpty()) {
122 return null;
123 }
124 List<SocInfo> targetSocs = this._getSocService().getSocsByIds(socIds, new ContextInfo());
125 for (SocInfo soc: targetSocs) {
126 if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.MAIN_SOC_TYPE_KEY)) {
127 return soc;
128 }
129 }
130 }
131 return null;
132 } catch (Exception e) {
133 return null;
134 }
135 }
136
137 private boolean _hasOfferingsInTargetTerm(TermInfo targetTerm) {
138 if (socDao == null) {
139 return false;
140 }
141 Long count = socDao.countLuisByTypeForTermId(LuiServiceConstants.COURSE_OFFERING_TYPE_KEY, targetTerm.getId());
142 return count > 0;
143 }
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158 private String _verifyTermsOfficial(TermInfo targetTerm, ContextInfo contextInfo) throws PermissionDeniedException, MissingParameterException, InvalidParameterException, OperationFailedException, DoesNotExistException {
159 if (!targetTerm.getStateKey().equals(AtpServiceConstants.ATP_OFFICIAL_STATE_KEY)) {
160 return targetTerm.getId();
161 }
162 List<TermInfo> childTerms = acalService.getIncludedTermsInTerm(targetTerm.getId(), contextInfo);
163 for (TermInfo termInfo: childTerms) {
164 if (!termInfo.getStateKey().equals(AtpServiceConstants.ATP_OFFICIAL_STATE_KEY)) {
165 return termInfo.getId();
166 }
167 }
168 return null;
169 }
170
171 @Override
172 public SocInfo rolloverSoc(String sourceSocId, String targetTermId, List<String> optionKeys, ContextInfo context)
173 throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
174 PermissionDeniedException {
175
176 TermInfo targetTerm = this.acalService.getTerm(targetTermId, context);
177 if (targetTerm == null) {
178 throw new DoesNotExistException("target term id (" + targetTermId + ") does not exist");
179 }
180
181 SocInfo sourceSoc = this._getSocService().getSoc(sourceSocId, context);
182 if (sourceSoc.getTermId().equals(targetTermId)) {
183 throw new InvalidParameterException("The term of the source soc and the target term must be different");
184 }
185 String termId = null;
186 if ((termId = _verifyTermsOfficial(targetTerm, context)) != null) {
187 throw new OperationFailedException("Target (sub)term (id=" + termId + ") does not have official state. Can't rollover");
188 } else {
189
190 if (!optionKeys.contains(CourseOfferingSetServiceConstants.TARGET_TERM_VALIDATED_OPTION_KEY)) {
191 optionKeys = new ArrayList<String>(optionKeys);
192 optionKeys.add(CourseOfferingSetServiceConstants.TARGET_TERM_VALIDATED_OPTION_KEY);
193 }
194 }
195
196
197
198 if (_hasOfferingsInTargetTerm(targetTerm)) {
199 throw new OperationFailedException("Can't rollover if course offerings exist in target term");
200 }
201
202
203 SocInfo targetSoc = _findTargetSoc(targetTermId);
204 boolean foundTargetSoc = true;
205 if (targetSoc == null) {
206
207 targetSoc = new SocInfo(sourceSoc);
208 foundTargetSoc = false;
209 targetSoc.setId(null);
210 targetSoc.getAttributes().clear();
211 targetSoc.setTermId(targetTermId);
212 targetSoc.setStateKey(CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY);
213 try {
214 targetSoc = this._getSocService().createSoc(targetSoc.getTermId(), targetSoc.getTypeKey(), targetSoc, context);
215 } catch (DataValidationErrorException ex) {
216 throw new OperationFailedException("Unexpected", ex);
217 } catch (ReadOnlyException ex) {
218 throw new OperationFailedException("Unexpected", ex);
219 }
220 } else {
221
222 if (!targetSoc.getStateKey().equals(CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY))
223
224
225 this._getSocService().changeSocState(targetSoc.getId(), CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY, context);
226 }
227
228
229
230 SocRolloverResultInfo result = new SocRolloverResultInfo();
231 result.setTypeKey(CourseOfferingSetServiceConstants.ROLLOVER_RESULT_TYPE_KEY);
232 result.setStateKey(CourseOfferingSetServiceConstants.SUBMITTED_RESULT_STATE_KEY);
233 result.setSourceSocId(sourceSocId);
234 result.setTargetTermId(targetTermId);
235 result.setOptionKeys(optionKeys);
236 result.setTargetSocId(targetSoc.getId());
237 Date now = new Date();
238 result.setDateInitiated(now);
239
240
241
242 result.setDateCompleted(now);
243 try {
244 result = this._getSocService().createSocRolloverResult(result.getTypeKey(), result, context);
245 } catch (DataValidationErrorException ex) {
246 throw new OperationFailedException("Unexpected", ex);
247 } catch (ReadOnlyException ex) {
248 throw new OperationFailedException("Unexpected", ex);
249 }
250
251 final CourseOfferingRolloverRunner runner = new CourseOfferingRolloverRunner();
252 runner.setContext(context);
253 runner.setCoService(coService);
254 runner.setCourseService(courseService);
255 runner.setAcalService(acalService);
256 runner.setSocService(this._getSocService());
257 runner.setRolloverAssist(rolloverAssist);
258 runner.setResult(result);
259
260 if (optionKeys.contains(CourseOfferingSetServiceConstants.RUN_SYNCHRONOUSLY_OPTION_KEY)) {
261
262 runner.run();
263 } else {
264
265 KSThreadRunnerAfterTransactionSynchronization.runAfterTransactionCompletes(runner);
266 }
267 return targetSoc;
268 }
269
270 @Override
271 public SocRolloverResultInfo reverseRollover(String rolloverResultId, List<String> optionKeys, ContextInfo context)
272 throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
273 PermissionDeniedException {
274 SocRolloverResultInfo rolloverResult = this._getSocService().getSocRolloverResult(rolloverResultId, context);
275 if (optionKeys.contains(CourseOfferingSetServiceConstants.REVERSE_JUST_CREATES_OPTION_KEY)) {
276 if (!rolloverResult.getOptionKeys().contains(CourseOfferingSetServiceConstants.LOG_SUCCESSES_OPTION_KEY)) {
277 throw new InvalidParameterException(
278 "You cannot reverse just the creates if the original rollover did not log the course offerings that it successfully created");
279 }
280 }
281 SocRolloverResultInfo reverseResult = new SocRolloverResultInfo(rolloverResult);
282 reverseResult.setTypeKey(CourseOfferingSetServiceConstants.ROLLOVER_RESULT_TYPE_KEY);
283 reverseResult.setStateKey(CourseOfferingSetServiceConstants.SUBMITTED_RESULT_STATE_KEY);
284 reverseResult.setOptionKeys(optionKeys);
285
286 try {
287 reverseResult = this._getSocService().createSocRolloverResult(reverseResult.getTypeKey(), reverseResult, context);
288 } catch (DataValidationErrorException ex) {
289 throw new OperationFailedException("Unexpected", ex);
290 } catch (ReadOnlyException ex) {
291 throw new OperationFailedException("Unexpected", ex);
292 }
293
294 CourseOfferingReverseRolloverRunner runner = new CourseOfferingReverseRolloverRunner();
295 runner.setContext(context);
296 runner.setCoService(coService);
297 runner.setSocService(this._getSocService());
298 runner.setCourseService(courseService);
299 runner.setAcalService(acalService);
300 runner.setRolloverResult(rolloverResult);
301 runner.setReverseResult(reverseResult);
302 if (optionKeys.contains(CourseOfferingSetServiceConstants.RUN_SYNCHRONOUSLY_OPTION_KEY)) {
303 runner.run();
304 } else {
305 Thread thread = new Thread(runner);
306 thread.start();
307 }
308 return reverseResult;
309 }
310
311 @Override
312 public List<String> getCourseOfferingIdsBySoc(String socId, ContextInfo context)
313 throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
314 PermissionDeniedException {
315
316 SocInfo soc = this._getSocService().getSoc(socId, context);
317
318 if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.MAIN_SOC_TYPE_KEY)) {
319 return coService.getCourseOfferingIdsByTerm(soc.getTermId(), Boolean.TRUE, context);
320 }
321
322 if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.SUBJECT_AREA_SOC_TYPE_KEY)) {
323 return coService.getCourseOfferingIdsByTermAndSubjectArea(soc.getTermId(), soc.getSubjectArea(), context);
324 }
325
326 if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.UNITS_CONTENT_OWNER_SOC_TYPE_KEY)) {
327 return coService.getCourseOfferingIdsByTermAndUnitsContentOwner(soc.getTermId(), soc.getUnitsContentOwnerId(), context);
328 }
329 throw new OperationFailedException(soc.getTypeKey() + " is an unsupported type for this implementation");
330
331
332
333
334
335
336
337 }
338
339 @Override
340 public Integer deleteCourseOfferingsBySoc(String socId, ContextInfo context)
341 throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
342 PermissionDeniedException {
343
344
345 List<String> ids = this.getCourseOfferingIdsBySoc(socId, context);
346 for (String id : ids) {
347 this.coService.deleteCourseOfferingCascaded(id, context);
348 }
349 return ids.size();
350 }
351
352 @Override
353 public Boolean isCourseOfferingInSoc(String socId, String courseOfferingId, ContextInfo context)
354 throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
355 PermissionDeniedException {
356
357 SocInfo soc = this._getSocService().getSoc(socId, context);
358 CourseOfferingInfo co = this.coService.getCourseOffering(courseOfferingId, context);
359
360 if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.MAIN_SOC_TYPE_KEY)) {
361 if (co.getTermId().equals(soc.getTermId())) {
362 return true;
363 }
364
365 return false;
366 }
367
368 if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.SUBJECT_AREA_SOC_TYPE_KEY)) {
369 if (co.getTermId().equals(soc.getTermId())) {
370 if (co.getSubjectArea().equals(soc.getSubjectArea())) {
371 return true;
372 }
373 }
374
375 return false;
376 }
377
378 if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.UNITS_CONTENT_OWNER_SOC_TYPE_KEY)) {
379 if (co.getTermId().equals(soc.getTermId())) {
380 if (co.getUnitsContentOwnerOrgIds().equals(soc.getUnitsContentOwnerId())) {
381 return true;
382 }
383 }
384
385 return false;
386 }
387
388 List<String> ids = this.getCourseOfferingIdsBySoc(socId, context);
389 return ids.contains(courseOfferingId);
390 }
391
392 @Override
393 public List<String> getPublishedCourseOfferingIdsBySoc(String socId, ContextInfo context)
394 throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
395 PermissionDeniedException {
396 List<String> list = new ArrayList<String>();
397 List<String> list2 = this.getCourseOfferingIdsBySoc(socId, context);
398 for (CourseOfferingInfo info : this.coService.getCourseOfferingsByIds(list2, context)) {
399
400
401 list.add(info.getId());
402
403 }
404 return list;
405 }
406
407 @Override
408 public List<String> getUnpublishedCourseOfferingIdsBySoc(String socId, ContextInfo context)
409 throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
410 PermissionDeniedException {
411 List<String> list = new ArrayList<String>();
412 List<String> list2 = this.getCourseOfferingIdsBySoc(socId, context);
413 for (CourseOfferingInfo info : this.coService.getCourseOfferingsByIds(list2, context)) {
414
415
416 list.add(info.getId());
417
418 }
419 return list;
420 }
421
422 @Override
423 public List<String> getUnpublishedActivityOfferingIdsBySoc(String socId, ContextInfo context)
424 throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
425 PermissionDeniedException {
426 List<String> list = new ArrayList<String>();
427 List<String> list2 = this.getCourseOfferingIdsBySoc(socId, context);
428 for (String coId : list2) {
429 for (ActivityOfferingInfo ao : this.coService.getActivityOfferingsByCourseOffering(coId, context)) {
430
431
432 list.add(ao.getId());
433
434 }
435 }
436 return list;
437 }
438
439 @Override
440 public List<String> getUnscheduledActivityOfferingIdsBySoc(String socId, ContextInfo context)
441 throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
442 PermissionDeniedException {
443 List<String> list = new ArrayList<String>();
444 List<String> list2 = this.getCourseOfferingIdsBySoc(socId, context);
445 for (String coId : list2) {
446 for (ActivityOfferingInfo ao : this.coService.getActivityOfferingsByCourseOffering(coId, context)) {
447
448
449 list.add(ao.getId());
450
451 }
452 }
453 return list;
454 }
455
456 @Override
457 public StatusInfo startScheduleSoc(String socId, List<String> optionKeys, ContextInfo context) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
458
459 SocInfo socInfo = this._getSocService().getSoc(socId, context);
460 if ( ! StringUtils.equals(socInfo.getStateKey(), CourseOfferingSetServiceConstants.LOCKED_SOC_STATE_KEY)) {
461 throw new OperationFailedException(String.format("SOC state [%s] was invalid for mass schduling.", socInfo.getStateKey()));
462 }
463
464 if (! StringUtils.equals(socInfo.getSchedulingStateKey(), CourseOfferingSetServiceConstants.SOC_SCHEDULING_STATE_IN_PROGRESS)) {
465 throw new OperationFailedException(String.format("SOC scheduling state [%s] was invalid for mass scheduling.", socInfo.getStateKey()));
466 }
467
468 final CourseOfferingSetSchedulingRunner schedulingRunner = new CourseOfferingSetSchedulingRunner(socInfo.getId());
469 schedulingRunner.setContextInfo(context);
470 schedulingRunner.setCoService(coService);
471 schedulingRunner.setSchedulingService(schedulingService);
472 schedulingRunner.setSocService(this._getSocService());
473
474
475 KSThreadRunnerAfterTransactionSynchronization.runAfterTransactionCompletes(schedulingRunner);
476
477 StatusInfo status = new StatusInfo();
478 status.setMessage("Scheduling runner started successfully");
479 return status;
480 }
481 }