use status service in details and stats components

This commit is contained in:
Jake McDermott
2018-04-02 22:48:42 -04:00
parent bdd36341ae
commit 1f9b325f38
7 changed files with 90 additions and 188 deletions

View File

@@ -10,6 +10,7 @@ let parse;
let prompt; let prompt;
let resource; let resource;
let strings; let strings;
let status;
let wait; let wait;
let vm; let vm;
@@ -19,8 +20,8 @@ function mapChoices (choices) {
return Object.assign(...choices.map(([k, v]) => ({ [k]: v }))); return Object.assign(...choices.map(([k, v]) => ({ [k]: v })));
} }
function getStatusDetails (status) { function getStatusDetails (jobStatus) {
const unmapped = status || resource.model.get('status'); const unmapped = jobStatus || resource.model.get('status');
if (!unmapped) { if (!unmapped) {
return null; return null;
@@ -373,11 +374,11 @@ function getLabelDetails () {
} }
function createErrorHandler (path, action) { function createErrorHandler (path, action) {
return ({ data, status }) => { return res => {
const hdr = strings.get('error.HEADER'); const hdr = strings.get('error.HEADER');
const msg = strings.get('error.CALL', { path, action, status }); const msg = strings.get('error.CALL', { path, action, status: res.status });
error($scope, data, status, null, { hdr, msg }); error($scope, res.data, res.status, null, { hdr, msg });
}; };
} }
@@ -454,24 +455,14 @@ function deleteJob () {
prompt({ hdr, resourceName, body, actionText, action }); prompt({ hdr, resourceName, body, actionText, action });
} }
function handleSocketEvent (data) { function AtJobDetailsController (
const project = resource.model.get('project');
if (resource.model.get('id') === data.unified_job_id) {
vm.status = getStatusDetails(data.status);
} else if (project && (project === data.project_id)) {
vm.project.update = vm.project.update || {};
vm.project.update.status = data.status;
}
}
function AtDetailsController (
_$http_, _$http_,
_$filter_, _$filter_,
_$state_, _$state_,
_error_, _error_,
_prompt_, _prompt_,
_strings_, _strings_,
_status_,
_wait_, _wait_,
ParseTypeChange, ParseTypeChange,
ParseVariableString, ParseVariableString,
@@ -486,6 +477,7 @@ function AtDetailsController (
parse = ParseVariableString; parse = ParseVariableString;
prompt = _prompt_; prompt = _prompt_;
strings = _strings_; strings = _strings_;
status = _status_;
wait = _wait_; wait = _wait_;
vm.init = _$scope_ => { vm.init = _$scope_ => {
@@ -538,47 +530,48 @@ function AtDetailsController (
vm.deleteJob = deleteJob; vm.deleteJob = deleteJob;
vm.toggleLabels = toggleLabels; vm.toggleLabels = toggleLabels;
$scope.$watch('started', value => { vm.started = getStartDetails(value); }); $scope.$watch(status.getStarted, value => { vm.started = getStartDetails(value); });
$scope.$watch('status', value => { vm.status = getStatusDetails(value); }); $scope.$watch(status.getJobStatus, value => { vm.status = getStatusDetails(value); });
$scope.$watch('finished', value => { vm.finished = getFinishDetails(value); }); $scope.$watch(status.getFinished, value => { vm.finished = getFinishDetails(value); });
$scope.$on(resource.ws.status, (e, data) => handleSocketEvent(data)); $scope.$watch(status.getProjectStatus, value => {
if (!value) return;
vm.project.update = vm.project.update || {};
vm.project.update.status = value;
});
}; };
} }
AtDetailsController.$inject = [ AtJobDetailsController.$inject = [
'$http', '$http',
'$filter', '$filter',
'$state', '$state',
'ProcessErrors', 'ProcessErrors',
'Prompt', 'Prompt',
'JobStrings', 'JobStrings',
'JobStatusService',
'Wait', 'Wait',
'ParseTypeChange', 'ParseTypeChange',
'ParseVariableString', 'ParseVariableString',
]; ];
function atDetailsLink (scope, el, attrs, controllers) { function atJobDetailsLink (scope, el, attrs, controllers) {
const [atDetailsController] = controllers; const [atDetailsController] = controllers;
atDetailsController.init(scope); atDetailsController.init(scope);
} }
function atDetails () { function atJobDetails () {
return { return {
templateUrl, templateUrl,
restrict: 'E', restrict: 'E',
require: ['atDetails'], require: ['atJobDetails'],
controllerAs: 'vm', controllerAs: 'vm',
link: atDetailsLink, link: atJobDetailsLink,
controller: AtDetailsController, controller: AtJobDetailsController,
scope: { scope: { resource: '=', },
finished: '=',
started: '=',
resource: '=',
status: '=',
},
}; };
} }
export default atDetails; export default atJobDetails;

View File

@@ -105,7 +105,7 @@ function JobEventEngine ($q) {
} }
}; };
this.pushEvent = data => { this.pushJobEvent = data => {
this.lag++; this.lag++;
this.chain = this.chain this.chain = this.chain

View File

@@ -1,25 +1,17 @@
const JOB_START = 'playbook_on_start';
const JOB_END = 'playbook_on_stats';
const PLAY_START = 'playbook_on_play_start';
const TASK_START = 'playbook_on_task_start';
let $compile; let $compile;
let $q; let $q;
let $scope; let $scope;
let $state; let $state;
let moment;
let page; let page;
let qs; let qs;
let render; let render;
let resource; let resource;
let scroll; let scroll;
let engine; let engine;
let status;
let vm; let vm;
let eventCounter;
let statsEvent;
function JobsIndexController ( function JobsIndexController (
_resource_, _resource_,
_page_, _page_,
@@ -31,7 +23,7 @@ function JobsIndexController (
_$q_, _$q_,
_$state_, _$state_,
_qs_, _qs_,
_moment_, _status_,
) { ) {
vm = this || {}; vm = this || {};
@@ -44,8 +36,7 @@ function JobsIndexController (
scroll = _scroll_; scroll = _scroll_;
render = _render_; render = _render_;
engine = _engine_; engine = _engine_;
status = _status_;
moment = _moment_;
// Development helper(s) // Development helper(s)
vm.clear = devClear; vm.clear = devClear;
@@ -55,31 +46,10 @@ function JobsIndexController (
vm.expand = expand; vm.expand = expand;
vm.isExpanded = true; vm.isExpanded = true;
// Events
eventCounter = null;
statsEvent = resource.stats;
// Panel // Panel
vm.resource = resource;
vm.title = resource.model.get('name'); vm.title = resource.model.get('name');
// Stats
vm.stats = {
event: statsEvent,
elapsed: resource.model.get('elapsed'),
download: resource.model.get('related.stdout'),
running: Boolean(resource.model.get('started')) && !resource.model.get('finished'),
plays: null,
tasks: null,
};
// Details
vm.details = {
resource,
status: resource.model.get('status'),
started: resource.model.get('started'),
finished: resource.model.get('finished'),
};
// Search // Search
$state = _$state_; $state = _$state_;
qs = _qs_; qs = _qs_;
@@ -107,10 +77,14 @@ function JobsIndexController (
up: scrollPageUp up: scrollPageUp
}; };
render.requestAnimationFrame(() => init(!vm.stats.running)); render.requestAnimationFrame(() => init());
} }
function init (pageMode) { function init () {
status.init({
resource,
});
page.init({ page.init({
resource, resource,
}); });
@@ -118,7 +92,7 @@ function init (pageMode) {
render.init({ render.init({
get: () => resource.model.get(`related.${resource.related}.results`), get: () => resource.model.get(`related.${resource.related}.results`),
compile: html => $compile(html)($scope), compile: html => $compile(html)($scope),
isStreamActive: engine.isActive isStreamActive: engine.isActive,
}); });
scroll.init({ scroll.init({
@@ -135,60 +109,34 @@ function init (pageMode) {
return shift().then(() => append(events, true)); return shift().then(() => append(events, true));
}, },
onStart () { onStart () {
vm.stats.plays = 0; status.resetCounts();
vm.stats.tasks = 0; status.setJobStatus('running');
vm.stats.running = true;
vm.search.disabled = true; vm.search.disabled = true;
vm.details.status = 'running';
}, },
onStop () { onStop () {
vm.stats.event = statsEvent; status.updateStats();
vm.stats.running = false;
vm.search.disabled = false; vm.search.disabled = false;
vm.details.status = statsEvent.failed ? 'failed' : 'successful';
vm.details.finished = statsEvent.created;
} }
}); });
$scope.$on(resource.ws.events, handleSocketEvent); $scope.$on(resource.ws.events, handleSocketEvent);
$scope.$on(resource.ws.status, handleStatusEvent);
if (pageMode) { if (!status.isRunning()) {
next(); next();
} }
} }
function handleStatusEvent (scope, data) {
status.pushStatusEvent(data);
}
function handleSocketEvent (scope, data) { function handleSocketEvent (scope, data) {
const isLatest = ((!eventCounter) || (data.counter > eventCounter)); engine.pushJobEvent(data);
if (isLatest) { status.pushJobEvent(data);
eventCounter = data.counter;
vm.details.status = _.get(data, 'summary_fields.job.status');
vm.stats.elapsed = moment(data.created)
.diff(resource.model.get('created'), 'seconds');
}
if (data.event === JOB_START) {
vm.details.started = data.created;
}
if (data.event === PLAY_START) {
vm.stats.plays++;
}
if (data.event === TASK_START) {
vm.stats.tasks++;
}
if (data.event === JOB_END) {
statsEvent = data;
}
engine.pushEvent(data);
} }
function devClear (pageMode) { function devClear (pageMode) {
@@ -466,7 +414,7 @@ JobsIndexController.$inject = [
'$q', '$q',
'$state', '$state',
'QuerySet', 'QuerySet',
'moment', 'JobStatusService',
]; ];
module.exports = JobsIndexController; module.exports = JobsIndexController;

View File

@@ -7,6 +7,7 @@ import PageService from '~features/output/page.service';
import RenderService from '~features/output/render.service'; import RenderService from '~features/output/render.service';
import ScrollService from '~features/output/scroll.service'; import ScrollService from '~features/output/scroll.service';
import EngineService from '~features/output/engine.service'; import EngineService from '~features/output/engine.service';
import StatusService from '~features/output/status.service';
import DetailsDirective from '~features/output/details.directive'; import DetailsDirective from '~features/output/details.directive';
import SearchKeyDirective from '~features/output/search-key.directive'; import SearchKeyDirective from '~features/output/search-key.directive';
@@ -217,9 +218,10 @@ angular
.service('JobScrollService', ScrollService) .service('JobScrollService', ScrollService)
.service('JobRenderService', RenderService) .service('JobRenderService', RenderService)
.service('JobEventEngine', EngineService) .service('JobEventEngine', EngineService)
.directive('atDetails', DetailsDirective) .service('JobStatusService', StatusService)
.directive('atJobDetails', DetailsDirective)
.directive('atSearchKey', SearchKeyDirective) .directive('atSearchKey', SearchKeyDirective)
.directive('atStats', StatsDirective) .directive('atJobStats', StatsDirective)
.run(JobsRun); .run(JobsRun);
export default MODULE_NAME; export default MODULE_NAME;

View File

@@ -1,28 +1,14 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="col-md-4"> <div class="col-md-4">
<at-panel> <at-panel>
<at-details <at-job-details resource="vm.resource"></at-job-details>
resource="vm.details.resource"
started="vm.details.started"
finished="vm.details.finished"
status="vm.details.status">
</at-details>
<!-- <p><button class="btn" ng-click="vm.clear(true)">Page Mode</button></p> -->
</at-panel> </at-panel>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<at-panel class="at-Stdout"> <at-panel class="at-Stdout">
<div class="at-Panel-headingTitle">{{ vm.title }}</div> <div class="at-Panel-headingTitle">{{ vm.title }}</div>
<at-stats <at-job-stats resource="vm.resource"></at-job-stats>
download="vm.stats.download"
elapsed="vm.stats.elapsed"
running="vm.stats.running"
event="vm.stats.event"
title="vm.stats.title"
plays="vm.stats.plays"
tasks="vm.stats.tasks">
</at-stats>
<!-- search ===================================================================================== --> <!-- search ===================================================================================== -->
<form ng-submit="vm.search.submitSearch()"> <form ng-submit="vm.search.submitSearch()">
<div class="input-group"> <div class="input-group">

View File

@@ -1,30 +1,7 @@
const templateUrl = require('~features/output/stats.partial.html'); const templateUrl = require('~features/output/stats.partial.html');
const HOST_STATUS_KEYS = ['dark', 'failures', 'changed', 'ok', 'skipped']; let status;
let strings;
function getHostStatusCounts (statsEvent) {
const countedHostNames = [];
const counts = Object.assign(...HOST_STATUS_KEYS.map(key => ({ [key]: 0 })));
HOST_STATUS_KEYS.forEach(key => {
const hostData = _.get(statsEvent, ['event_data', key], {});
Object.keys(hostData).forEach(hostName => {
const isAlreadyCounted = (countedHostNames.indexOf(hostName) > -1);
const shouldBeCounted = ((!isAlreadyCounted) && hostData[hostName] > 0);
if (shouldBeCounted) {
countedHostNames.push(hostName);
counts[key]++;
}
});
});
counts.hosts = countedHostNames.length;
return counts;
}
function createStatsBarTooltip (key, count) { function createStatsBarTooltip (key, count) {
const label = `<span class='HostStatusBar-tooltipLabel'>${key}</span>`; const label = `<span class='HostStatusBar-tooltipLabel'>${key}</span>`;
@@ -33,13 +10,16 @@ function createStatsBarTooltip (key, count) {
return `${label}${badge}`; return `${label}${badge}`;
} }
function atStatsLink (scope, el, attrs, controllers) { function atJobStatsLink (scope, el, attrs, controllers) {
const [atStatsController] = controllers; const [atJobStatsController] = controllers;
atStatsController.init(scope); atJobStatsController.init(scope);
} }
function AtStatsController (strings) { function AtJobStatsController (_strings_, _status_) {
status = _status_;
strings = _strings_;
const vm = this || {}; const vm = this || {};
vm.tooltips = { vm.tooltips = {
@@ -48,28 +28,23 @@ function AtStatsController (strings) {
}; };
vm.init = scope => { vm.init = scope => {
const { download, elapsed, running, event, plays, tasks } = scope; const { resource } = scope;
vm.download = download; vm.download = resource.model.get('related.stdout');
vm.plays = plays;
vm.tasks = tasks;
vm.elapsed = elapsed;
vm.running = running || false;
vm.setStats(event); vm.setHostStatusCounts(status.getHostStatusCounts());
scope.$watch('elapsed', value => { vm.elapsed = value; }); scope.$watch(status.getPlayCount, value => { vm.plays = value; });
scope.$watch('running', value => { vm.running = value; }); scope.$watch(status.getTaskCount, value => { vm.tasks = value; });
scope.$watch('plays', value => { vm.plays = value; }); scope.$watch(status.getElapsed, value => { vm.elapsed = value; });
scope.$watch('tasks', value => { vm.tasks = value; }); scope.$watch(status.getHostCount, value => { vm.hosts = value; });
scope.$watch(status.isRunning, value => { vm.running = value; });
scope.$watch('event', vm.setStats); scope.$watchCollection(status.getHostStatusCounts, vm.setHostStatusCounts);
}; };
vm.setStats = statsEvent => { vm.setHostStatusCounts = counts => {
const counts = getHostStatusCounts(statsEvent); Object.keys(counts).forEach(key => {
HOST_STATUS_KEYS.forEach(key => {
const count = counts[key]; const count = counts[key];
const statusBarElement = $(`.HostStatusBar-${key}`); const statusBarElement = $(`.HostStatusBar-${key}`);
@@ -78,31 +53,24 @@ function AtStatsController (strings) {
vm.tooltips[key] = createStatsBarTooltip(key, count); vm.tooltips[key] = createStatsBarTooltip(key, count);
}); });
vm.hosts = counts.hosts; vm.statsAreAvailable = Boolean(status.getStatsEvent());
vm.statsAreAvailable = Boolean(statsEvent);
}; };
} }
function atStats () { function atJobStats () {
return { return {
templateUrl, templateUrl,
restrict: 'E', restrict: 'E',
require: ['atStats'], require: ['atJobStats'],
controllerAs: 'vm', controllerAs: 'vm',
link: atStatsLink, link: atJobStatsLink,
controller: [ controller: [
'JobStrings', 'JobStrings',
AtStatsController 'JobStatusService',
AtJobStatsController
], ],
scope: { scope: { resource: '=', },
download: '=',
elapsed: '=',
running: '=',
event: '=',
plays: '=',
tasks: '=',
},
}; };
} }
export default atStats; export default atJobStats;

View File

@@ -40,26 +40,31 @@
<div class="HostStatusBar"> <div class="HostStatusBar">
<div class="HostStatusBar-ok" <div class="HostStatusBar-ok"
ng-show="!vm.running"
data-placement="top" data-placement="top"
aw-tool-tip="{{ vm.tooltips.ok }}" aw-tool-tip="{{ vm.tooltips.ok }}"
data-tip-watch="vm.tooltips.ok"> data-tip-watch="vm.tooltips.ok">
</div> </div>
<div class="HostStatusBar-skipped" <div class="HostStatusBar-skipped"
ng-show="!vm.running"
data-placement="top" data-placement="top"
aw-tool-tip="{{ vm.tooltips.skipped }}" aw-tool-tip="{{ vm.tooltips.skipped }}"
data-tip-watch="vm.tooltips.skipped"> data-tip-watch="vm.tooltips.skipped">
</div> </div>
<div class="HostStatusBar-changed" <div class="HostStatusBar-changed"
ng-show="!vm.running"
data-placement="top" data-placement="top"
aw-tool-tip="{{ vm.tooltips.changed }}" aw-tool-tip="{{ vm.tooltips.changed }}"
data-tip-watch="vm.tooltips.changed"> data-tip-watch="vm.tooltips.changed">
</div> </div>
<div class="HostStatusBar-failures" <div class="HostStatusBar-failures"
ng-show="!vm.running"
data-placement="top" data-placement="top"
aw-tool-tip="{{ vm.tooltips.failures }}" aw-tool-tip="{{ vm.tooltips.failures }}"
data-tip-watch="vm.tooltips.failures"> data-tip-watch="vm.tooltips.failures">
</div> </div>
<div class="HostStatusBar-dark" <div class="HostStatusBar-dark"
ng-show="!vm.running"
data-placement="top" data-placement="top"
aw-tool-tip="{{ vm.tooltips.dark }}" aw-tool-tip="{{ vm.tooltips.dark }}"
data-tip-watch="vm.tooltips.dark"> data-tip-watch="vm.tooltips.dark">