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

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

job_monarch/css/styles.css:

  • added nodes filter class

job_monarch/js/jobgrid.js:

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