Complete user interface rewrite
This commit introduces the first iteration of a vast web UI
refactor.
The home page now highlights the data recorded by ARA and
the core of the UI now revolves around the one and single
playbook reports page.
There were three main objectives with this UI work:
- Improve UX (ex: being able to search, find & sort things easily
* Everything is now searchable and sortable
* Browsing tips have been added to help users get the most out
of the interface features
- Improve scalability and performance: the interface should be
fast and easy to browse whether you have dozens or thousands
of hosts and tasks
* Every result list or table are now paginated
* You can customize pagination preferences with the
ARA_PLAYBOOK_PER_PAGE and ARA_RESULT_PER_PAGE
configuration parameters.
- Improve static generation time and weight
Examples of the same data sets before and after:
* ARA integration tests (5 playbooks, 59 tasks, 69 results):
* Before: 5.4 seconds, 1.6MB (gzipped), 217 files
* After: 2 seconds, 1.2MB (gzipped), 119 files
* OpenStack-Ansible (1 playbook, 1547 tasks, 1667 results):
* Before: 6m21 seconds, 31MB (gzipped), 3710 files
* After: 20 seconds, 8.9MB (gzipped), 1916 files
Misc:
- Jinja HTML templates are now fully indented with no regards
to line length or PEP8 to privilege readability over long and
nested content.
- Added some missing web application unit tests
- Various javascript and css optimizations
- The web application backend in itself was significantly
simplified: less routes, less templates, less code
- Added a configuration parameter ARA_PLAYBOOK_PER_PAGE which
controls the amount of playbooks per page in the playbook
report list.
- Added a configuration parameter ARA_RESULT_PER_PAGE which
controls the amount of results per page in the data results
table (such as hosts, plays and tasks).
Known issues:
- The file list table in the file panel will eventually
be replaced by a folder/file hierarchy tree
Change-Id: I3802562ae1593a9dbb549d2654d63d00fd0c8310
This commit is contained in:
@@ -23,6 +23,8 @@ DEFAULT_ARA_LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
DEFAULT_ARA_SQL_DEBUG = False
|
||||
DEFAULT_ARA_PATH_MAX = 40
|
||||
DEFAULT_ARA_IGNORE_MIMETYPE_WARNINGS = True
|
||||
DEFAULT_ARA_PLAYBOOK_PER_PAGE = 10
|
||||
DEFAULT_ARA_RESULT_PER_PAGE = 25
|
||||
|
||||
config, path = load_config_file()
|
||||
|
||||
@@ -42,6 +44,14 @@ try:
|
||||
ARA_PLAYBOOK_OVERRIDE = get_config(
|
||||
config, 'ara', 'playbook_override', 'ARA_PLAYBOOK_OVERRIDE',
|
||||
None, islist=True)
|
||||
ARA_PLAYBOOK_PER_PAGE = get_config(
|
||||
config, 'ara', 'playbook_per_page', 'ARA_PLAYBOOK_PER_PAGE',
|
||||
DEFAULT_ARA_PLAYBOOK_PER_PAGE, integer=True
|
||||
)
|
||||
ARA_RESULT_PER_PAGE = get_config(
|
||||
config, 'ara', 'result_per_page', 'ARA_RESULT_PER_PAGE',
|
||||
DEFAULT_ARA_RESULT_PER_PAGE, integer=True
|
||||
)
|
||||
except TypeError:
|
||||
ARA_TMP_DIR = get_config(
|
||||
config, 'defaults', 'local_tmp', 'ANSIBLE_LOCAL_TEMP',
|
||||
@@ -49,6 +59,14 @@ except TypeError:
|
||||
ARA_PLAYBOOK_OVERRIDE = get_config(
|
||||
config, 'ara', 'playbook_override', 'ARA_PLAYBOOK_OVERRIDE',
|
||||
None, value_type='list')
|
||||
ARA_PLAYBOOK_PER_PAGE = get_config(
|
||||
config, 'ara', 'playbook_per_page', 'ARA_PLAYBOOK_PER_PAGE',
|
||||
DEFAULT_ARA_PLAYBOOK_PER_PAGE, value_type='integer'
|
||||
)
|
||||
ARA_RESULT_PER_PAGE = get_config(
|
||||
config, 'ara', 'result_per_page', 'ARA_RESULT_PER_PAGE',
|
||||
DEFAULT_ARA_RESULT_PER_PAGE, value_type='integer'
|
||||
)
|
||||
ARA_LOG_FILE = get_config(
|
||||
config, 'ara', 'logfile', 'ARA_LOG_FILE',
|
||||
DEFAULT_ARA_LOG_FILE)
|
||||
|
||||
@@ -1,43 +1,23 @@
|
||||
/* Color for highlighted line */
|
||||
.hll {
|
||||
background-color: #39a5dc;
|
||||
color: white;
|
||||
/* Main container layout size and offset */
|
||||
.col-md-custom {
|
||||
-webkit-box-flex: 0;
|
||||
-webkit-flex: 0 0 95%;
|
||||
-ms-flex: 0 0 95%;
|
||||
flex: 0 0 95%;
|
||||
max-width: 95%;
|
||||
}
|
||||
|
||||
table.table.table-striped tr.active {
|
||||
/*
|
||||
This doesn't actually work as intended, it doesn't set the actual color...
|
||||
But it still changes the row's color to highlight it, let's fix it later.
|
||||
*/
|
||||
background-color: blue;
|
||||
.col-md-offset-custom {
|
||||
margin-left: 2.5%;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
padding-left: 6px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.action-available {
|
||||
color: #008000;
|
||||
}
|
||||
|
||||
.action-unavailable {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* Wrap pre tags so they don't print endlessly horizontally */
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
white-space: -moz-pre-wrap;
|
||||
white-space: -pre-wrap;
|
||||
white-space: -o-pre-wrap;
|
||||
word-wrap: break-word;
|
||||
/* Home stat highlight badges */
|
||||
.stat-highlight {
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Status styling */
|
||||
/* Status text styling */
|
||||
.status-label {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
@@ -61,6 +41,17 @@ pre {
|
||||
}
|
||||
|
||||
/* Table styling */
|
||||
/* Wrap pre tags so they don't print endlessly horizontally */
|
||||
.table pre, .results pre {
|
||||
white-space: pre-wrap;
|
||||
white-space: -moz-pre-wrap;
|
||||
white-space: -pre-wrap;
|
||||
white-space: -o-pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table th {
|
||||
font-size: 120%;
|
||||
}
|
||||
@@ -69,15 +60,36 @@ pre {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td a {
|
||||
width:100%;;
|
||||
.table td a {
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.date td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table tbody>tr>td.vert-align{
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Tooltips */
|
||||
.tooltip-inner {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Status icon on left side of report list */
|
||||
.list-view-pf-left {
|
||||
max-width: 36px;
|
||||
}
|
||||
|
||||
/* Width of date on left side of report list */
|
||||
.timestamp-heading {
|
||||
max-width: 125px;
|
||||
}
|
||||
|
||||
/* Combined width of date and playbook path in report list */
|
||||
.list-view-pf-description {
|
||||
max-width: 40%;
|
||||
}
|
||||
|
||||
/* Combined width of playbook highlights/panel items in report list */
|
||||
.list-view-pf-additional-info {
|
||||
max-width: 70%;
|
||||
}
|
||||
BIN
ara/static/images/ara.png
Executable file
BIN
ara/static/images/ara.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
1
ara/static/images/logo-header.svg
Normal file
1
ara/static/images/logo-header.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 6.9 KiB |
1
ara/static/images/logo-main.svg
Normal file
1
ara/static/images/logo-main.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 7.0 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 7.9 KiB |
@@ -1,76 +1,65 @@
|
||||
/* Taken from http://stackoverflow.com/a/13067009 */
|
||||
(function(document, history, location) {
|
||||
var HISTORY_SUPPORT = !!(history && history.pushState);
|
||||
$(document).ready(function () {
|
||||
// Toggle dropdown menu
|
||||
$(".list-view-pf-actions").on("show.bs.dropdown", function () {
|
||||
var $this = $(this);
|
||||
var $dropdown = $this.find(".dropdown");
|
||||
var space = $(window).height() - $dropdown[0].getBoundingClientRect().top - $this.find(".dropdown-menu").outerHeight(true);
|
||||
$dropdown.toggleClass("dropup", space < 10);
|
||||
});
|
||||
|
||||
var anchorScrolls = {
|
||||
ANCHOR_REGEX: /^#[^ ]+$/,
|
||||
OFFSET_HEIGHT_PX: 12,
|
||||
// Compound expansion
|
||||
$(".list-view-pf-expand").on("click", function () {
|
||||
var $this = $(this);
|
||||
var $heading = $(this).parents(".list-group-item");
|
||||
//var $row = $heading.parent();
|
||||
var $subPanels = $heading.find(".list-group-item-container");
|
||||
var index = $heading.find(".list-view-pf-expand").index(this);
|
||||
|
||||
/**
|
||||
* Establish events, and fix initial scroll position if a hash is provided.
|
||||
*/
|
||||
init: function() {
|
||||
this.scrollToCurrent();
|
||||
$(window).on('hashchange', $.proxy(this, 'scrollToCurrent'));
|
||||
$('body').on('click', 'a', $.proxy(this, 'delegateAnchors'));
|
||||
},
|
||||
// Remove all active status
|
||||
$heading.find(".list-view-pf-expand.active").find(".fa-angle-right").removeClass("fa-angle-down")
|
||||
.end().removeClass("active")
|
||||
.end().removeClass("list-view-pf-expand-active");
|
||||
|
||||
/**
|
||||
* Return the offset amount to deduct from the normal scroll position.
|
||||
* Modify as appropriate to allow for dynamic calculations
|
||||
*/
|
||||
getFixedOffset: function() {
|
||||
return this.OFFSET_HEIGHT_PX;
|
||||
},
|
||||
// Add active to the clicked item
|
||||
$(this).addClass("active")
|
||||
.parents(".list-group-item").addClass("list-view-pf-expand-active")
|
||||
.end().find(".fa-angle-right").addClass("fa-angle-down");
|
||||
|
||||
/**
|
||||
* If the provided href is an anchor which resolves to an element on the
|
||||
* page, scroll to it.
|
||||
* @param {String} href
|
||||
* @return {Boolean} - Was the href an anchor.
|
||||
*/
|
||||
scrollIfAnchor: function(href, pushToHistory) {
|
||||
var match, anchorOffset;
|
||||
|
||||
if(!this.ANCHOR_REGEX.test(href)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
match = document.getElementById(href.slice(1));
|
||||
|
||||
if(match) {
|
||||
anchorOffset = $(match).offset().top - this.getFixedOffset();
|
||||
$('html, body').animate({ scrollTop: anchorOffset});
|
||||
|
||||
// Add the state to history as-per normal anchor links
|
||||
if(HISTORY_SUPPORT && pushToHistory) {
|
||||
history.pushState({}, document.title, location.pathname + href);
|
||||
// Check if it needs to hide
|
||||
if($subPanels.eq(index).hasClass("hidden")) {
|
||||
$heading.find(".list-group-item-container:visible").addClass("hidden");
|
||||
$subPanels.eq(index).removeClass("hidden");
|
||||
} else {
|
||||
$subPanels.eq(index).addClass("hidden");
|
||||
$heading.find(".list-view-pf-expand.active").find(".fa-angle-right").removeClass("fa-angle-down")
|
||||
.end().removeClass("active")
|
||||
.end().removeClass("list-view-pf-expand-active");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return !!match;
|
||||
},
|
||||
// Click close button to close the panel
|
||||
$(".list-group-item-container .close").on("click", function () {
|
||||
var $this = $(this);
|
||||
var $panel = $this.parent();
|
||||
|
||||
/**
|
||||
* Attempt to scroll to the current location's hash.
|
||||
*/
|
||||
scrollToCurrent: function(e) {
|
||||
if(this.scrollIfAnchor(window.location.hash) && e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
// Close the container and remove the active status
|
||||
$panel.addClass("hidden")
|
||||
.parent().removeClass("list-view-pf-expand-active")
|
||||
.find(".list-view-pf-expand.active").removeClass("active")
|
||||
.find(".fa-angle-right").removeClass("fa-angle-down")
|
||||
});
|
||||
|
||||
/**
|
||||
* If the click event's target was an anchor, fix the scroll position.
|
||||
*/
|
||||
delegateAnchors: function(e) {
|
||||
var elem = e.target;
|
||||
// Initialize tooltips
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
if(this.scrollIfAnchor(elem.getAttribute('href'), true)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
// Highlight the anchor line in file views
|
||||
var hash = $(location).attr('hash');
|
||||
$(hash).closest('span').addClass('hll');
|
||||
|
||||
$(document).ready($.proxy(anchorScrolls, 'init'));
|
||||
})(window.document, window.history, window.location);
|
||||
// Refresh the highlighted line when clicking on a new line in file views
|
||||
$('a').click(function(){
|
||||
$("span.hll").removeClass('hll');
|
||||
var hash = $(this).attr('href');
|
||||
$(hash).closest('span').addClass('hll');
|
||||
});
|
||||
});
|
||||
|
||||
167
ara/static/js/jquery.dataTables.min.js
vendored
Normal file
167
ara/static/js/jquery.dataTables.min.js
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
/*!
|
||||
DataTables 1.10.13
|
||||
©2008-2016 SpryMedia Ltd - datatables.net/license
|
||||
*/
|
||||
(function(h){"function"===typeof define&&define.amd?define(["jquery"],function(E){return h(E,window,document)}):"object"===typeof exports?module.exports=function(E,H){E||(E=window);H||(H="undefined"!==typeof window?require("jquery"):require("jquery")(E));return h(H,E,E.document)}:h(jQuery,window,document)})(function(h,E,H,k){function Y(a){var b,c,d={};h.each(a,function(e){if((b=e.match(/^([^A-Z]+?)([A-Z])/))&&-1!=="a aa ai ao as b fn i m o s ".indexOf(b[1]+" "))c=e.replace(b[0],b[2].toLowerCase()),
|
||||
d[c]=e,"o"===b[1]&&Y(a[e])});a._hungarianMap=d}function J(a,b,c){a._hungarianMap||Y(a);var d;h.each(b,function(e){d=a._hungarianMap[e];if(d!==k&&(c||b[d]===k))"o"===d.charAt(0)?(b[d]||(b[d]={}),h.extend(!0,b[d],b[e]),J(a[d],b[d],c)):b[d]=b[e]})}function Fa(a){var b=m.defaults.oLanguage,c=a.sZeroRecords;!a.sEmptyTable&&(c&&"No data available in table"===b.sEmptyTable)&&F(a,a,"sZeroRecords","sEmptyTable");!a.sLoadingRecords&&(c&&"Loading..."===b.sLoadingRecords)&&F(a,a,"sZeroRecords","sLoadingRecords");
|
||||
a.sInfoThousands&&(a.sThousands=a.sInfoThousands);(a=a.sDecimal)&&fb(a)}function gb(a){A(a,"ordering","bSort");A(a,"orderMulti","bSortMulti");A(a,"orderClasses","bSortClasses");A(a,"orderCellsTop","bSortCellsTop");A(a,"order","aaSorting");A(a,"orderFixed","aaSortingFixed");A(a,"paging","bPaginate");A(a,"pagingType","sPaginationType");A(a,"pageLength","iDisplayLength");A(a,"searching","bFilter");"boolean"===typeof a.sScrollX&&(a.sScrollX=a.sScrollX?"100%":"");"boolean"===typeof a.scrollX&&(a.scrollX=
|
||||
a.scrollX?"100%":"");if(a=a.aoSearchCols)for(var b=0,c=a.length;b<c;b++)a[b]&&J(m.models.oSearch,a[b])}function hb(a){A(a,"orderable","bSortable");A(a,"orderData","aDataSort");A(a,"orderSequence","asSorting");A(a,"orderDataType","sortDataType");var b=a.aDataSort;b&&!h.isArray(b)&&(a.aDataSort=[b])}function ib(a){if(!m.__browser){var b={};m.__browser=b;var c=h("<div/>").css({position:"fixed",top:0,left:-1*h(E).scrollLeft(),height:1,width:1,overflow:"hidden"}).append(h("<div/>").css({position:"absolute",
|
||||
top:1,left:1,width:100,overflow:"scroll"}).append(h("<div/>").css({width:"100%",height:10}))).appendTo("body"),d=c.children(),e=d.children();b.barWidth=d[0].offsetWidth-d[0].clientWidth;b.bScrollOversize=100===e[0].offsetWidth&&100!==d[0].clientWidth;b.bScrollbarLeft=1!==Math.round(e.offset().left);b.bBounding=c[0].getBoundingClientRect().width?!0:!1;c.remove()}h.extend(a.oBrowser,m.__browser);a.oScroll.iBarWidth=m.__browser.barWidth}function jb(a,b,c,d,e,f){var g,j=!1;c!==k&&(g=c,j=!0);for(;d!==
|
||||
e;)a.hasOwnProperty(d)&&(g=j?b(g,a[d],d,a):a[d],j=!0,d+=f);return g}function Ga(a,b){var c=m.defaults.column,d=a.aoColumns.length,c=h.extend({},m.models.oColumn,c,{nTh:b?b:H.createElement("th"),sTitle:c.sTitle?c.sTitle:b?b.innerHTML:"",aDataSort:c.aDataSort?c.aDataSort:[d],mData:c.mData?c.mData:d,idx:d});a.aoColumns.push(c);c=a.aoPreSearchCols;c[d]=h.extend({},m.models.oSearch,c[d]);la(a,d,h(b).data())}function la(a,b,c){var b=a.aoColumns[b],d=a.oClasses,e=h(b.nTh);if(!b.sWidthOrig){b.sWidthOrig=
|
||||
e.attr("width")||null;var f=(e.attr("style")||"").match(/width:\s*(\d+[pxem%]+)/);f&&(b.sWidthOrig=f[1])}c!==k&&null!==c&&(hb(c),J(m.defaults.column,c),c.mDataProp!==k&&!c.mData&&(c.mData=c.mDataProp),c.sType&&(b._sManualType=c.sType),c.className&&!c.sClass&&(c.sClass=c.className),h.extend(b,c),F(b,c,"sWidth","sWidthOrig"),c.iDataSort!==k&&(b.aDataSort=[c.iDataSort]),F(b,c,"aDataSort"));var g=b.mData,j=R(g),i=b.mRender?R(b.mRender):null,c=function(a){return"string"===typeof a&&-1!==a.indexOf("@")};
|
||||
b._bAttrSrc=h.isPlainObject(g)&&(c(g.sort)||c(g.type)||c(g.filter));b._setter=null;b.fnGetData=function(a,b,c){var d=j(a,b,k,c);return i&&b?i(d,b,a,c):d};b.fnSetData=function(a,b,c){return S(g)(a,b,c)};"number"!==typeof g&&(a._rowReadObject=!0);a.oFeatures.bSort||(b.bSortable=!1,e.addClass(d.sSortableNone));a=-1!==h.inArray("asc",b.asSorting);c=-1!==h.inArray("desc",b.asSorting);!b.bSortable||!a&&!c?(b.sSortingClass=d.sSortableNone,b.sSortingClassJUI=""):a&&!c?(b.sSortingClass=d.sSortableAsc,b.sSortingClassJUI=
|
||||
d.sSortJUIAscAllowed):!a&&c?(b.sSortingClass=d.sSortableDesc,b.sSortingClassJUI=d.sSortJUIDescAllowed):(b.sSortingClass=d.sSortable,b.sSortingClassJUI=d.sSortJUI)}function Z(a){if(!1!==a.oFeatures.bAutoWidth){var b=a.aoColumns;Ha(a);for(var c=0,d=b.length;c<d;c++)b[c].nTh.style.width=b[c].sWidth}b=a.oScroll;(""!==b.sY||""!==b.sX)&&ma(a);s(a,null,"column-sizing",[a])}function $(a,b){var c=na(a,"bVisible");return"number"===typeof c[b]?c[b]:null}function aa(a,b){var c=na(a,"bVisible"),c=h.inArray(b,
|
||||
c);return-1!==c?c:null}function ba(a){var b=0;h.each(a.aoColumns,function(a,d){d.bVisible&&"none"!==h(d.nTh).css("display")&&b++});return b}function na(a,b){var c=[];h.map(a.aoColumns,function(a,e){a[b]&&c.push(e)});return c}function Ia(a){var b=a.aoColumns,c=a.aoData,d=m.ext.type.detect,e,f,g,j,i,h,l,q,r;e=0;for(f=b.length;e<f;e++)if(l=b[e],r=[],!l.sType&&l._sManualType)l.sType=l._sManualType;else if(!l.sType){g=0;for(j=d.length;g<j;g++){i=0;for(h=c.length;i<h;i++){r[i]===k&&(r[i]=B(a,i,e,"type"));
|
||||
q=d[g](r[i],a);if(!q&&g!==d.length-1)break;if("html"===q)break}if(q){l.sType=q;break}}l.sType||(l.sType="string")}}function kb(a,b,c,d){var e,f,g,j,i,n,l=a.aoColumns;if(b)for(e=b.length-1;0<=e;e--){n=b[e];var q=n.targets!==k?n.targets:n.aTargets;h.isArray(q)||(q=[q]);f=0;for(g=q.length;f<g;f++)if("number"===typeof q[f]&&0<=q[f]){for(;l.length<=q[f];)Ga(a);d(q[f],n)}else if("number"===typeof q[f]&&0>q[f])d(l.length+q[f],n);else if("string"===typeof q[f]){j=0;for(i=l.length;j<i;j++)("_all"==q[f]||h(l[j].nTh).hasClass(q[f]))&&
|
||||
d(j,n)}}if(c){e=0;for(a=c.length;e<a;e++)d(e,c[e])}}function N(a,b,c,d){var e=a.aoData.length,f=h.extend(!0,{},m.models.oRow,{src:c?"dom":"data",idx:e});f._aData=b;a.aoData.push(f);for(var g=a.aoColumns,j=0,i=g.length;j<i;j++)g[j].sType=null;a.aiDisplayMaster.push(e);b=a.rowIdFn(b);b!==k&&(a.aIds[b]=f);(c||!a.oFeatures.bDeferRender)&&Ja(a,e,c,d);return e}function oa(a,b){var c;b instanceof h||(b=h(b));return b.map(function(b,e){c=Ka(a,e);return N(a,c.data,e,c.cells)})}function B(a,b,c,d){var e=a.iDraw,
|
||||
f=a.aoColumns[c],g=a.aoData[b]._aData,j=f.sDefaultContent,i=f.fnGetData(g,d,{settings:a,row:b,col:c});if(i===k)return a.iDrawError!=e&&null===j&&(K(a,0,"Requested unknown parameter "+("function"==typeof f.mData?"{function}":"'"+f.mData+"'")+" for row "+b+", column "+c,4),a.iDrawError=e),j;if((i===g||null===i)&&null!==j&&d!==k)i=j;else if("function"===typeof i)return i.call(g);return null===i&&"display"==d?"":i}function lb(a,b,c,d){a.aoColumns[c].fnSetData(a.aoData[b]._aData,d,{settings:a,row:b,col:c})}
|
||||
function La(a){return h.map(a.match(/(\\.|[^\.])+/g)||[""],function(a){return a.replace(/\\\./g,".")})}function R(a){if(h.isPlainObject(a)){var b={};h.each(a,function(a,c){c&&(b[a]=R(c))});return function(a,c,f,g){var j=b[c]||b._;return j!==k?j(a,c,f,g):a}}if(null===a)return function(a){return a};if("function"===typeof a)return function(b,c,f,g){return a(b,c,f,g)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var c=function(a,b,f){var g,j;if(""!==f){j=La(f);
|
||||
for(var i=0,n=j.length;i<n;i++){f=j[i].match(ca);g=j[i].match(V);if(f){j[i]=j[i].replace(ca,"");""!==j[i]&&(a=a[j[i]]);g=[];j.splice(0,i+1);j=j.join(".");if(h.isArray(a)){i=0;for(n=a.length;i<n;i++)g.push(c(a[i],b,j))}a=f[0].substring(1,f[0].length-1);a=""===a?g:g.join(a);break}else if(g){j[i]=j[i].replace(V,"");a=a[j[i]]();continue}if(null===a||a[j[i]]===k)return k;a=a[j[i]]}}return a};return function(b,e){return c(b,e,a)}}return function(b){return b[a]}}function S(a){if(h.isPlainObject(a))return S(a._);
|
||||
if(null===a)return function(){};if("function"===typeof a)return function(b,d,e){a(b,"set",d,e)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var b=function(a,d,e){var e=La(e),f;f=e[e.length-1];for(var g,j,i=0,n=e.length-1;i<n;i++){g=e[i].match(ca);j=e[i].match(V);if(g){e[i]=e[i].replace(ca,"");a[e[i]]=[];f=e.slice();f.splice(0,i+1);g=f.join(".");if(h.isArray(d)){j=0;for(n=d.length;j<n;j++)f={},b(f,d[j],g),a[e[i]].push(f)}else a[e[i]]=d;return}j&&(e[i]=e[i].replace(V,
|
||||
""),a=a[e[i]](d));if(null===a[e[i]]||a[e[i]]===k)a[e[i]]={};a=a[e[i]]}if(f.match(V))a[f.replace(V,"")](d);else a[f.replace(ca,"")]=d};return function(c,d){return b(c,d,a)}}return function(b,d){b[a]=d}}function Ma(a){return D(a.aoData,"_aData")}function pa(a){a.aoData.length=0;a.aiDisplayMaster.length=0;a.aiDisplay.length=0;a.aIds={}}function qa(a,b,c){for(var d=-1,e=0,f=a.length;e<f;e++)a[e]==b?d=e:a[e]>b&&a[e]--; -1!=d&&c===k&&a.splice(d,1)}function da(a,b,c,d){var e=a.aoData[b],f,g=function(c,d){for(;c.childNodes.length;)c.removeChild(c.firstChild);
|
||||
c.innerHTML=B(a,b,d,"display")};if("dom"===c||(!c||"auto"===c)&&"dom"===e.src)e._aData=Ka(a,e,d,d===k?k:e._aData).data;else{var j=e.anCells;if(j)if(d!==k)g(j[d],d);else{c=0;for(f=j.length;c<f;c++)g(j[c],c)}}e._aSortData=null;e._aFilterData=null;g=a.aoColumns;if(d!==k)g[d].sType=null;else{c=0;for(f=g.length;c<f;c++)g[c].sType=null;Na(a,e)}}function Ka(a,b,c,d){var e=[],f=b.firstChild,g,j,i=0,n,l=a.aoColumns,q=a._rowReadObject,d=d!==k?d:q?{}:[],r=function(a,b){if("string"===typeof a){var c=a.indexOf("@");
|
||||
-1!==c&&(c=a.substring(c+1),S(a)(d,b.getAttribute(c)))}},m=function(a){if(c===k||c===i)j=l[i],n=h.trim(a.innerHTML),j&&j._bAttrSrc?(S(j.mData._)(d,n),r(j.mData.sort,a),r(j.mData.type,a),r(j.mData.filter,a)):q?(j._setter||(j._setter=S(j.mData)),j._setter(d,n)):d[i]=n;i++};if(f)for(;f;){g=f.nodeName.toUpperCase();if("TD"==g||"TH"==g)m(f),e.push(f);f=f.nextSibling}else{e=b.anCells;f=0;for(g=e.length;f<g;f++)m(e[f])}if(b=b.firstChild?b:b.nTr)(b=b.getAttribute("id"))&&S(a.rowId)(d,b);return{data:d,cells:e}}
|
||||
function Ja(a,b,c,d){var e=a.aoData[b],f=e._aData,g=[],j,i,n,l,q;if(null===e.nTr){j=c||H.createElement("tr");e.nTr=j;e.anCells=g;j._DT_RowIndex=b;Na(a,e);l=0;for(q=a.aoColumns.length;l<q;l++){n=a.aoColumns[l];i=c?d[l]:H.createElement(n.sCellType);i._DT_CellIndex={row:b,column:l};g.push(i);if((!c||n.mRender||n.mData!==l)&&(!h.isPlainObject(n.mData)||n.mData._!==l+".display"))i.innerHTML=B(a,b,l,"display");n.sClass&&(i.className+=" "+n.sClass);n.bVisible&&!c?j.appendChild(i):!n.bVisible&&c&&i.parentNode.removeChild(i);
|
||||
n.fnCreatedCell&&n.fnCreatedCell.call(a.oInstance,i,B(a,b,l),f,b,l)}s(a,"aoRowCreatedCallback",null,[j,f,b])}e.nTr.setAttribute("role","row")}function Na(a,b){var c=b.nTr,d=b._aData;if(c){var e=a.rowIdFn(d);e&&(c.id=e);d.DT_RowClass&&(e=d.DT_RowClass.split(" "),b.__rowc=b.__rowc?sa(b.__rowc.concat(e)):e,h(c).removeClass(b.__rowc.join(" ")).addClass(d.DT_RowClass));d.DT_RowAttr&&h(c).attr(d.DT_RowAttr);d.DT_RowData&&h(c).data(d.DT_RowData)}}function mb(a){var b,c,d,e,f,g=a.nTHead,j=a.nTFoot,i=0===
|
||||
h("th, td",g).length,n=a.oClasses,l=a.aoColumns;i&&(e=h("<tr/>").appendTo(g));b=0;for(c=l.length;b<c;b++)f=l[b],d=h(f.nTh).addClass(f.sClass),i&&d.appendTo(e),a.oFeatures.bSort&&(d.addClass(f.sSortingClass),!1!==f.bSortable&&(d.attr("tabindex",a.iTabIndex).attr("aria-controls",a.sTableId),Oa(a,f.nTh,b))),f.sTitle!=d[0].innerHTML&&d.html(f.sTitle),Pa(a,"header")(a,d,f,n);i&&ea(a.aoHeader,g);h(g).find(">tr").attr("role","row");h(g).find(">tr>th, >tr>td").addClass(n.sHeaderTH);h(j).find(">tr>th, >tr>td").addClass(n.sFooterTH);
|
||||
if(null!==j){a=a.aoFooter[0];b=0;for(c=a.length;b<c;b++)f=l[b],f.nTf=a[b].cell,f.sClass&&h(f.nTf).addClass(f.sClass)}}function fa(a,b,c){var d,e,f,g=[],j=[],i=a.aoColumns.length,n;if(b){c===k&&(c=!1);d=0;for(e=b.length;d<e;d++){g[d]=b[d].slice();g[d].nTr=b[d].nTr;for(f=i-1;0<=f;f--)!a.aoColumns[f].bVisible&&!c&&g[d].splice(f,1);j.push([])}d=0;for(e=g.length;d<e;d++){if(a=g[d].nTr)for(;f=a.firstChild;)a.removeChild(f);f=0;for(b=g[d].length;f<b;f++)if(n=i=1,j[d][f]===k){a.appendChild(g[d][f].cell);
|
||||
for(j[d][f]=1;g[d+i]!==k&&g[d][f].cell==g[d+i][f].cell;)j[d+i][f]=1,i++;for(;g[d][f+n]!==k&&g[d][f].cell==g[d][f+n].cell;){for(c=0;c<i;c++)j[d+c][f+n]=1;n++}h(g[d][f].cell).attr("rowspan",i).attr("colspan",n)}}}}function O(a){var b=s(a,"aoPreDrawCallback","preDraw",[a]);if(-1!==h.inArray(!1,b))C(a,!1);else{var b=[],c=0,d=a.asStripeClasses,e=d.length,f=a.oLanguage,g=a.iInitDisplayStart,j="ssp"==y(a),i=a.aiDisplay;a.bDrawing=!0;g!==k&&-1!==g&&(a._iDisplayStart=j?g:g>=a.fnRecordsDisplay()?0:g,a.iInitDisplayStart=
|
||||
-1);var g=a._iDisplayStart,n=a.fnDisplayEnd();if(a.bDeferLoading)a.bDeferLoading=!1,a.iDraw++,C(a,!1);else if(j){if(!a.bDestroying&&!nb(a))return}else a.iDraw++;if(0!==i.length){f=j?a.aoData.length:n;for(j=j?0:g;j<f;j++){var l=i[j],q=a.aoData[l];null===q.nTr&&Ja(a,l);l=q.nTr;if(0!==e){var r=d[c%e];q._sRowStripe!=r&&(h(l).removeClass(q._sRowStripe).addClass(r),q._sRowStripe=r)}s(a,"aoRowCallback",null,[l,q._aData,c,j]);b.push(l);c++}}else c=f.sZeroRecords,1==a.iDraw&&"ajax"==y(a)?c=f.sLoadingRecords:
|
||||
f.sEmptyTable&&0===a.fnRecordsTotal()&&(c=f.sEmptyTable),b[0]=h("<tr/>",{"class":e?d[0]:""}).append(h("<td />",{valign:"top",colSpan:ba(a),"class":a.oClasses.sRowEmpty}).html(c))[0];s(a,"aoHeaderCallback","header",[h(a.nTHead).children("tr")[0],Ma(a),g,n,i]);s(a,"aoFooterCallback","footer",[h(a.nTFoot).children("tr")[0],Ma(a),g,n,i]);d=h(a.nTBody);d.children().detach();d.append(h(b));s(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=!1}}function T(a,b){var c=a.oFeatures,d=c.bFilter;
|
||||
c.bSort&&ob(a);d?ga(a,a.oPreviousSearch):a.aiDisplay=a.aiDisplayMaster.slice();!0!==b&&(a._iDisplayStart=0);a._drawHold=b;O(a);a._drawHold=!1}function pb(a){var b=a.oClasses,c=h(a.nTable),c=h("<div/>").insertBefore(c),d=a.oFeatures,e=h("<div/>",{id:a.sTableId+"_wrapper","class":b.sWrapper+(a.nTFoot?"":" "+b.sNoFooter)});a.nHolding=c[0];a.nTableWrapper=e[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var f=a.sDom.split(""),g,j,i,n,l,q,k=0;k<f.length;k++){g=null;j=f[k];if("<"==j){i=h("<div/>")[0];
|
||||
n=f[k+1];if("'"==n||'"'==n){l="";for(q=2;f[k+q]!=n;)l+=f[k+q],q++;"H"==l?l=b.sJUIHeader:"F"==l&&(l=b.sJUIFooter);-1!=l.indexOf(".")?(n=l.split("."),i.id=n[0].substr(1,n[0].length-1),i.className=n[1]):"#"==l.charAt(0)?i.id=l.substr(1,l.length-1):i.className=l;k+=q}e.append(i);e=h(i)}else if(">"==j)e=e.parent();else if("l"==j&&d.bPaginate&&d.bLengthChange)g=qb(a);else if("f"==j&&d.bFilter)g=rb(a);else if("r"==j&&d.bProcessing)g=sb(a);else if("t"==j)g=tb(a);else if("i"==j&&d.bInfo)g=ub(a);else if("p"==
|
||||
j&&d.bPaginate)g=vb(a);else if(0!==m.ext.feature.length){i=m.ext.feature;q=0;for(n=i.length;q<n;q++)if(j==i[q].cFeature){g=i[q].fnInit(a);break}}g&&(i=a.aanFeatures,i[j]||(i[j]=[]),i[j].push(g),e.append(g))}c.replaceWith(e);a.nHolding=null}function ea(a,b){var c=h(b).children("tr"),d,e,f,g,j,i,n,l,q,k;a.splice(0,a.length);f=0;for(i=c.length;f<i;f++)a.push([]);f=0;for(i=c.length;f<i;f++){d=c[f];for(e=d.firstChild;e;){if("TD"==e.nodeName.toUpperCase()||"TH"==e.nodeName.toUpperCase()){l=1*e.getAttribute("colspan");
|
||||
q=1*e.getAttribute("rowspan");l=!l||0===l||1===l?1:l;q=!q||0===q||1===q?1:q;g=0;for(j=a[f];j[g];)g++;n=g;k=1===l?!0:!1;for(j=0;j<l;j++)for(g=0;g<q;g++)a[f+g][n+j]={cell:e,unique:k},a[f+g].nTr=d}e=e.nextSibling}}}function ta(a,b,c){var d=[];c||(c=a.aoHeader,b&&(c=[],ea(c,b)));for(var b=0,e=c.length;b<e;b++)for(var f=0,g=c[b].length;f<g;f++)if(c[b][f].unique&&(!d[f]||!a.bSortCellsTop))d[f]=c[b][f].cell;return d}function ua(a,b,c){s(a,"aoServerParams","serverParams",[b]);if(b&&h.isArray(b)){var d={},
|
||||
e=/(.*?)\[\]$/;h.each(b,function(a,b){var c=b.name.match(e);c?(c=c[0],d[c]||(d[c]=[]),d[c].push(b.value)):d[b.name]=b.value});b=d}var f,g=a.ajax,j=a.oInstance,i=function(b){s(a,null,"xhr",[a,b,a.jqXHR]);c(b)};if(h.isPlainObject(g)&&g.data){f=g.data;var n=h.isFunction(f)?f(b,a):f,b=h.isFunction(f)&&n?n:h.extend(!0,b,n);delete g.data}n={data:b,success:function(b){var c=b.error||b.sError;c&&K(a,0,c);a.json=b;i(b)},dataType:"json",cache:!1,type:a.sServerMethod,error:function(b,c){var d=s(a,null,"xhr",
|
||||
[a,null,a.jqXHR]);-1===h.inArray(!0,d)&&("parsererror"==c?K(a,0,"Invalid JSON response",1):4===b.readyState&&K(a,0,"Ajax error",7));C(a,!1)}};a.oAjaxData=b;s(a,null,"preXhr",[a,b]);a.fnServerData?a.fnServerData.call(j,a.sAjaxSource,h.map(b,function(a,b){return{name:b,value:a}}),i,a):a.sAjaxSource||"string"===typeof g?a.jqXHR=h.ajax(h.extend(n,{url:g||a.sAjaxSource})):h.isFunction(g)?a.jqXHR=g.call(j,b,i,a):(a.jqXHR=h.ajax(h.extend(n,g)),g.data=f)}function nb(a){return a.bAjaxDataGet?(a.iDraw++,C(a,
|
||||
!0),ua(a,wb(a),function(b){xb(a,b)}),!1):!0}function wb(a){var b=a.aoColumns,c=b.length,d=a.oFeatures,e=a.oPreviousSearch,f=a.aoPreSearchCols,g,j=[],i,n,l,k=W(a);g=a._iDisplayStart;i=!1!==d.bPaginate?a._iDisplayLength:-1;var r=function(a,b){j.push({name:a,value:b})};r("sEcho",a.iDraw);r("iColumns",c);r("sColumns",D(b,"sName").join(","));r("iDisplayStart",g);r("iDisplayLength",i);var ra={draw:a.iDraw,columns:[],order:[],start:g,length:i,search:{value:e.sSearch,regex:e.bRegex}};for(g=0;g<c;g++)n=b[g],
|
||||
l=f[g],i="function"==typeof n.mData?"function":n.mData,ra.columns.push({data:i,name:n.sName,searchable:n.bSearchable,orderable:n.bSortable,search:{value:l.sSearch,regex:l.bRegex}}),r("mDataProp_"+g,i),d.bFilter&&(r("sSearch_"+g,l.sSearch),r("bRegex_"+g,l.bRegex),r("bSearchable_"+g,n.bSearchable)),d.bSort&&r("bSortable_"+g,n.bSortable);d.bFilter&&(r("sSearch",e.sSearch),r("bRegex",e.bRegex));d.bSort&&(h.each(k,function(a,b){ra.order.push({column:b.col,dir:b.dir});r("iSortCol_"+a,b.col);r("sSortDir_"+
|
||||
a,b.dir)}),r("iSortingCols",k.length));b=m.ext.legacy.ajax;return null===b?a.sAjaxSource?j:ra:b?j:ra}function xb(a,b){var c=va(a,b),d=b.sEcho!==k?b.sEcho:b.draw,e=b.iTotalRecords!==k?b.iTotalRecords:b.recordsTotal,f=b.iTotalDisplayRecords!==k?b.iTotalDisplayRecords:b.recordsFiltered;if(d){if(1*d<a.iDraw)return;a.iDraw=1*d}pa(a);a._iRecordsTotal=parseInt(e,10);a._iRecordsDisplay=parseInt(f,10);d=0;for(e=c.length;d<e;d++)N(a,c[d]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=!1;O(a);a._bInitComplete||
|
||||
wa(a,b);a.bAjaxDataGet=!0;C(a,!1)}function va(a,b){var c=h.isPlainObject(a.ajax)&&a.ajax.dataSrc!==k?a.ajax.dataSrc:a.sAjaxDataProp;return"data"===c?b.aaData||b[c]:""!==c?R(c)(b):b}function rb(a){var b=a.oClasses,c=a.sTableId,d=a.oLanguage,e=a.oPreviousSearch,f=a.aanFeatures,g='<input type="search" class="'+b.sFilterInput+'"/>',j=d.sSearch,j=j.match(/_INPUT_/)?j.replace("_INPUT_",g):j+g,b=h("<div/>",{id:!f.f?c+"_filter":null,"class":b.sFilter}).append(h("<label/>").append(j)),f=function(){var b=!this.value?
|
||||
"":this.value;b!=e.sSearch&&(ga(a,{sSearch:b,bRegex:e.bRegex,bSmart:e.bSmart,bCaseInsensitive:e.bCaseInsensitive}),a._iDisplayStart=0,O(a))},g=null!==a.searchDelay?a.searchDelay:"ssp"===y(a)?400:0,i=h("input",b).val(e.sSearch).attr("placeholder",d.sSearchPlaceholder).on("keyup.DT search.DT input.DT paste.DT cut.DT",g?Qa(f,g):f).on("keypress.DT",function(a){if(13==a.keyCode)return!1}).attr("aria-controls",c);h(a.nTable).on("search.dt.DT",function(b,c){if(a===c)try{i[0]!==H.activeElement&&i.val(e.sSearch)}catch(d){}});
|
||||
return b[0]}function ga(a,b,c){var d=a.oPreviousSearch,e=a.aoPreSearchCols,f=function(a){d.sSearch=a.sSearch;d.bRegex=a.bRegex;d.bSmart=a.bSmart;d.bCaseInsensitive=a.bCaseInsensitive};Ia(a);if("ssp"!=y(a)){yb(a,b.sSearch,c,b.bEscapeRegex!==k?!b.bEscapeRegex:b.bRegex,b.bSmart,b.bCaseInsensitive);f(b);for(b=0;b<e.length;b++)zb(a,e[b].sSearch,b,e[b].bEscapeRegex!==k?!e[b].bEscapeRegex:e[b].bRegex,e[b].bSmart,e[b].bCaseInsensitive);Ab(a)}else f(b);a.bFiltered=!0;s(a,null,"search",[a])}function Ab(a){for(var b=
|
||||
m.ext.search,c=a.aiDisplay,d,e,f=0,g=b.length;f<g;f++){for(var j=[],i=0,n=c.length;i<n;i++)e=c[i],d=a.aoData[e],b[f](a,d._aFilterData,e,d._aData,i)&&j.push(e);c.length=0;h.merge(c,j)}}function zb(a,b,c,d,e,f){if(""!==b){for(var g=[],j=a.aiDisplay,d=Ra(b,d,e,f),e=0;e<j.length;e++)b=a.aoData[j[e]]._aFilterData[c],d.test(b)&&g.push(j[e]);a.aiDisplay=g}}function yb(a,b,c,d,e,f){var d=Ra(b,d,e,f),f=a.oPreviousSearch.sSearch,g=a.aiDisplayMaster,j,e=[];0!==m.ext.search.length&&(c=!0);j=Bb(a);if(0>=b.length)a.aiDisplay=
|
||||
g.slice();else{if(j||c||f.length>b.length||0!==b.indexOf(f)||a.bSorted)a.aiDisplay=g.slice();b=a.aiDisplay;for(c=0;c<b.length;c++)d.test(a.aoData[b[c]]._sFilterRow)&&e.push(b[c]);a.aiDisplay=e}}function Ra(a,b,c,d){a=b?a:Sa(a);c&&(a="^(?=.*?"+h.map(a.match(/"[^"]+"|[^ ]+/g)||[""],function(a){if('"'===a.charAt(0))var b=a.match(/^"(.*)"$/),a=b?b[1]:a;return a.replace('"',"")}).join(")(?=.*?")+").*$");return RegExp(a,d?"i":"")}function Bb(a){var b=a.aoColumns,c,d,e,f,g,j,i,h,l=m.ext.type.search;c=!1;
|
||||
d=0;for(f=a.aoData.length;d<f;d++)if(h=a.aoData[d],!h._aFilterData){j=[];e=0;for(g=b.length;e<g;e++)c=b[e],c.bSearchable?(i=B(a,d,e,"filter"),l[c.sType]&&(i=l[c.sType](i)),null===i&&(i=""),"string"!==typeof i&&i.toString&&(i=i.toString())):i="",i.indexOf&&-1!==i.indexOf("&")&&(xa.innerHTML=i,i=$b?xa.textContent:xa.innerText),i.replace&&(i=i.replace(/[\r\n]/g,"")),j.push(i);h._aFilterData=j;h._sFilterRow=j.join(" ");c=!0}return c}function Cb(a){return{search:a.sSearch,smart:a.bSmart,regex:a.bRegex,
|
||||
caseInsensitive:a.bCaseInsensitive}}function Db(a){return{sSearch:a.search,bSmart:a.smart,bRegex:a.regex,bCaseInsensitive:a.caseInsensitive}}function ub(a){var b=a.sTableId,c=a.aanFeatures.i,d=h("<div/>",{"class":a.oClasses.sInfo,id:!c?b+"_info":null});c||(a.aoDrawCallback.push({fn:Eb,sName:"information"}),d.attr("role","status").attr("aria-live","polite"),h(a.nTable).attr("aria-describedby",b+"_info"));return d[0]}function Eb(a){var b=a.aanFeatures.i;if(0!==b.length){var c=a.oLanguage,d=a._iDisplayStart+
|
||||
1,e=a.fnDisplayEnd(),f=a.fnRecordsTotal(),g=a.fnRecordsDisplay(),j=g?c.sInfo:c.sInfoEmpty;g!==f&&(j+=" "+c.sInfoFiltered);j+=c.sInfoPostFix;j=Fb(a,j);c=c.fnInfoCallback;null!==c&&(j=c.call(a.oInstance,a,d,e,f,g,j));h(b).html(j)}}function Fb(a,b){var c=a.fnFormatNumber,d=a._iDisplayStart+1,e=a._iDisplayLength,f=a.fnRecordsDisplay(),g=-1===e;return b.replace(/_START_/g,c.call(a,d)).replace(/_END_/g,c.call(a,a.fnDisplayEnd())).replace(/_MAX_/g,c.call(a,a.fnRecordsTotal())).replace(/_TOTAL_/g,c.call(a,
|
||||
f)).replace(/_PAGE_/g,c.call(a,g?1:Math.ceil(d/e))).replace(/_PAGES_/g,c.call(a,g?1:Math.ceil(f/e)))}function ha(a){var b,c,d=a.iInitDisplayStart,e=a.aoColumns,f;c=a.oFeatures;var g=a.bDeferLoading;if(a.bInitialised){pb(a);mb(a);fa(a,a.aoHeader);fa(a,a.aoFooter);C(a,!0);c.bAutoWidth&&Ha(a);b=0;for(c=e.length;b<c;b++)f=e[b],f.sWidth&&(f.nTh.style.width=v(f.sWidth));s(a,null,"preInit",[a]);T(a);e=y(a);if("ssp"!=e||g)"ajax"==e?ua(a,[],function(c){var f=va(a,c);for(b=0;b<f.length;b++)N(a,f[b]);a.iInitDisplayStart=
|
||||
d;T(a);C(a,!1);wa(a,c)},a):(C(a,!1),wa(a))}else setTimeout(function(){ha(a)},200)}function wa(a,b){a._bInitComplete=!0;(b||a.oInit.aaData)&&Z(a);s(a,null,"plugin-init",[a,b]);s(a,"aoInitComplete","init",[a,b])}function Ta(a,b){var c=parseInt(b,10);a._iDisplayLength=c;Ua(a);s(a,null,"length",[a,c])}function qb(a){for(var b=a.oClasses,c=a.sTableId,d=a.aLengthMenu,e=h.isArray(d[0]),f=e?d[0]:d,d=e?d[1]:d,e=h("<select/>",{name:c+"_length","aria-controls":c,"class":b.sLengthSelect}),g=0,j=f.length;g<j;g++)e[0][g]=
|
||||
new Option(d[g],f[g]);var i=h("<div><label/></div>").addClass(b.sLength);a.aanFeatures.l||(i[0].id=c+"_length");i.children().append(a.oLanguage.sLengthMenu.replace("_MENU_",e[0].outerHTML));h("select",i).val(a._iDisplayLength).on("change.DT",function(){Ta(a,h(this).val());O(a)});h(a.nTable).on("length.dt.DT",function(b,c,d){a===c&&h("select",i).val(d)});return i[0]}function vb(a){var b=a.sPaginationType,c=m.ext.pager[b],d="function"===typeof c,e=function(a){O(a)},b=h("<div/>").addClass(a.oClasses.sPaging+
|
||||
b)[0],f=a.aanFeatures;d||c.fnInit(a,b,e);f.p||(b.id=a.sTableId+"_paginate",a.aoDrawCallback.push({fn:function(a){if(d){var b=a._iDisplayStart,i=a._iDisplayLength,h=a.fnRecordsDisplay(),l=-1===i,b=l?0:Math.ceil(b/i),i=l?1:Math.ceil(h/i),h=c(b,i),k,l=0;for(k=f.p.length;l<k;l++)Pa(a,"pageButton")(a,f.p[l],l,h,b,i)}else c.fnUpdate(a,e)},sName:"pagination"}));return b}function Va(a,b,c){var d=a._iDisplayStart,e=a._iDisplayLength,f=a.fnRecordsDisplay();0===f||-1===e?d=0:"number"===typeof b?(d=b*e,d>f&&
|
||||
(d=0)):"first"==b?d=0:"previous"==b?(d=0<=e?d-e:0,0>d&&(d=0)):"next"==b?d+e<f&&(d+=e):"last"==b?d=Math.floor((f-1)/e)*e:K(a,0,"Unknown paging action: "+b,5);b=a._iDisplayStart!==d;a._iDisplayStart=d;b&&(s(a,null,"page",[a]),c&&O(a));return b}function sb(a){return h("<div/>",{id:!a.aanFeatures.r?a.sTableId+"_processing":null,"class":a.oClasses.sProcessing}).html(a.oLanguage.sProcessing).insertBefore(a.nTable)[0]}function C(a,b){a.oFeatures.bProcessing&&h(a.aanFeatures.r).css("display",b?"block":"none");
|
||||
s(a,null,"processing",[a,b])}function tb(a){var b=h(a.nTable);b.attr("role","grid");var c=a.oScroll;if(""===c.sX&&""===c.sY)return a.nTable;var d=c.sX,e=c.sY,f=a.oClasses,g=b.children("caption"),j=g.length?g[0]._captionSide:null,i=h(b[0].cloneNode(!1)),n=h(b[0].cloneNode(!1)),l=b.children("tfoot");l.length||(l=null);i=h("<div/>",{"class":f.sScrollWrapper}).append(h("<div/>",{"class":f.sScrollHead}).css({overflow:"hidden",position:"relative",border:0,width:d?!d?null:v(d):"100%"}).append(h("<div/>",
|
||||
{"class":f.sScrollHeadInner}).css({"box-sizing":"content-box",width:c.sXInner||"100%"}).append(i.removeAttr("id").css("margin-left",0).append("top"===j?g:null).append(b.children("thead"))))).append(h("<div/>",{"class":f.sScrollBody}).css({position:"relative",overflow:"auto",width:!d?null:v(d)}).append(b));l&&i.append(h("<div/>",{"class":f.sScrollFoot}).css({overflow:"hidden",border:0,width:d?!d?null:v(d):"100%"}).append(h("<div/>",{"class":f.sScrollFootInner}).append(n.removeAttr("id").css("margin-left",
|
||||
0).append("bottom"===j?g:null).append(b.children("tfoot")))));var b=i.children(),k=b[0],f=b[1],r=l?b[2]:null;if(d)h(f).on("scroll.DT",function(){var a=this.scrollLeft;k.scrollLeft=a;l&&(r.scrollLeft=a)});h(f).css(e&&c.bCollapse?"max-height":"height",e);a.nScrollHead=k;a.nScrollBody=f;a.nScrollFoot=r;a.aoDrawCallback.push({fn:ma,sName:"scrolling"});return i[0]}function ma(a){var b=a.oScroll,c=b.sX,d=b.sXInner,e=b.sY,b=b.iBarWidth,f=h(a.nScrollHead),g=f[0].style,j=f.children("div"),i=j[0].style,n=j.children("table"),
|
||||
j=a.nScrollBody,l=h(j),q=j.style,r=h(a.nScrollFoot).children("div"),m=r.children("table"),p=h(a.nTHead),o=h(a.nTable),u=o[0],s=u.style,t=a.nTFoot?h(a.nTFoot):null,x=a.oBrowser,U=x.bScrollOversize,ac=D(a.aoColumns,"nTh"),P,L,Q,w,Wa=[],y=[],z=[],A=[],B,C=function(a){a=a.style;a.paddingTop="0";a.paddingBottom="0";a.borderTopWidth="0";a.borderBottomWidth="0";a.height=0};L=j.scrollHeight>j.clientHeight;if(a.scrollBarVis!==L&&a.scrollBarVis!==k)a.scrollBarVis=L,Z(a);else{a.scrollBarVis=L;o.children("thead, tfoot").remove();
|
||||
t&&(Q=t.clone().prependTo(o),P=t.find("tr"),Q=Q.find("tr"));w=p.clone().prependTo(o);p=p.find("tr");L=w.find("tr");w.find("th, td").removeAttr("tabindex");c||(q.width="100%",f[0].style.width="100%");h.each(ta(a,w),function(b,c){B=$(a,b);c.style.width=a.aoColumns[B].sWidth});t&&I(function(a){a.style.width=""},Q);f=o.outerWidth();if(""===c){s.width="100%";if(U&&(o.find("tbody").height()>j.offsetHeight||"scroll"==l.css("overflow-y")))s.width=v(o.outerWidth()-b);f=o.outerWidth()}else""!==d&&(s.width=
|
||||
v(d),f=o.outerWidth());I(C,L);I(function(a){z.push(a.innerHTML);Wa.push(v(h(a).css("width")))},L);I(function(a,b){if(h.inArray(a,ac)!==-1)a.style.width=Wa[b]},p);h(L).height(0);t&&(I(C,Q),I(function(a){A.push(a.innerHTML);y.push(v(h(a).css("width")))},Q),I(function(a,b){a.style.width=y[b]},P),h(Q).height(0));I(function(a,b){a.innerHTML='<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+z[b]+"</div>";a.style.width=Wa[b]},L);t&&I(function(a,b){a.innerHTML='<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+
|
||||
A[b]+"</div>";a.style.width=y[b]},Q);if(o.outerWidth()<f){P=j.scrollHeight>j.offsetHeight||"scroll"==l.css("overflow-y")?f+b:f;if(U&&(j.scrollHeight>j.offsetHeight||"scroll"==l.css("overflow-y")))s.width=v(P-b);(""===c||""!==d)&&K(a,1,"Possible column misalignment",6)}else P="100%";q.width=v(P);g.width=v(P);t&&(a.nScrollFoot.style.width=v(P));!e&&U&&(q.height=v(u.offsetHeight+b));c=o.outerWidth();n[0].style.width=v(c);i.width=v(c);d=o.height()>j.clientHeight||"scroll"==l.css("overflow-y");e="padding"+
|
||||
(x.bScrollbarLeft?"Left":"Right");i[e]=d?b+"px":"0px";t&&(m[0].style.width=v(c),r[0].style.width=v(c),r[0].style[e]=d?b+"px":"0px");o.children("colgroup").insertBefore(o.children("thead"));l.scroll();if((a.bSorted||a.bFiltered)&&!a._drawHold)j.scrollTop=0}}function I(a,b,c){for(var d=0,e=0,f=b.length,g,j;e<f;){g=b[e].firstChild;for(j=c?c[e].firstChild:null;g;)1===g.nodeType&&(c?a(g,j,d):a(g,d),d++),g=g.nextSibling,j=c?j.nextSibling:null;e++}}function Ha(a){var b=a.nTable,c=a.aoColumns,d=a.oScroll,
|
||||
e=d.sY,f=d.sX,g=d.sXInner,j=c.length,i=na(a,"bVisible"),n=h("th",a.nTHead),l=b.getAttribute("width"),k=b.parentNode,r=!1,m,p,o=a.oBrowser,d=o.bScrollOversize;(m=b.style.width)&&-1!==m.indexOf("%")&&(l=m);for(m=0;m<i.length;m++)p=c[i[m]],null!==p.sWidth&&(p.sWidth=Gb(p.sWidthOrig,k),r=!0);if(d||!r&&!f&&!e&&j==ba(a)&&j==n.length)for(m=0;m<j;m++)i=$(a,m),null!==i&&(c[i].sWidth=v(n.eq(m).width()));else{j=h(b).clone().css("visibility","hidden").removeAttr("id");j.find("tbody tr").remove();var u=h("<tr/>").appendTo(j.find("tbody"));
|
||||
j.find("thead, tfoot").remove();j.append(h(a.nTHead).clone()).append(h(a.nTFoot).clone());j.find("tfoot th, tfoot td").css("width","");n=ta(a,j.find("thead")[0]);for(m=0;m<i.length;m++)p=c[i[m]],n[m].style.width=null!==p.sWidthOrig&&""!==p.sWidthOrig?v(p.sWidthOrig):"",p.sWidthOrig&&f&&h(n[m]).append(h("<div/>").css({width:p.sWidthOrig,margin:0,padding:0,border:0,height:1}));if(a.aoData.length)for(m=0;m<i.length;m++)r=i[m],p=c[r],h(Hb(a,r)).clone(!1).append(p.sContentPadding).appendTo(u);h("[name]",
|
||||
j).removeAttr("name");p=h("<div/>").css(f||e?{position:"absolute",top:0,left:0,height:1,right:0,overflow:"hidden"}:{}).append(j).appendTo(k);f&&g?j.width(g):f?(j.css("width","auto"),j.removeAttr("width"),j.width()<k.clientWidth&&l&&j.width(k.clientWidth)):e?j.width(k.clientWidth):l&&j.width(l);for(m=e=0;m<i.length;m++)k=h(n[m]),g=k.outerWidth()-k.width(),k=o.bBounding?Math.ceil(n[m].getBoundingClientRect().width):k.outerWidth(),e+=k,c[i[m]].sWidth=v(k-g);b.style.width=v(e);p.remove()}l&&(b.style.width=
|
||||
v(l));if((l||f)&&!a._reszEvt)b=function(){h(E).on("resize.DT-"+a.sInstance,Qa(function(){Z(a)}))},d?setTimeout(b,1E3):b(),a._reszEvt=!0}function Gb(a,b){if(!a)return 0;var c=h("<div/>").css("width",v(a)).appendTo(b||H.body),d=c[0].offsetWidth;c.remove();return d}function Hb(a,b){var c=Ib(a,b);if(0>c)return null;var d=a.aoData[c];return!d.nTr?h("<td/>").html(B(a,c,b,"display"))[0]:d.anCells[b]}function Ib(a,b){for(var c,d=-1,e=-1,f=0,g=a.aoData.length;f<g;f++)c=B(a,f,b,"display")+"",c=c.replace(bc,
|
||||
""),c=c.replace(/ /g," "),c.length>d&&(d=c.length,e=f);return e}function v(a){return null===a?"0px":"number"==typeof a?0>a?"0px":a+"px":a.match(/\d$/)?a+"px":a}function W(a){var b,c,d=[],e=a.aoColumns,f,g,j,i;b=a.aaSortingFixed;c=h.isPlainObject(b);var n=[];f=function(a){a.length&&!h.isArray(a[0])?n.push(a):h.merge(n,a)};h.isArray(b)&&f(b);c&&b.pre&&f(b.pre);f(a.aaSorting);c&&b.post&&f(b.post);for(a=0;a<n.length;a++){i=n[a][0];f=e[i].aDataSort;b=0;for(c=f.length;b<c;b++)g=f[b],j=e[g].sType||
|
||||
"string",n[a]._idx===k&&(n[a]._idx=h.inArray(n[a][1],e[g].asSorting)),d.push({src:i,col:g,dir:n[a][1],index:n[a]._idx,type:j,formatter:m.ext.type.order[j+"-pre"]})}return d}function ob(a){var b,c,d=[],e=m.ext.type.order,f=a.aoData,g=0,j,i=a.aiDisplayMaster,h;Ia(a);h=W(a);b=0;for(c=h.length;b<c;b++)j=h[b],j.formatter&&g++,Jb(a,j.col);if("ssp"!=y(a)&&0!==h.length){b=0;for(c=i.length;b<c;b++)d[i[b]]=b;g===h.length?i.sort(function(a,b){var c,e,g,j,i=h.length,k=f[a]._aSortData,m=f[b]._aSortData;for(g=
|
||||
0;g<i;g++)if(j=h[g],c=k[j.col],e=m[j.col],c=c<e?-1:c>e?1:0,0!==c)return"asc"===j.dir?c:-c;c=d[a];e=d[b];return c<e?-1:c>e?1:0}):i.sort(function(a,b){var c,g,j,i,k=h.length,m=f[a]._aSortData,p=f[b]._aSortData;for(j=0;j<k;j++)if(i=h[j],c=m[i.col],g=p[i.col],i=e[i.type+"-"+i.dir]||e["string-"+i.dir],c=i(c,g),0!==c)return c;c=d[a];g=d[b];return c<g?-1:c>g?1:0})}a.bSorted=!0}function Kb(a){for(var b,c,d=a.aoColumns,e=W(a),a=a.oLanguage.oAria,f=0,g=d.length;f<g;f++){c=d[f];var j=c.asSorting;b=c.sTitle.replace(/<.*?>/g,
|
||||
"");var i=c.nTh;i.removeAttribute("aria-sort");c.bSortable&&(0<e.length&&e[0].col==f?(i.setAttribute("aria-sort","asc"==e[0].dir?"ascending":"descending"),c=j[e[0].index+1]||j[0]):c=j[0],b+="asc"===c?a.sSortAscending:a.sSortDescending);i.setAttribute("aria-label",b)}}function Xa(a,b,c,d){var e=a.aaSorting,f=a.aoColumns[b].asSorting,g=function(a,b){var c=a._idx;c===k&&(c=h.inArray(a[1],f));return c+1<f.length?c+1:b?null:0};"number"===typeof e[0]&&(e=a.aaSorting=[e]);c&&a.oFeatures.bSortMulti?(c=h.inArray(b,
|
||||
D(e,"0")),-1!==c?(b=g(e[c],!0),null===b&&1===e.length&&(b=0),null===b?e.splice(c,1):(e[c][1]=f[b],e[c]._idx=b)):(e.push([b,f[0],0]),e[e.length-1]._idx=0)):e.length&&e[0][0]==b?(b=g(e[0]),e.length=1,e[0][1]=f[b],e[0]._idx=b):(e.length=0,e.push([b,f[0]]),e[0]._idx=0);T(a);"function"==typeof d&&d(a)}function Oa(a,b,c,d){var e=a.aoColumns[c];Ya(b,{},function(b){!1!==e.bSortable&&(a.oFeatures.bProcessing?(C(a,!0),setTimeout(function(){Xa(a,c,b.shiftKey,d);"ssp"!==y(a)&&C(a,!1)},0)):Xa(a,c,b.shiftKey,d))})}
|
||||
function ya(a){var b=a.aLastSort,c=a.oClasses.sSortColumn,d=W(a),e=a.oFeatures,f,g;if(e.bSort&&e.bSortClasses){e=0;for(f=b.length;e<f;e++)g=b[e].src,h(D(a.aoData,"anCells",g)).removeClass(c+(2>e?e+1:3));e=0;for(f=d.length;e<f;e++)g=d[e].src,h(D(a.aoData,"anCells",g)).addClass(c+(2>e?e+1:3))}a.aLastSort=d}function Jb(a,b){var c=a.aoColumns[b],d=m.ext.order[c.sSortDataType],e;d&&(e=d.call(a.oInstance,a,b,aa(a,b)));for(var f,g=m.ext.type.order[c.sType+"-pre"],j=0,i=a.aoData.length;j<i;j++)if(c=a.aoData[j],
|
||||
c._aSortData||(c._aSortData=[]),!c._aSortData[b]||d)f=d?e[j]:B(a,j,b,"sort"),c._aSortData[b]=g?g(f):f}function za(a){if(a.oFeatures.bStateSave&&!a.bDestroying){var b={time:+new Date,start:a._iDisplayStart,length:a._iDisplayLength,order:h.extend(!0,[],a.aaSorting),search:Cb(a.oPreviousSearch),columns:h.map(a.aoColumns,function(b,d){return{visible:b.bVisible,search:Cb(a.aoPreSearchCols[d])}})};s(a,"aoStateSaveParams","stateSaveParams",[a,b]);a.oSavedState=b;a.fnStateSaveCallback.call(a.oInstance,a,
|
||||
b)}}function Lb(a,b,c){var d,e,f=a.aoColumns,b=function(b){if(b&&b.time){var i=s(a,"aoStateLoadParams","stateLoadParams",[a,g]);if(-1===h.inArray(!1,i)&&(i=a.iStateDuration,!(0<i&&b.time<+new Date-1E3*i)&&!(b.columns&&f.length!==b.columns.length))){a.oLoadedState=h.extend(!0,{},g);b.start!==k&&(a._iDisplayStart=b.start,a.iInitDisplayStart=b.start);b.length!==k&&(a._iDisplayLength=b.length);b.order!==k&&(a.aaSorting=[],h.each(b.order,function(b,c){a.aaSorting.push(c[0]>=f.length?[0,c[1]]:c)}));b.search!==
|
||||
k&&h.extend(a.oPreviousSearch,Db(b.search));if(b.columns){d=0;for(e=b.columns.length;d<e;d++)i=b.columns[d],i.visible!==k&&(f[d].bVisible=i.visible),i.search!==k&&h.extend(a.aoPreSearchCols[d],Db(i.search))}s(a,"aoStateLoaded","stateLoaded",[a,g])}}c()};if(a.oFeatures.bStateSave){var g=a.fnStateLoadCallback.call(a.oInstance,a,b);g!==k&&b(g)}else c()}function Aa(a){var b=m.settings,a=h.inArray(a,D(b,"nTable"));return-1!==a?b[a]:null}function K(a,b,c,d){c="DataTables warning: "+(a?"table id="+a.sTableId+
|
||||
" - ":"")+c;d&&(c+=". For more information about this error, please see http://datatables.net/tn/"+d);if(b)E.console&&console.log&&console.log(c);else if(b=m.ext,b=b.sErrMode||b.errMode,a&&s(a,null,"error",[a,d,c]),"alert"==b)alert(c);else{if("throw"==b)throw Error(c);"function"==typeof b&&b(a,d,c)}}function F(a,b,c,d){h.isArray(c)?h.each(c,function(c,d){h.isArray(d)?F(a,b,d[0],d[1]):F(a,b,d)}):(d===k&&(d=c),b[c]!==k&&(a[d]=b[c]))}function Mb(a,b,c){var d,e;for(e in b)b.hasOwnProperty(e)&&(d=b[e],
|
||||
h.isPlainObject(d)?(h.isPlainObject(a[e])||(a[e]={}),h.extend(!0,a[e],d)):a[e]=c&&"data"!==e&&"aaData"!==e&&h.isArray(d)?d.slice():d);return a}function Ya(a,b,c){h(a).on("click.DT",b,function(b){a.blur();c(b)}).on("keypress.DT",b,function(a){13===a.which&&(a.preventDefault(),c(a))}).on("selectstart.DT",function(){return!1})}function z(a,b,c,d){c&&a[b].push({fn:c,sName:d})}function s(a,b,c,d){var e=[];b&&(e=h.map(a[b].slice().reverse(),function(b){return b.fn.apply(a.oInstance,d)}));null!==c&&(b=h.Event(c+
|
||||
".dt"),h(a.nTable).trigger(b,d),e.push(b.result));return e}function Ua(a){var b=a._iDisplayStart,c=a.fnDisplayEnd(),d=a._iDisplayLength;b>=c&&(b=c-d);b-=b%d;if(-1===d||0>b)b=0;a._iDisplayStart=b}function Pa(a,b){var c=a.renderer,d=m.ext.renderer[b];return h.isPlainObject(c)&&c[b]?d[c[b]]||d._:"string"===typeof c?d[c]||d._:d._}function y(a){return a.oFeatures.bServerSide?"ssp":a.ajax||a.sAjaxSource?"ajax":"dom"}function ia(a,b){var c=[],c=Nb.numbers_length,d=Math.floor(c/2);b<=c?c=X(0,b):a<=d?(c=X(0,
|
||||
c-2),c.push("ellipsis"),c.push(b-1)):(a>=b-1-d?c=X(b-(c-2),b):(c=X(a-d+2,a+d-1),c.push("ellipsis"),c.push(b-1)),c.splice(0,0,"ellipsis"),c.splice(0,0,0));c.DT_el="span";return c}function fb(a){h.each({num:function(b){return Ba(b,a)},"num-fmt":function(b){return Ba(b,a,Za)},"html-num":function(b){return Ba(b,a,Ca)},"html-num-fmt":function(b){return Ba(b,a,Ca,Za)}},function(b,c){x.type.order[b+a+"-pre"]=c;b.match(/^html\-/)&&(x.type.search[b+a]=x.type.search.html)})}function Ob(a){return function(){var b=
|
||||
[Aa(this[m.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return m.ext.internal[a].apply(this,b)}}var m=function(a){this.$=function(a,b){return this.api(!0).$(a,b)};this._=function(a,b){return this.api(!0).rows(a,b).data()};this.api=function(a){return a?new u(Aa(this[x.iApiIndex])):new u(this)};this.fnAddData=function(a,b){var c=this.api(!0),d=h.isArray(a)&&(h.isArray(a[0])||h.isPlainObject(a[0]))?c.rows.add(a):c.row.add(a);(b===k||b)&&c.draw();return d.flatten().toArray()};this.fnAdjustColumnSizing=
|
||||
function(a){var b=this.api(!0).columns.adjust(),c=b.settings()[0],d=c.oScroll;a===k||a?b.draw(!1):(""!==d.sX||""!==d.sY)&&ma(c)};this.fnClearTable=function(a){var b=this.api(!0).clear();(a===k||a)&&b.draw()};this.fnClose=function(a){this.api(!0).row(a).child.hide()};this.fnDeleteRow=function(a,b,c){var d=this.api(!0),a=d.rows(a),e=a.settings()[0],h=e.aoData[a[0][0]];a.remove();b&&b.call(this,e,h);(c===k||c)&&d.draw();return h};this.fnDestroy=function(a){this.api(!0).destroy(a)};this.fnDraw=function(a){this.api(!0).draw(a)};
|
||||
this.fnFilter=function(a,b,c,d,e,h){e=this.api(!0);null===b||b===k?e.search(a,c,d,h):e.column(b).search(a,c,d,h);e.draw()};this.fnGetData=function(a,b){var c=this.api(!0);if(a!==k){var d=a.nodeName?a.nodeName.toLowerCase():"";return b!==k||"td"==d||"th"==d?c.cell(a,b).data():c.row(a).data()||null}return c.data().toArray()};this.fnGetNodes=function(a){var b=this.api(!0);return a!==k?b.row(a).node():b.rows().nodes().flatten().toArray()};this.fnGetPosition=function(a){var b=this.api(!0),c=a.nodeName.toUpperCase();
|
||||
return"TR"==c?b.row(a).index():"TD"==c||"TH"==c?(a=b.cell(a).index(),[a.row,a.columnVisible,a.column]):null};this.fnIsOpen=function(a){return this.api(!0).row(a).child.isShown()};this.fnOpen=function(a,b,c){return this.api(!0).row(a).child(b,c).show().child()[0]};this.fnPageChange=function(a,b){var c=this.api(!0).page(a);(b===k||b)&&c.draw(!1)};this.fnSetColumnVis=function(a,b,c){a=this.api(!0).column(a).visible(b);(c===k||c)&&a.columns.adjust().draw()};this.fnSettings=function(){return Aa(this[x.iApiIndex])};
|
||||
this.fnSort=function(a){this.api(!0).order(a).draw()};this.fnSortListener=function(a,b,c){this.api(!0).order.listener(a,b,c)};this.fnUpdate=function(a,b,c,d,e){var h=this.api(!0);c===k||null===c?h.row(b).data(a):h.cell(b,c).data(a);(e===k||e)&&h.columns.adjust();(d===k||d)&&h.draw();return 0};this.fnVersionCheck=x.fnVersionCheck;var b=this,c=a===k,d=this.length;c&&(a={});this.oApi=this.internal=x.internal;for(var e in m.ext.internal)e&&(this[e]=Ob(e));this.each(function(){var e={},g=1<d?Mb(e,a,!0):
|
||||
a,j=0,i,e=this.getAttribute("id"),n=!1,l=m.defaults,q=h(this);if("table"!=this.nodeName.toLowerCase())K(null,0,"Non-table node initialisation ("+this.nodeName+")",2);else{gb(l);hb(l.column);J(l,l,!0);J(l.column,l.column,!0);J(l,h.extend(g,q.data()));var r=m.settings,j=0;for(i=r.length;j<i;j++){var p=r[j];if(p.nTable==this||p.nTHead.parentNode==this||p.nTFoot&&p.nTFoot.parentNode==this){var u=g.bRetrieve!==k?g.bRetrieve:l.bRetrieve;if(c||u)return p.oInstance;if(g.bDestroy!==k?g.bDestroy:l.bDestroy){p.oInstance.fnDestroy();
|
||||
break}else{K(p,0,"Cannot reinitialise DataTable",3);return}}if(p.sTableId==this.id){r.splice(j,1);break}}if(null===e||""===e)this.id=e="DataTables_Table_"+m.ext._unique++;var o=h.extend(!0,{},m.models.oSettings,{sDestroyWidth:q[0].style.width,sInstance:e,sTableId:e});o.nTable=this;o.oApi=b.internal;o.oInit=g;r.push(o);o.oInstance=1===b.length?b:q.dataTable();gb(g);g.oLanguage&&Fa(g.oLanguage);g.aLengthMenu&&!g.iDisplayLength&&(g.iDisplayLength=h.isArray(g.aLengthMenu[0])?g.aLengthMenu[0][0]:g.aLengthMenu[0]);
|
||||
g=Mb(h.extend(!0,{},l),g);F(o.oFeatures,g,"bPaginate bLengthChange bFilter bSort bSortMulti bInfo bProcessing bAutoWidth bSortClasses bServerSide bDeferRender".split(" "));F(o,g,["asStripeClasses","ajax","fnServerData","fnFormatNumber","sServerMethod","aaSorting","aaSortingFixed","aLengthMenu","sPaginationType","sAjaxSource","sAjaxDataProp","iStateDuration","sDom","bSortCellsTop","iTabIndex","fnStateLoadCallback","fnStateSaveCallback","renderer","searchDelay","rowId",["iCookieDuration","iStateDuration"],
|
||||
["oSearch","oPreviousSearch"],["aoSearchCols","aoPreSearchCols"],["iDisplayLength","_iDisplayLength"],["bJQueryUI","bJUI"]]);F(o.oScroll,g,[["sScrollX","sX"],["sScrollXInner","sXInner"],["sScrollY","sY"],["bScrollCollapse","bCollapse"]]);F(o.oLanguage,g,"fnInfoCallback");z(o,"aoDrawCallback",g.fnDrawCallback,"user");z(o,"aoServerParams",g.fnServerParams,"user");z(o,"aoStateSaveParams",g.fnStateSaveParams,"user");z(o,"aoStateLoadParams",g.fnStateLoadParams,"user");z(o,"aoStateLoaded",g.fnStateLoaded,
|
||||
"user");z(o,"aoRowCallback",g.fnRowCallback,"user");z(o,"aoRowCreatedCallback",g.fnCreatedRow,"user");z(o,"aoHeaderCallback",g.fnHeaderCallback,"user");z(o,"aoFooterCallback",g.fnFooterCallback,"user");z(o,"aoInitComplete",g.fnInitComplete,"user");z(o,"aoPreDrawCallback",g.fnPreDrawCallback,"user");o.rowIdFn=R(g.rowId);ib(o);var t=o.oClasses;g.bJQueryUI?(h.extend(t,m.ext.oJUIClasses,g.oClasses),g.sDom===l.sDom&&"lfrtip"===l.sDom&&(o.sDom='<"H"lfr>t<"F"ip>'),o.renderer)?h.isPlainObject(o.renderer)&&
|
||||
!o.renderer.header&&(o.renderer.header="jqueryui"):o.renderer="jqueryui":h.extend(t,m.ext.classes,g.oClasses);q.addClass(t.sTable);o.iInitDisplayStart===k&&(o.iInitDisplayStart=g.iDisplayStart,o._iDisplayStart=g.iDisplayStart);null!==g.iDeferLoading&&(o.bDeferLoading=!0,e=h.isArray(g.iDeferLoading),o._iRecordsDisplay=e?g.iDeferLoading[0]:g.iDeferLoading,o._iRecordsTotal=e?g.iDeferLoading[1]:g.iDeferLoading);var v=o.oLanguage;h.extend(!0,v,g.oLanguage);v.sUrl&&(h.ajax({dataType:"json",url:v.sUrl,success:function(a){Fa(a);
|
||||
J(l.oLanguage,a);h.extend(true,v,a);ha(o)},error:function(){ha(o)}}),n=!0);null===g.asStripeClasses&&(o.asStripeClasses=[t.sStripeOdd,t.sStripeEven]);var e=o.asStripeClasses,x=q.children("tbody").find("tr").eq(0);-1!==h.inArray(!0,h.map(e,function(a){return x.hasClass(a)}))&&(h("tbody tr",this).removeClass(e.join(" ")),o.asDestroyStripes=e.slice());e=[];r=this.getElementsByTagName("thead");0!==r.length&&(ea(o.aoHeader,r[0]),e=ta(o));if(null===g.aoColumns){r=[];j=0;for(i=e.length;j<i;j++)r.push(null)}else r=
|
||||
g.aoColumns;j=0;for(i=r.length;j<i;j++)Ga(o,e?e[j]:null);kb(o,g.aoColumnDefs,r,function(a,b){la(o,a,b)});if(x.length){var w=function(a,b){return a.getAttribute("data-"+b)!==null?b:null};h(x[0]).children("th, td").each(function(a,b){var c=o.aoColumns[a];if(c.mData===a){var d=w(b,"sort")||w(b,"order"),e=w(b,"filter")||w(b,"search");if(d!==null||e!==null){c.mData={_:a+".display",sort:d!==null?a+".@data-"+d:k,type:d!==null?a+".@data-"+d:k,filter:e!==null?a+".@data-"+e:k};la(o,a)}}})}var U=o.oFeatures,
|
||||
e=function(){if(g.aaSorting===k){var a=o.aaSorting;j=0;for(i=a.length;j<i;j++)a[j][1]=o.aoColumns[j].asSorting[0]}ya(o);U.bSort&&z(o,"aoDrawCallback",function(){if(o.bSorted){var a=W(o),b={};h.each(a,function(a,c){b[c.src]=c.dir});s(o,null,"order",[o,a,b]);Kb(o)}});z(o,"aoDrawCallback",function(){(o.bSorted||y(o)==="ssp"||U.bDeferRender)&&ya(o)},"sc");var a=q.children("caption").each(function(){this._captionSide=h(this).css("caption-side")}),b=q.children("thead");b.length===0&&(b=h("<thead/>").appendTo(q));
|
||||
o.nTHead=b[0];b=q.children("tbody");b.length===0&&(b=h("<tbody/>").appendTo(q));o.nTBody=b[0];b=q.children("tfoot");if(b.length===0&&a.length>0&&(o.oScroll.sX!==""||o.oScroll.sY!==""))b=h("<tfoot/>").appendTo(q);if(b.length===0||b.children().length===0)q.addClass(t.sNoFooter);else if(b.length>0){o.nTFoot=b[0];ea(o.aoFooter,o.nTFoot)}if(g.aaData)for(j=0;j<g.aaData.length;j++)N(o,g.aaData[j]);else(o.bDeferLoading||y(o)=="dom")&&oa(o,h(o.nTBody).children("tr"));o.aiDisplay=o.aiDisplayMaster.slice();
|
||||
o.bInitialised=true;n===false&&ha(o)};g.bStateSave?(U.bStateSave=!0,z(o,"aoDrawCallback",za,"state_save"),Lb(o,g,e)):e()}});b=null;return this},x,u,p,t,$a={},Pb=/[\r\n]/g,Ca=/<.*?>/g,cc=/^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/,dc=RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"),Za=/[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi,M=function(a){return!a||!0===a||"-"===a?!0:!1},Qb=function(a){var b=parseInt(a,10);return!isNaN(b)&&
|
||||
isFinite(a)?b:null},Rb=function(a,b){$a[b]||($a[b]=RegExp(Sa(b),"g"));return"string"===typeof a&&"."!==b?a.replace(/\./g,"").replace($a[b],"."):a},ab=function(a,b,c){var d="string"===typeof a;if(M(a))return!0;b&&d&&(a=Rb(a,b));c&&d&&(a=a.replace(Za,""));return!isNaN(parseFloat(a))&&isFinite(a)},Sb=function(a,b,c){return M(a)?!0:!(M(a)||"string"===typeof a)?null:ab(a.replace(Ca,""),b,c)?!0:null},D=function(a,b,c){var d=[],e=0,f=a.length;if(c!==k)for(;e<f;e++)a[e]&&a[e][b]&&d.push(a[e][b][c]);else for(;e<
|
||||
f;e++)a[e]&&d.push(a[e][b]);return d},ja=function(a,b,c,d){var e=[],f=0,g=b.length;if(d!==k)for(;f<g;f++)a[b[f]][c]&&e.push(a[b[f]][c][d]);else for(;f<g;f++)e.push(a[b[f]][c]);return e},X=function(a,b){var c=[],d;b===k?(b=0,d=a):(d=b,b=a);for(var e=b;e<d;e++)c.push(e);return c},Tb=function(a){for(var b=[],c=0,d=a.length;c<d;c++)a[c]&&b.push(a[c]);return b},sa=function(a){var b=[],c,d,e=a.length,f,g=0;d=0;a:for(;d<e;d++){c=a[d];for(f=0;f<g;f++)if(b[f]===c)continue a;b.push(c);g++}return b};m.util=
|
||||
{throttle:function(a,b){var c=b!==k?b:200,d,e;return function(){var b=this,g=+new Date,h=arguments;d&&g<d+c?(clearTimeout(e),e=setTimeout(function(){d=k;a.apply(b,h)},c)):(d=g,a.apply(b,h))}},escapeRegex:function(a){return a.replace(dc,"\\$1")}};var A=function(a,b,c){a[b]!==k&&(a[c]=a[b])},ca=/\[.*?\]$/,V=/\(\)$/,Sa=m.util.escapeRegex,xa=h("<div>")[0],$b=xa.textContent!==k,bc=/<.*?>/g,Qa=m.util.throttle,Ub=[],w=Array.prototype,ec=function(a){var b,c,d=m.settings,e=h.map(d,function(a){return a.nTable});
|
||||
if(a){if(a.nTable&&a.oApi)return[a];if(a.nodeName&&"table"===a.nodeName.toLowerCase())return b=h.inArray(a,e),-1!==b?[d[b]]:null;if(a&&"function"===typeof a.settings)return a.settings().toArray();"string"===typeof a?c=h(a):a instanceof h&&(c=a)}else return[];if(c)return c.map(function(){b=h.inArray(this,e);return-1!==b?d[b]:null}).toArray()};u=function(a,b){if(!(this instanceof u))return new u(a,b);var c=[],d=function(a){(a=ec(a))&&(c=c.concat(a))};if(h.isArray(a))for(var e=0,f=a.length;e<f;e++)d(a[e]);
|
||||
else d(a);this.context=sa(c);b&&h.merge(this,b);this.selector={rows:null,cols:null,opts:null};u.extend(this,this,Ub)};m.Api=u;h.extend(u.prototype,{any:function(){return 0!==this.count()},concat:w.concat,context:[],count:function(){return this.flatten().length},each:function(a){for(var b=0,c=this.length;b<c;b++)a.call(this,this[b],b,this);return this},eq:function(a){var b=this.context;return b.length>a?new u(b[a],this[a]):null},filter:function(a){var b=[];if(w.filter)b=w.filter.call(this,a,this);
|
||||
else for(var c=0,d=this.length;c<d;c++)a.call(this,this[c],c,this)&&b.push(this[c]);return new u(this.context,b)},flatten:function(){var a=[];return new u(this.context,a.concat.apply(a,this.toArray()))},join:w.join,indexOf:w.indexOf||function(a,b){for(var c=b||0,d=this.length;c<d;c++)if(this[c]===a)return c;return-1},iterator:function(a,b,c,d){var e=[],f,g,h,i,n,l=this.context,m,p,t=this.selector;"string"===typeof a&&(d=c,c=b,b=a,a=!1);g=0;for(h=l.length;g<h;g++){var s=new u(l[g]);if("table"===b)f=
|
||||
c.call(s,l[g],g),f!==k&&e.push(f);else if("columns"===b||"rows"===b)f=c.call(s,l[g],this[g],g),f!==k&&e.push(f);else if("column"===b||"column-rows"===b||"row"===b||"cell"===b){p=this[g];"column-rows"===b&&(m=Da(l[g],t.opts));i=0;for(n=p.length;i<n;i++)f=p[i],f="cell"===b?c.call(s,l[g],f.row,f.column,g,i):c.call(s,l[g],f,g,i,m),f!==k&&e.push(f)}}return e.length||d?(a=new u(l,a?e.concat.apply([],e):e),b=a.selector,b.rows=t.rows,b.cols=t.cols,b.opts=t.opts,a):this},lastIndexOf:w.lastIndexOf||function(a,
|
||||
b){return this.indexOf.apply(this.toArray.reverse(),arguments)},length:0,map:function(a){var b=[];if(w.map)b=w.map.call(this,a,this);else for(var c=0,d=this.length;c<d;c++)b.push(a.call(this,this[c],c));return new u(this.context,b)},pluck:function(a){return this.map(function(b){return b[a]})},pop:w.pop,push:w.push,reduce:w.reduce||function(a,b){return jb(this,a,b,0,this.length,1)},reduceRight:w.reduceRight||function(a,b){return jb(this,a,b,this.length-1,-1,-1)},reverse:w.reverse,selector:null,shift:w.shift,
|
||||
sort:w.sort,splice:w.splice,toArray:function(){return w.slice.call(this)},to$:function(){return h(this)},toJQuery:function(){return h(this)},unique:function(){return new u(this.context,sa(this))},unshift:w.unshift});u.extend=function(a,b,c){if(c.length&&b&&(b instanceof u||b.__dt_wrapper)){var d,e,f,g=function(a,b,c){return function(){var d=b.apply(a,arguments);u.extend(d,d,c.methodExt);return d}};d=0;for(e=c.length;d<e;d++)f=c[d],b[f.name]="function"===typeof f.val?g(a,f.val,f):h.isPlainObject(f.val)?
|
||||
{}:f.val,b[f.name].__dt_wrapper=!0,u.extend(a,b[f.name],f.propExt)}};u.register=p=function(a,b){if(h.isArray(a))for(var c=0,d=a.length;c<d;c++)u.register(a[c],b);else for(var e=a.split("."),f=Ub,g,j,c=0,d=e.length;c<d;c++){g=(j=-1!==e[c].indexOf("()"))?e[c].replace("()",""):e[c];var i;a:{i=0;for(var n=f.length;i<n;i++)if(f[i].name===g){i=f[i];break a}i=null}i||(i={name:g,val:{},methodExt:[],propExt:[]},f.push(i));c===d-1?i.val=b:f=j?i.methodExt:i.propExt}};u.registerPlural=t=function(a,b,c){u.register(a,
|
||||
c);u.register(b,function(){var a=c.apply(this,arguments);return a===this?this:a instanceof u?a.length?h.isArray(a[0])?new u(a.context,a[0]):a[0]:k:a})};p("tables()",function(a){var b;if(a){b=u;var c=this.context;if("number"===typeof a)a=[c[a]];else var d=h.map(c,function(a){return a.nTable}),a=h(d).filter(a).map(function(){var a=h.inArray(this,d);return c[a]}).toArray();b=new b(a)}else b=this;return b});p("table()",function(a){var a=this.tables(a),b=a.context;return b.length?new u(b[0]):a});t("tables().nodes()",
|
||||
"table().node()",function(){return this.iterator("table",function(a){return a.nTable},1)});t("tables().body()","table().body()",function(){return this.iterator("table",function(a){return a.nTBody},1)});t("tables().header()","table().header()",function(){return this.iterator("table",function(a){return a.nTHead},1)});t("tables().footer()","table().footer()",function(){return this.iterator("table",function(a){return a.nTFoot},1)});t("tables().containers()","table().container()",function(){return this.iterator("table",
|
||||
function(a){return a.nTableWrapper},1)});p("draw()",function(a){return this.iterator("table",function(b){"page"===a?O(b):("string"===typeof a&&(a="full-hold"===a?!1:!0),T(b,!1===a))})});p("page()",function(a){return a===k?this.page.info().page:this.iterator("table",function(b){Va(b,a)})});p("page.info()",function(){if(0===this.context.length)return k;var a=this.context[0],b=a._iDisplayStart,c=a.oFeatures.bPaginate?a._iDisplayLength:-1,d=a.fnRecordsDisplay(),e=-1===c;return{page:e?0:Math.floor(b/c),
|
||||
pages:e?1:Math.ceil(d/c),start:b,end:a.fnDisplayEnd(),length:c,recordsTotal:a.fnRecordsTotal(),recordsDisplay:d,serverSide:"ssp"===y(a)}});p("page.len()",function(a){return a===k?0!==this.context.length?this.context[0]._iDisplayLength:k:this.iterator("table",function(b){Ta(b,a)})});var Vb=function(a,b,c){if(c){var d=new u(a);d.one("draw",function(){c(d.ajax.json())})}if("ssp"==y(a))T(a,b);else{C(a,!0);var e=a.jqXHR;e&&4!==e.readyState&&e.abort();ua(a,[],function(c){pa(a);for(var c=va(a,c),d=0,e=c.length;d<
|
||||
e;d++)N(a,c[d]);T(a,b);C(a,!1)})}};p("ajax.json()",function(){var a=this.context;if(0<a.length)return a[0].json});p("ajax.params()",function(){var a=this.context;if(0<a.length)return a[0].oAjaxData});p("ajax.reload()",function(a,b){return this.iterator("table",function(c){Vb(c,!1===b,a)})});p("ajax.url()",function(a){var b=this.context;if(a===k){if(0===b.length)return k;b=b[0];return b.ajax?h.isPlainObject(b.ajax)?b.ajax.url:b.ajax:b.sAjaxSource}return this.iterator("table",function(b){h.isPlainObject(b.ajax)?
|
||||
b.ajax.url=a:b.ajax=a})});p("ajax.url().load()",function(a,b){return this.iterator("table",function(c){Vb(c,!1===b,a)})});var bb=function(a,b,c,d,e){var f=[],g,j,i,n,l,m;i=typeof b;if(!b||"string"===i||"function"===i||b.length===k)b=[b];i=0;for(n=b.length;i<n;i++){j=b[i]&&b[i].split&&!b[i].match(/[\[\(:]/)?b[i].split(","):[b[i]];l=0;for(m=j.length;l<m;l++)(g=c("string"===typeof j[l]?h.trim(j[l]):j[l]))&&g.length&&(f=f.concat(g))}a=x.selector[a];if(a.length){i=0;for(n=a.length;i<n;i++)f=a[i](d,e,f)}return sa(f)},
|
||||
cb=function(a){a||(a={});a.filter&&a.search===k&&(a.search=a.filter);return h.extend({search:"none",order:"current",page:"all"},a)},db=function(a){for(var b=0,c=a.length;b<c;b++)if(0<a[b].length)return a[0]=a[b],a[0].length=1,a.length=1,a.context=[a.context[b]],a;a.length=0;return a},Da=function(a,b){var c,d,e,f=[],g=a.aiDisplay;c=a.aiDisplayMaster;var j=b.search;d=b.order;e=b.page;if("ssp"==y(a))return"removed"===j?[]:X(0,c.length);if("current"==e){c=a._iDisplayStart;for(d=a.fnDisplayEnd();c<d;c++)f.push(g[c])}else if("current"==
|
||||
d||"applied"==d)f="none"==j?c.slice():"applied"==j?g.slice():h.map(c,function(a){return-1===h.inArray(a,g)?a:null});else if("index"==d||"original"==d){c=0;for(d=a.aoData.length;c<d;c++)"none"==j?f.push(c):(e=h.inArray(c,g),(-1===e&&"removed"==j||0<=e&&"applied"==j)&&f.push(c))}return f};p("rows()",function(a,b){a===k?a="":h.isPlainObject(a)&&(b=a,a="");var b=cb(b),c=this.iterator("table",function(c){var e=b,f;return bb("row",a,function(a){var b=Qb(a);if(b!==null&&!e)return[b];f||(f=Da(c,e));if(b!==
|
||||
null&&h.inArray(b,f)!==-1)return[b];if(a===null||a===k||a==="")return f;if(typeof a==="function")return h.map(f,function(b){var e=c.aoData[b];return a(b,e._aData,e.nTr)?b:null});b=Tb(ja(c.aoData,f,"nTr"));if(a.nodeName){if(a._DT_RowIndex!==k)return[a._DT_RowIndex];if(a._DT_CellIndex)return[a._DT_CellIndex.row];b=h(a).closest("*[data-dt-row]");return b.length?[b.data("dt-row")]:[]}if(typeof a==="string"&&a.charAt(0)==="#"){var i=c.aIds[a.replace(/^#/,"")];if(i!==k)return[i.idx]}return h(b).filter(a).map(function(){return this._DT_RowIndex}).toArray()},
|
||||
c,e)},1);c.selector.rows=a;c.selector.opts=b;return c});p("rows().nodes()",function(){return this.iterator("row",function(a,b){return a.aoData[b].nTr||k},1)});p("rows().data()",function(){return this.iterator(!0,"rows",function(a,b){return ja(a.aoData,b,"_aData")},1)});t("rows().cache()","row().cache()",function(a){return this.iterator("row",function(b,c){var d=b.aoData[c];return"search"===a?d._aFilterData:d._aSortData},1)});t("rows().invalidate()","row().invalidate()",function(a){return this.iterator("row",
|
||||
function(b,c){da(b,c,a)})});t("rows().indexes()","row().index()",function(){return this.iterator("row",function(a,b){return b},1)});t("rows().ids()","row().id()",function(a){for(var b=[],c=this.context,d=0,e=c.length;d<e;d++)for(var f=0,g=this[d].length;f<g;f++){var h=c[d].rowIdFn(c[d].aoData[this[d][f]]._aData);b.push((!0===a?"#":"")+h)}return new u(c,b)});t("rows().remove()","row().remove()",function(){var a=this;this.iterator("row",function(b,c,d){var e=b.aoData,f=e[c],g,h,i,n,l;e.splice(c,1);
|
||||
g=0;for(h=e.length;g<h;g++)if(i=e[g],l=i.anCells,null!==i.nTr&&(i.nTr._DT_RowIndex=g),null!==l){i=0;for(n=l.length;i<n;i++)l[i]._DT_CellIndex.row=g}qa(b.aiDisplayMaster,c);qa(b.aiDisplay,c);qa(a[d],c,!1);Ua(b);c=b.rowIdFn(f._aData);c!==k&&delete b.aIds[c]});this.iterator("table",function(a){for(var c=0,d=a.aoData.length;c<d;c++)a.aoData[c].idx=c});return this});p("rows.add()",function(a){var b=this.iterator("table",function(b){var c,f,g,h=[];f=0;for(g=a.length;f<g;f++)c=a[f],c.nodeName&&"TR"===c.nodeName.toUpperCase()?
|
||||
h.push(oa(b,c)[0]):h.push(N(b,c));return h},1),c=this.rows(-1);c.pop();h.merge(c,b);return c});p("row()",function(a,b){return db(this.rows(a,b))});p("row().data()",function(a){var b=this.context;if(a===k)return b.length&&this.length?b[0].aoData[this[0]]._aData:k;b[0].aoData[this[0]]._aData=a;da(b[0],this[0],"data");return this});p("row().node()",function(){var a=this.context;return a.length&&this.length?a[0].aoData[this[0]].nTr||null:null});p("row.add()",function(a){a instanceof h&&a.length&&(a=a[0]);
|
||||
var b=this.iterator("table",function(b){return a.nodeName&&"TR"===a.nodeName.toUpperCase()?oa(b,a)[0]:N(b,a)});return this.row(b[0])});var eb=function(a,b){var c=a.context;if(c.length&&(c=c[0].aoData[b!==k?b:a[0]])&&c._details)c._details.remove(),c._detailsShow=k,c._details=k},Wb=function(a,b){var c=a.context;if(c.length&&a.length){var d=c[0].aoData[a[0]];if(d._details){(d._detailsShow=b)?d._details.insertAfter(d.nTr):d._details.detach();var e=c[0],f=new u(e),g=e.aoData;f.off("draw.dt.DT_details column-visibility.dt.DT_details destroy.dt.DT_details");
|
||||
0<D(g,"_details").length&&(f.on("draw.dt.DT_details",function(a,b){e===b&&f.rows({page:"current"}).eq(0).each(function(a){a=g[a];a._detailsShow&&a._details.insertAfter(a.nTr)})}),f.on("column-visibility.dt.DT_details",function(a,b){if(e===b)for(var c,d=ba(b),f=0,h=g.length;f<h;f++)c=g[f],c._details&&c._details.children("td[colspan]").attr("colspan",d)}),f.on("destroy.dt.DT_details",function(a,b){if(e===b)for(var c=0,d=g.length;c<d;c++)g[c]._details&&eb(f,c)}))}}};p("row().child()",function(a,b){var c=
|
||||
this.context;if(a===k)return c.length&&this.length?c[0].aoData[this[0]]._details:k;if(!0===a)this.child.show();else if(!1===a)eb(this);else if(c.length&&this.length){var d=c[0],c=c[0].aoData[this[0]],e=[],f=function(a,b){if(h.isArray(a)||a instanceof h)for(var c=0,k=a.length;c<k;c++)f(a[c],b);else a.nodeName&&"tr"===a.nodeName.toLowerCase()?e.push(a):(c=h("<tr><td/></tr>").addClass(b),h("td",c).addClass(b).html(a)[0].colSpan=ba(d),e.push(c[0]))};f(a,b);c._details&&c._details.detach();c._details=h(e);
|
||||
c._detailsShow&&c._details.insertAfter(c.nTr)}return this});p(["row().child.show()","row().child().show()"],function(){Wb(this,!0);return this});p(["row().child.hide()","row().child().hide()"],function(){Wb(this,!1);return this});p(["row().child.remove()","row().child().remove()"],function(){eb(this);return this});p("row().child.isShown()",function(){var a=this.context;return a.length&&this.length?a[0].aoData[this[0]]._detailsShow||!1:!1});var fc=/^([^:]+):(name|visIdx|visible)$/,Xb=function(a,b,
|
||||
c,d,e){for(var c=[],d=0,f=e.length;d<f;d++)c.push(B(a,e[d],b));return c};p("columns()",function(a,b){a===k?a="":h.isPlainObject(a)&&(b=a,a="");var b=cb(b),c=this.iterator("table",function(c){var e=a,f=b,g=c.aoColumns,j=D(g,"sName"),i=D(g,"nTh");return bb("column",e,function(a){var b=Qb(a);if(a==="")return X(g.length);if(b!==null)return[b>=0?b:g.length+b];if(typeof a==="function"){var e=Da(c,f);return h.map(g,function(b,f){return a(f,Xb(c,f,0,0,e),i[f])?f:null})}var k=typeof a==="string"?a.match(fc):
|
||||
"";if(k)switch(k[2]){case "visIdx":case "visible":b=parseInt(k[1],10);if(b<0){var m=h.map(g,function(a,b){return a.bVisible?b:null});return[m[m.length+b]]}return[$(c,b)];case "name":return h.map(j,function(a,b){return a===k[1]?b:null});default:return[]}if(a.nodeName&&a._DT_CellIndex)return[a._DT_CellIndex.column];b=h(i).filter(a).map(function(){return h.inArray(this,i)}).toArray();if(b.length||!a.nodeName)return b;b=h(a).closest("*[data-dt-column]");return b.length?[b.data("dt-column")]:[]},c,f)},
|
||||
1);c.selector.cols=a;c.selector.opts=b;return c});t("columns().header()","column().header()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTh},1)});t("columns().footer()","column().footer()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTf},1)});t("columns().data()","column().data()",function(){return this.iterator("column-rows",Xb,1)});t("columns().dataSrc()","column().dataSrc()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].mData},
|
||||
1)});t("columns().cache()","column().cache()",function(a){return this.iterator("column-rows",function(b,c,d,e,f){return ja(b.aoData,f,"search"===a?"_aFilterData":"_aSortData",c)},1)});t("columns().nodes()","column().nodes()",function(){return this.iterator("column-rows",function(a,b,c,d,e){return ja(a.aoData,e,"anCells",b)},1)});t("columns().visible()","column().visible()",function(a,b){var c=this.iterator("column",function(b,c){if(a===k)return b.aoColumns[c].bVisible;var f=b.aoColumns,g=f[c],j=b.aoData,
|
||||
i,n,l;if(a!==k&&g.bVisible!==a){if(a){var m=h.inArray(!0,D(f,"bVisible"),c+1);i=0;for(n=j.length;i<n;i++)l=j[i].nTr,f=j[i].anCells,l&&l.insertBefore(f[c],f[m]||null)}else h(D(b.aoData,"anCells",c)).detach();g.bVisible=a;fa(b,b.aoHeader);fa(b,b.aoFooter);za(b)}});a!==k&&(this.iterator("column",function(c,e){s(c,null,"column-visibility",[c,e,a,b])}),(b===k||b)&&this.columns.adjust());return c});t("columns().indexes()","column().index()",function(a){return this.iterator("column",function(b,c){return"visible"===
|
||||
a?aa(b,c):c},1)});p("columns.adjust()",function(){return this.iterator("table",function(a){Z(a)},1)});p("column.index()",function(a,b){if(0!==this.context.length){var c=this.context[0];if("fromVisible"===a||"toData"===a)return $(c,b);if("fromData"===a||"toVisible"===a)return aa(c,b)}});p("column()",function(a,b){return db(this.columns(a,b))});p("cells()",function(a,b,c){h.isPlainObject(a)&&(a.row===k?(c=a,a=null):(c=b,b=null));h.isPlainObject(b)&&(c=b,b=null);if(null===b||b===k)return this.iterator("table",
|
||||
function(b){var d=a,e=cb(c),f=b.aoData,g=Da(b,e),i=Tb(ja(f,g,"anCells")),j=h([].concat.apply([],i)),l,n=b.aoColumns.length,m,p,t,u,s,v;return bb("cell",d,function(a){var c=typeof a==="function";if(a===null||a===k||c){m=[];p=0;for(t=g.length;p<t;p++){l=g[p];for(u=0;u<n;u++){s={row:l,column:u};if(c){v=f[l];a(s,B(b,l,u),v.anCells?v.anCells[u]:null)&&m.push(s)}else m.push(s)}}return m}if(h.isPlainObject(a))return[a];c=j.filter(a).map(function(a,b){return{row:b._DT_CellIndex.row,column:b._DT_CellIndex.column}}).toArray();
|
||||
if(c.length||!a.nodeName)return c;v=h(a).closest("*[data-dt-row]");return v.length?[{row:v.data("dt-row"),column:v.data("dt-column")}]:[]},b,e)});var d=this.columns(b,c),e=this.rows(a,c),f,g,j,i,n,l=this.iterator("table",function(a,b){f=[];g=0;for(j=e[b].length;g<j;g++){i=0;for(n=d[b].length;i<n;i++)f.push({row:e[b][g],column:d[b][i]})}return f},1);h.extend(l.selector,{cols:b,rows:a,opts:c});return l});t("cells().nodes()","cell().node()",function(){return this.iterator("cell",function(a,b,c){return(a=
|
||||
a.aoData[b])&&a.anCells?a.anCells[c]:k},1)});p("cells().data()",function(){return this.iterator("cell",function(a,b,c){return B(a,b,c)},1)});t("cells().cache()","cell().cache()",function(a){a="search"===a?"_aFilterData":"_aSortData";return this.iterator("cell",function(b,c,d){return b.aoData[c][a][d]},1)});t("cells().render()","cell().render()",function(a){return this.iterator("cell",function(b,c,d){return B(b,c,d,a)},1)});t("cells().indexes()","cell().index()",function(){return this.iterator("cell",
|
||||
function(a,b,c){return{row:b,column:c,columnVisible:aa(a,c)}},1)});t("cells().invalidate()","cell().invalidate()",function(a){return this.iterator("cell",function(b,c,d){da(b,c,a,d)})});p("cell()",function(a,b,c){return db(this.cells(a,b,c))});p("cell().data()",function(a){var b=this.context,c=this[0];if(a===k)return b.length&&c.length?B(b[0],c[0].row,c[0].column):k;lb(b[0],c[0].row,c[0].column,a);da(b[0],c[0].row,"data",c[0].column);return this});p("order()",function(a,b){var c=this.context;if(a===
|
||||
k)return 0!==c.length?c[0].aaSorting:k;"number"===typeof a?a=[[a,b]]:a.length&&!h.isArray(a[0])&&(a=Array.prototype.slice.call(arguments));return this.iterator("table",function(b){b.aaSorting=a.slice()})});p("order.listener()",function(a,b,c){return this.iterator("table",function(d){Oa(d,a,b,c)})});p("order.fixed()",function(a){if(!a){var b=this.context,b=b.length?b[0].aaSortingFixed:k;return h.isArray(b)?{pre:b}:b}return this.iterator("table",function(b){b.aaSortingFixed=h.extend(!0,{},a)})});p(["columns().order()",
|
||||
"column().order()"],function(a){var b=this;return this.iterator("table",function(c,d){var e=[];h.each(b[d],function(b,c){e.push([c,a])});c.aaSorting=e})});p("search()",function(a,b,c,d){var e=this.context;return a===k?0!==e.length?e[0].oPreviousSearch.sSearch:k:this.iterator("table",function(e){e.oFeatures.bFilter&&ga(e,h.extend({},e.oPreviousSearch,{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:c,bCaseInsensitive:null===d?!0:d}),1)})});t("columns().search()","column().search()",function(a,
|
||||
b,c,d){return this.iterator("column",function(e,f){var g=e.aoPreSearchCols;if(a===k)return g[f].sSearch;e.oFeatures.bFilter&&(h.extend(g[f],{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:c,bCaseInsensitive:null===d?!0:d}),ga(e,e.oPreviousSearch,1))})});p("state()",function(){return this.context.length?this.context[0].oSavedState:null});p("state.clear()",function(){return this.iterator("table",function(a){a.fnStateSaveCallback.call(a.oInstance,a,{})})});p("state.loaded()",function(){return this.context.length?
|
||||
this.context[0].oLoadedState:null});p("state.save()",function(){return this.iterator("table",function(a){za(a)})});m.versionCheck=m.fnVersionCheck=function(a){for(var b=m.version.split("."),a=a.split("."),c,d,e=0,f=a.length;e<f;e++)if(c=parseInt(b[e],10)||0,d=parseInt(a[e],10)||0,c!==d)return c>d;return!0};m.isDataTable=m.fnIsDataTable=function(a){var b=h(a).get(0),c=!1;if(a instanceof m.Api)return!0;h.each(m.settings,function(a,e){var f=e.nScrollHead?h("table",e.nScrollHead)[0]:null,g=e.nScrollFoot?
|
||||
h("table",e.nScrollFoot)[0]:null;if(e.nTable===b||f===b||g===b)c=!0});return c};m.tables=m.fnTables=function(a){var b=!1;h.isPlainObject(a)&&(b=a.api,a=a.visible);var c=h.map(m.settings,function(b){if(!a||a&&h(b.nTable).is(":visible"))return b.nTable});return b?new u(c):c};m.camelToHungarian=J;p("$()",function(a,b){var c=this.rows(b).nodes(),c=h(c);return h([].concat(c.filter(a).toArray(),c.find(a).toArray()))});h.each(["on","one","off"],function(a,b){p(b+"()",function(){var a=Array.prototype.slice.call(arguments);
|
||||
a[0]=h.map(a[0].split(/\s/),function(a){return!a.match(/\.dt\b/)?a+".dt":a}).join(" ");var d=h(this.tables().nodes());d[b].apply(d,a);return this})});p("clear()",function(){return this.iterator("table",function(a){pa(a)})});p("settings()",function(){return new u(this.context,this.context)});p("init()",function(){var a=this.context;return a.length?a[0].oInit:null});p("data()",function(){return this.iterator("table",function(a){return D(a.aoData,"_aData")}).flatten()});p("destroy()",function(a){a=a||
|
||||
!1;return this.iterator("table",function(b){var c=b.nTableWrapper.parentNode,d=b.oClasses,e=b.nTable,f=b.nTBody,g=b.nTHead,j=b.nTFoot,i=h(e),f=h(f),k=h(b.nTableWrapper),l=h.map(b.aoData,function(a){return a.nTr}),p;b.bDestroying=!0;s(b,"aoDestroyCallback","destroy",[b]);a||(new u(b)).columns().visible(!0);k.off(".DT").find(":not(tbody *)").off(".DT");h(E).off(".DT-"+b.sInstance);e!=g.parentNode&&(i.children("thead").detach(),i.append(g));j&&e!=j.parentNode&&(i.children("tfoot").detach(),i.append(j));
|
||||
b.aaSorting=[];b.aaSortingFixed=[];ya(b);h(l).removeClass(b.asStripeClasses.join(" "));h("th, td",g).removeClass(d.sSortable+" "+d.sSortableAsc+" "+d.sSortableDesc+" "+d.sSortableNone);b.bJUI&&(h("th span."+d.sSortIcon+", td span."+d.sSortIcon,g).detach(),h("th, td",g).each(function(){var a=h("div."+d.sSortJUIWrapper,this);h(this).append(a.contents());a.detach()}));f.children().detach();f.append(l);g=a?"remove":"detach";i[g]();k[g]();!a&&c&&(c.insertBefore(e,b.nTableReinsertBefore),i.css("width",
|
||||
b.sDestroyWidth).removeClass(d.sTable),(p=b.asDestroyStripes.length)&&f.children().each(function(a){h(this).addClass(b.asDestroyStripes[a%p])}));c=h.inArray(b,m.settings);-1!==c&&m.settings.splice(c,1)})});h.each(["column","row","cell"],function(a,b){p(b+"s().every()",function(a){var d=this.selector.opts,e=this;return this.iterator(b,function(f,g,h,i,m){a.call(e[b](g,"cell"===b?h:d,"cell"===b?d:k),g,h,i,m)})})});p("i18n()",function(a,b,c){var d=this.context[0],a=R(a)(d.oLanguage);a===k&&(a=b);c!==
|
||||
k&&h.isPlainObject(a)&&(a=a[c]!==k?a[c]:a._);return a.replace("%d",c)});m.version="1.10.13";m.settings=[];m.models={};m.models.oSearch={bCaseInsensitive:!0,sSearch:"",bRegex:!1,bSmart:!0};m.models.oRow={nTr:null,anCells:null,_aData:[],_aSortData:null,_aFilterData:null,_sFilterRow:null,_sRowStripe:"",src:null,idx:-1};m.models.oColumn={idx:null,aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bVisible:null,_sManualType:null,_bAttrSrc:!1,fnCreatedCell:null,fnGetData:null,fnSetData:null,
|
||||
mData:null,mRender:null,nTh:null,nTf:null,sClass:null,sContentPadding:null,sDefaultContent:null,sName:null,sSortDataType:"std",sSortingClass:null,sSortingClassJUI:null,sTitle:null,sType:null,sWidth:null,sWidthOrig:null};m.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:[],ajax:null,aLengthMenu:[10,25,50,100],aoColumns:null,aoColumnDefs:null,aoSearchCols:[],asStripeClasses:null,bAutoWidth:!0,bDeferRender:!1,bDestroy:!1,bFilter:!0,bInfo:!0,bJQueryUI:!1,bLengthChange:!0,bPaginate:!0,bProcessing:!1,
|
||||
bRetrieve:!1,bScrollCollapse:!1,bServerSide:!1,bSort:!0,bSortMulti:!0,bSortCellsTop:!1,bSortClasses:!0,bStateSave:!1,fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(a){return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g,this.oLanguage.sThousands)},fnHeaderCallback:null,fnInfoCallback:null,fnInitComplete:null,fnPreDrawCallback:null,fnRowCallback:null,fnServerData:null,fnServerParams:null,fnStateLoadCallback:function(a){try{return JSON.parse((-1===a.iStateDuration?
|
||||
sessionStorage:localStorage).getItem("DataTables_"+a.sInstance+"_"+location.pathname))}catch(b){}},fnStateLoadParams:null,fnStateLoaded:null,fnStateSaveCallback:function(a,b){try{(-1===a.iStateDuration?sessionStorage:localStorage).setItem("DataTables_"+a.sInstance+"_"+location.pathname,JSON.stringify(b))}catch(c){}},fnStateSaveParams:null,iStateDuration:7200,iDeferLoading:null,iDisplayLength:10,iDisplayStart:0,iTabIndex:0,oClasses:{},oLanguage:{oAria:{sSortAscending:": activate to sort column ascending",
|
||||
sSortDescending:": activate to sort column descending"},oPaginate:{sFirst:"First",sLast:"Last",sNext:"Next",sPrevious:"Previous"},sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sDecimal:"",sThousands:",",sLengthMenu:"Show _MENU_ entries",sLoadingRecords:"Loading...",sProcessing:"Processing...",sSearch:"Search:",sSearchPlaceholder:"",sUrl:"",sZeroRecords:"No matching records found"},
|
||||
oSearch:h.extend({},m.models.oSearch),sAjaxDataProp:"data",sAjaxSource:null,sDom:"lfrtip",searchDelay:null,sPaginationType:"simple_numbers",sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET",renderer:null,rowId:"DT_RowId"};Y(m.defaults);m.defaults.column={aDataSort:null,iDataSort:-1,asSorting:["asc","desc"],bSearchable:!0,bSortable:!0,bVisible:!0,fnCreatedCell:null,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,
|
||||
sType:null,sWidth:null};Y(m.defaults.column);m.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender:null,bFilter:null,bInfo:null,bLengthChange:null,bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortMulti:null,bSortClasses:null,bStateSave:null},oScroll:{bCollapse:null,iBarWidth:0,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1,bScrollbarLeft:!1,bBounding:!1,barWidth:0},ajax:null,aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],
|
||||
aIds:{},aoColumns:[],aoHeader:[],aoFooter:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],asStripeClasses:null,asDestroyStripes:[],sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,
|
||||
searchDelay:null,sPaginationType:"two_button",iStateDuration:0,aoStateSave:[],aoStateLoad:[],oSavedState:null,oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,bAjaxDataGet:!0,jqXHR:null,json:k,oAjaxData:k,fnServerData:null,aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,bJUI:null,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],
|
||||
fnRecordsTotal:function(){return"ssp"==y(this)?1*this._iRecordsTotal:this.aiDisplayMaster.length},fnRecordsDisplay:function(){return"ssp"==y(this)?1*this._iRecordsDisplay:this.aiDisplay.length},fnDisplayEnd:function(){var a=this._iDisplayLength,b=this._iDisplayStart,c=b+a,d=this.aiDisplay.length,e=this.oFeatures,f=e.bPaginate;return e.bServerSide?!1===f||-1===a?b+d:Math.min(b+a,this._iRecordsDisplay):!f||c>d||-1===a?d:c},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null,
|
||||
aLastSort:[],oPlugins:{},rowIdFn:null,rowId:null};m.ext=x={buttons:{},classes:{},builder:"-source-",errMode:"alert",feature:[],search:[],selector:{cell:[],column:[],row:[]},internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},header:{}},order:{},type:{detect:[],search:{},order:{}},_unique:0,fnVersionCheck:m.fnVersionCheck,iApiIndex:0,oJUIClasses:{},sVersion:m.version};h.extend(x,{afnFiltering:x.search,aTypes:x.type.detect,ofnSearch:x.type.search,oSort:x.type.order,afnSortData:x.order,aoFeatures:x.feature,
|
||||
oApi:x.internal,oStdClasses:x.classes,oPagination:x.pager});h.extend(m.ext.classes,{sTable:"dataTable",sNoFooter:"no-footer",sPageButton:"paginate_button",sPageButtonActive:"current",sPageButtonDisabled:"disabled",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",
|
||||
sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sFilterInput:"",sLengthSelect:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sHeaderTH:"",sFooterTH:"",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",
|
||||
sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sJUIHeader:"",sJUIFooter:""});var Ea="",Ea="",G=Ea+"ui-state-default",ka=Ea+"css_right ui-icon ui-icon-",Yb=Ea+"fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix";h.extend(m.ext.oJUIClasses,m.ext.classes,{sPageButton:"fg-button ui-button "+G,sPageButtonActive:"ui-state-disabled",sPageButtonDisabled:"ui-state-disabled",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sSortAsc:G+" sorting_asc",
|
||||
sSortDesc:G+" sorting_desc",sSortable:G+" sorting",sSortableAsc:G+" sorting_asc_disabled",sSortableDesc:G+" sorting_desc_disabled",sSortableNone:G+" sorting_disabled",sSortJUIAsc:ka+"triangle-1-n",sSortJUIDesc:ka+"triangle-1-s",sSortJUI:ka+"carat-2-n-s",sSortJUIAscAllowed:ka+"carat-1-n",sSortJUIDescAllowed:ka+"carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",sSortIcon:"DataTables_sort_icon",sScrollHead:"dataTables_scrollHead "+G,sScrollFoot:"dataTables_scrollFoot "+G,sHeaderTH:G,sFooterTH:G,sJUIHeader:Yb+
|
||||
" ui-corner-tl ui-corner-tr",sJUIFooter:Yb+" ui-corner-bl ui-corner-br"});var Nb=m.ext.pager;h.extend(Nb,{simple:function(){return["previous","next"]},full:function(){return["first","previous","next","last"]},numbers:function(a,b){return[ia(a,b)]},simple_numbers:function(a,b){return["previous",ia(a,b),"next"]},full_numbers:function(a,b){return["first","previous",ia(a,b),"next","last"]},first_last_numbers:function(a,b){return["first",ia(a,b),"last"]},_numbers:ia,numbers_length:7});h.extend(!0,m.ext.renderer,
|
||||
{pageButton:{_:function(a,b,c,d,e,f){var g=a.oClasses,j=a.oLanguage.oPaginate,i=a.oLanguage.oAria.paginate||{},m,l,p=0,r=function(b,d){var k,t,u,s,v=function(b){Va(a,b.data.action,true)};k=0;for(t=d.length;k<t;k++){s=d[k];if(h.isArray(s)){u=h("<"+(s.DT_el||"div")+"/>").appendTo(b);r(u,s)}else{m=null;l="";switch(s){case "ellipsis":b.append('<span class="ellipsis">…</span>');break;case "first":m=j.sFirst;l=s+(e>0?"":" "+g.sPageButtonDisabled);break;case "previous":m=j.sPrevious;l=s+(e>0?"":" "+
|
||||
g.sPageButtonDisabled);break;case "next":m=j.sNext;l=s+(e<f-1?"":" "+g.sPageButtonDisabled);break;case "last":m=j.sLast;l=s+(e<f-1?"":" "+g.sPageButtonDisabled);break;default:m=s+1;l=e===s?g.sPageButtonActive:""}if(m!==null){u=h("<a>",{"class":g.sPageButton+" "+l,"aria-controls":a.sTableId,"aria-label":i[s],"data-dt-idx":p,tabindex:a.iTabIndex,id:c===0&&typeof s==="string"?a.sTableId+"_"+s:null}).html(m).appendTo(b);Ya(u,{action:s},v);p++}}}},t;try{t=h(b).find(H.activeElement).data("dt-idx")}catch(u){}r(h(b).empty(),
|
||||
d);t!==k&&h(b).find("[data-dt-idx="+t+"]").focus()}}});h.extend(m.ext.type.detect,[function(a,b){var c=b.oLanguage.sDecimal;return ab(a,c)?"num"+c:null},function(a){if(a&&!(a instanceof Date)&&!cc.test(a))return null;var b=Date.parse(a);return null!==b&&!isNaN(b)||M(a)?"date":null},function(a,b){var c=b.oLanguage.sDecimal;return ab(a,c,!0)?"num-fmt"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Sb(a,c)?"html-num"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Sb(a,c,!0)?"html-num-fmt"+
|
||||
c:null},function(a){return M(a)||"string"===typeof a&&-1!==a.indexOf("<")?"html":null}]);h.extend(m.ext.type.search,{html:function(a){return M(a)?a:"string"===typeof a?a.replace(Pb," ").replace(Ca,""):""},string:function(a){return M(a)?a:"string"===typeof a?a.replace(Pb," "):a}});var Ba=function(a,b,c,d){if(0!==a&&(!a||"-"===a))return-Infinity;b&&(a=Rb(a,b));a.replace&&(c&&(a=a.replace(c,"")),d&&(a=a.replace(d,"")));return 1*a};h.extend(x.type.order,{"date-pre":function(a){return Date.parse(a)||-Infinity},
|
||||
"html-pre":function(a){return M(a)?"":a.replace?a.replace(/<.*?>/g,"").toLowerCase():a+""},"string-pre":function(a){return M(a)?"":"string"===typeof a?a.toLowerCase():!a.toString?"":a.toString()},"string-asc":function(a,b){return a<b?-1:a>b?1:0},"string-desc":function(a,b){return a<b?1:a>b?-1:0}});fb("");h.extend(!0,m.ext.renderer,{header:{_:function(a,b,c,d){h(a.nTable).on("order.dt.DT",function(e,f,g,h){if(a===f){e=c.idx;b.removeClass(c.sSortingClass+" "+d.sSortAsc+" "+d.sSortDesc).addClass(h[e]==
|
||||
"asc"?d.sSortAsc:h[e]=="desc"?d.sSortDesc:c.sSortingClass)}})},jqueryui:function(a,b,c,d){h("<div/>").addClass(d.sSortJUIWrapper).append(b.contents()).append(h("<span/>").addClass(d.sSortIcon+" "+c.sSortingClassJUI)).appendTo(b);h(a.nTable).on("order.dt.DT",function(e,f,g,h){if(a===f){e=c.idx;b.removeClass(d.sSortAsc+" "+d.sSortDesc).addClass(h[e]=="asc"?d.sSortAsc:h[e]=="desc"?d.sSortDesc:c.sSortingClass);b.find("span."+d.sSortIcon).removeClass(d.sSortJUIAsc+" "+d.sSortJUIDesc+" "+d.sSortJUI+" "+
|
||||
d.sSortJUIAscAllowed+" "+d.sSortJUIDescAllowed).addClass(h[e]=="asc"?d.sSortJUIAsc:h[e]=="desc"?d.sSortJUIDesc:c.sSortingClassJUI)}})}}});var Zb=function(a){return"string"===typeof a?a.replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""):a};m.render={number:function(a,b,c,d,e){return{display:function(f){if("number"!==typeof f&&"string"!==typeof f)return f;var g=0>f?"-":"",h=parseFloat(f);if(isNaN(h))return Zb(f);h=h.toFixed(c);f=Math.abs(h);h=parseInt(f,10);f=c?b+(f-h).toFixed(c).substring(2):
|
||||
"";return g+(d||"")+h.toString().replace(/\B(?=(\d{3})+(?!\d))/g,a)+f+(e||"")}}},text:function(){return{display:Zb}}};h.extend(m.ext.internal,{_fnExternApiFunc:Ob,_fnBuildAjax:ua,_fnAjaxUpdate:nb,_fnAjaxParameters:wb,_fnAjaxUpdateDraw:xb,_fnAjaxDataSrc:va,_fnAddColumn:Ga,_fnColumnOptions:la,_fnAdjustColumnSizing:Z,_fnVisibleToColumnIndex:$,_fnColumnIndexToVisible:aa,_fnVisbleColumns:ba,_fnGetColumns:na,_fnColumnTypes:Ia,_fnApplyColumnDefs:kb,_fnHungarianMap:Y,_fnCamelToHungarian:J,_fnLanguageCompat:Fa,
|
||||
_fnBrowserDetect:ib,_fnAddData:N,_fnAddTr:oa,_fnNodeToDataIndex:function(a,b){return b._DT_RowIndex!==k?b._DT_RowIndex:null},_fnNodeToColumnIndex:function(a,b,c){return h.inArray(c,a.aoData[b].anCells)},_fnGetCellData:B,_fnSetCellData:lb,_fnSplitObjNotation:La,_fnGetObjectDataFn:R,_fnSetObjectDataFn:S,_fnGetDataMaster:Ma,_fnClearTable:pa,_fnDeleteIndex:qa,_fnInvalidate:da,_fnGetRowElements:Ka,_fnCreateTr:Ja,_fnBuildHead:mb,_fnDrawHead:fa,_fnDraw:O,_fnReDraw:T,_fnAddOptionsHtml:pb,_fnDetectHeader:ea,
|
||||
_fnGetUniqueThs:ta,_fnFeatureHtmlFilter:rb,_fnFilterComplete:ga,_fnFilterCustom:Ab,_fnFilterColumn:zb,_fnFilter:yb,_fnFilterCreateSearch:Ra,_fnEscapeRegex:Sa,_fnFilterData:Bb,_fnFeatureHtmlInfo:ub,_fnUpdateInfo:Eb,_fnInfoMacros:Fb,_fnInitialise:ha,_fnInitComplete:wa,_fnLengthChange:Ta,_fnFeatureHtmlLength:qb,_fnFeatureHtmlPaginate:vb,_fnPageChange:Va,_fnFeatureHtmlProcessing:sb,_fnProcessingDisplay:C,_fnFeatureHtmlTable:tb,_fnScrollDraw:ma,_fnApplyToChildren:I,_fnCalculateColumnWidths:Ha,_fnThrottle:Qa,
|
||||
_fnConvertToWidth:Gb,_fnGetWidestNode:Hb,_fnGetMaxLenString:Ib,_fnStringToCss:v,_fnSortFlatten:W,_fnSort:ob,_fnSortAria:Kb,_fnSortListener:Xa,_fnSortAttachListener:Oa,_fnSortingClasses:ya,_fnSortData:Jb,_fnSaveState:za,_fnLoadState:Lb,_fnSettingsFromNode:Aa,_fnLog:K,_fnMap:F,_fnBindAction:Ya,_fnCallbackReg:z,_fnCallbackFire:s,_fnLengthOverflow:Ua,_fnRenderer:Pa,_fnDataSource:y,_fnRowAttributes:Na,_fnCalculateEnd:function(){}});h.fn.dataTable=m;m.$=h;h.fn.dataTableSettings=m.settings;h.fn.dataTableExt=
|
||||
m.ext;h.fn.DataTable=function(a){return h(this).dataTable(a).api()};h.each(m,function(a,b){h.fn.DataTable[a]=b});return h.fn.dataTable});
|
||||
12
ara/static/js/jquery.matchHeight-min.js
vendored
12
ara/static/js/jquery.matchHeight-min.js
vendored
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
* jquery-match-height 0.7.0 by @liabru
|
||||
* http://brm.io/jquery-match-height/
|
||||
* License MIT
|
||||
*/
|
||||
!function(t){"use strict";"function"==typeof define&&define.amd?define(["jquery"],t):"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):t(jQuery)}(function(t){var e=-1,o=-1,i=function(t){return parseFloat(t)||0},a=function(e){var o=1,a=t(e),n=null,r=[];return a.each(function(){var e=t(this),a=e.offset().top-i(e.css("margin-top")),s=r.length>0?r[r.length-1]:null;null===s?r.push(e):Math.floor(Math.abs(n-a))<=o?r[r.length-1]=s.add(e):r.push(e),n=a}),r},n=function(e){var o={
|
||||
byRow:!0,property:"height",target:null,remove:!1};return"object"==typeof e?t.extend(o,e):("boolean"==typeof e?o.byRow=e:"remove"===e&&(o.remove=!0),o)},r=t.fn.matchHeight=function(e){var o=n(e);if(o.remove){var i=this;return this.css(o.property,""),t.each(r._groups,function(t,e){e.elements=e.elements.not(i)}),this}return this.length<=1&&!o.target?this:(r._groups.push({elements:this,options:o}),r._apply(this,o),this)};r.version="0.7.0",r._groups=[],r._throttle=80,r._maintainScroll=!1,r._beforeUpdate=null,
|
||||
r._afterUpdate=null,r._rows=a,r._parse=i,r._parseOptions=n,r._apply=function(e,o){var s=n(o),h=t(e),l=[h],c=t(window).scrollTop(),p=t("html").outerHeight(!0),d=h.parents().filter(":hidden");return d.each(function(){var e=t(this);e.data("style-cache",e.attr("style"))}),d.css("display","block"),s.byRow&&!s.target&&(h.each(function(){var e=t(this),o=e.css("display");"inline-block"!==o&&"flex"!==o&&"inline-flex"!==o&&(o="block"),e.data("style-cache",e.attr("style")),e.css({display:o,"padding-top":"0",
|
||||
"padding-bottom":"0","margin-top":"0","margin-bottom":"0","border-top-width":"0","border-bottom-width":"0",height:"100px",overflow:"hidden"})}),l=a(h),h.each(function(){var e=t(this);e.attr("style",e.data("style-cache")||"")})),t.each(l,function(e,o){var a=t(o),n=0;if(s.target)n=s.target.outerHeight(!1);else{if(s.byRow&&a.length<=1)return void a.css(s.property,"");a.each(function(){var e=t(this),o=e.attr("style"),i=e.css("display");"inline-block"!==i&&"flex"!==i&&"inline-flex"!==i&&(i="block");var a={
|
||||
display:i};a[s.property]="",e.css(a),e.outerHeight(!1)>n&&(n=e.outerHeight(!1)),o?e.attr("style",o):e.css("display","")})}a.each(function(){var e=t(this),o=0;s.target&&e.is(s.target)||("border-box"!==e.css("box-sizing")&&(o+=i(e.css("border-top-width"))+i(e.css("border-bottom-width")),o+=i(e.css("padding-top"))+i(e.css("padding-bottom"))),e.css(s.property,n-o+"px"))})}),d.each(function(){var e=t(this);e.attr("style",e.data("style-cache")||null)}),r._maintainScroll&&t(window).scrollTop(c/p*t("html").outerHeight(!0)),
|
||||
this},r._applyDataApi=function(){var e={};t("[data-match-height], [data-mh]").each(function(){var o=t(this),i=o.attr("data-mh")||o.attr("data-match-height");i in e?e[i]=e[i].add(o):e[i]=o}),t.each(e,function(){this.matchHeight(!0)})};var s=function(e){r._beforeUpdate&&r._beforeUpdate(e,r._groups),t.each(r._groups,function(){r._apply(this.elements,this.options)}),r._afterUpdate&&r._afterUpdate(e,r._groups)};r._update=function(i,a){if(a&&"resize"===a.type){var n=t(window).width();if(n===e)return;e=n;
|
||||
}i?-1===o&&(o=setTimeout(function(){s(a),o=-1},r._throttle)):s(a)},t(r._applyDataApi),t(window).bind("load",function(t){r._update(!1,t)}),t(window).bind("resize orientationchange",function(t){r._update(!0,t)})});
|
||||
@@ -1,37 +1,15 @@
|
||||
{% extends "layout.html" %}
|
||||
{% include "head.html" %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Header container -->
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<h1><strong>Playbook</strong>: {{ macros.make_link('playbook.show_playbook', file_.playbook.path, playbook=file_.playbook.id) }}{% if file_.playbook.ansible_version %} with Ansible v{{ file_.playbook.ansible_version }}{% endif %}</h1>
|
||||
<h2>{{ file_.playbook.time_start |datefmt }} <span class="fa fa-angle-double-right"></span> {{ file_.playbook.time_end |datefmt }}</h2>
|
||||
<h1><strong>File</strong>: {{ file_.path }}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>File: <strong>{{ file_.path }}</strong></h2>
|
||||
<p>Last updated: {{ file_.playbook.time_end |datefmt }}</p>
|
||||
<p>Ansible version: <strong>{{ file_.playbook.ansible_version }}</strong></p>
|
||||
{{ file_.content.content | yamlhighlight | safe }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Detail container -->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-11 col-md-offset-1">
|
||||
<h1><strong>Contents</strong></h1>
|
||||
{{ file_.content.content | yamlhighlight | safe }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
/* Highlight the anchor line */
|
||||
var hash = $(location).attr('hash');
|
||||
$(hash).closest('span').addClass('hll');
|
||||
|
||||
/* Refresh the highlighted line when clicking on a new line */
|
||||
$("a").click(function(){
|
||||
$("span.hll").removeClass('hll');
|
||||
var hash = $(this).attr('href');
|
||||
$(hash).closest('span').addClass('hll');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
27
ara/templates/file_index.html
Normal file
27
ara/templates/file_index.html
Normal file
@@ -0,0 +1,27 @@
|
||||
{% include "head.html" %}
|
||||
<!--
|
||||
This template is not actually served or linked from within the web application.
|
||||
-->
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>File</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for file in files %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('file.show_file', file_=file.id) }}">{{ file.id }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
26
ara/templates/head.html
Normal file
26
ara/templates/head.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="layout-pf">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="ARA | Ansible Run Analysis records Ansible playbook runs seamlessly to make them easier to visualize, understand and troubleshoot">
|
||||
|
||||
<title>ARA | Ansible Run Analysis</title>
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='images/favicon.ico') }}">
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/ara.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/patternfly.min.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
<link href="{{ url_for('static', filename='css/patternfly.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/patternfly-additions.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/pygments.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/ara.css') }}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
</head>
|
||||
@@ -1,49 +1,61 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main container -->
|
||||
<!-- Begin home -->
|
||||
<div class="container-fluid">
|
||||
<div class="row text-center">
|
||||
<h1>ARA - Ansible Run Analysis</h1>
|
||||
<h2><a href="https://github.com/openstack/ara">ARA</a> records <a href="https://www.ansible.com/">Ansible</a> Playbook runs seamlessly to make them easier to visualize, understand and troubleshoot.</h2>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2><strong>Latest playbook runs</strong></h2>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Playbook</th>
|
||||
<th class="col-md-1">Hosts</th>
|
||||
<th class="date col-md-2">Date</th>
|
||||
<th class="date col-md-1">Duration</th>
|
||||
<th class="col-md-1"><span class="ok label status-label">OK</span></th>
|
||||
<th class="col-md-1"><span class="changed label status-label">CHANGED</span></th>
|
||||
<th class="col-md-1"><span class="failed label status-label">FAILED</span></th>
|
||||
<th class="col-md-1"><span class="skipped label status-label">SKIPPED</span></th>
|
||||
<th class="col-md-1"><span class="unreachable label status-label">UNREACHABLE</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for playbook in playbooks %}
|
||||
<tr>
|
||||
<td class="vert-align">{{ macros.render_status(stats[playbook.id].status) }}</td>
|
||||
<td>{{ macros.make_link('playbook.show_playbook', playbook.path|pathtruncate,
|
||||
playbook=playbook.id) }}</td>
|
||||
<td>{{ playbook.hosts|list|length }}</td>
|
||||
<td>{{ playbook.time_start |datefmt }}</td>
|
||||
<td>{{ playbook.duration |timefmt }}</td>
|
||||
<td>{{ stats[playbook.id].ok }}</td>
|
||||
<td>{{ stats[playbook.id].changed }}</td>
|
||||
<td>{{ stats[playbook.id].failed }}</td>
|
||||
<td>{{ stats[playbook.id].skipped }}</td>
|
||||
<td>{{ stats[playbook.id].unreachable }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="col-md-custom col-md-offset-custom">
|
||||
<div class="row text-center">
|
||||
<br>
|
||||
<img src="{{ url_for('static', filename='images/logo-main.svg') }}" width="287" height="226" alt="ARA: Ansible Run Analysis" />
|
||||
<h1><strong>ARA - Ansible Run Analysis</strong></h1>
|
||||
<h2><a href="https://github.com/openstack/ara" target="_blank">ARA</a> records <a href="https://www.ansible.com/" target="_blank">Ansible</a> playbook runs seamlessly to make them easier to visualize, understand and troubleshoot.</h2>
|
||||
|
||||
{% if playbooks and playbooks > 0 %}
|
||||
<h2>Here's the data that ARA is making available to help you:</h2>
|
||||
<br>
|
||||
<ul class="list-group stat-highlight col-md-2 col-md-offset-5">
|
||||
<li class="list-group-item">
|
||||
<span class="badge">{{ playbooks }}</span>
|
||||
Playbook runs
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span class="badge">{{ tasks }}</span>
|
||||
Tasks
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span class="badge">{{ task_results }}</span>
|
||||
Task results
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span class="badge">{{ hosts }}</span>
|
||||
Hosts
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span class="badge">{{ host_facts }}</span>
|
||||
Hosts with gathered facts
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span class="badge">{{ files }}</span>
|
||||
Playbook, role and task files
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span class="badge">{{ records }}</span>
|
||||
Records
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="row text-center">
|
||||
<h2>Get started by looking at your <a href="{{ url_for('reports.report_list') }}">playbook reports</a>.</h2>
|
||||
{% else %}
|
||||
<h2><strong>The ARA database is empty or the web application is not configured properly to find your playbook data.</strong></h2>
|
||||
<h2>Get started by <a href="https://ara.readthedocs.io/en/latest/configuration.html">configuring Ansible to use ARA</a>.</h2>
|
||||
<h2>Your data will be available in the interface as soon as you've ran an <code>ansible-playbook</code> at least once after configuring Ansible to use ARA.</h2>
|
||||
<br>
|
||||
<br>
|
||||
<h3>Need help ? Look at the <a href="https://ara.readthedocs.io/en/latest/">documentation</a> or come chat with users and developers on IRC in #ara on the freenode network.</h3>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
<!-- End home -->
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,60 +1,30 @@
|
||||
{% extends "layout.html" %}
|
||||
{% include "head.html" %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Header container -->
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<h1><strong>Playbook</strong>: {{ macros.make_link('playbook.show_playbook', host.playbook.path, playbook=host.playbook.id) }}{% if host.playbook.ansible_version %} with Ansible v{{ host.playbook.ansible_version }}{% endif %}</h1>
|
||||
<h2>{{ host.playbook.time_start |datefmt }} <span class="fa fa-angle-double-right"></span> {{ host.playbook.time_end |datefmt }}</h2>
|
||||
<h1><strong>Host</strong>: {{ host.name }}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>Host facts: <strong>{{ host.name }}</strong></h2>
|
||||
<p>Last updated: {{ host.facts.timestamp |datefmt }}</p>
|
||||
<p>Ansible version: <strong>{{ host.playbook.ansible_version }}</strong></p>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Fact</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for fact, value in facts %}
|
||||
<tr>
|
||||
<td id="{{ fact }}"><a href="#{{ fact }}">{{ fact }}</a></td>
|
||||
<td><pre>{{ value | to_nice_json }}</pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<h2><strong>Stats</strong></h2>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="ok label status-label">OK</span></th>
|
||||
<th><span class="changed label status-label">CHANGED</span></th>
|
||||
<th><span class="failed label status-label">FAILED</span></th>
|
||||
<th><span class="skipped label status-label">SKIPPED</span></th>
|
||||
<th><span class="unreachable label status-label">UNREACHABLE</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
{{ macros.statslink(host.stats, 'ok', host.playbook, host) }}
|
||||
{{ macros.statslink(host.stats, 'changed', host.playbook, host) }}
|
||||
{{ macros.statslink(host.stats, 'failed', host.playbook, host) }}
|
||||
{{ macros.statslink(host.stats, 'skipped', host.playbook, host) }}
|
||||
{{ macros.statslink(host.stats, 'unreachable', host.playbook, host) }}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2><strong>Facts</strong></h2>
|
||||
<p>Last updated at {{ host.facts.timestamp |datefmt }}</p>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Fact</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for fact, value in facts %}
|
||||
<tr>
|
||||
<td id="{{ fact }}"><a href="#{{ fact }}">{{ fact }}</a></td>
|
||||
<td><pre>{{ value | to_nice_json }}</pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
29
ara/templates/host_index.html
Normal file
29
ara/templates/host_index.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{% include "head.html" %}
|
||||
<!--
|
||||
This template is not actually served or linked from within the web application.
|
||||
-->
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Host</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for host in hosts %}
|
||||
{% if host.facts %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('host.show_host', id=host.id) }}">{{ host.id }}</a></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,44 +1,10 @@
|
||||
<html class="layout-pf">
|
||||
{% include "head.html" %}
|
||||
{% import "macros.html" as macros %}
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>ARA | Ansible Run Analysis</title>
|
||||
<meta name="description" content="ARA: Ansible Run Analysis records Ansible Playbook runs and provides intuitive interfaces to browse them.">
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='images/favicon.ico') }}">
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/ara.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/jquery.matchHeight-min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/patternfly.min.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
<link href="{{ url_for('static', filename='css/patternfly.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/patternfly-additions.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/pygments.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/ara.css') }}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% include "navbar.html" %}
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// matchHeight the contents of each .card-pf and then the .card-pf itself
|
||||
$(".row-cards-pf > [class*='col'] > .card-pf .card-pf-title").matchHeight();
|
||||
$(".row-cards-pf > [class*='col'] > .card-pf > .card-pf-body").matchHeight();
|
||||
$(".row-cards-pf > [class*='col'] > .card-pf > .card-pf-footer").matchHeight();
|
||||
$(".row-cards-pf > [class*='col'] > .card-pf").matchHeight();
|
||||
|
||||
// Initialize the vertical navigation
|
||||
$().setupVerticalNavigation(true);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,45 +1,86 @@
|
||||
<div id="macros">
|
||||
{% macro make_link(view, label) -%}
|
||||
<a href="{{ url_for(view, **kwargs) }}">{{label}}</a>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{% macro statslink(stats, stat, playbook, host) %}
|
||||
<td>
|
||||
{% if stats[stat] and stats[stat] >= 1 -%}
|
||||
{{ make_link('playbook.playbook_results', stats[stat],
|
||||
playbook=playbook.id, host=host.name, status=stat) }}
|
||||
{% else -%}
|
||||
0
|
||||
{% endif %}
|
||||
</td>
|
||||
{% macro render_status(status) %}
|
||||
{% if status == 'success' %}
|
||||
<div data-toggle="tooltip" data-placement="bottom" title="Playbook completed successfully">
|
||||
<span class="pficon pficon-ok list-view-pf-icon-sm list-view-pf-icon-success"></span>
|
||||
</div>
|
||||
{% elif status == 'failed' %}
|
||||
<div data-toggle="tooltip" data-placement="bottom" title="Playbook completed with errors">
|
||||
<span class="pficon pficon-error-circle-o list-view-pf-icon-sm list-view-pf-icon-danger"></span>
|
||||
</div>
|
||||
{% elif status == 'incomplete' %}
|
||||
<div data-toggle="tooltip" data-placement="bottom" title="Playbook execution was interrupted: data will be incomplete and inconsistent">
|
||||
<span class="pficon pficon-info list-view-pf-icon-sm list-view-pf-icon-info"></span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div data-toggle="tooltip" data-placement="bottom" title="The status of this playbook is unknown">
|
||||
<span class="fa fa-question-circle"></span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_status(status) %}
|
||||
{% if status == 'success' %}
|
||||
<span class="pficon pficon-ok list-view-pf-icon-md list-view-pf-icon-success" title="Playbook finished successfully"></span>
|
||||
{% elif status == 'failed' %}
|
||||
<span class="pficon pficon-error-circle-o list-view-pf-icon-md list-view-pf-icon-danger" title="Playbook finished with errors"></span>
|
||||
{% elif status == 'incomplete' %}
|
||||
<span class="pficon pficon-info list-view-pf-icon-md list-view-pf-icon-info" title="Playbook was interrupted: data will be incomplete."></span>
|
||||
{% else %}
|
||||
<span class="fa fa-question-circle" title="The status of this playbook is unknown."></span>
|
||||
{% endif %}
|
||||
{% macro render_playbook_summary(playbook) %}
|
||||
<dl class='dl-horizontal'>
|
||||
<dt>Ansible version</dt>
|
||||
<dd>{{ playbook.ansible_version }}</dd>
|
||||
<dt>Path</dt>
|
||||
<dd>{{ playbook.path }}</dd>
|
||||
</dl>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_host_facts(host) %}
|
||||
{% if host.facts.values %}
|
||||
{% set facts = host.facts.values|from_json %}
|
||||
{% set items = ['ansible_fqdn',
|
||||
'ansible_processor_cores',
|
||||
'ansible_processor_vcpus',
|
||||
'ansible_memtotal_mb',
|
||||
'ansible_swaptotal_mb',
|
||||
'ansible_distribution',
|
||||
'ansible_distribution_version'] %}
|
||||
{% set lists = ['ansible_all_ipv4_addresses', 'ansible_all_ipv6_addresses'] %}
|
||||
<dl class='dl-horizontal'>
|
||||
{% for value in items %}
|
||||
{% if facts[value] %}
|
||||
<dt>{{ value }}</dt>
|
||||
<dd>{{ facts[value] }}</dd>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for value in lists %}
|
||||
{% if facts[value] %}
|
||||
<dt>{{ value }}</dt>
|
||||
{% for item in facts[value] %}
|
||||
<dd>{{ item }}</dd>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</dl>
|
||||
{% else %}
|
||||
No host facts recorded for this host.
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_value_type(value, type) %}
|
||||
{% if type == 'json' or type == 'dict' %}
|
||||
<pre>{{ value |to_nice_json |safe }}</pre>
|
||||
{% elif type == 'list' %}
|
||||
<ul class="text-left">
|
||||
{% for item in value %}
|
||||
<li>{{ item }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% elif type == 'url' %}
|
||||
<a href="{{ value }}" target="_blank">{{ value }}</a>
|
||||
{% else %}
|
||||
<pre>{{ value }}</pre>
|
||||
{% endif %}
|
||||
{% if type == 'json' or type == 'dict' %}
|
||||
<pre>{{ value |to_nice_json |safe }}</pre>
|
||||
{% elif type == 'list' %}
|
||||
<ul class="text-left">
|
||||
{% for item in value %}
|
||||
<li>{{ item }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% elif type == 'url' %}
|
||||
<a href="{{ value }}" target="_blank">{{ value }}</a>
|
||||
{% else %}
|
||||
<pre>{{ value }}</pre>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro result_page_length(length) %}
|
||||
{% if length == 0 %}
|
||||
"paging": false,
|
||||
{% else %}
|
||||
"pageLength": {{ length }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,38 @@
|
||||
<nav class="navbar navbar-default navbar-pf" role="navigation">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse-1">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="{{ url_for('home.main') }}">
|
||||
<img src="{{ url_for('static', filename='images/logo.svg') }}" alt="ARA: Ansible Run Analysis" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse navbar-collapse-1">
|
||||
<ul class="nav navbar-nav navbar-utility">
|
||||
<li><a href="https://ara.readthedocs.io/en/latest/" target="_blank">Documentation</a></li>
|
||||
<li><a href="https://github.com/openstack/ara" target="_blank"><strong>ARA</strong> {{ ara_version }}</a></li>
|
||||
<li><a href="https://www.ansible.com/" target="_blank"><strong>Ansible</strong> {{ ansible_version }}</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-primary persistent-secondary">
|
||||
<li><a href="{{ url_for('home.main') }}">Home</a></li>
|
||||
<li><a href="{{ url_for('playbook.playbook_summary') }}">All Playbooks</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse-1">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="{{ url_for('home.main') }}">
|
||||
<img src="{{ url_for('static', filename='images/logo-header.svg') }}" width="81" height="32" alt="ARA: Ansible Run Analysis" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse navbar-collapse-1">
|
||||
<ul class="nav navbar-nav navbar-utility">
|
||||
<li><a href="https://ara.readthedocs.io/en/latest/" target="_blank">Documentation</a></li>
|
||||
<li><a href="https://github.com/openstack/ara" target="_blank"><strong>ARA</strong> {{ ara_version }}</a></li>
|
||||
<li><a href="https://www.ansible.com/" target="_blank"><strong>Ansible</strong> {{ ansible_version }}</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-primary">
|
||||
{% if active and active == 'home' %}
|
||||
<li class="active">
|
||||
{% else %}
|
||||
<li>
|
||||
{% endif %}
|
||||
<a href="{{ url_for('home.main') }}">Home</a>
|
||||
</li>
|
||||
{% if playbooks and playbooks > 0 %}
|
||||
{% if active and active == 'reports' %}
|
||||
<li class="active">
|
||||
{% else %}
|
||||
<li>
|
||||
{% endif %}
|
||||
<a href="{{ url_for('reports.report_list') }}">Playbook reports</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Header container -->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<h1><strong>Playbook</strong>: {{ macros.make_link('playbook.show_playbook', playbook.path, playbook=playbook.id) }}{% if playbook.ansible_version %} with Ansible v{{ playbook.ansible_version }}{% endif %}</h1>
|
||||
<h2>{{ playbook.time_start |datefmt }} <span class="fa fa-angle-double-right"></span> {{ playbook.time_end |datefmt }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Dashboard container -->
|
||||
<div class="container-fluid container-cards-pf">
|
||||
<div class="row row-cards-pf">
|
||||
<div class="col-md-2 col-md-offset-1">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<a href="#hosts">
|
||||
<span class="pficon pficon-server"></span>
|
||||
<span class="card-pf-aggregate-status-count">{{ playbook.hosts.count() }}</span> <strong>Hosts</strong>
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<a href="#plays">
|
||||
<span class="pficon pficon-flag"></span>
|
||||
<span class="card-pf-aggregate-status-count">{{ playbook.plays.count() }}</span> <strong>Plays</strong>
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<a href="#files">
|
||||
<span class="pficon pficon-folder-open"></span>
|
||||
<span class="card-pf-aggregate-status-count">{{ playbook.files.count() }}</span> <strong>Files</strong>
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<a href="#tasks">
|
||||
<span class="fa fa-check"></span>
|
||||
<span class="card-pf-aggregate-status-count">{{ playbook.tasks.count() }}</span> <strong>Tasks</strong>
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<span class="fa fa-clock-o"></span>
|
||||
<span class="card-pf-aggregate-status-count">{{ playbook.duration |timefmt }}</span> <strong>Duration</strong>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Detail container -->
|
||||
<div class="container-fluid container-pf-nav-pf-vertical container-pf-nav-pf-vertical-with-secondary">
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<a href="#hosts"><h2 id="hosts"><strong>Hosts</strong></h2></a>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-left">Host</span></th>
|
||||
<th class="col-md-1"><span class="ok label status-label">OK</span></th>
|
||||
<th class="col-md-1"><span class="changed label status-label">CHANGED</span></th>
|
||||
<th class="col-md-1"><span class="failed label status-label">FAILED</span></th>
|
||||
<th class="col-md-1"><span class="skipped label status-label">SKIPPED</span></th>
|
||||
<th class="col-md-1"><span class="unreachable label status-label">UNREACHABLE</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for stat in playbook.stats %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if stat.host.facts %}
|
||||
<a href="{{ url_for('host.show_host', id=stat.host.id) }}">
|
||||
<span class="pf pficon-info action-icon action-available pull-left" title="Host facts available"></span>
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="pf pficon-info action-icon action-unavailable pull-left" title="Host facts unavailable"></span>
|
||||
{% endif %}
|
||||
<span class="pull-left">{{ macros.make_link('playbook.playbook_results', stat.host.name, playbook=playbook.id, host=stat.host.name) }}</span>
|
||||
</td>
|
||||
{{ macros.statslink(stat, 'ok', playbook, stat.host) }}
|
||||
{{ macros.statslink(stat, 'changed', playbook, stat.host) }}
|
||||
{{ macros.statslink(stat, 'failed', playbook, stat.host) }}
|
||||
{{ macros.statslink(stat, 'skipped', playbook, stat.host) }}
|
||||
{{ macros.statslink(stat, 'unreachable', playbook, stat.host) }}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<a href="#plays"><h2 id="plays"><strong>Plays</strong></h2></a>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-left">Play</span></th>
|
||||
<th class="date col-md-2">Start</th>
|
||||
<th class="date col-md-2">End</th>
|
||||
<th class="date col-md-1">Duration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for play in plays %}
|
||||
<tr>
|
||||
<td><span class="pull-left">{{ macros.make_link('playbook.playbook_results', play.name, playbook=playbook.id, play=play.id) }}</span></td>
|
||||
<td>{{ play.time_start |datefmt }}</td>
|
||||
<td>{{ play.time_end |datefmt }}</td>
|
||||
<td>{{ play.duration |timefmt }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<a href="#files"><h2 id="files"><strong>Files</strong></h2></a>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-left">File</span></th>
|
||||
<th class="date col-md-1"></th>
|
||||
<th class="date col-md-1"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="pull-left">{{ playbook_file.path |pathtruncate(75) }} <strong>(Playbook)</strong></span></td>
|
||||
<td colspan="2">{{ macros.make_link('file.show_file', 'View', file_=playbook_file.id) }}</td>
|
||||
</tr>
|
||||
{% for file in files %}
|
||||
<tr>
|
||||
<td><span class="pull-left">{{ file.path |pathtruncate(75) }}</span></td>
|
||||
<td>{{ macros.make_link('file.show_file', 'View', file_=file.id) }}</td>
|
||||
<td>{{ macros.make_link('playbook.show_playbook', 'Filter', playbook=playbook.id, file_=file.id) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<a href="#tasks"><h2 id="tasks"><strong>Tasks</strong></h2></a>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-left">Task</span></th>
|
||||
<th>Action</th>
|
||||
<th class="col-md-3">File</th>
|
||||
<th class="date col-md-1">Elapsed</th>
|
||||
<th class="date col-md-1">Duration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for task in tasks %}
|
||||
<tr>
|
||||
<td><span class="pull-left">{{ macros.make_link('playbook.playbook_results', task.name, playbook=playbook.id, task=task.id) }}</span></td>
|
||||
<td>{{ task.action }}</td>
|
||||
<td>
|
||||
{% if task.file %}
|
||||
{{ macros.make_link('file.show_file', task.file.path |pathtruncate(25) + "#" + task.lineno|string, file_=task.file.id, _anchor="line-" ~ task.lineno) }}
|
||||
<a href="{{ url_for('playbook.show_playbook', playbook=playbook.id, file_=task.file.id) }}"><span class="fa fa-filter pull-right" title="Filter tasks from this file"></span></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ task.offset_from_playbook |timefmt }}</td>
|
||||
<td>{{ task.duration |timefmt }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2><strong>Recorded data</strong></h2>
|
||||
{% if data %}
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-right">Key</span></th>
|
||||
<th><span class="pull-left">Value</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for item in data %}
|
||||
<tr>
|
||||
<td class="vert-align" id="{{ item.id }}"><span class="pull-right"><a href="#{{ item.id }}">{{ item.key }}</a></span></td>
|
||||
<td><span class="pull-left">{{ macros.render_value_type(item.value, item.type) }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<h3>There is no recorded data for this playbook. Get started with <a href="http://ara.readthedocs.io/en/latest/usage.html#using-the-ara-record-module" target="_blank">ara_record</a>.</h3>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,45 +1,16 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h1><strong>All Playbooks</strong></h1>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Playbook</th>
|
||||
<th class="col-md-1">Hosts</th>
|
||||
<th class="date col-md-2">Date</th>
|
||||
<th class="date col-md-1">Duration</th>
|
||||
<th class="col-md-1"><span class="ok label status-label">OK</span></th>
|
||||
<th class="col-md-1"><span class="changed label status-label">CHANGED</span></th>
|
||||
<th class="col-md-1"><span class="failed label status-label">FAILED</span></th>
|
||||
<th class="col-md-1"><span class="skipped label status-label">SKIPPED</span></th>
|
||||
<th class="col-md-1"><span class="unreachable label status-label">UNREACHABLE</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for playbook in playbooks %}
|
||||
<tr>
|
||||
<td class="vert-align">{{ macros.render_status(stats[playbook.id].status) }}</td>
|
||||
<td>
|
||||
<span class="pull-left">{{ macros.make_link('playbook.show_playbook', playbook.path|pathtruncate(35), playbook=playbook.id) }}</span>
|
||||
</td>
|
||||
<td>{{ playbook.hosts|list|length }}</td>
|
||||
<td>{{ playbook.time_start |datefmt }}</td>
|
||||
<td>{{ playbook.duration |timefmt }}</td>
|
||||
<td>{{ stats[playbook.id].ok }}</td>
|
||||
<td>{{ stats[playbook.id].changed }}</td>
|
||||
<td>{{ stats[playbook.id].failed }}</td>
|
||||
<td>{{ stats[playbook.id].skipped }}</td>
|
||||
<td>{{ stats[playbook.id].unreachable }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row text-center">
|
||||
<h1>This URL has moved to the <a href="{{ url_for('reports.report_list') }}">Playbook reports</a> page.</h1>
|
||||
<h2>It is deprecated and will be removed in a future version.</h2>
|
||||
|
||||
<h2>Click <a href="{{ url_for('reports.report_list') }}">here</a> to access the new page if you are not redirected automatically.</h2>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.setTimeout(function(){
|
||||
window.location.href = "{{ url_for('reports.report_list') }}";
|
||||
}, 10000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Header container -->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-6 col-md-10 col-md-offset-1">
|
||||
<h1><strong>Playbook</strong>: {{ macros.make_link('playbook.show_playbook', playbook.path, playbook=playbook.id) }}{% if playbook.ansible_version %} with Ansible v{{ playbook.ansible_version }}{% endif %}</h1>
|
||||
<h2>{{ playbook.time_start |datefmt }} <span class="fa fa-angle-double-right"></span> {{ playbook.time_end |datefmt }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Dashboard container -->
|
||||
<div class="container-fluid container-cards-pf">
|
||||
<div class="row row-cards-pf">
|
||||
<div class="col-md-2 col-md-offset-1">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<a href="#hosts">
|
||||
<span class="pficon pficon-server"></span>
|
||||
<span class="card-pf-aggregate-status-count">{{ playbook.hosts.count() }}</span> <strong>Hosts</strong>
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<a href="#plays">
|
||||
<span class="pficon pficon-flag"></span>
|
||||
<span class="card-pf-aggregate-status-count">{{ playbook.plays.count() }}</span> <strong>Plays</strong>
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<a href="#files">
|
||||
<span class="pficon pficon-folder-open"></span>
|
||||
<span class="card-pf-aggregate-status-count">{{ playbook.files.count() }}</span> <strong>Files</strong>
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<a href="#tasks">
|
||||
<span class="fa fa-check"></span>
|
||||
<span class="card-pf-aggregate-status-count">{{ playbook.tasks.count() }}</span> <strong>Tasks</strong>
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<span class="fa fa-clock-o"></span>
|
||||
<span class="card-pf-aggregate-status-count">{{ playbook.duration |timefmt }}</span> <strong>Duration</strong>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Detail container -->
|
||||
<div class="container-fluid container-pf-nav-pf-vertical container-pf-nav-pf-vertical-with-secondary">
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<a href="#hosts"><h2 id="hosts"><strong>Hosts</strong></h2></a>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-left">Host</span></th>
|
||||
<th class="col-md-1"><span class="ok label status-label">OK</span></th>
|
||||
<th class="col-md-1"><span class="changed label status-label">CHANGED</span></th>
|
||||
<th class="col-md-1"><span class="failed label status-label">FAILED</span></th>
|
||||
<th class="col-md-1"><span class="skipped label status-label">SKIPPED</span></th>
|
||||
<th class="col-md-1"><span class="unreachable label status-label">UNREACHABLE</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for stat in playbook.stats %}
|
||||
{% if hosts and stat.host.name in hosts %}
|
||||
<tr class="active">
|
||||
{% else %}
|
||||
<tr>
|
||||
{% endif %}
|
||||
<td>
|
||||
{% if stat.host.facts %}
|
||||
<a href="{{ url_for('host.show_host', id=stat.host.id) }}">
|
||||
<span class="pf pficon-info action-icon action-available pull-left" title="Host facts available"></span>
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="pf pficon-info action-icon action-unavailable pull-left" title="Host facts unavailable"></span>
|
||||
{% endif %}
|
||||
<span class="pull-left">{{ macros.make_link('playbook.playbook_results', stat.host.name, playbook=playbook.id, host=stat.host.name) }}</span>
|
||||
</td>
|
||||
{{ macros.statslink(stat, 'ok', playbook, stat.host) }}
|
||||
{{ macros.statslink(stat, 'changed', playbook, stat.host) }}
|
||||
{{ macros.statslink(stat, 'failed', playbook, stat.host) }}
|
||||
{{ macros.statslink(stat, 'skipped', playbook, stat.host) }}
|
||||
{{ macros.statslink(stat, 'unreachable', playbook, stat.host) }}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<a href="#plays"><h2 id="plays"><strong>Plays</strong></h2></a>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-left">Play</span></th>
|
||||
<th class="date col-md-2">Start</th>
|
||||
<th class="date col-md-2">End</th>
|
||||
<th class="date col-md-1">Duration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for play in playbook.plays %}
|
||||
{% if plays and play.id in plays %}
|
||||
<tr class="active">
|
||||
{% else %}
|
||||
<tr>
|
||||
{% endif %}
|
||||
<td><span class="pull-left">{{ macros.make_link('playbook.playbook_results', play.name, playbook=playbook.id, play=play.id) }}</span></td>
|
||||
<td class="date">{{ play.time_start |datefmt }}</td>
|
||||
<td class="date">{{ play.time_end |datefmt }}</td>
|
||||
<td>{{ play.duration |timefmt }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<a href="#files"><h2 id="files"><strong>Files</strong></h2></a>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-left">File</span></th>
|
||||
<th class="date col-md-1"></th>
|
||||
<th class="date col-md-1"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="pull-left">{{ playbook_file.path |pathtruncate(75) }} <strong>(Playbook)</strong></span></td>
|
||||
<td colspan="2">{{ macros.make_link('file.show_file', 'View', file_=playbook_file.id) }}</td>
|
||||
</tr>
|
||||
{% for file in files %}
|
||||
<tr>
|
||||
<td><span class="pull-left">{{ file.path |pathtruncate(75) }}</span></td>
|
||||
<td>{{ macros.make_link('file.show_file', 'View', file_=file.id) }}</td>
|
||||
<td>{{ macros.make_link('playbook.show_playbook', 'Filter', playbook=playbook.id, file_=file.id) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<a href="#tasks"><h2 id="tasks"><strong>Task Results</strong></h2></a>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-left">Task</span></th>
|
||||
<th>Host</th>
|
||||
<th>Action</th>
|
||||
<th class="date col-md-1">Elapsed</th>
|
||||
<th class="date col-md-1">Duration</th>
|
||||
<th class="col-md-1">Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for result in task_results %}
|
||||
{% set status = result.derived_status %}
|
||||
<tr>
|
||||
<td><span class="pull-left">{{ result.task.name }}</span></td>
|
||||
<td>{{ result.host.name }}</td>
|
||||
<td>{{ result.task.action }}</td>
|
||||
<td>{{ result.task.offset_from_playbook|timefmt }}</td>
|
||||
<td>{{ result.duration |timefmt }}</td>
|
||||
<td><span class="{{ status }} label status-label">{{ status |upper }}</span></td>
|
||||
<td>
|
||||
{{ macros.make_link('result.show_result', 'details', task_result=result.id) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2><strong>Recorded data</strong></h2>
|
||||
{% if data %}
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-right">Key</span></th>
|
||||
<th><span class="pull-left">Value</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for item in data %}
|
||||
<tr>
|
||||
<td class="vert-align" id="{{ item.id }}"><span class="pull-right"><a href="#{{ item.id }}">{{ item.key }}</a></span></td>
|
||||
<td><span class="pull-left">{{ macros.render_value_type(item.value, item.type) }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<h3>There is no recorded data for this playbook. Get started with <a href="http://ara.readthedocs.io/en/latest/usage.html#using-the-ara-record-module" target="_blank">ara_record</a>.</h3>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
510
ara/templates/report_list.html
Normal file
510
ara/templates/report_list.html
Normal file
@@ -0,0 +1,510 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Begin report list -->
|
||||
<div class="col-md-custom col-md-offset-custom">
|
||||
<div class="row">
|
||||
<br>
|
||||
<div class="alert alert-info">
|
||||
<span class="pficon pficon-info"></span>
|
||||
Browse your playbook reports here: Get started by expanding the "<strong>Hosts</strong>", "<strong>Plays</strong>", "<strong>Files</strong>" or "<strong>Tasks</strong>" panels below.
|
||||
You can mouse over most elements to have more details about them.
|
||||
</div>
|
||||
<div class="list-group list-view-pf list-view-pf-view">
|
||||
{% for playbook in playbooks.items %}
|
||||
<!-- Begin playbook iteration -->
|
||||
<div class="list-group-item">
|
||||
<div class="list-view-pf-left">
|
||||
{{ macros.render_status(stats[playbook.id].status) }}
|
||||
</div>
|
||||
<!-- Begin playbook entry in the list -->
|
||||
<div class="list-view-pf-main-info">
|
||||
<div class="list-view-pf-body">
|
||||
<div class="list-view-pf-description">
|
||||
<div class="list-group-item-heading timestamp-heading">
|
||||
<span title="Date at which the playbook started">{{ playbook.time_start |datefmt }}</span>
|
||||
</div>
|
||||
<div class="list-group-item-heading" data-toggle="tooltip" data-placement="bottom" data-html="true" title="{{ macros.render_playbook_summary(playbook) }}">
|
||||
<a href="#" data-toggle="modal" data-target="#file_modal" data-load="{{ playbook.file.id }}">{{ playbook.path }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info">
|
||||
<div class="list-view-pf-additional-info-item" title="Duration of the entire playbook">
|
||||
<span class="fa fa-clock-o"></span>
|
||||
<strong>{{ playbook.duration |timefmt }}</strong>
|
||||
</div>
|
||||
<br>
|
||||
<div class="list-view-pf-additional-info-item" title="Hosts involved in the playbook">
|
||||
<div class="list-view-pf-expand">
|
||||
<span class="fa fa-angle-right"></span>
|
||||
<span class="pficon pficon-server"></span>
|
||||
<strong>{{ playbook.hosts|list|length }}</strong> Hosts
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info-item" title="Plays involved in the playbook">
|
||||
<div class="list-view-pf-expand">
|
||||
<span class="fa fa-angle-right"></span>
|
||||
<span class="pficon pficon-flag"></span>
|
||||
<strong>{{ playbook.plays|list|length }}</strong> Plays
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info-item" title="Role and task files involved in the playbook">
|
||||
<div class="list-view-pf-expand">
|
||||
<span class="fa fa-angle-right"></span>
|
||||
<span class="fa fa-folder-open"></span>
|
||||
<strong>{{ playbook.files|list|length }}</strong> Files
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info-item" title="Tasks involved in the playbook">
|
||||
<div class="list-view-pf-expand">
|
||||
<span class="fa fa-angle-right"></span>
|
||||
<span class="fa fa-check"></span>
|
||||
<strong>{{ playbook.tasks|list|length }}</strong> Tasks
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info-item" title="Records saved with ara_record throughout the playbook">
|
||||
<div class="list-view-pf-expand">
|
||||
<span class="fa fa-angle-right"></span>
|
||||
<span class="fa pficon-save"></span>
|
||||
<strong>{{ playbook.data|list|length }}</strong> Records
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End playbook entry in the list -->
|
||||
|
||||
<!-- Begin playbook details -->
|
||||
<!-- Begin playbook host details -->
|
||||
<div class="list-group-item-container container-fluid hidden">
|
||||
<div class="close">
|
||||
<span class="pficon pficon-close"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h3>
|
||||
<strong>Hosts</strong>
|
||||
</h3>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><span class="pficon pficon-info"></span> Browsing tips</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<ul>
|
||||
<li>All table columns are sortable</li>
|
||||
<li>If facts have been gathered:
|
||||
<ul>
|
||||
<li>Hover hosts to see a quick highlight summary of the recorded host facts</li>
|
||||
<li>Click on a host to browse the full list of recorded host facts</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Use the search box to find specific hosts based on their properties, for example:
|
||||
<ul>
|
||||
<li><strong>localhost</strong> would match only 'localhost' (provided by default: the Ansible inventory hostname)</li>
|
||||
<li><strong>fedora</strong> would match all 'fedora' hosts (provided by the <i>ansible_distribution</i> fact)</li>
|
||||
<li><strong>192.168.1</strong> would match all hosts with an IP in the '192.168.1' range (provided by the <i>ansible_all_ipv4_addresses</i> fact)</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<table class="table table-striped table-bordered table-hover table-condensed" id="hosts_{{ playbook.id }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-left">Host</span></th>
|
||||
<th class="col-md-2"><span class="ok label status-label">OK</span></th>
|
||||
<th class="col-md-2"><span class="changed label status-label">CHANGED</span></th>
|
||||
<th class="col-md-2"><span class="failed label status-label">FAILED</span></th>
|
||||
<th class="col-md-2"><span class="skipped label status-label">SKIPPED</span></th>
|
||||
<th class="col-md-2"><span class="unreachable label status-label">UNREACHABLE</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for stat in playbook.stats %}
|
||||
<tr>
|
||||
<td>
|
||||
<span class="pull-left">
|
||||
<div data-toggle="tooltip" data-placement="bottom" data-html="true" title="{{ macros.render_host_facts(stat.host) }}">
|
||||
{% if stat.host.facts %}
|
||||
<a href="#" data-toggle="modal" data-target="#host_modal" data-load="{{ stat.host.id }}">{{ stat.host.name }}</a>
|
||||
{% else %}
|
||||
{{ stat.host.name }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ stat['ok'] if stat['ok'] >= 1 else '0' }}</td>
|
||||
<td>{{ stat['changed'] if stat['changed'] >= 1 else '0' }}</td>
|
||||
<td>{{ stat['failed'] if stat['failed'] >= 1 else '0' }}</td>
|
||||
<td>{{ stat['skipped'] if stat['skipped'] >= 1 else '0' }}</td>
|
||||
<td>{{ stat['unreachable'] if stat['unreachable'] >= 1 else '0' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End playbook host details -->
|
||||
|
||||
<!-- Begin playbook play details -->
|
||||
<div class="list-group-item-container container-fluid hidden">
|
||||
<div class="close">
|
||||
<span class="pficon pficon-close"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h3>
|
||||
<strong>Plays</strong>
|
||||
</h3>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><span class="pficon pficon-info"></span> Browsing tips</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<ul>
|
||||
<li>All table columns are sortable</li>
|
||||
<li>Use the search box to find a specific play based on it's name</li>
|
||||
<li>To see your playbook file, click the playbook file path in the playbook report list</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<table class="table table-striped table-bordered table-hover table-condensed" id="plays_{{ playbook.id }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-left">Play</span></th>
|
||||
<th class="col-md-2">Start</th>
|
||||
<th class="col-md-2">End</th>
|
||||
<th class="col-md-2">Duration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for play in playbook.plays %}
|
||||
<tr>
|
||||
<td><span class="pull-left">{{ play.name }}</span></td>
|
||||
<td>{{ play.time_start |datefmt }}</td>
|
||||
<td>{{ play.time_end |datefmt }}</td>
|
||||
<td>{{ play.duration |timefmt }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End playbook play details -->
|
||||
|
||||
<!-- Begin playbook file details -->
|
||||
<div class="list-group-item-container container-fluid hidden">
|
||||
<div class="close">
|
||||
<span class="pficon pficon-close"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h3>
|
||||
<strong>Files</strong>
|
||||
</h3>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><span class="pficon pficon-info"></span> Browsing tips</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<ul>
|
||||
<li>The table column is sortable</li>
|
||||
<li>Use the search box to find a specific file based on it's name or path</li>
|
||||
<li>Click on any file to see it's contents</li>
|
||||
<li><strong>Note:</strong> This panel is temporary and will be improved in a future version.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<table class="table table-striped table-bordered table-hover table-condensed" id="files_{{ playbook.id }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-left">File</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for file in playbook.files %}
|
||||
<tr>
|
||||
<td><span class="pull-left"><a href="#" data-toggle="modal" data-target="#file_modal" data-load="{{ file.id }}">{{ file.path }}</a></span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End playbook file details -->
|
||||
|
||||
<!-- Begin playbook task details -->
|
||||
<div class="list-group-item-container container-fluid hidden">
|
||||
<div class="close">
|
||||
<span class="pficon pficon-close"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h3>
|
||||
<strong>Tasks</strong>
|
||||
</h3>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><span class="pficon pficon-info"></span> Browsing tips</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<ul>
|
||||
<li>All table columns are sortable</li>
|
||||
<li>Click on a task status (ex: 'failed') to see the detailed task result</li>
|
||||
<li>Use the search box to find specific tasks based on their properties, for example:
|
||||
<ul>
|
||||
<li><strong>do something</strong> would only match tasks with 'do something' in their name</li>
|
||||
<li><strong>localhost</strong> would only match tasks on the host 'localhost'</li>
|
||||
<li><strong>include</strong> would match tasks with 'include' in their task name or as the task action
|
||||
<li><strong>failed</strong> would only match tasks with the 'failed' status</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<table class="table table-striped table-bordered table-hover table-condensed" id="tasks_{{ playbook.id }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-left">Task</span></th>
|
||||
<th>Host</th>
|
||||
<th>Action</th>
|
||||
<th class="col-md-1">Elapsed</th>
|
||||
<th class="col-md-1">Duration</th>
|
||||
<th class="col-md-1">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if playbook.tasks %}
|
||||
{% for task in playbook.tasks %}
|
||||
{% if task.task_results %}
|
||||
{% for result in task.task_results %}
|
||||
{% set status = result.derived_status %}
|
||||
<tr>
|
||||
<td><span class="pull-left">{{ result.task.name }}</span></td>
|
||||
<td>{{ result.host.name }}</td>
|
||||
{% if result.task.file %}
|
||||
<td>
|
||||
<a href="#" data-toggle="modal" data-target="#file_modal" data-load="{{ result.task.file.id }}#line-{{ result.task.lineno }}">{{ result.task.action }}</a
|
||||
</td>
|
||||
{% else %}
|
||||
<td>{{ result.task.action }}</td>
|
||||
{% endif %}
|
||||
<td>{{ result.task.offset_from_playbook|timefmt }}</td>
|
||||
<td>{{ result.duration |timefmt }}</td>
|
||||
<td>
|
||||
<a href="#" data-toggle="modal" data-target="#task_result_modal" data-load="{{ result.id }}"><span class="{{ status }} label status-label">{{ status |upper }}</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End playbook task details -->
|
||||
|
||||
<!-- Begin playbook data details -->
|
||||
<div class="list-group-item-container container-fluid hidden">
|
||||
<div class="close">
|
||||
<span class="pficon pficon-close"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h3>
|
||||
<strong>Records</strong>
|
||||
</h3>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><span class="pficon pficon-info"></span> Browsing tips</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<ul>
|
||||
<li>Data recorded with the <a href="http://ara.readthedocs.io/en/latest/usage.html#ara-record" target="_blank">ara_record</a> Ansible module will be displayed here</li>
|
||||
<li>Use the search box to find a specific key based on it's name or expected contents.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{% if playbook.data and playbook.data|list|length > 0 %}
|
||||
<table class="table table-striped table-bordered table-hover table-condensed" id="data_{{ playbook.id }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-right">Key</span></th>
|
||||
<th><span class="pull-left">Value</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in playbook.data %}
|
||||
<tr>
|
||||
<td class="vert-align"><span class="pull-right" title="Type: {{ item.type }}">{{ item.key }}</span></td>
|
||||
<td><span class="pull-left">{{ macros.render_value_type(item.value, item.type) }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="text-center">
|
||||
<h2>There is no recorded data for this playbook.</h2>
|
||||
<h3>The <a href="http://ara.readthedocs.io/en/latest/usage.html#ara-record" target="_blank">ara_record</a> Ansible module helps you record arbitrary persistent data that is displayed here.</h3>
|
||||
<h3>It provides the ability to save strings, lists, dicts, json or urls and each will be rendered accordingly. Try it out!</h3>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End playbook data details -->
|
||||
<!-- End playbook details -->
|
||||
</div>
|
||||
|
||||
<!-- Begin modals -->
|
||||
<div class="modal fade" id="host_modal" tabindex="-1" role="dialog" aria-labelledby="host_modal" aria-hidden="true">
|
||||
<div class="modal-lg modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
|
||||
<span class="pficon pficon-close"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body embed-responsive embed-responsive-item">
|
||||
<iframe id="host_iframe" class="iframe" data-src="{{ url_for('host.index') }}"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="file_modal" tabindex="-1" role="dialog" aria-labelledby="file_modal" aria-hidden="true">
|
||||
<div class="modal-lg modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
|
||||
<span class="pficon pficon-close"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body embed-responsive embed-responsive-item">
|
||||
<iframe id="file_iframe" class="iframe" data-src="{{ url_for('file.index') }}"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="task_result_modal" tabindex="-1" role="dialog" aria-labelledby="task_result_modal" aria-hidden="true">
|
||||
<div class="modal-lg modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
|
||||
<span class="pficon pficon-close"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body embed-responsive embed-responsive-item">
|
||||
<iframe id="result_iframe" class="iframe" data-src="{{ url_for('result.index') }}"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End modals -->
|
||||
<!-- End playbook iteration -->
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Begin pagination -->
|
||||
<div class="col-md-11 col-md-offset-custom">
|
||||
<div class="row text-center">
|
||||
Displaying <strong>{{ playbooks.items |length }}</strong> playbook reports out of a total of <strong>{{ playbooks.total }}</strong>.
|
||||
</div>
|
||||
{% if playbooks.pages > 1 %}
|
||||
<div class="row text-center">
|
||||
<ul class="pagination">
|
||||
{% if playbooks.has_prev %}
|
||||
<li>
|
||||
<a href="{{ url_for('reports.report_list', page=playbooks.prev_num) }}"><span class="i fa fa-angle-left"></span></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for page in playbooks.iter_pages() %}
|
||||
{% if page == playbooks.page %}
|
||||
<li class="active">
|
||||
{% else %}
|
||||
<li>
|
||||
{% endif %}
|
||||
<a href="{{ url_for('reports.report_list', page=page) }}">{{ page }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% if playbooks.has_next %}
|
||||
<li>
|
||||
<a href="{{ url_for('reports.report_list', page=playbooks.next_num) }}"><span class="i fa fa-angle-right"></span></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- End pagination -->
|
||||
|
||||
<script>
|
||||
{% for playbook in playbooks.items %}
|
||||
$("#hosts_{{ playbook.id }}").DataTable({
|
||||
"order": [[ 0, "asc" ]],
|
||||
"oLanguage": {
|
||||
"sSearch": "Search: "
|
||||
},
|
||||
{{ macros.result_page_length(result_per_page) }}
|
||||
});
|
||||
$("#plays_{{ playbook.id }}").DataTable({
|
||||
"order": [[ 1, "asc" ]],
|
||||
"oLanguage": {
|
||||
"sSearch": "Search: "
|
||||
},
|
||||
{{ macros.result_page_length(result_per_page) }}
|
||||
});
|
||||
$("#files_{{ playbook.id }}").DataTable({
|
||||
"order": [[ 0, "asc" ]],
|
||||
"oLanguage": {
|
||||
"sSearch": "Search: "
|
||||
},
|
||||
{{ macros.result_page_length(result_per_page) }}
|
||||
});
|
||||
$("#tasks_{{ playbook.id }}").DataTable({
|
||||
"order": [[ 3, "asc" ]],
|
||||
"oLanguage": {
|
||||
"sSearch": "Search: "
|
||||
},
|
||||
{{ macros.result_page_length(result_per_page) }}
|
||||
});
|
||||
$("#data_{{ playbook.id }}").DataTable({
|
||||
"ordering": false,
|
||||
"oLanguage": {
|
||||
"sSearch": "Search: "
|
||||
},
|
||||
{{ macros.result_page_length(result_per_page) }}
|
||||
});
|
||||
{% endfor %}
|
||||
|
||||
$('.modal').on('show.bs.modal', function(e) {
|
||||
/* Adjust modal and iframe height based on window size */
|
||||
$('.modal .modal-body').css('max-height', $(window).height() * 0.7);
|
||||
$('.embed-responsive-item').css('height', $(window).height() * 0.7);
|
||||
|
||||
/* Only load the iframe content when the modal is opened */
|
||||
var load = $(e.relatedTarget).data('load');
|
||||
$(this).find('iframe').prop('src', function() {
|
||||
// Set their src attribute to the value of data-src
|
||||
return $(this).data('src').replace('index.html','').concat(load);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<!-- End report list -->
|
||||
{% endblock %}
|
||||
@@ -1,105 +1,48 @@
|
||||
{% extends "layout.html" %}
|
||||
{% include "head.html" %}
|
||||
|
||||
{% macro display_result(result, hdr) %}
|
||||
{% if result is mapping %}
|
||||
{% if 'item' in result %}
|
||||
<{{ hdr }}>Item</{{ hdr }}>
|
||||
<pre>{{ result.item |to_nice_json |safe }}</pre>
|
||||
{% endif %}
|
||||
|
||||
{% for attr in result.keys()|sort
|
||||
if attr not in ['item', 'changed', 'stdout_lines'] %}
|
||||
{% if result[attr]|default(False) %}
|
||||
<{{ hdr }}>{{ attr|title }}</{{ hdr }}>
|
||||
{% if result[attr] is string %}
|
||||
<pre>{{ result[attr] }}</pre>
|
||||
{% elif result[attr] is mapping or result[attr] is iterable%}
|
||||
<pre>{{ result[attr] |to_nice_json |safe }}</pre>
|
||||
{% else %}
|
||||
<pre>{{ result |to_nice_json |safe }}</pre>
|
||||
{% if result is mapping %}
|
||||
{% if 'item' in result %}
|
||||
<{{ hdr }}>Item</{{ hdr }}>
|
||||
<pre>{{ result.item |to_nice_json |safe }}</pre>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<pre>{{ result |to_nice_json |safe }}</pre>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
<!-- Header container -->
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<h1><strong>Playbook</strong>: {{ macros.make_link('playbook.show_playbook', task_result.task.playbook.path, playbook=task_result.task.playbook.id) }}{% if task_result.task.playbook.ansible_version %} with Ansible v{{ task_result.task.playbook.ansible_version }}{% endif %}</h1>
|
||||
<h2>{{ task_result.time_start |datefmt }} <span class="fa fa-angle-double-right"></span> {{ task_result.time_end |datefmt }}</h2>
|
||||
<h1><strong>Task</strong>: {{ task_result.task.name }}</h1>
|
||||
<h1><strong>Host</strong>: {{ task_result.host.name }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Dashboard container -->
|
||||
<div class="container-fluid container-cards-pf">
|
||||
<div class="row row-cards-pf">
|
||||
<div class="col-md-1"></div>
|
||||
{% if task_result.task.file %}
|
||||
<div class="col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<span class="pficon pficon-folder-open"></span>
|
||||
<span class="card-pf-aggregate-status-count">
|
||||
{{ macros.make_link('file.show_file', task_result.task.file.path |pathtruncate(15), file_=task_result.task.file.id) }}
|
||||
</span> <strong>File</strong>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<span class="fa fa-search"></span>
|
||||
<span class="card-pf-aggregate-status-count">
|
||||
{{ macros.make_link('file.show_file', task_result.task.lineno, file_=task_result.task.file.id, _anchor="line-" ~ task_result.task.lineno) }}
|
||||
</span> <strong>File line</strong>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<span class="fa pficon-info"></span>
|
||||
<span class="card-pf-aggregate-status-count">{{ task_result.task.action }}</span> <strong>Action</strong>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status card-pf-aggregate-status-mini" style="height: 59px;">
|
||||
<h2 class="card-pf-title" style="height: 42px;">
|
||||
<span class="fa fa-clock-o"></span>
|
||||
<span class="card-pf-aggregate-status-count">{{ task_result.duration |timefmt }}</span> <strong>Duration</strong>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Detail container -->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<h2><strong>Task result information</strong></h2>
|
||||
{% set result = task_result.result|from_json %}
|
||||
|
||||
{% if 'results' in result %}
|
||||
{% for item in result.results %}
|
||||
<h2>Result {{loop.index}}</h2>
|
||||
|
||||
{{ display_result(item, 'h3') }}
|
||||
{% for attr in result.keys()|sort if attr not in ['item', 'changed', 'stdout_lines'] %}
|
||||
{% if result[attr]|default(False) %}
|
||||
<{{ hdr }}>{{ attr|title }}</{{ hdr }}>
|
||||
{% if result[attr] is string %}
|
||||
<pre>{{ result[attr] }}</pre>
|
||||
{% elif result[attr] is mapping or result[attr] is iterable%}
|
||||
<pre>{{ result[attr] |to_nice_json |safe }}</pre>
|
||||
{% else %}
|
||||
<pre>{{ result |to_nice_json |safe }}</pre>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ display_result(result, 'h2') }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<pre>{{ result |to_nice_json |safe }}</pre>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 results">
|
||||
<h2>Task: <strong>{{ task_result.task.name }}</strong></h2>
|
||||
<h2>Host: <strong>{{ task_result.host.name }}</strong></h2>
|
||||
<p>Time: <strong>{{ task_result.time_start |datefmt }}</strong></p>
|
||||
<p>Ansible version: <strong>{{ task_result.task.playbook.ansible_version }}</strong></p>
|
||||
|
||||
{% set result = task_result.result|from_json %}
|
||||
|
||||
{% if 'results' in result %}
|
||||
{% for item in result.results %}
|
||||
<h2>Result {{loop.index}}</h2>
|
||||
|
||||
{{ display_result(item, 'h3') }}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ display_result(result, 'h2') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
27
ara/templates/task_result_index.html
Normal file
27
ara/templates/task_result_index.html
Normal file
@@ -0,0 +1,27 @@
|
||||
{% include "head.html" %}
|
||||
<!--
|
||||
This template is not actually served or linked from within the web application.
|
||||
-->
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Result</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for result in results %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('result.show_result', task_result=result.id) }}">{{ result.id }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -25,23 +25,117 @@ class TestApp(TestAra):
|
||||
|
||||
def tearDown(self):
|
||||
super(TestApp, self).tearDown()
|
||||
# Reset app config which might have been altered to defaults
|
||||
self.app.config['ARA_PLAYBOOK_OVERRIDE'] = None
|
||||
self.app.config['ARA_PLAYBOOK_PER_PAGE'] = 10
|
||||
|
||||
def test_home(self):
|
||||
def test_home_with_data(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_home_with_override(self):
|
||||
def test_home_without_data(self):
|
||||
res = self.client.get('/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_reports_without_data(self):
|
||||
res = self.client.get('/reports/')
|
||||
self.assertEqual(res.status_code, 302)
|
||||
|
||||
def test_reports(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/reports/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_reports_with_incomplete(self):
|
||||
ansible_run(complete=False)
|
||||
res = self.client.get('/reports/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_reports_with_override(self):
|
||||
ctx = ansible_run()
|
||||
self.app.config['ARA_PLAYBOOK_OVERRIDE'] = [ctx['playbook'].id]
|
||||
res = self.client.get('/')
|
||||
res = self.client.get('/reports/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_home_with_bad_override(self):
|
||||
def test_reports_with_bad_override(self):
|
||||
ansible_run()
|
||||
self.app.config['ARA_PLAYBOOK_OVERRIDE'] = ['uuuu-iiii-dddd-0000']
|
||||
res = self.client.get('/')
|
||||
res = self.client.get('/reports/')
|
||||
self.assertEqual(res.status_code, 302)
|
||||
|
||||
def test_reports_with_pagination(self):
|
||||
ansible_run()
|
||||
ansible_run()
|
||||
self.app.config['ARA_PLAYBOOK_PER_PAGE'] = 1
|
||||
res = self.client.get('/reports/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
res = self.client.get('/reports/1')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
res = self.client.get('/reports/2')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_file(self):
|
||||
ctx = ansible_run()
|
||||
res = self.client.get('/file/{0}/'.format(ctx['pb_file'].id))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_file_index(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/file/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_host(self):
|
||||
ctx = ansible_run()
|
||||
res = self.client.get('/host/{}/'.format(ctx['host'].id))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_host_index(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/host/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_host_missing(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/host/foo/')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_show_host_exists_facts_missing(self):
|
||||
ctx = ansible_run(gather_facts=False)
|
||||
res = self.client.get('/host/{}/'.format(ctx['host'].id))
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_show_host_missing_facts_missing(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/host/foo/')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_show_result(self):
|
||||
ctx = ansible_run()
|
||||
res = self.client.get('/result/{}/'.format(ctx['result'].id))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_result_index(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/result/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_result_missing(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/result/foo/')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
@pytest.mark.incomplete
|
||||
def test_show_result_incomplete(self):
|
||||
ctx = ansible_run(complete=False)
|
||||
res = self.client.get('/result/{}/'.format(ctx['result'].id))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
#####
|
||||
# Tests on deprecated /playbook/, to be removed
|
||||
#####
|
||||
def test_list_playbook(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/playbook/')
|
||||
@@ -52,29 +146,12 @@ class TestApp(TestAra):
|
||||
res = self.client.get('/playbook/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_list_playbook_with_override(self):
|
||||
ctx = ansible_run()
|
||||
self.app.config['ARA_PLAYBOOK_OVERRIDE'] = [ctx['playbook'].id]
|
||||
res = self.client.get('/playbook/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_list_playbook_with_bad_override(self):
|
||||
ansible_run()
|
||||
self.app.config['ARA_PLAYBOOK_OVERRIDE'] = ['uuuu-iiii-dddd-0000']
|
||||
res = self.client.get('/playbook/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_playbook(self):
|
||||
ctx = ansible_run()
|
||||
res = self.client.get('/playbook/{}/'.format(
|
||||
ctx['playbook'].id))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_playbook_missing(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/playbook/foo/')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
@pytest.mark.incomplete
|
||||
def test_show_playbook_incomplete(self):
|
||||
ctx = ansible_run(complete=False)
|
||||
@@ -115,52 +192,3 @@ class TestApp(TestAra):
|
||||
ctx['playbook'].id,
|
||||
ctx['task'].id))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_playbook_results_missing(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/playbook/foo/results/')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_show_host(self):
|
||||
ctx = ansible_run()
|
||||
res = self.client.get('/host/{}/'.format(ctx['host'].id))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_host_missing(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/host/foo/')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_show_host_exists_facts_missing(self):
|
||||
ctx = ansible_run(gather_facts=False)
|
||||
res = self.client.get('/host/{}/'.format(ctx['host'].id))
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_show_host_missing_facts_missing(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/host/foo/')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
@pytest.mark.incomplete
|
||||
def test_show_host_incomplete(self):
|
||||
ctx = ansible_run(complete=False)
|
||||
res = self.client.get('/host/{}/'.format(ctx['host'].id))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_result(self):
|
||||
ctx = ansible_run()
|
||||
res = self.client.get('/result/{}/'.format(
|
||||
ctx['result'].id))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_show_result_missing(self):
|
||||
ansible_run()
|
||||
res = self.client.get('/result/foo/')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
@pytest.mark.incomplete
|
||||
def test_show_result_incomplete(self):
|
||||
ctx = ansible_run(complete=False)
|
||||
res = self.client.get('/result/{}/'.format(
|
||||
ctx['result'].id))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
@@ -628,25 +628,19 @@ class TestCLIGenerate(TestAra):
|
||||
args = parser.parse_args([dir])
|
||||
cmd.take_action(args)
|
||||
|
||||
host_id = ctx['host'].id
|
||||
file_id = ctx['task'].file_id
|
||||
play_id = ctx['play'].id
|
||||
playbook_id = ctx['playbook'].id
|
||||
host_id = ctx['host'].id
|
||||
result_id = ctx['result'].id
|
||||
task_id = ctx['task'].id
|
||||
|
||||
file_filter_path = 'playbook/{0}/file/{1}'.format(playbook_id, file_id)
|
||||
play_path = 'playbook/{0}/play/{1}'.format(playbook_id, play_id)
|
||||
task_path = 'playbook/{0}/task/{1}'.format(playbook_id, task_id)
|
||||
paths = [
|
||||
os.path.join(dir, 'index.html'),
|
||||
os.path.join(dir, 'static'),
|
||||
os.path.join(dir, 'file/index.html'),
|
||||
os.path.join(dir, 'file/{0}'.format(file_id)),
|
||||
os.path.join(dir, 'host/index.html'),
|
||||
os.path.join(dir, 'host/{0}'.format(host_id)),
|
||||
os.path.join(dir, 'playbook/{0}'.format(playbook_id)),
|
||||
os.path.join(dir, file_filter_path),
|
||||
os.path.join(dir, play_path),
|
||||
os.path.join(dir, task_path),
|
||||
os.path.join(dir, 'reports/index.html'),
|
||||
os.path.join(dir, 'playbook/index.html'),
|
||||
os.path.join(dir, 'result/index.html'),
|
||||
os.path.join(dir, 'result/{0}'.format(result_id))
|
||||
]
|
||||
|
||||
@@ -666,25 +660,19 @@ class TestCLIGenerate(TestAra):
|
||||
args = parser.parse_args([dir, '--playbook', ctx['playbook'].id])
|
||||
cmd.take_action(args)
|
||||
|
||||
host_id = ctx['host'].id
|
||||
file_id = ctx['task'].file_id
|
||||
play_id = ctx['play'].id
|
||||
playbook_id = ctx['playbook'].id
|
||||
host_id = ctx['host'].id
|
||||
result_id = ctx['result'].id
|
||||
task_id = ctx['task'].id
|
||||
|
||||
file_filter_path = 'playbook/{0}/file/{1}'.format(playbook_id, file_id)
|
||||
play_path = 'playbook/{0}/play/{1}'.format(playbook_id, play_id)
|
||||
task_path = 'playbook/{0}/task/{1}'.format(playbook_id, task_id)
|
||||
paths = [
|
||||
os.path.join(dir, 'index.html'),
|
||||
os.path.join(dir, 'static'),
|
||||
os.path.join(dir, 'file/index.html'),
|
||||
os.path.join(dir, 'file/{0}'.format(file_id)),
|
||||
os.path.join(dir, 'host/index.html'),
|
||||
os.path.join(dir, 'host/{0}'.format(host_id)),
|
||||
os.path.join(dir, 'playbook/{0}'.format(playbook_id)),
|
||||
os.path.join(dir, file_filter_path),
|
||||
os.path.join(dir, play_path),
|
||||
os.path.join(dir, task_path),
|
||||
os.path.join(dir, 'reports/index.html'),
|
||||
os.path.join(dir, 'playbook/index.html'),
|
||||
os.path.join(dir, 'result/index.html'),
|
||||
os.path.join(dir, 'result/{0}'.format(result_id))
|
||||
]
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from .playbook import playbook
|
||||
from .debug import debug
|
||||
from .host import host
|
||||
from .result import result
|
||||
from .home import home
|
||||
from .file import file
|
||||
from .home import home
|
||||
from .host import host
|
||||
from .playbook import playbook
|
||||
from .reports import reports
|
||||
from .result import result
|
||||
|
||||
# flake8: noqa
|
||||
|
||||
@@ -12,12 +12,31 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from flask import render_template, abort, Blueprint
|
||||
from flask import render_template, abort, Blueprint, current_app
|
||||
from ara import models
|
||||
|
||||
file = Blueprint('file', __name__)
|
||||
|
||||
|
||||
@file.route('/')
|
||||
def index():
|
||||
"""
|
||||
This is not served anywhere in the web application.
|
||||
It is used explicitly in the context of generating static files since
|
||||
flask-frozen requires url_for's to crawl content.
|
||||
url_for's are not used with file.show_file directly and are instead
|
||||
dynamically generated through javascript for performance purposes.
|
||||
"""
|
||||
if current_app.config['ARA_PLAYBOOK_OVERRIDE'] is not None:
|
||||
override = current_app.config['ARA_PLAYBOOK_OVERRIDE']
|
||||
files = (models.File.query
|
||||
.filter(models.File.playbook_id.in_(override)))
|
||||
else:
|
||||
files = models.File.query.all()
|
||||
|
||||
return render_template('file_index.html', files=files)
|
||||
|
||||
|
||||
@file.route('/<file_>/')
|
||||
def show_file(file_):
|
||||
""" Returns details of a file """
|
||||
|
||||
@@ -13,24 +13,47 @@
|
||||
# under the License.
|
||||
|
||||
from flask import render_template, Blueprint, current_app
|
||||
from ara import models, utils
|
||||
from ara import models
|
||||
|
||||
home = Blueprint('home', __name__)
|
||||
|
||||
|
||||
@home.route('/')
|
||||
def main():
|
||||
""" Returns the dashboard """
|
||||
""" Returns the home page """
|
||||
if current_app.config['ARA_PLAYBOOK_OVERRIDE'] is not None:
|
||||
override = current_app.config['ARA_PLAYBOOK_OVERRIDE']
|
||||
files = (models.File.query
|
||||
.filter(models.File.playbook_id.in_(override)))
|
||||
host_facts = (models.HostFacts.query
|
||||
.join(models.Host)
|
||||
.filter(models.Host.playbook_id.in_(override)))
|
||||
hosts = (models.Host.query
|
||||
.filter(models.Host.playbook_id.in_(override)))
|
||||
playbooks = (models.Playbook.query
|
||||
.filter(models.Playbook.id.in_(override))
|
||||
.order_by(models.Playbook.time_start.desc()))
|
||||
.filter(models.Playbook.id.in_(override)))
|
||||
records = (models.Data.query
|
||||
.filter(models.Data.playbook_id.in_(override)))
|
||||
tasks = (models.Task.query
|
||||
.filter(models.Task.playbook_id.in_(override)))
|
||||
task_results = (models.TaskResult.query
|
||||
.join(models.Task)
|
||||
.filter(models.Task.playbook_id.in_(override)))
|
||||
else:
|
||||
playbooks = (models.Playbook.query
|
||||
.order_by(models.Playbook.time_start.desc())
|
||||
.limit(10))
|
||||
files = models.File.query
|
||||
host_facts = models.HostFacts.query
|
||||
hosts = models.Host.query
|
||||
playbooks = models.Playbook.query
|
||||
records = models.Data.query
|
||||
tasks = models.Task.query
|
||||
task_results = models.TaskResult.query
|
||||
|
||||
stats = utils.get_summary_stats(playbooks, 'playbook_id')
|
||||
|
||||
return render_template('home.html', playbooks=playbooks, stats=stats)
|
||||
return render_template('home.html',
|
||||
active='home',
|
||||
files=files.count(),
|
||||
host_facts=host_facts.count(),
|
||||
hosts=hosts.count(),
|
||||
playbooks=playbooks.count(),
|
||||
records=records.count(),
|
||||
tasks=tasks.count(),
|
||||
task_results=task_results.count())
|
||||
|
||||
@@ -15,12 +15,31 @@
|
||||
import json
|
||||
import six
|
||||
|
||||
from flask import render_template, abort, Blueprint
|
||||
from flask import render_template, abort, Blueprint, current_app
|
||||
from ara import models
|
||||
|
||||
host = Blueprint('host', __name__)
|
||||
|
||||
|
||||
@host.route('/')
|
||||
def index():
|
||||
"""
|
||||
This is not served anywhere in the web application.
|
||||
It is used explicitly in the context of generating static files since
|
||||
flask-frozen requires url_for's to crawl content.
|
||||
url_for's are not used with host.show_host directly and are instead
|
||||
dynamically generated through javascript for performance purposes.
|
||||
"""
|
||||
if current_app.config['ARA_PLAYBOOK_OVERRIDE'] is not None:
|
||||
override = current_app.config['ARA_PLAYBOOK_OVERRIDE']
|
||||
hosts = (models.Host.query
|
||||
.filter(models.Host.playbook_id.in_(override)))
|
||||
else:
|
||||
hosts = models.Host.query.all()
|
||||
|
||||
return render_template('host_index.html', hosts=hosts)
|
||||
|
||||
|
||||
@host.route('/<id>/')
|
||||
def show_host(id):
|
||||
try:
|
||||
|
||||
@@ -12,153 +12,20 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from flask import render_template, abort, Blueprint, request, current_app
|
||||
from ara import models, utils
|
||||
from flask import render_template, Blueprint
|
||||
|
||||
playbook = Blueprint('playbook', __name__)
|
||||
|
||||
|
||||
@playbook.route('/')
|
||||
def playbook_summary():
|
||||
if current_app.config['ARA_PLAYBOOK_OVERRIDE'] is not None:
|
||||
override = current_app.config['ARA_PLAYBOOK_OVERRIDE']
|
||||
playbooks = (models.Playbook.query
|
||||
.filter(models.Playbook.id.in_(override))
|
||||
.order_by(models.Playbook.time_start.desc()))
|
||||
else:
|
||||
playbooks = (models.Playbook.query
|
||||
.order_by(models.Playbook.time_start.desc()))
|
||||
|
||||
stats = utils.get_summary_stats(playbooks, 'playbook_id')
|
||||
|
||||
return render_template('playbook_list.html',
|
||||
playbooks=playbooks,
|
||||
stats=stats)
|
||||
|
||||
|
||||
@playbook.route('/<playbook>/')
|
||||
@playbook.route('/<playbook>/file/<file_>/')
|
||||
def show_playbook(playbook, file_=None):
|
||||
playbook = models.Playbook.query.get(playbook)
|
||||
if playbook is None:
|
||||
abort(404)
|
||||
|
||||
plays = (models.Play.query
|
||||
.filter(models.Play.playbook_id == playbook.id)
|
||||
.order_by(models.Play.sortkey))
|
||||
|
||||
playbook_file = (models.File.query
|
||||
.filter(models.File.playbook_id == playbook.id)
|
||||
.filter(models.File.is_playbook.is_(True))).one()
|
||||
|
||||
files = (models.File.query
|
||||
.filter(models.File.playbook_id == playbook.id)
|
||||
.filter(models.File.is_playbook.is_(False))
|
||||
.order_by(models.File.path))
|
||||
|
||||
tasks = (models.Task.query
|
||||
.filter(models.Task.playbook_id == playbook.id)
|
||||
.order_by(models.Task.sortkey))
|
||||
|
||||
try:
|
||||
data = (models.Data.query
|
||||
.filter(models.Data.playbook_id == playbook.id)
|
||||
.order_by(models.Data.key))
|
||||
# If there are no results, don't return an empty query
|
||||
if not data.count():
|
||||
data = None
|
||||
except models.NoResultFound:
|
||||
data = None
|
||||
|
||||
if file_:
|
||||
file_ = (models.File.query.get(file_))
|
||||
if file_ is None:
|
||||
abort(404)
|
||||
|
||||
tasks = (tasks
|
||||
.join(models.File)
|
||||
.filter(models.File.id == file_.id))
|
||||
|
||||
return render_template('playbook.html',
|
||||
playbook=playbook,
|
||||
plays=plays,
|
||||
playbook_file=playbook_file,
|
||||
files=files,
|
||||
tasks=tasks,
|
||||
data=data,
|
||||
file_=file_)
|
||||
|
||||
|
||||
@playbook.route('/<playbook>/results/')
|
||||
@playbook.route('/<playbook>/host/<host>/')
|
||||
@playbook.route('/<playbook>/host/<host>/<status>/')
|
||||
@playbook.route('/<playbook>/play/<play>/')
|
||||
@playbook.route('/<playbook>/task/<task>/')
|
||||
def playbook_results(playbook, host=None, play=None, task=None, status=None):
|
||||
playbook = models.Playbook.query.get(playbook)
|
||||
if playbook is None:
|
||||
abort(404)
|
||||
def playbook_summary(playbook=None, file_=None, host=None, status=None,
|
||||
play=None, task=None):
|
||||
|
||||
task_results = (models.TaskResult.query
|
||||
.join(models.Task)
|
||||
.join(models.Play)
|
||||
.join(models.Host)
|
||||
.join(models.Playbook)
|
||||
.filter(models.Playbook.id == playbook.id)
|
||||
.order_by(models.TaskResult.time_start))
|
||||
|
||||
hosts = None
|
||||
host = host or request.args.get('host')
|
||||
if host is not None:
|
||||
hosts = [str(h) for h in host.split(',')]
|
||||
task_results = (task_results
|
||||
.filter(models.Host.name.in_(hosts)))
|
||||
|
||||
plays = None
|
||||
play = play or request.args.get('play')
|
||||
if play is not None:
|
||||
plays = [str(h) for h in play.split(',')]
|
||||
task_results = (task_results
|
||||
.filter(models.Play.id.in_(plays)))
|
||||
|
||||
playbook_file = (models.File.query
|
||||
.filter(models.File.playbook_id == playbook.id)
|
||||
.filter(models.File.is_playbook.is_(True))).one()
|
||||
|
||||
files = (models.File.query
|
||||
.filter(models.File.playbook_id == playbook.id)
|
||||
.filter(models.File.is_playbook.is_(False))
|
||||
.order_by(models.File.path))
|
||||
|
||||
task = task or request.args.get('task')
|
||||
if task is not None:
|
||||
tasks = [str(h) for h in task.split(',')]
|
||||
task_results = (task_results
|
||||
.filter(models.Task.id.in_(tasks)))
|
||||
|
||||
try:
|
||||
data = (models.Data.query
|
||||
.filter(models.Data.playbook_id == playbook.id)
|
||||
.order_by(models.Data.key))
|
||||
# If there are no results, don't return an empty query
|
||||
if not data.count():
|
||||
data = None
|
||||
except models.NoResultFound:
|
||||
data = None
|
||||
|
||||
# LKS: We're filtering this with Python rather than SQL. This
|
||||
# may become relevant if we implement result paging.
|
||||
status = status or request.args.get('status')
|
||||
if status is not None:
|
||||
status = status.split(',')
|
||||
task_results = (res for res in task_results
|
||||
if res.derived_status in status)
|
||||
|
||||
return render_template('playbook_results.html',
|
||||
hosts=hosts,
|
||||
plays=plays,
|
||||
files=files,
|
||||
data=data,
|
||||
playbook=playbook,
|
||||
playbook_file=playbook_file,
|
||||
task_results=task_results)
|
||||
return render_template('playbook_list.html')
|
||||
|
||||
51
ara/views/reports.py
Normal file
51
ara/views/reports.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# Copyright 2017 Red Hat, Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from flask import render_template, Blueprint, current_app, redirect, url_for
|
||||
from ara import models, utils
|
||||
|
||||
reports = Blueprint('reports', __name__)
|
||||
|
||||
|
||||
@reports.route('/')
|
||||
@reports.route('/<int:page>')
|
||||
def report_list(page=1):
|
||||
if current_app.config['ARA_PLAYBOOK_OVERRIDE'] is not None:
|
||||
override = current_app.config['ARA_PLAYBOOK_OVERRIDE']
|
||||
playbooks = (models.Playbook.query
|
||||
.filter(models.Playbook.id.in_(override))
|
||||
.order_by(models.Playbook.time_start.desc()))
|
||||
else:
|
||||
playbooks = (models.Playbook.query
|
||||
.order_by(models.Playbook.time_start.desc()))
|
||||
|
||||
if not playbooks.count():
|
||||
return redirect(url_for('home.main'))
|
||||
|
||||
playbook_per_page = current_app.config['ARA_PLAYBOOK_PER_PAGE']
|
||||
# Paginate unless playbook_per_page is set to 0
|
||||
if playbook_per_page >= 1:
|
||||
playbooks = playbooks.paginate(page, playbook_per_page, False)
|
||||
else:
|
||||
playbooks = playbooks.paginate(page, None, False)
|
||||
|
||||
stats = utils.get_summary_stats(playbooks.items, 'playbook_id')
|
||||
|
||||
result_per_page = current_app.config['ARA_RESULT_PER_PAGE']
|
||||
|
||||
return render_template('report_list.html',
|
||||
active='reports',
|
||||
result_per_page=result_per_page,
|
||||
playbooks=playbooks,
|
||||
stats=stats)
|
||||
@@ -12,15 +12,36 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from flask import render_template, abort, Blueprint
|
||||
from flask import render_template, abort, Blueprint, current_app
|
||||
from ara import models
|
||||
|
||||
result = Blueprint('result', __name__)
|
||||
|
||||
|
||||
@result.route('/')
|
||||
def index():
|
||||
"""
|
||||
This is not served anywhere in the web application.
|
||||
It is used explicitly in the context of generating static files since
|
||||
flask-frozen requires url_for's to crawl content.
|
||||
url_for's are not used with result.show_result directly and are instead
|
||||
dynamically generated through javascript for performance purposes.
|
||||
"""
|
||||
if current_app.config['ARA_PLAYBOOK_OVERRIDE'] is not None:
|
||||
override = current_app.config['ARA_PLAYBOOK_OVERRIDE']
|
||||
results = (models.TaskResult.query
|
||||
.join(models.Task)
|
||||
.filter(models.Task.playbook_id.in_(override)))
|
||||
else:
|
||||
results = models.TaskResult.query.all()
|
||||
|
||||
return render_template('task_result_index.html', results=results)
|
||||
|
||||
|
||||
@result.route('/<task_result>/')
|
||||
def show_result(task_result):
|
||||
task_result = models.TaskResult.query.get(task_result)
|
||||
if task_result is None:
|
||||
abort(404)
|
||||
|
||||
return render_template('task_result.html', task_result=task_result)
|
||||
|
||||
@@ -33,10 +33,11 @@ import ara.config
|
||||
DEFAULT_APP_NAME = 'ara'
|
||||
|
||||
views = (
|
||||
(ara.views.home, ''),
|
||||
(ara.views.file, '/file'),
|
||||
(ara.views.playbook, '/playbook'),
|
||||
(ara.views.home, ''),
|
||||
(ara.views.host, '/host'),
|
||||
(ara.views.playbook, '/playbook'),
|
||||
(ara.views.reports, '/reports'),
|
||||
(ara.views.result, '/result'),
|
||||
)
|
||||
|
||||
|
||||
@@ -113,6 +113,10 @@ Parameters and their defaults
|
||||
+-------------------------------+--------------------------+-------------------------------------------+
|
||||
| ARA_PLAYBOOK_OVERRIDE_ | playbook_override | None |
|
||||
+-------------------------------+--------------------------+-------------------------------------------+
|
||||
| ARA_PLAYBOOK_PER_PAGE_ | playbook_per_page | 10 |
|
||||
+-------------------------------+--------------------------+-------------------------------------------+
|
||||
| ARA_RESULT_PER_PAGE_ | result_per_page | 25 |
|
||||
+-------------------------------+--------------------------+-------------------------------------------+
|
||||
|
||||
ARA_DIR
|
||||
~~~~~~~
|
||||
@@ -193,6 +197,30 @@ to the list of playbook IDs specified.
|
||||
This is expected to be playbook IDs (ex: retrieved through
|
||||
``ara playbook list``) in a comma-separated list.
|
||||
|
||||
ARA_PLAYBOOK_PER_PAGE
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is the amount of playbooks runs shown in a single page in the ARA web
|
||||
interface. The default is ``10`` but you might want to tweak this number up
|
||||
or down depending on the amount of hosts, tasks and task results contained in
|
||||
your playbooks.
|
||||
This directly influences the weight of the pages that will end up being
|
||||
displayed. Setting this value too high might yield very heavy pages.
|
||||
|
||||
Set this parameter to ``0`` to disable playbook listing pagination entirely.
|
||||
|
||||
ARA_RESULT_PER_PAGE
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is the amount of results shown in a single page in the different data
|
||||
tables such as hosts, plays and tasks of the ARA web interface.
|
||||
The default is ``25`` but you might want to tweak this number up or down
|
||||
depending on your preference.
|
||||
This has no direct impact on the weight of the page being sent for the reports
|
||||
as these data tables are rendered on the client side.
|
||||
|
||||
Set this parameter to ``0`` to disable pagination for results entirely.
|
||||
|
||||
The CLI client and the web application
|
||||
--------------------------------------
|
||||
|
||||
|
||||
@@ -19,17 +19,18 @@ The Patternfly assets are imported directly from
|
||||
.. _3.21.0: https://github.com/patternfly/patternfly/releases/tag/v3.21.0
|
||||
.. _Patternfly's compiled source: https://github.com/patternfly/patternfly/tree/v3.21
|
||||
|
||||
jquery.matchHeight
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
jquery.datatables
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Patternfly's vertical navigation needs the `jquery.matchHeight`_ library.
|
||||
The version of jquery.matchHeight is currently `0.7.0`_.
|
||||
`jquery.datatables`_ is what provides the ability for the different tables
|
||||
in the web interface to be searchable and sortable.
|
||||
|
||||
This is imported directly from the `jquery.matchHeight source`_.
|
||||
The current version of jquery.datatables is 1.10.13.
|
||||
|
||||
.. _jquery.matchHeight: https://github.com/liabru/jquery-match-height
|
||||
.. _0.7.0: https://github.com/liabru/jquery-match-height/releases/tag/0.7.0
|
||||
.. _jquery.matchHeight source: https://github.com/liabru/jquery-match-height/tree/0.7.0
|
||||
This is imported directly from the `jquery.datatables source`_.
|
||||
|
||||
.. _jquery.datatables: https://datatables.net/
|
||||
.. _jquery.datatables source: https://cdn.datatables.net/
|
||||
|
||||
Bootstrap JS
|
||||
~~~~~~~~~~~~
|
||||
|
||||
@@ -107,6 +107,10 @@ ara stats show $(ara stats list -c ID -f value |head -n1)
|
||||
ara task show $(ara task list -a -c ID -f value |head -n1)
|
||||
ara file list -b $pbid
|
||||
ara file show $(ara file list -b $pbid -c ID -f value|head -n1)
|
||||
|
||||
# We want to test pagination in html generation
|
||||
export ARA_PLAYBOOK_PER_PAGE=3
|
||||
export ARA_RESULT_PER_PAGE=20
|
||||
ara generate html ${LOGDIR}/build
|
||||
ara generate html ${LOGDIR}/build-playbook --playbook $pbid
|
||||
ara generate junit ${LOGDIR}/junit.xml
|
||||
|
||||
Reference in New Issue
Block a user