You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by al...@apache.org on 2014/02/13 00:12:33 UTC

git commit: AMBARI-4642. Jobs: implement server side filtering/limit on Apps table (alexantonenko)

Updated Branches:
  refs/heads/trunk f64fed6a1 -> 99a26c9a5


AMBARI-4642. Jobs: implement server side filtering/limit on Apps table (alexantonenko)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/99a26c9a
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/99a26c9a
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/99a26c9a

Branch: refs/heads/trunk
Commit: 99a26c9a50cf019eefddb13d7aec03c6145eb65b
Parents: f64fed6
Author: Alex Antonenko <hi...@gmail.com>
Authored: Thu Feb 13 01:07:12 2014 +0200
Committer: Alex Antonenko <hi...@gmail.com>
Committed: Thu Feb 13 01:07:12 2014 +0200

----------------------------------------------------------------------
 .../app/controllers/main/jobs_controller.js     | 220 ++++++++++++++++++-
 ambari-web/app/messages.js                      |   7 +
 ambari-web/app/styles/apps.less                 |   5 +
 .../templates/main/jobs/custom_dates_popup.hbs  |  39 ++++
 ambari-web/app/views.js                         |   1 +
 .../views/main/jobs/select_custom_date_view.js  |  37 ++++
 ambari-web/app/views/main/jobs_view.js          |  22 +-
 7 files changed, 303 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/99a26c9a/ambari-web/app/controllers/main/jobs_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/jobs_controller.js b/ambari-web/app/controllers/main/jobs_controller.js
