ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From onechipore...@apache.org
Subject [1/2] git commit: AMBARI-6693. Jobs View: Integrate UI with API. (onechiporenko)
Date Thu, 31 Jul 2014 12:13:07 GMT
Repository: ambari
Updated Branches:
  refs/heads/trunk c635f886b -> bf10914af


AMBARI-6693. Jobs View: Integrate UI with API. (onechiporenko)


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

Branch: refs/heads/trunk
Commit: 34c3355de97f332323ec36d184dea42151618ce3
Parents: 0f4f7d7
Author: Oleg Nechiporenko <onechiporenko@apache.org>
Authored: Thu Jul 31 15:10:26 2014 +0300
Committer: Oleg Nechiporenko <onechiporenko@apache.org>
Committed: Thu Jul 31 15:10:26 2014 +0300

----------------------------------------------------------------------
 .../src/main/resources/ui/app/scripts/app.js    |   2 +-
 .../app/scripts/controllers/job_controller.js   |  44 ++++--
 .../app/scripts/controllers/jobs_controller.js  |  99 ++++++------
 .../resources/ui/app/scripts/helpers/number.js  |   1 +
 .../scripts/mappers/jobs/hive_jobs_mapper.js    |  20 ++-
 .../ui/app/scripts/routes/application_route.js  |   8 +
 .../views/job/hive_job_details_tez_dag_view.js  | 155 ++++++++++++++-----
 .../scripts/views/job/hive_job_details_view.js  | 115 ++++++++++++--
 .../resources/ui/app/scripts/views/jobs_view.js |  59 ++++++-
 .../src/main/resources/ui/app/styles/main.less  |   8 +-
 .../resources/ui/app/templates/application.hbs  |   2 +-
 .../main/resources/ui/app/templates/job/job.hbs |   2 +-
 .../main/resources/ui/app/templates/jobs.hbs    |   1 +
 13 files changed, 381 insertions(+), 135 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/scripts/app.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/app.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/app.js
