Browse Source

AMBARI-1254. Modify App Browser to use server-side paging/sorting/filtering. (yusaku)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/trunk@1438321 13f79535-47bb-0310-9956-ffa450edef68
Yusaku Sako 12 years ago
parent
commit
87ed8a93bb

+ 3 - 0
CHANGES.txt

@@ -25,6 +25,9 @@ Trunk (unreleased changes):
 
  IMPROVEMENTS
 
+ AMBARI-1254. Modify App Browser to use server-side paging/sorting/filtering.
+ (yusaku)
+
  AMBARI-1258. Minor refactoring of User Management code. (yusaku)
 
  AMBARI-1253. Use ember-precompiler-brunch npm plugin. (yusaku)

+ 3 - 3
ambari-web/app/classes/job_class.js

@@ -31,11 +31,11 @@ App.Job2 = Ember.Object.extend({
   status: "", //string
   input: 0, //number
   output: 0, //number
-  elapsedTime: 0, //number
+  elapsed_time: 0, //number
 
   duration: function() {
-    return date.timingFormat(parseInt(this.get('elapsedTime')));
-  }.property('elapsedTime'),
+    return date.timingFormat(parseInt(this.get('elapsed_time')));
+  }.property('elapsed_time'),
 
   inputFormatted: function () {
     return misc.formatBandwidth(this.get('input'));

+ 0 - 19
ambari-web/app/controllers/global/cluster_controller.js

@@ -47,9 +47,6 @@ App.ClusterController = Em.Controller.extend({
     'users':false
   }),
 
-  postLoadList:{
-    'runs':false
-  },
   /**
    * load cluster name
    */
@@ -178,22 +175,6 @@ App.ClusterController = Em.Controller.extend({
     });
     this.set('alerts', sortedArray);
   },
-  loadRuns:function () {
-    if (this.get('postLoadList.runs')) {
-      return;
-    }
-
-    var self = this;
-    var runsUrl = App.testMode ? "/data/apps/runs.json" : App.apiPrefix + "/jobhistory/workflow?orderBy=startTime&sortDir=DESC&limit=" + App.maxRunsForAppBrowser;
-
-    App.HttpClient.get(runsUrl, App.runsMapper, {
-      complete:function (jqXHR, textStatus) {
-        self.set('postLoadList.runs', true);
-      }
-    }, function () {
-      self.set('postLoadList.runs', true);
-    });
-  },
 
   /**
    * This method automatically loads alerts when Nagios URL

+ 5 - 2
ambari-web/app/controllers/main/apps/item_controller.js

@@ -24,7 +24,8 @@ App.MainAppsItemController = Em.Controller.extend({
    * Was set outside in App.MainAppsView.
    * It's instance of App.Run model
    */
-  content: null,
+  content: [],
+  jobsLoaded:false,
 
   lastJobId : null,
   gettingJobs:function(){
@@ -41,7 +42,9 @@ App.MainAppsItemController = Em.Controller.extend({
     var url = App.testMode ? '/data/apps/jobs.json' :
       App.apiPrefix + "/jobhistory/job?workflowId=" + currentId;
 
-    App.HttpClient.get(url, App.jobsMapper,{
+    var mapper = App.jobsMapper;
+    mapper.set('controller', this);
+    App.HttpClient.get(url, mapper,{
       complete:function(jqXHR, textStatus) {
         self.set('content.loadAllJobs', true);
       }

+ 436 - 49
ambari-web/app/controllers/main/apps_controller.js

@@ -17,64 +17,451 @@
  */
 
 var App = require('app');
-require('utils/jquery.unique');
+var misc = require('utils/misc');
+var date = require('utils/date');
 
 App.MainAppsController = Em.ArrayController.extend({
 
   name:'mainAppsController',
-  content: function(){
-    return App.Run.find();
-  }.property('App.router.clusterController.postLoadList.runs'),
-  /**
-   * Mark all Runs as not Filtered
-   */
-  clearFilteredRuns: function() {
-    this.get('content').setEach('isFiltered', false);
-    this.set('filteredRunsLength', 0);
-  },
-  /**
-   * Mark Run as filtered
-   * @param id runId
-   */
-  addFilteredRun: function(id) {
-    this.get('content').findProperty('id', id).set('isFiltered', true);
-    this.set('filteredRunsLength', this.get('content').filterProperty('isFiltered', true).length);
-  },
-  /**
-   * Mark Runs as filtered
-   * @param ids array of Run id
-   */
-  filterFilteredRuns: function(ids) {
-    this.get('content').filter(function(item) {
-      if ($.inArray(item.get('id'), ids) !== -1) {
-        item.set('isFiltered', true);
+  content: [],
+
+  loaded : false,
+  loading : false,
+
+
+  loadRuns:function () {
+    console.log("--------------------loadRuns");
+    this.set('loading', true);
+    var self = this;
+
+    //var runsUrl = App.testMode ? "/data/apps/runs.json" : App.apiPrefix + "/jobhistory/workflow?orderBy=startTime&sortDir=DESC&limit=" + App.maxRunsForAppBrowser;
+    var runsUrl = App.testMode ? "/data/apps/runs.json" : App.apiPrefix + this.get("runUrl");
+
+    App.HttpClient.get(runsUrl, App.runsMapper, {
+      complete:function (jqXHR, textStatus) {
+        self.set('loading', false);
+        self.set('loaded', true);
       }
     });
-    this.set('filteredRunsLength', this.get('content').filterProperty('isFiltered', true).length);
   },
-  /**
-   * Identifier of the last starred/unstarred run
-   */
-  lastStarClicked: null,
-  /**
-   * Starred Runs count
+
+  //Pagination Object
+
+  paginationObject:{
+    iTotalDisplayRecords :null,
+    iTotalRecords:null,
+    startIndex:null,
+    endIndex:null
+  },
+
+  /*
+   Set number of filtered jobs when switching to all jobs
    */
-  staredRunsLength: function() {
-    return this.get('content').filterProperty('isStared', true).length;
-  }.property('content'),
+  iTotalDisplayRecordsObserver:function(){
+    if(this.get("filterObject.allFilterActivated")){
+      //this.set("paginationObject.filteredDisplayRecords","-10");
+      this.set("filterObject.allFilterActivated", false);
+    }else{
+      this.set("filterObject.filteredDisplayRecords",this.get("paginationObject.iTotalDisplayRecords"));
+    }
+  }.observes("paginationObject.iTotalDisplayRecords"),
+
+
+  //Filter object
+
+  filterObject : Ember.Object.create({
+    sSearch_0:"",
+    sSearch_1:"",
+    sSearch_2:"",
+    sSearch_3:"",
+    minJobs:"",
+    maxJobs:"",
+    minInputBytes:"",
+    maxInputBytes:"",
+    minOutputBytes:"",
+    maxOutputBytes:"",
+    minDuration:"",
+    maxDuration:"",
+    minStartTime:"",
+    maxStartTime:"",
+    sSearch:"",
+    iDisplayLength:"",
+    iDisplayStart:"",
+    iSortCol_0:"",
+    sSortDir_0:"",
+
+    allFilterActivated:false,
+    filteredDisplayRecords:null,
+
+    viewType:"all",
+    viewTypeClickEvent:false,
+
+    /**
+     * Direct binding to job filter field
+     */
+    runType:"",
+    onRunTypeChange:function(){
+      if(this.runType == "MapReduce"){
+        this.set("sSearch_2","mr");
+      }else if(this.runType == "Hive"){
+        this.set("sSearch_2","hive");
+      }else if(this.runType == "Pig"){
+        this.set("sSearch_2","pig");
+      }else{
+        this.set("sSearch_2","");
+      }
+    }.observes("runType"),
+
+    /**
+     * Direct binding to job filter field
+     */
+    jobs:"",
+    onJobsChange:function(){
+      var minMaxTmp = this.parseNumber(this.jobs);
+      this.set("minJobs", minMaxTmp.min);
+      this.set("maxJobs", minMaxTmp.max);
+    }.observes("jobs"),
+
+    /**
+     * Direct binding to Input filter field
+     */
+    input:"",
+    onInputChange:function(){
+      var minMaxTmp = this.parseBandWidth(this.input);
+      this.set("minInputBytes", minMaxTmp.min);
+      this.set("maxInputBytes", minMaxTmp.max);
+    }.observes("input"),
+
+    /**
+     * Direct binding to Output filter field
+     */
+    output:"",
+    onOutputChange:function(){
+      var minMaxTmp = this.parseBandWidth(this.output);
+      this.set("minOutputBytes", minMaxTmp.min);
+      this.set("maxOutputBytes", minMaxTmp.max);
+    }.observes("output"),
+
+    /**
+     * Direct binding to Duration filter field
+     */
+    duration:"",
+    onDurationChange:function(){
+      var minMaxTmp = this.parseNumber(this.duration);
+      if(parseFloat(minMaxTmp.min) == parseFloat(minMaxTmp.max)){
+        this.set("minDuration" ,Math.ceil((parseFloat(minMaxTmp.min)-0.01)*1000));
+        this.set("maxDuration" ,Math.floor((parseFloat(minMaxTmp.max)+0.01)*1000));
+      }else{
+        this.set("minDuration", parseFloat(minMaxTmp.min)*1000);
+        this.set("maxDuration", parseFloat(minMaxTmp.max)*1000);
+      }
+    }.observes("duration"),
+
+    /**
+     * Direct binding to Run Date filter field
+     */
+    runDate:"",
+    onRunDateChange:function(){
+      var minMaxTmp = this.parseDate(this.runDate);
+      this.set("minStartTime", minMaxTmp.min);
+      this.set("maxStartTime", minMaxTmp.max);
+    }.observes("runDate"),
+
+    parseDate:function(value){
+      var tmp={
+        min:"",
+        max:""
+      };
+      var nowTime = new Date().getTime();
+
+      switch (value){
+        case 'Any':
+          break;
+        case 'Past 1 Day':
+          tmp.min= nowTime - 86400000;
+          break;
+        case 'Past 2 Days':
+          tmp.min= nowTime - 172800000;
+          break;
+        case 'Past 7 Days':
+          tmp.min= nowTime - 604800000;
+          break;
+        case 'Past 14 Days':
+          tmp.min= nowTime - 1209600000;
+          break;
+        case 'Past 30 Days':
+          tmp.min= nowTime - 2592000000;
+          break;
+        case 'Running Now':
+          tmp.min= nowTime;
+          break;
+      }
+      return tmp;
+    },
+
+    parseBandWidth:function(value){
+      var tmp={
+        min:"",
+        max:""
+      };
+      var compareChar = isNaN(value.charAt(0)) ? value.charAt(0) : false;
+      var compareScale = value.charAt(value.length - 1);
+      var compareValue = compareChar ? parseFloat(value.substr(1, value.length)) : parseFloat(value.substr(0, value.length));
+      if(isNaN(compareValue)){
+        return tmp;
+      }
+      switch (compareScale) {
+        case 'g':
+          tmp.min = Math.max(1073741824,Math.ceil((compareValue-0.005)*1073741824));
+          tmp.max = Math.floor((compareValue+0.005)*1073741824);
+          break;
+        case 'm':
+          tmp.min = Math.max(1048576,Math.ceil((compareValue-0.05)*1048576));
+          tmp.max = Math.min(1073741823,Math.floor((compareValue+0.05)*1048576));
+          break;
+        case 'k':
+          tmp.min = Math.max(1024,Math.ceil((compareValue-0.05)*1024));
+          tmp.max = Math.min(1048575,Math.floor((compareValue+0.05)*1024));
+          break;
+        default:
+          tmp.min = Math.max(1024,Math.ceil((compareValue-0.05)*1024));
+          tmp.max = Math.min(1048575,Math.floor((compareValue+0.05)*1024));
+      }
+      switch (compareChar) {
+        case '<':
+          tmp.min="";
+          break;
+        case '>':
+          tmp.max="";
+          break;
+      }
+      return tmp;
+    },
+    parseNumber:function(value){
+      var tmp={
+        min:"",
+        max:""
+      };
+      switch (value.charAt(0)) {
+        case '<':
+          tmp.max=value.substr(1);
+          break;
+        case '>':
+          tmp.min=value.substr(1);
+          break;
+        case '=':
+          tmp.min=value.substr(1);
+          tmp.max=value.substr(1);
+          break;
+        default:
+          tmp.min=value;
+          tmp.max=value;
+      }
+      return tmp;
+    },
+    createAppLink:function(){
+      var link = "/jobhistory/datatable?";
+
+      if(this.sSearch_0){
+        link +=  "sSearch_0=" + this.sSearch_0 + "&";
+      }
+      if(this.sSearch_1){
+        link +=  "sSearch_1=" + this.sSearch_1 + "&";
+      }
+      if(this.sSearch_2 && this.sSearch_2 != "Any"){
+        link +=  "sSearch_2=" + this.sSearch_2 + "&";
+      }
+      if(this.sSearch_3){
+        link +=  "sSearch_3=" + this.sSearch_3 + "&";
+      }
+      if(this.minJobs){
+        link +=  "minJobs=" + this.minJobs + "&";
+      }
+      if(this.maxJobs){
+        link +=  "maxJobs=" + this.maxJobs + "&";
+      }
+      if(this.minInputBytes){
+        link +=  "minInputBytes=" + this.minInputBytes + "&";
+      }
+      if(this.maxInputBytes){
+        link +=  "maxInputBytes=" + this.maxInputBytes + "&";
+      }
+      if(this.minOutputBytes){
+        link +=  "minOutputBytes=" + this.minOutputBytes + "&";
+      }
+      if(this.maxOutputBytes){
+        link +=  "maxOutputBytes=" + this.maxOutputBytes + "&";
+      }
+      if(this.minDuration){
+        link +=  "minDuration=" + this.minDuration + "&";
+      }
+      if(this.maxDuration){
+        link +=  "maxDuration=" + this.maxDuration + "&";
+      }
+      if(this.minStartTime){
+        link +=  "minStartTime=" + this.minStartTime + "&";
+      }
+      if(this.maxStartTime){
+        link +=  "maxStartTime=" + this.maxStartTime + "&";
+      }
+      if(this.sSearch){
+        link +=  "sSearch=" + this.sSearch + "&";
+      }
+      if(this.iDisplayLength){
+        link +=  "iDisplayLength=" + this.iDisplayLength + "&";
+      }
+      if(this.iDisplayStart){
+        link +=  "iDisplayStart=" + this.iDisplayStart + "&";
+      }
+      if(this.iSortCol_0){
+        link +=  "iSortCol_0=" + this.iSortCol_0 + "&";
+      }
+      if(this.sSortDir_0){
+        link +=  "sSortDir_0=" + this.sSortDir_0 + "&";
+      }
+
+      link = link.slice(0,link.length-1);
+
+      var valueInString=link.match(/&/g);
+
+      if(!this.get("viewTypeClickEvent"))
+      if(valueInString != null){
+        this.set("viewType","filtered");
+      }else{
+        this.set("viewType","all");
+      }
+
+      return link;
+    }
+  }),
+
+  runUrl : "/jobhistory/datatable",
+  runTimeout : null,
+
+  valueObserver: function(){
+    var link = this.get('filterObject').createAppLink();
+
+    if(this.get("filterObject.viewType") == "filtered"){
+      this.set("runUrl", link);
+    }else{
+      this.set("runUrl",  "/jobhistory/datatable?iDisplayLength="+this.get('filterObject.iDisplayLength'));
+    }
+
+    var timeout = this.get('runTimeout');
+    var self = this;
+
+    clearTimeout(timeout);
+    timeout = setTimeout(function(){
+      console.log(self.get("runUrl"));
+      self.loadRuns();
+    }, 300);
+
+    this.set('runTimeout', timeout);
+
+  }.observes(
+      'filterObject.sSearch_0',
+      'filterObject.sSearch_1',
+      'filterObject.sSearch_2',
+      'filterObject.sSearch_3',
+      'filterObject.minJobs',
+      'filterObject.maxJobs',
+      'filterObject.minInputBytes',
+      'filterObject.maxInputBytes',
+      'filterObject.minOutputBytes',
+      'filterObject.maxOutputBytes',
+      'filterObject.minDuration',
+      'filterObject.maxDuration',
+      'filterObject.minStartTime',
+      'filterObject.maxStartTime',
+      'filterObject.sSearch',
+      'filterObject.iDisplayLength',
+      'filterObject.iDisplayStart',
+      'filterObject.iSortCol_0',
+      'filterObject.sSortDir_0',
+      'filterObject.viewType'
+  ),
+
+  serverData: null,
+  summary: null,
+
   /**
-   * Click on star on table row
-   * @return {Boolean} false for prevent default event handler
+   * Observer for summary data from server
    */
-  starClick: function(event) {
-    event.target.classList.toggle('stared');
-    var id = jQuery(event.target).parent().parent().parent().find('.appId').attr('title');
-    var run = this.get('content').findProperty('id', id);
-    if (run) {
-      run.set('isStared', !run.get('isStared'));
+  summaryInfo: function(){
+    var tmp;
+    var summary = this.get('serverData');
+    if(!summary){
+      tmp = {
+        'jobs': {
+          'avg': 'undefined',
+          'min': 'undefined',
+          'max': 'undefined'
+        },
+        'input': {
+          'avg': 'undefined',
+          'min': 'undefined',
+          'max': 'undefined'
+        },
+        'output': {
+          'avg': 'undefined',
+          'min': 'undefined',
+          'max': 'undefined'
+        },
+        'duration': {
+          'avg': 'undefined',
+          'min': 'undefined',
+          'max': 'undefined'
+        },
+        'times': {
+          'oldest': 'undefined',
+          'youngest': 'undefined'
+        }
+      };
+    }else{
+      tmp = {
+        'jobs': {
+          'avg': summary.jobs.avg.toFixed(2),
+          'min': summary.jobs.min,
+          'max': summary.jobs.max
+        },
+        'input': {
+          'avg': misc.formatBandwidth(summary.input.avg),
+          'min': misc.formatBandwidth(summary.input.min),
+          'max': misc.formatBandwidth(summary.input.max)
+        },
+        'output': {
+          'avg': misc.formatBandwidth(summary.output.avg),
+          'min': misc.formatBandwidth(summary.output.min),
+          'max': misc.formatBandwidth(summary.output.max)
+        },
+        'duration': {
+          'avg': date.timingFormat(Math.round(summary.duration.avg)),
+          'min': date.timingFormat(summary.duration.min),
+          'max': date.timingFormat(summary.duration.max)
+        },
+        'times': {
+          'oldest': new Date(summary.times.oldest).toDateString(),
+          'youngest': new Date(summary.times.youngest).toDateString()
+        }
+      };
     }
-    this.set('staredRunsLength', this.get('content').filterProperty('isStared', true).length);
-    this.set('lastStarClicked', id);
-    return false;
-  }
+    this.set("summary",tmp);
+  }.observes('serverData'),
+
+
+  columnsName: Ember.ArrayController.create({
+    content: [
+      { name: 'App ID', index: 0 },
+      { name: 'Name', index: 1 },
+      { name: 'Type', index: 2 },
+      { name: 'User', index: 3 },
+      { name: 'Jobs', index: 4 },
+      { name: 'Input', index: 5 },
+      { name: 'Output', index: 6 },
+      { name: 'Duration', index: 7 },
+      { name: 'Run Date', index: 8 }
+    ]
+  })
+
+
 })

+ 7 - 1
ambari-web/app/mappers/jobs_mapper.js

@@ -29,7 +29,13 @@ App.jobsMapper = App.QuickDataMapper.create({
       json.jobs.forEach(function (item) {
         result.push(this.parseIt(item, this.config));
       }, this);
-      App.store.loadMany(this.get('model'), result);
+
+      var r = Ember.ArrayProxy.create({"content":[]});
+      result.forEach(function(item){
+        r.content.push(App.Job2.create(item));
+      });
+
+      this.set('controller.content.jobs', r.content);
     }
   },
   config:{

+ 28 - 10
ambari-web/app/mappers/runs_mapper.js

@@ -24,9 +24,17 @@ App.runsMapper = App.QuickDataMapper.create({
     if(!this.get('model')) {
       return;
     }
-    if(json && json.workflows) {
+    if(json && json.aaData) {
       var result = [];
-      json.workflows.forEach(function(item) {
+
+      var pagination_info={
+        iTotalDisplayRecords :json.iTotalDisplayRecords ,
+        iTotalRecords:json.iTotalRecords,
+        startIndex:parseInt(json.startIndex)+1,
+        endIndex:parseInt(json.endIndex)+1
+      }
+
+      json.aaData.forEach(function(item) {
         var o = this.parseIt(item, this.config);
 
         var r = '{dag: {';
@@ -47,21 +55,31 @@ App.runsMapper = App.QuickDataMapper.create({
         });
         r = r.substr(0, r.length - 1);
         r += '}}';
-        o.workflow_context = r;
+        o.workflowContext = r;
 
         result.push(o);
       }, this);
-      App.store.loadMany(this.get('model'), result);
+
+      var r = [];
+      result.forEach(function(item){
+        r.push(App.Run2.create(item));
+      });
+
+      App.router.get('mainAppsController').set('content', r);
+      App.router.get('mainAppsController').set('paginationObject', pagination_info);
+      App.router.get('mainAppsController').set('serverData', json.summary);
     }
+
+
   },
   config : {
     id: 'workflowId',
-    app_name: 'workflowName',
-    num_jobs_total: 'numJobsTotal',
-    num_jobs_completed: 'numJobsCompleted',
-    user_name:'userName',
-    start_time: 'startTime',
-    elapsed_time: 'elapsedTime',
+    appName: 'workflowName',
+    numJobsTotal: 'numJobsTotal',
+    numJobsCompleted: 'numJobsCompleted',
+    userName:'userName',
+    startTime: 'startTime',
+    elapsedTime: 'elapsedTime',
     input: 'inputBytes',
     output: 'outputBytes'
   }

+ 3 - 1
ambari-web/app/models.js

@@ -42,4 +42,6 @@ require('models/app');
 require('models/background_operation');
 require('models/host_component');
 
-require('classes/run_class');
+require('classes/run_class');
+require('classes/job_class');
+require('classes/job_class');

+ 2 - 2
ambari-web/app/routes/main.js

@@ -83,8 +83,8 @@ module.exports = Em.Route.extend({
   apps:Em.Route.extend({
     route:'/apps',
     connectOutlets:function (router) {
-
-      router.get('clusterController').loadRuns();
+      //router.get('clusterController').loadRuns();
+      router.get('mainAppsController').loadRuns();
       router.get('mainController').connectOutlet('mainApps');
     }
   }),

+ 56 - 24
ambari-web/app/styles/apps.less

@@ -21,13 +21,9 @@
   td .red {
     color: red;
   }
-  .table {
-    thead {
-      th{
-        padding-left: 0 !important;
-        vertical-align: top;
-      }
-    }
+  .table thead th{
+    vertical-align:top;
+    padding-bottom: 0px;
   }
   .avg-table {
     table-layout: fixed;
@@ -42,14 +38,61 @@
     }
   }
 
-  #dataTable {
+  .clear_filter{
+    width:578px;
+  }
+
+  .runsList {
     table-layout: fixed;
+    border: 1px solid silver;
+    th {
+      border-top:none;
+    }
     td {
-      padding-right: 4px;
       word-wrap: break-word;
     }
+
+    .input-small-app{
+      width:148px
+    }
+
+    .col0,
+    td:first-child + td,
+    th:first-child + th{
+      width: 176px;
+    }
+    .col1,
+    td:first-child + td + td,
+    th:first-child + th + th{
+      width: 118px;
+    }
+    .col2,
+    td:first-child + td + td + td,
+    th:first-child + th + th + th{
+      width: 108px;
+    }
+    .col3,
+    td:first-child + td + td + td + td,
+    th:first-child + th + th + th + th{
+      width: 93px;
+    }
+    .col4,.col5,.col6,.col7,
+    td:first-child + td + td + td + td + td,
+    th:first-child + th + th + th + th + th,
+    td:first-child + td + td + td + td + td + td,
+    th:first-child + th + th + th + th + th + th,
+    td:first-child + td + td + td + td + td + td + td,
+    th:first-child + th + th + th + th + th + th + th,
+    td:first-child + td + td + td + td + td + td + td + td,
+    th:first-child + th + th + th + th + th + th + th + th
+    {
+      width: 88px;
+    }
   }
 
+  .dropdown-menu label.checkbox {
+    margin-left: 10px;
+  }
   .dropdown-menu label.checkbox {
     margin-left: 10px;
   }
@@ -87,17 +130,11 @@
     font-size:12px;
   }
   .search-bar {
-    float:right;
+
   }
   .clear {
     clear:both;
   }
-  > div > .dataTable {
-    border: 1px solid silver;
-    th {
-      border-top:none;
-    }
-  }
   .content {
     padding: 0;
   }
@@ -107,15 +144,10 @@
   .app-table-row{
     cursor: pointer;
   }
-  #filter_info {
-    float:left;
-    padding-top:10px;
-
-    > .span4 > a.selected{
+  .filter_info > .span4 > a.selected{
       cursor: default;
       text-decoration: none;
       color: #000;
-    }
   }
   .page-bar {
     border: 1px solid silver;
@@ -124,7 +156,7 @@
       display: inline-block;
       margin:0 10px;
     }
-    .dataTables_length {
+    .items-on-page {
       label {
         display:inline;
       }
@@ -134,7 +166,7 @@
         width:70px;
       }
     }
-    .dataTables_paginate {
+    .paging_two_button {
       a {
         padding:0 5px;
       }

+ 57 - 49
ambari-web/app/templates/main/apps.hbs

@@ -17,84 +17,92 @@
 }}
 
 <div id="apps">
-  <table class="table table-bordered table-stripe avg-table">
+  <table class="table table-bordered avg-table">
     <tbody>
     <tr>
-      <td rowspan="3" class="avg-star"><a href="#" {{action "avgStarClick" target="view"}} class="icon-star a"></a></td>
+      <td></td>
       <td>Jobs</td>
       <td>Input</td>
       <td>Output</td>
       <td>Duration</td>
       <td>Oldest</td>
       <td>Most Recent</td>
-      <td></td>
     </tr>
     <tr class="avg-info">
-      <td>{{view.avgData.jobs.avg}}</td>
-      <td>{{view.avgData.input.avg}}</td>
-      <td>{{view.avgData.output.avg}}</td>
-      <td>{{view.avgData.duration.avg}}</td>
-      <td>{{view.avgData.times.oldest}}</td>
-      <td>{{view.avgData.times.youngest}}</td>
       <td>Avg</td>
+      <td>{{summary.jobs.avg}}</td>
+      <td>{{summary.input.avg}}</td>
+      <td>{{summary.output.avg}}</td>
+      <td>{{summary.duration.avg}}</td>
+      <td>{{summary.times.oldest}}</td>
+      <td>{{summary.times.youngest}}</td>
+
     </tr>
     <tr class="compare-info">
-      <td>{{view.avgData.jobs.min}} / {{view.avgData.jobs.max}}</td>
-      <td>{{view.avgData.input.min}} / {{view.avgData.input.max}}</td>
-      <td>{{view.avgData.output.min}} / {{view.avgData.output.max}}</td>
-      <td>{{view.avgData.duration.min}} / {{view.avgData.duration.max}}</td>
+      <td>Min / Max</td>
+      <td>{{summary.jobs.min}} / {{summary.jobs.max}}</td>
+      <td>{{summary.input.min}} / {{summary.input.max}}</td>
+      <td>{{summary.output.min}} / {{summary.output.max}}</td>
+      <td>{{summary.duration.min}} / {{summary.duration.max}}</td>
       <td></td>
       <td></td>
-      <td>Min / Max</td>
     </tr>
     </tbody>
   </table>
-  <div id="filter_info" class="row">
+
+  <div class="filter_info">
+
     <div class="span4" id="filter_buttons">Show:
-      <a href="javascript:void(0)" class="all selected" {{action "clickViewType" target="view"}} data-view-type="all">All
-        ({{view.content.length}})</a> &#124;
-      <a href="javascript:void(0)" class="filtered" {{action "clickViewType" target="view"}} data-view-type="filtered">Filtered
-        ({{view.filtered}})</a> &#124;
-      <a href="javascript:void(0)" class="stared" {{action "clickViewType" target="view"}} data-view-type="starred">Starred ({{view.stared}})</a>
+      <a class="all selected" {{action "clickViewType" target="view"}} data-view-type="all">All
+        ({{controller.paginationObject.iTotalRecords}})</a> &#124;
+      <a class="filtered" {{action "clickViewType" target="view"}} data-view-type="filtered">Filtered
+        ({{controller.filterObject.filteredDisplayRecords}})</a>
     </div>
-    <div class="span2"><a href="#" {{action "clearFilters" target="view"}}>Clear filters</a>&nbsp;&#124;&nbsp;<a
-      href="#" {{action "clearStars" target="view"}}>Clear stars</a></div>
-  </div>
-  <div>
+    <div class="span2 clear_filter">
+        <a {{action "clearFilters" target="view"}}>Clear filters</a>
   </div>
-  <table class="table table-striped" id="dataTable">
+
+   <div class="search-bar">
+        {{view view.appSearchThrough valueBinding="controller.filterObject.sSearch"}}
+      </div>
+
+
+  <table class="table table-striped runsList" id="dataTable">
     <thead>
+    {{#view view.wrapSorting}}
+      {{#each controller.columnsName}}
+        {{#view view.parentView.sortingColumns contentBinding="this"}}
+          {{name}}
+        {{/view}}
+      {{/each}}
+    {{/view}}
     <tr>
-      <th></th>
-      <th></th>
-      <th>App ID</th>
-      <th>Name</th>
-      <th>Type</th>
-      <th>User</th>
-      <th>Jobs</th>
-      <th>Input</th>
-      <th>Output</th>
-      <th>Duration</th>
-      <th>Run Date</th>
-    </tr>
-    <tr>
-      <th class="notActive"><div class="view-wrapper">{{view view.starFilterView viewName="starFilterViewInstance"}}</div></th>
-      <th></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.appidFilterView viewName="appidFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_appidFilterViewInstance" class="ui-icon ui-icon-circle-close ui-appid"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.nameFilterView viewName="nameFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_nameFilterViewInstance" class="ui-icon ui-icon-circle-close ui-name"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.typeSelectView viewName="typeSelectViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_typeSelectViewInstance" class="ui-icon ui-icon-circle-close ui-type"></a></th>
+      <th class="notActive"><div class="view-wrapper">{{view view.appidFilterView valueBinding="controller.filterObject.sSearch_0" viewName="appidFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_appidFilterViewInstance" class="ui-icon ui-icon-circle-close ui-appid"></a></th>
+      <th class="notActive"><div class="view-wrapper">{{view view.nameFilterView valueBinding="controller.filterObject.sSearch_1" viewName="nameFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_nameFilterViewInstance" class="ui-icon ui-icon-circle-close ui-name"></a></th>
+      <th class="notActive"><div class="view-wrapper">{{view view.typeSelectView selectionBinding="controller.filterObject.runType" viewName="typeSelectViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_typeSelectViewInstance" class="ui-icon ui-icon-circle-close ui-type"></a></th>
       <th class="notActive"><div class="view-wrapper">{{view view.userFilterView viewName="userFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_userFilterViewInstance" class="ui-icon ui-icon-circle-close ui-user"></a><input id="user_filter" type="hidden"></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.jobsFilterView viewName="jobsFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_jobsFilterViewInstance" class="ui-icon ui-icon-circle-close ui-jobs"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.inputFilterView viewName="inputFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_inputFilterViewInstance" class="ui-icon ui-icon-circle-close ui-input"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.outputFilterView viewName="outputFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_outputFilterViewInstance" class="ui-icon ui-icon-circle-close ui-output"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.durationFilterView viewName="durationFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_durationFilterViewInstance" class="ui-icon ui-icon-circle-close ui-duration"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.rundateSelectView viewName="rundateSelectViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_rundateSelectViewInstance" class="ui-icon ui-icon-circle-close ui-rundate"></a><input id="custom_rundate_filter" type="hidden"></th>
+      <th class="notActive"><div class="view-wrapper">{{view view.jobsFilterView valueBinding="controller.filterObject.jobs" viewName="jobsFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_jobsFilterViewInstance" class="ui-icon ui-icon-circle-close ui-jobs"></a></th>
+      <th class="notActive"><div class="view-wrapper">{{view view.inputFilterView valueBinding="controller.filterObject.input" viewName="inputFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_inputFilterViewInstance" class="ui-icon ui-icon-circle-close ui-input"></a></th>
+      <th class="notActive"><div class="view-wrapper">{{view view.outputFilterView valueBinding="controller.filterObject.output" viewName="outputFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_outputFilterViewInstance" class="ui-icon ui-icon-circle-close ui-output"></a></th>
+      <th class="notActive"><div class="view-wrapper">{{view view.durationFilterView valueBinding="controller.filterObject.duration" viewName="durationFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_durationFilterViewInstance" class="ui-icon ui-icon-circle-close ui-duration"></a></th>
+      <th class="notActive"><div class="view-wrapper">{{view view.rundateSelectView selectionBinding="controller.filterObject.runDate" viewName="rundateSelectViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_rundateSelectViewInstance" class="ui-icon ui-icon-circle-close ui-rundate"></a><input id="custom_rundate_filter" type="hidden"></th>
     </tr>
     </thead>
     <tbody>
-    {{#each run in view.content}}
+    {{#each run in content}}
       {{view view.containerRow runBinding="run" currentViewBinding="view.appTableRow"}}
     {{/each}}
     </tbody>
   </table>
+
+  <div class="page-bar">
+      <div class="items-on-page">
+          <label>Show: {{view view.runPerPageSelectView viewName="runPerPageSelectView" selectionBinding="controller.filterObject.iDisplayLength"}}</label>
+      </div>
+      <div class="info">{{controller.paginationObject.startIndex}} - {{controller.paginationObject.endIndex}} of {{controller.paginationObject.iTotalDisplayRecords}}</div>
+      <div class="paging_two_button">
+          {{view view.paginationLeft}}
+          {{view view.paginationRight}}
+      </div>
+  </div>
 </div>

+ 0 - 23
ambari-web/app/templates/main/apps/custom_rundate_popup.hbs

@@ -1,23 +0,0 @@
-{{!
-* 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.
-}}
-
-<p>Select date and time range:</p>
-From {{view view.lowerBoundView}}
-To {{view view.upperBoundView}}
-<p>either specify exact date and time:</p>
-Equal {{view view.equalView}}

+ 2 - 2
ambari-web/app/templates/main/apps/item/dag.hbs

@@ -31,9 +31,9 @@
       </tr>
       </thead>
       <tbody>
-      {{#each job in view.content}}
+      {{#each job in controller.content.jobs}}
       <tr>
-        <td>{{job.workflowEntityName}}</td>
+        <td>{{job.workflow_entity_name}}</td>
         <td>{{job.id}}</td>
         <td>{{job.status}}</td>
         <td>{{job.maps}}</td>

+ 1 - 2
ambari-web/app/templates/main/apps/list_row.hbs

@@ -15,8 +15,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 }}
-<td><a href="#" {{action "starClick" target="controller"}}><span class="icon-star"></span></a></td>
-<td>{{unbound run.id}}</td>
+
 <td class="appId" title="{{unbound run.id}}">{{unbound run.idFormatted}}</td>
 <td>{{unbound run.appName}}</td>
 <td>{{unbound run.type}}</td>

+ 1 - 0
ambari-web/app/utils/http_client.js

@@ -77,6 +77,7 @@ App.HttpClient = Em.Object.create({
    * @param {number} interval - frequecy request
    */
   get: function (url, mapper, data, errorHandler, interval) {
+    var eHandler = data.complete
     var client = this;
     var request = function () {
       client.request(url, data, mapper, errorHandler);

+ 0 - 35
ambari-web/app/utils/jquery.unique.js

@@ -1,35 +0,0 @@
-/**
- * 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.
- */
-
-(function($){
-
-  var _old = $.unique;
-
-  /*$.unique = function(arr){
-
-    // do the default behavior only if we got an array of elements
-    if (!!arr[0].nodeType){
-      return _old.apply(this,arguments);
-    } else {
-      // reduce the array to contain no dupes via grep/inArray
-      return $.grep(arr,function(v,k){
-        return $.inArray(v,arr) === k;
-      });
-    }
-  };*/
-})(jQuery);

+ 10 - 2
ambari-web/app/views/main/apps/item/bar_view.js

@@ -79,14 +79,22 @@ App.MainAppsItemBarView = Em.View.extend({
       "&width=" + this.get('width');
     var mapper = App.jobTimeLineMapper;
     mapper.set('model', this);
-    App.HttpClient.get(url, mapper);
+    App.HttpClient.get(url, mapper,{
+      complete:function(jqXHR, textStatus) {
+        console.log("updateTimeLine");
+      }
+    });
   }.observes('getChartData'),
 
   updateTasksView:function () {
     var url = App.testMode ? '/data/apps/jobs/taskview.json' : App.apiPrefix + "/jobhistory/tasklocality?jobId=" + this.get('activeJob').get('id');
     var mapper = App.jobTasksMapper;
     mapper.set('model', this);
-    App.HttpClient.get(url, mapper);
+    App.HttpClient.get(url, mapper,{
+      complete:function(jqXHR, textStatus) {
+        console.log("updateTasksView");
+      }
+    });
   }.observes('getChartData'),
 
   drawJobTimeline:function () {

+ 9 - 4
ambari-web/app/views/main/apps/item/dag_view.js

@@ -22,7 +22,12 @@ App.MainAppsItemDagView = Em.View.extend({
   templateName: require('templates/main/apps/item/dag'),
   elementId : 'jobs',
   content:function(){
-    return this.get('controller.content.jobs');
+    //if(this.get("controller.jobsLoaded") == true)
+   // {
+      return this.get('controller.content.jobs');
+  //  }
+      return this.get('controller.content.jobs');
+  //  }
   }.property('controller.content.jobs'),
 
   classNames:['table','dataTable'],
@@ -35,11 +40,11 @@ App.MainAppsItemDagView = Em.View.extend({
     c.forEach(function(item, index){
       result[index] = new Object({
         'name' : item.get('id'),
-        'entityName' : item.get('workflowEntityName'),
+        'entityName' : item.get('workflow_entity_name'),
         'status' : item.get('status') == 'SUCCESS',
         'info' : [],
-        'input' : item.get('inputBytes'),
-        'output' : item.get('outputBytes')
+        'input' : item.get('input'),
+        'output' : item.get('output')
       })
     });
     return result;

File diff suppressed because it is too large
+ 198 - 710
ambari-web/app/views/main/apps_view.js


Some files were not shown because too many files changed in this diff