1
2
3
4
5 package org.kuali.student.enrollment.class2.courseofferingset.service.impl;
6
7 import org.apache.log4j.Logger;
8 import java.util.ArrayList;
9 import java.util.List;
10
11 import org.kuali.student.enrollment.acal.dto.TermInfo;
12 import org.kuali.student.enrollment.acal.service.AcademicCalendarService;
13 import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingInfo;
14 import org.kuali.student.enrollment.courseoffering.dto.FormatOfferingInfo;
15 import org.kuali.student.enrollment.courseoffering.service.CourseOfferingService;
16 import org.kuali.student.enrollment.courseofferingset.dto.SocRolloverResultInfo;
17 import org.kuali.student.enrollment.courseofferingset.dto.SocRolloverResultItemInfo;
18 import org.kuali.student.enrollment.courseofferingset.service.CourseOfferingSetService;
19 import org.kuali.student.r2.lum.course.dto.CourseInfo;
20 import org.kuali.student.r2.lum.course.service.CourseService;
21 import org.kuali.student.r2.common.dto.ContextInfo;
22 import org.kuali.student.r2.common.exceptions.DoesNotExistException;
23 import org.kuali.student.r2.common.exceptions.OperationFailedException;
24 import org.kuali.student.r2.common.util.RichTextHelper;
25 import org.kuali.student.r2.common.util.constants.CourseOfferingSetServiceConstants;
26
27
28
29
30
31 public class CourseOfferingReverseRolloverRunner implements Runnable {
32
33 final static Logger logger = Logger.getLogger(CourseOfferingRolloverRunner.class);
34 private CourseOfferingService coService;
35 private CourseOfferingSetService socService;
36 private CourseService courseService;
37 private AcademicCalendarService acalService;
38 private ContextInfo context;
39 private SocRolloverResultInfo rolloverResult;
40 private SocRolloverResultInfo reverseResult;
41
42 public CourseOfferingService getCoService() {
43 return coService;
44 }
45
46 public void setCoService(CourseOfferingService coService) {
47 this.coService = coService;
48 }
49
50 public CourseOfferingSetService getSocService() {
51 return socService;
52 }
53
54 public void setSocService(CourseOfferingSetService socService) {
55 this.socService = socService;
56 }
57
58 public CourseService getCourseService() {
59 return courseService;
60 }
61
62 public void setCourseService(CourseService courseService) {
63 this.courseService = courseService;
64 }
65
66 public AcademicCalendarService getAcalService() {
67 return acalService;
68 }
69
70 public void setAcalService(AcademicCalendarService acalService) {
71 this.acalService = acalService;
72 }
73
74 public ContextInfo getContext() {
75 return context;
76 }
77
78 public void setContext(ContextInfo context) {
79 this.context = context;
80 }
81
82 public SocRolloverResultInfo getReverseResult() {
83 return reverseResult;
84 }
85
86 public void setReverseResult(SocRolloverResultInfo reverseResult) {
87 this.reverseResult = reverseResult;
88 }
89
90 public SocRolloverResultInfo getRolloverResult() {
91 return rolloverResult;
92 }
93
94 public void setRolloverResult(SocRolloverResultInfo rolloverResult) {
95 this.rolloverResult = rolloverResult;
96 }
97
98 private void loadOptions() {
99 this.justCreates = getBooleanOption(CourseOfferingSetServiceConstants.REVERSE_JUST_CREATES_OPTION_KEY, false);
100 this.logSuccesses = getBooleanOption(CourseOfferingSetServiceConstants.LOG_SUCCESSES_OPTION_KEY, false);
101 this.progressFrequency = getIntOption(CourseOfferingSetServiceConstants.LOG_FREQUENCY_OPTION_KEY_PREFIX, 10);
102 this.haltErrorsMax = getIntOption(CourseOfferingSetServiceConstants.HALT_ERRORS_MAX_OPTION_KEY_PREFIX, 10);
103
104 }
105
106 private boolean justCreates = false;
107 private boolean logSuccesses = false;
108 private int progressFrequency = 100;
109 private int haltErrorsMax = -1;
110
111 private boolean getBooleanOption(String key, boolean defValue) {
112 for (String optionKey : this.reverseResult.getOptionKeys()) {
113 if (optionKey.equals(key)) {
114 return true;
115 }
116 }
117
118 return defValue;
119 }
120
121 private int getIntOption(String keyPrefix, int defValue) {
122 for (String optionKey : this.reverseResult.getOptionKeys()) {
123 if (optionKey.startsWith(keyPrefix)) {
124 return Integer.parseInt(optionKey);
125 }
126 }
127
128 return defValue;
129 }
130
131
132
133
134 @Override
135 public void run() {
136 try {
137 runInternal();
138 } catch (Exception ex) {
139 try {
140 reverseResult = this.socService.getSocRolloverResult(reverseResult.getId(), context);
141 this.reverseResult.setStateKey(CourseOfferingSetServiceConstants.ABORTED_RESULT_STATE_KEY);
142 this.reverseResult.setMessage(new RichTextHelper().fromPlain("Got an unexpected exception running rolloever:\n" +
143 ex.toString()));
144 this.socService.updateSocRolloverResult(reverseResult.getId(), reverseResult, context);
145 } catch (Exception ex1) {
146 logger.fatal(reverseResult, ex);
147 throw new RuntimeException(ex1);
148 }
149 }
150 }
151
152 private void runInternal() throws Exception {
153 this.loadOptions();
154
155 reverseResult.setStateKey(CourseOfferingSetServiceConstants.RUNNING_RESULT_STATE_KEY);
156 this.socService.updateSocRolloverResult(reverseResult.getId(), reverseResult, context);
157
158 List<String> targetCoIds = socService.getCourseOfferingIdsBySoc(this.reverseResult.getTargetSocId(), context);
159 reverseResult.setItemsProcessed(0);
160 reverseResult.setItemsExpected(targetCoIds.size());
161 this.socService.updateSocRolloverResult(reverseResult.getId(), reverseResult, context);
162
163
164 int i = 0;
165 int errors = 0;
166 List<SocRolloverResultItemInfo> items = new ArrayList<SocRolloverResultItemInfo>();
167 for (String targetCoId : targetCoIds) {
168 logger.info("Processing" + targetCoId);
169 try {
170 SocRolloverResultItemInfo item = reverseOneCourseOffering(targetCoId);
171 items.add(item);
172 reportProgressIfModulo(items, i);
173 if (!CourseOfferingSetServiceConstants.SUCCESSFUL_RESULT_ITEM_STATES.contains(item.getStateKey())) {
174 errors++;
175 if (this.haltErrorsMax != -1) {
176 if (errors > this.haltErrorsMax) {
177 throw new OperationFailedException("Too many errors, exceeded the halt threshold: " + errors +
178 " out of " + i + " course offerings rolled over");
179 }
180 }
181 }
182 } catch (Exception ex) {
183
184 logger.fatal("failed while processing the " + i + "th course offering " + targetCoId, ex);
185 throw ex;
186 }
187 i++;
188 }
189 reportProgress(items, i);
190
191 reverseResult = this.socService.getSocRolloverResult(reverseResult.getId(), context);
192 reverseResult.setStateKey(CourseOfferingSetServiceConstants.FINISHED_RESULT_STATE_KEY);
193 this.socService.updateSocRolloverResult(reverseResult.getId(), reverseResult, context);
194 }
195
196 private void reportProgressIfModulo(List<SocRolloverResultItemInfo> items, int i) throws Exception {
197 int modulo = i % progressFrequency;
198 if (modulo != 0) {
199 return;
200 }
201 this.reportProgress(items, i);
202 }
203
204 private void reportProgress(List<SocRolloverResultItemInfo> items, int i) throws Exception {
205 this.socService.updateSocRolloverProgress(reverseResult.getId(), i, context);
206 if (!this.logSuccesses) {
207 stripSuccesses(items);
208 }
209 if (!items.isEmpty()) {
210 this.socService.createSocRolloverResultItems(reverseResult.getId(),
211 CourseOfferingSetServiceConstants.DELETE_RESULT_ITEM_TYPE_KEY,
212 items,
213 context);
214 }
215 items.clear();
216 }
217
218 private void stripSuccesses(List<SocRolloverResultItemInfo> items) {
219 List<SocRolloverResultItemInfo> list = new ArrayList<SocRolloverResultItemInfo>();
220 for (SocRolloverResultItemInfo item : items) {
221 if (!CourseOfferingSetServiceConstants.SUCCESSFUL_RESULT_ITEM_STATES.contains(item.getStateKey())) {
222 list.add(item);
223 }
224 }
225 if (items.size() != list.size()) {
226 items.clear();
227 items.addAll(list);
228 }
229 }
230
231
232
233 private SocRolloverResultItemInfo reverseOneCourseOffering(String coId) throws Exception {
234 if (this.justCreates) {
235 if (!this.wasCreatedInRollover(coId)) {
236 SocRolloverResultItemInfo item = new SocRolloverResultItemInfo();
237 item.setSocRolloverResultId(reverseResult.getId());
238 item.setTypeKey(CourseOfferingSetServiceConstants.DELETE_RESULT_ITEM_TYPE_KEY);
239 item.setStateKey(CourseOfferingSetServiceConstants.INFO_RESULT_ITEM_STATE_KEY);
240 item.setSourceCourseOfferingId(coId);
241 item.setTargetCourseOfferingId(coId);
242 item.setMessage(new RichTextHelper().fromPlain(
243 "Skipped because course offering was not created in the original rollover"));
244 return item;
245 }
246 }
247
248 for (FormatOfferingInfo fo : this.coService.getFormatOfferingsByCourseOffering(coId, context)) {
249 for (ActivityOfferingInfo ao : this.coService.getActivityOfferingsByFormatOffering(fo.getId(), context)) {
250 coService.deleteActivityOffering(ao.getId(), context);
251 }
252 coService.deleteFormatOffering(fo.getId(), context);
253 }
254 this.coService.deleteCourseOffering(coId, context);
255
256 SocRolloverResultItemInfo item = new SocRolloverResultItemInfo();
257 item.setSocRolloverResultId(reverseResult.getId());
258 item.setTypeKey(CourseOfferingSetServiceConstants.DELETE_RESULT_ITEM_TYPE_KEY);
259 item.setStateKey(CourseOfferingSetServiceConstants.DELETED_RESULT_ITEM_STATE_KEY);
260 item.setSourceCourseOfferingId(coId);
261 item.setTargetCourseOfferingId(coId);
262 return item;
263 }
264
265 private boolean wasCreatedInRollover(String coId) throws Exception {
266 List<SocRolloverResultItemInfo> list = this.socService.getSocRolloverResultItemsByResultIdAndTargetCourseOfferingId(
267 rolloverResult.getId(), coId, context);
268 if (list.isEmpty()) {
269 return false;
270 }
271 for (SocRolloverResultItemInfo item : list) {
272 if (coId.equals(item.getTargetCourseOfferingId())) {
273 if (CourseOfferingSetServiceConstants.SUCCESSFUL_RESULT_ITEM_STATES.contains(item.getStateKey())) {
274 if (item.getTypeKey().equals(CourseOfferingSetServiceConstants.CREATE_RESULT_ITEM_TYPE_KEY)) {
275 return true;
276 }
277 }
278 }
279 }
280 return false;
281 }
282
283
284 private CourseInfo getCourse(String courseId) throws Exception {
285 CourseInfo course = null;
286 try {
287 course = courseService.getCourse(courseId, context);
288 } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException e) {
289 throw new DoesNotExistException("The course does not exist. course: " + courseId, e);
290 } catch (Exception e) {
291 throw new OperationFailedException("unxpected trying to get course " + courseId, e);
292 }
293 return course;
294 }
295 }