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

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

job_monarch/js/jobgrid.js:

  • search query is now properly cleared from filters
  • append filters to Window titles
File size: 16.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    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 Overview' + 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
303    if( fieldName == 'owner' || fieldName == 'jid' || fieldName == 'status' || fieldName == 'queue' )
304    {
305      if( inMyArrayKeys( myfilters, fieldName ) )
306      {
307        Ext.fly(cell).removeClass( 'filterenabled' );
308        Ext.fly(cell).addClass( 'filter' );
309
310        // Remove this filter
311        //
312        delete myfilters[fieldName];
313        delete myparams[fieldName];
314
315        reloadJobStore();
316        reloadClusterImage();
317      }
318      else
319      {
320        Ext.fly(cell).removeClass( 'filter' );
321        Ext.fly(cell).addClass( 'filterenabled' );
322
323        // Set filter for selected column to selected cell value
324        //
325        myfilters[fieldName] = data;
326
327        reloadJobStore();
328        reloadClusterImage();
329      }
330      filter_str = myparams.c + ' Jobs Overview' + makeFilterString();
331      JobListingWindow.setTitle( filter_str );
332    }
333  }
334
335  function jobCellRender( value, metadata, record, rowindex, colindex, store )
336  {
337    var fieldName = JobsColumnModel.getColumnById( colindex ).dataIndex;
338
339    if( fieldName == 'owner' || fieldName == 'jid' || fieldName == 'status' || fieldName == 'queue' )
340    {
341      if( myfilters[fieldName] != null )
342      {
343        metadata.css = 'filterenabled';
344      }
345      else
346      {
347        metadata.css = 'filter';
348      }
349    }
350    return value;
351  }
352
353  JobProxy = new Ext.data.HttpProxy({
354                url: 'jobstore.php',
355                method: 'POST'
356            });
357
358
359  JobsDataStore = new Ext.data.Store({
360      id: 'JobsDataStore',
361      proxy: JobProxy,
362      baseParams: { task: "GETJOBS" },
363      reader: new Ext.data.JsonReader({
364        root: 'results',
365        totalProperty: 'total',
366        id: 'id'
367      },[
368        {name: 'jid', type: 'int', mapping: 'jid'},
369        {name: 'status', type: 'string', mapping: 'status'},
370        {name: 'owner', type: 'string', mapping: 'owner'},
371        {name: 'queue', type: 'string', mapping: 'queue'},
372        {name: 'name', type: 'string', mapping: 'name'},
373        {name: 'requested_time', type: 'string', mapping: 'requested_time'},
374        {name: 'requested_memory', type: 'string', mapping: 'requested_memory'},
375        {name: 'ppn', type: 'int', mapping: 'ppn'},
376        {name: 'nodect', type: 'int', mapping: 'nodect'},
377        {name: 'nodes', type: 'string', mapping: 'nodes'},
378        {name: 'queued_timestamp', type: 'string', mapping: 'queued_timestamp'},
379        {name: 'start_timestamp', type: 'string', mapping: 'start_timestamp'},
380        {name: 'runningtime', type: 'string', mapping: 'runningtime'}
381      ]),
382      sortInfo: { field: 'jid', direction: "DESC" },
383      remoteSort: true,
384      listeners:
385        { 
386                'beforeload':
387                {
388                        scope: this,
389                        fn: function()
390                        {
391                                if( SearchField )
392                                {
393                                        search_value = SearchField.getEl().dom.value;
394                                        if( search_value == '' )
395                                        {
396                                                delete SearchField.store.baseParams['query'];
397                                                delete myfilters['query'];
398                                                delete myparams['query'];
399                                        }
400                                }
401                        }
402                },
403                'load':
404                {
405                        scope: this,
406                        fn: function()
407                        {
408                                if( SearchField )
409                                {
410                                        search_value = SearchField.getEl().dom.value;
411
412                                        if( search_value != '' )
413                                        {
414                                                myfilters['query']      = search_value;
415                                        }
416
417                                        reloadClusterImage();
418
419                                        filter_str = myparams.c + ' Jobs Overview' + makeFilterString();
420                                        JobListingWindow.setTitle( filter_str );
421
422                                        if( search_value != '' )
423                                        {
424                                                delete myfilters['query'];
425                                        }
426                                }
427                        }
428                }
429        }
430    });
431   
432  var CheckJobs = new Ext.grid.CheckboxSelectionModel();
433
434  JobsColumnModel = new Ext.grid.ColumnModel(
435    [ CheckJobs,
436    {
437        header: '#',
438        tooltip: 'Job id',
439        readOnly: true,
440        dataIndex: 'jid',
441        width: 50,
442        hidden: false,
443        renderer: jobCellRender
444      },{
445        header: 'S',
446        tooltip: 'Job status',
447        readOnly: true,
448        dataIndex: 'status',
449        width: 20,
450        hidden: false,
451        renderer: jobCellRender
452      },{
453        header: 'User',
454        tooltip: 'Owner of job',
455        readOnly: true,
456        dataIndex: 'owner',
457        width: 60,
458        hidden: false,
459        renderer: jobCellRender
460      },{
461        header: 'Queue',
462        tooltip: 'In which queue does this job reside',
463        readOnly: true,
464        dataIndex: 'queue',
465        width: 60,
466        hidden: false,
467        renderer: jobCellRender
468      },{
469        header: 'Name',
470        tooltip: 'Name of job',
471        readOnly: true,
472        dataIndex: 'name',
473        width: 100,
474        hidden: false
475      },{
476        header: 'Requested Time',
477        tooltip: 'Amount of requested time (wallclock)',
478        readOnly: true,
479        dataIndex: 'requested_time',
480        width: 100,
481        hidden: false
482      },{
483        header: 'Requested Memory',
484        tooltip: 'Amount of requested memory',
485        readOnly: true,
486        dataIndex: 'requested_memory',
487        width: 100,
488        hidden: true
489      },{
490        header: 'P',
491        tooltip: 'Number of processors per node (PPN)',
492        readOnly: true,
493        dataIndex: 'ppn',
494        width: 25,
495        hidden: false
496      },{
497        header: 'N',
498        tooltip: 'Number of nodes (hosts)',
499        readOnly: true,
500        dataIndex: 'nodect',
501        width: 25,
502        hidden: false
503      },{
504        header: 'Nodes',
505        readOnly: true,
506        dataIndex: 'nodes',
507        width: 100,
508        hidden: true
509      },{
510        header: 'Queued',
511        tooltip: 'At what time did this job enter the queue',
512        readOnly: true,
513        dataIndex: 'queued_timestamp',
514        width: 120,
515        hidden: false
516      },{
517        header: 'Started',
518        tooltip: 'At what time did this job enter the running status',
519        readOnly: true,
520        dataIndex: 'start_timestamp',
521        width: 120,
522        hidden: false
523      },{
524        header: 'Runningtime',
525        tooltip: 'How long has this job been in the running status',
526        readOnly: true,
527        dataIndex: 'runningtime',
528        width: 140,
529        hidden: false
530      }]
531    );
532    JobsColumnModel.defaultSortable= true;
533
534  var win;
535
536  SearchField   = new Ext.app.SearchField({
537                                store: JobsDataStore,
538                                params: {start: 0, limit: mylimit},
539                                width: 200
540                    });
541
542  NodesDataStore = new Ext.data.Store({
543      id: 'NodesDataStore',
544      proxy: JobProxy,
545      autoLoad: false,
546      baseParams: { task: "GETNODES" },
547      reader: new Ext.data.JsonReader({
548        root: 'results',
549        totalProperty: 'total',
550        id: 'id'
551      },[
552        {name: 'c', type: 'string', mapping: 'c'},
553        {name: 'h', type: 'string', mapping: 'h'},
554        {name: 'v', type: 'string', mapping: 'v'},
555        {name: 'x', type: 'string', mapping: 'x'}
556      ]),
557      listeners: {
558                'beforeload': {
559                        scope: this,
560                        fn: function() {
561                                        var jids;
562
563                                        var row_records = CheckJobs.getSelections();
564
565                                        for(var i=0; i<row_records.length; i++ )
566                                        {
567                                                rsel = row_records[i];
568                                                if( !jids )
569                                                {
570                                                        jids = rsel.get('jid');
571                                                }
572                                                else
573                                                {
574                                                        jids = jids + ',' + rsel.get('jid');
575                                                }
576                                        }
577                                        NodesDataStore.baseParams.jids  = jids;
578                                        NodesDataStore.baseParams.c     = myparams.c;
579                        }
580                }
581        }
582    });
583
584function ShowGraphs( Button, Event ) {
585   
586    var GraphView = new Ext.DataView({
587        itemSelector: 'thumb',
588        style:'overflow:auto',
589        multiSelect: true,
590        store: NodesDataStore,
591        tpl: new Ext.XTemplate(
592            '<tpl for=".">',
593            '<div class="thumb"><img src="../../graph.php?z=small&h={h}&x={x}&v={v}&c={c}" border="0"></div>',
594            '</tpl>'
595        )
596    });
597
598    var images = new Ext.Panel({
599        id:'images',
600        title:'My Images',
601        region:'center',
602        margins: '5 5 5 0',
603        layout:'fit',
604        items: GraphView
605    });
606
607        if(!win){
608            win = new Ext.Window({
609                        animateTarget: Button,
610                        width       : 500,
611                        height      : 300,
612                        closeAction :'hide',
613                        items:  [ images ]
614                    });
615                }
616        NodesDataStore.load();
617        win.show(Button);
618}
619
620  JobListingEditorGrid =  new Ext.grid.EditorGridPanel({
621      id: 'JobListingEditorGrid',
622      store: JobsDataStore,
623      cm: JobsColumnModel,
624      enableColLock:false,
625      clicksToEdit:1,
626      loadMask: true,
627      selModel: new Ext.grid.RowSelectionModel({singleSelect:false}),
628      stripeRows: true,
629      sm: CheckJobs,
630      bbar: new Ext.PagingToolbar({
631                pageSize: 15,
632                store: JobsDataStore,
633                displayInfo: true,
634                displayMsg: 'Displaying jobs {0} - {1} out of {2} jobs total found.',
635                emptyMsg: 'No jobs found to display',
636                plugins: [new Ext.ux.PageSizePlugin()]
637            }),
638      tbar: [ SearchField,
639                new Ext.Button({
640                                text: 'Show graphs',
641                                tooltip: 'Show node graphs for selected jobs',
642                                iconCls: 'option',
643                                listeners: {
644                                        'click': {
645                                                scope: this,
646                                                fn: ShowGraphs
647                                        }
648                                }
649                        }) ]
650    });
651
652  ClusterImageWindow = new Ext.Window({
653      id: 'ClusterImageWindow',
654      title: 'Nodes',
655      closable: true,
656      collapsible: true,
657      animCollapse: true,
658      width: 1,
659      height: 1,
660      y: 15,
661      plain: true,
662      shadow: true,
663      resizable: false,
664      shadowOffset: 10,
665      layout: 'fit',
666      bbar: new Ext.StatusBar({
667                defaultText: 'Ready.',
668                id: 'basic-statusbar',
669                defaultIconCls: ''
670        })
671    });
672
673  GraphSummaryWindow = new Ext.Window({
674      id: 'GraphSummaryWindow',
675      title: 'Graph Summary',
676      closable: true,
677      collapsible: true,
678      animCollapse: true,
679      width: 300,
680      height: 500,
681      x: 10,
682      y: 10,
683      plain: true,
684      shadow: true,
685      resizable: true,
686      shadowOffset: 10,
687      layout: 'table',
688      layoutConfig: {
689                columns: 2
690        },
691      defaults:{border: false},
692      items: [{
693        id: 'monarchlogo',
694        cls: 'monarch',
695        bodyStyle: 'background: transparent',
696        html: '<A HREF="https://subtrac.sara.nl/oss/jobmonarch/" TARGET="_blank"><IMG SRC="./jobmonarch.gif" ALT="Job Monarch" BORDER="0"></A>'
697        //colspan: 2
698       },{
699        id: 'summarycount'
700       },{
701        id: 'rjqjgraph'
702       },{
703        id: 'pie',
704        colspan: 2
705       }],
706      bbar: new Ext.StatusBar({
707                defaultText: 'Ready.',
708                id: 'basic-statusbar',
709                defaultIconCls: ''
710        })
711    });
712
713  JobListingWindow = new Ext.Window({
714      id: 'JobListingWindow',
715      title: 'Cluster Jobs Overview',
716      closable:true,
717      collapsible: true,
718      animCollapse: true,
719      maximizable: true,
720      y: 375,
721      width:860,
722      height:427,
723      plain:true,
724      shadow: true,
725      shadowOffset: 10,
726      layout: 'fit',
727      items: JobListingEditorGrid
728    });
729
730  JobListingEditorGrid.addListener( 'cellclick', jobCellClick );
731}
Note: See TracBrowser for help on using the repository browser.