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  
16  package org.kuali.student.lum.program.client.requirements;
17  
18  import java.util.HashMap;
19  import java.util.List;
20  import java.util.Map;
21  
22  import org.kuali.student.r1.common.assembly.data.Metadata;
23  import org.kuali.student.r1.core.statement.dto.ReqCompFieldTypeInfo;
24  import org.kuali.student.r1.core.statement.dto.ReqComponentTypeInfo;
25  import org.kuali.student.r1.core.statement.dto.ReqCompFieldInfo;
26  import org.kuali.student.r1.core.statement.dto.ReqComponentInfo;
27  import org.kuali.student.r1.core.statement.dto.StatementOperatorTypeKey;
28  import org.kuali.student.r1.core.statement.dto.StatementTreeViewInfo;
29  import org.kuali.student.r2.core.versionmanagement.dto.VersionDisplayInfo;
30  
31  import org.kuali.student.common.ui.client.application.Application;
32  import org.kuali.student.common.ui.client.application.KSAsyncCallback;
33  import org.kuali.student.common.ui.client.configurable.mvc.SectionTitle;
34  import org.kuali.student.common.ui.client.configurable.mvc.views.VerticalSectionView;
35  import org.kuali.student.common.ui.client.mvc.Callback;
36  import org.kuali.student.common.ui.client.mvc.View;
37  import org.kuali.student.common.ui.client.service.MetadataRpcService;
38  import org.kuali.student.common.ui.client.service.MetadataRpcServiceAsync;
39  import org.kuali.student.common.ui.client.widgets.KSProgressIndicator;
40  import org.kuali.student.common.ui.client.widgets.buttongroups.ButtonEnumerations;
41  import org.kuali.student.common.ui.client.widgets.field.layout.button.ActionCancelGroup;
42  import org.kuali.student.common.ui.client.widgets.progress.BlockingTask;
43  import org.kuali.student.common.ui.client.widgets.progress.KSBlockingProgressIndicator;
44  import org.kuali.student.core.statement.ui.client.widgets.rules.ReqCompEditWidget;
45  import org.kuali.student.core.statement.ui.client.widgets.rules.ReqComponentInfoUi;
46  import org.kuali.student.core.statement.ui.client.widgets.rules.RuleManageWidget;
47  import org.kuali.student.core.statement.ui.client.widgets.rules.RulesUtil;
48  import org.kuali.student.lum.common.client.widgets.*;
49  import org.kuali.student.r2.lum.clu.dto.CluInfo;
50  import org.kuali.student.lum.program.client.ProgramMsgConstants;
51  import org.kuali.student.lum.program.client.rpc.StatementRpcService;
52  import org.kuali.student.lum.program.client.rpc.StatementRpcServiceAsync;
53  import org.kuali.student.r2.lum.program.dto.ProgramRequirementInfo;
54  
55  
56  import com.google.gwt.core.client.GWT;
57  import com.google.gwt.user.client.Window;
58  import com.google.gwt.user.client.rpc.AsyncCallback;
59  import com.google.gwt.user.client.ui.SimplePanel;
60  import com.google.gwt.user.client.ui.VerticalPanel;
61  import com.google.gwt.user.client.ui.Widget;
62  
63  public class ProgramRequirementsManageView extends VerticalSectionView {
64  
65      private StatementRpcServiceAsync statementRpcServiceAsync = GWT.create(StatementRpcService.class);
66      private MetadataRpcServiceAsync metadataServiceAsync = GWT.create(MetadataRpcService.class);   
67  
68      protected static final String TEMLATE_LANGUAGE = "en";
69      protected static final String RULEEDIT_TEMLATE = "KUALI.RULE";
70      protected static final String RULEPREVIEW_TEMLATE = RULEEDIT_TEMLATE + ".PREVIEW";
71      protected static final String COMPOSITION_TEMLATE = "KUALI.RULE.COMPOSITION";
72      private static final String LU_NAMESPACE = "http://student.kuali.org/wsdl/lu";
73      private static final String CLU_NAMESPACE_URI = "{" + LU_NAMESPACE + "}cluInfo";
74  
75      private ProgramRequirementsViewController parentController;
76  
77      //view's widgets
78      private VerticalPanel layout = new VerticalPanel();
79      private ReqCompEditWidget editReqCompWidget;
80      private RuleManageWidget ruleManageWidget;
81      private SimplePanel twiddlerPanel = new SimplePanel();
82      private ActionCancelGroup actionCancelButtons = new ActionCancelGroup(ButtonEnumerations.SaveCancelEnum.SAVE, ButtonEnumerations.SaveCancelEnum.CANCEL);
83  
84      //view's data
85      private StatementTreeViewInfo rule = null;
86      private boolean isInitialized = false;
87      private boolean isNewRule = false;
88      private ReqComponentInfo editedReqCompInfo = null;
89      private Integer internalProgReqID = null;
90      private String originalReqCompNL;
91      private String originalLogicExpression;
92  
93      //   private boolean isLocalDirty = false;
94  	private BlockingTask creatingRuleTask = new BlockingTask("Creating Rule");
95  
96      public ProgramRequirementsManageView(ProgramRequirementsViewController parentController, Enum<?> viewEnum, String name, String modelId) {
97          super(viewEnum, name, modelId);
98          this.parentController = parentController;
99      }
100 
101     @Override
102     public void beforeShow(final Callback<Boolean> onReadyCallback) {
103 
104         retrieveAndSetupReqCompTypes(); //TODO cache it for each statement type?
105         if (!isInitialized) {
106             setupHandlers();
107             draw();
108             isInitialized = true;
109         }
110 
111         onReadyCallback.exec(true);
112     }
113 
114     private void setupHandlers() {
115         editReqCompWidget.setReqCompConfirmButtonClickCallback(actionButtonClickedReqCompCallback);
116     // 
117         editReqCompWidget.setNewReqCompSelectedCallbackCallback(newReqCompSelectedCallbackCallback);
118 
119         editReqCompWidget.setRetrieveCompositionTemplateCallback(retrieveCompositionTemplateCallback);
120         editReqCompWidget.setRetrieveFieldsMetadataCallback(retrieveFieldsMetadataCallback);
121         editReqCompWidget.setRetrieveCustomWidgetCallback(retrieveCustomWidgetCallback);
122         ruleManageWidget.setReqCompEditButtonClickCallback(editReqCompCallback);
123     }
124 
125     private void draw() {
126 
127         remove(layout);
128         layout.clear();
129 
130         //STEP 1
131         SectionTitle title = SectionTitle.generateH3Title(getLabel(ProgramMsgConstants.PROGRAMREQUIREMENTS_MANAGEVIEWPAGESTEP1TITLE));
132         title.setStyleName("KS-Program-Requirements-Manage-Step-header1");  //make the header orange
133         layout.add(title);
134 
135         layout.add(editReqCompWidget);
136 
137         //STEP 2
138         title = SectionTitle.generateH3Title(getLabel(ProgramMsgConstants.PROGRAMREQUIREMENTS_MANAGEVIEWPAGESTEP2TITLE));
139         title.setStyleName("KS-Program-Requirements-Manage-Step-header2");  //make the header orange
140         layout.add(title);
141         layout.add(ruleManageWidget);
142 
143         //add progressive indicator when rules are being simplified
144         KSProgressIndicator twiddler = new KSProgressIndicator();
145         twiddler.setVisible(false);
146         twiddlerPanel.setWidget(twiddler);
147         layout.add(twiddlerPanel);
148 
149         addWidget(layout);
150 
151         displaySaveButton();
152     }
153 
154     private void displaySaveButton() {
155         actionCancelButtons.addStyleName("KS-Program-Requirements-Save-Button");
156         actionCancelButtons.addCallback(new Callback<ButtonEnumerations.ButtonEnum>(){
157              @Override
158             public void exec(final ButtonEnumerations.ButtonEnum result) {
159                 parentController.getView(ProgramRequirementsViewController.ProgramRequirementsViews.PREVIEW, new Callback<View>(){
160                     @Override
161                     public void exec(View preview) {
162                         //update rules data model and summary view
163                     	         if ((result == ButtonEnumerations.SaveCancelEnum.SAVE) && isDirty()) {
164                             ProgramRequirementInfo affectedRule = ((ProgramRequirementsSummaryView) preview).getRules().updateRules(getRuleTree(), internalProgReqID, isNewRule());
165                             ((ProgramRequirementsSummaryView) preview).updateRequirementWidgets(affectedRule);                            
166                         }
167                         isDirty = false;
168                         parentController.showView(ProgramRequirementsViewController.ProgramRequirementsViews.PREVIEW);
169                     }
170                 });
171              }
172         });
173         addWidget(actionCancelButtons);
174     }
175 
176     // called by requirement display widget when user wants to edit or add a sub-rule
177     public void setRuleTree(StatementTreeViewInfo stmtTreeInfo, boolean newRuleFlag, Integer internalProgReqID) {
178 
179         if (!isInitialized) {
180             editReqCompWidget = new ReqCompEditWidget(ProgramRequirementsSummaryView.NEW_REQ_COMP_ID);
181             ruleManageWidget = new RuleManageWidget();
182             ruleManageWidget.setReqCompEditButtonClickCallback(editReqCompCallback);
183             ruleManageWidget.setRuleChangedButtonClickCallback(ruleChangedCallback);
184         }
185 
186         this.internalProgReqID = internalProgReqID;
187         editedReqCompInfo = null;
188         rule = RulesUtil.clone(stmtTreeInfo);
189         isNewRule = newRuleFlag;
190         originalReqCompNL = getAllReqCompNLs();
191 
192         //update screen elements
193         editReqCompWidget.setupNewReqComp();
194         ruleManageWidget.redraw(rule, false);
195        // originalLogicExpression = ruleManageWidget.getLogicExpression();
196     }
197 
198     //retrieve the latest version from rule table widget and update the local copy
199     public StatementTreeViewInfo getRuleTree() {
200         rule = ruleManageWidget.getStatementTreeViewInfo();
201         return rule;
202     }
203 
204     public boolean isNewRule() {
205         return isNewRule;
206     }
207 
208     //called when user clicked on rule 'edit' link
209     protected Callback<ReqComponentInfo> editReqCompCallback = new Callback<ReqComponentInfo>(){
210         public void exec(ReqComponentInfo reqComp) {
211             setEnabled(false);
212        
213             editReqCompWidget.setupExistingReqComp(reqComp);
214             editedReqCompInfo = reqComp;
215         }
216     };
217 
218     protected Callback<Boolean> ruleChangedCallback = new Callback<Boolean>(){
219         public void exec(Boolean ruleChanged) {
220             actionCancelButtons.getButton(ButtonEnumerations.SaveCancelEnum.SAVE).setEnabled(ruleChanged);
221         }
222     };
223 
224     protected void setEnabled(boolean enabled) {
225         ruleManageWidget.setEnabled(enabled);
226         actionCancelButtons.getButton(ButtonEnumerations.SaveCancelEnum.SAVE).setEnabled(enabled);
227     }
228 
229     @Override
230     public boolean isDirty() {
231         if (!isInitialized) {
232             return false;
233         }
234 
235         //TODO until we figure out how to detect changes, always return true
236         return true;
237 
238         //first check logic expression
239 //        if (!ruleManageWidget.getLogicExpression().equals(originalLogicExpression)) {
240 //            return true;
241 //        }
242 
243         //next check NL for req. components
244       //  if ((originalNL == null) && (rule.getNaturalLanguageTranslation() == null)) {
245       //      return !ruleManageWidget.getLogicExpression().equals(originalLogicExpression);
246       //  }
247         //TODO how to check whether rule changed or not?
248        // !(ruleManageWidget.getLogicExpression().equals(originalLogicExpression) && getAllReqCompNLs().equals(originalReqCompNL));
249     }
250 
251     private String getAllReqCompNLs() {
252     	StringBuilder NL = new StringBuilder();
253         for (StatementTreeViewInfo tree : rule.getStatements()) {
254             for (ReqComponentInfo reqComp : tree.getReqComponents()) {
255                 NL.append(reqComp.getNaturalLanguageTranslation());
256             }
257         }
258         return NL.toString();
259     }
260 
261     //called when user clicks 'Add Rule' or 'Update Rule' when editing a req. component
262     protected Callback<ReqComponentInfoUi> actionButtonClickedReqCompCallback = new Callback<ReqComponentInfoUi>(){
263         public void exec(final ReqComponentInfoUi reqComp) {
264 
265             editReqCompWidget.setupNewReqComp();
266             setEnabled(true);
267 
268             //true if user cancel adding/editing req. component
269             if (reqComp == null) {
270                 return;
271             }
272 
273             KSBlockingProgressIndicator.addTask(creatingRuleTask);
274 
275             //1. update NL for the req. component
276             statementRpcServiceAsync.translateReqComponentToNLs(reqComp, new String[]{RULEEDIT_TEMLATE,RULEPREVIEW_TEMLATE}, TEMLATE_LANGUAGE, new KSAsyncCallback<List<String>>() {
277                 public void handleFailure(Throwable caught) {
278                     KSBlockingProgressIndicator.removeTask(creatingRuleTask);
279                     Window.alert(caught.getMessage());
280                     GWT.log("translateReqComponentToNL failed", caught);
281                }
282 
283                 public void onSuccess(final List<String> reqCompNL) {
284 
285                     reqComp.setNaturalLanguageTranslation(reqCompNL.get(0));
286                     reqComp.setPreviewNaturalLanguageTranslation(reqCompNL.get(1));
287 
288                     //2. add / update req. component
289                     rule = ruleManageWidget.getStatementTreeViewInfo();  //TODO ?
290 
291                     if (editedReqCompInfo == null) {  //add req. component
292                         if (rule.getStatements() != null && !rule.getStatements().isEmpty()) {
293                             StatementTreeViewInfo newStatementTreeViewInfo = new StatementTreeViewInfo();
294                             newStatementTreeViewInfo.setId(ProgramRequirementsSummaryView.NEW_STMT_TREE_ID + Integer.toString(ProgramRequirementsSummaryView.tempStmtTreeID++));
295                             newStatementTreeViewInfo.setOperator(rule.getStatements().get(0).getOperator());
296                             newStatementTreeViewInfo.getReqComponents().add(reqComp);
297                             rule.getStatements().add(newStatementTreeViewInfo);
298                         } else {
299                         	rule.getReqComponents().add(reqComp);
300                             //set default operator between req. components of the rule
301                          if (rule.getOperator() == null) {                               
302                         	 rule.setOperator(StatementOperatorTypeKey.AND);
303                           }
304                         }
305                     } else {    //update req. component
306                         editedReqCompInfo.setNaturalLanguageTranslation(reqComp.getNaturalLanguageTranslation());
307                                                 
308                         editedReqCompInfo.setReqCompFields(reqComp.getReqCompFields());
309                         editedReqCompInfo.setType(reqComp.getType());
310                         editedReqCompInfo = null;  //de-reference from existing req. component
311                     }
312 
313                     ruleManageWidget.redraw(rule, true);
314                     KSBlockingProgressIndicator.removeTask(creatingRuleTask);
315                 }
316             });
317         }
318     };
319 
320     //called when user selects a rule type in the editor
321     protected Callback<ReqComponentInfo> newReqCompSelectedCallbackCallback = new Callback<ReqComponentInfo>(){
322         public void exec(final ReqComponentInfo reqComp) {
323             setEnabled(false);
324         }
325     };
326 
327     private void retrieveAndSetupReqCompTypes() {
328     
329        statementRpcServiceAsync.getReqComponentTypesForStatementType(rule.getType(), new KSAsyncCallback<List<ReqComponentTypeInfo>>() {
330             public void handleFailure(Throwable cause) {
331             	GWT.log("Failed to get req. component types for statement of type:" + rule.getType(), cause);
332             	Window.alert("Failed to get req. component types for statement of type:" + rule.getType());
333             }
334 
335             public void onSuccess(final List<ReqComponentTypeInfo> reqComponentTypeInfoList) {
336                 if (reqComponentTypeInfoList == null || reqComponentTypeInfoList.size() == 0) {
337                     GWT.log("Missing Requirement Component Types", null);
338                     Window.alert("Missing Requirement Component Types");
339                     return;
340                 }
341                 editReqCompWidget.setReqCompList(reqComponentTypeInfoList);
342                 editReqCompWidget.setCustomWidgets(getCustomWidgets(reqComponentTypeInfoList));                
343             }
344         });
345     }
346 
347     private Map<String, Widget> getCustomWidgets(List<ReqComponentTypeInfo> reqComponentTypeInfoList) {
348         Map<String, Widget> customWidgets = new HashMap<String, Widget>();
349 
350         for (ReqComponentTypeInfo reqCompTypeInfo : reqComponentTypeInfoList) {
351             for (ReqCompFieldTypeInfo fieldTypeInfo : reqCompTypeInfo.getReqCompFieldTypeInfos()) {
352                 if (RulesUtil.isGradeWidget(fieldTypeInfo.getId())) {
353                     customWidgets.put("kuali.reqComponent.field.type.grade.id", new GradeWidget());
354                 } else if (RulesUtil.isCourseWidget(fieldTypeInfo.getId())) {
355 
356                     final CourseWidget courseWidget = GWT.create(CourseWidget.class);
357                     
358                     courseWidget.addGetCluNameCallback(new Callback() {
359 
360                         @Override
361                         public void exec(Object id) {
362 
363                             statementRpcServiceAsync.getCurrentVersion(CLU_NAMESPACE_URI, (String)id, new AsyncCallback<VersionDisplayInfo>() {
364                                 @Override
365                                 public void onFailure(Throwable throwable) {
366                                     Window.alert(throwable.getMessage());
367                                     GWT.log("Failed to retrieve clu for id: '" +  "'", throwable);
368                                 }
369 
370                                 @Override
371                                 public void onSuccess(final VersionDisplayInfo versionInfo) {
372                                     statementRpcServiceAsync.getClu(versionInfo.getId(), new AsyncCallback<CluInfo>() {
373                                         @Override
374                                         public void onFailure(Throwable throwable) {
375                                             Window.alert(throwable.getMessage());
376                                             GWT.log("Failed to retrieve clu", throwable);
377                                         }
378 
379                                         @Override
380                                         public void onSuccess(CluInfo cluInfo) {
381                                             courseWidget.setLabelContent(cluInfo.getVersion().getVersionIndId(), cluInfo.getOfficialIdentifier().getCode());
382                                         }
383                                     });
384                                 }
385                             });
386 
387 
388                         }
389                     });
390 
391                     customWidgets.put("kuali.reqComponent.field.type.course.clu.id", courseWidget);
392                 } else if (RulesUtil.isProgramWidget(fieldTypeInfo.getId())) {
393                     final ProgramWidget programWidget = new ProgramWidget();
394 
395                     programWidget.addGetCluNameCallback(new Callback() {
396 
397                         @Override
398                         public void exec(Object id) {
399 
400                             statementRpcServiceAsync.getCurrentVersion(CLU_NAMESPACE_URI, (String)id, new AsyncCallback<VersionDisplayInfo>() {
401                                 @Override
402                                 public void onFailure(Throwable throwable) {
403                                     Window.alert(throwable.getMessage());
404                                     GWT.log("Failed to retrieve clu for id: '" +  "'", throwable);
405                                 }
406 
407                                 @Override
408                                 public void onSuccess(final VersionDisplayInfo versionInfo) {
409                                     statementRpcServiceAsync.getClu(versionInfo.getId(), new AsyncCallback<CluInfo>() {
410                                         @Override
411                                         public void onFailure(Throwable throwable) {
412                                             Window.alert(throwable.getMessage());
413                                             GWT.log("Failed to retrieve clu", throwable);
414                                         }
415 
416                                         @Override
417                                         public void onSuccess(CluInfo cluInfo) {
418                                             programWidget.setLabelContent(cluInfo.getVersion().getVersionIndId(), cluInfo.getOfficialIdentifier().getCode());
419                                         }
420                                     });
421                                 }
422                             });
423                         }
424                     });
425 
426                     customWidgets.put("kuali.reqComponent.field.type.program.clu.id", programWidget);
427                 }
428             }
429         }
430         return customWidgets;
431     }
432 
433     //called when user selects a rule type in the rule editor
434      protected Callback<ReqComponentInfo> retrieveCompositionTemplateCallback = new Callback<ReqComponentInfo>(){
435        public void exec(final ReqComponentInfo reqComp) {
436     	   
437     	   statementRpcServiceAsync.translateReqComponentToNL(reqComp, COMPOSITION_TEMLATE, TEMLATE_LANGUAGE, new KSAsyncCallback<String>() {
438                 public void handleFailure(Throwable caught) {
439                     Window.alert(caught.getMessage());
440                     GWT.log("translateReqComponentToNL failed for req. comp. type: '" + reqComp.getType() + "'",caught);
441                 }
442 
443                 public void onSuccess(final String compositionTemplate) {
444                     editReqCompWidget.displayFieldsStart(compositionTemplate);
445                 }
446             });
447         }
448     };
449 
450     protected Callback<List<String>> retrieveFieldsMetadataCallback = new Callback<List<String>>(){
451         public void exec(final List<String> fieldTypes) {
452 
453             if (fieldTypes.contains("kuali.reqComponent.field.type.grade.id")) {
454                 fieldTypes.add("kuali.reqComponent.field.type.gradeType.id");
455             }
456 
457             metadataServiceAsync.getMetadataList(ReqCompFieldInfo.class.getName(), fieldTypes, null, new KSAsyncCallback<List<Metadata>>() {
458                 public void handleFailure(Throwable caught) {
459                     Window.alert(caught.getMessage());
460                     GWT.log("getMetadataList failed for req. comp. types: '" + fieldTypes.toString() + "'",caught);
461                 }
462 
463                 public void onSuccess(final List<Metadata> metadataList) {
464                     editReqCompWidget.displayFieldsEnd(metadataList);
465                 }
466             });
467         }
468     };
469 
470     protected Callback<String> retrieveCustomWidgetCallback = new Callback<String>(){
471         public void exec(final String fieldType) {
472             if (RulesUtil.isCluSetWidget(fieldType)) {
473                 String clusetType = "kuali.cluSet.type.Course";
474                 if (fieldType.toLowerCase().indexOf("program") > 0) {
475                     clusetType = "kuali.cluSet.type.Program";
476                 }
477                 editReqCompWidget.displayCustomWidget(fieldType, new BuildCluSetWidget(new CluSetRetrieverImpl(), clusetType, false));
478             }
479         }
480     };
481 
482     public Integer getInternalProgReqID() {
483         return internalProgReqID;
484     }
485     
486     private String getLabel(String messageKey) {
487         return Application.getApplicationContext().getUILabel(ProgramMsgConstants.PROGRAM_MSG_GROUP, messageKey);
488     }
489 }