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