From 783f784e691272f09f6f63f6e0b4550181d53390 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Sun, 6 Nov 2016 13:41:17 -0500 Subject: [PATCH] update to standard out pane - add lines to pane - support show and hide toggling of lines NOTE: layout is hardcoded, will need to move to dealing with various browser heights better --- .../src/job-results/event-queue.service.js | 6 + .../job-results-stdout.block.less | 149 +++++++++++++++--- .../job-results-stdout.directive.js | 70 +++++++- .../job-results-stdout.partial.html | 32 +++- .../src/job-results/job-results.block.less | 4 + .../src/job-results/job-results.controller.js | 7 +- .../src/job-results/job-results.route.js | 4 +- .../src/job-results/job-results.service.js | 3 +- .../src/job-results/parse-stdout.service.js | 60 ++++--- 9 files changed, 273 insertions(+), 62 deletions(-) diff --git a/awx/ui/client/src/job-results/event-queue.service.js b/awx/ui/client/src/job-results/event-queue.service.js index 929c5dc9a8..b8796c361e 100644 --- a/awx/ui/client/src/job-results/event-queue.service.js +++ b/awx/ui/client/src/job-results/event-queue.service.js @@ -175,6 +175,12 @@ export default ['jobResultsService', 'parseStdoutService', '$q', function(jobRes val.populateDefers[event.counter] = $q.defer(); } + if (val.queue[event.counter] && + val.queue[event.counter].processed) { + val.populateDefers.reject("duplicate event: " + + event); + } + if (!val.queue[event.counter]) { var resolvePopulation = function(event) { // to resolve, put the event on the queue and diff --git a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.block.less b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.block.less index a669d2b5a0..be005bb7b2 100644 --- a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.block.less +++ b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.block.less @@ -1,19 +1,128 @@ @import '../../shared/branding/colors.default.less'; -.JobResultsStdOut{ +.JobResultsStdOut { height: 100%; - margin-top: 15px; - background-color: @default-no-items-bord; - border-radius: 5px; - margin-bottom: 10px; } -.JobResultsStdOut-aLineOfStdOut, -.JobResultsStdOut-expandLine { +.JobResultsStdOut-toolbar { display: flex; + height: 38px; + margin-top: 15px; + border: 1px solid @default-list-header-bg; + border-bottom: 0px; + border-radius: 5px; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; } -.JobResultsStdOut-lineNumberColumn{ +.JobResultsStdOut-toolbarNumberColumn { + background-color: @default-list-header-bg; + color: @b7grey; + flex: initial; + display: flex; + justify-content: space-between; + width: 70px; + padding-bottom: 0px; + padding-left: 8px; + padding-right: 8px; + padding-top: 10px; + border-top-left-radius: 5px; +} + +.JobResultsStdOut-expandAllButton { + height: 18px; + width: 18px; + padding-left: 4px; + padding-top: 1px; + border-radius: 50%; + background-color: @default-bg; + font-size: 12px; + cursor: pointer; +} + +.JobResultsStdOut-expandAllButton:hover .JobResultsStdOut-expandAllIcon, +.JobResultsStdOut-expandAllIcon:hover { + color: @default-data-txt; +} + +.JobResultsStdOut-toolbarStdoutColumn { + white-space: normal; + flex: 1; + display: flex; + justify-content: flex-end; + padding-right: 10px; + background-color: @default-no-items-bord; +} + +.JobResultsStdOut-followButton { + cursor: pointer; + width: 18px; + height: 18px; + width: 18px; + padding-left: 3.8px; + border-radius: 50%; + margin-top: 10px; + font-size: 12px; + background-color: @default-icon; + color: @default-border; +} + +.JobResultsStdOut-followIcon { + color: @default-border; +} + +.JobResultsStdOut-followButton:hover { + background-color: @default-icon-hov; +} + +.JobResultsStdOut-followButton:hover .JobResultsStdOut-followIcon, +.JobResultsStdOut-followIcon:hover { + color: @default-interface-txt; +} + +.JobResultsStdOut-stdoutContainer { + height: ~"calc(100% - 108px)"; + background-color: @default-no-items-bord; + border: 1px solid @default-list-header-bg; + border-top: 0px; + border-radius: 5px; + border-top-left-radius: 0px; + border-top-right-radius: 0px; + margin-bottom: 10px; + overflow: scroll; +} + +.JobResultsStdOut-numberColumnPreload { + background-color: #EBEBEB; + width: 70px; + position: fixed; +} + +.JobResultsStdOut-aLineOfStdOut { + display: flex; + font-family: Monaco, Menlo, Consolas, "Courier New", monospace; +} + +.JobResultsStdOut-lineExpander { + text-align: left; + padding-left: 11px; + margin-right: auto; +} + +.JobResultsStdOut-lineExpanderIcon { + font-size: 19px; + cursor: pointer; +} + +.JobResultsStdOut-lineExpanderIcon:hover { + color: @default-data-txt; +} + +.JobResultsStdOut-lineNumberColumn { display: flex; background-color: @default-list-header-bg; text-align: right; @@ -22,28 +131,16 @@ padding-bottom: 2px; color: @b7grey; width: 75px; + flex: initial; white-space: pre-line; user-select: none; -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; + z-index: 1; } -.JobResultsStdOut-lineExpander { - text-align: left; - padding-left: 10px; - margin-right: auto; -} - -.JobResultsStdOut-lineNumberColumn--first{ - text-align: left; - padding: 0px; - padding-left: 11px; - padding-top: 10px; - white-space: normal; -} - -.JobResultsStdOut-stdoutColumn{ +.JobResultsStdOut-stdoutColumn { padding-left: 20px; padding-top: 2px; padding-bottom: 2px; @@ -54,6 +151,8 @@ width:100%; } -.JobResultsStdOut-stdoutColumn--first{ - padding-top:0px; + +// TODO: needs to be set based on height of browser window +.JobResultsStdOut-numberColumnPreload { + height: 720px; } diff --git a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js index e6bdefe91d..dd7366fc11 100644 --- a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js +++ b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js @@ -5,15 +5,75 @@ *************************************************/ // import hostStatusBarController from './host-status-bar.controller'; -export default [ 'templateUrl', - function(templateUrl) { +export default [ 'templateUrl', '$timeout', + function(templateUrl, $timeout) { return { - scope: true, + scope: false, templateUrl: templateUrl('job-results/job-results-stdout/job-results-stdout'), restrict: 'E', - // controller: jobResultsStdOutController, - link: function(scope) { + link: function(scope, element, attrs) { + scope.toggleAllStdout = function(type) { + var expandClass; + if (type === 'expand') { + expandClass = "fa-caret-right"; + } else { + expandClass = "fa-caret-down"; + } + element.find(".expanderizer--task."+expandClass) + .each((i, val) => { + $timeout(function(){ + angular.element(val).trigger('click'); + }); + }); + + element.find(".expanderizer--play."+expandClass) + .each((i, val) => { + if(angular.element("." + + angular.element(val).attr("data-uuid")) + .find(".expanderizer--task") + .length === 0 || + type !== 'collapse') { + + $timeout(function(){ + angular.element(val) + .trigger('click'); + }); + } + }); + }; + + scope.toggleLine = function($event, id) { + if ($($event.currentTarget).hasClass("fa-caret-down")) { + $(id).hide(); + $($event.currentTarget) + .removeClass("fa-caret-down"); + $($event.currentTarget) + .addClass("fa-caret-right"); + } else { + $(id).show(); + $($event.currentTarget) + .removeClass("fa-caret-right"); + $($event.currentTarget) + .addClass("fa-caret-down"); + + if ($($event.currentTarget) + .hasClass("expanderizer--play")) { + $("." + $($event.currentTarget) + .attr("data-uuid")) + .find(".expanderizer--task") + .each((i, val) => { + if ($(val) + .hasClass("fa-caret-right")) { + $timeout(function(){ + angular.element(val) + .trigger('click'); + }); + } + }); + } + } + }; } }; }]; diff --git a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html index 283a80a502..152429c7d4 100644 --- a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html +++ b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html @@ -1,11 +1,31 @@
-
-
- +
+
+
+ + +
+
+ + +
-
+
+
+ + +
+
+
+
diff --git a/awx/ui/client/src/job-results/job-results.block.less b/awx/ui/client/src/job-results/job-results.block.less index fa1e139501..060c08bdce 100644 --- a/awx/ui/client/src/job-results/job-results.block.less +++ b/awx/ui/client/src/job-results/job-results.block.less @@ -17,10 +17,14 @@ .JobResults-leftSide { .OnePlusTwo-left--panel(100%, @breakpoint-md); + // TODO: needs to be set based on height of browser window + height: 870px !important; } .JobResults-rightSide { .OnePlusTwo-right--panel(100%, @breakpoint-md); + // TODO: needs to be set based on height of browser window + height: 870px !important; @media (max-width: @breakpoint-md - 1px) { padding-right: 15px; diff --git a/awx/ui/client/src/job-results/job-results.controller.js b/awx/ui/client/src/job-results/job-results.controller.js index a920cdcdd5..73d7c1fc53 100644 --- a/awx/ui/client/src/job-results/job-results.controller.js +++ b/awx/ui/client/src/job-results/job-results.controller.js @@ -1,4 +1,4 @@ -export default ['jobData', 'jobDataOptions', 'jobLabels', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', '$rootScope', 'eventQueue', function(jobData, jobDataOptions, jobLabels, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, $rootScope, eventQueue) { +export default ['jobData', 'jobDataOptions', 'jobLabels', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', '$rootScope', 'eventQueue', '$compile', function(jobData, jobDataOptions, jobLabels, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, $rootScope, eventQueue, $compile) { var getTowerLinks = function() { var getTowerLink = function(key) { if ($scope.job.related[key]) { @@ -132,7 +132,10 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'count', '$scope', 'Pa } if(change === 'stdout'){ - $(".JobResultsStdOut").append(mungedEvent.stdout); + angular + .element(".JobResultsStdOut-stdoutContainer") + .append($compile(mungedEvent + .stdout)($scope)); } }); } diff --git a/awx/ui/client/src/job-results/job-results.route.js b/awx/ui/client/src/job-results/job-results.route.js index 54e47e9655..c2bf5f3e01 100644 --- a/awx/ui/client/src/job-results/job-results.route.js +++ b/awx/ui/client/src/job-results/job-results.route.js @@ -53,8 +53,8 @@ export default { if (jobData.finished) { // if the job is finished, grab the playbook_on_stats // role to get the final count - Rest.setUrl(jobData.related.job_events);// + - // "?event=playbook_on_stats"); + Rest.setUrl(jobData.related.job_events + + "?event=playbook_on_stats"); Rest.get() .success(function(data) { if(!data.results[0]){ diff --git a/awx/ui/client/src/job-results/job-results.service.js b/awx/ui/client/src/job-results/job-results.service.js index 0109eeee55..3ca6019587 100644 --- a/awx/ui/client/src/job-results/job-results.service.js +++ b/awx/ui/client/src/job-results/job-results.service.js @@ -81,7 +81,8 @@ export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErr Rest.setUrl(url); Rest.get() .success(function(data) { - val.resolve({results: data.results, next: data.next}); + val.resolve({results: data.results, + next: data.next}); }) .error(function(obj, status) { ProcessErrors(null, obj, status, null, { diff --git a/awx/ui/client/src/job-results/parse-stdout.service.js b/awx/ui/client/src/job-results/parse-stdout.service.js index 585032d7f9..d569b3420f 100644 --- a/awx/ui/client/src/job-results/parse-stdout.service.js +++ b/awx/ui/client/src/job-results/parse-stdout.service.js @@ -7,21 +7,13 @@ export default [function(){ var val = { prettify: function(line){ - - // this function right now just removes the 'rn' strings - // that i'm currently seeing on this branch on the beginning - // and end of each event string. In the future it could be - // used to add styling classes to the actual lines of stdout - // line = line.replace(/rn/g, '\n'); + // TODO: figure out from Jared what this is line = line.replace(/u001b/g, ''); - // ok + // ansi classes line = line.replace(/\[0;32m/g, ''); - - //unreachable line = line.replace(/\[1;31m/g, ''); line = line.replace(/\[0;31m/g, ''); - line = line.replace(/\[0;32m=/g, ''); line = line.replace(/\[0;32m1/g, ''); line = line.replace(/\[0;33m/g, ''); @@ -34,12 +26,12 @@ export default [function(){ getCollapseClasses: function(event) { var string = ""; if (event.event_name === "playbook_on_play_start") { - return string; + string += " header_play"; } else if (event.event_name === "playbook_on_task_start") { + string += " header_task"; if (event.event_data.play_uuid) { string += " play_" + event.event_data.play_uuid; } - return string; } else { if (event.event_data.play_uuid) { string += " play_" + event.event_data.play_uuid; @@ -47,18 +39,48 @@ export default [function(){ if (event.event_data.task_uuid) { string += " task_" + event.event_data.task_uuid; } - return string; } + + return string; }, getCollapseIcon: function(event, line) { - if ((event.event_name === "playbook_on_play_start" || event.event_name === "playbook_on_task_start") && line !== "") { - return ``; + var clickClass, + expanderizerSpecifier; + + var emptySpan = ` +`; + + if ((event.event_name === "playbook_on_play_start" || + event.event_name === "playbook_on_task_start") && + line !== "") { + if (event.event_name === "playbook_on_play_start" && + line.indexOf("PLAY") > -1) { + expanderizerSpecifier = "play"; + clickClass = "play_" + + event.event_data.play_uuid; + } else if (line.indexOf("TASK") > -1 || + line.indexOf("RUNNING HANDLER") > -1) { + expanderizerSpecifier = "task"; + clickClass = "task_" + + event.event_data.task_uuid; + } else { + return emptySpan; + } + + return ` + + + +`; } else { - return ``; + return emptySpan; } }, parseStdout: function(event){ - var stdoutStrings = _ + return _ .zip(_.range(event.start_line + 1, event.end_line + 1), event.stdout.split("\r\n").slice(0, -1)) @@ -69,10 +91,6 @@ export default [function(){
${this.prettify(lineArr[1])}
`; }).join(""); - // this object will be used by the ng-repeat in the - // job-results-stdout.partial.html. probably need to add the - // elapsed time in here too - return stdoutStrings; } }; return val;