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