|
2 | 2 |
|
3 | 3 | {% block head %}
|
4 | 4 | <script type="text/javascript">
|
| 5 | + var jobsTable; |
| 6 | + |
| 7 | + // This modal view is used to display job details when a job has errored or |
| 8 | + // succeeded. Needs to be in the main scope because this function is called |
| 9 | + // from the "onclick" attribute of some elements. |
| 10 | + var populate_modal = function(title, main, artifact_id) { |
| 11 | + $('#job-output-modal').find('[name="modal-title"]').html(title); |
| 12 | + var $main = $('#job-output-modal').find('[name="modal-main-text"]'); |
| 13 | + |
| 14 | + if (artifact_id === undefined) { |
| 15 | + $main.html(unescape(decodeURIComponent(main))); |
| 16 | + } |
| 17 | + else { |
| 18 | + $.get('/artifact/' + artifact_id + '/summary/', function(data){ |
| 19 | + $main.html(data); |
| 20 | + }) |
| 21 | + .fail(function(object, status, error_msg) { |
| 22 | + $main.html("Error loading artifact information: " + status + " " + object.statusText); |
| 23 | + } |
| 24 | + ); |
| 25 | + } |
| 26 | + } |
| 27 | + |
| 28 | + // Initialize the previous jobs table once the document is ready |
| 29 | + $(function() { |
| 30 | + var renderJobArguments = function(data, type, row, meta) { |
| 31 | + // select how you want to render the data |
| 32 | + var lines = [], container; |
| 33 | + |
| 34 | + for (key in data) { |
| 35 | + lines.push('<b>' + key + '</b>:'); |
| 36 | + if (typeof data[key] === 'string'){ |
| 37 | + lines.push(data[key]); |
| 38 | + } |
| 39 | + else if (typeof data[key] === 'object') { |
| 40 | + container = data[key]; |
| 41 | + |
| 42 | + // if we have all these attributes then render a named hyperlink to |
| 43 | + //download the data |
| 44 | + if (container['body'] !== undefined && |
| 45 | + container['filename'] !== undefined && |
| 46 | + container['content_type'] !== undefined) { |
| 47 | + lines.push('<a download="' + container['filename'] + |
| 48 | + '" href="data:text/plain;charset=utf-8,' + |
| 49 | + encodeURIComponent(container['body']) + '">' + |
| 50 | + container['filename'] + '</a>'); |
| 51 | + } |
| 52 | + else { |
| 53 | + lines.push('Unsupported JavaScript Object') |
| 54 | + } |
| 55 | + } |
| 56 | + else { |
| 57 | + lines.push(data[key]); |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + return lines.join('<br>'); |
| 62 | + } |
| 63 | + |
| 64 | + var renderRichDescription = function(data, type, row, meta) { |
| 65 | + var out = [], status = row[2]; |
| 66 | + var statusToClass = { |
| 67 | + 'error': 'text-danger', |
| 68 | + 'success': 'text-success', |
| 69 | + 'queued': 'text-muted', |
| 70 | + 'running': 'text-info' |
| 71 | + }; |
| 72 | + |
| 73 | + out.push('<b class="' + statusToClass[status] + '">' + status + |
| 74 | + '</b>: '); |
| 75 | + |
| 76 | + if (status === 'running' || status === 'queued') { |
| 77 | + out.push(row[0] + ' <i>' + row[3] + '</i>') |
| 78 | + } |
| 79 | + else { |
| 80 | + // We write a callback attribute on the link to be able to display a |
| 81 | + // modal window with the additional success/error details. Note, the |
| 82 | + // quotation is a bit hard to follow but all in all it's ensuring any |
| 83 | + // text we get back from the server is properly formatted for |
| 84 | + // inclusion in an HTML attribute. |
| 85 | + var callback = 'populate_modal("' + row[0] + " completed on " + |
| 86 | + row[6] + '",'; |
| 87 | + out.push('<a href="#" data-toggle="modal" data-target="#job-output-modal" onclick=\'' + callback) |
| 88 | + |
| 89 | + if (status === 'success') { |
| 90 | + // job output is in the fourth element |
| 91 | + // We only expect one output with the artifact id in the 2 element |
| 92 | + out.push('undefined, ' + row[4][0][1] +')\''); |
| 93 | + out.push('>View results</a>') |
| 94 | + |
| 95 | + } |
| 96 | + else if (status === 'error') { |
| 97 | + out.push('"' + escape(encodeURIComponent(row[3])) + '")\''); |
| 98 | + out.push('>View error log summary</a>') |
| 99 | + } |
| 100 | + } |
| 101 | + return out.join(''); |
| 102 | + } |
| 103 | + |
| 104 | + jobsTable = $('#private-jobs-table').DataTable({ |
| 105 | + "order": [[2, "desc"]], |
| 106 | + "oLanguage": { |
| 107 | + "sZeroRecords": "There are no jobs available", |
| 108 | + "search": "Filter results", |
| 109 | + "loadingRecords": "Please wait - loading previous jobs ...", |
| 110 | + }, |
| 111 | + "destroy": true, |
| 112 | + "autoWidth": false, |
| 113 | + "columnDefs": [ |
| 114 | + {'width': '80%', 'targets': 0, 'data': 0, 'render': renderRichDescription}, |
| 115 | + {'width': '10%', 'targets': 1, 'data': 7, 'render': renderJobArguments}, |
| 116 | + {'width': '10%', 'targets': 2, 'data': 6}, |
| 117 | + ], |
| 118 | + }); |
| 119 | + |
| 120 | + // update the table every 10 seconds |
| 121 | + setInterval(function () { |
| 122 | + if (jobsTable.ajax.url()) { |
| 123 | + // user paging is not reset on reload |
| 124 | + jobsTable.ajax.reload(null, false); |
| 125 | + } |
| 126 | + }, 10000); |
| 127 | + |
| 128 | + }); |
| 129 | + |
5 | 130 | /**
|
6 | 131 | * This is a modified version of loadParameterGUI in networkVue.js
|
7 | 132 | **/
|
|
148 | 273 | })
|
149 | 274 | .always(function() {
|
150 | 275 | _helper_clean_objects();
|
| 276 | + |
| 277 | + // trigger a courtesy reload of the table to show the new job |
| 278 | + jobsTable.ajax.reload(null, false); |
151 | 279 | });
|
152 | 280 | },
|
153 | 281 | error: function (object, status, error_msg) {
|
|
160 | 288 |
|
161 | 289 | function generate_summary(option_selected){
|
162 | 290 | var command_id = option_selected.attr('value');
|
| 291 | + var command_name = option_selected.text(); |
163 | 292 | $('#cmd-description').html(option_selected.attr('description'));
|
164 | 293 | $("#parameters").empty();
|
165 | 294 |
|
|
189 | 318 | this.disabled = false;
|
190 | 319 | });
|
191 | 320 |
|
192 |
| - }); |
193 |
| - } |
194 |
| - |
195 |
| - // $('#private-jobs-table').dataTable({ |
196 |
| - // "deferRender": true, |
197 |
| - // "iDisplayLength": 50, |
198 |
| - // "oLanguage": { |
199 |
| - // "sZeroRecords": "No studies belong to selected portal" |
200 |
| - // }, |
201 |
| - // "columns": [ |
202 |
| - // {"className": 'select', "data": null}, |
203 |
| - // {"data": "x, y, z"}, |
204 |
| - // {"data": "y, z"}, |
205 |
| - // ], |
206 |
| - // // 'aoColumnDefs': [ |
207 |
| - // // { 'mRender': render_checkbox, 'mData': 2, 'aTargets': [ 0 ] } |
208 |
| - // // ], |
209 |
| - // "ajax": { |
210 |
| - // "url": "/admin/processing_jobs/list?sEcho=" + Math.floor(Math.random()*1001), |
211 |
| - // "aoColumns": [ |
212 |
| - // {"sSortDataType": "dom-checkbox", "bSearchable": false, "bSortable": false}, |
213 |
| - // {"data": "x, y, z"}, |
214 |
| - // {"data": "y, z"}, |
215 |
| - // ], |
216 |
| - // "error": function(jqXHR, textStatus, ex) { |
217 |
| - // $("#add-button").prop('disabled', true); |
218 |
| - // $("#remove-button").prop('disabled', true); |
219 |
| - // $("#errors").append("<li id='comm-error'>Internal Server Error, please try again later</li>"); |
220 |
| - // } |
221 |
| - // } |
222 |
| - // }); |
223 |
| - |
224 |
| - $('#private-jobs-table').dataTable( { |
225 |
| - "ajax": { |
226 |
| - "url": "/admin/processing_jobs/list?sEcho=" + Math.floor(Math.random()*1001), |
227 |
| - "type": "POST" |
228 |
| - } |
229 |
| -} ); |
| 321 | + // load the jobs table for the selected command |
| 322 | + $('#previous-jobs').show(); |
| 323 | + $('#previous-jobs-tin').html('Previous "' + command_name + '" jobs'); |
230 | 324 |
|
| 325 | + // update the table |
| 326 | + jobsTable.ajax.url("/admin/processing_jobs/list?sEcho=" + Math.floor(Math.random()*10001) + "&commandId=" + command_id).load(); |
231 | 327 |
|
232 |
| - function display_artifact_info(artifactId){ |
233 |
| - $.get('/artifact/' + artifactId + '/summary/', function(data){ |
234 |
| - $("#modal-body").html(data); |
235 |
| - }) |
236 |
| - .fail(function(object, status, error_msg) { |
237 |
| - $("#modal-body").html("Error loading artifact information: " + status + " " + object.statusText); |
238 |
| - } |
239 |
| - ); |
| 328 | + }); |
240 | 329 | }
|
241 |
| - |
242 |
| - |
243 | 330 | </script>
|
244 | 331 |
|
245 | 332 | {% end %}
|
@@ -287,8 +374,36 @@ <h5 class="modal-title" id="title">Artifact Summary</h5>
|
287 | 374 | </div>
|
288 | 375 | </div>
|
289 | 376 |
|
290 |
| - <h3 class="gray-msg">Previous Jobs</h3> |
291 |
| - <table id="private-jobs-table" class="table table-bordered gray-msg"></table> |
| 377 | + <!-- Job Output Modal --> |
| 378 | + <div class="modal fade" tabindex="-1" role="dialog" aria-hidden="true" id="job-output-modal"> |
| 379 | + <div class="modal-dialog modal-med"> |
| 380 | + <div class="modal-content"> |
| 381 | + <div class="modal-header"> |
| 382 | + <h3 name="modal-title"></h3> |
| 383 | + </div> |
| 384 | + <div class="modal-body" name="modal-main-text"> |
| 385 | + </div> |
| 386 | + <div class="modal-footer"> |
| 387 | + <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> |
| 388 | + </div> |
| 389 | + </div> |
| 390 | + </div> |
| 391 | + </div> |
292 | 392 |
|
| 393 | + <div id="previous-jobs" style="display: none;" class="row"> |
| 394 | + <div class="col-sm-12"> |
| 395 | + <h3 id="previous-jobs-tin" class="gray-msg">Previous Jobs</h3> |
| 396 | + <table id="private-jobs-table" class="table table-bordered gray-msg" style="width: 30px!"> |
| 397 | + <thead> |
| 398 | + <tr> |
| 399 | + <th class="sorting sorting_asc" tabindex="0" aria-controls="private-jobs-table" rowspan="1" colspan="1" aria-sort="ascending" aria-label="Last Updated">Summary</th> |
| 400 | + <th class="sorting sorting_asc" tabindex="0" aria-controls="private-jobs-table" rowspan="1" colspan="1" aria-sort="ascending" aria-label="Last Updated">Arguments</th> |
| 401 | + <th class="sorting sorting_asc" tabindex="0" aria-controls="private-jobs-table" rowspan="1" colspan="1" aria-sort="ascending" aria-label="Last Updated">Last Updated</th> |
| 402 | + </thead> |
| 403 | + <tbody> |
| 404 | + </tbody> |
| 405 | + </table> |
| 406 | + </div> |
| 407 | + </div> |
293 | 408 |
|
294 | 409 | {% end %}
|
0 commit comments