--- a +++ b/qiita_pet/static/js/qiita.js @@ -0,0 +1,426 @@ +/* + * bootstrapAlert will add a Bootstrap alert message to the body of the current + * page. + * + * @param message: Message to display + * @param severit: One of 'danger', 'info', 'warning' or 'success'. + * @param timeout: OPTIONAL. When given, time (in ms) before alert fades out + * + */ + +var timeoutHandleForBoostrapAlert = null; + +function bootstrapAlert(message, severity, timeout){ + // make timeout an optional parameter + timeout = timeout || -1; + + severity = typeof severity !== 'undefined' ? severity : 'danger'; + $("#alert-message").remove(); + var alertDiv = $('<div>', { 'class': 'alert fade in alert-'+severity, 'role': 'alert', 'id': 'alert-message'}); + + alertDiv.append('<a href="#" class="close" data-dismiss="alert">×</a>'); + alertDiv.append('<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>'); + + // prepend a space to separate the message from the '!' icon + alertDiv.append(' '+message); + + // prepend the "Need help" message + if (severity == 'warning' || severity == 'danger'){ + alertDiv.append('<p style="text-align:center">Need help? Send us an <a href="mailto:qiita.help@gmail.com">email</a>.</p>'); + } + + if ($(".topfloat")[0]){ + $( ".topfloat" ).children().prepend(alertDiv); + } else { + $('#template-content').prepend(alertDiv); + } + + if(timeout > 0) { + if (timeoutHandleForBoostrapAlert != null) { + window.clearTimeout(timeoutHandleForBoostrapAlert); + } + timeoutHandleForBoostrapAlert = window.setTimeout(function() { + $('#alert-message').remove(); + timeoutHandleForBoostrapAlert = null; + }, timeout); + } +} + + +/* + * format_extra_info_processing_jobs will add new rows to the study lists + * + * @param message: data, the original data object for the row + * 0: blank +/- button + * 1: heartbeat + * 2: name + * 3: status + * 4: step + * 5: id + * 6: params + * 7: processing_job_workflow_id + * + */ + +function format_extra_info_processing_jobs ( data ) { + + let row = '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">'+ + '<tr>'+ + '<td><b>ID:</b></td>'+ + '<td>'+ data[5] +'</td>'+ + '</tr>'+ + '<tr>'+ + '<td colspan="2"><h5>Parameters:</h5>'+ data[6] +'</td>'+ + '</tr>'; + row += '</table>'; + + return row +} + +/* + * show_hide toggles visibility for the given div + * + * @param message: div, the div to toggle visibility + * + */ + +function show_hide(div) { + $('#' + div).toggle(); +} + +/* + * delete_analysis will delete an analysis + * + * @param aname: The name of the analysis to delete + * @param analysis_id: The id of the analysis to delete + * + */ + +function delete_analysis(aname, analysis_id) { + if (confirm('Are you sure you want to delete analysis: ' + aname + '?')) { + var form = $("<form>") + .attr("action", window.location.href) + .attr("method", "post") + .append($("<input>") + .attr("type", "hidden") + .attr("name", "analysis_id") + .attr("value", analysis_id)) + .append($("<input>") + .attr("type", "hidden") + .attr("name", "action") + .attr("value", "delete_analysis")); + $("body").append(form); + form.submit(); + } +} + +/* + * send_samples_to_analysis send the selected samples for the given artifact ids to analysis + * + * @param button: the button object that triggered this request + * @param aids: A list of artifact ids to add + * + * Note that we have a list of artifact ids cause the user can select one single + * artifact to add or all study artifacts + */ +function send_samples_to_analysis(button, aids, samples = null) { + button.value = 'Adding'; + button.disabled = true; + $(button).addClass("btn-info"); + bootstrapAlert('We are adding ' + aids.length + ' artifact(s) to the analysis. This ' + + 'might take some time based on the number of samples on each artifact.', "warning", 10000); + if (samples === null) { + $.get('/artifact/samples/', {ids:aids}) + .done(function ( data ) { + if (data['status']=='success') { + qiita_websocket.send('sel', data['data']); + button.value = 'Added'; + $(button).removeClass("btn-info"); + } else { + bootstrapAlert('ERROR: ' + data['msg'], "danger"); + button.value = 'There was an error, scroll up to see it'; + button.disabled = false; + $(button).addClass("btn-danger"); + } + }); + } else { + $.each(aids, function(i, aid) { + var to_send = {}; + to_send[aid] = samples.split(','); + qiita_websocket.send('sel', to_send); + }); + button.value = 'Added'; + $(button).removeClass("btn-info"); + } +} + +/** + * + * Function to update the name of an artifact + * + * @param portal_dir: string. The portal that qiita is running under + * @param artifact_id: int. The artifact to be changed + * @param new_name: string. The new artifact name + * @param on_success_func: function. Function to execute when the name has been + * successfully updated + * + */ +function change_artifact_name(portal_dir, artifact_id, new_name, on_success_func) { + $.ajax({ + url: portal_dir + '/artifact/' + artifact_id + '/', + type: 'PATCH', + data: {'op': 'replace', 'path': '/name/', 'value': new_name}, + success: on_success_func, + error: function(object, status, error_msg) { + // Something went wrong, show the message + bootstrapAlert("Error changing artifact name: " + error_msg, "danger"); + } + }); +} + +/** + * Taken from https://goo.gl/KkQ1S4 + * + * Original script information: + * @author Daniel McDonald + * @copyright Copyright 2014, biocore + * @credits Daniel McDonald, Joshua Shorenstein, Jose Navas + * @license BSD + * @version 0.1.0-dev + * @maintainer Daniel McDonald + * @email mcdonadt@colorado.edu + * @status Development + * + * + * @name qiita_websocket + * + * @class manages WebSocket for job information + * + */ + +var qiita_websocket = new function () { + var + /* the server end of the websocket */ + host = null, + /* the websocket */ + ws = null, + + /* registered callbacks */ + callbacks = {}, + + /* the encode and decode methods used for communication */ + encode = JSON.stringify, + decode = JSON.parse; + + /** + * + * Registers a callback method for a given action + * + * @param {action} The associated action verb, str. + * @param {func} The associated function, function. This function must + * accept an object. Any return is ignored. + * + */ + this.add_callback = function(action, func) { callbacks[action] = func; }; + + /** + * + * Packages data into an object, and passes an encoded version of the + * object to the websocket. + * + * @param {action} The associated action to send, str. + * @param {data} The data to send, str or Array of str. + */ + this.send = function(action, data) { + to_send = {}; + to_send[action] = data; + ws.send(encode(to_send)); + }; + + /** + * + * Verify the browser supports websockets, and if so, initialize the + * websocket. On construction, this method will send a message over the + * socket to get all known job information associated with this client. + * + * @param {host} The URL for the websocket, minus the ws:// header, or null + * to use the default qiita_websocket-ws. + * @param {on_close} Optional function for action when websocket is closed. + * @param {on_error} Optional function for action when websocket errors. + */ + this.init = function(host, on_close, on_error) { + if (!("WebSocket" in window)) { + alert("Your browser does not appear to support websockets!"); + return; + } + //check if we need regular or secure websocket + socket = window.location.protocol == "https:" ? 'wss://' : 'ws://'; + ws = new WebSocket(socket + host); + + // retrive all messages + var on_open_message = []; + + ws.onopen = function(){}; + ws.onclose = on_close; + ws.onerror = on_error; + + ws.onmessage = function(evt) { + var data = evt.data; + if (data === 'hello'){ + message = ''; + } else { + message = decode(evt.data); + } + for(var action in message) { + if(action in callbacks) { + callbacks[action](message[action]); + } + } + }; + }; +}; + +function error(evt) { + $('#search-error').html("<b>Server communication error. Sample selection will not be recorded. Please try again later.</b>"); +} + +function show_alert(data) { + bootstrapAlert(data + ' samples selected.', "success", 10000); + $('#dflt-sel-info').css('color', 'rgb(0, 160, 0)'); + updateSelectedSamplesMenu(function(){ + // Show the dropdown menu + $('#selected-samples-dropdown-menu').addClass('custom-dropdown-menu'); + // Hide it after 3 seconds + setTimeout(function() { $('#selected-samples-dropdown-menu').removeClass('custom-dropdown-menu'); }, 3000) + }); +} + +function send_children_buttons(button, aids) { + button.disabled = true; + + $.each(aids, function(idx, aid){ + button.value = 'Adding ' + (idx + 1); + $('#send-button-'+aid).trigger("click"); + }); + + button.value = 'Added'; + $(button).removeClass("btn-info"); +} + +function format_biom_rows(data, row, for_study_list = true, samples = null) { + var proc_data_table = '<table class="table" cellpadding="0" cellspacing="0" border="0" style="padding-left:0px;width:95%">'; + var processing_method = {}; + proc_data_table += '<tr>'; + if (for_study_list) { + proc_data_table += '<th></th>'; + proc_data_table += '<th>Artifacts</th>'; + } + proc_data_table += '<th>Processing method</th>'; + if (for_study_list) { + proc_data_table += '<th>Data type</th>'; + } + proc_data_table += '</tr>'; + + // grouping by processing_method, data_type and parameters + $.each(data, function (idx, info) { + // ignore the artifacts that were generated with software that is deprecated + if (!info['deprecated']) { + if (typeof info !== 'string' && !(info instanceof String)) { + var algorithm = info.algorithm; + if (!(algorithm in processing_method)) { + processing_method[algorithm] = {}; + } + + var data_type = info.data_type + ' (' + info.target_subfragment.join(', ') + ')'; + if (!(data_type in processing_method[algorithm])) { + processing_method[algorithm][data_type] = []; + } + processing_method[algorithm][data_type].push(info); + } + } + }); + + // creating rows + $.each(Object.keys(processing_method).sort(), function (idx, pm) { + var data_types = processing_method[pm]; + $.each(data_types, function (dt, artifacts) { + proc_data_table += '<tr>'; + + if (for_study_list) { + var artifact_to_send = []; + $.each(artifacts, function (idx, a) { + var aid = a.artifact_id; + artifact_to_send.push(aid); + }); + var artifact_to_send_name = artifact_to_send.join(''); + proc_data_table += '<td>'; + proc_data_table += '<input type="button" class="btn btn-sm" value="Add all" onclick="send_children_buttons(this, [' + artifact_to_send + '])"></td>'; + proc_data_table += '<td>' + + '<button class="btn btn-secondary btn-sm" data-toggle="collapse" data-target="#aids-' + artifact_to_send_name + '">' + + 'Per Artifact (' + artifacts.length + ')' + + '</button>' + '</td>'; + } + proc_data_table += '<td>' + pm + '</td>'; + if (for_study_list) { + proc_data_table += '<td>' + dt + '</td>'; + } + + proc_data_table += '</tr>'; + if (for_study_list) { + proc_data_table += '<tr id="aids-' + artifact_to_send_name + '" class="collapse">' + + '<td></td>' + + '<td colspan="4">' + + '<table class="table table-striped table-bordered">' + + '<thead class="thead-default">' + + '<tr>' + + '<th></th>' + + '<th>Name</th>' + + '<th>Samples in Prep Info</th>' + + '<th>Files</th>' + + '</tr>' + + '</thead>' + + '<tbody>'; + $.each(artifacts, function(idx, a){ + var aid = a.artifact_id; + proc_data_table += '<tr>'; + proc_data_table += '<td><input type="button" id="send-button-' + aid + '" class="btn btn-sm" value="Add" onclick="send_samples_to_analysis(this, [' + aid + ']'; + if (samples === null) { + proc_data_table += ')"></td>'; + } else { + proc_data_table += ", '" + samples[aid].join(',') + "'" + ')"></td>'; + } + proc_data_table += '<td>' + a.name + ' (' + aid + ' - ' + a.timestamp.split('.')[0] + ')</td>'; + proc_data_table += '<td>' + a.prep_samples + '</td>'; + proc_data_table += '<td><small>' + a.files.join('<br/>') + '</small></td>'; + proc_data_table += '</tr>'; + }); + proc_data_table += '</tbody>' + '</table>'; + proc_data_table += '</td>' + '</tr>'; + } + }); + }); + + proc_data_table += '</table>'; + return proc_data_table; +} + +function generate_private_download_link(artifact_id){ + $.ajax({ + url: "/private_download/" + artifact_id, + method: 'POST', + success: function(response){ + var newLink = $('<a>',{ + text: response.url, + title: response.url, + href: response.url + }); + $('#privateDownloadText').text('Link will expire in 7 days'); + $('#privateDownloadText').append('<br/>') + $('#privateDownloadText').append(newLink) + $('#privateDownloadLink').collapse('show') + }, + error: function(resp){ + $('#downloadLinkText').text('Failed to Generate Download Link'); + $('#privateDownloadLink').collapse('show') + }}); +}