From 5ccdcb3b1590476bd0ae97e2c42c48b42056716f Mon Sep 17 00:00:00 2001 From: chouseknecht Date: Fri, 11 Oct 2013 21:41:20 +0000 Subject: [PATCH] AC-503 Latest dashboard progress. --- awx/ui/static/js/app.js | 2 + awx/ui/static/js/controllers/Home.js | 11 +- .../static/js/widgets/InventorySyncStatus.js | 141 ++++++++++++++++ awx/ui/static/js/widgets/JobStatus.js | 63 ++++++-- awx/ui/static/js/widgets/SCMSyncStatus.js | 152 ++++++++++++++++++ awx/ui/static/lib/ansible/Utilities.js | 23 ++- awx/ui/static/partials/home.html | 4 +- awx/ui/templates/ui/index.html | 3 +- 8 files changed, 374 insertions(+), 25 deletions(-) create mode 100644 awx/ui/static/js/widgets/InventorySyncStatus.js create mode 100644 awx/ui/static/js/widgets/SCMSyncStatus.js diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index 00e53a38c4..3e8e77b43e 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -71,6 +71,8 @@ angular.module('ansible', [ 'License', 'HostGroupsFormDefinition', 'JobStatusWidget', + 'InventorySyncStatusWidget', + 'SCMSyncStatusWidget', 'JobsHelper', 'InventoryStatusDefinition' ]) diff --git a/awx/ui/static/js/controllers/Home.js b/awx/ui/static/js/controllers/Home.js index 3e897ec0b9..6aa4e6dd75 100644 --- a/awx/ui/static/js/controllers/Home.js +++ b/awx/ui/static/js/controllers/Home.js @@ -10,20 +10,22 @@ 'use strict'; -function Home ($routeParams, $scope, $rootScope, $location, Wait, JobStatus, ClearScope) +function Home ($routeParams, $scope, $rootScope, $location, Wait, JobStatus, InventorySyncStatus, SCMSyncStatus, ClearScope) { ClearScope('home'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. - var waitCount = 1; + var waitCount = 3; var loadedCount = 0; if (!$routeParams['login']) { // If we're not logging in, start the Wait widget. Otherwise, it's already running. Wait('start'); } - + JobStatus({ target: 'container1' }); + InventorySyncStatus({ target: 'container2' }); + SCMSyncStatus({ target: 'container3' }); $rootScope.$on('WidgetLoaded', function() { // Once all the widgets report back 'loaded', turn off Wait widget @@ -34,4 +36,5 @@ function Home ($routeParams, $scope, $rootScope, $location, Wait, JobStatus, Cle }); } -Home.$inject=[ '$routeParams', '$scope', '$rootScope', '$location', 'Wait', 'JobStatus', 'ClearScope']; \ No newline at end of file +Home.$inject=[ '$routeParams', '$scope', '$rootScope', '$location', 'Wait', 'JobStatus', 'InventorySyncStatus', + 'SCMSyncStatus', 'ClearScope']; \ No newline at end of file diff --git a/awx/ui/static/js/widgets/InventorySyncStatus.js b/awx/ui/static/js/widgets/InventorySyncStatus.js new file mode 100644 index 0000000000..9c9643a624 --- /dev/null +++ b/awx/ui/static/js/widgets/InventorySyncStatus.js @@ -0,0 +1,141 @@ +/********************************************* + * Copyright (c) 2013 AnsibleWorks, Inc. + * + * InventorySyncStatus.js + * + * Dashboard widget showing object counts and license availability. + * + */ + +angular.module('InventorySyncStatusWidget', ['RestServices', 'Utilities']) + .factory('InventorySyncStatus', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', + function($rootScope, $compile, Rest, GetBasePath, ProcessErrors, Wait) { + return function(params) { + + var scope = $rootScope.$new(); + var inventoryCount, inventoryFails, groupCount, groupFails, hostCount; + var hostFails = 0; + var counts = 0; + var expectedCounts = 4; + var target = params.target; + + if (scope.removeCountReceived) { + scope.removeCountReceived(); + } + scope.removeCountReceived = scope.$on('CountReceived', function() { + + var rowcount = 0; + + function makeRow(label, count, fail) { + var html = "\n"; + html += "" + label + "\n"; + html += ""; + html += (fail > 0) ? "" + fail + "" : ""; + html += "\n"; + html += ""; + html += (count > 0) ? "" + count + "" : ""; + html += "\n"; + return html; + } + + counts++; + if (counts == expectedCounts) { + // all the counts came back, now generate the HTML + var html = "
\n"; + html += "
Inventory Sync Status
\n"; + html += "
\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + + console.log('inventory count:' + inventoryCount); + console.log('group count:' + groupCount); + console.log('host count:' + hostCount); + + if (inventoryCount > 0) { + html += makeRow('Inventories', inventoryCount, inventoryFails); + rowcount++; + } + if (groupCount > 0) { + html += makeRow('Groups', groupCount, groupFails); + rowcount++; + } + if (hostCount > 0) { + html += makeRow('Hosts', hostCount, hostFails); + rowcount++; + } + + if (rowcount == 0) { + html += "\n"; + } + + html += "\n"; + html += "
FailedTotal
No inventories configured for external sync
\n"; + html += "
\n"; + html += "
\n"; + html += "\n"; + var element = angular.element(document.getElementById(target)); + element.html(html); + $compile(element)(scope); + $rootScope.$emit('WidgetLoaded'); + } + }); + + var url = GetBasePath('inventory') + '?has_inventory_sources=true&page=1'; + Rest.setUrl(url); + Rest.get() + .success( function(data, status, headers, config) { + inventoryCount=data.count; + scope.$emit('CountReceived'); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Failed to get ' + url + '. GET status: ' + status }); + }); + + inventoryFails = 0; + + url = GetBasePath('groups') + '?has_inventory_sources=true&page=1'; + Rest.setUrl(url); + Rest.get() + .success( function(data, status, headers, config) { + groupCount=data.count; + scope.$emit('CountReceived'); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Failed to get ' + url + '. GET status: ' + status }); + }); + + url = GetBasePath('groups') + '?has_inventory_sources=true&inventory_source__status=failed&page=1'; + Rest.setUrl(url); + Rest.get() + .success( function(data, status, headers, config) { + groupFails=data.count; + scope.$emit('CountReceived'); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Failed to get ' + url + '. GET status: ' + status }); + }); + + url = GetBasePath('hosts') + '?has_inventory_sources=true&page=1'; + Rest.setUrl(url); + Rest.get() + .success( function(data, status, headers, config) { + hostCount=data.count; + scope.$emit('CountReceived'); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Failed to get ' + url + '. GET status: ' + status }); + }); + + } + }]); \ No newline at end of file diff --git a/awx/ui/static/js/widgets/JobStatus.js b/awx/ui/static/js/widgets/JobStatus.js index 2f4a4fad73..4fd87ce43e 100644 --- a/awx/ui/static/js/widgets/JobStatus.js +++ b/awx/ui/static/js/widgets/JobStatus.js @@ -18,13 +18,24 @@ angular.module('JobStatusWidget', ['RestServices', 'Utilities']) var expectedCounts = 8; var target = params.target; - scope.$on('CountReceived', function() { + if (scope.removeCountReceived) { + scope.removeCountReceived(); + } + scope.removeCountReceived = scope.$on('CountReceived', function() { + + var rowcount = 0; function makeRow(label, count, fail) { - return "" + label + - "" + - fail + "" + - count + ""; + var html = ''; + html += "\n"; + html += "" + label + "\n"; + html += ""; + html += (fail > 0) ? "" + fail + "" : ""; + html += "\n"; + html += "" + html += (count > 0) ? "" + count + "" : ""; + html += "\n"; + return html; } counts++; @@ -36,27 +47,45 @@ angular.module('JobStatusWidget', ['RestServices', 'Utilities']) html += "\n"; html += "\n"; html += "\n"; - html += "\n"; - html += "\n"; - html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; html += "\n"; html += "\n"; html += "\n"; - html += makeRow('Jobs', jobCount, jobFails); - html += makeRow('Inventories', inventoryCount, inventoryFails); - html += makeRow('Groups', groupCount, groupFails); - html += makeRow('Hosts', hostCount, hostFails); + + if (jobCount > 0) { + html += makeRow('Jobs', jobCount, jobFails); + rowcount++; + } + if (inventoryCount > 0) { + html += makeRow('Inventories', inventoryCount, inventoryFails); + rowcount++; + } + if (groupCount > 0) { + html += makeRow('Groups', groupCount, groupFails); + rowcount++; + } + if (hostCount > 0) { + html += makeRow('Hosts', hostCount, hostFails); + rowcount++; + } + + if (rowcount == 0) { + html += "\n"; + } + html += "\n"; html += "
FailedTotalFailedTotal
No job data found
\n"; html += "\n"; html += "\n"; html += "\n"; - } - var element = angular.element(document.getElementById(target)); - element.html(html); - $compile(element)(scope); - $rootScope.$emit('WidgetLoaded'); + var element = angular.element(document.getElementById(target)); + element.html(html); + $compile(element)(scope); + $rootScope.$emit('WidgetLoaded'); + } }); var url = GetBasePath('jobs') + '?page=1'; diff --git a/awx/ui/static/js/widgets/SCMSyncStatus.js b/awx/ui/static/js/widgets/SCMSyncStatus.js new file mode 100644 index 0000000000..d814f4e41a --- /dev/null +++ b/awx/ui/static/js/widgets/SCMSyncStatus.js @@ -0,0 +1,152 @@ +/********************************************* + * Copyright (c) 2013 AnsibleWorks, Inc. + * + * SCMSyncStatus.js + * + * Dashboard widget showing object counts and license availability. + * + */ + +angular.module('SCMSyncStatusWidget', ['RestServices', 'Utilities']) + .factory('SCMSyncStatus', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'GetChoices', + function($rootScope, $compile, Rest, GetBasePath, ProcessErrors, Wait, GetChoices) { + return function(params) { + + var scope = $rootScope.$new(); + var target = params.target; + var expected = 0; + var results = []; + var scm_choices; + + if (scope.removeCountReceived) { + scope.removeCountReceived(); + } + scope.removeCountReceived = scope.$on('CountReceived', function(e, label, count, fail) { + + var rowcount = 0; + + function makeRow(label, count, fail) { + var value; + for (var i=0; i < scm_choices.length; i++) { + if (scm_choices[i][1] == label) { + value = scm_choices[i][0]; + break; + } + } + var html = '' + html += "" + label + "\n"; + html += ""; + html += (fail > 0) ? "" + fail + "" : ""; + html += "\n"; + html += ""; + html += (count > 0) ? "" + count + "" : ""; + html += "\n"; + html += "\n"; + return html; + } + + results.push({ label: label, count: count, fail: fail }); + + if (results.length == expected) { + // all the counts came back, now generate the HTML + + var html = "
\n"; + html += "
Project SCM Status
\n"; + html += "
\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + + for (var i=0; i < results.length; i++) { + if (results[i].count > 0) { + html += makeRow(results[i].label, results[i].count, results[i].fail); + rowcount++; + } + } + if (rowcount == 0) { + html += "\n"; + } + + html += "\n"; + html += "
FailedTotal
No projects conigured for SCM sync
\n"; + html += "
\n"; + html += "
\n"; + html += "\n"; + + var element = angular.element(document.getElementById(target)); + element.html(html); + $compile(element)(scope); + $rootScope.$emit('WidgetLoaded'); + } + }); + + + scope.removeCountProjects = scope.$on('CountProjects', function(e, choices) { + + scm_choices = choices; + + function getScmLabel(config) { + var url = config.url; + var scm_type = url.match(/scm_type=.*\&/)[0].replace(/scm_type=/,'').replace(/\&/,''); + var label; + for (var i=0; i < choices.length; i++) { + if (choices[i][0] == scm_type) { + label = choices[i][1]; + break; + } + } + return label; + } + + // Remove ---- option from list of choices + for (var i=0; i < choices.length; i++) { + if (choices[i][1].match(/^---/)) { + choices.splice(i,1); + break; + } + } + // Remove Manual option from list of choices + for (var i=0; i < choices.length; i++) { + if (choices[i][1].match(/Manual/)) { + choices.splice(i,1); + break; + } + } + + expected = choices.length; + + for (var i=0; i < choices.length; i++) { + if (!choices[i][1].match(/^---/)) { + var url = GetBasePath('projects') + '?scm_type=' + choices[i][0] + '&page=1'; + Rest.setUrl(url); + Rest.get() + .success( function(data, status, headers, config) { + // figure out the scm_type we're looking at and its label + var label = getScmLabel(config); + var count = data.count; + var fail = 0; + for (var i=0; i < data.results.length; i++) { + if (data.results[i].status == 'failed') { + fail++; + } + } + scope.$emit('CountReceived', label, count, fail); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Failed to get ' + url + '. GET status: ' + status }); + }); + } + } + }); + + GetChoices({ scope: scope, url: GetBasePath('projects'), field: 'scm_type', emit: 'CountProjects'}); + + } + }]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/Utilities.js b/awx/ui/static/lib/ansible/Utilities.js index 6b55938c33..8240d3bef2 100644 --- a/awx/ui/static/lib/ansible/Utilities.js +++ b/awx/ui/static/lib/ansible/Utilities.js @@ -5,7 +5,7 @@ * Utility functions * */ -angular.module('Utilities',[]) +angular.module('Utilities',['RestServices']) .factory('ClearScope', function() { return function(id) { @@ -314,6 +314,27 @@ angular.module('Utilities',[]) } }]) + .factory('GetChoices', [ 'Rest', 'ProcessErrors', function(Rest, ProcessErrors) { + return function(params) { + // Get dropdown options + + var scope = params.scope; + var url = params.url; + var field = params.field; + var emit_callback = params.emit; + + Rest.setUrl(url); + Rest.options() + .success( function(data, status, headers, config) { + scope.$emit(emit_callback, data.actions['GET'][field].choices); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Failed to get ' + url + '. GET status: ' + status }); + }); + } + }]) + /* DeugForm(form_name) * * Use to log the $pristine and $invalid properties of each form element. Helpful when form diff --git a/awx/ui/static/partials/home.html b/awx/ui/static/partials/home.html index 62dda61343..5173abcfdf 100644 --- a/awx/ui/static/partials/home.html +++ b/awx/ui/static/partials/home.html @@ -1,10 +1,10 @@
-
+
-
+
\ No newline at end of file diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index 69641a71da..43eddb68eb 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -109,7 +109,8 @@ - + + {% endif %}