diff --git a/.gitignore b/.gitignore index 5994375544..0f07b6eaa4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ awx/projects awx/job_output awx/public/media awx/public/static +awx/ui/tests/test-results.xml awx/ui/static/js/awx.min.js awx/ui/static/js/local_config.js awx/ui/static/css/awx.min.css @@ -26,7 +27,7 @@ tar-build *.py[c,o] # JavaScript -/GruntFile.js +/Gruntfile.js /bower.json /package.json node_modules/** diff --git a/.jshintrc b/.jshintrc index 81365330d8..8562d25f2e 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,21 +1,31 @@ { - // Details: https://github.com/victorporof/Sublime-JSHint#using-your-own-jshintrc-options - // Example: https://github.com/jshint/jshint/blob/master/examples/.jshintrc - // Documentation: http://www.jshint.com/docs/ - "browser": true, "jquery": true, "esnext": true, "globalstrict": true, - "globals": { "angular":false, "alert":false, "$AnsibleConfig":true, "$basePath":true, "jsyaml":false, "_":false, "d3":false, "Donut3D":false, "nv":false }, + "curly": true, + "immed": true, + "latedef": "nofunc", + "noarg": true, + "nonew": true, + "notypeof": true, + "globals": { + "angular":false, + "alert":false, + "$AnsibleConfig":true, + "$basePath":true, + "jsyaml":false, + "_":false, + "d3":false, + "Donut3D":false, + "nv":false + }, "strict": false, "quotmark": false, - "smarttabs": true, "trailing": true, "undef": true, "unused": true, "eqeqeq": true, "indent": 4, - "onevar": true, "newcap": false } diff --git a/Makefile b/Makefile index 0bc3af431d..d1f2f03dd3 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,7 @@ MOCK_CFG ?= .PHONY: clean rebase push requirements requirements_pypi requirements_jenkins \ develop refresh adduser syncdb migrate dbchange dbshell runserver celeryd \ - receiver test test_coverage coverage_html test_ui test_jenkins dev_build \ + receiver test test_coverage coverage_html ui_analysis_report test_ui test_jenkins dev_build \ release_build release_clean sdist rpmtar mock-rpm mock-srpm \ deb deb-src debian reprepro setup_tarball @@ -240,9 +240,12 @@ test_coverage: coverage_html: coverage html -# Run UI unit tests using Selenium. -test_ui: - $(PYTHON) manage.py test -v2 awx.ui.tests +ui_analysis_report: node_modules + $(GRUNT) plato:report + +# Run UI unit tests +test_ui: node_modules + $(GRUNT) karma:ci # Run API unit tests across multiple Python/Django versions with Tox. test_tox: diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index d551d6af11..a777d252c3 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -18,11 +18,14 @@ if ($basePath) { urlPrefix = $basePath; } + angular.module('Tower', [ 'ngRoute', 'ngSanitize', 'ngCookies', 'RestServices', + 'DataServices', + 'DashboardGraphs', 'AuthService', 'Utilities', 'LicenseHelper', @@ -84,9 +87,6 @@ angular.module('Tower', [ 'SelectionHelper', 'HostGroupsFormDefinition', 'DashboardCountsWidget', - 'JobStatusGraphWidget', - 'HostPieChartWidget', - 'HostGraphWidget', 'DashboardJobsWidget', 'PortalJobsWidget', 'StreamWidget', @@ -131,7 +131,6 @@ angular.module('Tower', [ .constant('AngularScheduler.useTimezone', true) .constant('AngularScheduler.showUTCField', true) .constant('$timezones.definitions.location', urlPrefix + 'lib/angular-tz-extensions/tz/data') - .config(['$routeProvider', function ($routeProvider) { @@ -399,7 +398,15 @@ angular.module('Tower', [ when('/home', { templateUrl: urlPrefix + 'partials/home.html', - controller: 'Home' + controller: 'Home', + resolve: { + graphData: function($q, jobStatusGraphData, hostCountGraphData) { + return $q.all({ + jobStatus: jobStatusGraphData.get("month", "all"), + hostCounts: hostCountGraphData.get() + }); + } + } }). when('/home/groups', { diff --git a/awx/ui/static/js/config.js b/awx/ui/static/js/config.js index abed710242..3531acafc0 100644 --- a/awx/ui/static/js/config.js +++ b/awx/ui/static/js/config.js @@ -28,7 +28,7 @@ // > password_strength = green // It also controls password validation. Passwords are rejected if the score is not > password_strength. - session_timeout: 1800, // Number of seconds before an inactive session is automatically timed out and forced to log in again. + session_timeout: 1800, // Number of seconds before an inactive session is automatically timed out and forced to log in again. // Separate from time out value set in API. diff --git a/awx/ui/static/js/controllers/Home.js b/awx/ui/static/js/controllers/Home.js index 820ef907c9..8ecb0fcb81 100644 --- a/awx/ui/static/js/controllers/Home.js +++ b/awx/ui/static/js/controllers/Home.js @@ -25,12 +25,12 @@ * Host count graph should only be loaded if the user is a super user * */ -function Home($scope, $compile, $routeParams, $rootScope, $location, $log, Wait, DashboardCounts, HostGraph, JobStatusGraph, HostPieChart, DashboardJobs, - ClearScope, Stream, Rest, GetBasePath, ProcessErrors, Button){ +function Home($scope, $compile, $routeParams, $rootScope, $location, $log, Wait, DashboardCounts, DashboardJobs, + ClearScope, Stream, Rest, GetBasePath, ProcessErrors, Button, $window, graphData){ ClearScope('home'); - var buttons, html, e, waitCount, loadedCount,borderStyles, jobs_scope, schedule_scope; + var buttons, html, e, borderStyles; // Add buttons to the top of the Home page. We're using lib/ansible/generator_helpers.js-> Buttons() // to build buttons dynamically and insure all styling and icons match the rest of the application. @@ -64,39 +64,16 @@ function Home($scope, $compile, $routeParams, $rootScope, $location, $log, Wait, e.html(html); $compile(e)($scope); - waitCount = 4; - loadedCount = 0; - if (!$routeParams.login) { // If we're not logging in, start the Wait widget. Otherwise, it's already running. //Wait('start'); } - if ($scope.removeWidgetLoaded) { - $scope.removeWidgetLoaded(); - } - $scope.removeWidgetLoaded = $scope.$on('WidgetLoaded', function (e, label, jobscope, schedulescope) { - // Once all the widgets report back 'loaded', turn off Wait widget - if(label==="dashboard_jobs"){ - jobs_scope = jobscope; - schedule_scope = schedulescope; - } - loadedCount++; - if (loadedCount === waitCount) { - $(window).resize(_.debounce(function() { - $scope.$emit('ResizeJobGraph'); - $scope.$emit('ResizeHostGraph'); - $scope.$emit('ResizeHostPieGraph'); - Wait('stop'); - }, 500)); - $(window).resize(); - } - }); - if ($scope.removeDashboardReady) { $scope.removeDashboardReady(); } $scope.removeDashboardReady = $scope.$on('dashboardReady', function (e, data) { + nv.dev=false; @@ -106,64 +83,24 @@ function Home($scope, $compile, $routeParams, $rootScope, $location, $log, Wait, "margin-bottom": "15px"}; $('.graph-container').css(borderStyles); - var winHeight = $(window).height(), - available_height = winHeight - $('#main-menu-container .navbar').outerHeight() - $('#count-container').outerHeight() - 120; - $('.graph-container').height(available_height/2); - // // chart.update(); - DashboardCounts({ scope: $scope, target: 'dash-counts', dashboard: data }); - JobStatusGraph({ - scope: $scope, - target: 'dash-job-status-graph', - dashboard: data - }); + // // chart.update(); + + $scope.graphData = graphData; - if ($rootScope.user_is_superuser === true) { - waitCount = 5; - HostGraph({ - scope: $scope, - target: 'dash-host-count-graph', - dashboard: data - }); - } - else{ - $('#dash-host-count-graph').remove(); //replaceWith("
"); - } DashboardJobs({ scope: $scope, target: 'dash-jobs-list', dashboard: data }); - HostPieChart({ - scope: $scope, - target: 'dash-host-status-graph', - dashboard: data - }); }); - if ($rootScope.removeJobStatusChange) { - $rootScope.removeJobStatusChange(); - } - $rootScope.removeJobStatusChange = $rootScope.$on('JobStatusChange', function() { - jobs_scope.refreshJobs(); - $scope.$emit('ReloadJobStatusGraph'); - - }); - - if ($rootScope.removeScheduleChange) { - $rootScope.removeScheduleChange(); - } - $rootScope.removeScheduleChange = $rootScope.$on('ScheduleChange', function() { - schedule_scope.refreshSchedules(); - $scope.$emit('ReloadJobStatusGraph'); - }); - $scope.showActivity = function () { Stream({ scope: $scope @@ -172,23 +109,23 @@ function Home($scope, $compile, $routeParams, $rootScope, $location, $log, Wait, $scope.refresh = function () { Wait('start'); - loadedCount = 0; Rest.setUrl(GetBasePath('dashboard')); Rest.get() - .success(function (data) { - $scope.$emit('dashboardReady', data); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard: ' + status }); - }); + .success(function (data) { + $scope.dashboardData = data; + $scope.$emit('dashboardReady', data); + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard: ' + status }); + }); }; $scope.refresh(); } -Home.$inject = ['$scope', '$compile', '$routeParams', '$rootScope', '$location', '$log','Wait', 'DashboardCounts', 'HostGraph','JobStatusGraph', 'HostPieChart', 'DashboardJobs', - 'ClearScope', 'Stream', 'Rest', 'GetBasePath', 'ProcessErrors', 'Button' +Home.$inject = ['$scope', '$compile', '$routeParams', '$rootScope', '$location', '$log','Wait', 'DashboardCounts', 'DashboardJobs', + 'ClearScope', 'Stream', 'Rest', 'GetBasePath', 'ProcessErrors', 'Button', '$window', 'graphData' ]; @@ -757,4 +694,4 @@ function HomeHosts($scope, $location, $routeParams, HomeHostList, GenerateList, HomeHosts.$inject = ['$scope', '$location', '$routeParams', 'HomeHostList', 'GenerateList', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GetBasePath', 'SearchInit', 'PaginateInit', 'FormatDate', 'SetStatus', 'ToggleHostEnabled', 'HostsEdit', 'Stream', 'Find', 'ShowJobSummary', 'ViewJob' -]; \ No newline at end of file +]; diff --git a/awx/ui/static/js/controllers/JobTemplates.js b/awx/ui/static/js/controllers/JobTemplates.js index 7719229cd9..2238c81698 100644 --- a/awx/ui/static/js/controllers/JobTemplates.js +++ b/awx/ui/static/js/controllers/JobTemplates.js @@ -527,7 +527,9 @@ function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routePa if($scope.survey_enabled === true && $scope.survey_exists!==true){ $scope.$emit("PromptForSurvey"); - } else $scope.$emit("GatherFormFields"); + } else { + $scope.$emit("GatherFormFields"); + } }; @@ -634,7 +636,9 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP ' project or make the playbooks available on the file system.', 'alert-info'); }); } - else Wait('stop'); + else { + Wait('stop'); + } }; // Detect and alert user to potential SCM status issues @@ -964,7 +968,9 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP if($scope.survey_enabled === true && $scope.survey_exists!==true){ $scope.$emit("PromptForSurvey"); - } else $scope.$emit("GatherFormFields"); + } else { + $scope.$emit("GatherFormFields"); + } }; @@ -1021,11 +1027,12 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP if($scope.survey_enabled === true && $scope.survey_exists!==true){ $scope.$emit("PromptForSurvey"); } - else + else { PlaybookRun({ scope: $scope, id: id }); + } }; // handler for 'Enable Survey' button @@ -1063,4 +1070,4 @@ JobTemplatesEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$l 'GetBasePath', 'md5Setup', 'ParseTypeChange', 'JobStatusToolTip', 'FormatDate', 'Wait', 'Stream', 'Empty', 'Prompt', 'ParseVariableString', 'ToJSON', 'SchedulesControllerInit', 'JobsControllerInit', 'JobsListUpdate', 'GetChoices', 'SchedulesListInit', 'SchedulesList', 'CallbackHelpInit', 'PlaybookRun' , 'SurveyControllerInit', '$sce' -]; \ No newline at end of file +]; diff --git a/awx/ui/static/js/directives/auto-size-module.js b/awx/ui/static/js/directives/auto-size-module.js new file mode 100644 index 0000000000..2df92c53fd --- /dev/null +++ b/awx/ui/static/js/directives/auto-size-module.js @@ -0,0 +1,53 @@ +angular.module('DashboardGraphs') +.directive('autoSizeModule', ['$window', '$timeout', function($window, $timeout) { + + // Adjusts the size of the module so that all modules + // fit into a single a page; assumes there are 2 rows + // of modules, with the available height being offset + // by the navbar & the count summaries module + return function(scope, element) { + + // We need to trigger a resize on the first call + // to this when the view things load; but we don't want + // to trigger a global window resize for everything that + // has an auto resize, since they'll all pick it up with + // a single call + var triggerResize = + _.throttle(function() { + $($window).resize(); + }, 1000); + + function adjustSizeInitially() { + adjustSize(); + triggerResize(); + } + + function adjustSize() { + var winHeight = $($window).height(), + available_height = winHeight - $('#main-menu-container .navbar').outerHeight() - $('#count-container').outerHeight() - 120; + element.height(available_height/2); + } + + $($window).resize(adjustSize); + + element.on('$destroy', function() { + $($window).off('resize', adjustSize); + }); + + // Wait a second or until dashboardReady triggers, + // whichever comes first. The timeout handles cases + // where dashboardReady never fires. + + var dashboardReadyTimeout = $timeout(adjustSizeInitially, 500); + + // This makes sure count-container div is loaded + // by controllers/Home.js before we use it + // to determine the available window height + scope.$on('dashboardReady', function() { + $timeout.cancel(dashboardReadyTimeout); + adjustSizeInitially(); + }); + + }; + +}]); diff --git a/awx/ui/static/js/directives/dashboard-graphs.js b/awx/ui/static/js/directives/dashboard-graphs.js new file mode 100644 index 0000000000..ed86866e80 --- /dev/null +++ b/awx/ui/static/js/directives/dashboard-graphs.js @@ -0,0 +1 @@ +angular.module('DashboardGraphs', []); diff --git a/awx/ui/static/js/directives/host-count-graph.js b/awx/ui/static/js/directives/host-count-graph.js new file mode 100644 index 0000000000..bffb5f8c76 --- /dev/null +++ b/awx/ui/static/js/directives/host-count-graph.js @@ -0,0 +1,126 @@ +angular.module('DashboardGraphs'). + directive('hostCountGraph', ['GetBasePath', 'Rest', 'adjustGraphSize', '$window', function(getBasePath, Rest, adjustGraphSize, $window) { + + return { + restrict: 'E', + templateUrl: '/static/partials/host_count_graph.html', + link: link + }; + + function link(scope, element, attr) { + var license_graph; + + scope.$watch(attr.data, function(data) { + + if(!data) { + return; + } + + createGraph(data.hosts, data.license); + }); + + function onResize() { + + if(!license_graph) { + return; + } + + adjustGraphSize(license_graph, element); + } + + angular.element($window).on('resize', function() { + + if(!license_graph) { + return; + } + + adjustGraphSize(license_graph, element); + }); + + element.on('$destroy', function() { + angular.element($window).off('resize', onResize); + }); + + + + function createGraph(data, license) { + //url = getBasePath('dashboard')+'graphs/'; + var graphData = [ + { "key" : "Hosts" , + "color" : "#1778c3", + "values": data.hosts + }, + { "key" : "License" , + "color" : "#171717", + "values": data.hosts + } + ]; + + graphData.map(function(series) { + if(series.key==="Hosts"){ + series.values = series.values.map(function(d) { + return { + x: d[0], + y: d[1] + }; + }); + } + if(series.key==="License"){ + series.values = series.values.map(function(d) { + return { + x: d[0], + y: license + }; + }); + + } + return series; + + }); + + var width = $('.graph-container').width(), // nv.utils.windowSize().width/3, + height = $('.graph-container').height()*0.6; //nv.utils.windowSize().height/5, + license_graph = nv.models.lineChart() + .margin({top: 15, right: 75, bottom: 40, left: 85}) + .x(function(d,i) { return i ;}) + .useInteractiveGuideline(true) //We want nice looking tooltips and a guideline! + .transitionDuration(350) //how fast do you want the lines to transition? + .showLegend(true) //Show the legend, allowing users to turn on/off line series. + .showYAxis(true) //Show the y-axis + .showXAxis(true) //Show the x-axis + ; + + license_graph.xAxis + .axisLabel("Time") + .tickFormat(function(d) { + var dx = graphData[0].values[d] && graphData[0].values[d].x || 0; + return dx ? d3.time.format('%m/%d')(new Date(Number(dx+'000'))) : ''; + }); + + license_graph.yAxis //Chart y-axis settings + .axisLabel('Hosts') + .tickFormat(d3.format('.f')); + + d3.select(element.find('svg')[0]) + .datum(graphData).transition() + .attr('width', width) + .attr('height', height) + .duration(500) + .call(license_graph) + .style({ + "font-family": 'Open Sans', + "font-style": "normal", + "font-weight":400, + "src": "url(/static/fonts/OpenSans-Regular.ttf)" + }); + + + scope.$emit('WidgetLoaded'); + + adjustGraphSize(license_graph, element); + + return license_graph; + + } + } +}]); diff --git a/awx/ui/static/js/directives/host-status-graph.js b/awx/ui/static/js/directives/host-status-graph.js new file mode 100644 index 0000000000..11b0dbf227 --- /dev/null +++ b/awx/ui/static/js/directives/host-status-graph.js @@ -0,0 +1,102 @@ +angular.module('DashboardGraphs') + .directive('hostStatusGraph', ['$compile', '$window', + function ($compile, $window) { + return { + restrict: 'E', + link: link, + templateUrl: '/static/partials/host_status_graph.html' + }; + + function link(scope, element, attr) { + var host_pie_chart; + + scope.$watch(attr.data, function(data) { + if (data && data.hosts) { + createGraph(data); + } + }); + + function adjustGraphSize() { + + if (angular.isUndefined(host_pie_chart)) { + return; + } + + var parentHeight = element.parent().parent().height(); + var toolbarHeight = element.find('.toolbar').height(); + var container = element.find('svg').parent(); + var margins = host_pie_chart.margin(); + + var newHeight = parentHeight - toolbarHeight - margins.bottom; + + $(container).height(newHeight); + + host_pie_chart.update(); + } + + angular.element($window).on('resize', adjustGraphSize); + + element.on('$destroy', function() { + angular.element($window).off('resize', adjustGraphSize); + }); + + function createGraph(data) { + if(data.hosts.total+data.hosts.failed>0){ + data = [ + { "label": "Successful", + "color": "#00aa00", + "value" : data.hosts.total + } , + { "label": "Failed", + "color" : "#aa0000", + "value" : data.hosts.failed + } + ]; + + host_pie_chart = nv.models.pieChart() + .margin({top: 5, right: 75, bottom: 25, left: 85}) + .x(function(d) { return d.label; }) + .y(function(d) { return d.value; }) + .showLabels(true) + .labelThreshold(0.01) + .tooltipContent(function(x, y) { + return ''+x+''+ '' + Math.floor(y.replace(',','')) + ' Hosts ' + '
'; + }) + .color(['#00aa00', '#aa0000']); + + host_pie_chart.pie.pieLabelsOutside(true).labelType("percent"); + + d3.select(element.find('svg')[0]) + .datum(data) + .transition().duration(350) + .call(host_pie_chart) + .style({ + "font-family": 'Open Sans', + "font-style": "normal", + "font-weight":400, + "src": "url(/static/fonts/OpenSans-Regular.ttf)" + }); + + adjustGraphSize(); + return host_pie_chart; + } + else{ + // This should go in a template or something + // but I'm at the end of a card and need to get this done. + // We definitely need to refactor this, I'm letting + // good enough be good enough for right now. + var notFoundContainer = $(''); + notFoundContainer.css({ + 'text-align': 'center', + 'width': '100%', + 'padding-top': '2em' + }); + + notFoundContainer.text('No host data'); + + element.find('svg').replaceWith(notFoundContainer); + } + + } + } + }]); diff --git a/awx/ui/static/js/directives/job-status-graph.js b/awx/ui/static/js/directives/job-status-graph.js new file mode 100644 index 0000000000..3cf6696bf2 --- /dev/null +++ b/awx/ui/static/js/directives/job-status-graph.js @@ -0,0 +1,120 @@ +angular.module('DashboardGraphs') + .directive('jobStatusGraph', ['$rootScope', '$compile', '$location' , '$window', 'Wait', 'adjustGraphSize', 'jobStatusGraphData', + function ($rootScope, $compile , $location, $window, Wait, adjustGraphSize) { + return { + restrict: 'E', + templateUrl: '/static/partials/job_status_graph.html', + link: link + }; + + function link(scope, element, attr) { + var job_type, job_status_chart = nv.models.lineChart(); + + scope.period="month"; + scope.jobType="all"; + + scope.$watch(attr.data, function(value) { + if (value) { + createGraph(value, scope.period, scope.jobType); + } + }); + + function createGraph(data, period, jobtype){ + + scope.period = period; + scope.jobType = jobtype; + + var timeFormat, graphData = [ + { "color": "#00aa00", + "key": "Successful", + "values": data.jobs.successful + }, + { "key" : "Failed" , + "color" : "#aa0000", + "values": data.jobs.failed + } + ]; + + if(period==="day") { + timeFormat="%H:%M"; + } + else { + timeFormat = '%m/%d'; + } + graphData.map(function(series) { + series.values = series.values.map(function(d) { + return { + x: d[0], + y: d[1] + }; + }); + return series; + }); + + job_status_chart + .margin({top: 5, right: 75, bottom: 40, left: 85}) //Adjust chart margins to give the x-axis some breathing room. + .x(function(d,i) { return i; }) + .useInteractiveGuideline(true) //We want nice looking tooltips and a guideline! + .showLegend(true) //Show the legend, allowing users to turn on/off line series. + .showYAxis(true) //Show the y-axis + .showXAxis(true); //Show the x-axis + + + job_status_chart.xAxis + .axisLabel("Time")//.showMaxMin(true) + .tickFormat(function(d) { + var dx = graphData[0].values[d] && graphData[0].values[d].x || 0; + return dx ? d3.time.format(timeFormat)(new Date(Number(dx+'000'))) : ''; + }); + + job_status_chart.yAxis //Chart y-axis settings + .axisLabel('Jobs') + .tickFormat(d3.format('.f')); + + d3.select(element.find('svg')[0]) + .datum(graphData) + .call(job_status_chart) + .style({ + "font-family": 'Open Sans', + "font-style": "normal", + "font-weight":400, + "src": "url(/static/fonts/OpenSans-Regular.ttf)" + }); + + // when the Period drop down filter is used, create a new graph based on the + d3.selectAll(element.find(".n")) + .on("click", function() { + period = this.getAttribute("id"); + $('#period-dropdown').replaceWith(""+this.text+"\n"); + + createGraph(data, period, job_type); + }); + + //On click, update with new data + d3.selectAll(element.find(".m")) + .on("click", function() { + job_type = this.getAttribute("id"); + $('#type-dropdown').replaceWith(""+this.text+"\n"); + + createGraph(data, period, job_type); + }); + + adjustGraphSize(job_status_chart, element); + } + + function onResize() { + adjustGraphSize(job_status_chart, element); + } + + angular.element($window).on('resize', onResize); + + element.on('$destroy', function() { + angular.element($window).off('resize', onResize); + }); + + if (scope.removeGraphDataReady) { + scope.removeGraphDataReady(); + } + + } + }]); diff --git a/awx/ui/static/js/helpers/Groups.js b/awx/ui/static/js/helpers/Groups.js index 6c240530b6..0b34c48d2f 100644 --- a/awx/ui/static/js/helpers/Groups.js +++ b/awx/ui/static/js/helpers/Groups.js @@ -1074,8 +1074,9 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' msg: 'Failed to retrieve inventory source. GET status: ' + status }); }); } - else + else { modal_scope.$emit('groupVariablesLoaded'); // JT-- "groupVariablesLoaded" is where the schedule info is loaded, so I make a call after the sources_scope.source has been loaded + } }); if (sources_scope.removeScopeSourceTypeOptionsReady) { diff --git a/awx/ui/static/js/helpers/JobDetail.js b/awx/ui/static/js/helpers/JobDetail.js index f71e9850fe..87f8aa38f4 100644 --- a/awx/ui/static/js/helpers/JobDetail.js +++ b/awx/ui/static/js/helpers/JobDetail.js @@ -1208,10 +1208,12 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge keys; function listSort(a,b) { - if (parseInt(a,10) < parseInt(b,10)) + if (parseInt(a,10) < parseInt(b,10)) { return -1; - if (parseInt(a,10) > parseInt(b,10)) + } + if (parseInt(a,10) > parseInt(b,10)) { return 1; + } return 0; } @@ -1276,10 +1278,12 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge idx, key, keys, newKeys, tasks, t; function listSort(a,b) { - if (parseInt(a,10) < parseInt(b,10)) + if (parseInt(a,10) < parseInt(b,10)) { return -1; - if (parseInt(a,10) > parseInt(b,10)) + } + if (parseInt(a,10) > parseInt(b,10)) { return 1; + } return 0; } @@ -1385,15 +1389,19 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge keys = Object.keys(filteredListB); keys.sort(function compare(a, b) { if (filteredListB[a].name === filteredListB[b].name) { - if (filteredListB[a].counter < filteredListB[b].counter) + if (filteredListB[a].counter < filteredListB[b].counter) { return -1; - if (filteredListB[a].counter >filteredListB[b].counter) + } + if (filteredListB[a].counter >filteredListB[b].counter) { return 1; + } } else { - if (filteredListB[a].name < filteredListB[b].name) + if (filteredListB[a].name < filteredListB[b].name) { return -1; - if (filteredListB[a].name > filteredListB[b].name) + } + if (filteredListB[a].name > filteredListB[b].name) { return 1; + } } // a must be equal to b return 0; @@ -1453,10 +1461,12 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge keys = Object.keys(filteredListB); keys.sort(function(a,b) { - if (filteredListB[a].name > filteredListB[b].name) + if (filteredListB[a].name > filteredListB[b].name) { return 1; - if (filteredListB[a].name < filteredListB[b].name) + } + if (filteredListB[a].name < filteredListB[b].name) { return -1; + } // a must be equal to b return 0; }); diff --git a/awx/ui/static/js/helpers/JobSubmission.js b/awx/ui/static/js/helpers/JobSubmission.js index e8e89ed40e..90bf9e62ca 100644 --- a/awx/ui/static/js/helpers/JobSubmission.js +++ b/awx/ui/static/js/helpers/JobSubmission.js @@ -111,7 +111,9 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential if(scope.prompt_for_vars===false && scope.survey_enabled===true){ scope.$emit('GetExtraVars'); } - else scope.$emit('BuildData'); + else { + scope.$emit('BuildData'); + } }; @@ -831,7 +833,9 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi else if (!Empty(scope.survey_enabled) && scope.survey_enabled===true) { scope.$emit('PromptForSurvey', html, url); } - else scope.$emit('StartPlaybookRun', url); + else { + scope.$emit('StartPlaybookRun', url); + } } }) @@ -1021,4 +1025,4 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi }); }; } -]); \ No newline at end of file +]); diff --git a/awx/ui/static/js/helpers/Jobs.js b/awx/ui/static/js/helpers/Jobs.js index e161167605..52d52f96b5 100644 --- a/awx/ui/static/js/helpers/Jobs.js +++ b/awx/ui/static/js/helpers/Jobs.js @@ -95,7 +95,9 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job if(scope.$parent.portalMode===true){ $window.open('/#/jobs/' + job.id, '_blank'); } - else $location.url('/jobs/' + job.id); + else { + $location.url('/jobs/' + job.id); + } } else { LogViewer({ diff --git a/awx/ui/static/js/helpers/Permissions.js b/awx/ui/static/js/helpers/Permissions.js index bc263549cb..de2a64156b 100644 --- a/awx/ui/static/js/helpers/Permissions.js +++ b/awx/ui/static/js/helpers/Permissions.js @@ -34,8 +34,8 @@ angular.module('PermissionsHelper', []) } else { scope.projectrequired = true; html = "' + Math.floor(y.replace(',','')) + ' Hosts ' + '
'; - }) - .color(['#00aa00', '#aa0000']); - - host_pie_chart.pie.pieLabelsOutside(true).labelType("percent"); - - d3.select(".host-pie-chart svg") - .datum(data) - .attr('width', width) - .attr('height', height) - .transition().duration(350) - .call(host_pie_chart) - .style({ - "font-family": 'Open Sans', - "font-style": "normal", - "font-weight":400, - "src": "url(/static/fonts/OpenSans-Regular.ttf)" - }); - // nv.utils.windowResize(host_pie_chart.update); - scope.$emit('WidgetLoaded'); - return host_pie_chart; - }); - } - else{ - winHeight = $(window).height(); - available_height = winHeight - $('#main-menu-container .navbar').outerHeight() - $('#count-container').outerHeight() - 120; - $('.graph-container:eq(1)').height(available_height/2); - $('.host-pie-chart svg').replaceWith(''); - - canvas = document.getElementById("circlecanvas"); - context = canvas.getContext("2d"); - context.arc(55, 55, 50, 0, Math.PI * 2, false); - context.lineWidth = 1; - context.strokeStyle = '#1778c3'; - context.stroke(); - context.font = "12px Open Sans"; - context.fillText("No Host data",18,55); - - scope.$emit('WidgetLoaded'); - } - }; - } - ]); diff --git a/awx/ui/static/js/widgets/JobStatusGraph.js b/awx/ui/static/js/widgets/JobStatusGraph.js deleted file mode 100644 index 6477ca7b6f..0000000000 --- a/awx/ui/static/js/widgets/JobStatusGraph.js +++ /dev/null @@ -1,219 +0,0 @@ -/********************************************* - * Copyright (c) 2014 AnsibleWorks, Inc. - */ - /** - * @ngdoc function - * @name widgets.function:JobStatusGraph - * @description - */ - - -'use strict'; - -angular.module('JobStatusGraphWidget', ['RestServices', 'Utilities']) - .factory('JobStatusGraph', ['$rootScope', '$compile', '$location' , 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', - function ($rootScope, $compile , $location, Rest, GetBasePath, ProcessErrors) { - return function (params) { - - var scope = params.scope, - target = params.target, - // dashboard = params.dashboard, - html, element, url, job_status_chart, - period="month", - job_type="all"; - - // html = "