source: trunk/web2/addons/job_monarch/js/jobgrid.js @ 572

Last change on this file since 572 was 572, checked in by ramonb, 15 years ago

job_monarch/js/jobgrid.js:

  • changed clusterimage node click host filter behaviour
File size: 18.5 KB
Line 
1var JobsDataStore;
2var JobsColumnModel;
3var JobListingEditorGrid;
4var JobListingWindow;
5var JobProxy;
6var SearchField;
7var myfilters = { };
8var myparams = { };
9var mylimit = 15;
10var ClusterImageArgs = { };
11
12var filterfields = [ "jid", "queue", "name", "owner" ];
13
14Ext.namespace('Ext.ux');
15
16Ext.ux.PageSizePlugin = function() {
17    Ext.ux.PageSizePlugin.superclass.constructor.call(this, {
18        store: new Ext.data.SimpleStore({
19            fields: ['text', 'value'],
20            data: [['10', 10], ['15', 15], ['20', 20], ['30', 30], ['50', 50], ['100', 100], ['max', 'max' ]]
21        }),
22        mode: 'local',
23        displayField: 'text',
24        valueField: 'value',
25        editable: false,
26        allowBlank: false,
27        triggerAction: 'all',
28        width: 40
29    });
30};
31
32Ext.extend(Ext.ux.PageSizePlugin, Ext.form.ComboBox, {
33    init: function(paging) {
34        paging.on('render', this.onInitView, this);
35    },
36   
37    onInitView: function(paging) {
38        paging.add('-',
39            this,
40            'jobs per page'
41        );
42        this.setValue(paging.pageSize);
43        this.on('select', this.onPageSizeChanged, paging);
44    },
45   
46    onPageSizeChanged: function(combo) {
47        if ( combo.getValue() == 'max' )
48          mylimit = JobsDataStore.getTotalCount();
49        else
50          mylimit = parseInt(combo.getValue());
51        this.pageSize = mylimit;
52        this.doLoad(0);
53    }
54});
55
56Ext.namespace( 'Ext' );
57
58function makeArrayURL( somearr )
59{
60  filter_url = '';
61  filter_sep = '';
62
63  for( filtername in somearr )
64  {
65    filter_url = filter_url + filter_sep + filtername + '=' + somearr[filtername];
66    filter_sep = '&';
67  }
68
69  return filter_url;
70}
71
72
73function isset( somevar )
74{
75  try
76  {
77    if( eval( somevar ) ) { }
78  }
79  catch( err )
80  {
81    return false;
82  }
83  return true;
84}
85
86function inMyArray( arr, someval )
87{
88  for( arval in arr )
89  {
90    if( arval == someval )
91    {
92      return true;
93    }
94  }
95  return false;
96}
97
98function inMyArrayValues( arr, someval )
99{
100  for( arkey in arr )
101  {
102    if( arr[arkey] == someval )
103    {
104      return true;
105    }
106  }
107  return false;
108}
109
110function inMyArrayKeys( arr, someval )
111{
112  for( arkey in arr )
113  {
114    if( arkey == someval )
115    {
116      return true;
117    }
118  }
119  return false;
120}
121
122function joinMyArray( arr1, arr2 )
123{
124  for( arkey in arr2 )
125  {
126    arr1[arkey] = arr2[arkey];
127  }
128
129  return arr1;
130}
131
132function ClusterImageSelectHost( somehost )
133{
134
135  if( !inMyArrayKeys( myfilters, 'host' ) )
136  {
137    myfilters['host'] = somehost;
138  }
139  else
140  {
141    if( myfilters['host'] == somehost )
142    {
143      delete myfilters['host'];
144      delete myparams['host'];
145    }
146    else
147    {
148      myfilters['host'] = somehost;
149    }
150  }
151
152  reloadClusterImage();
153  reloadJobStore();
154
155  return false;
156}
157
158function reloadJobStore()
159{
160  // Respect any other parameters that may have been set outside filters
161  //
162  myparams = joinMyArray( myparams, myfilters );
163
164  // Can't be sure if there are enough pages for new filter: reset to page 1
165  //
166  myparams = joinMyArray( myparams, { start: 0, limit: mylimit } );
167
168  JobsDataStore.reload( { params: myparams } );
169}
170
171function addListener(element, type, expression, bubbling)
172{
173  bubbling = bubbling || false;
174  if(window.addEventListener)
175  { // Standard
176    element.addEventListener(type, expression, bubbling);
177    return true;
178  } 
179  else if(window.attachEvent) 
180  { // IE
181    element.attachEvent('on' + type, expression);
182    return true;
183  } 
184  else 
185    return false;
186}
187
188function makeFilterString()
189{
190  var filter_str = '';
191
192  for( arkey in myfilters )
193  {
194    filter_str = filter_str + ' > ' + myfilters[arkey];
195  }
196
197  return filter_str;
198}
199
200var ImageLoader = function( id, url )
201{
202  this.url = url;
203  this.image = document.getElementById( id );
204  this.loadEvent = null;
205};
206
207ImageLoader.prototype = 
208{
209  load:function()
210  {
211    var url = this.url;
212    var image = this.image;
213    var loadEvent = this.loadEvent;
214    addListener( this.image, 'load', function(e)
215    {
216      if( loadEvent != null )
217      {
218        loadEvent( url, image );
219      }
220    }, false);
221    this.image.src = this.url;
222  },
223  getImage: function()
224  {
225    return this.image;
226  }
227};
228
229function achorJobListing()
230{
231  JobListingWindow.anchorTo( "ClusterImageWindow", "tr-br", [ 0, 10 ] );
232}
233
234function setClusterImagePosition()
235{
236  ci_x = (window.innerWidth - ClusterImageWindow.getSize()['width'] - 20); 
237  ClusterImageWindow.setPosition( ci_x, 10 );
238}
239
240function reloadClusterImage()
241{
242  ClusterImageArgs['view'] = 'big-clusterimage';
243
244  filt_url = makeArrayURL( myfilters );
245  imag_url = makeArrayURL( ClusterImageArgs );
246  img_url = './image.php?' + filt_url + '&' + imag_url;
247
248  var newClusterImage = new ImageLoader( 'clusterimage', img_url );
249  newClusterImage.loadEvent = function( url, image ) 
250    {
251      ClusterImageWindow.getBottomToolbar().clearStatus( { useDefaults:true } );
252      setTimeout( "resizeClusterImage()", 250 );
253      setTimeout( "setClusterImagePosition()", 500 );
254      setTimeout( "achorJobListing()", 1000 );
255    }
256
257  ClusterImageWindow.getBottomToolbar().showBusy();
258
259  filter_str = 'Nodes' + makeFilterString();
260  ClusterImageWindow.setTitle( filter_str );
261
262  newClusterImage.load();
263}
264
265function resizeClusterImage()
266{
267  var ci_height = document.getElementById( "clusterimage" ).height + ClusterImageWindow.getFrameHeight();
268  var ci_width = document.getElementById( "clusterimage" ).width + ClusterImageWindow.getFrameWidth();
269
270  ClusterImageWindow.setSize( ci_width, ci_height );
271}
272
273Ext.apply(Ext.form.VTypes, {
274        num: function(val, field) {
275
276                if (val) {
277                   var strValidChars = "0123456789";
278                   var blnResult = true;
279
280                   if (val.length == 0) return false;
281
282                   //  test strString consists of valid characters listed above
283                   for (i = 0; i < val.length && blnResult == true; i++)
284                      {
285                      strChar = val.charAt(i);
286                      if (strValidChars.indexOf(strChar) == -1)
287                         {
288                         blnResult = false;
289                         }
290                      }
291                   return blnResult;
292
293                }
294                },
295        numText: 'Must be numeric'
296});
297
298function initJobGrid() {
299
300  Ext.QuickTips.init();
301
302  function jobCellClick(grid, rowIndex, columnIndex, e)
303  {
304    var record = grid.getStore().getAt(rowIndex);  // Get the Record
305    var fieldName = grid.getColumnModel().getDataIndex(columnIndex);
306    var data = record.get(fieldName);
307    var view = grid.getView();
308    var cell = view.getCell( rowIndex, columnIndex );
309    var filter_title = false;
310    var fil_dis = 'filter';
311    var fil_ena = 'filterenabled';
312    var filterName = fieldName;
313
314    if( fieldName == 'owner' || fieldName == 'jid' || fieldName == 'status' || fieldName == 'queue' || fieldName == 'nodes')
315    {
316      if( fieldName == 'nodes' )
317      {
318        filterName = 'host';
319        fil_dis = 'nodesfilter';
320        fil_ena = 'nodesfilterenabled';
321      }
322      if( inMyArrayKeys( myfilters, filterName ) )
323      {
324        Ext.fly(cell).removeClass( fil_ena );
325        Ext.fly(cell).addClass( fil_dis );
326
327        // Remove this filter
328        //
329        delete myfilters[filterName];
330        delete myparams[filterName];
331
332        reloadJobStore();
333        reloadClusterImage();
334      }
335      else
336      {
337        Ext.fly(cell).removeClass( fil_dis );
338        Ext.fly(cell).addClass( fil_ena );
339
340        if( fieldName == 'nodes' )
341        { // Get the first node (master mom) as node filter
342          new_data = data.split( ',' )[0];
343          data = new_data;
344        }
345
346        // Set filter for selected column to selected cell value
347        //
348        myfilters[filterName] = data;
349
350        reloadJobStore();
351        reloadClusterImage();
352      }
353      JobListingWindow.setTitle( filter_str );
354      filter_title = true;
355      filter_str = myparams.c + ' Jobs Overview' + makeFilterString();
356    }
357  }
358
359  function jobCellRender( value, metadata, record, rowindex, colindex, store )
360  {
361    var fieldName = JobsColumnModel.getColumnById( colindex ).dataIndex;
362    var fil_dis = 'filter';
363    var fil_ena = 'filterenabled';
364    var filterName = fieldName;
365
366    if( fieldName == 'owner' || fieldName == 'jid' || fieldName == 'status' || fieldName == 'queue' || fieldName == 'nodes' )
367    {
368      if( fieldName == 'nodes' )
369      {
370        fil_dis = 'nodesfilter';
371        fil_ena = 'nodesfilterenabled';
372        filterName = 'host';
373      }
374      if( myfilters[filterName] != null )
375      {
376        metadata.css = fil_ena;
377      }
378      else
379      {
380        metadata.css = fil_dis;
381      }
382    }
383    return value;
384  }
385
386  JobProxy = new Ext.data.HttpProxy({
387                url: 'jobstore.php',
388                method: 'POST'
389            });
390
391
392  JobsDataStore = new Ext.data.Store({
393      id: 'JobsDataStore',
394      proxy: JobProxy,
395      baseParams: { task: "GETJOBS" },
396      reader: new Ext.data.JsonReader({
397        root: 'results',
398        totalProperty: 'total',
399        id: 'id'
400      },[
401        {name: 'jid', type: 'int', mapping: 'jid'},
402        {name: 'status', type: 'string', mapping: 'status'},
403        {name: 'owner', type: 'string', mapping: 'owner'},
404        {name: 'queue', type: 'string', mapping: 'queue'},
405        {name: 'name', type: 'string', mapping: 'name'},
406        {name: 'requested_time', type: 'string', mapping: 'requested_time'},
407        {name: 'requested_memory', type: 'string', mapping: 'requested_memory'},
408        {name: 'ppn', type: 'int', mapping: 'ppn'},
409        {name: 'nodect', type: 'int', mapping: 'nodect'},
410        {name: 'nodes', type: 'string', mapping: 'nodes'},
411        {name: 'queued_timestamp', type: 'string', mapping: 'queued_timestamp'},
412        {name: 'start_timestamp', type: 'string', mapping: 'start_timestamp'},
413        {name: 'runningtime', type: 'string', mapping: 'runningtime'}
414      ]),
415      sortInfo: { field: 'jid', direction: "DESC" },
416      remoteSort: true,
417      listeners:
418        { 
419                'beforeload':
420                {
421                        scope: this,
422                        fn: function()
423                        {
424                                if( SearchField )
425                                {
426                                        search_value = SearchField.getEl().dom.value;
427                                        if( search_value == '' )
428                                        {
429                                                delete SearchField.store.baseParams['query'];
430                                                delete myfilters['query'];
431                                                delete myparams['query'];
432                                        }
433                                }
434                        }
435                },
436                'load':
437                {
438                        scope: this,
439                        fn: function()
440                        {
441                                if( SearchField )
442                                {
443                                        search_value = SearchField.getEl().dom.value;
444
445                                        if( search_value != '' )
446                                        {
447                                                myfilters['query']      = search_value;
448                                        }
449
450                                        reloadClusterImage();
451
452                                        filter_str = myparams.c + ' Jobs Overview' + makeFilterString();
453                                        JobListingWindow.setTitle( filter_str );
454
455                                        if( search_value != '' )
456                                        {
457                                                delete myfilters['query'];
458                                        }
459                                }
460                        }
461                }
462        }
463    });
464   
465  var CheckJobs = new Ext.grid.CheckboxSelectionModel();
466
467  JobsColumnModel = new Ext.grid.ColumnModel(
468    [ CheckJobs,
469    {
470        header: '#',
471        tooltip: 'Job id',
472        readOnly: true,
473        dataIndex: 'jid',
474        width: 50,
475        hidden: false,
476        renderer: jobCellRender
477      },{
478        header: 'S',
479        tooltip: 'Job status',
480        readOnly: true,
481        dataIndex: 'status',
482        width: 20,
483        hidden: false,
484        renderer: jobCellRender
485      },{
486        header: 'User',
487        tooltip: 'Owner of job',
488        readOnly: true,
489        dataIndex: 'owner',
490        width: 60,
491        hidden: false,
492        renderer: jobCellRender
493      },{
494        header: 'Queue',
495        tooltip: 'In which queue does this job reside',
496        readOnly: true,
497        dataIndex: 'queue',
498        width: 60,
499        hidden: false,
500        renderer: jobCellRender
501      },{
502        header: 'Name',
503        tooltip: 'Name of job',
504        readOnly: true,
505        dataIndex: 'name',
506        width: 100,
507        hidden: false
508      },{
509        header: 'Requested Time',
510        tooltip: 'Amount of requested time (wallclock)',
511        readOnly: true,
512        dataIndex: 'requested_time',
513        width: 100,
514        hidden: false
515      },{
516        header: 'Requested Memory',
517        tooltip: 'Amount of requested memory',
518        readOnly: true,
519        dataIndex: 'requested_memory',
520        width: 100,
521        hidden: true
522      },{
523        header: 'P',
524        tooltip: 'Number of processors per node (PPN)',
525        readOnly: true,
526        dataIndex: 'ppn',
527        width: 25,
528        hidden: false
529      },{
530        header: 'N',
531        tooltip: 'Number of nodes (hosts)',
532        readOnly: true,
533        dataIndex: 'nodect',
534        width: 25,
535        hidden: false
536      },{
537        header: 'Nodes',
538        readOnly: true,
539        dataIndex: 'nodes',
540        width: 100,
541        hidden: false,
542        renderer: jobCellRender
543      },{
544        header: 'Queued',
545        tooltip: 'At what time did this job enter the queue',
546        readOnly: true,
547        dataIndex: 'queued_timestamp',
548        width: 120,
549        hidden: false
550      },{
551        header: 'Started',
552        tooltip: 'At what time did this job enter the running status',
553        readOnly: true,
554        dataIndex: 'start_timestamp',
555        width: 120,
556        hidden: false
557      },{
558        header: 'Runningtime',
559        tooltip: 'How long has this job been in the running status',
560        readOnly: true,
561        dataIndex: 'runningtime',
562        width: 140,
563        hidden: false
564      }]
565    );
566    JobsColumnModel.defaultSortable= true;
567
568  var win;
569
570  MetricsDataStore = new Ext.data.Store({
571      id: 'MetricsDataStore',
572      proxy: JobProxy,
573      autoLoad: false,
574      baseParams: { task: "GETMETRICS" },
575      reader: new Ext.data.JsonReader({
576        root: 'names',
577        totalProperty: 'total',
578        id: 'id'
579      },[{
580        name: 'ID'
581      },{
582        name: 'name'
583        }])
584  });
585
586  SearchField   = new Ext.app.SearchField({
587                                store: JobsDataStore,
588                                params: {start: 0, limit: mylimit},
589                                width: 200
590                    });
591
592  NodesDataStore = new Ext.data.Store({
593      id: 'NodesDataStore',
594      proxy: JobProxy,
595      autoLoad: false,
596      baseParams: { task: "GETNODES" },
597      reader: new Ext.data.JsonReader({
598        root: 'results',
599        totalProperty: 'total',
600        id: 'id'
601      },[
602        {name: 'c', type: 'string', mapping: 'c'},
603        {name: 'h', type: 'string', mapping: 'h'},
604        {name: 'x', type: 'string', mapping: 'x'},
605        {name: 'v', type: 'string', mapping: 'v'},
606        {name: 'l', type: 'string', mapping: 'l'},
607        {name: 'jr', type: 'string', mapping: 'jr'},
608        {name: 'js', type: 'string', mapping: 'js'}
609      ]),
610      listeners: {
611                'beforeload': {
612                        scope: this,
613                        fn: function() {
614                                        var jids;
615
616                                        var row_records = CheckJobs.getSelections();
617
618                                        for(var i=0; i<row_records.length; i++ )
619                                        {
620                                                rsel = row_records[i];
621                                                if( !jids )
622                                                {
623                                                        jids = rsel.get('jid');
624                                                }
625                                                else
626                                                {
627                                                        jids = jids + ',' + rsel.get('jid');
628                                                }
629                                        }
630                                        NodesDataStore.baseParams.jids  = jids;
631                                        NodesDataStore.baseParams.c     = myparams.c;
632                        }
633                }
634        }
635    });
636
637function ShowGraphs( Button, Event ) {
638   
639    var GraphView = new Ext.DataView({
640        itemSelector: 'thumb',
641        style:'overflow:auto',
642        multiSelect: true,
643        store: NodesDataStore,
644        tpl: new Ext.XTemplate(
645            '<tpl for=".">',
646            '<div class="rrd-float"><img src="../../graph.php?z=small&c={c}&h={h}&l={l}&v={v}&x={x}&r=job&jr={jr}&js={js}" border="0"></div>',
647            '</tpl>'
648        )
649    });
650
651    var images = new Ext.Panel({
652        id:'images',
653        //title:'My Images',
654        region:'center',
655        bodyStyle: 'background: transparent',
656        //margins: '2 2 2 0',
657        layout:'fit',
658        items: GraphView
659    });
660
661        if(!win){
662            win = new Ext.Window({
663                        animateTarget: Button,
664                        width       : 500,
665                        height      : 300,
666                        closeAction :'hide',
667                        collapsible: true,
668                        animCollapse: true,
669                        maximizable: true,
670                        title:  'Node graph details',
671                        layout: 'fit',
672                        tbar:   new Ext.form.ComboBox({
673                                        fieldLabel: 'Metric',
674                                        //hiddenName:'ID',
675                                        store: MetricsDataStore,
676                                        valueField:'name',
677                                        displayField:'name',
678                                        typeAhead: true,
679                                        mode: 'remote',
680                                        triggerAction: 'all',
681                                        emptyText:'Select metric',
682                                        selectOnFocus:true,
683                                        xtype: 'combo',
684                                        width:190,
685                                        listeners: {
686                                                select: function(combo, record, index){
687                                                        var metric = record.data.name;
688                                                        // doe iets
689                                                }
690                                        }
691                                       
692                                }),
693                        items:  [ images ]
694                    });
695        }
696        NodesDataStore.load();
697        win.show(Button);
698}
699
700  JobListingEditorGrid =  new Ext.grid.EditorGridPanel({
701      id: 'JobListingEditorGrid',
702      store: JobsDataStore,
703      cm: JobsColumnModel,
704      enableColLock:false,
705      clicksToEdit:1,
706      loadMask: true,
707      selModel: new Ext.grid.RowSelectionModel({singleSelect:false}),
708      stripeRows: true,
709      sm: CheckJobs,
710      bbar: new Ext.PagingToolbar({
711                pageSize: 15,
712                store: JobsDataStore,
713                displayInfo: true,
714                displayMsg: 'Displaying jobs {0} - {1} out of {2} jobs total found.',
715                emptyMsg: 'No jobs found to display',
716                plugins: [new Ext.ux.PageSizePlugin()]
717            }),
718      tbar: [ SearchField,
719                new Ext.Button({
720                                text: 'Show graphs',
721                                tooltip: 'Show node graphs for selected jobs',
722                                iconCls: 'option',
723                                listeners: {
724                                        'click': {
725                                                scope: this,
726                                                fn: ShowGraphs
727                                        }
728                                }
729                        }) ]
730    });
731
732  ClusterImageWindow = new Ext.Window({
733      id: 'ClusterImageWindow',
734      title: 'Nodes',
735      closable: true,
736      collapsible: true,
737      animCollapse: true,
738      width: 1,
739      height: 1,
740      y: 15,
741      plain: true,
742      shadow: true,
743      resizable: false,
744      shadowOffset: 10,
745      layout: 'fit',
746      bbar: new Ext.StatusBar({
747                defaultText: 'Ready.',
748                id: 'basic-statusbar',
749                defaultIconCls: ''
750        })
751    });
752
753  GraphSummaryWindow = new Ext.Window({
754      id: 'GraphSummaryWindow',
755      title: 'Graph Summary',
756      closable: true,
757      collapsible: true,
758      animCollapse: true,
759      width: 500,
760      height: 400,
761      x: 10,
762      y: 10,
763      plain: true,
764      shadow: true,
765      resizable: true,
766      shadowOffset: 10,
767      layout: 'table',
768      layoutConfig: {
769                columns: 2
770        },
771      defaults:{border: false},
772      items: [{
773        id: 'monarchlogo',
774        cls: 'monarch',
775        bodyStyle: 'background: transparent',
776        html: '<A HREF="https://subtrac.sara.nl/oss/jobmonarch/" TARGET="_blank"><IMG SRC="./jobmonarch.gif" ALT="Job Monarch" BORDER="0"></A>'
777        //colspan: 2
778       },{
779        id: 'summarycount'
780       },{
781        id: 'rjqjgraph'
782       },{
783        id: 'pie',
784        colspan: 2
785       }],
786      bbar: new Ext.StatusBar({
787                defaultText: 'Ready.',
788                id: 'basic-statusbar',
789                defaultIconCls: ''
790        })
791    });
792
793  JobListingWindow = new Ext.Window({
794      id: 'JobListingWindow',
795      title: 'Cluster Jobs Overview',
796      closable:true,
797      collapsible: true,
798      animCollapse: true,
799      maximizable: true,
800      y: 375,
801      width:860,
802      height:445,
803      plain:true,
804      shadow: true,
805      shadowOffset: 10,
806      layout: 'fit',
807      items: JobListingEditorGrid
808    });
809
810  JobListingEditorGrid.addListener( 'cellclick', jobCellClick );
811}
Note: See TracBrowser for help on using the repository browser.