index 1067704..3ab2c30 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/app.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/app.js
@@ -45,7 +45,7 @@ App.initializer({
        * Prefix for API-requests
        * @type {string}
        */
-      urlPrefix: '/api/v1',
+      urlPrefix: '/api/v1/',
 
       /**
        * Current cluster name

http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/job_controller.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/job_controller.js
b/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/job_controller.js
index bce4526..6047695 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/job_controller.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/job_controller.js
@@ -16,22 +16,41 @@
  * limitations under the License.
  */
 
-App.JobController = Ember.ObjectController.extend({
+App.JobController = Ember.ObjectController.extend(App.RunPeriodically, {
 
   name: 'jobController',
 
+  /**
+   * Is Job details info loaded
+   * @type {bool}
+   */
   loaded: false,
 
+  /**
+   * Timeout for <code>loadJobDetails</code> periodic call
+   * @type {number}
+   */
   loadTimeout: null,
 
-  job: null,
-
+  /**
+   * Column which is currently sorted
+   * @type {string}
+   */
   sortingColumn: null,
 
+  /**
+   * Modal-popup buttons
+   * @type {Em.Object[]}
+   */
   showPopupButtons: [
     Ember.Object.create({title: Em.I18n.t('ok'), dismiss: 'modal'})
   ],
 
+  /**
+   * Show popup with message about job-details loading error
+   * @param {string} title
+   * @method showPopup
+   */
   showPopup: function (title) {
     Bootstrap.ModalManager.open(
       'errorPopup',
@@ -42,6 +61,11 @@ App.JobController = Ember.ObjectController.extend({
     );
   },
 
+  /**
+   * Init method called  in <code>router.setupController</code>
+   * Load job's details info (like Tez Dag etc)
+   * @method loadJobDetails
+   */
   loadJobDetails: function () {
     var self = this,
       timeout = this.get('loadTimeout'),
@@ -85,20 +109,6 @@ App.JobController = Ember.ObjectController.extend({
         self.loadJobDetails();
       }, 300);
     }
-  },
-
-  /**
-   * open jobs page
-   * @method routeToJobs
-   */
-  routeToJobs: function () {
-    this.transitionToRoute('jobs');
-  },
-
-  actions: {
-    actionRouteToJobs: function () {
-      this.routeToJobs();
-    }
   }
 
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/jobs_controller.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/jobs_controller.js
b/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/jobs_controller.js
index f0fd64a..b5cdfc7 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/jobs_controller.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/jobs_controller.js
@@ -66,6 +66,18 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically,
{
   ],
 
   actions: {
+
+    updateJobsByClick: function () {
+      this.set('navIDs.backIDs', []);
+      this.set('navIDs.nextID', '');
+      this.set('filterObject.nextFromId', '');
+      this.set('filterObject.backFromId', '');
+      this.set('filterObject.fromTs', '');
+      this.set('hasNewJobs', false);
+      this.set('resetPagination', true);
+      this.loadJobs();
+    },
+
     submitCustomDate: function () {
       if(this.get('filterObject').submitCustomDate())
         Bootstrap.ModalManager.close('customDate');
@@ -74,11 +86,19 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically,
{
     dismissCustomDate: function() {
       this.set('filterObject.startTime', 'Any');
     }
+
   },
 
   contentAndSortObserver: function () {
     Ember.run.once(this, 'contentAndSortUpdater');
-  }.observes('content.length', 'content.@each.id', 'content.@each.startTime', 'content.@each.endTime',
'sortProperties', 'sortAscending'),
+  }.observes(
+      'content.length',
+      'content.@each.id',
+      'content.@each.startTime',
+      'content.@each.endTime',
+      'sortProperties',
+      'sortAscending'
+    ),
 
   contentAndSortUpdater: function () {
     this.set('sortingDone', false);
@@ -236,7 +256,7 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically,
{
       // Check that endDate is after startDate
       var startDate = this.createCustomStartDate(),
         endDate = this.createCustomEndDate();
-      if (startDate && endDate && (startDate > endDate)) {
+      if (startDate && endDate && (startDate.getTime() > endDate.getTime()))
{
         errors.set('isEndDateError', true);
         errorMessages.set('endDate', Em.I18n.t('jobs.customDateFilter.error.date.order'));
       }
@@ -280,6 +300,7 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically,
{
 
       return link;
     }
+
   }),
 
   sortingColumnObserver: function () {
@@ -289,25 +310,6 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically,
{
     }
   }.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', '');
-    this.set('hasNewJobs', false);
-    this.set('resetPagination', true);
-    this.loadJobs();
-  },
-
-  updateJobs: function (controllerName, funcName) {
-    clearInterval(this.get('jobsUpdate'));
-    var interval = setInterval(function () {
-      App.router.get(controllerName)[funcName]();
-    }, this.get('jobsUpdateInterval'));
-    this.set('jobsUpdate', interval);
-  },
-
   setTotalOfJobs: function () {
     if (this.get('totalOfJobs') < this.get('content.length')) {
       this.set('totalOfJobs', this.get('content.length'));
@@ -315,8 +317,8 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically,
{
   }.observes('content.length'),
 
   startTimeObserver: function () {
-    var time = "";
-    var curTime = new Date().getTime();
+    var time = "",
+      curTime = new Date().getTime();
     switch (this.get('filterObject.startTime')) {
       case 'Past 1 hour':
         time = curTime - 3600000;
@@ -347,7 +349,7 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically,
{
       this.set("filterObject.windowStart", time);
       this.set("filterObject.windowEnd", "");
     }
-  }.observes('filterObject.startTime'),
+  },
 
   showCustomDatePopup: function () {
     Bootstrap.ModalManager.open(
@@ -366,14 +368,16 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically,
{
     var lastReceivedID = data.entities[0].entity;
     if (this.get('lastJobID') == '') {
       this.set('lastJobID', lastReceivedID);
-      if (this.get('loaded') && App.HiveJob.find().get('length') < 1) {
+      if (this.get('loaded') && App.HiveJob.store.all('hiveJob').get('length') <
1) {
         this.set('hasNewJobs', true);
       }
     }
-    else if (this.get('lastJobID') !== lastReceivedID) {
-      this.set('lastJobID', lastReceivedID);
-      if (!App.HiveJob.find().findProperty('id', lastReceivedID)) {
-        this.set('hasNewJobs', true);
+    else {
+      if (this.get('lastJobID') !== lastReceivedID) {
+        this.set('lastJobID', lastReceivedID);
+        if (!App.HiveJob.store.getById('hiveJob', lastReceivedID)) {
+          this.set('hasNewJobs', true);
+        }
       }
     }
   },
@@ -402,11 +406,6 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically,
{
     }
   },
 
-  init: function () {
-    this.set('interval', 6000);
-    this.loop('loadJobs');
-  },
-
   loadJobs: function () {
     var yarnService = App.HiveJob.store.getById('service', 'YARN'),
       atsComponent = App.HiveJob.store.getById('component', 'APP_TIMELINE_SERVER'),
@@ -415,16 +414,16 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically,
{
     if (!Em.isNone(yarnService) && atsInValidState) {
       this.set('loading', true);
       var historyServerHostName = atsComponent.get('hostName');
-      /*App.ajax.send({
-       name: 'jobs.lastID',
-       sender: self,
-       data: {
-       historyServerHostName: '',//historyServerHostName,
-       ahsWebPort: ''//yarnService.get('ahsWebPort')
-       },
-       success: 'lastIDSuccessCallback',
-       error : 'lastIDErrorCallback'
-       });*/
+      App.ajax.send({
+        name: 'jobs_lastID',
+        sender: this,
+        data: {
+          historyServerHostName: historyServerHostName,
+          ahsWebPort: yarnService.get('ahsWebPort')
+        },
+        success: 'lastIDSuccessCallback',
+        error : 'lastIDErrorCallback'
+      });
       App.ajax.send({
         name: 'load_jobs',
         sender: this,
@@ -489,11 +488,11 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically,
{
   refreshLoadedJobs: function () {
     this.loadJobs();
   }.observes(
-      'filterObject.id',
-      'filterObject.jobsLimit',
-      'filterObject.user',
-      'filterObject.windowStart',
-      'filterObject.windowEnd'
-    )
+    'filterObject.id',
+    'filterObject.jobsLimit',
+    'filterObject.user',
+    'filterObject.windowStart',
+    'filterObject.windowEnd'
+  )
 
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/number.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/number.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/number.js
index 4adf792..a8e9f6b 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/number.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/number.js
@@ -30,6 +30,7 @@ App.Helpers.number = {
    * @return {String} Returns converted value with abbreviation.
    */
   bytesToSize: function (bytes, precision, parseType, multiplyBy) {
+    if (isNaN(bytes)) bytes = 0;
     if (Em.isNone(bytes)) {
       return 'n/a';
     } else {

http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/scripts/mappers/jobs/hive_jobs_mapper.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/mappers/jobs/hive_jobs_mapper.js
b/contrib/views/jobs/src/main/resources/ui/app/scripts/mappers/jobs/hive_jobs_mapper.js
index e87d8e9..1cdb76d 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/mappers/jobs/hive_jobs_mapper.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/mappers/jobs/hive_jobs_mapper.js
@@ -53,7 +53,6 @@ App.hiveJobsMapper = App.QuickDataMapper.create({
   map: function (json) {
 
     var model = this.get('model'),
-      jobsToDelete = App.HiveJob.store.all('hiveJob').get('content').mapProperty('id'),
       map = this.get('json_map'),
       hiveJobs = [];
 
@@ -64,8 +63,9 @@ App.hiveJobsMapper = App.QuickDataMapper.create({
           json.entities = [json];
         }
       }
-
+      var currentEntityMap = {};
       json.entities.forEach(function (entity) {
+        currentEntityMap[entity.entity] = entity.entity;
         var hiveJob = Ember.JsonMapper.map(entity, map);
 
         if (entity.events != null) {
@@ -87,12 +87,20 @@ App.hiveJobsMapper = App.QuickDataMapper.create({
         if (!Em.isNone(tezDag)) {
           hiveJob.tezDag = tezDag.id;
         }
-        jobsToDelete = jobsToDelete.without(hiveJob.id);
       });
 
-      jobsToDelete.forEach(function (id) {
-        var r = App.HiveJob.store.getById('hiveJob', id);
-        if(r) r.destroyRecord();
+      var jobsController = App.__container__.lookup('controller:Jobs');
+      if(hiveJobs.length > jobsController.get('filterObject.jobsLimit')) {
+        var lastJob = hiveJobs.pop();
+        if(jobsController.get('navIDs.nextID') != lastJob.id) {
+          jobsController.set('navIDs.nextID', lastJob.id);
+        }
+        currentEntityMap[lastJob.id] = null;
+      }
+      App.HiveJob.store.all('hiveJob').forEach(function (r) {
+        if(r && !currentEntityMap[r.get('id')]) {
+          r.destroyRecord();
+        }
       });
 
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/scripts/routes/application_route.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/routes/application_route.js
b/contrib/views/jobs/src/main/resources/ui/app/scripts/routes/application_route.js
index 7532197..a34d085 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/routes/application_route.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/routes/application_route.js
@@ -32,6 +32,13 @@ App.JobsRoute = Ember.Route.extend({
 
   model: function () {
     return this.get('store').find('hiveJob');
+  },
+
+  setupController: function(controller, model) {
+    this._super(controller, model);
+    controller.set('interval', 6000);
+    controller.loop('loadJobs', true);
+    Em.addObserver(controller, 'filterObject.startTime', controller, 'startTimeObserver');
   }
 
 });
@@ -41,6 +48,7 @@ App.JobRoute = Ember.Route.extend({
   setupController: function(controller, model) {
     this._super(controller, model);
     controller.set('loaded', false);
+    controller.loop('loadJobDetails', true);
   },
 
   model: function (params) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job/hive_job_details_tez_dag_view.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job/hive_job_details_tez_dag_view.js
b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job/hive_job_details_tez_dag_view.js
index 1224d1e..7938fc5 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job/hive_job_details_tez_dag_view.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job/hive_job_details_tez_dag_view.js
@@ -16,19 +16,39 @@
  */
 
 App.MainHiveJobDetailsTezDagView = Em.View.extend({
+
   templateName: 'job/hive_job_details_tez_dag',
+
+  /**
+   * Selected Vertex
+   * @type {App.TezDagVertex}
+   */
   selectedVertex: null,
+
+  /**
+   * @type {string}
+   */
   summaryMetricType: null,
-  svgVerticesLayer: null, // The contents of the <svg> element.
+
+  /**
+   * The contents of the <svg> element
+   */
+  svgVerticesLayer: null,
+
   svgTezRoot: null,
+
   svgWidth: -1,
+
   svgHeight: -1,
 
   // zoomScaleFom: -1, // Bound from parent view
   // zoomScaleTo: -1, // Bound from parent view
   // zoomScale: -1, // Bound from parent view
+
   zoomTranslate: [0, 0],
+
   zoomBehavior: null,
+
   svgCreated: false,
 
   /**
@@ -88,6 +108,10 @@ App.MainHiveJobDetailsTezDagView = Em.View.extend({
     $('.svg-tooltip').tooltip('destroy');
   },
 
+  /**
+   * Basic init for graph
+   * @method createSvg
+   */
   createSvg: function () {
     var self = this;
     var dagVisualModel = this.get('dagVisualModel');
@@ -124,6 +148,10 @@ App.MainHiveJobDetailsTezDagView = Em.View.extend({
     this.set('svgCreated', true);
   },
 
+  /**
+   * Change graph's zoom
+   * @method zoomScaleObserver
+   */
   zoomScaleObserver: function () {
     var tezRoot = this.get("svgTezRoot"),
       newScale = this.get('zoomScale'),
@@ -163,8 +191,8 @@ App.MainHiveJobDetailsTezDagView = Em.View.extend({
   }.observes('zoomScale', 'zoomScaleFrom', 'zoomScaleTo', 'zoomTranslate'),
 
   /**
-   * We have to make the height of the DAG section match the height of the
-   * Summary section.
+   * We have to make the height of the DAG section match the height of the Summary section.
+   * @method adjustGraphHeight
    */
   adjustGraphHeight: function () {
     var rhsDiv = document.getElementById('tez-vertices-rhs'),
@@ -180,20 +208,24 @@ App.MainHiveJobDetailsTezDagView = Em.View.extend({
     }
   },
 
+  /**
+   * Update graph when <code>selectedVertex</code> changed
+   * @method vertexSelectionUpdated
+   */
   vertexSelectionUpdated: function () {
-    var vertexId = this.get('selectedVertex.id');
-    var zoomTranslate = [];
-    var zoomBehavior = this.get('zoomBehavior');
-    var selectedNode = this.get('dagVisualModel').nodes.findProperty('id', vertexId);
-    var dagVisualModel = this.get('dagVisualModel');
+    var vertexId = this.get('selectedVertex.id'),
+      zoomTranslate = [],
+      zoomBehavior = this.get('zoomBehavior'),
+      selectedNode = this.get('dagVisualModel').nodes.findProperty('id', vertexId),
+      dagVisualModel = this.get('dagVisualModel');
     if (dagVisualModel && dagVisualModel.nodes && dagVisualModel.nodes.length
> 0) {
       dagVisualModel.nodes.forEach(function (node) {
         node.selected = node.id == vertexId;
       })
     }
     if (!this.get('selectedVertex.notTableClick')) {
-      var cX = selectedNode.x + (selectedNode.width) / 2;
-      var cY = selectedNode.y + (selectedNode.height) / 2;
+      var cX = selectedNode.x + (selectedNode.width) / 2,
+        cY = selectedNode.y + (selectedNode.height) / 2;
       zoomTranslate[0] = (225 / zoomBehavior.scale() - cX);
       zoomTranslate[1] = (250 / zoomBehavior.scale() - cY);
       this.set('zoomTranslate', [0, 0]);
@@ -203,21 +235,26 @@ App.MainHiveJobDetailsTezDagView = Em.View.extend({
     this.refreshGraphUI();
   }.observes('selectedVertex'),
 
+  /**
+   * Update graph when new summary metric is selected
+   * @method summaryMetricTypeUpdated
+   */
   summaryMetricTypeUpdated: function () {
-    var summaryMetricType = this.get('summaryMetricType');
-    var dagVisualModel = this.get('dagVisualModel');
-    var min = dagVisualModel.minMetrics[summaryMetricType];
-    var max = dagVisualModel.maxMetrics[summaryMetricType];
+    var summaryMetricType = this.get('summaryMetricType'),
+      dagVisualModel = this.get('dagVisualModel'),
+      min = dagVisualModel.minMetrics[summaryMetricType],
+      max = dagVisualModel.maxMetrics[summaryMetricType];
     dagVisualModel.nodes.forEach(function (node) {
-      var value = node.metrics[summaryMetricType];
-      var percent = -1;
+      var value = node.metrics[summaryMetricType],
+        percent = -1;
       if (App.Helpers.number.validateInteger(value) == null && value >= 0) {
         if (App.Helpers.number.validateInteger(min) == null && App.Helpers.number.validateInteger(max)
== null) {
           if (max > min && value >= 0) {
             percent = Math.round((value - min) * 100 / (max - min));
           }
         }
-      } else {
+      }
+      else {
         value = '';
       }
       switch (summaryMetricType) {
@@ -236,7 +273,8 @@ App.MainHiveJobDetailsTezDagView = Em.View.extend({
   }.observes('summaryMetricType'),
 
   /**
-   * Observes metrics of all vertices.
+   * Observes metrics of all vertices
+   * @method vertexMetricsUpdated
    */
   vertexMetricsUpdated: function () {
     var dagVisualModel = this.get('dagVisualModel');
@@ -285,11 +323,25 @@ App.MainHiveJobDetailsTezDagView = Em.View.extend({
       });
     }
     Ember.run.once(this, 'summaryMetricTypeUpdated');
-  }.observes('content.tezDag.vertices.@each.fileReadBytes', 'content.tezDag.vertices.@each.fileWriteBytes',
-      'content.tezDag.vertices.@each.hdfsReadBytes', 'content.tezDag.vertices.@each.hdfsWriteBytes',
-      'content.tezDag.vertices.@each.recordReadCount', 'content.tezDag.vertices.@each.recordWriteCount',
-      'content.tezDag.vertices.@each.state', 'content.tezDag.vertices.@each.spilledRecords'),
+  }.observes(
+      'content.tezDag.vertices.@each.fileReadBytes',
+      'content.tezDag.vertices.@each.fileWriteBytes',
+      'content.tezDag.vertices.@each.hdfsReadBytes',
+      'content.tezDag.vertices.@each.hdfsWriteBytes',
+      'content.tezDag.vertices.@each.recordReadCount',
+      'content.tezDag.vertices.@each.recordWriteCount',
+      'content.tezDag.vertices.@each.state',
+      'content.tezDag.vertices.@each.spilledRecords'
+    ),
 
+  /**
+   * Create object with data for graph popups
+   * @param {string} vertexName
+   * @param {string} op
+   * @param {number} opIndex
+   * @returns {{name: string, value: string}[]}
+   * @method createOperationPlanObj
+   */
   createOperationPlanObj: function (vertexName, op, opIndex) {
     var operatorPlanObj = [],
       text = this.get('content.tezDag.vertices').findBy('name', vertexName).get('operationPlan');
@@ -316,6 +368,7 @@ App.MainHiveJobDetailsTezDagView = Em.View.extend({
    *
    * Terminology: 'vertices' and 'edges' are Tez terms. 'nodes' and 'links' are
    * visual (d3) terms.
+   * @method drawTezDag
    */
   drawTezDag: function () {
     var self = this,
@@ -768,35 +821,46 @@ App.MainHiveJobDetailsTezDagView = Em.View.extend({
 
   /**
    * Refreshes UI of the Tez graph with latest values
+   * @method refreshGraphUI
    */
   refreshGraphUI: function () {
     var svgLayer = this.get('svgVerticesLayer');
     if (svgLayer != null) {
-      var self = this;
-      var metricNodes = svgLayer.selectAll(".metric");
-      var metricNodeTexts = svgLayer.selectAll(".metric-text");
-      var metricNodeTitles = svgLayer.selectAll(".metric-title");
-      var nodeBackgrounds = svgLayer.selectAll(".background");
-      var vertexIconTexts = svgLayer.selectAll(".vertex-icon-text");
-      var vertexIconRects = svgLayer.selectAll(".vertex-icon-rect");
+      var self = this,
+        metricNodes = svgLayer.selectAll(".metric"),
+        metricNodeTexts = svgLayer.selectAll(".metric-text"),
+        metricNodeTitles = svgLayer.selectAll(".metric-title"),
+        nodeBackgrounds = svgLayer.selectAll(".background"),
+        vertexIconTexts = svgLayer.selectAll(".vertex-icon-text"),
+        vertexIconRects = svgLayer.selectAll(".vertex-icon-rect");
       metricNodes.attr("class", function (node) {
-        var classes = "metric ";
-        var percent = node.metricPercent;
+        var classes = "metric ",
+          percent = node.metricPercent;
         if (App.Helpers.number.validateInteger(percent) == null && percent >=
0) {
           if (percent <= 20) {
             classes += "heat-0-20 ";
-          } else if (percent <= 40) {
-            classes += "heat-20-40 ";
-          } else if (percent <= 60) {
-            classes += "heat-40-60 ";
-          } else if (percent <= 80) {
-            classes += "heat-60-80 ";
-          } else if (percent <= 100) {
-            classes += "heat-80-100 ";
-          } else {
-            classes += "heat-none";
           }
-        } else {
+          else
+            if (percent <= 40) {
+              classes += "heat-20-40 ";
+            }
+            else
+              if (percent <= 60) {
+                classes += "heat-40-60 ";
+              }
+              else
+                if (percent <= 80) {
+                  classes += "heat-60-80 ";
+                }
+                else
+                  if (percent <= 100) {
+                    classes += "heat-80-100 ";
+                  }
+                  else {
+                    classes += "heat-none";
+                  }
+        }
+        else {
           classes += "heat-none";
         }
         return classes;
@@ -846,6 +910,12 @@ App.MainHiveJobDetailsTezDagView = Em.View.extend({
     }
   },
 
+  /**
+   * Get icon for vertex according to node state
+   * @param {object} node
+   * @returns {string}
+   * @method getVertexIcon
+   */
   getVertexIcon: function (node) {
     var icon = "";
     switch (node.state) {
@@ -888,6 +958,7 @@ App.MainHiveJobDetailsTezDagView = Em.View.extend({
    *  drawHeight: 40 // Height of actual drawing (that will be scaled)
    * }
    * </code>
+   * @method getNodeCalculatedDimensions
    */
   getNodeCalculatedDimensions: function (node, minVertexDuration, maxVertexDuration) {
     var size = {

http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job/hive_job_details_view.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job/hive_job_details_view.js
b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job/hive_job_details_view.js
index 122f236..807dd78 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job/hive_job_details_view.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job/hive_job_details_view.js
@@ -29,12 +29,16 @@ App.JobView = Em.View.extend({
 
   zoomScale: 1,
 
+  /**
+   * Is query visible
+   * @type {bool}
+   */
   showQuery: false,
 
-  willInsertElement: function () {
-    this.get('controller').loadJobDetails();
-  },
-
+  /**
+   * Current graph zoom
+   * @type {number}
+   */
   zoomStep: function () {
     var zoomStep = 0.01;
     var zoomFrom = this.get('zoomScaleFrom');
@@ -45,10 +49,19 @@ App.JobView = Em.View.extend({
     return zoomStep;
   }.property('zoomScaleFrom', 'zoomScaleTo'),
 
+  /**
+   * Is graph in maximum zoom
+   * @type {bool}
+   */
   isGraphMaximized: false,
 
   actions: {
 
+    /**
+     * Summary metric change handler
+     * @param {string} summaryType
+     * @method doSelectSummaryMetricType
+     */
     doSelectSummaryMetricType: function (summaryType) {
       switch (summaryType) {
         case Em.I18n.t('jobs.hive.tez.metric.input'):
@@ -75,6 +88,10 @@ App.JobView = Em.View.extend({
       this.set('summaryMetricType', summaryType);
     },
 
+    /**
+     * "Show more/less" click handler
+     * @method toggleShowQuery
+     */
     toggleShowQuery: function () {
       this.toggleProperty('showQuery');
       var queryBlock = $('.query-info');
@@ -86,10 +103,20 @@ App.JobView = Em.View.extend({
       }
     },
 
+    /**
+     * Click handler for vertex-name in the table of vertexes
+     * @param {App.TezDagVertex} event
+     * @param {bool} notTableClick
+     * @method actionDoSelectVertex
+     */
     actionDoSelectVertex: function (event, notTableClick) {
       this.doSelectVertex(event, notTableClick);
     },
 
+    /**
+     * Zoom-In click-handler
+     * @method doGraphZoomIn
+     */
     doGraphZoomIn: function () {
       var zoomTo = this.get('zoomScaleTo'),
         zoomScale = this.get('zoomScale'),
@@ -101,6 +128,10 @@ App.JobView = Em.View.extend({
       }
     },
 
+    /**
+     * Zoom-out click-handler
+     * @method doGraphZoomOut
+     */
     doGraphZoomOut: function () {
       var zoomFrom = this.get('zoomScaleFrom'),
         zoomScale = this.get('zoomScale'),
@@ -112,22 +143,42 @@ App.JobView = Em.View.extend({
       }
     },
 
+    /**
+     * Maximize graph
+     * @method doGraphMaximize
+     */
     doGraphMaximize: function () {
       this.set('isGraphMaximized', true);
     },
 
+    /**
+     * Minimize graph
+     * @method doGraphMinimize
+     */
     doGraphMinimize: function () {
       this.set('isGraphMaximized', false);
     }
 
   },
 
+  /**
+   * "Show more/less"-message
+   * @type {string}
+   */
   toggleShowQueryText: function () {
     return this.get('showQuery') ? Em.I18n.t('jobs.hive.less') : Em.I18n.t('jobs.hive.more');
   }.property('showQuery'),
 
+  /**
+   * Current metric type in the metrics-type listbox
+   * @type {string}
+   */
   summaryMetricType: 'input',
 
+  /**
+   * List of available values for <code>summaryMetricType</code>
+   * @type {string[]}
+   */
   summaryMetricTypesDisplay: [
     Em.I18n.t('jobs.hive.tez.metric.input'),
     Em.I18n.t('jobs.hive.tez.metric.output'),
@@ -137,10 +188,18 @@ App.JobView = Em.View.extend({
     Em.I18n.t('jobs.hive.tez.metric.spilledRecords')
   ],
 
+  /**
+   * Display-value for <code>summaryMetricType</code>
+   * @type {string}
+   */
   summaryMetricTypeDisplay: function () {
     return Em.I18n.t('jobs.hive.tez.metric.' + this.get('summaryMetricType'));
   }.property('summaryMetricType'),
 
+  /**
+   * List of sorted vertexes for current job
+   * @type {App.TezDagVertex[]}
+   */
   sortedVertices: function () {
     var sortColumn = this.get('controller.sortingColumn');
     if (sortColumn && sortColumn.get('status')) {
@@ -156,12 +215,20 @@ App.JobView = Em.View.extend({
     return vertices;
   }.property('content.tezDag.vertices', 'controller.sortingColumn'),
 
+  /**
+   * When all data loaded in the controller, set <code>content</code>-value
+   * @method initialDataLoaded
+   */
   initialDataLoaded: function () {
     if (this.get('controller.loaded')) {
       this.set('content', this.get('controller.content'));
     }
   }.observes('controller.loaded'),
 
+  /**
+   * Set proper value to <code>isSelected</code> for each vertex
+   * @method jobObserver
+   */
   jobObserver: function () {
     var content = this.get('content'),
       selectedVertex = this.get('selectedVertex');
@@ -174,6 +241,12 @@ App.JobView = Em.View.extend({
     }
   }.observes('selectedVertex', 'content.tezDag.vertices.@each.id'),
 
+  /**
+   * Set <code>selectedVertex</code>
+   * @param {App.TezDagVertex} newVertex
+   * @param {bool} notTableClick
+   * @method doSelectVertex
+   */
   doSelectVertex: function (newVertex, notTableClick) {
     var currentVertex = this.get('selectedVertex');
     if (currentVertex != null) {
@@ -185,8 +258,6 @@ App.JobView = Em.View.extend({
   },
 
   /**
-   * Provides display information for vertex I/O.
-   *
    * {
    *  'file': {
    *    'read': {
@@ -219,12 +290,30 @@ App.JobView = Em.View.extend({
    */
   selectedVertexIODisplay: {},
 
+  /**
+   * Handler to call <code>selectedVertexIODisplayObs</code> once
+   * @method selectedVertexIODisplayObsOnce
+   */
   selectedVertexIODisplayObsOnce: function() {
     Em.run.once(this, 'selectedVertexIODisplayObs');
-  }.observes('selectedVertex.fileReadOps', 'selectedVertex.fileWriteOps', 'selectedVertex.hdfsReadOps',
'selectedVertex.hdfdWriteOps',
-      'selectedVertex.fileReadBytes', 'selectedVertex.fileWriteBytes', 'selectedVertex.hdfsReadBytes',
'selectedVertex.hdfdWriteBytes',
-      'selectedVertex.recordReadCount', 'selectedVertex.recordWriteCount', 'selectedVertex.status'),
+  }.observes(
+      'selectedVertex.fileReadOps',
+      'selectedVertex.fileWriteOps',
+      'selectedVertex.hdfsReadOps',
+      'selectedVertex.hdfdWriteOps',
+      'selectedVertex.fileReadBytes',
+      'selectedVertex.fileWriteBytes',
+      'selectedVertex.hdfsReadBytes',
+      'selectedVertex.hdfdWriteBytes',
+      'selectedVertex.recordReadCount',
+      'selectedVertex.recordWriteCount',
+      'selectedVertex.status'
+    ),
 
+  /**
+   * Provides display information for vertex I/O.
+   * @method selectedVertexIODisplayObs
+   */
   selectedVertexIODisplayObs: function () {
     var v = this.get('selectedVertex'),
       naString = Em.I18n.t('common.na'),
@@ -265,10 +354,18 @@ App.JobView = Em.View.extend({
     this.set('selectedVertexIODisplay', r);
   },
 
+  /**
+   * Can graph be zoomed-in
+   * @type {bool}
+   */
   canGraphZoomIn: function () {
     return this.get('zoomScale') < this.get('zoomScaleTo');
   }.property('zoomScale', 'zoomScaleTo'),
 
+  /**
+   * Can graph be zoomed-out
+   * @type {bool}
+   */
   canGraphZoomOut: function () {
     return this.get('zoomScale') > this.get('zoomScaleFrom');
   }.property('zoomScale', 'zoomScaleFrom')

http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/scripts/views/jobs_view.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/views/jobs_view.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/jobs_view.js
index 279298b..5fd9b01 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/views/jobs_view.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/jobs_view.js
@@ -30,8 +30,9 @@ App.JobsView = App.TableView.extend({
 
   filterCondition:[],
 
-  /*
-   If no jobs to display set noDataToShow to true, else set emptyData to false.
+  /**
+   * If no jobs to display set noDataToShow to true, else set emptyData to false.
+   * @method noDataToShowObserver
    */
   noDataToShowObserver: function () {
     this.set("noDataToShow", this.get("controller.content.length") === 0);
@@ -54,6 +55,10 @@ App.JobsView = App.TableView.extend({
     }
   },
 
+  /**
+   * Handler for id-filter applying
+   * @method onApplyIdFilter
+   */
   onApplyIdFilter: function() {
     var isIdFilterApplied = this.get('controller.filterObject.isIdFilterApplied');
     this.get('childViews').forEach(function(childView) {
@@ -71,6 +76,10 @@ App.JobsView = App.TableView.extend({
     });
   }.observes('controller.filterObject.isIdFilterApplied'),
 
+  /**
+   * Save filter when filtering is complete
+   * @method saveFilter
+   */
   saveFilter: function () {
     if(this.get('tableFilteringComplete')){
       this.updateFilter(1, this.get('controller.filterObject.id'), 'string');
@@ -140,11 +149,16 @@ App.JobsView = App.TableView.extend({
   /**
    * return filtered number of all content number information displayed on the page footer
bar
    * @returns {String}
+   * @method filteredJobs
    */
   filteredJobs: function () {
     return Em.I18n.t('jobs.filtered.jobs').fmt(this.get('controller.content.length'));
   }.property('controller.content.length', 'controller.totalOfJobs'),
 
+  /**
+   * Manage tooltips for jobs
+   * @method pageContentObserver
+   */
   pageContentObserver: function () {
     if (!this.get('controller.loading')) {
       var tooltip = $('.tooltip');
@@ -204,21 +218,41 @@ App.JobsView = App.TableView.extend({
     }
   }),
 
+  /**
+   * View for job's name
+   * @type {Em.View}
+   */
   jobNameView: Em.View.extend({
 
+    /**
+     * Classname for link
+     * @type {string}
+     */
     isLink: 'is-not-link',
 
+    /**
+     * Update link-status (enabled/disabled) after sorting is complete
+     */
     isLinkObserver: function () {
       this.refreshLinks();
     }.observes('controller.sortingDone'),
 
+    /**
+     * Update <code>isLink</code> according to <code>job.hasTezDag<code>
+     * @method refreshLinks
+     */
     refreshLinks: function () {
       this.set('isLink', this.get('job.hasTezDag') ? "" : "is-not-link");
     },
 
     templateName: 'jobs/jobs_name',
 
-    click: function(event) {
+    /**
+     * Click-handler.
+     * Go to Jobs details page if current job has Tez Dag
+     * @returns {null|boolean}
+     */
+    click: function() {
       if (this.get('job.hasTezDag')) {
         this.get('controller').transitionToRoute('job', this.get('job'));
       }
@@ -231,7 +265,8 @@ App.JobsView = App.TableView.extend({
   }),
 
   /**
-   * associations between content (jobs list) property and column index
+   * Associations between content (jobs list) property and column index
+   * @type {string[]}
    */
   colPropAssoc: function () {
     var associations = [];
@@ -254,6 +289,9 @@ App.JobsView = App.TableView.extend({
     return Em.I18n.t('jobs.table.job.fail');
   }.property(),
 
+  /**
+   * @type {Em.View}
+   */
   jobsPaginationLeft: Ember.View.extend({
     tagName: 'a',
     templateName: 'table/navigation/pagination_left',
@@ -272,6 +310,9 @@ App.JobsView = App.TableView.extend({
     }
   }),
 
+  /**
+   * @type {Em.View}
+   */
   jobsPaginationRight: Ember.View.extend({
     tagName: 'a',
     templateName: 'table/navigation/pagination_right',
@@ -290,10 +331,18 @@ App.JobsView = App.TableView.extend({
     }
   }),
 
+  /**
+   * Enable/disable "next"-arrow
+   * @type {bool}
+   */
   hasNextJobs: function() {
-    return (this.get("controller.navIDs.nextID.length") > 0);
+    return (this.get("controller.navIDs.nextID.length") > 1);
   }.property('controller.navIDs.nextID'),
 
+  /**
+   * Enable/disable "back"-arrow
+   * @type {bool}
+   */
   hasBackLinks: function() {
     return (this.get("controller.navIDs.backIDs").length > 1);
   }.property('controller.navIDs.backIDs.[].length')

http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/styles/main.less
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/styles/main.less b/contrib/views/jobs/src/main/resources/ui/app/styles/main.less
index ce87bff..e648fe0 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/styles/main.less
+++ b/contrib/views/jobs/src/main/resources/ui/app/styles/main.less
@@ -16,8 +16,7 @@
  * limitations under the License.
  */
 
-@import '../../app/bower_components/jquery-ui/themes/base/core.css';
-@import '../../app/bower_components/jquery-ui/themes/base/datepicker.css';
+@import '../../app/bower_components/jquery-ui/themes/base/all.css';
 @import '../../app/bower_components/bootstrap/less/bootstrap';
 @import '../../app/bower_components/font-awesome/less/font-awesome';
 
@@ -29,6 +28,10 @@ a {
   cursor: pointer;
 }
 
+.jobs-container {
+  margin-top: 20px;
+}
+
 
 .icon-remove-sign {
   color: #FF4B4B;
@@ -156,7 +159,6 @@ a {
     .apply-btn {
       font-size: 12px;
       padding: 0px 8px;
-      margin-left: 6px;
       margin-top: -8px;
       line-height: 22px;
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/templates/application.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/templates/application.hbs b/contrib/views/jobs/src/main/resources/ui/app/templates/application.hbs
index 1a0b52e..f71f976 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/templates/application.hbs
+++ b/contrib/views/jobs/src/main/resources/ui/app/templates/application.hbs
@@ -16,6 +16,6 @@
 * limitations under the License.
 }}
 
-<div class="container">
+<div class="container jobs-container">
   {{outlet}}
 </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/templates/job/job.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/templates/job/job.hbs b/contrib/views/jobs/src/main/resources/ui/app/templates/job/job.hbs
index aab95d7..46a6a35 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/templates/job/job.hbs
+++ b/contrib/views/jobs/src/main/resources/ui/app/templates/job/job.hbs
@@ -21,7 +21,7 @@
   {{#if controller.loaded}}
     <!-- Top Bar -->
     <div class="top-bar">
-      <a {{action "actionRouteToJobs" target="controller"}} href="#">{{t menu.item.jobs}}</a>
> {{view.content.name}}
+      {{#link-to "jobs"}}{{t menu.item.jobs}}{{/link-to}} > {{view.content.name}}
       <a {{action "toggleShowQuery" target="view"}} href="#" id="toggle-query">{{view.toggleShowQueryText}}</a>
 
       <div class="pull-right">{{t apps.item.dag.type}}: <span class="label label-info">{{view.content.jobType}}</span></div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/34c3355d/contrib/views/jobs/src/main/resources/ui/app/templates/jobs.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/templates/jobs.hbs b/contrib/views/jobs/src/main/resources/ui/app/templates/jobs.hbs
index 212c5d4..31aea32 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/templates/jobs.hbs
+++ b/contrib/views/jobs/src/main/resources/ui/app/templates/jobs.hbs
@@ -19,6 +19,7 @@
 
 <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>


Mime
View raw message