Selaa lähdekoodia

AMBARI-5260. Jobs table should support pagination (alexantonenko)

Alex Antonenko 11 vuotta sitten
vanhempi
commit
7f60c1c40c

+ 100 - 4
ambari-web/app/controllers/main/jobs_controller.js

@@ -85,6 +85,12 @@ App.MainJobsController = Em.Controller.extend({
     sortedContent.length = 0;
   },
 
+  navIDs: {
+    backIDs: [],
+    nextID: ''
+  },
+  lastJobID: '',
+  hasNewJobs: false,
   loaded : false,
   loading : false,
   loadJobsTimeout: null,
@@ -102,6 +108,14 @@ App.MainJobsController = Em.Controller.extend({
     }
   }.observes('sortingColumn.name','sortingColumn.status'),
 
+  updateJobsByClick: function () {
+    this.set('navIDs.backIDs', []);
+    this.set('navIDs.nextID', '');
+    this.get('filterObject').set('nextFromId', '');
+    this.get('filterObject').set('backFromId', '');
+    this.get('filterObject').set('fromTs', '');
+  },
+
   updateJobs: function (controllerName, funcName) {
     clearInterval(this.get('jobsUpdate'));
     var self = this;
@@ -125,6 +139,10 @@ App.MainJobsController = Em.Controller.extend({
     user: "",
     windowStart: "",
     windowEnd: "",
+    nextFromId: "",
+    backFromId: "",
+    fromTs: "",
+    isAnyFilterApplied: false,
 
     onApplyIdFilter: function () {
       if(this.get('id') == ""){
@@ -285,20 +303,40 @@ App.MainJobsController = Em.Controller.extend({
      */
     createJobsFiltersLink: function() {
       var link = "?fields=events,primaryfilters,otherinfo";
+      var numberOfAppliedFilters = 0;
 
       if(this.get("id") !== "") {
         link = "/" + this.get("id") + link;
+        numberOfAppliedFilters++;
       }
 
-      link += "&limit=" + this.get("jobsLimit");
+      link += "&limit=" + (parseInt(this.get("jobsLimit")) + 1);
 
       if(this.get("user") !== ""){
         link += "&primaryFilter=user:" + this.get("user");
+        numberOfAppliedFilters++;
+      }
+      if(this.get("backFromId") != ""){
+        link += "&fromId=" + this.get("backFromId");
+      }
+      if(this.get("nextFromId") != ""){
+        link += "&fromId=" + this.get("nextFromId");
+      }
+      if(this.get("fromTs") != ""){
+        link += "&fromTs=" + this.get("fromTs");
       }
-      if(this.get("startTime") !== ""){
+      if(this.get("startTime") !== "" && this.get("startTime") !== "Any"){
         link += this.get("windowStart") !== "" ? ("&windowStart=" + this.get("windowStart")) : "";
         link += this.get("windowEnd") !== "" ? ("&windowEnd=" + this.get("windowEnd")) : "";
+        numberOfAppliedFilters++;
       }
+
+      if(numberOfAppliedFilters > 0){
+        this.set('isAnyFilterApplied', true);
+      }else{
+        this.set('isAnyFilterApplied', false);
+      }
+
       return link;
     }
   }),
@@ -313,6 +351,22 @@ App.MainJobsController = Em.Controller.extend({
     ]
   }),
 
+  lastIDSuccessCallback: function(data, jqXHR, textStatus) {
+    var lastReceivedID = data.entities[0].entity;
+    if(this.get('lastJobID') == '') {
+      this.set('lastJobID', lastReceivedID);
+    } else {
+      if (this.get('lastJobID') !== lastReceivedID) {
+        this.set('lastJobID', lastReceivedID);
+        this.set('hasNewJobs', true);
+      }
+    }
+  },
+
+  lastIDErrorCallback: function(data, jqXHR, textStatus) {
+    console.debug(jqXHR);
+  },
+
   loadJobs : function() {
     var self = this;
     var timeout = this.get('loadTimeout');
@@ -322,10 +376,29 @@ App.MainJobsController = Em.Controller.extend({
       var historyServerHostName = yarnService.get('appTimelineServerNode.hostName');
       var filtersLink = this.get('filterObject').createJobsFiltersLink();
       var hiveQueriesUrl = App.testMode ? "/data/jobs/hive-queries.json" : "/proxy?url=http://" + historyServerHostName
-          + ":" + yarnService.get('ahsWebPort') + "/ws/v1/timeline/HIVE_QUERY_ID" + filtersLink;
+        + ":" + yarnService.get('ahsWebPort') + "/ws/v1/timeline/HIVE_QUERY_ID" + filtersLink;
+      App.ajax.send({
+        name: 'jobs.lastID',
+        sender: self,
+        data: {
+          historyServerHostName: historyServerHostName,
+          ahsWebPort: yarnService.get('ahsWebPort')
+        },
+        success: 'lastIDSuccessCallback',
+        error : 'lastIDErrorCallback'
+      }),
       App.HttpClient.get(hiveQueriesUrl, App.hiveJobsMapper, {
-        complete : function(jqXHR, textStatus) {
+        complete : function(data, jqXHR, textStatus) {
           self.set('loading', false);
+          if(self.get('loaded') == false){
+            var back_link_IDs = self.get('navIDs.backIDs.[]');
+            if(!back_link_IDs.contains(App.HiveJob.find().objectAt(0).get('id'))) {
+              back_link_IDs.push(App.HiveJob.find().objectAt(0).get('id'));
+              self.set('navIDs.backIDs.[]', back_link_IDs);
+            }
+            self.set('filterObject.backFromId', App.HiveJob.find().objectAt(0).get('id'));
+            self.get('filterObject').set('fromTs', App.get('currentServerTime'));
+          }
           self.set('loaded', true);
         }
       }, function (jqXHR, textStatus) {
@@ -339,6 +412,29 @@ App.MainJobsController = Em.Controller.extend({
     }
   },
 
+  navigateNext: function() {
+    this.set("filterObject.backFromId", '');
+    var back_link_IDs = this.get('navIDs.backIDs.[]');
+    var lastBackID = this.get('navIDs.nextID');
+    if(!back_link_IDs.contains(lastBackID)) {
+      back_link_IDs.push(lastBackID);
+    }
+    this.set('navIDs.backIDs.[]', back_link_IDs);
+    this.set("filterObject.nextFromId", this.get('navIDs.nextID'));
+    this.set('navIDs.nextID', '');
+    this.loadJobs();
+  },
+
+  navigateBack: function() {
+    this.set("filterObject.nextFromId", '');
+    var back_link_IDs = this.get('navIDs.backIDs.[]');
+    back_link_IDs.pop();
+    var lastBackID = back_link_IDs[back_link_IDs.length - 1]
+    this.set('navIDs.backIDs.[]', back_link_IDs);
+    this.set("filterObject.backFromId", lastBackID);
+    this.loadJobs();
+  },
+
   refreshLoadedJobs : function() {
     var timeout = this.get('loadJobsTimeout');
     var self = this;

+ 10 - 1
ambari-web/app/mappers/jobs/hive_jobs_mapper.js

@@ -32,7 +32,7 @@ App.hiveJobsMapper = App.QuickDataMapper.create({
           json.entities = [json];
         }
       }
-      var currentEntityMap = {}
+      var currentEntityMap = {};
       json.entities.forEach(function(entity) {
         currentEntityMap[entity.entity] = entity.entity;
         var hiveJob = {
@@ -75,6 +75,15 @@ App.hiveJobsMapper = App.QuickDataMapper.create({
         hiveJob = null;
         entity = null;
       });
+
+      if(hiveJobs.length > App.router.get('mainJobsController.filterObject.jobsLimit')) {
+        var lastJob = hiveJobs.pop();
+        if(App.router.get('mainJobsController.navIDs.nextID') != lastJob.id) {
+          App.router.set('mainJobsController.navIDs.nextID', lastJob.id);
+        }
+        currentEntityMap[lastJob.id] = null;
+      }
+
       // Delete IDs not seen from server
       var hiveJobsModel = model.find().toArray();
       hiveJobsModel.forEach(function(job) {

+ 1 - 0
ambari-web/app/messages.js

@@ -1833,6 +1833,7 @@ Em.I18n.translations = {
   'jobs.column.start.time':'Start Time',
   'jobs.column.end.time':'End Time',
   'jobs.column.duration':'Duration',
+  'jobs.new_jobs.info':'New jobs available on server.',
 
   'apps.table.column.appId':'App ID',
   'apps.table.column.runDate': 'Run Date',

+ 39 - 0
ambari-web/app/styles/apps.less

@@ -194,6 +194,24 @@
       a {
         padding:0 5px;
       }
+      a.paginate_disabled_next, a.paginate_disabled_previous {
+        color: gray;
+        &:hover {
+          color: gray;
+          text-decoration: none;
+          cursor: default;
+          i {
+            cursor: default;
+          }
+        }
+      }
+
+      a.paginate_next, a.paginate_previous {
+        &:hover {
+          text-decoration: none;
+          cursor: pointer;
+        }
+      }
     }
   }
   #graph1 {
@@ -310,6 +328,12 @@
     margin-top: -24px;
   }
 
+  .new-jobs-link {
+    float: left;
+    margin-left: 496px;
+    margin-top: -20px;
+  }
+
   #filtered-jobs{
     float: left;
     margin-top: 8px;
@@ -348,6 +372,21 @@
       a {
         padding:0 5px;
       }
+      a.paginate_disabled_next, a.paginate_disabled_previous {
+        color: gray;
+        &:hover i{
+          color: gray;
+          text-decoration: none;
+          cursor: default;
+        }
+      }
+
+      a.paginate_next, a.paginate_previous {
+        &:hover {
+          text-decoration: none;
+          cursor: pointer;
+        }
+      }
     }
   }
 

+ 9 - 0
ambari-web/app/templates/main/jobs.hbs

@@ -20,6 +20,11 @@
   <div id="jobs">
       <div class="jobs_head">
         <div>{{t menu.item.jobs}}</div>
+        {{#if controller.hasNewJobs}}
+          <div class="new-jobs-link">
+            <a href="javascript:void(null);" {{action updateJobsByClick target="controller"}}>{{t jobs.new_jobs.info}}</a>
+          </div>
+        {{/if}}
         <div class="jobs-type">
          {{t jobs.type}} : <span class="label label-info">{{t jobs.type.hive}}</span>
         </div>
@@ -91,6 +96,10 @@
           <div class="items-on-page">
               <label>{{t jobs.show.up.to}}: {{view view.rowsPerPageSelectView selectionBinding="view.displayLength"}}</label>
           </div>
+        <div class="paging_two_button">
+          {{view view.jobsPaginationLeft}}
+          {{view view.jobsPaginationRight}}
+        </div>
       </div>
   </div>
 

+ 6 - 0
ambari-web/app/utils/ajax.js

@@ -1724,6 +1724,12 @@ var urls = {
     }
   },
 
+  'jobs.lastID': {
+    'real': '/proxy?url=http://{historyServerHostName}:{ahsWebPort}/ws/v1/timeline/HIVE_QUERY_ID?limit=1',
+    'mock': 'data/jobs/hive-queries.json',
+    'apiPrefix': ''
+  },
+
   'jobs.tezDag.NametoID': {
     'real': '/proxy?url=http://{historyServerHostName}:{ahsWebPort}/ws/v1/timeline/TEZ_DAG_ID?primaryFilter=dagName:{tezDagName}',
     'mock': '/data/jobs/tezDag-name-to-id.json',

+ 61 - 4
ambari-web/app/views/main/jobs_view.js

@@ -107,9 +107,9 @@ App.MainJobsView = App.TableView.extend({
       this.updateFilter(4, this.get('controller.filterObject.windowEnd'), 'date');
     }
   }.observes(
-      'controller.filterObject.id',
-      'controller.filterObject.user',
-      'controller.filterObject.windowEnd'
+    'controller.filterObject.id',
+    'controller.filterObject.user',
+    'controller.filterObject.windowEnd'
   ),
 
   sortView: sort.wrapperView,
@@ -151,7 +151,19 @@ App.MainJobsView = App.TableView.extend({
   rowsPerPageSelectView: Em.Select.extend({
     content: ['10', '25', '50', '100', "250", "500"],
     valueBinding: "controller.filterObject.jobsLimit",
+    attributeBindings: ['disabled'],
+    disabled: false,
+    disabledObserver: function () {
+      if (this.get("parentView.hasBackLinks")) {
+        this.set('disabled', true);
+      }else{
+        this.set('disabled', false);
+      }
+    }.observes('parentView.hasBackLinks'),
+    attributeBindings: ['disabled'],
+    disabled: false,
     change: function () {
+      this.get('controller').set('navIDs.nextID', '');
       this.get('parentView').saveDisplayLength();
     }
   }),
@@ -274,5 +286,50 @@ App.MainJobsView = App.TableView.extend({
 
   jobFailMessage: function() {
     return Em.I18n.t('jobs.table.job.fail');
-  }.property()
+  }.property(),
+
+  jobsPaginationLeft: Ember.View.extend({
+    tagName: 'a',
+    template: Ember.Handlebars.compile('<i class="icon-arrow-left"></i>'),
+    classNameBindings: ['class'],
+    class: function () {
+      if (this.get("parentView.hasBackLinks") && !this.get('controller.filterObject.isAnyFilterApplied')) {
+        return "paginate_previous";
+      }
+      return "paginate_disabled_previous";
+    }.property('parentView.hasBackLinks', 'controller.filterObject.isAnyFilterApplied'),
+
+    click: function () {
+      if (this.get("parentView.hasBackLinks") && !this.get('controller.filterObject.isAnyFilterApplied')) {
+        this.get('controller').navigateBack();
+      }
+    }
+  }),
+
+  jobsPaginationRight: Ember.View.extend({
+    tagName: 'a',
+    template: Ember.Handlebars.compile('<i class="icon-arrow-right"></i>'),
+    classNameBindings: ['class'],
+    class: function () {
+      if (this.get("parentView.hasNextJobs") && !this.get('controller.filterObject.isAnyFilterApplied')) {
+        return "paginate_next";
+      }
+      return "paginate_disabled_next";
+    }.property("parentView.hasNextJobs", 'controller.filterObject.isAnyFilterApplied'),
+
+    click: function () {
+      if (this.get("parentView.hasNextJobs") && !this.get('controller.filterObject.isAnyFilterApplied')) {
+        this.get('controller').navigateNext();
+      }
+    }
+  }),
+
+  hasNextJobs: function() {
+    return (this.get("controller.navIDs.nextID.length") > 0);
+  }.property('controller.navIDs.nextID'),
+
+  hasBackLinks: function() {
+    return (this.get("controller.navIDs.backIDs").length > 1);
+  }.property('controller.navIDs.backIDs.[].length')
+
 })