mirror of
https://github.com/ansible/awx.git
synced 2026-03-11 06:29:31 -02:30
Extract html string to template
This commit is contained in:
@@ -1,201 +1,157 @@
|
|||||||
angular.module('GraphDirectives', [])
|
angular.module('GraphDirectives', [])
|
||||||
.directive('jobStatusGraph', ['$rootScope', '$compile', '$location' , '$window', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'jobStatusGraphData',
|
.directive('jobStatusGraph', ['$rootScope', '$compile', '$location' , '$window', 'Wait', 'jobStatusGraphData',
|
||||||
function ($rootScope, $compile , $location, $window, Rest, GetBasePath, ProcessErrors, Wait, jobStatusGraphData) {
|
function ($rootScope, $compile , $location, $window, Rest, Wait, jobStatusGraphData) {
|
||||||
return function (scope, element, attr) {
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
templateUrl: '/static/partials/job_status_graph.html',
|
||||||
|
link: link
|
||||||
|
};
|
||||||
|
|
||||||
var html, url, job_status_chart,
|
function link(scope, element, attr) {
|
||||||
period="month",
|
|
||||||
job_type="all";
|
|
||||||
|
|
||||||
var cleanup = angular.noop;
|
var html, url, job_status_chart,
|
||||||
|
period="month",
|
||||||
|
job_type="all";
|
||||||
|
|
||||||
var data;
|
var cleanup = angular.noop;
|
||||||
scope.$watch(attr.data, function(value) {
|
|
||||||
if (value) {
|
|
||||||
scope.$emit('graphDataReady', value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
scope.$on('$destroy', cleanup);
|
var data;
|
||||||
|
scope.$watch(attr.data, function(value) {
|
||||||
|
if (value) {
|
||||||
|
scope.$emit('graphDataReady', value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
html = "<div class=\"row\">\n";
|
scope.$on('$destroy', cleanup);
|
||||||
html += "<div id=\"job-status-title\" class=\"h6 col-xs-2 col-sm-3 col-lg-4 text-center\"><b>Job Status</b></div>\n"; // for All Jobs, Past Month
|
|
||||||
|
|
||||||
html += "<div class=\"h6 col-xs-5 col-sm-5 col-lg-4\">\n";
|
cleanup = _.compose(
|
||||||
html += "<div class=\"dropdown\">\n";
|
[ cleanup,
|
||||||
html += "Job Type: <a id=\"type-dropdown\" role=\"button\" data-toggle=\"dropdown\" data-target=\"#\" href=\"/page.html\">\n";
|
scope.$on('DataReceived:JobStatusGraph',
|
||||||
html += "All<span class=\"caret\"></span>\n";
|
|
||||||
html += " </a>\n";
|
|
||||||
|
|
||||||
html += "<ul class=\"dropdown-menu\" role=\"menu\" aria-labelledby=\"type-dropdown\">\n";
|
|
||||||
html += "<li><a class=\"m\" id=\"all\">All</a></li>\n";
|
|
||||||
html += "<li><a class=\"m\" id=\"inv_sync\">Inventory Sync</a></li>\n";
|
|
||||||
html += "<li><a class=\"m\" id=\"scm_update\">SCM Update</a></li>\n";
|
|
||||||
html += "<li><a class=\"m\" id=\"playbook_run\">Playbook Run</a></li>\n";
|
|
||||||
html += "</ul>\n";
|
|
||||||
html += "</div>\n";
|
|
||||||
|
|
||||||
html += "</div>\n"; //end of filter div
|
|
||||||
|
|
||||||
html += "<div class=\"h6 col-xs-5 col-sm-4 col-lg-4\">\n";
|
|
||||||
html += "<div class=\"dropdown\">\n";
|
|
||||||
html += "Period: <a id=\"period-dropdown\" role=\"button\" data-toggle=\"dropdown\" data-target=\"#\" href=\"/page.html\">\n";
|
|
||||||
html += "Past Month<span class=\"caret\"></span>\n";
|
|
||||||
html += " </a>\n";
|
|
||||||
|
|
||||||
html += "<ul class=\"dropdown-menu\" role=\"menu\" aria-labelledby=\"period-dropdown\">\n";
|
|
||||||
html += "<li><a class=\"n\" id=\"day\" >Past 24 Hours </a></li>\n";
|
|
||||||
html += "<li><a class=\"n\" id=\"week\">Past Week</a></li>\n";
|
|
||||||
html += "<li><a class=\"n\" id=\"month\">Past Month</a></li>\n";
|
|
||||||
html += "</ul>\n";
|
|
||||||
html += "</div>\n";
|
|
||||||
html += "</div>\n"; //end of filter div
|
|
||||||
|
|
||||||
html += "</div>\n"; // end of row
|
|
||||||
|
|
||||||
html +="<div class=\"row\">\n";
|
|
||||||
html += "<div class=\"job-status-graph\"><svg></svg></div>\n";
|
|
||||||
html += "</div>\n";
|
|
||||||
|
|
||||||
// html += "</div>\n";
|
|
||||||
|
|
||||||
cleanup = _.compose(
|
|
||||||
[ cleanup,
|
|
||||||
scope.$on('DataReceived:JobStatusGraph',
|
|
||||||
function(e, data) {
|
function(e, data) {
|
||||||
scope.$emit('graphDataReady', data);
|
scope.$emit('graphDataReady', data);
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function createGraph(period, jobtype){
|
function createGraph(period, jobtype){
|
||||||
jobStatusGraphData.get(period, jobtype).then(function(data) {
|
console.log('createGraph');
|
||||||
scope.$emit('graphDataReady', data);
|
// jobStatusGraphData.get(period, jobtype).then(function(data) {
|
||||||
});
|
// scope.$emit('graphDataReady', data);
|
||||||
}
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
element.html($compile(html)(scope));
|
cleanup = _.compose(
|
||||||
|
[ cleanup,
|
||||||
|
angular.element($window).bind('resize', function() {
|
||||||
|
if($(window).width()<500){
|
||||||
|
$('.graph-container').height(300);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
var winHeight = $(window).height(),
|
||||||
|
available_height = winHeight - $('#main-menu-container .navbar').outerHeight() - $('#count-container').outerHeight() - 120;
|
||||||
|
$('.graph-container').height(available_height/2);
|
||||||
|
job_status_chart.update();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
cleanup = _.compose(
|
if (scope.removeGraphDataReady) {
|
||||||
[ cleanup,
|
scope.removeGraphDataReady();
|
||||||
angular.element($window).bind('resize', function() {
|
}
|
||||||
if($(window).width()<500){
|
scope.removeGraphDataReady = scope.$on('graphDataReady', function (e, data) {
|
||||||
$('.graph-container').height(300);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
var winHeight = $(window).height(),
|
|
||||||
available_height = winHeight - $('#main-menu-container .navbar').outerHeight() - $('#count-container').outerHeight() - 120;
|
|
||||||
$('.graph-container').height(available_height/2);
|
|
||||||
job_status_chart.update();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (scope.removeGraphDataReady) {
|
console.log("building graph", data);
|
||||||
scope.removeGraphDataReady();
|
|
||||||
}
|
|
||||||
scope.removeGraphDataReady = scope.$on('graphDataReady', function (e, data, third) {
|
|
||||||
|
|
||||||
|
var timeFormat, graphData = [
|
||||||
|
{
|
||||||
|
"color": "#00aa00",
|
||||||
|
"key": "Successful",
|
||||||
|
"values": data.jobs.successful
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Failed" ,
|
||||||
|
"color" : "#aa0000",
|
||||||
|
"values": data.jobs.failed
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
var timeFormat, graphData = [
|
if(period==="day"){
|
||||||
{
|
timeFormat="%H:%M";
|
||||||
"color": "#00aa00",
|
}
|
||||||
"key": "Successful",
|
else {
|
||||||
"values": data.jobs.successful
|
timeFormat = '%m/%d';
|
||||||
},
|
}
|
||||||
{
|
graphData.map(function(series) {
|
||||||
"key" : "Failed" ,
|
series.values = series.values.map(function(d) {
|
||||||
"color" : "#aa0000",
|
return {
|
||||||
"values": data.jobs.failed
|
x: d[0],
|
||||||
}
|
y: d[1]
|
||||||
];
|
};
|
||||||
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
|
||||||
nv.addGraph({
|
|
||||||
generate: function() {
|
|
||||||
var width = $('.graph-container').width(), // nv.utils.windowSize().width/3,
|
|
||||||
height = $('.graph-container').height()*0.7; //nv.utils.windowSize().height/5,
|
|
||||||
job_status_chart = nv.models.lineChart()
|
|
||||||
.margin({top: 5, right: 75, bottom: 80, 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!
|
|
||||||
.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
|
|
||||||
// .width(width)
|
|
||||||
// .height(height)
|
|
||||||
;
|
|
||||||
|
|
||||||
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('.job-status-graph svg')
|
|
||||||
.datum(graphData).transition()
|
|
||||||
.attr('width', width)
|
|
||||||
.attr('height', height)
|
|
||||||
.duration(1000)
|
|
||||||
.call(job_status_chart)
|
|
||||||
.style({
|
|
||||||
// 'width': width,
|
|
||||||
// 'height': height,
|
|
||||||
"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(".n")
|
|
||||||
.on("click", function() {
|
|
||||||
period = this.getAttribute("id");
|
|
||||||
$('#period-dropdown').replaceWith("<a id=\"period-dropdown\" role=\"button\" data-toggle=\"dropdown\" data-target=\"#\" href=\"/page.html\">"+this.text+"<span class=\"caret\"><span>\n");
|
|
||||||
|
|
||||||
createGraph(period, job_type);
|
|
||||||
});
|
|
||||||
|
|
||||||
//On click, update with new data
|
|
||||||
d3.selectAll(".m")
|
|
||||||
.on("click", function() {
|
|
||||||
job_type = this.getAttribute("id");
|
|
||||||
$('#type-dropdown').replaceWith("<a id=\"type-dropdown\" role=\"button\" data-toggle=\"dropdown\" data-target=\"#\" href=\"/page.html\">"+this.text+"<span class=\"caret\"><span>\n");
|
|
||||||
|
|
||||||
data
|
|
||||||
createGraph(period, job_type);
|
|
||||||
});
|
|
||||||
|
|
||||||
scope.$emit('WidgetLoaded');
|
|
||||||
return job_status_chart;
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
return series;
|
||||||
|
});
|
||||||
|
|
||||||
|
var job_status_chart = nv.models.lineChart()
|
||||||
|
.margin({top: 5, right: 75, bottom: 80, 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!
|
||||||
|
.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
|
||||||
|
// .width(width)
|
||||||
|
// .height(height)
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
};
|
var width = $('.graph-container').width(); // nv.utils.windowSize().width/3,
|
||||||
|
var height = $('.graph-container').height()*0.7; //nv.utils.windowSize().height/5,
|
||||||
|
|
||||||
|
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).transition()
|
||||||
|
.attr('width', width)
|
||||||
|
.attr('height', height)
|
||||||
|
.duration(1000)
|
||||||
|
.call(job_status_chart)
|
||||||
|
.style({
|
||||||
|
// 'width': width,
|
||||||
|
// 'height': height,
|
||||||
|
"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")[0])
|
||||||
|
.on("click", function() {
|
||||||
|
period = this.getAttribute("id");
|
||||||
|
$('#period-dropdown').replaceWith("<a id=\"period-dropdown\" role=\"button\" data-toggle=\"dropdown\" data-target=\"#\" href=\"/page.html\">"+this.text+"<span class=\"caret\"><span>\n");
|
||||||
|
|
||||||
|
createGraph(period, job_type);
|
||||||
|
});
|
||||||
|
|
||||||
|
//On click, update with new data
|
||||||
|
d3.selectAll(element.find(".m")[0])
|
||||||
|
.on("click", function() {
|
||||||
|
job_type = this.getAttribute("id");
|
||||||
|
$('#type-dropdown').replaceWith("<a id=\"type-dropdown\" role=\"button\" data-toggle=\"dropdown\" data-target=\"#\" href=\"/page.html\">"+this.text+"<span class=\"caret\"><span>\n");
|
||||||
|
|
||||||
|
createGraph(period, job_type);
|
||||||
|
});
|
||||||
|
|
||||||
|
return job_status_chart;
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
}]);
|
}]);
|
||||||
|
|||||||
39
awx/ui/static/partials/job_status_graph.html
Normal file
39
awx/ui/static/partials/job_status_graph.html
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div id="job-status-title" class="h6 col-xs-2 col-sm-3 col-lg-4 text-center"><b>Job Status</b></div>
|
||||||
|
|
||||||
|
<div class="h6 col-xs-5 col-sm-5 col-lg-4">
|
||||||
|
<div class="dropdown">
|
||||||
|
Job Type: <a id="type-dropdown" role="button" data-toggle="dropdown" data-target="#" href="/page.html">
|
||||||
|
All<span class="caret"></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ul class="dropdown-menu" role="menu" aria-labelledby="type-dropdown">
|
||||||
|
<li><a class="m" id="all">All</a></li>
|
||||||
|
<li><a class="m" id="inv_sync">Inventory Sync</a></li>
|
||||||
|
<li><a class="m" id="scm_update">SCM Update</a></li>
|
||||||
|
<li><a class="m" id="playbook_run">Playbook Run</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="h6 col-xs-5 col-sm-4 col-lg-4">
|
||||||
|
<div class="dropdown">
|
||||||
|
Period: <a id="period-dropdown" role="button" data-toggle="dropdown" data-target="#" href="/page.html">
|
||||||
|
Past Month<span class="caret"></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ul class="dropdown-menu" role="menu" aria-labelledby="period-dropdown">
|
||||||
|
<li><a class="n" id="day" >Past 24 Hours </a></li>
|
||||||
|
<li><a class="n" id="week">Past Week</a></li>
|
||||||
|
<li><a class="n" id="month">Past Month</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="job-status-graph"><svg></svg></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
76
awx/ui/tests/unit/directives/job-status-graph-test.js
Normal file
76
awx/ui/tests/unit/directives/job-status-graph-test.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
describe('Job Status Graph Directive', function() {
|
||||||
|
var element, scope, httpBackend;
|
||||||
|
|
||||||
|
beforeEach(module('Tower'));
|
||||||
|
|
||||||
|
beforeEach(module(function($provide) {
|
||||||
|
$provide.value('LoadBasePaths', angular.noop);
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(inject(function($rootScope, $compile, $httpBackend) {
|
||||||
|
httpBackend = $httpBackend;
|
||||||
|
$httpBackend.expectGET('/static/js/local_config.js').respond({
|
||||||
|
});
|
||||||
|
|
||||||
|
$httpBackend.whenGET('/static/partials/job_status_graph.html')
|
||||||
|
.respond("<div class='m'></div><div class='n'></div><div class='job-status-graph'><svg></svg></div>");
|
||||||
|
|
||||||
|
// $httpBackend.whenGET('/api/').respond(200,
|
||||||
|
// {"available_versions": {"v1": "/api/v1/"}, "description": "Ansible Tower REST API", "current_version": "/api/v1/"});
|
||||||
|
|
||||||
|
scope = $rootScope.$new();
|
||||||
|
|
||||||
|
element = '<div job-status-graph class="job-status-graph" data="data"></div>';
|
||||||
|
|
||||||
|
// Takes jobs grouped by result (successful or failure
|
||||||
|
// Then looks at each array of arrays, where index 0 is the timestamp & index 1 is the count of jobs with that status
|
||||||
|
scope.data =
|
||||||
|
{ jobs:
|
||||||
|
{ successful: [[1, 0], [2, 0], [3,0], [4,0], [5,0]],
|
||||||
|
failed: [[1,0],[2,0],[3,0],[4,0],[5,0]]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
element = $compile(element)(scope);
|
||||||
|
scope.$digest();
|
||||||
|
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
httpBackend.verifyNoOutstandingRequest();
|
||||||
|
});
|
||||||
|
|
||||||
|
function filterDataSeries(key, data) {
|
||||||
|
return data.map(function(datum) {
|
||||||
|
return datum.values;
|
||||||
|
})[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
it('uses successes & failures from scope', function() {
|
||||||
|
var chartContainer = d3.select(element.find('svg')[0]);
|
||||||
|
var lineData = chartContainer.datum();
|
||||||
|
|
||||||
|
var successfulSeries = filterDataSeries(0, lineData);
|
||||||
|
var failedSeries = filterDataSeries(1, lineData);
|
||||||
|
|
||||||
|
console.log("test done");
|
||||||
|
expect(successfulSeries).to.eql(
|
||||||
|
[ {x: 1, y: 0, series: 0},
|
||||||
|
{x: 2, y: 0, series: 0},
|
||||||
|
{x: 3, y: 0, series: 0},
|
||||||
|
{x: 4, y: 0, series: 0},
|
||||||
|
{x: 5, y: 0, series: 0}]);
|
||||||
|
|
||||||
|
expect(failedSeries).to.eql(
|
||||||
|
[ {x: 1, y: 0, series: 1},
|
||||||
|
{x: 2, y: 0, series: 1},
|
||||||
|
{x: 3, y: 0, series: 1},
|
||||||
|
{x: 4, y: 0, series: 1},
|
||||||
|
{x: 5, y: 0, series: 1}]);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
Reference in New Issue
Block a user