index 00980c7..c97b06b 100644
--- a/ambari-web/app/controllers/main/jobs_controller.js
+++ b/ambari-web/app/controllers/main/jobs_controller.js
@@ -26,10 +26,190 @@ App.MainJobsController = Em.ArrayController.extend({
 
   loaded : false,
   loading : false,
-  /**
-   * The number of jobs to be shown by last submitted time.
-   */
-  jobsLimit : -1,
+  loadJobsTimeout: null,
+
+  totalOfJobs: 0,
+  setTotalOfJobs: function () {
+    if(this.get('totalOfJobs') < this.get('content.length')){
+      this.set('totalOfJobs', this.get('content.length'));
+    }
+  }.observes('content.length'),
+
+  filterObject: Ember.Object.create({
+    id: "",
+    jobsLimit: -1,
+    user: "",
+    windowStart: "",
+    windowEnd: "",
+
+    /**
+     * Direct binding to startTime filter field
+     */
+    startTime: "",
+    onStartTimeChange:function(){
+      var time = "";
+      var curTime = new Date().getTime();
+      switch (this.get('startTime')) {
+        case 'Past 1 hour':
+          time = curTime - 3600000;
+          break;
+        case 'Past 1 Day':
+          time = curTime - 86400000;
+          break;
+        case 'Past 2 Days':
+          time = curTime - 172800000;
+          break;
+        case 'Past 7 Days':
+          time = curTime - 604800000;
+          break;
+        case 'Past 14 Days':
+          time = curTime - 1209600000;
+          break;
+        case 'Past 30 Days':
+          time = curTime - 2592000000;
+          break;
+        case 'Custom':
+          this.showCustomDatePopup();
+          break;
+        case 'Any':
+          time = "";
+          break;
+      }
+      if(this.get('startTime') != "Custom"){
+        this.set("windowStart", time);
+        this.set("windowEnd", "");
+      }
+    }.observes("startTime"),
+
+    // Fields values from Select Custom Dates form
+    customDateFormFields: Ember.Object.create({
+      startDate: null,
+      hoursForStart: null,
+      minutesForStart: null,
+      middayPeriodForStart: null,
+      endDate: null,
+      hoursForEnd: null,
+      minutesForEnd: null,
+      middayPeriodForEnd: null
+    }),
+
+    errors: Ember.Object.create({
+      isStartDateError: false,
+      isEndDateError: false
+    }),
+
+    errorMessages: Ember.Object.create({
+      startDate: '',
+      endDate: ''
+    }),
+
+    showCustomDatePopup: function () {
+      var self = this;
+      var windowEnd = "";
+      var windowStart = "";
+      App.ModalPopup.show({
+        header: Em.I18n.t('jobs.table.custom.date.header'),
+        onPrimary: function () {
+          self.validate();
+          if(self.get('errors.isStartDateError') || self.get('errors.isEndDateError')){
+            return false;
+          }
+
+          var windowStart = self.createCustomStartDate();
+          var windowEnd = self.createCustomEndDate();
+
+          self.set("windowEnd", windowStart.getTime());
+          self.set("windowStart", windowEnd.getTime());
+          this.hide();
+        },
+        onSecondary: function () {
+          self.set('startTime','Any');
+          this.hide();
+        },
+        bodyClass: App.JobsCustomDatesSelectView.extend({
+          controller: self
+        })
+      });
+    },
+
+    createCustomStartDate : function () {
+      var startDate = this.get('customDateFormFields.startDate');
+      var hoursForStart = this.get('customDateFormFields.hoursForStart');
+      var minutesForStart = this.get('customDateFormFields.minutesForStart');
+      var middayPeriodForStart = this.get('customDateFormFields.middayPeriodForStart');
+      if (startDate && hoursForStart && minutesForStart && middayPeriodForStart) {
+        return new Date(startDate + ' ' + hoursForStart + ':' + minutesForStart + ' ' + middayPeriodForStart);
+      }
+      return null;
+    },
+
+    createCustomEndDate : function () {
+      var endDate = this.get('customDateFormFields.endDate');
+      var hoursForEnd = this.get('customDateFormFields.hoursForEnd');
+      var minutesForEnd = this.get('customDateFormFields.minutesForEnd');
+      var middayPeriodForEnd = this.get('customDateFormFields.middayPeriodForEnd');
+      if (endDate && hoursForEnd && minutesForEnd && middayPeriodForEnd) {
+        return new Date(endDate + ' ' + hoursForEnd + ':' + minutesForEnd + ' ' + middayPeriodForEnd);
+      }
+      return null;
+    },
+
+    clearErrors: function () {
+      var errorMessages = this.get('errorMessages');
+      Em.keys(errorMessages).forEach(function (key) {
+        errorMessages.set(key, '');
+      }, this);
+      var errors = this.get('errors');
+      Em.keys(errors).forEach(function (key) {
+        errors.set(key, false);
+      }, this);
+    },
+
+    // Validation for every field in customDateFormFields
+    validate: function () {
+      var formFields = this.get('customDateFormFields');
+      var errors = this.get('errors');
+      var errorMessages = this.get('errorMessages');
+      this.clearErrors();
+      // Check if feild is empty
+      Em.keys(errorMessages).forEach(function (key) {
+        if (!formFields.get(key)) {
+          errors.set('is' + key.capitalize() + 'Error', true);
+          errorMessages.set(key, Em.I18n.t('jobs.customDateFilter.error.required'));
+        }
+      }, this);
+      // Check that endDate is after startDate
+      var startDate = this.createCustomStartDate();
+      var endDate = this.createCustomEndDate();
+      if (startDate && endDate && (startDate > endDate)) {
+        errors.set('isEndDateError', true);
+        errorMessages.set('endDate', Em.I18n.t('jobs.customDateFilter.error.date.order'));
+      }
+    },
+
+    /**
+     * Create link for server request
+     * @return {String}
+     */
+    createJobsFiltersLink: function() {
+      var link = "?fields=events,primaryfilters";
+
+      if(this.get("id") !== "") {
+        link = "/" + this.get("id") + link;
+      }
+      if(this.get("jobsLimit") != -1){
+        link += "&limit=" + this.get("jobsLimit");
+      }
+      if(this.get("user") !== ""){
+        link += "&primaryFilter=user:" + this.get("user");
+      }
+      if(this.get("startTime") !== ""){
+        link += this.get("windowStart") !== "" ? ("&windowStart=" + this.get("windowStart")) : "";
+        link += this.get("windowEnd") !== "" ? ("&windowEnd=" + this.get("windowEnd")) : "";
+      }
+      return link;
+    }
+  }),
 
   /**
    * List of users.
@@ -57,26 +237,42 @@ App.MainJobsController = Em.ArrayController.extend({
 
   loadJobs : function() {
     var self = this;
-    var jobsLimit = this.get('jobsLimit');
     var yarnService = App.YARNService.find().objectAt(0);
     if (yarnService != null) {
       this.set('loading', true);
-      var historyServerHostName = yarnService.get('resourceManagerNode.hostName')
+      var historyServerHostName = yarnService.get('resourceManagerNode.hostName');
+      var filtersLink = this.get('filterObject').createJobsFiltersLink();
       var hiveQueriesUrl = App.testMode ? "/data/jobs/hive-queries.json" : "/proxy?url=http://" + historyServerHostName
-          + ":8188/ws/v1/apptimeline/HIVE_QUERY_ID?fields=events,primaryfilters";
-      if (jobsLimit > 0) {
-        hiveQueriesUrl += ("?limit=" + jobsLimit);
-      }
+          + ":8188/ws/v1/apptimeline/HIVE_QUERY_ID" + filtersLink;
       App.HttpClient.get(hiveQueriesUrl, App.hiveJobsMapper, {
         complete : function(jqXHR, textStatus) {
           self.set('loading', false);
           self.set('loaded', true);
         }
+      }, function (jqXHR, textStatus) {
+        App.hiveJobsMapper.map({entities : []});
       });
     }
   },
 
+
+
   refreshLoadedJobs : function() {
-    this.loadJobs();
-  }.observes('jobsLimit', 'App.router.clusterController.isLoaded')
+    var timeout = this.get('loadJobsTimeout');
+    var self = this;
+
+    clearTimeout(timeout);
+    timeout = setTimeout(function(){
+      self.loadJobs();
+    }, 300);
+
+    this.set('loadJobsTimeout', timeout);
+  }.observes(
+      'filterObject.id',
+      'filterObject.jobsLimit',
+      'filterObject.user',
+      'filterObject.windowStart',
+      'filterObject.windowEnd',
+      'App.router.clusterController.isLoaded'
+  )
 })

http://git-wip-us.apache.org/repos/asf/ambari/blob/99a26c9a/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 0a967c5..2d82507 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -1845,6 +1845,13 @@ Em.I18n.translations = {
   'menu.item.jobs':'Jobs',
   'menu.item.admin':'Admin',
 
+  'jobs.table.custom.date.am':'AM',
+  'jobs.table.custom.date.pm':'PM',
+  'jobs.table.custom.date.header':'Select Custom Dates',
+  'jobs.customDateFilter.error.required':'This field is required',
+  'jobs.customDateFilter.error.date.order':'End Date must be after Start Date',
+  'jobs.customDateFilter.startTime':'Start Time',
+  'jobs.customDateFilter.endTime':'End Time',
   'jobs.hive.tez.tasks':'Tez Tasks',
   'jobs.hive.tez.hdfs':'HDFS',
   'jobs.hive.tez.localFiles':'Local Files',

http://git-wip-us.apache.org/repos/asf/ambari/blob/99a26c9a/ambari-web/app/styles/apps.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/apps.less b/ambari-web/app/styles/apps.less
index 15c5c9c..8d3ea6e 100644
--- a/ambari-web/app/styles/apps.less
+++ b/ambari-web/app/styles/apps.less
@@ -747,6 +747,11 @@
   }
 }
 
+.jobs-custom-dates{
+  .help-inline{
+    color:#b94a48;
+  }
+}
 
 
 @media all and (max-width: 1024px) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/99a26c9a/ambari-web/app/templates/main/jobs/custom_dates_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/jobs/custom_dates_popup.hbs b/ambari-web/app/templates/main/jobs/custom_dates_popup.hbs
new file mode 100644
index 0000000..1b1d920
--- /dev/null
+++ b/ambari-web/app/templates/main/jobs/custom_dates_popup.hbs
@@ -0,0 +1,39 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<div class="jobs-custom-dates">
+  <div>
+    <label>{{t jobs.customDateFilter.startTime}}</label>
+    {{view Ember.TextField valueBinding="customDateFormFields.startDate" class="input-small datepicker"}}
+    {{view Ember.Select contentBinding="view.hourOptions" selectionBinding="customDateFormFields.hoursForStart" class="input-mini"}}
+    {{view Ember.Select contentBinding="view.minuteOptions" selectionBinding="customDateFormFields.minutesForStart" class="input-mini"}}
+    {{view Ember.Select contentBinding="view.middayPeriodOptions" selectionBinding="customDateFormFields.middayPeriodForStart" class="input-mini"}}
+    <span class="help-inline">{{errorMessages.startDate}}</span>
+  </div>
+  <div>
+
+  </div>
+  <div>
+    <label>{{t jobs.customDateFilter.endTime}}</label>
+    {{view Ember.TextField valueBinding="customDateFormFields.endDate" class="input-small datepicker"}}
+    {{view Ember.Select contentBinding="view.hourOptions" selectionBinding="customDateFormFields.hoursForEnd" class="input-mini"}}
+    {{view Ember.Select contentBinding="view.minuteOptions" selectionBinding="customDateFormFields.minutesForEnd" class="input-mini"}}
+    {{view Ember.Select contentBinding="view.middayPeriodOptions" selectionBinding="customDateFormFields.middayPeriodForEnd" class="input-mini"}}
+    <span class="help-inline">{{errorMessages.endDate}}</span>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99a26c9a/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index 13330ed..88cf9b1 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -215,6 +215,7 @@ require('views/main/charts/heatmap/heatmap_host');
 require('views/main/charts/heatmap/heatmap_host_detail');
 require('views/main/apps_view');
 require('views/main/jobs_view');
+require('views/main/jobs/select_custom_date_view');
 require('views/main/jobs/hive_job_details_view');
 require('views/main/jobs/hive_job_details_tez_dag_view');
 require('views/main/apps/item_view');

http://git-wip-us.apache.org/repos/asf/ambari/blob/99a26c9a/ambari-web/app/views/main/jobs/select_custom_date_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/jobs/select_custom_date_view.js b/ambari-web/app/views/main/jobs/select_custom_date_view.js
new file mode 100644
index 0000000..d0c03ef
--- /dev/null
+++ b/ambari-web/app/views/main/jobs/select_custom_date_view.js
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+var date = require('utils/date');
+
+App.JobsCustomDatesSelectView = Em.View.extend({
+  name: 'jobsCustomDatesSelectView',
+  templateName: require('templates/main/jobs/custom_dates_popup'),
+
+  middayPeriodOptions: [Em.I18n.t('jobs.table.custom.date.am'), Em.I18n.t('jobs.table.custom.date.pm')],
+
+  hourOptions: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
+
+  minuteOptions: ['00', '05', '10', '15', '20', '25', '30', '35', '40', '45', '50', '55'],
+
+  didInsertElement: function () {
+    $('.datepicker').datepicker({
+      format: 'mm/dd/yyyy'
+    });
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/99a26c9a/ambari-web/app/views/main/jobs_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/jobs_view.js b/ambari-web/app/views/main/jobs_view.js
index 0cc3a7b..aeccab0 100644
--- a/ambari-web/app/views/main/jobs_view.js
+++ b/ambari-web/app/views/main/jobs_view.js
@@ -69,6 +69,7 @@ App.MainJobsView = App.TableView.extend({
    */
   rowsPerPageSelectView: Em.Select.extend({
     content: ['10', '25', '50', '100', "250", "500"],
+    valueBinding: "controller.filterObject.jobsLimit",
     change: function () {
       this.get('parentView').saveDisplayLength();
     }
@@ -79,23 +80,16 @@ App.MainJobsView = App.TableView.extend({
    * @returns {String}
    */
   filteredJobs: function () {
-    return Em.I18n.t('jobs.filtered.jobs').format(this.get('filteredContent.length'), this.get('content').get('length'));
+    return Em.I18n.t('jobs.filtered.jobs').format(this.get('content').get('length'), this.get('controller.totalOfJobs'));
   }.property('content.length', 'filteredContent.length'),
 
   /**
    * Filter-field for Jobs ID.
    * Based on <code>filters</code> library
    */
-  /*jobsIdFilterView: filters.createTextView({
-    valueBinding: "controller.filterObject.id"
-  }),*/
-
   jobsIdFilterView: filters.createTextView({
     column: 0,
-    fieldType: 'width70',
-    onChangeValue: function(){
-      this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'string');
-    }
+    valueBinding: "controller.filterObject.id"
   }),
 
   /**
@@ -145,9 +139,7 @@ App.MainJobsView = App.TableView.extend({
       }.observes('users.length')
     }),
 
-    onChangeValue: function(){
-      this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'multiple');
-    }
+    valueBinding: 'controller.filterObject.user'
   }),
 
   /**
@@ -157,10 +149,8 @@ App.MainJobsView = App.TableView.extend({
   startTimeFilterView: filters.createSelectView({
     fieldType: 'input-medium',
     column: 2,
-    content: ['Any', 'Past 1 hour',  'Past 1 Day', 'Past 2 Days', 'Past 7 Days', 'Past 14 Days', 'Past 30 Days'],
-    onChangeValue: function () {
-      this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'date');
-    }
+    content: ['Any', 'Past 1 hour',  'Past 1 Day', 'Past 2 Days', 'Past 7 Days', 'Past 14 Days', 'Past 30 Days', 'Custom'],
+    valueBinding: "controller.filterObject.startTime"
   }),
 
   /**