|
@@ -19,205 +19,350 @@
|
|
|
from desktop.views import commonheader, commonfooter
|
|
from desktop.views import commonheader, commonfooter
|
|
|
from django.utils.translation import ugettext as _
|
|
from django.utils.translation import ugettext as _
|
|
|
%>
|
|
%>
|
|
|
-<%namespace name="comps" file="jobbrowser_components.mako" />
|
|
|
|
|
-
|
|
|
|
|
-<%def name="get_state(option, state)">
|
|
|
|
|
-% if option == state:
|
|
|
|
|
- selected="true"
|
|
|
|
|
-% endif
|
|
|
|
|
-</%def>
|
|
|
|
|
|
|
+<%namespace name="actionbar" file="actionbar.mako" />
|
|
|
|
|
|
|
|
${ commonheader(None, "jobbrowser", user) | n,unicode }
|
|
${ commonheader(None, "jobbrowser", user) | n,unicode }
|
|
|
|
|
+
|
|
|
|
|
+<link href="/jobbrowser/static/css/jobbrowser.css" rel="stylesheet">
|
|
|
|
|
+
|
|
|
<div class="container-fluid">
|
|
<div class="container-fluid">
|
|
|
<h1>${_('Job Browser')}</h1>
|
|
<h1>${_('Job Browser')}</h1>
|
|
|
- <form class="well form-inline" action="/jobbrowser/jobs" method="GET">
|
|
|
|
|
- <label>
|
|
|
|
|
- ${_('Job status:')}
|
|
|
|
|
- <select name="state" class="submitter">
|
|
|
|
|
- <option value="all" ${get_state('all', state_filter)}>${_('All States')}</option>
|
|
|
|
|
- <option value="running" ${get_state('running', state_filter)}>${_('Running')}</option>
|
|
|
|
|
- <option value="completed" ${get_state('completed', state_filter)}>${_('Completed')}</option>
|
|
|
|
|
- <option value="failed" ${get_state('failed', state_filter)}>${_('Failed')}</option>
|
|
|
|
|
- <option value="killed" ${get_state('killed', state_filter)}>${_('Killed')}</option>
|
|
|
|
|
- </select>
|
|
|
|
|
- </label>
|
|
|
|
|
-
|
|
|
|
|
- <label class="checkbox">
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <%actionbar:render>
|
|
|
|
|
+ <%def name="search()">
|
|
|
|
|
+ ${_('Username')} <input id="userFilter" type="text" class="input-medium search-query" placeholder="${_('Search for username')}" value="${ user_filter or '' }">
|
|
|
|
|
+ ${_('Text')} <input id="textFilter" type="text" class="input-xlarge search-query" placeholder="${_('Search for text')}" value="${ text_filter or '' }">
|
|
|
|
|
+ <img id="loading" src="/static/art/spinner.gif" />
|
|
|
|
|
+ </%def>
|
|
|
|
|
+
|
|
|
|
|
+ <%def name="creation()">
|
|
|
|
|
+ <label class="checkbox retired">
|
|
|
<%
|
|
<%
|
|
|
checked = ""
|
|
checked = ""
|
|
|
if retired == "on":
|
|
if retired == "on":
|
|
|
checked = 'checked="checked"'
|
|
checked = 'checked="checked"'
|
|
|
%>
|
|
%>
|
|
|
- <input name="retired" type="checkbox" class="submitter" ${checked}> ${_('Show retired jobs')}
|
|
|
|
|
- </label>
|
|
|
|
|
-
|
|
|
|
|
- <label class="pull-right">
|
|
|
|
|
-
|
|
|
|
|
- ${_('Text:')}
|
|
|
|
|
- <input type="text" name="text" title="${_('Text Filter')}" value="${ text_filter or '' }" placeholder="${_('Text Filter')}" class="submitter input-large search-query"/>
|
|
|
|
|
- </label>
|
|
|
|
|
- <label class="pull-right">
|
|
|
|
|
- ${_('Username:')}
|
|
|
|
|
- <input type="text" name="user" title="${_('User Name Filter')}" value="${ user_filter or '' }" placeholder="${_('User Name Filter')}" class="submitter input-large search-query" />
|
|
|
|
|
- </label>
|
|
|
|
|
- </form>
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-% if not jobs:
|
|
|
|
|
-<p>${_('There were no jobs that match your search criteria.')}</p>
|
|
|
|
|
-% else:
|
|
|
|
|
-<style>
|
|
|
|
|
- .job-row {
|
|
|
|
|
- height:45px;
|
|
|
|
|
- }
|
|
|
|
|
-</style>
|
|
|
|
|
-<table class="datatables table table-striped table-condensed">
|
|
|
|
|
|
|
+ <input id="showRetired" type="checkbox" ${checked}> ${_('Show retired jobs')}
|
|
|
|
|
+ </label>
|
|
|
|
|
+ <span class="btn-group">
|
|
|
|
|
+ <a class="btn btn-status btn-success" data-value="completed">${ _('Succeeded') }</a>
|
|
|
|
|
+ <a class="btn btn-status btn-warning" data-value="running">${ _('Running') }</a>
|
|
|
|
|
+ <a class="btn btn-status btn-danger disable-feedback" data-value="failed">${ _('Failed') }</a>
|
|
|
|
|
+ <a class="btn btn-status btn-danger disable-feedback" data-value="killed">${ _('Killed') }</a>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </%def>
|
|
|
|
|
+ </%actionbar:render>
|
|
|
|
|
+
|
|
|
|
|
+ <div id="noJobs" class="alert"><i class="icon-warning-sign"></i> ${_('There are no jobs that match your search criteria.')}</div>
|
|
|
|
|
+
|
|
|
|
|
+ <table id="jobsTable" class="datatables table table-striped table-condensed">
|
|
|
<thead>
|
|
<thead>
|
|
|
- <tr>
|
|
|
|
|
- <th width="4%">${_('Logs')}</th>
|
|
|
|
|
- <th width="10%">${_('ID')}</th>
|
|
|
|
|
- <th width="49%">${_('Name')}</th>
|
|
|
|
|
- <th width="5%">${_('Status')}</th>
|
|
|
|
|
- <th width="5%">${_('User')}</th>
|
|
|
|
|
- <th width="2%">${_('Maps')}</th>
|
|
|
|
|
- <th width="2%">${_('Reduces')}</th>
|
|
|
|
|
- <th width="5%">${_('Queue')}</th>
|
|
|
|
|
- <th width="4%">${_('Priority')}</th>
|
|
|
|
|
- <th width="4%">${_('Duration')}</th>
|
|
|
|
|
- <th width="12%">${_('Date')}</th>
|
|
|
|
|
- <th width="3%" data-row-selector-exclude="true" ></th>
|
|
|
|
|
- </tr>
|
|
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <th width="4%">${_('Logs')}</th>
|
|
|
|
|
+ <th width="10%">${_('ID')}</th>
|
|
|
|
|
+ <th width="49%">${_('Name')}</th>
|
|
|
|
|
+ <th width="5%">${_('Status')}</th>
|
|
|
|
|
+ <th width="5%">${_('User')}</th>
|
|
|
|
|
+ <th width="2%">${_('Maps')}</th>
|
|
|
|
|
+ <th width="2%">${_('Reduces')}</th>
|
|
|
|
|
+ <th width="5%">${_('Queue')}</th>
|
|
|
|
|
+ <th width="4%">${_('Priority')}</th>
|
|
|
|
|
+ <th width="4%">${_('Duration')}</th>
|
|
|
|
|
+ <th width="12%">${_('Date')}</th>
|
|
|
|
|
+ <th width="3%" data-row-selector-exclude="true"></th>
|
|
|
|
|
+ </tr>
|
|
|
</thead>
|
|
</thead>
|
|
|
<tbody>
|
|
<tbody>
|
|
|
- % for job in jobs:
|
|
|
|
|
- <tr class="job-row">
|
|
|
|
|
- <td data-row-selector-exclude="true">
|
|
|
|
|
- <a href="${ url('jobbrowser.views.job_single_logs', job=job.jobId) }" data-row-selector-exclude="true"><i class="icon-tasks"></i></a>
|
|
|
|
|
- </td>
|
|
|
|
|
- <td>
|
|
|
|
|
- <a href="${url('jobbrowser.views.single_job', job=job.jobId)}" title="${_('View this job')}" data-row-selector="true">${job.jobId_short}</a>
|
|
|
|
|
- </td>
|
|
|
|
|
- <td>
|
|
|
|
|
- ${job.jobName}
|
|
|
|
|
- </td>
|
|
|
|
|
- <td>
|
|
|
|
|
- <a href="${url('jobbrowser.views.jobs')}?${get_state_link(request, 'state', job.status.lower())}"
|
|
|
|
|
- title="${_('Show only %(status)s jobs') % dict(status=job.status.lower())}" class="nounderline">
|
|
|
|
|
- ${comps.get_status(job)}
|
|
|
|
|
- </a>
|
|
|
|
|
- </td>
|
|
|
|
|
- <td>
|
|
|
|
|
- <a href="${url('jobbrowser.views.jobs')}?${get_state_link(request, 'user', job.user.lower())}"
|
|
|
|
|
- title="${_('Show only %(status)s jobs') % dict(status=job.user.lower())}">${job.user}</a>
|
|
|
|
|
- </td>
|
|
|
|
|
- <td data-sort-value="${job.maps_percent_complete}">
|
|
|
|
|
- % if job.is_retired:
|
|
|
|
|
- <div class="center">${_('N/A')}</div>
|
|
|
|
|
- % else:
|
|
|
|
|
- ${comps.mr_graph_maps(job)}
|
|
|
|
|
- % endif
|
|
|
|
|
- </td>
|
|
|
|
|
- <td data-sort-value="${job.reduces_percent_complete}">
|
|
|
|
|
- % if job.is_retired:
|
|
|
|
|
- <div class="center">${_('N/A')}</div>
|
|
|
|
|
- % else:
|
|
|
|
|
- ${comps.mr_graph_reduces(job)}
|
|
|
|
|
- % endif
|
|
|
|
|
- </td>
|
|
|
|
|
- <td>${job.queueName}</td>
|
|
|
|
|
- <td>${job.priority.lower() or _('N/A')}</td>
|
|
|
|
|
- <td data-sort-value="${job.durationInMillis}" data-row-selector-exclude="true">
|
|
|
|
|
- % if job.is_retired:
|
|
|
|
|
- ${_('N/A')}
|
|
|
|
|
- % else:
|
|
|
|
|
- ${job.durationFormatted}
|
|
|
|
|
- % endif
|
|
|
|
|
- </td>
|
|
|
|
|
- <td data-sort-value="${job.startTimeMs}">${job.startTimeFormatted}</td>
|
|
|
|
|
- <td data-row-selector-exclude="true" style="padding-right: 60px">
|
|
|
|
|
- % if (job.status.lower() == 'running' or job.status.lower() == 'pending') and not job.is_mr2:
|
|
|
|
|
- % if request.user.is_superuser or request.user.username == job.user:
|
|
|
|
|
- <a href="#" title="${_('Kill this job')}" kill-action="${url('jobbrowser.views.kill_job', job=job.jobId)}?next=${request.get_full_path() | urlencode}"
|
|
|
|
|
- data-row-selector-exclude="true" data-keyboard="true" class="btn btn-mini kill">
|
|
|
|
|
- <i class="icon-remove"></i> ${_('Kill')}
|
|
|
|
|
- </a>
|
|
|
|
|
- % endif
|
|
|
|
|
- % endif
|
|
|
|
|
- </td>
|
|
|
|
|
- </tr>
|
|
|
|
|
- % endfor
|
|
|
|
|
- </tbody>
|
|
|
|
|
- </table>
|
|
|
|
|
- % endif
|
|
|
|
|
|
|
+ </tbody>
|
|
|
|
|
+ </table>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div id="killModal" class="modal hide fade">
|
|
<div id="killModal" class="modal hide fade">
|
|
|
- <div class="modal-header">
|
|
|
|
|
- <a href="#" class="close" data-dismiss="modal">×</a>
|
|
|
|
|
- <h3>${_('Please Confirm')}</h3>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="modal-body">
|
|
|
|
|
- <p>${_('Are you sure you want to kill this job?')}</p>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="modal-footer">
|
|
|
|
|
- <form id="kill-job" action="" method="POST" class="form-stacked">
|
|
|
|
|
- <a id="cancelKillBtn" class="btn">${_('No')}</a>
|
|
|
|
|
- <input type="submit" value="${_('Yes')}" class="btn btn-danger" />
|
|
|
|
|
- </form>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div class="modal-header">
|
|
|
|
|
+ <a href="#" class="close" data-dismiss="modal">×</a>
|
|
|
|
|
+ <h3>${_('Please Confirm')}</h3>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="modal-body">
|
|
|
|
|
+ <p>${_('Are you sure you want to kill this job?')}</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="modal-footer">
|
|
|
|
|
+ <a class="btn" data-dismiss="modal">${_('No')}</a>
|
|
|
|
|
+ <a id="killJobBtn" class="btn btn-danger disable-feedback">${_('Yes')}</a>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+<script src="/static/ext/js/datatables-paging-0.1.js" type="text/javascript" charset="utf-8"></script>
|
|
|
|
|
+<script src="/jobbrowser/static/js/utils.js" type="text/javascript" charset="utf-8"></script>
|
|
|
|
|
+
|
|
|
<script type="text/javascript" charset="utf-8">
|
|
<script type="text/javascript" charset="utf-8">
|
|
|
- $(document).ready(function(){
|
|
|
|
|
- $(".datatables").dataTable({
|
|
|
|
|
- "bPaginate": false,
|
|
|
|
|
- "bLengthChange": false,
|
|
|
|
|
- "bFilter": false,
|
|
|
|
|
- "bInfo": false,
|
|
|
|
|
- "aaSorting": [[1, "desc"]],
|
|
|
|
|
- "aoColumns": [
|
|
|
|
|
- {"bSortable": false},
|
|
|
|
|
- null,
|
|
|
|
|
- null,
|
|
|
|
|
- null,
|
|
|
|
|
- null,
|
|
|
|
|
- { "sSortDataType": "dom-sort-value", "sType": "numeric", "sWidth": "60px" },
|
|
|
|
|
- { "sSortDataType": "dom-sort-value", "sType": "numeric", "sWidth": "60px" },
|
|
|
|
|
- null,
|
|
|
|
|
- null,
|
|
|
|
|
- { "sSortDataType": "dom-sort-value", "sType": "numeric" },
|
|
|
|
|
- { "sSortDataType": "dom-sort-value", "sType": "numeric" },
|
|
|
|
|
- {"bSortable":false}
|
|
|
|
|
- ],
|
|
|
|
|
- "oLanguage": {
|
|
|
|
|
- "sEmptyTable": "${_('No data available')}",
|
|
|
|
|
- "sZeroRecords": "${_('No matching records')}",
|
|
|
|
|
|
|
+
|
|
|
|
|
+ $(document).ready(function () {
|
|
|
|
|
+
|
|
|
|
|
+ var jobTable = $(".datatables").dataTable({
|
|
|
|
|
+ "sPaginationType": "bootstrap",
|
|
|
|
|
+ "iDisplayLength": 30,
|
|
|
|
|
+ "bLengthChange": false,
|
|
|
|
|
+ "sDom": "<'row'r>t<'row'<'span6'i><''p>>",
|
|
|
|
|
+ "aaSorting": [
|
|
|
|
|
+ [1, "desc"]
|
|
|
|
|
+ ],
|
|
|
|
|
+ "aoColumns": [
|
|
|
|
|
+ {"bSortable": false, "sWidth": "20px"},
|
|
|
|
|
+ null,
|
|
|
|
|
+ null,
|
|
|
|
|
+ null,
|
|
|
|
|
+ null,
|
|
|
|
|
+ { "sType": "title-numeric", "sWidth": "60px" },
|
|
|
|
|
+ { "sType": "title-numeric", "sWidth": "60px" },
|
|
|
|
|
+ null,
|
|
|
|
|
+ null,
|
|
|
|
|
+ { "sType": "title-numeric" },
|
|
|
|
|
+ { "sType": "title-numeric" },
|
|
|
|
|
+ {"bSortable": false}
|
|
|
|
|
+ ],
|
|
|
|
|
+ "oLanguage": {
|
|
|
|
|
+ "sEmptyTable": "${_('No data available')}",
|
|
|
|
|
+ "sZeroRecords": "${_('No matching records')}"
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ $(document).ajaxError(function (event, jqxhr, settings, exception) {
|
|
|
|
|
+ if (jqxhr.status == 500) {
|
|
|
|
|
+ window.clearInterval(_runningInterval);
|
|
|
|
|
+ $.jHueNotify.error("${_('There was a problem communicating with the server. Please refresh the page.')}");
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ function populateTable(data) {
|
|
|
|
|
+ if (data != null) {
|
|
|
|
|
+ jobTable.fnClearTable();
|
|
|
|
|
+ $("#loading").addClass("hide");
|
|
|
|
|
+ $("#noJobs").hide();
|
|
|
|
|
+ $(".datatables").show();
|
|
|
|
|
+ if (data.length == 0) {
|
|
|
|
|
+ $("#noJobs").show();
|
|
|
|
|
+ $(".datatables").hide();
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ $(data).each(function (cnt, job) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ jobTable.fnAddData(getJobRow(job));
|
|
|
|
|
+ $("a[data-row-selector='true']").jHueRowSelector();
|
|
|
}
|
|
}
|
|
|
- });
|
|
|
|
|
|
|
+ catch (error) {
|
|
|
|
|
+ $.jHueNotify.error(error);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- $(document).on("click", ".kill", function(e){
|
|
|
|
|
- $("#kill-job").attr("action", $(e.target).attr("kill-action"));
|
|
|
|
|
- $("#killModal").modal({
|
|
|
|
|
- keyboard: true,
|
|
|
|
|
- show: true
|
|
|
|
|
- });
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ var _isUpdating = false;
|
|
|
|
|
+ var newRows = [];
|
|
|
|
|
|
|
|
- $("#cancelKillBtn").click(function(){
|
|
|
|
|
- $("#killModal").modal("hide");
|
|
|
|
|
|
|
+ function updateRunning(data) {
|
|
|
|
|
+ if (data != null && data.length > 0) {
|
|
|
|
|
+ for (var i = 0; i < newRows.length; i++) {
|
|
|
|
|
+ var isDone = true;
|
|
|
|
|
+ $(data).each(function (cnt, job) {
|
|
|
|
|
+ if (job.id == newRows[i].id) {
|
|
|
|
|
+ isDone = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ if (isDone) {
|
|
|
|
|
+ callJobDetails(newRows[i]);
|
|
|
|
|
+ newRows.splice(i);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ $(data).each(function (cnt, job) {
|
|
|
|
|
+ var nNodes = jobTable.fnGetNodes();
|
|
|
|
|
+ var foundRow = null;
|
|
|
|
|
+ $(nNodes).each(function (iNode, node) {
|
|
|
|
|
+ if ($(node).children("td").eq(1).text().trim() == job.shortId) {
|
|
|
|
|
+ foundRow = node;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ if (foundRow == null) {
|
|
|
|
|
+ if (['RUNNING', 'PREP', 'WAITING', 'SUSPENDED', 'PREPSUSPENDED', 'PREPPAUSED', 'PAUSED'].indexOf(job.status.toUpperCase()) > -1) {
|
|
|
|
|
+ newRows.push(job);
|
|
|
|
|
+ try {
|
|
|
|
|
+ jobTable.fnAddData(getJobRow(job));
|
|
|
|
|
+ $("a[data-row-selector='true']").jHueRowSelector();
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (error) {
|
|
|
|
|
+ $.jHueNotify.error(error);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ updateJobRow(job, foundRow);
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ for (var i = 0; i < newRows.length; i++) {
|
|
|
|
|
+ callJobDetails(newRows[i]);
|
|
|
|
|
+ newRows.splice(i);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ _isUpdating = false;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- var filterTimeout = -1;
|
|
|
|
|
- $(".search-query").keyup(function(){
|
|
|
|
|
- window.clearTimeout(filterTimeout);
|
|
|
|
|
- var el = $(this);
|
|
|
|
|
- filterTimeout = window.setTimeout(function(){
|
|
|
|
|
- el.closest("form").submit();
|
|
|
|
|
- }, 500);
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ function getJobRow(job) {
|
|
|
|
|
+ var _killCell = "";
|
|
|
|
|
+ if (job.canKill) {
|
|
|
|
|
+ _killCell = '<a class="btn btn-small btn-inverse kill" ' +
|
|
|
|
|
+ 'href="javascript:void(0)" ' +
|
|
|
|
|
+ 'data-url="' + job.url + '" ' +
|
|
|
|
|
+ 'data-killUrl="' + job.killUrl + '" ' +
|
|
|
|
|
+ 'data-shortid="' + job.shortId + '" ' +
|
|
|
|
|
+ 'title="${ _('Kill this job') }" ' +
|
|
|
|
|
+ '>${ _('Kill') }</a>';
|
|
|
|
|
+ }
|
|
|
|
|
+ return [
|
|
|
|
|
+ '<a href="' + emptyStringIfNull(job.logs) + '" data-row-selector-exclude="true"><i class="icon-tasks"></i></a>',
|
|
|
|
|
+ '<a href="' + emptyStringIfNull(job.url) + '" title="${_('View this job')}" data-row-selector="true">' + emptyStringIfNull(job.shortId) + '</a>',
|
|
|
|
|
+ emptyStringIfNull(job.name),
|
|
|
|
|
+ '<span class="label ' + getStatusClass(job.status) + '">' + (job.isRetired && !job.isMR2 ? '<i class="icon-briefcase icon-white" title="${ _('Retired') }"></i> ' : '') + job.status + '</span>',
|
|
|
|
|
+ emptyStringIfNull(job.user),
|
|
|
|
|
+ '<span title="' + emptyStringIfNull(job.mapsPercentComplete) + '">' + (job.isRetired ? '${_('N/A')}' : '<div class="progress" title="' + (job.isMR2 ? job.mapsPercentComplete : job.finishedMaps + '/' + job.desiredMaps) + '"><div class="bar-label">' + job.mapsPercentComplete + '%</div><div class="' + 'bar ' + getStatusClass(job.status, "bar-") + '" style="margin-top:-20px;width:' + job.mapsPercentComplete + '%"></div></div>') + '</span>',
|
|
|
|
|
+ '<span title="' + emptyStringIfNull(job.reducesPercentComplete) + '">' + (job.isRetired ? '${_('N/A')}' : '<div class="progress" title="' + (job.isMR2 ? job.reducesPercentComplete : job.finishedReduces + '/' + job.desiredReduces) + '"><div class="bar-label">' + job.reducesPercentComplete + '%</div><div class="' + 'bar ' + getStatusClass(job.status, "bar-") + '" style="margin-top:-20px;width:' + job.reducesPercentComplete + '%"></div></div>') + '</span>',
|
|
|
|
|
+ emptyStringIfNull(job.queueName),
|
|
|
|
|
+ emptyStringIfNull(job.priority),
|
|
|
|
|
+ '<span title="' + emptyStringIfNull(job.durationMs) + '">' + (job.isRetired ? '${_('N/A')}' : emptyStringIfNull(job.durationFormatted)) + '</span>',
|
|
|
|
|
+ '<span title="' + emptyStringIfNull(job.startTimeMs) + '">' + emptyStringIfNull(job.startTimeFormatted) + '</span>',
|
|
|
|
|
+ _killCell
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function updateJobRow(job, row) {
|
|
|
|
|
+ jobTable.fnUpdate('<span class="label ' + getStatusClass(job.status) + '">' + job.status + '</span>', row, 3, false);
|
|
|
|
|
+ jobTable.fnUpdate('<span title="' + emptyStringIfNull(job.mapsPercentComplete) + '"><div class="progress" title="' + (job.isMR2 ? job.mapsPercentComplete : job.finishedMaps + '/' + job.desiredMaps) + '"><div class="bar-label">' + job.mapsPercentComplete + '%</div><div class="' + 'bar ' + getStatusClass(job.status, "bar-") + '" style="margin-top:-20px;width:' + job.mapsPercentComplete + '%"></div></div></span>', row, 5, false);
|
|
|
|
|
+ jobTable.fnUpdate('<span title="' + emptyStringIfNull(job.reducesPercentComplete) + '"><div class="progress" title="' + (job.isMR2 ? job.reducesPercentComplete : job.finishedReduces + '/' + job.desiredReduces) + '"><div class="bar-label">' + job.reducesPercentComplete + '%</div><div class="' + 'bar ' + getStatusClass(job.status, "bar-") + '" style="margin-top:-20px;width:' + job.reducesPercentComplete + '%"></div></div></span>', row, 6, false);
|
|
|
|
|
+ jobTable.fnUpdate('<span title="' + emptyStringIfNull(job.durationMs) + '">' + emptyStringIfNull(job.durationFormatted) + '</span>', row, 9, false);
|
|
|
|
|
+ var _killCell = "";
|
|
|
|
|
+ if (job.canKill) {
|
|
|
|
|
+ _killCell = '<a class="btn btn-small btn-inverse kill" ' +
|
|
|
|
|
+ 'href="javascript:void(0)" ' +
|
|
|
|
|
+ 'data-url="' + job.url + '" ' +
|
|
|
|
|
+ 'data-killurl="' + job.killUrl + '" ' +
|
|
|
|
|
+ 'data-shortid="' + job.shortId + '" ' +
|
|
|
|
|
+ 'title="${ _('Kill this job') }" ' +
|
|
|
|
|
+ '>${ _('Kill') }</a>';
|
|
|
|
|
+ }
|
|
|
|
|
+ jobTable.fnUpdate(_killCell, row, 11, false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function callJobDetails(job) {
|
|
|
|
|
+ $.getJSON(job.url + "?format=json&rnd=" + Math.random(), function (job) {
|
|
|
|
|
+ if (job != null) {
|
|
|
|
|
+ var jobTableNodes = jobTable.fnGetNodes();
|
|
|
|
|
+ var _foundRow = null;
|
|
|
|
|
+ $(jobTableNodes).each(function (iNode, node) {
|
|
|
|
|
+ if ($(node).children("td").eq(1).text().trim() == job.shortId) {
|
|
|
|
|
+ _foundRow = node;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ if (_foundRow != null) {
|
|
|
|
|
+ updateJobRow(job, _foundRow);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function callJsonData(callback, justRunning) {
|
|
|
|
|
+ var _url = "?format=json";
|
|
|
|
|
+ if (justRunning == undefined) {
|
|
|
|
|
+ if ($(".btn-status.active").length > 0) {
|
|
|
|
|
+ _url += "&state=" + $(".btn-status.active").data("value");
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ _url += "&state=all";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ _isUpdating = true;
|
|
|
|
|
+ _url += "&state=running";
|
|
|
|
|
+ }
|
|
|
|
|
+ _url += "&user=" + $("#userFilter").val().trim();
|
|
|
|
|
+ if ($("#textFilter").val().trim() != "") {
|
|
|
|
|
+ _url += "&text=" + $("#textFilter").val().trim();
|
|
|
|
|
+ }
|
|
|
|
|
+ if ($("#showRetired").is(":checked")) {
|
|
|
|
|
+ _url += "&retired=on";
|
|
|
|
|
+ }
|
|
|
|
|
+ _url += "&rnd=" + Math.random(); // thanks IE!
|
|
|
|
|
+ $.getJSON(_url, callback);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var _filterTimeout = -1;
|
|
|
|
|
+ $(".search-query").on("keyup", function () {
|
|
|
|
|
+ window.clearTimeout(_filterTimeout);
|
|
|
|
|
+ _filterTimeout = window.setTimeout(function () {
|
|
|
|
|
+ $("#loading").removeClass("hide");
|
|
|
|
|
+ callJsonData(populateTable);
|
|
|
|
|
+ }, 300);
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- $("a[data-row-selector='true']").jHueRowSelector();
|
|
|
|
|
|
|
+ $("#showRetired").change(function () {
|
|
|
|
|
+ $("#loading").removeClass("hide");
|
|
|
|
|
+ callJsonData(populateTable);
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+ $(".btn-status").on("click", function () {
|
|
|
|
|
+ $(".btn-status").not($(this)).removeClass("active");
|
|
|
|
|
+ $(this).toggleClass("active");
|
|
|
|
|
+ $("#loading").removeClass("hide");
|
|
|
|
|
+ callJsonData(populateTable);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ $(document).on("click", ".kill", function (e) {
|
|
|
|
|
+ var _this = $(this);
|
|
|
|
|
+ $("#killJobBtn").data("url", _this.data("url"));
|
|
|
|
|
+ $("#killJobBtn").data("killurl", _this.data("killurl"));
|
|
|
|
|
+ $("#killModal").modal({
|
|
|
|
|
+ keyboard: true,
|
|
|
|
|
+ show: true
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ $("#killJobBtn").on("click", function () {
|
|
|
|
|
+ var _this = $(this);
|
|
|
|
|
+ _this.attr("data-loading-text", _this.text() + " ...");
|
|
|
|
|
+ _this.button("loading");
|
|
|
|
|
+ $.post(_this.data("killurl"),
|
|
|
|
|
+ {
|
|
|
|
|
+ "format": "json"
|
|
|
|
|
+ },
|
|
|
|
|
+ function (response) {
|
|
|
|
|
+ _this.button("reset");
|
|
|
|
|
+ $("#killModal").modal("hide");
|
|
|
|
|
+ if (response.status != 0) {
|
|
|
|
|
+ $.jHueNotify.error("${ _('There was a problem killing this job.') }");
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ callJobDetails({ url: _this.data("url")});
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // init job list
|
|
|
|
|
+ var _initialState = getQueryStringParameter("state");
|
|
|
|
|
+ if (_initialState != "") {
|
|
|
|
|
+ $(".btn-status[data-value='" + _initialState + "']").addClass("active");
|
|
|
|
|
+ }
|
|
|
|
|
+ callJsonData(populateTable);
|
|
|
|
|
+
|
|
|
|
|
+ var _runningInterval = window.setInterval(function () {
|
|
|
|
|
+ if (!_isUpdating) {
|
|
|
|
|
+ callJsonData(updateRunning, true);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 2000);
|
|
|
|
|
+
|
|
|
|
|
+ $("a[data-row-selector='true']").jHueRowSelector();
|
|
|
|
|
+ });
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
${ commonfooter(messages) | n,unicode }
|
|
${ commonfooter(messages) | n,unicode }
|