From 51b9a7bd0b63b0a50a643bb2f76cfbebdc62119d Mon Sep 17 00:00:00 2001 From: Chris Houseknecht Date: Fri, 18 Apr 2014 09:57:37 -0400 Subject: [PATCH] Fixed pagination issue. Moved reset of iterator + 'HidePaginator' var to Refresh.js helper exclusively. Latest changes to job details page. --- awx/ui/static/js/controllers/JobDetail.js | 7 +- awx/ui/static/js/helpers/JobDetail.js | 144 ++++++++++++------ awx/ui/static/js/helpers/PaginationHelpers.js | 6 +- awx/ui/static/js/helpers/refresh.js | 4 +- awx/ui/static/less/ansible-ui.less | 18 ++- awx/ui/static/less/job-details.less | 74 ++++++--- awx/ui/static/lib/ansible/list-generator.js | 3 +- awx/ui/static/partials/job_detail.html | 38 +++-- 8 files changed, 207 insertions(+), 87 deletions(-) diff --git a/awx/ui/static/js/controllers/JobDetail.js b/awx/ui/static/js/controllers/JobDetail.js index 132c3bd1ca..4bbaf8ede5 100644 --- a/awx/ui/static/js/controllers/JobDetail.js +++ b/awx/ui/static/js/controllers/JobDetail.js @@ -51,6 +51,9 @@ function JobDetailController ($scope, $compile, $routeParams, ClearScope, Breadc if (data.next) { $scope.$emit('JobReady', data.next); } + else { + Wait('stop'); + } }) .error(function(data, status) { ProcessErrors($scope, data, status, null, { hdr: 'Error!', @@ -91,7 +94,9 @@ function JobDetailController ($scope, $compile, $routeParams, ClearScope, Breadc }); } }); - + + Wait('start'); + // Load the job record Rest.setUrl(GetBasePath('jobs') + job_id + '/'); Rest.get() diff --git a/awx/ui/static/js/helpers/JobDetail.js b/awx/ui/static/js/helpers/JobDetail.js index 79d9aa524c..e300e407d4 100644 --- a/awx/ui/static/js/helpers/JobDetail.js +++ b/awx/ui/static/js/helpers/JobDetail.js @@ -47,6 +47,7 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla events = params.events; events.forEach(function(event) { + var hostCount; if (event.event === 'playbook_on_play_start') { scope.plays.push({ id: event.id, @@ -60,11 +61,18 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla }); } if (event.event === 'playbook_on_setup') { + hostCount = (scope.tasks.length > 0) ? scope.tasks[scope.tasks.length - 1].hostCount : 0; scope.tasks.push({ id: event.id, name: event.event_display, play_id: event.parent, - status: (event.failed) ? 'failed' : 'successful' + status: (event.failed) ? 'failed' : 'successful', + created: event.created, + hostCount: hostCount, + failedCount: 0, + changedCount: 0, + successfulCount: 0, + skippedCount: 0 }); UpdatePlayStatus({ scope: scope, @@ -72,21 +80,36 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla failed: event.failed, changed: event.changed }); + SelectTask({ + scope: scope, + id: event.id + }); } if (event.event === 'playbook_on_task_start') { + hostCount = (scope.tasks.length > 0) ? scope.tasks[scope.tasks.length - 1].hostCount : 0; scope.tasks.push({ id: event.id, name: event.task, play_id: event.parent, - status: (event.changed) ? 'changed' : (event.failed) ? 'failed' : 'successful' + status: ( (event.changed) ? 'changed' : (event.failed) ? 'failed' : 'successful' ), + role: event.role, + created: event.created, + hostCount: hostCount, + failedCount: 0, + changedCount: 0, + successfulCount: 0, + skippedCount: 0 }); + if (event.role) { + scope.hasRoles = true; + } UpdatePlayStatus({ scope: scope, play_id: event.parent, failed: event.failed, changed: event.changed }); - SelectTask({ + SelectTask({ scope: scope, id: event.id }); @@ -94,9 +117,38 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla /*if (event.event === 'playbook_on_no_hosts_matched') { UpdatePlayNoHostsMatched({ scope: scope, play_id: event.parent }); }*/ - if (event.event === 'runner_on_failed') { + + if (event.event === 'runner_on_unreachable') { + UpdateHostStatus({ + scope: scope, + name: event.event_data.host, + host_id: event.host, + task_id: event.parent, + status: 'unreachable', + event_id: event.id + }); } + if (event.event === 'runner_on_error') { + UpdateHostStatus({ + scope: scope, + name: event.event_data.host, + host_id: event.host, + task_id: event.parent, + status: 'failed', + event_id: event.id + }); + } + if (event.event === 'runner_on_skipped') { + UpdateHostStatus({ + scope: scope, + name: event.event_data.host, + host_id: event.host, + task_id: event.parent, + status: 'skipped', + event_id: event.id + }); + } if (event.event === 'runner_on_ok') { UpdateHostStatus({ scope: scope, @@ -104,11 +156,7 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla host_id: event.host, task_id: event.parent, status: (event.changed) ? 'changed' : 'ok', - results: (event.res && event.res.results) ? event.res.results : '' - }); - AddHostResult({ - scope: scope, - event: event + event_id: event.id }); } if (event.event === 'playbook_on_stats') { @@ -218,15 +266,17 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla }; }]) -// Update or add a new host -.factory('UpdateHostStatus', ['UpdateTaskStatus', function(UpdateTaskStatus) { +// Update host summary totals and update the task +.factory('UpdateHostStatus', ['UpdateTaskStatus', 'AddHostResult', function(UpdateTaskStatus, AddHostResult) { return function(params) { var scope = params.scope, status = params.status, // ok, changed, unreachable, failed name = params.name, + event_id = params.event_id, host_id = params.host_id, task_id = params.task_id, host_found = false; + scope.hosts.every(function(host, i) { if (host.id === host_id) { scope.hosts[i].ok += (status === 'ok' || status === 'changed') ? 1 : 0; @@ -238,6 +288,7 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla } return true; }); + if (!host_found) { scope.hosts.push({ id: host_id, @@ -248,54 +299,54 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla failed: (status === 'failed') ? 1 : 0 }); } + UpdateTaskStatus({ scope: scope, task_id: task_id, failed: (status === 'failed' || status === 'unreachable') ? true :false, changed: (status === 'changed') ? true : false }); + + AddHostResult({ + scope: scope, + task_id: task_id, + host_id: host_id, + event_id: event_id + }); }; }]) // Add a new host result -.factory('AddHostResult', ['Empty', function(Empty) { +.factory('AddHostResult', [ function() { return function(params) { var scope = params.scope, - event = params.event, - id, status, host_id, play_name, task_name, module_name, module_args, - results, rc; + task_id = params.task_id, + host_id = params.host_id, + event_id = params.event_id, + status = params.status; - id = event.id; status = (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful'; host_id = event.host; - play_name = event.play; - task_name = event.task; - if (event.event_data.res && event.event_data.res.invocation) { - module_name = event.event_data.res.invocation.module_name; - module_args = event.event_data.res.invocation.module_args; - } - else { - module_name = ''; - module_args = ''; - } - if (event.event_data.res && event.event_data.res.results) { - results = ''; - event.event_data.res.results.forEach(function(row) { - results += row; - }); - } - rc = (event.event_data.res && !Empty(event.event_data.res.rc)) ? event.event_data.res.rc : ''; + scope.hostResults.push({ - id: id, + id: event_id, status: status, host_id: host_id, - task_id: event.parent, - task_name: task_name, - host_name: event.event_data.host, - module_name: module_name, - module_args: module_args, - results: results, - rc: rc + task_id: event.parent + }); + + scope.tasks.forEach(function(task, idx) { + if (task.id === task_id) { + scope.tasks[idx].hostCount += (idx === 0) ? 1 : 0; // we only need to count hosts for the first task in a play + scope.tasks[idx].failedCount += (status === 'failed' || status === 'unreachable') ? 1 : 0; + scope.tasks[idx].changedCount += (status === 'changed') ? 1 : 0; + scope.tasks[idx].successfulCount += (status === 'successful' || status === 'changed') ? 1 : 0; + scope.tasks[idx].skippedCount += (status === 'skipped') ? 1 : 0; + scope.tasks[idx].failedPct = 100 * Math.round(scope.tasks[idx].failedCount / scope.tasks[idx].hostCount); + scope.tasks[idx].changedPct = 100 * (scope.tasks[idx].successfulCount) ? Math.round(scope.tasks[idx].changedCount / scope.tasks[idx].successfulCount) : 0; + scope.tasks[idx].skippedPct = 100 * Math.round(scope.tasks[idx].skippedCount / scope.tasks[idx].hostCount); + scope.tasks[idx].successfulPct = 100 * Math.round(scope.tasks[idx].successfulCount / scope.tasks[idx].hostCount); + } }); }; }]) @@ -327,10 +378,15 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla scope: scope, id: max_task_id, callback: function() { + // Scroll the task table all the way to the bottom, revealing the last row setTimeout(function() { - var inner_height = $('#job_tasks .job-detail-table').height(); - $('#job_tasks').scrollTop(inner_height); - }, 100); + var original_height = $('#task-table-body').css('height'), + table_height; + $('#task-table-body').css('height', 'auto'); + table_height = $('#task-table-body').height(); + $('#task-table-body').css('height', original_height); + $('#task-table-body').scrollTop(table_height); + }, 300); } }); }; diff --git a/awx/ui/static/js/helpers/PaginationHelpers.js b/awx/ui/static/js/helpers/PaginationHelpers.js index d6910f92c8..745cc2c670 100644 --- a/awx/ui/static/js/helpers/PaginationHelpers.js +++ b/awx/ui/static/js/helpers/PaginationHelpers.js @@ -49,7 +49,11 @@ angular.module('PaginationHelpers', ['Utilities', 'RefreshHelper', 'RefreshRelat for (i = first; i <= last; i++) { scope[iterator + '_page_range'].push(i); } - + console.log('first: ' + first); + console.log('last: ' + last); + console.log('range: '); + console.log(scope[iterator + '_page_range']); + console.log('num_pages: ' + scope[iterator + '_num_pages']); }; } ]) diff --git a/awx/ui/static/js/helpers/refresh.js b/awx/ui/static/js/helpers/refresh.js index c83b32e178..b1a284e305 100644 --- a/awx/ui/static/js/helpers/refresh.js +++ b/awx/ui/static/js/helpers/refresh.js @@ -26,6 +26,8 @@ angular.module('RefreshHelper', ['RestServices', 'Utilities', 'PaginationHelpers iterator = params.iterator, url = params.url; + scope[iterator + "HidePaginator"] = true; + //scope[iterator + 'Loading'] = true; scope.current_url = url; Rest.setUrl(url); @@ -45,7 +47,7 @@ angular.module('RefreshHelper', ['RestServices', 'Utilities', 'PaginationHelpers } scope[set] = data.results; scope[iterator + 'Loading'] = false; - scope[iterator + "HidePaginator"] = false; + scope[iterator + 'HidePaginator'] = false; Wait('stop'); scope.$emit('PostRefresh'); }) diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 9bfcae9a89..83de74e47b 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -996,28 +996,32 @@ input[type="checkbox"].checkbox-no-label { .icon-job-running:before, .icon-job-success:before, .icon-job-successful:before, - .icon-job-changed:before { + .icon-job-changed:before, + .icon-job-ok:before { content: "\f111"; } .icon-job-stopped:before, .icon-job-error:before, .icon-job-failed:before, - .icon-job-canceled:before { + .icon-job-canceled:before, + .icon-job-unreachable:before { content: "\f06a"; } .icon-job-pending:before, .icon-job-waiting:before, .icon-job-new:before, - .icon-job-none:before { + .icon-job-none:before, + .icon-job-skipped:before { content: "\f10c"; } .icon-job-active, .icon-job-running, .icon-job-success, - .icon-job-successful { + .icon-job-successful, + .icon-job-ok { color: @green; } @@ -1033,14 +1037,16 @@ input[type="checkbox"].checkbox-no-label { .icon-job-stopped, .icon-job-error, .icon-job-failed, - .icon-job-canceled { + .icon-job-canceled, + .icon-job-unreachable { color: @red; } .icon-job-none, .icon-job-pending, .icon-job-waiting, - .icon-job-new { + .icon-job-new, + .icon-job-skipped { color: @grey; opacity: 0.45; } diff --git a/awx/ui/static/less/job-details.less b/awx/ui/static/less/job-details.less index 295f5e8863..a5deb52591 100644 --- a/awx/ui/static/less/job-details.less +++ b/awx/ui/static/less/job-details.less @@ -7,35 +7,52 @@ * */ -.job-detail-tables { - .table { - margin-bottom: 0; +.job-detail-table { + margin-bottom: 0; + border: 1px solid @well; + + /** + The thing that makes the table body scrollable: + http://kamlekar.wordpress.com/2013/06/17/table-tbody-scroll-cross-browser/comment-page-1/ + **/ + thead { + display: table; + float: left; + width: 100%; } - .table>tbody>tr>td { + thead tr { + display: table-row; + width: 100%; + } + tbody { + display: block; + height: 122px; + width: 100%; + overflow-y: scroll; + float: left; + } + tbody tr { + display: table; + width: 100%; + } + tbody>tr>td { border-top-color: @well; - padding: 0; + padding-top: 0; + padding-bottom: 0; } - .table>thead>tr>th { - border-bottom-color: @well; - padding: 0; + thead>tr>th { + padding-top: 0; + padding-bottom: 0; + font-size: 14px; } - ul { - list-style-type: none; - margin: 0; - padding: 0; - } - li ul { - margin-left: 20px; - } - .status-column { - width: 25px; - font-size: 12px; + td.status-column { text-align: center; + font-size: 12px; i { margin-top: 4px; } } - .table>tbody>tr.active>td { + tbody>tr.active>td { background-color: @active-color; } } @@ -77,6 +94,23 @@ } } +.status-bar { + display: inline-block; + height: 15px; +} +.failed-hosts { + background-color: @red; +} +.successful-hosts { + background-color: @green; +} +.changed-hosts { + background-color: @warning; +} +.skipped-hosts { + background-color: @grey; +} + .job_well { padding: 8px; background-color: @well; diff --git a/awx/ui/static/lib/ansible/list-generator.js b/awx/ui/static/lib/ansible/list-generator.js index 3d3d59bcd5..439484d71e 100644 --- a/awx/ui/static/lib/ansible/list-generator.js +++ b/awx/ui/static/lib/ansible/list-generator.js @@ -82,8 +82,7 @@ angular.module('ListGenerator', ['GeneratorHelpers']) // Reset the scope to prevent displaying old data from our last visit to this list //this.scope[list.name] = null; this.scope[list.iterator] = []; - this.scope[list.iterator + "HidePaginator"] = true; - + // Remove any lingering tooltip and popover
elements $('.tooltip').each(function() { $(this).remove(); diff --git a/awx/ui/static/partials/job_detail.html b/awx/ui/static/partials/job_detail.html index ef6d56345c..102ef54703 100644 --- a/awx/ui/static/partials/job_detail.html +++ b/awx/ui/static/partials/job_detail.html @@ -77,21 +77,35 @@
-
Tasks for play: {{ activePlayName }}
-
- - - - - - - -
{{ task.name }}
-
+
Tasks
+ + + + + + + + + + + + + + + + +
StartedNameStatus +
{{ task.created | date: 'HH:mm:ss' }}{{ task.name }} +
+
+
+
+
+
-
Hosts in task: {{ activeTaskName }}
+
Hosts