1 /** 2 * @author Andrew 3 */ 4 5 Monk.component.WorksetTreeComponent = function(args) { 6 7 this.loadDialog = null; 8 9 this.saveDialog = null; 10 11 this.cachedData = null; 12 13 // following variables are used by the updateTree method 14 // they're properties of the component so that they're accessible 15 16 // temp hash of nodes to use for checking off tree nodes 17 this.tempHash = []; 18 19 // temp storage for nodes that were checked 20 this.tempNodes = []; 21 22 // temp hash to keep track of what collections the workset contains 23 this.tempCollectionsList = []; 24 25 Monk.component.WorksetTreeComponent.superclass.constructor.call(this, args); 26 } 27 28 Workbench.extend(Monk.component.WorksetTreeComponent, Workbench.component.Component, { 29 30 label : "Workset Tree Manager", 31 description : "For managing worksets of text chunks in Monk.", 32 "window" : this.window, 33 34 handle : function(monkEvent, data) { 35 if (monkEvent.instanceOf(Workbench.event.ComponentLoaded)) { 36 if (monkEvent.component == this) { 37 var data = Monk.component.dataManager.getWorklist(); 38 var chunks = []; 39 for (var chunkId in data) { 40 var chunk = data[chunkId]; 41 chunk.id = chunkId; 42 chunks.push(chunk); 43 } 44 if (chunks.length > 0) { 45 this.cachedData = chunks; 46 } 47 } 48 } else if (monkEvent.instanceOf(Monk.event.workset.WorksetLoaded)) { 49 var worklist = Monk.component.dataManager.getWorklist(); 50 this.updateTree(worklist); 51 } else if (monkEvent.instanceOf(Monk.event.workset.WorksetCreated)) { 52 this.saveDialog.hide(); 53 } else if (monkEvent.instanceOf(Monk.event.workset.WorksetReset)) { 54 // clear checks 55 this.updateTree([]); 56 } 57 }, 58 59 init : function() { 60 if (this.tree == null) { 61 var metaData = Monk.data.collection.getMetaDataHierarchy(); 62 // customize the nodes for this tree: add a uiProvider 63 for (var i = 0; i < metaData.length; i++) { 64 var collection = metaData[i]; 65 collection.uiProvider = Ext.tree.ColumnNodeUI; 66 for (var j = 0; j < collection.children.length; j++) { 67 var node = collection.children[j]; 68 node.uiProvider = Ext.tree.ColumnNodeUI; 69 node.userRating = ''; 70 node.systemRating = ''; 71 } 72 } 73 this.initializeTree(metaData); 74 if (this.cachedData != null) { 75 this.updateTree(this.cachedData); 76 this.cachedData = null; 77 } 78 } 79 }, 80 81 /** 82 * Update the tree with a set of nodes to have checked 83 * (all others will be unchecked) 84 * @param {Object} worklist The nodes that should be checked 85 */ 86 updateTree : function(worklist) { 87 var maskedElement = Ext.get("worksetTree"); 88 maskedElement.mask("working..."); 89 90 this.updating = true; 91 92 this.tempCollectionsList = {}; 93 this.tempHash = {}; 94 this.tempNodes = []; 95 96 for (var chunkId in worklist) { 97 var collectionId = worklist[chunkId].collectionId; 98 if (this.tempCollectionsList[collectionId] == undefined) { 99 this.tempCollectionsList[collectionId] = true; 100 } 101 } 102 103 this.tempHash = worklist; 104 105 this.recursiveNodeUpdater = function(node){ 106 var collection = node.attributes.collectionId; 107 var tokens = node.attributes.id.split('-'); 108 if (tokens.length >= 2) { 109 // it's a work or workpart 110 111 // check if there are workset workparts within this node 112 var expand = false; 113 for (var chunk in this.tempHash) { 114 // append a dash to the node ID to prevent false positives 115 // the test workparts against it 116 if (chunk.indexOf(node.attributes.id+'-') != -1) { 117 expand = true; 118 break; 119 } 120 } 121 if (expand) { 122 if (!node.isExpanded()) { 123 node.expand(false, false, function(node) { 124 node.eachChild( 125 this.worksetTreeComponent.recursiveNodeUpdater, 126 this.worksetTreeComponent 127 ); 128 }); 129 } else { 130 } 131 } 132 133 if (node.getUI().isChecked() && !this.tempHash[node.attributes.id]) { 134 node.getUI().setColumnValue(0, ''); 135 node.getUI().setColumnValue(1, ''); 136 node.getUI().toggleCheck(false); 137 } else if (node.getUI().isChecked() && this.tempHash[node.attributes.id]) { 138 var data = this.tempHash[node.attributes.id]; 139 if (data.systemRating) node.getUI().setColumnValue(0, data.systemRating); 140 if (data.userRating) node.getUI().setColumnValue(1, data.userRating); 141 // node's already checked so remove it from the list 142 delete this.tempHash[node.attributes.id]; 143 } else if (!node.getUI().isChecked() && this.tempHash[node.attributes.id]) { 144 var data = this.tempHash[node.attributes.id]; 145 if (data.systemRating) node.getUI().setColumnValue(0, data.systemRating); 146 if (data.userRating) node.getUI().setColumnValue(1, data.userRating); 147 148 node.getUI().toggleCheck(true); 149 this.tempNodes.push({ 150 id: node.attributes.id, 151 collectionId: node.attributes.collectionId, 152 text: node.text, 153 chunkType: node.attributes.chunkType 154 }); 155 // remove nodes we've taken care of 156 delete this.tempHash[node.attributes.id]; 157 } else { 158 node.getUI().setColumnValue(0, ''); 159 node.getUI().setColumnValue(1, ''); 160 } 161 162 if (this.updating) { 163 var itemCount = 0; 164 for (var id in this.tempHash) { 165 itemCount++; 166 } 167 if (itemCount == 0) { 168 // then we're done 169 this.notify( 170 new Monk.event.chunk.ChunksChecked({ 171 label: "Text chunks checked" 172 }), 173 this.tempNodes 174 ); 175 176 this.tempNodes = []; 177 this.updating = false; 178 } 179 } 180 181 } else { 182 // it's a collection branch 183 // only expand a branch if it belongs to a collection that is in our collectionsList 184 if (this.tempCollectionsList[collection]) node.expand(false, false); 185 } 186 } 187 188 // cascade through nodes, updating them as needed 189 this.tree.root.expand(false, false, function() { 190 this.worksetTreeComponent.tree.root.cascade( 191 this.worksetTreeComponent.recursiveNodeUpdater, 192 this.worksetTreeComponent 193 ); 194 }); 195 196 maskedElement.unmask(); 197 }, 198 199 initializeLoadDialog : function() { 200 var projectId = Monk.component.dataManager.getProjectId(); 201 202 var xmlReader = new Ext.data.XmlReader({ 203 record: 'workset', 204 id: '@id' 205 }, [ 206 {name: 'id', mapping: '@id'}, 207 'label', 208 'trainingList', 209 'trainingListRating', 210 'workList', 211 'workListRating' 212 ] 213 ); 214 215 var worksetsStore = new Ext.data.Store({ 216 reader: xmlReader, 217 proxy: new Ext.data.HttpProxy({ 218 url: Monk.data.PROXY_URL + 'get/ProjectManager.getWorksets', 219 method: 'GET' 220 }), 221 baseParams: {projectId: projectId} 222 }); 223 224 var worksetsCombo = new Ext.form.ComboBox({ 225 fieldLabel: 'Workset', 226 name: 'workset', 227 width: 175, 228 store: worksetsStore, 229 triggerAction: 'all', 230 allowBlank: false, 231 editable: false, 232 forceSelection: true, 233 displayField: 'label', 234 hiddenName: 'worksetId', 235 valueField: 'id' 236 }); 237 238 var loadForm = new Ext.form.FormPanel({ 239 labelAlign: 'right', 240 labelWidth: 70, 241 border: false 242 }); 243 244 loadForm.add(worksetsCombo); 245 246 this.loadDialog = new Ext.Window({ 247 title: 'Load Workset', 248 height: 120, 249 width: 300, 250 modal: true, 251 shadow: true, 252 plain: true, 253 border: false, 254 collapsible: false, 255 items: loadForm, 256 buttons: [ 257 { 258 text: 'Load', 259 handler: function(){ 260 if (loadForm.getForm().isValid()) { 261 var values = loadForm.getForm().getValues(); 262 var worksetId = values.worksetId; 263 var record = worksetsStore.getById(worksetId); 264 var label = record.get('label'); 265 var trainingList = record.get('trainingList'); 266 var trainingListRating = record.get('trainingListRating'); 267 var workList = record.get('workList'); 268 var workListRating = record.get('workListRating'); 269 270 var workset = { 271 id: worksetId, 272 label: label, 273 projectId: projectId, 274 trainingList: trainingList, 275 trainingListRating: trainingListRating, 276 workList: workList, 277 workListRating: workListRating 278 } 279 280 Monk.component.dataManager.setWorkset(workset); 281 282 loadForm.getForm().reset(); 283 this.loadDialog.hide(); 284 } 285 }, 286 scope: this 287 }, 288 { 289 text: 'Cancel', 290 handler: function(){ 291 this.loadDialog.hide(); 292 }, 293 scope: this 294 } 295 ] 296 }); 297 298 this.loadDialog.show(); 299 }, 300 301 initializeSaveDialog : function() { 302 var worksetName = new Ext.form.TextField({ 303 fieldLabel: 'Name', 304 name: 'name', 305 width: 175, 306 allowBlank: false 307 }); 308 309 var saveForm = new Ext.form.FormPanel({ 310 labelAlign: 'right', 311 labelWidth: 70, 312 border: false 313 }); 314 315 saveForm.add(worksetName); 316 317 this.saveDialog = new Ext.Window({ 318 title: 'Save Workset', 319 height: 120, 320 width: 300, 321 modal: true, 322 shadow: true, 323 plain: true, 324 border: false, 325 collapsible: false, 326 items: saveForm, 327 buttons: [ 328 { 329 text: 'Save', 330 handler: function(){ 331 if (saveForm.getForm().isValid()) { 332 var values = saveForm.getForm().getValues(); 333 var worksetName = values.name; 334 this.createNewWorkset(worksetName); 335 336 saveForm.getForm().reset(); 337 } 338 }, 339 scope: this 340 }, 341 { 342 text: 'Cancel', 343 handler: function(){ 344 this.saveDialog.hide(); 345 }, 346 scope: this 347 } 348 ] 349 }); 350 351 this.saveDialog.show(); 352 }, 353 354 /** 355 * Compiles the workset and saves it. 356 * @param {Boolean} saveAs If true, save workset as a copy 357 */ 358 saveWorkset : function(saveAs) { 359 var worksetId = Monk.component.dataManager.getWorksetId(); 360 if (worksetId == null || saveAs == true) { 361 // create new workset (or save a new copy) 362 if (this.saveDialog == null){ 363 this.initializeSaveDialog(); 364 } else { 365 this.saveDialog.show(); 366 } 367 } else { 368 // save the workset 369 var workset = Monk.component.dataManager.getWorkset(); 370 if (workset.trainingSet == null) { 371 // dummy data 372 workset.trainingSet = workset.worklist.split(',')[0]; 373 workset.trainingSetRating = 3; 374 } 375 if (workset.worklistRating == null) { 376 var count = workset.worklist.split(',').length; 377 var rating = "3"; 378 for (var i = 0; i < count - 1; i++){ 379 rating += ",3" 380 } 381 workset.worklistRating = rating; 382 } 383 Monk.data.workset.saveWorkset(workset); 384 } 385 }, 386 387 createNewWorkset : function(worksetName) { 388 Monk.component.dataManager.setWorksetName(worksetName); 389 390 // TODO: make sure training list is being sent with training list ratings 391 var workset = Monk.component.dataManager.getWorkset(); 392 Monk.data.workset.createWorkset(workset); 393 }, 394 395 initializeTree : function(collectionsHierarchy) { 396 397 // custom treeloader for handling XML 398 Ext.ux.CollectionTreeLoaderXML = function(args) { 399 Ext.ux.CollectionTreeLoaderXML.superclass.constructor.call(this, args); 400 } 401 402 Workbench.extend(Ext.ux.CollectionTreeLoaderXML, Ext.tree.TreeLoader, { 403 processResponse : function(response, node, callback){ 404 try { 405 var children = response.responseXML.getElementsByTagName('workpart'); 406 var collectionId = null; 407 for (var i = 0; i < children.length; i++) { 408 var child = children[i]; 409 if (child.nodeType == 1) { 410 var label = child.getElementsByTagName('label')[0].childNodes[0].data; 411 var id = child.getAttribute("id"); 412 var numChildren = parseInt(child.getAttribute("numChildren")); 413 // parse the ID to get the collection ID 414 if (collectionId == null) { 415 collectionId = id.match(/^(\w+)\-(.+)$/)[1]; 416 } 417 418 var treeNode = null; 419 if (numChildren > 0) { 420 var treeLoader = new Ext.ux.CollectionTreeLoaderXML({ 421 requestMethod:'GET', 422 dataUrl:Monk.data.PROXY_URL + 'get/CorpusManager.getWorkInfo?corpus='+collectionId+'&id='+id, 423 baseAttrs: {checked: false, iconCls:'chunk-tree-node'}, 424 uiProviders: { 425 'col': Ext.tree.ColumnNodeUI 426 } 427 }); 428 treeNode = new Ext.tree.AsyncTreeNode({ 429 workLabel: label, 430 uiProvider: Ext.tree.ColumnNodeUI, 431 id: id, 432 collectionId: collectionId, 433 userRating: '', 434 systemRating: '', 435 loader: treeLoader, 436 leaf: false, 437 checked: false, 438 iconCls:'chunk-tree-node' 439 }); 440 } else { 441 treeNode = new Ext.tree.TreeNode({ 442 workLabel: label, 443 uiProvider: Ext.tree.ColumnNodeUI, 444 id: id, 445 collectionId: collectionId, 446 userRating: '', 447 systemRating: '', 448 leaf: true, 449 checked: false, 450 iconCls: 'chunk-tree-node' 451 }); 452 } 453 node.appendChild(treeNode); 454 } 455 } 456 457 if(typeof callback == "function"){ 458 callback(this, node); 459 } 460 }catch(e){ 461 alert(e); 462 this.handleFailure(response); 463 } 464 } 465 }); 466 467 var treeLoader = new Ext.ux.CollectionTreeLoaderXML({ 468 requestMethod: 'GET', 469 dataUrl: Monk.data.PROXY_URL + 'get/CorpusManager.getWorkInfo', 470 baseAttrs: {checked: false, iconCls:'chunk-tree-node'}, 471 uiProviders:{ 472 'col': Ext.tree.ColumnNodeUI 473 } 474 }); 475 476 treeLoader.on('beforeload', function(loader, node) { 477 loader.baseParams.corpus = node.attributes.collectionId; 478 loader.baseParams.id = node.attributes.id; 479 }, this); 480 481 this.tree = new Ext.tree.ColumnTree({ 482 el: 'worksetTree', 483 width: 535, 484 height: 500, 485 rootVisible: true, 486 autoScroll: true, 487 enableDD: false, 488 loader: treeLoader, 489 title: 'Workset', 490 tbar: [{ 491 text:'Load Workset', 492 iconCls:'load-icon', 493 handler: 494 function(){ 495 if (this.loadDialog == null) { 496 this.initializeLoadDialog(); 497 } else { 498 this.loadDialog.show(); 499 } 500 }, 501 scope: this 502 },{ 503 xtype:'tbseparator' 504 },{ 505 text:'Save Workset', 506 tooltip: 'Save Workset', 507 iconCls:'save-icon', 508 handler: 509 function() { 510 /* 511 var json = this.tree.toJsonString(null, 512 function(key, val) { 513 return (key == 'id' || key =='userRating'|| key == 'systemRating'); 514 }, { 515 userRating: 'userRating', 516 systemRating: 'systemRating' 517 } 518 ); 519 alert(json); 520 */ 521 this.saveWorkset(); 522 }, 523 scope: this 524 },{ 525 xtype:'tbseparator' 526 },{ 527 text:'Save Workset As', 528 iconCls:'save-as-icon', 529 handler: 530 function(){ 531 this.saveWorkset(true); 532 }, 533 scope: this 534 },{ 535 xtype:'tbseparator' 536 } 537 ], 538 539 columns:[{ 540 header: 'Workparts', 541 width: 375, 542 dataIndex: 'workLabel' 543 },{ 544 header: 'SysRating', 545 width: 70, 546 editable: false, 547 dataIndex: 'systemRating' 548 },{ 549 header: 'UserRating', 550 width: 70, 551 dataIndex: 'userRating' 552 }], 553 554 root : new Ext.tree.AsyncTreeNode({ 555 text: 'Monk Collections', 556 draggable: false, 557 id: 'collections-root', 558 children: collectionsHierarchy, 559 checked: false, 560 expanded: true, 561 iconCls: 'chunk-tree-node', 562 uiProvider: Ext.tree.ColumnNodeUI 563 }) 564 }); 565 this.tree.render(); 566 567 this.tree.on("click", function(node, ev) { 568 if (node.hasChildNodes()) { 569 if (this.tree.getTarget(ev.target) == 'tree') { 570 node.toggle(); 571 } 572 } else { 573 // only load text from leaf nodes 574 this.handleNodeSelect(node); 575 } 576 }, this); 577 578 this.tree.on("checkchange", function(node,isChecked) { 579 this.handleNodeChange(node, isChecked); 580 }, this); 581 582 var te = new Ext.tree.ColumnTreeEditor(this.tree, { 583 editTree: false, 584 events: { 585 completeOnEnter: true, 586 ignoreNoChange: true 587 } 588 }); 589 }, 590 591 handleNodeSelect : function(node) { 592 this.notify( 593 new Monk.event.chunk.ChunkSelected({ 594 label: 'Text chunk selected: '+'"'+node.text+'"', 595 displayText: true 596 }), 597 { 598 id: node.attributes.id, 599 corpus: node.attributes.collectionId, 600 text: node.text, 601 chunkType: node.attributes.chunkType, 602 displayText: true 603 } 604 ); 605 }, 606 607 handleNodeChange : function(node, isChecked){ 608 this.notify( 609 new Monk.event.chunk.ChunkChecked({ 610 label: "Text chunk "+(isChecked ? "" : "un")+'checked: "'+node.text+'"', 611 checked: isChecked 612 }), 613 { 614 id: node.attributes.id, 615 corpus: node.attributes.collectionId, 616 text: node.text, 617 chunkType: node.attributes.chunkType, 618 checked: isChecked 619 } 620 ); 621 } 622 }); 623