View Javadoc

1   /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   */
15  package org.kuali.student.lum.lu.ui.course.client.requirements;
16  
17  import java.util.ArrayList;
18  import java.util.HashMap;
19  import java.util.LinkedHashMap;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.kuali.student.common.ui.client.application.Application;
24  import org.kuali.student.common.ui.client.application.KSAsyncCallback;
25  import org.kuali.student.common.ui.client.mvc.Callback;
26  import org.kuali.student.common.ui.client.mvc.Controller;
27  import org.kuali.student.common.ui.client.mvc.DataModel;
28  import org.kuali.student.common.ui.client.mvc.Model;
29  import org.kuali.student.common.ui.client.mvc.ModelRequestCallback;
30  import org.kuali.student.common.ui.client.widgets.dialog.ConfirmationDialog;
31  import org.kuali.student.common.ui.client.widgets.notification.KSNotification;
32  import org.kuali.student.common.ui.client.widgets.notification.KSNotifier;
33  import org.kuali.student.core.statement.ui.client.widgets.rules.RulesUtil;
34  import org.kuali.student.lum.common.client.lu.LUUIConstants;
35  import org.kuali.student.lum.lu.ui.course.client.service.CourseRpcService;
36  import org.kuali.student.lum.lu.ui.course.client.service.CourseRpcServiceAsync;
37  import org.kuali.student.lum.program.client.rpc.StatementRpcService;
38  import org.kuali.student.lum.program.client.rpc.StatementRpcServiceAsync;
39  import org.kuali.student.r1.core.statement.dto.ReqCompFieldInfo;
40  import org.kuali.student.r1.core.statement.dto.ReqComponentInfo;
41  import org.kuali.student.r1.core.statement.dto.StatementTreeViewInfo;
42  import org.kuali.student.r1.core.statement.dto.StatementTypeInfo;
43  
44  import com.google.gwt.core.client.GWT;
45  import com.google.gwt.event.dom.client.ClickEvent;
46  import com.google.gwt.event.dom.client.ClickHandler;
47  import com.google.gwt.user.client.Window;
48  
49  public class CourseRequirementsDataModel {
50  
51      private final CourseRpcServiceAsync courseRemoteService = GWT.create(CourseRpcService.class);
52      private final StatementRpcServiceAsync statementRpcServiceAsync = GWT.create(StatementRpcService.class);
53      private Controller parentController;
54  
55      //keeping track of rules and rule state
56      public enum requirementState {STORED, ADDED, EDITED, DELETED;}
57      private Map<Integer, StatementTreeViewInfo> courseReqInfos = new LinkedHashMap<Integer, StatementTreeViewInfo>();
58      private Map<Integer, StatementTreeViewInfo> origCourseReqInfos = new LinkedHashMap<Integer, StatementTreeViewInfo>();
59      private Map<Integer, requirementState> courseReqState = new HashMap<Integer, requirementState>();
60      private Map<Integer, requirementState> origCourseReqState = new HashMap<Integer, requirementState>();
61      private List<StatementTypeInfo> stmtTypes = new ArrayList<StatementTypeInfo>();
62      private boolean isInitialized = false;
63      private static Integer courseReqIDs = 111111;
64  
65      public CourseRequirementsDataModel(Controller parentController) {
66          this.parentController = parentController;
67      }
68  
69      //retrieve rules based on IDs associated with this course
70      public void retrieveCourseRequirements(String modelId, final Callback<Boolean> onReadyCallback) {
71          //initialize data
72          courseReqInfos = new LinkedHashMap<Integer, StatementTreeViewInfo>();
73          origCourseReqInfos = new LinkedHashMap<Integer, StatementTreeViewInfo>();
74          courseReqState = new HashMap<Integer, requirementState>();
75          origCourseReqState = new HashMap<Integer, requirementState>();
76          stmtTypes = new ArrayList<StatementTypeInfo>();
77          isInitialized = false;
78  
79          parentController.requestModel(modelId, new ModelRequestCallback() {
80  
81              @Override
82              public void onRequestFail(Throwable cause) {
83                  Window.alert(cause.getMessage());
84                  GWT.log("Unable to retrieve model for course requirements view", cause);
85                  onReadyCallback.exec(false);
86              }
87  
88              @Override
89              public void onModelReady(Model model) {
90                  String courseId = ((DataModel)model).getRoot().get("id");
91                  retrieveStatementTypes(courseId, onReadyCallback);
92              }
93          });
94      }
95  
96      public void retrieveStatementTypes(final String courseId, final Callback<Boolean> onReadyCallback) {
97  
98          //retrieve available course requirement types
99              statementRpcServiceAsync.getStatementTypesForStatementTypeForCourse("kuali.statement.type.course", new KSAsyncCallback<List<StatementTypeInfo>>() {
100                 @Override
101                 public void handleFailure(Throwable caught) {
102                     Window.alert(caught.getMessage());
103                     GWT.log("getStatementTypes failed", caught);
104                     onReadyCallback.exec(false);
105                 }
106 
107                 @Override
108                 public void onSuccess(List<StatementTypeInfo> stmtInfoTypes) {
109                     //store the statement types
110                     stmtTypes.clear();
111                     for (StatementTypeInfo stmtInfoType : stmtInfoTypes) {
112                         stmtTypes.add(stmtInfoType);
113                     }
114 
115                     //now retrieve the actual rules
116                     retrieveRules(courseId, onReadyCallback);
117                 }
118             });
119     }
120 
121     private void retrieveRules(String courseId, final Callback<Boolean> onReadyCallback) {
122 
123         //true if no course exists yet
124         if ((courseId == null) || courseId.isEmpty()) {
125             isInitialized = true;
126             onReadyCallback.exec(true);
127             return;
128         }
129 
130         courseRemoteService.getCourseStatements(courseId, CourseRequirementsManageView.RULEEDIT_TEMLATE,
131                                                     CourseRequirementsManageView.TEMLATE_LANGUAGE, new KSAsyncCallback<List<StatementTreeViewInfo>>() {
132             @Override
133             public void handleFailure(Throwable caught) {
134                 Window.alert(caught.getMessage());
135                 GWT.log("getCourseStatements failed", caught);
136                 onReadyCallback.exec(false);
137             }
138 
139             @Override
140             public void onSuccess(List<StatementTreeViewInfo> foundRules) {
141                 //update rules list with new course requirements
142                 origCourseReqInfos.clear();
143                 origCourseReqState.clear();
144                 courseReqInfos.clear();
145                 courseReqState.clear();
146             	if (foundRules != null) {
147             		for (StatementTreeViewInfo foundRule : foundRules) {
148 
149             			if (getStmtTypeInfo(foundRule.getType()) == null) {
150             				// Window.alert("Did not find corresponding statement type for course requirement of type: " + foundRule.getType());
151             				GWT.log("Did not find corresponding statement type for course requirement of type: " + foundRule.getType(), null);
152             			}
153 
154             			origCourseReqInfos.put(courseReqIDs, RulesUtil.clone(foundRule));
155             			origCourseReqState.put(courseReqIDs, requirementState.STORED);
156             			courseReqInfos.put(courseReqIDs, foundRule);
157             			courseReqState.put(courseReqIDs++, requirementState.STORED);
158             		}
159             	}
160 
161                 isInitialized = true;
162                 onReadyCallback.exec(true);
163             }
164         });
165     }
166 
167     public StatementTreeViewInfo updateRules(StatementTreeViewInfo newSubRule, Integer internalCourseReqID, boolean isNewRule) {
168 
169         StatementTreeViewInfo affectedRule = courseReqInfos.get(internalCourseReqID);
170 
171         if (affectedRule == null) {
172             Window.alert("Cannot find course requisite with a statement that has id: '" + newSubRule.getId() + "'");
173             GWT.log("Cannot find course requisite with a statement that has id: '" + newSubRule.getId() + "'", null);
174             return null;
175         }
176 
177         if (courseReqState.get(internalCourseReqID) == CourseRequirementsDataModel.requirementState.STORED) {
178             courseReqState.put(internalCourseReqID, CourseRequirementsDataModel.requirementState.EDITED);
179         }
180 
181         courseReqInfos.put(internalCourseReqID, newSubRule);
182 
183         return newSubRule;
184     }
185 
186     public void updateCourseRequisites(final String courseId, final String courseState, final Callback<List<StatementTreeViewInfo>> callback) {
187         //course proposal has to be in the database before we can save rules
188         if (courseId == null) {
189             final ConfirmationDialog dialog = new ConfirmationDialog(Application.getApplicationContext().getUILabel(
190                     LUUIConstants.COURSE_GROUP_NAME, "submitCourseRequisitesDialogTitle"), Application
191                     .getApplicationContext().getUILabel(LUUIConstants.COURSE_GROUP_NAME,
192                     "submitCourseRequisitesDialogMsg"));
193             dialog.getConfirmButton().addClickHandler(new ClickHandler(){
194                 @Override
195                 public void onClick(ClickEvent event) {
196                     dialog.hide();
197                 }
198             });
199             dialog.show();
200             return;
201         }
202 
203         final List<String> referencedProgReqIds = new ArrayList<String>();
204 
205         courseRemoteService.storeCourseStatements(courseId.toString(), courseState, courseReqState, courseReqInfos, new KSAsyncCallback<Map<Integer, StatementTreeViewInfo>>() {
206             @Override
207             public void handleFailure(Throwable caught) {
208                 Window.alert(caught.getMessage());
209                 GWT.log("storeProgramRequirements failed", caught);
210             }
211             @Override
212             public void onSuccess(Map<Integer, StatementTreeViewInfo> storedRules) {
213 
214                 for (Integer internalProgReqID : storedRules.keySet()) {
215                     StatementTreeViewInfo storedRule = storedRules.get(internalProgReqID);
216                     switch (courseReqState.get(internalProgReqID)) {
217                         case STORED:
218                             //rule was not changed so continue
219                             referencedProgReqIds.add(internalProgReqID.toString());
220                             break;
221                         case ADDED:
222                             referencedProgReqIds.add(storedRule.getId());
223                             courseReqInfos.put(internalProgReqID, storedRule);
224                             origCourseReqInfos.put(internalProgReqID, RulesUtil.clone(storedRule));
225                             origCourseReqState.put(internalProgReqID, requirementState.STORED);
226                             courseReqState.put(internalProgReqID, requirementState.STORED);
227                             break;
228                         case EDITED:
229                             referencedProgReqIds.add(storedRule.getId());
230                             courseReqInfos.put(internalProgReqID, storedRule);
231                             origCourseReqInfos.put(internalProgReqID, RulesUtil.clone(storedRule));
232                             origCourseReqState.put(internalProgReqID, requirementState.STORED);
233                             courseReqState.put(internalProgReqID, requirementState.STORED);
234                             break;
235                         case DELETED:
236                             courseReqInfos.remove(internalProgReqID);
237                             origCourseReqInfos.remove(internalProgReqID);
238                             origCourseReqState.remove(internalProgReqID);
239                             courseReqState.remove(internalProgReqID);
240                             break;
241                         default:
242                             break;
243                     }
244                 }
245 
246                 KSNotifier.add(new KSNotification("Requirements Save Successful", false, 4000));
247                 //MajorManager.getEventBus().fireEvent(new StoreRequirementIDsEvent(referencedProgReqIds));
248                 callback.exec(new ArrayList(storedRules.values()));  //update display widgets
249             }
250         });
251     }
252 
253     public static void stripStatementIds(StatementTreeViewInfo tree) {
254         List<StatementTreeViewInfo> statements = tree.getStatements();
255         List<ReqComponentInfo> reqComponentInfos = tree.getReqComponents();
256 
257         if ((tree.getId() != null) && (tree.getId().indexOf(CourseRequirementsSummaryView.NEW_STMT_TREE_ID)) >= 0) {
258             tree.setId(null);
259         }
260 
261         if ((statements != null) && (statements.size() > 0)) {
262             // retrieve all statements
263             for (StatementTreeViewInfo statement : statements) {
264                 stripStatementIds(statement); // inside set the children of this statementTreeViewInfo
265             }
266         } else if ((reqComponentInfos != null) && (reqComponentInfos.size() > 0)) {
267             // retrieve all req. component LEAFS
268             for (ReqComponentInfo reqComponent : reqComponentInfos) {
269                 if ((reqComponent.getId() != null) && (reqComponent.getId().indexOf(CourseRequirementsSummaryView.NEW_REQ_COMP_ID) >= 0)) {
270                     reqComponent.setId(null);
271                 }
272 
273                 for (ReqCompFieldInfo field : reqComponent.getReqCompFields()) {
274                     field.setId(null);
275                 }
276             }
277         }
278     }
279 
280     public List<StatementTreeViewInfo> getCourseReqInfo(String stmtTypeId) {
281         List<StatementTreeViewInfo> rules = new ArrayList<StatementTreeViewInfo>();
282         for(StatementTreeViewInfo rule : courseReqInfos.values()) {
283             if (rule.getType().equals(stmtTypeId)) {
284                 rules.add(rule);
285             }
286         }
287         return rules;
288     }
289 
290     public Integer getInternalCourseReqID(StatementTreeViewInfo rule) {
291         for(Integer key : courseReqInfos.keySet()) {
292             if (courseReqInfos.get(key) ==  rule) {
293                 return key;
294             }
295         }
296 
297         Window.alert("Problem retrieving key for course requisite: " +  rule.getId());
298         GWT.log("Problem retrieving key for course requisite: " +  rule.getId(), null);
299 
300         return null;
301     }
302 
303     public StatementTypeInfo getStmtTypeInfo(String stmtTypeId) {
304         for (StatementTypeInfo stmtInfo : stmtTypes) {
305             if (stmtInfo.getId().equals(stmtTypeId)) {
306                 return stmtInfo;
307             }
308         }
309 
310         try {
311 
312         Window.alert("Did not find StatementTypeInfo based on typeA: " + stmtTypeId + new Throwable().getStackTrace().toString());
313         GWT.log("Did not find StatementTypeInfo based on type: " + stmtTypeId + new Throwable().getStackTrace());
314 
315 
316         throw new Exception();
317 
318         } catch (Exception e) {
319             Window.alert("Exception" + e.getStackTrace().toString());
320         }
321         return null;
322     }
323 
324     public void deleteRule(Integer internalProgReqID) {
325         if (courseReqState.get(internalProgReqID) == requirementState.ADDED) {
326             //user added a rule, didn't save it and now wants to delete it
327             courseReqState.remove(internalProgReqID);
328             courseReqInfos.remove(internalProgReqID);
329         } else {
330             markRuleAsDeleted(internalProgReqID);
331         }
332     }
333 
334     public void addRule(StatementTreeViewInfo rule) {
335         courseReqInfos.put(courseReqIDs, rule);
336         courseReqState.put(courseReqIDs++, requirementState.ADDED);
337     }
338 
339     public void updateRule(Integer internalProgReqID, StatementTreeViewInfo rule) {
340         courseReqInfos.put(internalProgReqID, rule);
341         markRuleAsEdited(internalProgReqID);
342     }
343 
344     public void markRuleAsDeleted(Integer internalCourseReqID) {
345         if ((courseReqState.get(internalCourseReqID) == requirementState.STORED) ||
346             (courseReqState.get(internalCourseReqID) == requirementState.EDITED)) {
347             courseReqState.put(internalCourseReqID, requirementState.DELETED);
348         }
349     }
350 
351     public void markRuleAsEdited(Integer internalCourseReqID) {
352         if (courseReqState.get(internalCourseReqID) == requirementState.STORED) {
353             courseReqState.put(internalCourseReqID, requirementState.EDITED);
354         }
355     }
356 
357     public String getStmtTypeName(String stmtTypeId) {
358         String name = getStmtTypeInfo(stmtTypeId).getName();
359         return (name == null ? "" : name);
360     }
361 
362     public boolean isRuleExists(String stmtTypeId) {
363         boolean showNoRuleText = true;
364         for(StatementTreeViewInfo ruleInfo : courseReqInfos.values()) {
365             if ((ruleInfo.getType().equals(stmtTypeId)) && (courseReqState.get(getInternalCourseReqID(ruleInfo)) != requirementState.DELETED)) {
366                 showNoRuleText = false;
367             }
368         }
369         return showNoRuleText;
370     }
371 
372     public void removeEmptyRules() {
373         for(StatementTreeViewInfo rule : courseReqInfos.values()) {
374             if (isEmpty(rule)) {
375                 Integer ruleId = getInternalCourseReqID(rule);
376                 courseReqInfos.remove(ruleId);
377                 courseReqState.remove(ruleId);
378             }
379         }
380     }
381 
382     public boolean isDirty() {
383 
384         if (courseReqState.size() != origCourseReqState.size()) {
385             return true;
386         }
387 
388         for(Integer key : courseReqState.keySet()) {
389             if (!courseReqState.get(key).equals(origCourseReqState.get(key))) {
390                 return true;
391             }
392         }
393 
394         return false;
395     }
396 
397     public void revertRuleChanges() {
398         courseReqInfos = new HashMap<Integer, StatementTreeViewInfo>();
399         courseReqState = new HashMap<Integer, requirementState>();
400         for(Integer key : origCourseReqInfos.keySet()) {
401             courseReqInfos.put(key, RulesUtil.clone(origCourseReqInfos.get(key)));
402             courseReqState.put(key, origCourseReqState.get(key));
403         }
404     }
405 
406     public StatementTreeViewInfo getRule(Integer internalCourseReqID) {
407         return courseReqInfos.get(internalCourseReqID);
408     }
409 
410     public static void getStatementTypes(final Callback<List<StatementTypeInfo>> onReadyCallback) {
411 
412         StatementRpcServiceAsync statementRpcServiceAsync = GWT.create(StatementRpcService.class);
413 
414         //retrieve available course requirement types
415         statementRpcServiceAsync.getStatementTypesForStatementTypeForCourse("kuali.statement.type.course", new KSAsyncCallback<List<StatementTypeInfo>>() {
416             @Override
417             public void handleFailure(Throwable caught) {
418 	            Window.alert(caught.getMessage());
419 	            GWT.log("getStatementTypes failed", caught);
420                 onReadyCallback.exec(new ArrayList<StatementTypeInfo>());
421             }
422 
423             @Override
424             public void onSuccess(List<StatementTypeInfo> stmtInfoTypes) {
425                 //store the statement types
426                 List<StatementTypeInfo> stmtTypes = new ArrayList<StatementTypeInfo>();
427                 for (StatementTypeInfo stmtInfoType : stmtInfoTypes) {
428                     stmtTypes.add(stmtInfoType);
429                 }
430                 onReadyCallback.exec(stmtTypes);
431             }
432         });
433     }
434 
435     public static boolean isEmpty(StatementTreeViewInfo rule) {
436         return (((rule.getStatements() == null) || rule.getStatements().isEmpty()) && ((rule.getReqComponents() == null) || rule.getReqComponents().isEmpty()));
437     }
438 
439     public boolean isInitialized() {
440         return isInitialized;
441     }
442 
443     public void setInitialized(boolean initialized) {
444         isInitialized = initialized;
445     }
446 
447     public List<StatementTypeInfo> getStmtTypes() {
448         return stmtTypes;
449     }
450 }