1 /**
  2  * @class Monk.component.WorksetManagerComponent
  3  * @description A {@link Worbench.component.Component} for viewing a text chunk.
  4  * @extends Workbench.component.Component
  5  * @author Stéfan Sinclair
  6  * @version 0.1
  7  * @since Monk 0.1
  8  */
  9 Monk.component.WorksetManagerComponent = function(args) {
 10     
 11     this.worksetGrid = null;
 12     
 13     this.worksetId = null;
 14     
 15     // initialize worksets store
 16     this.worksetsStore = new Ext.data.Store({
 17         reader: new Ext.data.XmlReader(
 18             {record: 'workset', id: '@id'},
 19             [{name: 'id', mapping: '@id'},
 20             'label',
 21             'trainingList',
 22             'trainingListRating',
 23             'workList',
 24             'workListRating',
 25             'itinerary',
 26             'feature']
 27         ),
 28         proxy: new Ext.data.HttpProxy({
 29             url: Monk.data.PROXY_URL + 'get/ProjectManager.getWorksets',
 30             method: 'GET'
 31         }),
 32         baseParams: {projectId: Monk.component.dataManager.getProjectId()}
 33     });
 34     this.worksetsStore.load();
 35     
 36     this.loadDialog = null;
 37     
 38     this.saveDialog = null;
 39     
 40     this.cachedData = {chunks: null, ratings: null, hasSystemRating: false, modifier: null, minConfidence: null, maxConfidence: null};
 41     
 42     this.ratingStore = null;
 43     
 44     this.pageSize = 10; // how many workparts to display per page
 45     
 46     this.isValid = function() {
 47         if (Monk.component.dataManager.isWorksetValid()) {
 48             return true;
 49         } else {
 50             return 'Your workset is empty and/or it does not appear to have any training data - please load a workset and rate at least 2 items before proceeding.'
 51         }
 52     };
 53     
 54     Monk.component.WorksetManagerComponent.superclass.constructor.call(this, args);
 55 }
 56 
 57 Workbench.extend(Monk.component.WorksetManagerComponent, Workbench.component.Component, {
 58 
 59     label : "Workset Manager",
 60     description : "For managing worksets of text workparts in Monk.",
 61     "window" : this.window,
 62     handle : function(monkEvent, data){
 63     
 64     	if (monkEvent.instanceOf(Monk.event.workbench.LocalWindowFocus)) {
 65             //this.initializeWorksetGrid();
 66         }else if (monkEvent.instanceOf(Workbench.event.ComponentLoaded)) {
 67             if (monkEvent.component == this) {
 68                 this.cacheChunks(Monk.component.dataManager.getWorklist());
 69             }
 70         }else if (monkEvent.instanceOf(Monk.event.analysis.GotPrediction)) {
 71         	
 72         	var json = Monk.utils.xml2json(data.response.responseXML, " ");
 73             var workset = "";
 74             (eval('workset='+json));
 75             
 76             var worklistRating = workset.workset.workListRating.split(',');
 77             var worklist = workset.workset.workList.split(',');
 78             
 79             // update system ratings
 80             var store = this.worksetGrid.getStore();
 81             for (var i = 0; i < worklist.length; i++) {
 82                 var id = worklist[i];
 83                 var record = store.getById(id);
 84                 record.set('systemRating', worklistRating[i]);
 85             }
 86         } else if (monkEvent.instanceOf(Monk.event.workset.WorksetLoaded)) {
 87           
 88             this.cacheChunks(Monk.component.dataManager.getWorklist());
 89             if (this.worksetGrid == null) {
 90                 this.initializeWorksetGrid();
 91             } else {
 92                 var systemRatingColumn = this.worksetGrid.getColumnModel().getIndexById('systemRating');
 93                 this.worksetGrid.getColumnModel().setHidden(systemRatingColumn, !this.cachedData.hasSystemRating);
 94                 this.worksetGrid.getStore().proxy.update({nodes:this.cachedData.chunks}, false);
 95                 this.worksetGrid.getStore().load({params:{start:0, limit: this.pageSize}});
 96                 this.ratingStore.loadData(this.cachedData.ratings, false);
 97             }
 98             var worksetName = Monk.component.dataManager.getWorksetName();
 99             Monk.component.messenger.growl('Monk Workbench','Workset Loaded: '+worksetName);
100             this.worksetGrid.setTitle(Monk.component.dataManager.getWorksetLabel());
101             //Ext.get('componentContent').unmask();
102         } else if (monkEvent.instanceOf(Monk.event.chunk.ChunkChecked)) {
103             // TODO add handling for chunkschecked
104             var store = this.worksetGrid.getStore();
105             var index = store.proxy.contains(data.id);
106             if (index != -1) {
107                 if (!data.checked) {
108                     store.proxy.remove(data.id);
109                     this.worksetGrid.getBottomToolbar().refresh();
110                 }
111             } else if (data.checked) {
112                 data.workLabel = data.text;
113                 var work = Monk.data.collection.getWork(data.id);
114                 if (work != null) {
115                     data.authorsLabel = work.authorsLabel;
116                     data.publicationDates = work.publicationDates;
117                 }
118                 store.proxy.update({nodes:[data]}, true);
119                 this.worksetGrid.getBottomToolbar().moveLast();
120             }
121         } else if (monkEvent.instanceOf(Monk.event.chunk.ChunksRated)) {
122             this.worksetGrid.getStore().proxy.update({nodes:data}, true);
123             this.worksetGrid.getStore().load({params:{start:0, limit: this.pageSize}});
124             
125             for (var i = 0, len = data.length; i < len; i++) {
126                 var rating = data[i].userRating;
127                 if (this.ratingStore.getById(rating) == null && rating != '') {
128                     var ratingData = []
129                     ratingData['value'] = rating;
130                     var record = new Ext.data.Record(ratingData, rating);
131                     this.ratingStore.add(record);
132                 }
133             }
134         } else if (monkEvent.instanceOf(Monk.event.workset.WorksetCreated)) {
135             var recordDef = Ext.data.Record.create([
136                 {name: 'id'},
137                 {name: 'label'},
138                 {name: 'trainingList'},
139                 {name: 'trainingListRating'},
140                 {name: 'workList'},
141                 {name: 'workListRating'},
142                 {name: 'itinerary'},
143                 {name: 'feature'}
144             ]);
145             var workset = Monk.component.dataManager.getWorkset();
146             workset.id = workset.worksetId;
147             workset.label = workset.worksetName;
148             var record = new recordDef(workset); // creates a record with auto-ID
149             var recordCopy = record.copy(workset.id); // make a copy with the correct ID
150             this.worksetsStore.add(recordCopy);
151             
152             this.saveDialog.hide();
153             var worksetName = Monk.component.dataManager.getWorksetName();
154             Monk.component.messenger.growl('Monk Workbench','Workset Created: '+worksetName);
155         } else if (monkEvent.instanceOf(Monk.event.workset.WorksetSaved)) {
156             var worksetName = Monk.component.dataManager.getWorksetName();
157             Monk.component.messenger.growl('Monk Workbench','Workset Saved: '+worksetName);
158         } else if (monkEvent.instanceOf(Monk.event.workset.WorksetReset)) {
159             this.resetGrid();
160             //document.getElementById("count").innerHTML = 0;
161         } else if (monkEvent.instanceOf(Monk.event.chunk.ChunksContainingFeatureWithinWorksetRetrieved)) {
162             var json = Monk.utils.xml2json(data.response.responseXML, " ");
163             var works = "";
164             (eval('works='+json));
165 
166             var combinedWorks = [];
167             if (works.works.work != null) { // json array fix
168                 if (works.works.work.length == null) {
169                     works.works.work = [works.works.work];
170                 }
171                 combinedWorks = combinedWorks.concat(works.works.work);
172             }
173             if (works.works.workpart != null) { // json array fix
174                 if (works.works.workpart.length == null) {
175                     works.works.workpart = [works.works.workpart];
176                 }
177                 combinedWorks = combinedWorks.concat(works.works.workpart);
178             }
179             
180             var columns = this.worksetGrid.getColumnModel();
181             if (columns.isHidden(4)) {
182                 // show features if hidden
183                 columns.setHidden(4, false);
184             }
185             
186             for (var i = 0, length = combinedWorks.length; i < length; i++) {
187                 var workId = combinedWorks[i].id;
188                 var posTest = /\s+\(.+?\)$/;
189                 var feature = data.feature.split(posTest)[0];
190                 Monk.component.dataManager.addTermsToChunk(workId, [feature]);
191                 //var features = Monk.component.dataManager.getTermsForChunk(workId).join(', ');
192                 //this.worksetGrid.getStore().proxy.updateRecord(workId, {features: features});
193             }
194             this.worksetGrid.getBottomToolbar().refresh();
195         }
196         
197     },
198     
199     cacheChunks : function(worklist) {
200         var chunks = [];
201         var ratingsObject = {};
202         this.cachedData.hasSystemRating = false;
203         var minConfidence = null;
204         var maxConfidence = null;
205         
206         for (var chunkId in worklist) {
207             var work = Monk.data.collection.getWork(chunkId);
208             var chunk = worklist[chunkId];
209             
210             if (chunk.userRating != null) {
211                 ratingsObject[chunk.userRating] = [chunk.userRating];
212             }
213             if (chunk.systemRating) this.cachedData.hasSystemRating = true;
214             
215             var confidence = parseFloat(chunk.confidence);
216             if (confidence < minConfidence || minConfidence == null) minConfidence = confidence;
217             if (confidence > maxConfidence || maxConfidence == null) maxConfidence = confidence;
218 
219             chunk.id = chunkId;
220             if (work != null) {
221                 chunk.workLabel = work.text;
222                 chunk.authorsLabel = work.authorsLabel;
223                 chunk.publicationDates = work.publicationDates;
224             }
225             chunks.push(chunk);
226         }
227         if (chunks.length > 0) {
228             var ratingsArray = [];
229             for (var rating in ratingsObject) {
230                 ratingsArray.push(ratingsObject[rating]);
231             }
232             this.cachedData.chunks = chunks
233             this.cachedData.ratings = ratingsArray;
234             
235             this.cachedData.modifier = minConfidence * -1;
236             this.cachedData.minConfidence = minConfidence + this.cachedData.modifier;
237             this.cachedData.maxConfidence = maxConfidence + this.cachedData.modifier;
238         }       
239     },
240     
241     getFeatures : function(pager) {
242         pager.store.each(function(record){
243             var id = record.get('id');
244             var features = Monk.component.dataManager.getTermsForChunk(id);
245             if (features != null) {
246                 record.set('features', features.join(', '));
247             }
248         }, this);
249     },
250     
251     getWorksetFromStore : function(worksetId) {
252         var record = this.worksetsStore.getById(worksetId);
253         return {
254             id: worksetId,
255             label: record.get('label'),
256             projectId: Monk.component.dataManager.getProjectId(),
257             trainingList: trainingList = record.get('trainingList'),
258             trainingListRating: record.get('trainingListRating'),
259             workList: record.get('workList'),
260             workListRating: record.get('workListRating'),
261             itinerary: record.get('itinerary'),
262             feature: record.get('feature')
263         }
264         
265     },
266     
267     initializeLoadDialog : function() {
268         var worksetsCombo = new Ext.form.ComboBox({
269             fieldLabel: 'Workset',
270             name: 'workset',
271             width: 175,
272             maxHeight: 75,
273             store: this.worksetsStore,
274             triggerAction: 'all',
275             allowBlank: false,
276             editable: false,
277             forceSelection: true,
278             displayField: 'label',
279             hiddenName: 'worksetId',
280             valueField: 'id'
281         });
282         
283         var loadForm = new Ext.form.FormPanel({
284             labelAlign: 'right',
285             labelWidth: 70,
286             border: false
287         });
288         
289         loadForm.add(worksetsCombo);
290         this.loadDialog = new Ext.Window({
291             title: 'Load Workset',
292             height: 100,
293             width: 300,
294             modal: true,
295             shadow: true,
296             plain: true,
297             border: false,
298             collapsible: false,
299             items: loadForm,
300             buttons: [
301                 {
302                     text: 'Load',
303                     handler: function(){
304                         if (loadForm.getForm().isValid()) {
305                         	var values = loadForm.getForm().getValues();
306                             var worksetId = values.worksetId;
307                             
308                             var workset = this.getWorksetFromStore(worksetId);
309                             
310                             // reset grid for repopulating
311                             var store = this.worksetGrid.getStore();
312                             store.removeAll();
313                             
314                             //Ext.get("componentContent").mask("Loading workset...");
315                             Monk.component.dataManager.setWorkset(workset);
316                             
317                             loadForm.getForm().reset();
318                             this.loadDialog.hide();
319                         }
320                     },
321                     scope: this
322                 },
323                 {
324                     text: 'Cancel',
325                     handler: function(){
326                         this.loadDialog.hide();
327                     },
328                     scope: this
329                 }
330             ]
331         });
332         
333         this.loadDialog.show();
334     },
335     
336     initializeSaveDialog : function() {
337         var worksetName = new Ext.form.TextField({
338             fieldLabel: 'Name',
339             name: 'name',
340             width: 175,
341             allowBlank: false
342         });
343         
344         var saveForm = new Ext.form.FormPanel({
345             labelAlign: 'right',
346             labelWidth: 70,
347             border: false
348         });
349         
350         saveForm.add(worksetName);
351         
352         this.saveDialog = new Ext.Window({
353             title: 'Save Workset',
354             height: 100,
355             width: 300,
356             modal: true,
357             shadow: true,
358             plain: true,
359             border: false,
360             collapsible: false,
361             items: saveForm,
362             buttons: [
363                 {
364                     text: 'Save',
365                     handler: function(){
366                         if (saveForm.getForm().isValid()) {
367                             var values = saveForm.getForm().getValues();
368                             var worksetName = values.name;
369                             this.createNewWorkset(worksetName);
370                             
371                             saveForm.getForm().reset();
372                         }
373                     },
374                     scope: this
375                 },
376                 {
377                     text: 'Cancel',
378                     handler: function(){
379                         this.saveDialog.hide();
380                     },
381                     scope: this
382                 }
383             ]
384         });
385         
386         this.saveDialog.show();
387     },
388     
389     createNewWorkset : function(worksetName) {
390         Monk.component.dataManager.setWorksetName(worksetName); 
391         
392         // TODO: make sure training list is being sent with training list ratings
393         var workset = Monk.component.dataManager.getWorkset();
394         Monk.data.workset.createWorkset(workset);
395     },
396     
397     loadWorkset : function() {
398         if (this.loadDialog == null) {
399             this.initializeLoadDialog();
400         } else {
401             this.loadDialog.show();
402         }
403     },
404     
405     saveWorksetAs : function() {
406         this.saveWorkset(true);
407     },
408     
409     /**
410      * Compiles the workset and saves it.
411      * @param {Boolean} saveAs If true, save workset as a copy
412      */
413     saveWorkset : function(saveAs) {
414         var worksetId = Monk.component.dataManager.getWorksetId();
415         if (worksetId == null || saveAs == true) {
416             // create new workset (or save a new copy)
417             if (this.saveDialog == null){
418                 this.initializeSaveDialog();
419             } else {
420                 this.saveDialog.show();
421             }
422         } else {
423             // save the workset
424             var workset = Monk.component.dataManager.getWorkset();
425             if (workset.trainingSet == null) {
426                 // dummy data
427                 workset.trainingSet = workset.workList.split(',')[0];
428                 workset.trainingSetRating = 3;
429             }
430             if (workset.workListRating == null) {
431                 var count = workset.workList.split(',').length;
432                 var rating = "3";
433                 for (var i = 0; i < count - 1; i++){
434                     rating += ",3"
435                 }
436                 workset.workListRating = rating;
437             }
438             Monk.data.workset.saveWorkset(workset);
439         }
440     },
441     
442     resetWorkset : function() {
443         this.notify(new Monk.event.workset.WorksetReset({
444                 label: 'Workset reset'
445             })
446         );
447     },
448     
449     initializeWorksetGrid : function() {
450       
451        // debugger;
452         var recordDef = Ext.data.Record.create([
453           {name: 'id'},
454           {name: 'corpus'},
455           {name: 'text'},
456           {name: 'chunkType'},
457           {name: 'checked'},
458           {name: 'userRating'},
459           {name: 'systemRating'},
460           {name: 'confidence', type: 'float'},
461           {name: 'features'},
462           {name: 'workLabel'},
463           {name: 'authorsLabel'},
464           {name: 'publicationDates'}
465         ]);
466         
467         var reader = new Ext.data.JsonReader({
468             root: 'nodes',
469             id: 'id'
470         }, recordDef);
471         
472         var store = new Ext.data.GroupingStore({
473             reader: reader,
474             proxy: new Ext.ux.data.PagingMemoryProxy({nodes:this.cachedData.chunks}, {reader: reader}),
475             remoteSort: true,
476             groupField: 'corpus',
477             sortInfo:{field: 'text', direction: "ASC"}
478         });
479         
480         this.ratingStore = new Ext.data.SimpleStore({
481             fields: ['value'],
482             id: 0,
483             data: this.cachedData.ratings == null ? [] : this.cachedData.ratings
484         });
485         
486         var ratingCombo = new Ext.form.ComboBox({
487             lazyRender: true,
488             triggerAction: 'all',
489             mode: 'local',
490             displayField:'value',
491             valueField:'value',
492             store: this.ratingStore,
493             editable: true,
494             forceSelection: false,
495             validateOnBlur: false,
496             listeners: {
497                 specialkey: {
498                     fn: function(field, event) {
499                         if (event.getKey() == event.ENTER) {
500                             // stop the editor from moving to the next row
501                             this.worksetGrid.stopEditing();
502                         }
503                     }, scope: this
504                 }
505             }
506         });
507         
508         var confidenceGradient = function(value, metadata, record, rowIndex, colIndex, store, modifier, maxConfidence){
509             var rValue = 255;
510             var gbValue = Math.floor((value + modifier) / maxConfidence * 255);
511             metadata.attr = 'style = "background-color: rgb('+rValue+','+gbValue+','+gbValue+');"';
512             return '<div ext:qtip="The confidence in the rating predicted by the system." style="width: 50px;"> </div>'; // just show the colour
513         }
514         
515         var columnModel = new Ext.grid.ColumnModel([
516             {header: 'Corpus', width: 50, sortable: true, dataIndex: 'corpus', renderer: function(content, el, data) {return '<span ext:qtip="' + data.data.workLabel + ': ' + data.data.authorsLabel+'">'+content+"</span>"}},
517             {header: 'Workpart ID', hidden: true, sortable: true, dataIndex: 'id', renderer: function(content, el, data) {return '<span ext:qtip="' + data.data.workLabel + ': ' + data.data.authorsLabel+'">'+content+"</span>"}},
518             {header: 'User Rating', id: 'userRating', sortable: true, dataIndex: 'userRating', editor: ratingCombo, renderer: function(content){
519                 if (content == '') return '<span style="color: #999999;" ext:qtip="Click to assign a rating.">Click to Rate</span>';
520                 else return '<span ext:qtip="The rating assigned by the user.">'+content+'</span>';
521             }},
522             {header: 'Predicted Rating', id: 'systemRating', hidden: !this.cachedData.hasSystemRating, sortable: true, dataIndex: 'systemRating', renderer: function(content, el, data) {return '<span ext:qtip="The rating predicted by the system.">'+content+"</span>"}},
523             {header: 'Confidence', width: 60, id: 'confidence', hidden: !this.cachedData.hasSystemRating, sortable: true, dataIndex: 'confidence', renderer: confidenceGradient.createDelegate(this, [this.cachedData.modifier, this.cachedData.maxConfidence], true)},
524             {header: 'Title', sortable: true, dataIndex: 'text', renderer : function (content) {return content.length>22 ? '<span ext:qtip="'+content+'">'+content.substring(0,22)+"...</span>" : content}},
525             {header: 'Features', sortable: true, dataIndex: 'features', hidden: true},
526 //			{header: 'Work', width: 150, sortable: true, dataIndex: 'workLabel'},
527             {header: 'Author', sortable: true, dataIndex: 'authorsLabel', renderer : function (content) {return content.length>15 ? '<span ext:qtip="'+content+'">'+content.substring(0,15)+"...</span>" : content}},
528             {header: 'Date', sortable: true, dataIndex: 'publicationDates', renderer : function (content) {return content.split(/-/)[0]}}
529         ]);
530         
531 //        var selectionModel = new Ext.grid.CheckboxSelectionModel();
532         
533         var thisComponent = this;
534         
535         var viewport = new Ext.Viewport({
536             layout: 'fit',
537             renderTo: 'gridParent',
538             items: this.worksetGrid
539         });
540         
541         Ext.EventManager.onWindowResize(function(w, h) {
542             this.worksetGrid.setHeight(h);
543         }, this);
544         
545         this.worksetGrid = new Ext.grid.EditorGridPanel({
546             id : 'workset-manager-grid',
547             store: store,
548             /*view: new Ext.grid.GroupingView({
549                             forceFit:true,
550                             groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
551              }),
552              */
553             cm: columnModel,
554             sm: new Ext.grid.RowSelectionModel(),
555             clicksToEdit: 1,
556             renderTo: 'worksetGrid',
557             height: 300,
558             viewConfig: {forceFit: false},
559             autoWidth: true,
560             listeners: {
561                 'render': function(grid){
562                     var h = viewport.getBox().height;
563                     grid.setHeight(h);
564                 }
565             },
566             title : 'Workset',
567             tbar : new Ext.Toolbar([
568                 /*new Ext.Button({
569                     text : "New",
570                     iconCls : 'clearBtn',
571                     handler : function() {Monk.component.messenger.confirm(
572                         'Monk Workbench',
573                         'This will create a new, empty, workset. Do you wish to continue?',
574                         function(buttonId) {
575                             if (buttonId == 'yes') {this.resetWorkset();}
576                         }, this, this.window.parent ? this.window.parent.window : this.window)},
577                         scope : this
578                 }),
579                 */
580                 new Ext.Button({
581                     text : "Load",
582                     iconCls : 'loadBtn',
583                     handler : this.loadWorkset,
584                     scope : this
585                 }),
586                 new Ext.Button({
587                     text : "Save",
588                     menu : [
589                         new Ext.Action({
590                             text : 'Save',
591                             iconCls : 'saveBtn',
592                             handler : this.saveWorkset,
593                             scope : this
594                         }),
595                         new Ext.Action({
596                             text : 'Save As...',
597                             iconCls : 'saveasBtn',
598                             handler : this.saveWorksetAs,
599                             scope : this
600                         })
601                     ],
602                     iconCls : 'saveBtn'
603                 }),
604                 '-',
605                 new Ext.Button({
606                     text : "Revert",
607                     iconCls : 'revertBtn',
608                     handler : function() {
609                         Monk.component.messenger.confirm(
610                         'Monk Workbench',
611                         'This will revert the workset to the last time it was saved (and remove any changes made since then). Do you wish to continue?',
612                         function(buttonId) {
613                             if (buttonId == 'yes') {
614 
615                                 // reset grid for repopulating
616                                 this.resetGrid();
617                                 
618                                 //Ext.get("componentContent").mask("Loading workset...");
619                                 var worksetId = Monk.component.dataManager.getWorksetId();
620                                 if (worksetId == null) {
621                                     Monk.component.messenger.alert('Monk Workbench', 'You have no workset to revert back to.', this.window.parent ? this.window.parent.window : this.window);
622                                 } else {
623                                     Monk.component.dataManager.setWorkset(this.getWorksetFromStore(worksetId));
624                                 }
625                                 
626                             }
627                         }, this, this.window.parent ? this.window.parent.window : this.window)						
628                     },
629                     scope : this
630                 }),
631                 new Ext.Button({
632                     text : "Import/Export",
633                     iconCls : 'exportxmlBtn',
634                     menu : [
635                         new Ext.Action({
636                             text : 'Import tab separated values',
637                             handler : function () {
638                                 Monk.component.messenger.show({
639                                    title: 'Import Workset Data',
640                                    msg: 'Paste in rows (copied from Excel, for instance) of only two columns where the workset ID and the optional rating are separated by a tab (or space):',
641                                    width:600,
642                                    defaultTextHeight: 25,
643                                    buttons: Ext.MessageBox.OKCANCEL,
644                                    multiline: true,
645                                    fn: function handleImport(btn, text) {
646                                         var rows = text.split(/(\r\n|\r|\n)+/);
647                                         var nodes = [];
648                                         var parts;
649                                         var id;
650                                         var workId;
651                                         for (var i=0;i<rows.length;i++) {
652                                             parts = rows[i].split(/\s+/);
653                                             id = parts[0];
654                                             workId  = id.split("-");
655                                             workId = workId[0]+'-'+workId[1];
656                                             if (parts[0]) {
657 												var work = Monk.data.collection.getWork(workId);
658                                                 nodes.push({
659                                                     id: id,
660                                                     userRating: parts[1] || "",
661                                                     text: (workId==id ? '' : "[from] ") + (work && work.text ? work.text : 'unknown')
662                                                 });
663                                             }
664                                         }
665                                         this.notify(
666                                             new Monk.event.chunk.ChunksRated({
667                                                 label: "number of text workparts rated: "+nodes.length
668                                             }), nodes);
669                                     },
670                                    scope: this
671                                   }, this.window.parent ? this.window.parent.window : this.window);
672                             },
673                             scope : this
674                         }),
675                         new Ext.Action({
676                             text : 'Export as tab separated values',
677                             handler : function() {
678                                 var worklist = Monk.component.dataManager.getWorklist();
679                                 var output = ["Workpart ID","User Rating","System Rating","Workpart Type","Workpart Label"].join("\t") + "\n";
680                                 for (key in worklist) {
681                                     output += [worklist[key].id,worklist[key].userRating,worklist[key].systemRating,worklist[key].chunkType,worklist[key].text].join("\t") + "\n";
682                                 }
683                                 Monk.component.messenger.show({
684                                     title : 'Export Workset as Tab Separated Values (TSV)',
685                                     msg : 'Select all the data below, copy, and paste directly into your favourite spreadsheet program.',
686                                     width: 600,
687                                     defaultTextHeight: 25,
688                                    buttons: Ext.MessageBox.OK,
689                                    multiline : true,
690                                    value : output,
691                                    scope : this
692                                 }, this.window.parent ? this.window.parent.window : this.window)
693                             },
694                             scope : thisComponent
695                         })/*,
696                         
697                         new Ext.Action({
698                             text : 'export as XML',
699                             handler : function() {
700                                 var worklist = Monk.component.dataManager.getWorklist();
701                                 var output = ["Workpart ID","User Rating","System Rating","Workpart Type","Workpart Label"].join("\t") + "\n";
702                                 for (key in worklist) {
703                                     output += [worklist[key].id,worklist[key].userRating,worklist[key].systemRating,worklist[key].chunkType,worklist[key].text].join("\t") + "\n";
704                                 }
705                                 Monk.component.messenger.show({
706                                     title : 'Export Workset as Tab Separated Values (TSV)',
707                                     msg : 'Select all the data below, copy, and paste directly into your favourite spreadsheet program.',
708                                     width: 600,
709                                     defaultTextHeight: 25,
710                                    buttons: Ext.MessageBox.OK,
711                                    multiline : true,
712                                    value : output,
713                                    scope : this
714                                 }, this.window.parent ? this.window.parent.window : this.window)
715                             },
716                             scope : thisComponent
717                         }),
718                         */
719                     ]
720                 }),
721                 '-',
722                 new Ext.Button({
723                     text : "Remove Workpart",
724                     iconCls : 'deleteBtn',
725 //					handler: this.resetWorksetWithConfirmation,
726                     handler : function() {
727                         var selections = this.worksetGrid.getSelectionModel().getSelections();
728                         if (selections.length>0) {
729                             Monk.component.messenger.confirm(
730                                 'Monk Workbench',
731                                 'Are you sure you want to remove the selected text(s) from the workset?',
732                                 function(buttonId) {
733                                     if (buttonId == 'yes') {
734                                         for (var i=0;i<selections.length;i++) {
735                                             var node = selections[i];
736                                             this.notify(new Monk.event.chunk.ChunkChecked({
737                                                 label: "Text workpart unchecked: " + node.data.text + '"',
738                                                 checked: false
739                                             }), {
740                                                 id: node.id,
741                                                 checked: false
742                                             }, this);
743                                         }
744                                     }
745                                 }, this, this.window.parent ? this.window.parent.window : this.window)							
746                         }
747                         else {
748                             Monk.component.messenger.alert('No Workpart Selected', 'Please select a workpart before attempting to remove it.', this.window.parent ? this.window.parent.window : this.window);
749                         }
750                     },
751                     scope : this
752                 })
753             ]),
754             bbar: new Ext.PagingToolbar({
755                 store: store,
756                 pageSize: this.pageSize,
757                 displayInfo: true,
758                 displayMsg: '{0} - {1} of {2}',
759                 emptyMsg: 'No workparts to display',
760                 listeners: {
761                     'pagechange': this.getFeatures,
762                     scope: this
763                 },
764                 plugins: [new Ext.ux.PageSizePlugin()]
765             })
766         });
767         
768         this.worksetGrid.on('cellclick', function(grid, rowIndex, columnIndex, event) {
769             var store = grid.getStore();
770             var record = store.getAt(rowIndex);
771             
772             // send chunk selected event, unless it's the user rating column
773             var userRatingIndex = grid.getColumnModel().getIndexById('userRating');
774             if (columnIndex != userRatingIndex) {
775                 var text = record.get('text');
776                 var chunkType = record.get('chunkType');
777                 var id = record.get('id');
778                 var corpus = record.get('corpus');
779                 
780                 this.notify(new Monk.event.chunk.ChunkSelected({
781                         label: 'Text workpart selected: '+'"'+text+'"'
782                     }),
783                     {id: id, corpus: corpus, text: text, chunkType: chunkType, displayText:true}
784                 );
785             }
786         }, this);
787         
788         this.worksetGrid.on('afteredit', function(editEvent){
789             var rating = editEvent.value;
790             var rowId = editEvent.record.id;
791             
792             var selections = editEvent.grid.getSelectionModel().getSelections();
793             
794             for (var i=0;i<selections.length;i++) {
795                 var id = selections[i].id;
796                 
797                 if (rowId != id) {
798                     if (selections.length > 1) {
799                         selections[i].set("userRating", rating);
800                     } else {
801                         // the user rated a chunk, then click on another chunk
802                         // so assign the rating to the original chunk
803                         id = rowId;
804                     }
805                 }
806     
807                 if (this.ratingStore.getById(rating) == null) {
808                     var data = []
809                     data['value'] = rating;
810                     var record = new Ext.data.Record(data, rating);
811                     this.ratingStore.add(record);
812                 }
813                 
814                 this.worksetGrid.getStore().proxy.updateRecord(id, {userRating: rating});
815                 
816                 this.notify(new Monk.event.chunk.ChunkRated({
817                         label: 'Workpart '+id+' was given a rating of '+rating
818                     }),{chunkId: id, rating: rating});				
819             }
820         }, this);
821         
822         var autoSizeColumns = function() {
823             for (var i = 0; i < this.colModel.getColumnCount(); i++) {
824                 autoSizeColumn(i, this);
825             }
826             this.store.removeListener('load', autoSizeColumns, this); // only run this the first time
827         }
828     
829         var autoSizeColumn = function(c, grid) {
830             var w = grid.view.getHeaderCell(c).firstChild.scrollWidth;
831             for (var i = 0, l = grid.store.getCount(); i < l; i++) {
832                 w = Math.max(w, grid.view.getCell(i, c).firstChild.scrollWidth);
833             }
834             grid.colModel.setColumnWidth(c, w+2);
835             return w;
836         }
837         
838         store.on('load', autoSizeColumns, this.worksetGrid);
839         
840         if (this.cachedData.chunks != null) {
841             store.load({params:{start:0, limit: this.pageSize}});
842             this.cachedData == {chunks: null, ratings: null};
843             this.worksetGrid.setTitle(Monk.component.dataManager.getWorksetLabel());
844         }
845         
846     },
847     
848     resetGrid : function() {
849         this.worksetGrid.getStore().removeAll();
850         this.worksetGrid.getStore().proxy.update({nodes:[]});
851         this.worksetGrid.getStore().load();
852         this.ratingStore.removeAll();
853         this.worksetGrid.setTitle('Workset');
854     }
855 });
856