Job Events collapse/expand now handled using a filter on ng-repeat and setting 'show' attribute in job_events data set. No longer reading child rows from api on expand and throwing them away on collapse. New method is much faster. Added formatJSON() routine back in to transform relevant event_data bits into html form elments and display in list.

This commit is contained in:
chouseknecht
2013-07-03 12:02:20 -04:00
parent 20266c3054
commit 1b3902a23b
5 changed files with 91 additions and 157 deletions

View File

@@ -370,6 +370,10 @@
color: #5bb75b;
}
.job-changed {
color: #FF9900;
}
.job-detail-status {
font-size: 15px;
font-weight: bold;

View File

@@ -19,7 +19,7 @@ function JobEventsList ($scope, $rootScope, $location, $log, $routeParams, Rest,
var list = JobEventList;
list.base = $location.path();
var defaultUrl = GetBasePath('jobs') + $routeParams.id + '/job_events/?parent__isnull=1';
var defaultUrl = GetBasePath('jobs') + $routeParams.id + '/job_events/'; //?parent__isnull=1';
var view = GenerateList;
var base = $location.path().replace(/^\//,'').split('/')[0];
@@ -32,6 +32,47 @@ function JobEventsList ($scope, $rootScope, $location, $log, $routeParams, Rest,
scope.parentNode = 'parent-event'; // used in ngClass to dynamicall set row level class and control
scope.childNode = 'child-event'; // link color and cursor
function formatJSON(eventData) {
//turn JSON event data into an html form
var html = '';
if (eventData.res) {
var found = false;
var n, rows;
if (typeof eventData.res == 'string') {
n = eventData['res'].match(/\n/g);
rows = (n) ? n.length : 1;
found = true;
html += "<label>Traceback:</label>\n";
html += "<textarea readonly class=\"input-xxlarge\" rows=\"" + rows + "\">" + eventData.res + "</textarea>\n";
}
else {
for (var fld in eventData.res) {
if ( (fld == 'msg' || fld == 'stdout' || fld == 'stderr') &&
(eventData.res[fld] !== null && eventData.res[fld] !== '') ) {
html += "<label>";
switch(fld) {
case 'msg':
case 'stdout':
html += 'Output:';
break;
case 'stderr':
html += 'Error:';
break;
}
html += "</label>\n";
n = eventData['res'][fld].match(/\n/g);
rows = (n) ? n.length : 1;
html += "<textarea readonly class=\"input-xxlarge\" rows=\"" + rows + "\">" + eventData.res[fld] + "</textarea>\n";
found = true;
}
}
}
html = (found) ? "<form class=\"event-detail\">\n" + html + "</form>\n" : '';
}
html = (html == '' ) ? null : html;
return html;
}
if (scope.RemovePostRefresh) {
scope.RemovePostRefresh();
}
@@ -41,62 +82,35 @@ function JobEventsList ($scope, $rootScope, $location, $log, $routeParams, Rest,
var cDate;
for (var i=0; i < set.length; i++) {
set[i].event_display = set[i].event_display.replace(/^\u00a0*/g,'');
if (set[i].parent == null && set[i]['ngclick'] === undefined && set[i]['ngicon'] == undefined) {
set[i].parent = 0;
if (set[i].event_level < 3 ) {
set[i]['ngclick'] = "toggleChildren(" + set[i].id + ", \"" + set[i].related.children + "\")";
set[i]['ngicon'] = 'icon-expand-alt';
set[i]['level'] = 0;
set[i]['spaces'] = 0;
set[i]['ngicon'] = 'icon-collapse-alt';
set[i]['class'] = 'parentNode';
}
scope.jobevents[i].status = (scope.jobevents[i].failed) ? 'error' : 'success';
else {
set[i]['class'] = 'childNode';
set[i]['event_detail'] = formatJSON(set[i].event_data);
}
set[i]['show'] = true;
set[i]['spaces'] = set[i].event_level * 24;
if (scope.jobevents[i].failed) {
scope.jobevents[i].status = 'error';
}
else if (scope.jobevents[i].changed) {
scope.jobevents[i].status = 'changed';
}
else {
scope.jobevents[i].status = 'success';
}
cDate = new Date(set[i].created);
set[i].created = FormatDate(cDate);
}
// Expand all parent nodes
if (scope.removeSetExpanded) {
scope.removeSetExpanded();
}
scope.removeSetExpanded = scope.$on('setExpanded', function(event) {
// After ToggleChildren completes, look for the next parent that needs to be expanded
var found = false;
for (var i=0; i < set.length && found == false && scope.expand; i++) {
if (set[i]['related']['children'] && (set[i]['ngicon'] == undefined || set[i]['ngicon'] == 'icon-expand-alt')) {
found = true;
ToggleChildren({
scope: scope,
list: list,
id: set[i].id,
children: set[i]['related']['children']
});
}
}
if (found == false) {
// After looping through all the nodes and finding nothing to expand, turn off
// auto-expand. From now on user will manually collapse and expand nodes.
scope.expand = false;
}
});
// Start the auto expansion
set = scope[list.name];
for (var i=0; i < set.length; i++) {
if (set[i]['related']['children'] && (set[i]['ngicon'] == undefined || set[i]['ngicon'] == 'icon-expand-alt')) {
ToggleChildren({
scope: scope,
list: list,
id: set[i].id,
children: set[i]['related']['children']
});
}
}
});
SearchInit({ scope: scope, set: 'jobevents', list: list, url: defaultUrl });
PaginateInit({ scope: scope, list: list, url: defaultUrl });
// Called from Inventories tab host failed events link:
// Called from Inventories tab, host failed events link:
if ($routeParams.host) {
scope[list.iterator + 'SearchField'] = 'host';
scope[list.iterator + 'SearchValue'] = $routeParams.host;

View File

@@ -3,7 +3,9 @@
*
* ChildrenHelper
*
* Used in job_events to expand/collapse children
* Used in job_events to expand/collapse children by setting the
* 'show' attribute of each job_event in the set of job_events.
* See the filter in job_events.js list.
*
*/
@@ -18,37 +20,28 @@ angular.module('ChildrenHelper', ['RestServices', 'Utilities'])
var children = params.children;
var set = scope[list.name]; // set is now a pointer to scope[list.name]
function calcSpaces(lvl) {
return lvl * 24;
function expand(node) {
set[node]['ngicon'] = 'icon-collapse-alt';
for (var i = node + 1; i < set.length; i++) {
if (set[i].parent == set[node].id) {
set[i]['show'] = true;
if (set[i].related.children) {
expand(i);
}
}
}
}
function formatJSON(eventData) {
//turn JSON event data into an html form
var html = '';
if (eventData.res) {
var found = false;
for (var fld in eventData.res) {
if ( (fld == 'msg' || fld == 'stdout' || fld == 'stderr') &&
(eventData.res[fld] !== null && eventData.res[fld] !== '') ) {
html += "<label>";
switch(fld) {
case 'msg':
case 'stdout':
html += 'Output:';
break;
case 'stderr':
html += 'Error:';
break;
}
html += "</label>\n";
html += "<textarea readonly class=\"input-xxlarge\">" + eventData.res[fld] + "</textarea>\n";
found = true;
}
function collapse(node) {
set[node]['ngicon'] = 'icon-expand-alt';
for (var i = node + 1; i < set.length; i++) {
if (set[i].parent == set[node].id) {
set[i]['show'] = false;
if (set[i]['related']['children']) {
collapse(i);
}
}
html = (found) ? "<form class=\"event-detail\">\n" + html + "</form>\n" : '';
}
html = (html == '' ) ? null : html;
return html;
}
}
// Scan the array list and find the clicked element
@@ -62,90 +55,11 @@ angular.module('ChildrenHelper', ['RestServices', 'Utilities'])
}
// Expand or collapse children based on clicked element's icon
if (set[clicked]['ngicon'] == 'icon-expand-alt') {
// Expand: lookup and display children
var url = children;
var search = scope[list.iterator + 'SearchParams'].replace(/^\&/,'').replace(/^\?/,'');
url += (search) ? '?' + search : "";
Rest.setUrl(url);
Rest.get()
.success( function(data, status, headers, config) {
var found = false;
var level = (set[clicked].level !== undefined) ? set[clicked].level + 1 : 1;
var spaces = calcSpaces(level);
set[clicked]['ngicon'] = 'icon-collapse-alt';
for (var j=0; j < data.results.length; j++) {
data.results[j].level = level;
data.results[j].spaces = spaces;
data.results[j].status = (data.results[j].failed) ? 'error' : 'success';
data.results[j].event_display = data.results[j].event_display.replace(/^\u00a0*/g,'');
cDate = new Date(data.results[j].created);
data.results[j].created = FormatDate(cDate);
if (data.results[j].related.children) {
data.results[j]['ngclick'] = "toggleChildren(" + data.results[j].id + ", \"" + data.results[j].related.children + "\")";
data.results[j]['ngicon'] = 'icon-expand-alt';
data.results[j]['class'] = 'parentNode';
}
else {
data.results[j]['class'] = 'childNode';
// For child nodes, include some of the even_data detail, but in a nicer non-JSONy format
data.results[j]['event_detail'] = formatJSON(data.results[j]['event_data']);
}
/*if (data.results[j]['event_data']['res'] && data.results[j]['event_data']['res']['msg']) {
// Display the actual result message
data.results[j]['event_display'] = data.results[j]['event_data']['res']['msg'];
}
if (data.results[j]['event'] == 'playbook_on_stats') {
data.results[j]['event_display'] = 'Play Recap ****** ';
for (var key in data.results[j]['event_data']) {
var count = 0;
for (var itm in data.results[j]['event_data'][key]) {
count += data.results[j]['event_data'][key][itm];
}
data.results[j]['event_display'] += key + ": " + count + " ";
}
}
*/
if (clicked == (set.length - 1)) {
set.push(data.results[j]);
}
else {
set.splice(clicked + 1, 0, data.results[j]);
}
clicked++;
}
scope.$emit('setExpanded');
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + children + ' failed. GET returned status: ' + status });
});
// Expand: lookup and display children
expand(clicked);
}
else {
// Collapse: find and remove children
var parents = [];
function findChildren(parent, idx) {
// recursive look through the tree finding all
// parents including and related the clicked element
for (var i=idx; i < set.length; i++) {
if (set[i].parent == parent) {
parents.push(parent);
findChildren(set[i].id, i + 1);
}
}
}
findChildren(id, clicked + 1);
// Remove all the children of the clicked element
var count;
for (var i=0; i < parents.length; i++) {
count = 0;
for (var j=clicked + 1; j< set.length; j++) {
if (set[j].parent == parents[i]) {
set.splice(j,1);
j=clicked; // start back a the top of the list
}
}
}
set[clicked]['ngicon'] = 'icon-expand-alt';
collapse(clicked);
}
}
}]);

View File

@@ -16,6 +16,7 @@ angular.module('JobEventsListDefinition', [])
index: false,
hover: true,
hasChildren: true,
filterBy: '\{ show: true \}',
fields: {
created: {

View File

@@ -193,6 +193,7 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
html += (options.mode == 'lookup' || options.mode == 'select') ? "ng-class=\"" + list.iterator + "_\{\{ " + list.iterator + ".id \}\}_class\" " : "";
html += "class=\"" + list.iterator + "_class\" ng-repeat=\"" + list.iterator + " in " + list.name;
html += (list.orderBy) ? " | orderBy:'" + list.orderBy + "'" : "";
html += (list.filterBy) ? " | filter: " + list.filterBy : "";
html += "\"";
html += (options.mode == 'lookup' || options.mode == 'select') ? " ng-click=\"toggle_" + list.iterator +"({{ " + list.iterator + ".id }})\"" : "";
html += ">\n";