1 /**
  2  * @class Monk.component.SeasrManagerComponent
  3  * @description Sets up SEASR analyses, which get run by the Monk.component.dataManager.
  4  * @extends Workbench.component.Component
  5  * @author Andrew
  6  * 
  7  * @property {Boolean} cancelled Has the analysis been cancelled or not?
  8  * @property {Boolean} hasResults Have results come in from the server?
  9  * @property {Ext.form.FormPanel} analysisForm The form for this component.
 10  * @property {Ext.Window} saveDialog The dialog for saving a workset.
 11  * @property {Object} cachedData Stores cached data: feature, itinerary, form values, toolset id. Used to initialize the form with previous values, and for sending parameters to the data manager.
 12  */
 13 
 14 Monk.component.SeasrManagerComponent = function(args) {
 15     this.cancelled = false;
 16     this.hasResults = false;
 17     this.analysisForm = null;    
 18     this.saveDialog = null;
 19     this.cachedData = null;
 20     
 21     this.isValid = function() {
 22         if (this.cancelled == false && this.hasResults) {
 23             return true;
 24         } else {
 25             return 'There are no results to display. Please click the <b>Perform Analysis</b> button to get analysis results before proceeding.';
 26         }
 27     };
 28     
 29     var WordClassRecord= new Ext.data.Record.create([
 30                 {name : 'label', mapping : '@id'},
 31                 {name: 'value', mapping: '@id'}
 32                 ])
 33          this.wordclassStore = new Ext.data.Store({
 34             url: Monk.data.PROXY_URL + "get/AnalyticsManager.getMajorWordClasses",
 35             reader : new Ext.data.XmlReader({record : 'class'}
 36                 ,WordClassRecord),
 37             sortInfo: {field:'label', direction:"ASC"},
 38            autoLoad: true
 39          });
 40 
 41           // add the all Word Class
 42           this.wordclassStore.on('load',function(){
 43              var newRecord = new WordClassRecord({"label": 'All','value':'*'});
 44              this.wordclassStore.addSorted(newRecord);
 45          },this);
 46 
 47     
 48     
 49     Monk.component.SeasrManagerComponent.superclass.constructor.call(this, args);
 50 }
 51 
 52 Workbench.extend(Monk.component.SeasrManagerComponent, Workbench.component.Component, {
 53    /** @lends Monk.component.SeasrManagerComponent.prototype */
 54     
 55     label: 'Analysis Tool',
 56     description: 'Sets up SEASR analyses.',
 57     "window": this.window,
 58     
 59     /**
 60      * Events handled by this component:
 61      * <ul>
 62      * <li>{@link Workbench.event.ComponentLoaded}</li>
 63      * <li>{@link Monk.event.workset.WorksetLoaded}</li>
 64      * <li>{@link Monk.event.analysis.GotSeasrAnalysisResults}</li>
 65      * <li>{@link Monk.event.workset.AnalysisResultsProcessed}</li>
 66      * <li>{@link Monk.event.analysis.Aborted}</li>
 67      * </ul>
 68      */
 69     handle : function(monkEvent, data) {
 70         if (monkEvent.instanceOf(Workbench.event.ComponentLoaded)) {
 71             if (monkEvent.component == this) {
 72                 var feature = Monk.component.dataManager.getFeature();
 73                 var itinerary = Monk.component.dataManager.getItinerary();                
 74                 
 75                 this.cachedData = {feature: feature, itinerary: itinerary};
 76             }
 77         } else if (monkEvent.instanceOf(Monk.event.workset.WorksetLoaded)) {
 78             var featureValue = Monk.component.dataManager.getFeature();
 79             var itineraryValue = Monk.component.dataManager.getItinerary();
 80             this.analysisForm.getForm().setValues({feature: featureValue, itinerary: itineraryValue});
 81         } else if (monkEvent.instanceOf(Monk.event.analysis.GotSeasrAnalysisResults)) {
 82             if (!this.cancelled) {
 83                 this.getFeaturesButton.enable();
 84             }
 85         } else if (monkEvent.instanceOf(Monk.event.workset.AnalysisResultsProcessed)) {
 86             // TODO dataManager will still process results, should probably stop that
 87             if (!this.cancelled) {
 88                 var resultsMessage = "<p>MONK has analyzed your workset. Please press <span class='universal-continue-button-delegate' onclick='if (parent.Ext) {parent.Ext.getCmp(\"universal-continue-button\").getEl().frame(\"ff0000\", 1, { duration: 2, callback: function(el) {var b = parent.Ext.getCmp(\"universal-continue-button\"); b.handler.call(b.scope, b)} })}; return false'>continue</span> to see the following result(s): ";
 89                 for (var i = 0, length = data.results.length; i < length; i++) {
 90                     resultsMessage += data.results[i];
 91                     if (data.results[i + 1] != null) {
 92                         resultsMessage += ", ";
 93                     }
 94                 }
 95                 resultsMessage += ".</p>";
 96                 Ext.DomHelper.overwrite(Ext.get('resultsMessage'), resultsMessage);
 97                 
 98                 //set feature
 99                 Monk.component.dataManager.setFeature(Ext.getCmp("featureId").value);
100                 
101                 
102                 this.hasResults = true;
103             }
104         } else if (monkEvent.instanceOf(Monk.event.analysis.Aborted)) {
105             this.cancelAnalysis();
106         } else if (monkEvent.instanceOf(Monk.event.ServerError)) {
107             if (data.url != null) {
108                 if (data.url.match(/SchedulerManager.runAnalysis/)) this.cancelAnalysis();
109             }
110         }
111     },
112     
113     /**
114      * Called when the current analysis gets cancelled.
115      */
116     cancelAnalysis : function() {
117         this.getFeaturesButton.enable();
118         this.cancelled = true;
119         
120         Ext.DomHelper.overwrite(Ext.get('resultsMessage'), '');
121     },
122     
123     /**
124      * Loads parameter data and initializes the form.  Called from the HTML file when the DOM is ready.
125      */
126     initializeForm : function() {
127         var xmlReaderItin = new Ext.data.XmlReader({
128                 record: 'metadata'
129             }, [
130                 'title',
131                 'name',
132                 'date',
133                 'description'
134             ]
135         );
136         
137         // preload the itinerary data
138         var itinStore = new Ext.data.Store({
139             autoLoad: true,
140             reader: xmlReaderItin,
141             proxy: new Ext.data.HttpProxy({
142                 url: Monk.data.PROXY_URL + 'get/SeasrManager.getFlowMetadata',
143                 method: 'GET'
144             })
145         });
146         
147         // perform some checks, set values
148         itinStore.on('load', function(store, records, options) {
149             if (records.length == 0) {
150                 Monk.component.messenger.alert('Analysis Tool', 'Analytical routines aren\'t currently available, so you can\'t perform an analysis at this time.');
151                 this.getFeaturesButton.disable();
152             } else {
153                 if (this.cachedData) {
154                     this.analysisForm.getForm().setValues({
155                         feature: this.cachedData.feature,
156                         itinerary: this.cachedData.itinerary
157                     });
158                     if (this.cachedData.itinerary != "") {
159                         var itinMatch = store.query('title', this.cachedData.itinerary);
160                         if (itinMatch.getCount() == 0) {
161                             Monk.component.messenger.alert('Analysis Tool', 'You\'re using an old analytical routine that isn\'t currently supported. Please select a new one.' +
162                             		' This need not be an error -the analytics rountine\'s name may have changed.');
163                         } else {
164                             this.itinCombo.setValue(this.cachedData.itinerary);
165                             this.itinCombo.fireEvent('select', this.itinCombo, itinMatch.first(), 0);
166                         }
167                     }
168                     
169                     this.cachedData = null;
170                 } else {
171                     var record = store.getAt(0);
172                     this.itinCombo.setValue(record.get('title'));
173                     this.itinCombo.fireEvent('select', this.itinCombo, record, 0);
174                 }
175             }
176         }, this);
177         
178         this.itinCombo = new Ext.form.ComboBox({
179             fieldLabel: 'SEASR Analytical Routines',
180             id: "flowList",
181             name: 'itinerary',
182             width: 200,
183             editable: false,
184             forceSelection: true,
185             allowBlank: false,
186             store: itinStore,
187             mode: 'local',
188             emptyText:'Select a Routine...',
189             triggerAction: 'all',
190             displayField: 'name',
191             hiddenName: 'itinerary',
192             valueField: 'title'
193         });
194         
195         var xmlReaderFeature = new Ext.data.XmlReader({
196                 record: 'feature'
197             }, ['label']
198         );
199         
200         this.featureStore = new Ext.data.Store({
201             reader: xmlReaderFeature,
202             proxy: new Ext.data.HttpProxy({
203                 url: Monk.data.PROXY_URL + 'get/SeasrManager.getSupportedFeatures',
204                 method: 'GET'
205             })
206         });
207         
208         this.featureCombo = new Ext.form.ComboBox({
209         	id: 'featureId',
210             fieldLabel: 'Feature',
211             name: 'feature',
212             width: 200,
213             editable: false,
214             forceSelection: true,
215             allowBlank: false,
216             store: this.featureStore,
217             mode: 'local',
218             triggerAction: 'all',
219             displayField: 'label',
220             hiddenName: 'feature',
221             emptyText:'Select Routine First...',
222             valueField: 'label'
223         });        
224         
225         this.itinCombo.on('select', function(combo, record, index) {
226             var description = record.get('description');
227             Ext.DomHelper.overwrite(Ext.get('descriptionField'), description);
228             
229             var flowId = record.get('name');
230             this.featureStore.load({
231                 params: {flowId: flowId},
232                 add: false,
233                 callback: function(records, options, success) {
234                     this.featureCombo.setValue(records[0].get('label'));
235                 },
236                 scope: this
237             });
238         }, this);
239         
240         this.featureCombo.on('beforequery', function(queryEvent) {
241             var flowId = this.itinCombo.getValue();
242             queryEvent.query = flowId;
243         }, this);
244         
245         function extraParamsValidator(value) {
246             var validate = this.analysisForm.getForm().getValues().emailResults == 'on';
247             if (validate && value == '') {
248                 return '';
249             } else {
250                 return true;
251             }
252         }
253         
254         this.getFeaturesButton = new Ext.Button({
255             text: 'Perform Analysis',
256             id: 'perform-analysis',
257             disabled: Monk.component.dataManager.performingAnalysis, // disable if there's already an analysis running
258             handler: function() {
259                 var worksetValid = Monk.component.dataManager.isWorksetValid();
260                 if (this.analysisForm.getForm().isValid() && worksetValid) {
261                     this.showLogButton.enable();
262                     this.cancelled = false;
263                     this.hasResults = false;
264                     this.getFeaturesButton.disable();
265                     
266                     var values = this.analysisForm.getForm().getValues();
267                     if (values.emailResults == 'on') {
268                         values.async = true;
269                     } else {
270                         values.async = false;
271                     }
272                     var feature = values.feature;
273                     var itinerary = values.itinerary;
274                     this.cachedData = values;
275                     this.cachedData.toolsetId = Monk.component.dataManager.getToolsetId();
276                     Monk.component.dataManager.setFeature(feature);
277                     Monk.component.dataManager.setItinerary(itinerary);
278                     if (Monk.component.dataManager.getWorksetId() == null) {
279                         this.initializeSaveDialog();
280                     } else {
281                         Monk.data.workset.saveWorkset(Monk.component.dataManager.getWorkset());
282                         // run analysis setup after workset is saved
283                         Monk.component.dataManager.initializeProgressDialog(this.cachedData);
284                     }
285                 } else if (!worksetValid) {
286                     Monk.component.messenger.alert('Workbench Error','Your workset does not appear to have any training data - please rate items in the workset before launching analysis.')
287                 }
288             },
289             scope: this
290         });
291         
292         this.showLogButton = new Ext.Button({
293             disabled: true,
294             text: 'Show Log',
295             handler: function() {
296                 var conn = new Ext.data.Connection();
297                 conn.request({
298                     url: Monk.data.PROXY_URL + 'get/SchedulerManager.log',
299                     method: 'GET',
300                     params: {token : Monk.component.dataManager.token},
301                     success: function(responseObject) {
302                         var log = responseObject.responseText;
303                         log = log.replace(/</g, '<');
304                         log = log.replace(/>/g, '>');
305                         log = log.replace(/\s{2,10}/g, '<br/>');
306                         var oldWindow = Ext.getCmp('logWindow');
307                         if (oldWindow != null) oldWindow.close();  
308                         var logWindow = new Ext.Window({
309                             id: 'logWindow',
310                             title: 'Analysis Log',
311                             height: 400,
312                             width: 400,
313                             modal: false,
314                             shadow: true,
315                             plain: true,
316                             border: false,
317                             collapsible: false,
318                             closable: true,
319                             layout: 'fit',
320                             items: {
321                                 xtype: 'panel',
322                                 html: '<div style="overflow: auto; height: 100%; width: 100%;">'+log+'</div>'
323                             }
324                         });
325                         logWindow.show();
326                     },
327                     failure: function(response, options) {
328                         Ext.Msg.alert('Analysis Tool', 'Error getting log.<br/><b>Error</b>: '+response.statusText);
329                     },
330                     scope: this
331                 });
332             },
333             scope: this
334         });
335         
336         this.analysisForm = new Ext.form.FormPanel({
337             renderTo: 'analysisForm',
338             labelAlign: 'right',
339             labelWidth: 160,
340             width: 400,
341             border: false,
342             buttonAlign: 'left',
343             items: [
344                     this.itinCombo, {
345                     border: false,
346                     width: 300,
347                     style: 'margin: 5px 0 5px 62px',
348                     html: '<div id="descriptionField"></div>'
349                     },
350                     this.featureCombo,
351                     {
352                        fieldLabel: 'Feature Class',
353                        xtype: 'combo',
354                        tooltip: {title:'Major Word Classes',text:'Select Major Word Class.'},
355                        name: 'wordClass',
356                        hiddenName: 'wordClass',
357                        triggerAction: 'all',
358                        emptyText: 'Word Class',
359                        valueField: 'value',
360                        displayField: 'label',
361                        mode: 'local',
362                        allowBlank : false,
363                        width: 200,
364                        store: this.wordclassStore
365                     },{
366                         fieldLabel: 'Number of Features',
367                         xtype: 'combo',
368                         store: new Ext.data.SimpleStore({
369                             fields: ['label', 'value'],
370                             data : [['10','10'],['20','20'],['50','50'],['100','100']]
371                         }),
372                         name: 'numfeature',
373                         width: 100,
374                         editable: false,
375                         forceSelection: true,
376                         allowBlank: false,
377                         mode: 'local',
378                         triggerAction: 'all',
379                         displayField: 'label',
380                         hiddenName: 'numfeature',
381                         emptyText: 'Select Top N...',
382                         valueField: 'value',
383                         value: '100'
384                     }/*,
385                     {
386                     layout: 'column',
387                     border: false,
388                     width: 400,
389                     defaults: {border: false},
390                     items: [{
391                         columnWidth: 0.2,
392                         html: ' '
393                     },{
394                         columnWidth: 0.22,
395                         html: '<div class="x-form-item">Result Visibilty:</div>'
396                     },{
397                         columnWidth: 0.15,
398                         layout: 'form',
399                         labelWidth: 30,
400                         items: {
401                             xtype: 'radio',
402                             fieldLabel: 'Public',
403                             labelSeparator: '',
404                             name: 'share',
405                             inputValue: 'public',
406                             checked: true
407                         }
408                     },{
409                         columnWidth: 0.43,
410                         layout: 'form',
411                         labelWidth: 40,
412                         items: {
413                             xtype: 'radio',
414                             fieldLabel: 'Private',
415                             labelSeparator: '',
416                             name: 'share',
417                             inputValue: 'private'
418                         }
419                         
420                     }]
421                 }*/,{
422                     xtype: 'textfield',
423                     fieldLabel: 'Result Name',
424                     name: 'name',
425                     width: 200,
426                     selectOnFocus : true,
427                     value : new Date().format('Y-m-d H:i'),
428                     allowBlank: false
429                 },{
430                     xtype: 'textarea',
431                     fieldLabel: 'Result Description',
432                     name: 'description',
433                     width: 200,
434                     value : "Project: "+Monk.component.dataManager.getProjectData().label+"\n"+
435                         Monk.component.dataManager.getWorksetLabel()+"\n"+
436                         "Time: "+new Date().format('Y-m-d H:i'),
437                     selectOnFocus : true,
438                     allowBlank: false
439                 },{
440                     xtype: 'fieldset',
441                     title: 'Email Results',
442                     checkboxToggle: true,
443                     checkboxName: 'emailResults',
444                     collapsed: true,
445                     autoHeight: true,
446                     items: [
447                         {
448                             xtype: 'panel',
449                             border: false,
450                             width: 300,
451                             style: 'margin: 5px 0 5px 52px',
452                             html: '<div>A notification will be emailed to you when results have completed. This is useful when analyzing large worksets.</div>'
453                         },{
454                             xtype: 'textfield',
455                             fieldLabel: 'Your Email',
456                             name: 'email',
457                             width: 175,
458                             selectOnFocus : true,
459                             validator: extraParamsValidator.createDelegate(this)
460                         }
461                     ]
462                 }
463             ],
464             buttons: [this.getFeaturesButton,this.showLogButton]
465         });
466     },
467 
468     /**
469      * Initializes the save workset dialog.
470      */
471     initializeSaveDialog : function() {
472         if (this.saveDialog == null) {
473             var worksetName = new Ext.form.TextField({
474                 fieldLabel: 'Name',
475                 name: 'name',
476                 width: 175,
477                 allowBlank: false
478             });
479             
480             var saveForm = new Ext.form.FormPanel({
481                 labelAlign: 'right',
482                 labelWidth: 70,
483                 border: false,
484                 items: [
485                     {xtype: 'panel',
486                     border: false,
487                     html: '<div style="padding: 5px;">You must save your workset before running an analysis. Please enter a name for your workset.</div>'},
488                     worksetName
489                 ]
490             });
491             
492             this.saveDialog = new Ext.Window({
493                 title: 'Save Workset',
494                 height: 130,
495                 width: 300,
496                 modal: true,
497                 shadow: true,
498                 plain: true,
499                 border: false,
500                 collapsible: false,
501                 items: saveForm,
502                 buttons: [
503                     {
504                         text: 'Save',
505                         handler: function(){
506                             if (saveForm.getForm().isValid()) {
507                                 var values = saveForm.getForm().getValues();
508                                 var worksetName = values.name;
509                                 Monk.component.dataManager.setWorksetName(worksetName);
510                                 Monk.data.workset.createWorkset(Monk.component.dataManager.getWorkset());
511                                 saveForm.getForm().reset();
512                                 this.saveDialog.hide();
513                                 Monk.component.dataManager.initializeProgressDialog(this.cachedData);
514                             }
515                         },
516                         scope: this
517                     },
518                     {
519                         text: 'Cancel',
520                         handler: function(){
521                             this.saveDialog.hide();
522                             this.getFeaturesButton.enable();
523                         },
524                         scope: this
525                     }
526                 ]
527             });
528         }
529         this.saveDialog.show();
530     }
531 });