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 });