View Javadoc

1   /*
2    * To change this template, choose Tools | Templates
3    * and open the template in the editor.
4    */
5   package org.kuali.student.enrollment.class2.courseofferingset.service.impl;
6   
7   import java.util.ArrayList;
8   import java.util.Date;
9   import java.util.List;
10  
11  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
12  import org.kuali.student.enrollment.acal.dto.TermInfo;
13  import org.kuali.student.enrollment.acal.service.AcademicCalendarService;
14  import org.kuali.student.enrollment.class2.courseofferingset.dao.SocDao;
15  import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingInfo;
16  import org.kuali.student.enrollment.courseoffering.dto.CourseOfferingInfo;
17  import org.kuali.student.enrollment.courseoffering.service.CourseOfferingService;
18  import org.kuali.student.enrollment.courseofferingset.dto.SocInfo;
19  import org.kuali.student.enrollment.courseofferingset.dto.SocRolloverResultInfo;
20  import org.kuali.student.enrollment.courseofferingset.service.CourseOfferingSetService;
21  import org.kuali.student.enrollment.courseofferingset.service.CourseOfferingSetServiceBusinessLogic;
22  import org.kuali.student.r2.lum.course.service.CourseService;
23  import org.kuali.student.r2.common.dto.ContextInfo;
24  import org.kuali.student.r2.common.exceptions.*;
25  import org.kuali.student.r2.common.util.constants.CourseOfferingSetServiceConstants;
26  import org.kuali.student.r2.common.util.constants.LuiServiceConstants;
27  
28  import javax.xml.namespace.QName;
29  
30  public class CourseOfferingSetServiceBusinessLogicImpl implements CourseOfferingSetServiceBusinessLogic {
31  
32      private CourseOfferingService coService;
33      private CourseService courseService;
34      private AcademicCalendarService acalService;
35      private CourseOfferingSetService socService;
36      private SocDao socDao;
37  
38      public SocDao getSocDao() {
39          return socDao;
40      }
41  
42      public void setSocDao(SocDao socDao) {
43          this.socDao = socDao;
44      }
45  
46      public CourseOfferingSetService getSocService() {
47          return socService;
48      }
49  
50      public void setSocService(CourseOfferingSetService socService) {
51          this.socService = socService;
52      }
53      
54      public CourseOfferingService getCoService() {
55          return coService;
56      }
57  
58      public void setCoService(CourseOfferingService coService) {
59          this.coService = coService;
60      }
61  
62      public CourseService getCourseService() {
63          return courseService;
64      }
65  
66      public void setCourseService(CourseService courseService) {
67          this.courseService = courseService;
68      }
69  
70      public AcademicCalendarService getAcalService() {
71          return acalService;
72      }
73  
74      public void setAcalService(AcademicCalendarService acalService) {
75          this.acalService = acalService;
76      }
77  
78      private CourseOfferingSetService _getSocService() {
79          // If it hasn't been set by Spring, then look it up by GlobalResourceLoader
80          if (socService == null) {
81              socService = (CourseOfferingSetService) GlobalResourceLoader.getService(new QName(CourseOfferingSetServiceConstants.NAMESPACE,
82                                                                                      CourseOfferingSetServiceConstants.SERVICE_NAME_LOCAL_PART));
83          }
84          return socService;
85      }
86  
87      private SocInfo _findTargetSoc(String targetTermId) {
88          try {
89              List<String> socIds = this._getSocService().getSocIdsByTerm(targetTermId, new ContextInfo());
90              if (socIds != null) {
91                  if (socIds.isEmpty()) {
92                      return null;
93                  }
94                  for (String socId: socIds) {
95                      SocInfo targetSoc = this._getSocService().getSoc(socId, new ContextInfo());
96                      if (targetSoc.getTypeKey().equals(CourseOfferingSetServiceConstants.MAIN_SOC_TYPE_KEY)) {
97                          return targetSoc;
98                      }
99                  }
100             }
101             return null;
102         } catch (Exception e) {
103             return null;
104         }
105     }
106 
107     private boolean _hasOfferingsInTargetTerm(TermInfo targetTerm) {
108         if (socDao == null) {
109             return false; // Mostly to satisfy Norm's mock impls
110         }
111         Long count = socDao.countLuisByTypeForTermId(LuiServiceConstants.COURSE_OFFERING_TYPE_KEY, targetTerm.getId());
112         return count > 0;
113     }
114 
115     @Override
116     public SocInfo rolloverSoc(String sourceSocId, String targetTermId, List<String> optionKeys, ContextInfo context)
117             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
118             PermissionDeniedException {
119         // validate the target term
120         TermInfo targetTerm = this.acalService.getTerm(targetTermId, context);
121         if (targetTerm == null) {
122             throw new DoesNotExistException("target term id (" + targetTermId + ") does not exist");
123         }
124         // first create the new soc
125         SocInfo sourceSoc = this._getSocService().getSoc(sourceSocId, context);
126         if (sourceSoc.getTermId().equals(targetTermId)) {
127             throw new InvalidParameterException("The term of the source soc and the target term must be different");
128         }
129         // DanS says if there are any offerings in the target term, we shouldn't perform rollover.  The implication
130         // is that any courses that were copied from canonical prior to a rollover would prevent a rollover from
131         // happening. DanS has said this is fine (as of 5/20/2012)
132         if (_hasOfferingsInTargetTerm(targetTerm)) {
133             throw new OperationFailedException("Can't rollover if course offerings exist in target term");
134         }
135         // Reuse SOC in target term
136         SocInfo targetSoc = _findTargetSoc(targetTermId);
137         boolean foundTargetSoc = true;
138         if (targetSoc == null) {
139             // Did not find target SOC, make a new one
140             targetSoc = new SocInfo(sourceSoc);
141             foundTargetSoc = false;
142             targetSoc.setId(null);
143             targetSoc.setTermId(targetTermId);
144             targetSoc.setStateKey(CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY);
145             try {
146                 targetSoc = this._getSocService().createSoc(targetSoc.getTermId(), targetSoc.getTypeKey(), targetSoc, context);
147             } catch (DataValidationErrorException ex) {
148                 throw new OperationFailedException("Unexpected", ex);
149             } catch (ReadOnlyException ex) {
150                 throw new OperationFailedException("Unexpected", ex);
151             }
152         } else { // There is already a target SOC, so re-use it?
153             // TODO: if foundTargetSoc is true, should we do more cleanup?
154             targetSoc.setStateKey(CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY); // Make it draft in the new term
155             try {
156                 // Persist the draft state
157                 this._getSocService().updateSoc(targetSoc.getId(), targetSoc, context);
158             } catch (DataValidationErrorException ex) {
159                 throw new OperationFailedException("Unexpected", ex);
160             } catch (ReadOnlyException ex) {
161                 throw new OperationFailedException("Unexpected", ex);
162             } catch (VersionMismatchException ex) {
163                 throw new OperationFailedException("Unexpected", ex);
164             }
165         }
166 
167 
168         // then build the result so we can track stuff
169         SocRolloverResultInfo result = new SocRolloverResultInfo();
170         result.setTypeKey(CourseOfferingSetServiceConstants.ROLLOVER_RESULT_TYPE_KEY);
171         result.setStateKey(CourseOfferingSetServiceConstants.SUBMITTED_RESULT_STATE_KEY);
172         result.setSourceSocId(sourceSocId);
173         result.setTargetTermId(targetTermId);
174         result.setOptionKeys(optionKeys);
175         result.setTargetSocId(targetSoc.getId());
176         Date now = new Date();
177         result.setDateInitiated(now);
178         // Although it's not completed, as long as the SocRolloverResultInfo is either in the submitted or running
179         // state, the date completed field represents the current time.  It also prevents NPEs when computing
180         // duration.
181         result.setDateCompleted(now);
182         try {
183             result = this._getSocService().createSocRolloverResult(result.getTypeKey(), result, context);
184         } catch (DataValidationErrorException ex) {
185             throw new OperationFailedException("Unexpected", ex);
186         } catch (ReadOnlyException ex) {
187             throw new OperationFailedException("Unexpected", ex);
188         }
189         // create the runner so we can kick it off in another thread
190         final CourseOfferingRolloverRunner runner = new CourseOfferingRolloverRunner();
191         runner.setContext(context);
192         runner.setCoService(coService);
193         runner.setCourseService(courseService);
194         runner.setAcalService(acalService);
195         runner.setSocService(this._getSocService());
196         runner.setResult(result);
197 
198         if (optionKeys.contains(CourseOfferingSetServiceConstants.RUN_SYNCHRONOUSLY_OPTION_KEY)) {
199             //Run this thread synchronously
200             runner.run();
201         } else {
202             //Try to run this after the transaction completes
203             KSThreadRunnerAfterTransactionSynchronization.runAfterTransactionCompletes(runner);
204         }
205         return targetSoc;
206     }
207 
208     @Override
209     public SocRolloverResultInfo reverseRollover(String rolloverResultId, List<String> optionKeys, ContextInfo context)
210             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
211             PermissionDeniedException {// validate the target term
212         SocRolloverResultInfo rolloverResult = this._getSocService().getSocRolloverResult(rolloverResultId, context);
213         if (optionKeys.contains(CourseOfferingSetServiceConstants.REVERSE_JUST_CREATES_OPTION_KEY)) {
214             if (!rolloverResult.getOptionKeys().contains(CourseOfferingSetServiceConstants.LOG_SUCCESSES_OPTION_KEY)) {
215                 throw new InvalidParameterException(
216                         "You cannot reverse just the creates if the original rollover did not log the course offerings that it successfully created");
217             }
218         }
219         SocRolloverResultInfo reverseResult = new SocRolloverResultInfo(rolloverResult);
220         reverseResult.setTypeKey(CourseOfferingSetServiceConstants.ROLLOVER_RESULT_TYPE_KEY);
221         reverseResult.setStateKey(CourseOfferingSetServiceConstants.SUBMITTED_RESULT_STATE_KEY);
222         reverseResult.setOptionKeys(optionKeys);
223 
224         try {
225             reverseResult = this._getSocService().createSocRolloverResult(reverseResult.getTypeKey(), reverseResult, context);
226         } catch (DataValidationErrorException ex) {
227             throw new OperationFailedException("Unexpected", ex);
228         } catch (ReadOnlyException ex) {
229             throw new OperationFailedException("Unexpected", ex);
230         }
231         // create the runner so we can kick it off in another thread
232         CourseOfferingReverseRolloverRunner runner = new CourseOfferingReverseRolloverRunner();
233         runner.setContext(context);
234         runner.setCoService(coService);
235         runner.setSocService(this._getSocService());
236         runner.setCourseService(courseService);
237         runner.setAcalService(acalService);
238         runner.setRolloverResult(rolloverResult);
239         runner.setReverseResult(reverseResult);
240         if (optionKeys.contains(CourseOfferingSetServiceConstants.RUN_SYNCHRONOUSLY_OPTION_KEY)) {
241             runner.run();
242         } else {
243             Thread thread = new Thread(runner);
244             thread.start();
245         }
246         return reverseResult;
247     }
248 
249     @Override
250     public List<String> getCourseOfferingIdsBySoc(String socId, ContextInfo context)
251             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
252             PermissionDeniedException {
253         // THIS IS BASICALLY A SWITCH STATEMENT BASED ON THE TYPE OF THE SOC
254         SocInfo soc = this._getSocService().getSoc(socId, context);
255         // main
256         if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.MAIN_SOC_TYPE_KEY)) {
257             return coService.getCourseOfferingIdsByTerm(soc.getTermId(), Boolean.TRUE, context);
258         }
259         // subject area
260         if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.SUBJECT_AREA_SOC_TYPE_KEY)) {
261             return coService.getCourseOfferingIdsByTermAndSubjectArea(soc.getTermId(), soc.getSubjectArea(), context);
262         }
263         // units content owner
264         if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.UNITS_CONTENT_OWNER_SOC_TYPE_KEY)) {
265             return coService.getCourseOfferingIdsByTermAndUnitsContentOwner(soc.getTermId(), soc.getUnitsContentOwnerId(), context);
266         }
267         throw new OperationFailedException(soc.getTypeKey() + " is an unsupported type for this implementation");
268 //        List<String> list = new ArrayList<String>();
269 //        for (CourseOfferingInfo info : courseOfferingMap.values()) {
270 //            if (socId.equals(info.getSocId())) {
271 //                list.add(info.getId());
272 //            }
273 //        }
274 //        return list;
275     }
276 
277     @Override
278     public Integer deleteCourseOfferingsBySoc(String socId, ContextInfo context)
279             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
280             PermissionDeniedException {
281         // TODO: add bulk ops to CourseOfferingService so this can call them 
282         // to delete all for a term or delete all for a subject area intead of doing it one by one
283         List<String> ids = this.getCourseOfferingIdsBySoc(socId, context);
284         for (String id : ids) {
285             try {
286                 this.coService.deleteCourseOffering(socId, context);
287             } catch (DependentObjectsExistException e) {
288                 throw new OperationFailedException(e.getMessage());
289             }
290         }
291         return ids.size();
292     }
293 
294     @Override
295     public Boolean isCourseOfferingInSoc(String socId, String courseOfferingId, ContextInfo context)
296             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
297             PermissionDeniedException {
298 
299         SocInfo soc = this._getSocService().getSoc(socId, context);
300         CourseOfferingInfo co = this.coService.getCourseOffering(courseOfferingId, context);
301         // main
302         if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.MAIN_SOC_TYPE_KEY)) {
303             if (co.getTermId().equals(soc.getTermId())) {
304                 return true;
305             }
306             // TODO: handle sub-terms  before returning false
307             return false;
308         }
309         // subject area
310         if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.SUBJECT_AREA_SOC_TYPE_KEY)) {
311             if (co.getTermId().equals(soc.getTermId())) {
312                 if (co.getSubjectArea().equals(soc.getSubjectArea())) {
313                     return true;
314                 }
315             }
316             // TODO: handle sub-terms  before returning false
317             return false;
318         }
319         // units content owner
320         if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.UNITS_CONTENT_OWNER_SOC_TYPE_KEY)) {
321             if (co.getTermId().equals(soc.getTermId())) {
322                 if (co.getUnitsContentOwnerOrgIds().equals(soc.getUnitsContentOwnerId())) {
323                     return true;
324                 }
325             }
326             // TODO: handle sub-terms before returning false
327             return false;
328         }
329         // else get all of them and check if in the list
330         List<String> ids = this.getCourseOfferingIdsBySoc(socId, context);
331         return ids.contains(courseOfferingId);
332     }
333 
334     @Override
335     public List<String> getPublishedCourseOfferingIdsBySoc(String socId, ContextInfo context)
336             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
337             PermissionDeniedException {
338         List<String> list = new ArrayList<String>();
339         List<String> list2 = this.getCourseOfferingIdsBySoc(socId, context);
340         for (CourseOfferingInfo info : this.coService.getCourseOfferingsByIds(list2, context)) {
341             // TODO: add the published course offering state to the constants 
342 //            if (info.getStateKey().equals(CourseOfferingServiceConstants.PUBLISHED_STATE_KEY) {
343             list.add(info.getId());
344 //            }
345         }
346         return list;
347     }
348 
349     @Override
350     public List<String> getUnpublishedCourseOfferingIdsBySoc(String socId, ContextInfo context)
351             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
352             PermissionDeniedException {
353         List<String> list = new ArrayList<String>();
354         List<String> list2 = this.getCourseOfferingIdsBySoc(socId, context);
355         for (CourseOfferingInfo info : this.coService.getCourseOfferingsByIds(list2, context)) {
356             // TODO: add the published course offering state to the constants 
357 //            if (info.getStateKey().equals(CourseOfferingServiceConstants.PUBLISHED_STATE_KEY) {
358             list.add(info.getId());
359 //            }
360         }
361         return list;
362     }
363 
364     @Override
365     public List<String> getUnpublishedActivityOfferingIdsBySoc(String socId, ContextInfo context)
366             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
367             PermissionDeniedException {
368         List<String> list = new ArrayList<String>();
369         List<String> list2 = this.getCourseOfferingIdsBySoc(socId, context);
370         for (String coId : list2) {
371             for (ActivityOfferingInfo ao : this.coService.getActivityOfferingsByCourseOffering(coId, context)) {
372                 // TODO: add the published course offering state to the constants 
373 //            if (!ao.getStateKey().equals(CourseOfferingServiceConstants.PUBLISHED_STATE_KEY) {
374                 list.add(ao.getId());
375 //            }
376             }
377         }
378         return list;
379     }
380 
381     @Override
382     public List<String> getUnscheduledActivityOfferingIdsBySoc(String socId, ContextInfo context)
383             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
384             PermissionDeniedException {
385         List<String> list = new ArrayList<String>();
386         List<String> list2 = this.getCourseOfferingIdsBySoc(socId, context);
387         for (String coId : list2) {
388             for (ActivityOfferingInfo ao : this.coService.getActivityOfferingsByCourseOffering(coId, context)) {
389                 // TODO: add the published course offering state to the constants 
390 //            if (!ao.getStateKey().equals(CourseOfferingServiceConstants.SCHEDULED_STATE_KEY) {
391                 list.add(ao.getId());
392 //            }
393             }
394         }
395         return list;
396     }
397 }