diff --git a/.gitignore b/.gitignore index c11bd69955f1624a10ca36bdea9898d7e29e24b4..0235d7090c43b8f7b00253c49ba51aa3a9564fb5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,11 @@ *.pyc .*.swp *~ +__pycache__ +ubuntu-bionic-18.04-cloudimg-console.log +.vagrant +jupyter_openbis_extension.egg-info +venv +.ipynb_checkpoints +.DS_Store +*.ipynb diff --git a/jupyter-openbis-extension/__init__.py b/jupyter-openbis-extension/__init__.py index 22b2aabac22028215549cb4634d33428f05c7124..032652cf4581de8167edaad981442e54fdc49552 100644 --- a/jupyter-openbis-extension/__init__.py +++ b/jupyter-openbis-extension/__init__.py @@ -19,7 +19,7 @@ def _jupyter_nbextension_paths(): # # directory in the `nbextension/` namespace 'dest': "openbis", # _also_ in the `nbextension/` namespace - 'require' : "openbis/dialog" + 'require' : "openbis/main" }] def load_jupyter_server_extension(nbapp): diff --git a/jupyter-openbis-extension/__pycache__/extension.cpython-36.pyc b/jupyter-openbis-extension/__pycache__/extension.cpython-36.pyc deleted file mode 100644 index d5f03417cffd6d3922495cb9dca6bf4efab96353..0000000000000000000000000000000000000000 Binary files a/jupyter-openbis-extension/__pycache__/extension.cpython-36.pyc and /dev/null differ diff --git a/jupyter-openbis-extension/static/common.js b/jupyter-openbis-extension/static/common.js new file mode 100644 index 0000000000000000000000000000000000000000..41f42fdba90ec6f3205e0f169f00a893e65ca722 --- /dev/null +++ b/jupyter-openbis-extension/static/common.js @@ -0,0 +1,30 @@ +define([ + "base/js/namespace" + ], + function (IPython) { + + function createFeedback(type, content) { + var close = document.createElement("BUTTON") + close.className = "close" + close.setAttribute("data-dismiss", "alert") + close.setAttribute("aria-label", "Close") + var x = document.createElement("SPAN") + x.setAttribute("aria-hidden", true) + x.innerHTML = "×" + close.appendChild(x) + + var feedbackBox = document.createElement("DIV") + feedbackBox.className = "openbis-feedback alert alert-dismissible alert-" + type + feedbackBox.setAttribute("role", "alert") + feedbackBox.innerHTML = content + feedbackBox.prepend(close) + + var nb_container = document.getElementById('notebook-container') + nb_container.prepend(feedbackBox) + } + + return { + createFeedback: createFeedback + } + } +) \ No newline at end of file diff --git a/jupyter-openbis-extension/static/connectionDialog.js b/jupyter-openbis-extension/static/connectionDialog.js new file mode 100644 index 0000000000000000000000000000000000000000..4028d6b00862201dd9be12e22d774ccde2c3d901 --- /dev/null +++ b/jupyter-openbis-extension/static/connectionDialog.js @@ -0,0 +1,245 @@ +define( + [ + "base/js/dialog", + "./state", + "./connections" + ], + function (dialog, state, connections) { + + var currentDownloadPath = null + + function show_available_connections(env, data, conn_table, onclick_cbf) { + if (!currentDownloadPath) { + currentDownloadPath = data.cwd + } + + var table = document.createElement("TABLE") + table.className = 'table-bordered table-striped table-condensed' + var thead = table.createTHead() + var thead_row = thead.insertRow(0) + var titles = ['', 'Name', 'URL', 'Status', 'Username / Password'] + for (title of titles) { + thead_row.insertCell().textContent = title + } + + tbody = table.createTBody() + for (connection of data.connections) { + var conn = document.createElement("INPUT") + conn.type = "radio" + conn.name = "connection_name" + conn.value = connection.name + conn.setAttribute("url", connection.url) + + conn.checked = connection.name === state.connection.candidateName; + if (onclick_cbf === undefined) { + conn.onclick = function () { + state.connection.candidateName = this.value + } + } else { + conn.onclick = onclick_cbf + } + + var row = tbody.insertRow() + row.insertCell().appendChild(conn) + row.insertCell().textContent = connection.name + row.insertCell().textContent = connection.url + + var status_cell = row.insertCell() + + var status_badge = document.createElement("SPAN") + status_badge.id = connection.name + "-badge" + status_badge.textContent = connection.status + if (connection.status === "connected") { + status_badge.className = "label label-success" + } else { + status_badge.className = "label label-danger" + } + status_cell.appendChild(status_badge) + + var username = document.createElement("INPUT") + username.type = "text" + username.name = "username" + username.autocomplete = "on" + username.value = connection.username + username.setAttribute("form", connection.name) + + var password = document.createElement("INPUT") + password.type = "password" + password.name = "password" + password.autocomplete = "current-password" + password.value = connection.password + password.setAttribute("form", connection.name) + + // Username / Password form + var pwform = document.createElement("FORM") + pwform.id = connection.name + pwform.onsubmit = function (event) { + var form_data = new FormData(this) + var status_badge = document.getElementById(this.id + "-badge") + connections.connect(env, this.id, + form_data.get("username"), form_data.get("password") + ) + .then(function (response) { + //console.log(response) + if (status_badge.nextElementSibling !== null) { + status_badge.parentNode.removeChild(status_badge.nextElementSibling) + } + if (response.ok) { + status_badge.textContent = "connected" + status_badge.className = "label label-success" + } else { + status_badge.textContent = "not connected" + status_badge.className = "label label-danger" + message = document.createElement("p") + if (response.status === 401) { + message.textContent = "username/password incorrect" + } else if (response.status === 500) { + message.textContent = "Connection error" + } else { + message.textContent = "General error" + } + status_badge.parentNode.insertBefore(message, status_badge.nextSibling) + } + }) + .catch(error => console.error("Error while attempting to reconnect: ", error)) + + return false + } + + + var connect_button = document.createElement("BUTTON") + connect_button.className = "btn btn-primary btn-xs" + connect_button.textContent = "connect" + + pwform.appendChild(username) + pwform.appendChild(password) + pwform.appendChild(connect_button) + + var cell = row.insertCell() + cell.appendChild(pwform) + } + + // add row for new connection + var row = tbody.insertRow() + + var conn_form = document.createElement("FORM") + conn_form.id = "new_connection" + conn_form.onsubmit = function (event) { + var inputs = document.querySelectorAll("input[form=new_connection]") + + data = {} + for (input of inputs) { + data[input.name] = input.value + } + for (missing of ['connection_name', 'url', 'username', 'password']) { + if (data[missing] === "") { + alert("Please provide: " + missing) + return false + } + } + connections.create(env, data.connection_name, data.url, data.username, data.password) + .then(function (response) { + if (response.ok) { + response.json() + .then(function (data) { + show_available_connections(env, data, conn_table) + }) + } + }) + return false + } + var conn_name = document.createElement("INPUT") + conn_name.type = "input" + conn_name.name = "connection_name" + conn_name.setAttribute("form", conn_form.id) + conn_name.placeholder = "openBIS instance name" + row.insertCell().appendChild(conn_form) + row.insertCell().appendChild(conn_name) + + var conn_url = document.createElement("INPUT") + conn_url.type = "input" + conn_url.name = "url" + conn_url.setAttribute("form", conn_form.id) + conn_url.placeholder = "https://openbis.domain:port" + row.insertCell().appendChild(conn_url) + row.insertCell() + + var username = document.createElement("INPUT") + username.autocomplete = "off" + username.type = "text" + username.name = "username" + username.setAttribute("form", conn_form.id) + username.placeholder = "username" + var password = document.createElement("INPUT") + password.type = "password" + password.name = "password" + password.autocomplete = "new-password" + password.setAttribute("form", conn_form.id) + var create_btn = document.createElement("BUTTON") + create_btn.setAttribute("form", conn_form.id) + create_btn.textContent = "create" + var uname_pw_cell = row.insertCell() + uname_pw_cell.appendChild(username) + uname_pw_cell.appendChild(password) + uname_pw_cell.appendChild(create_btn) + + conn_table.innerHTML = "" + table_title = document.createElement("STRONG") + table_title.textContent = "Please choose a connection" + conn_table.appendChild(table_title) + conn_table.appendChild(table) + } + + return { + help: 'configure openBIS connections', + icon: 'fa-sliders', + help_index: '', + handler: function (env) { + conn_table = document.createElement("DIV") + var dst_title = document.createElement("STRONG") + dst_title.textContent = "DataSet type" + var dataset_types = document.createElement("SELECT") + dataset_types.id = "dataset_type" + dataset_types.className = "form-control select-xs" + + var input_fields = document.createElement("DIV") + conn_table.id = "openbis_connections" + + var onclick_cbf = function () { + state.connection.candidateName = this.value + } + + connections.list(env) + .done(function (data) { + show_available_connections(env, data, conn_table, onclick_cbf) + }) + .fail(function (data) { + alert(data.status) + }) + + var uploadDialogBox = $('<div/>').append(conn_table) + + // get the canvas for user feedback + var container = $('#notebook-container') + + function onOk() { + state.connection.name = state.connection.candidateName + } + + dialog.modal({ + body: uploadDialogBox, + title: 'Choose openBIS connection', + buttons: { + 'Cancel': {}, + 'Choose connection': { + class: 'btn-primary btn-large', + click: onOk + } + }, + notebook: env.notebook, + keyboard_manager: env.notebook.keyboard_manager + }) + } + } + } +) \ No newline at end of file diff --git a/jupyter-openbis-extension/static/connections.js b/jupyter-openbis-extension/static/connections.js new file mode 100644 index 0000000000000000000000000000000000000000..1f5972cf8f4f83b06d944796970186bba9982853 --- /dev/null +++ b/jupyter-openbis-extension/static/connections.js @@ -0,0 +1,67 @@ +define([ + "base/js/utils" + ], + function (utils) { + + function list(env) { + + var connectionsUrl = env.notebook.base_url + 'openbis/conns' + var settings = { + url: connectionsUrl, + processData: false, + type: 'GET', + dataType: 'json', + contentType: 'application/json' + } + return utils.ajax(settings) + } + + function connect(env, connection, username, password) { + var url = env.notebook.base_url + 'openbis/conn/' + connection + body = { + "username": username, + "password": password + } + + var cookie = decodeURIComponent(document.cookie) + var xsrf_token = cookie.split("_xsrf=")[1] + + return fetch(url, { + method: "PUT", + headers: { + "Content-Type": "application/json", + "X-XSRFToken": xsrf_token, + }, + body: JSON.stringify(body) + }) + } + + function create(env, connection_name, connection_url, username, password) { + var endpoint = env.notebook.base_url + 'openbis/conns' + body = { + "name": connection_name, + "url": connection_url, + "username": username, + "password": password + } + + var cookie = decodeURIComponent(document.cookie) + var xsrf_token = cookie.split("_xsrf=")[1] + + return fetch(endpoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-XSRFToken": xsrf_token, + }, + body: JSON.stringify(body) + }) + } + + return { + list: list, + create: create, + connect: connect + } + } +) \ No newline at end of file diff --git a/jupyter-openbis-extension/static/dialog.js b/jupyter-openbis-extension/static/dialog.js deleted file mode 100644 index 8c999c89682dff741940e0bba896a84448ff209f..0000000000000000000000000000000000000000 --- a/jupyter-openbis-extension/static/dialog.js +++ /dev/null @@ -1,826 +0,0 @@ -define([ - "base/js/namespace", - "base/js/dialog", - "base/js/utils", - "jquery" - ], - function(IPython, dialog, utils, $) { - var currentConnection = '' - var currentSampleIdentifier = null - var currentDownloadPath = null - var ds_type = document.createElement("SELECT") - ds_type.size = '40' - ds_type.className = "select-xs" - ds_type.id = "dataset_type" - var conn_table = null - - function show_available_connections(env, data, conn_table, onclick_cbf) { - if (!currentDownloadPath) { - currentDownloadPath = data.cwd - } - - var table = document.createElement("TABLE") - table.className = 'table-bordered table-striped table-condensed' - var thead = table.createTHead() - var thead_row = thead.insertRow(0) - var titles = ['', 'Name', 'URL', 'Status', 'Username / Password'] - for (title of titles) { - thead_row.insertCell().textContent = title - } - - tbody = table.createTBody() - for (connection of data.connections) { - var conn = document.createElement("INPUT") - conn.type = "radio" - conn.name = "connection_name" - conn.value = connection.name - conn.setAttribute("url", connection.url) - - conn.checked = connection.name === currentConnection; - if (onclick_cbf === undefined) { - conn.onclick = function() { - currentConnection = this.value - } - } - else { - conn.onclick = onclick_cbf - } - - var row = tbody.insertRow() - row.insertCell().appendChild(conn) - row.insertCell().textContent = connection.name - row.insertCell().textContent = connection.url - - var status_cell = row.insertCell() - - var status_badge = document.createElement("SPAN") - status_badge.id = connection.name + "-badge" - status_badge.textContent = connection.status - if (connection.status === "connected") { - status_badge.className = "label label-success" - } - else { - status_badge.className = "label label-danger" - } - status_cell.appendChild(status_badge) - - var username = document.createElement("INPUT") - username.type = "text" - username.name = "username" - username.autocomplete = "on" - username.value = connection.username - username.setAttribute("form", connection.name) - - var password = document.createElement("INPUT") - password.type = "password" - password.name = "password" - password.autocomplete = "current-password" - password.value = connection.password - password.setAttribute("form", connection.name) - - // Username / Password form - var pwform = document.createElement("FORM") - pwform.id = connection.name - pwform.onsubmit = function(event) { - var form_data = new FormData(this) - var status_badge = document.getElementById(this.id + "-badge") - reconnect_connection(env, this.id, - form_data.get("username"), form_data.get("password") - ) - .then( function(response) { - //console.log(response) - if (status_badge.nextElementSibling !== null) { - status_badge.parentNode.removeChild(status_badge.nextElementSibling) - } - if (response.ok) { - status_badge.textContent = "connected" - status_badge.className = "label label-success" - } - else { - status_badge.textContent = "not connected" - status_badge.className = "label label-danger" - message = document.createElement("p") - if (response.status === 401) { - message.textContent = "username/password incorrect" - } - else if (response.status === 500) { - message.textContent = "Connection error" - } - else { - message.textContent = "General error" - } - status_badge.parentNode.insertBefore(message, status_badge.nextSibling) - } - }) - .catch( error => console.error("Error while attempting to reconnect: ",error) ) - - return false - } - - - var connect_button = document.createElement("BUTTON") - connect_button.className = "btn btn-primary btn-xs" - connect_button.textContent = "connect" - - pwform.appendChild(username) - pwform.appendChild(password) - pwform.appendChild(connect_button) - - var cell = row.insertCell() - cell.appendChild(pwform) - } - - // add row for new connection - var row = tbody.insertRow() - - var conn_form = document.createElement("FORM") - conn_form.id = "new_connection" - conn_form.onsubmit = function(event) { - var inputs = document.querySelectorAll("input[form=new_connection]") - - data = {} - for (input of inputs) { - data[input.name] = input.value - } - for (missing of ['connection_name','url', 'username', 'password']) { - if (data[missing] === ""){ - alert("Please provide: " + missing) - return false - } - } - new_connection( - env, data.connection_name, data.url, data.username, data.password - ) - .then( function(response){ - if (response.ok) { - response.json() - .then( function(data){ - show_available_connections(env, data, conn_table) - }) - } - }) - return false - } - var conn_name = document.createElement("INPUT") - conn_name.type = "input" - conn_name.name = "connection_name" - conn_name.setAttribute("form", conn_form.id) - conn_name.placeholder = "openBIS instance name" - row.insertCell().appendChild(conn_form) - row.insertCell().appendChild(conn_name) - - var conn_url = document.createElement("INPUT") - conn_url.type = "input" - conn_url.name = "url" - conn_url.setAttribute("form", conn_form.id) - conn_url.placeholder = "https://openbis.domain:port" - row.insertCell().appendChild(conn_url) - row.insertCell() - - var username = document.createElement("INPUT") - username.autocomplete = "off" - username.type = "text" - username.name = "username" - username.setAttribute("form", conn_form.id) - username.placeholder = "username" - var password = document.createElement("INPUT") - password.type = "password" - password.name = "password" - password.autocomplete = "new-password" - password.setAttribute("form", conn_form.id) - var create_btn = document.createElement("BUTTON") - create_btn.setAttribute("form", conn_form.id) - create_btn.textContent = "create" - var uname_pw_cell = row.insertCell() - uname_pw_cell.appendChild(username) - uname_pw_cell.appendChild(password) - uname_pw_cell.appendChild(create_btn) - - conn_table.innerHTML = "" - table_title = document.createElement("STRONG") - table_title.textContent = "Please choose a connection" - conn_table.appendChild(table_title) - conn_table.appendChild(table) - } - - function show_datasets_table( data, datasets_table) { - var table = document.createElement("TABLE") - table.className = "table-bordered table-striped table-condensed text-nowrap" - var thead = table.createTHead() - var t_row = thead.insertRow() - var titles = ['', 'permId', 'Type', 'Experiment', 'Registration Date', 'Status', 'Size'] - titles.forEach( function(title) { - t_row.insertCell().textContent = title - }) - var tbody = table.createTBody() - - for (dataSet of data.dataSets) { - - var permId = document.createElement("INPUT") - permId.type = "checkbox" - permId.name = "permId" - permId.value = dataSet.permId - - var row = tbody.insertRow() - row.insertCell().appendChild(permId) - row.insertCell().textContent = dataSet.permId - row.insertCell().textContent = dataSet.type - row.insertCell().textContent = dataSet.experiment - row.insertCell().textContent = dataSet.registrationDate - row.insertCell().textContent = dataSet.status - row.insertCell().textContent = dataSet.size - } - - while (datasets_table.firstChild) { - datasets_table.removeChild(datasets_table.firstChild); - } - datasets_table.appendChild(table) - } - - // gets the status of the avialable openBIS connections - function getOpenBisConnections(env) { - - var connectionsUrl = env.notebook.base_url + 'openbis/conns' - var settings = { - url: connectionsUrl, - processData: false, - type: 'GET', - dataType: 'json', - contentType: 'application/json' - } - return utils.ajax(settings) - } - - function reconnect_connection(env, connection, username, password){ - var url = env.notebook.base_url + 'openbis/conn/' + connection - body = { - "username": username, - "password": password - } - - var cookie = decodeURIComponent(document.cookie) - var xsrf_token = cookie.split("_xsrf=")[1] - - return fetch(url, { - method: "PUT", - headers: { - "Content-Type": "application/json", - "X-XSRFToken": xsrf_token, - }, - body: JSON.stringify(body) - }) - } - - function new_connection(env, connection_name, connection_url, username, password){ - var endpoint = env.notebook.base_url + 'openbis/conns' - body = { - "name" : connection_name, - "url" : connection_url, - "username": username, - "password": password - } - - var cookie = decodeURIComponent(document.cookie) - var xsrf_token = cookie.split("_xsrf=")[1] - - return fetch(endpoint, { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-XSRFToken": xsrf_token, - }, - body: JSON.stringify(body) - }) - } - - function getDatasetTypes(env, connection_name, dataset_types, input_fields) { - // get all DatasetTypes of a given connection - - var url = env.notebook.base_url + 'openbis/datasetTypes/'+ connection_name - fetch(url) - .then( function(response) { - if (response.ok) { - response.json() - .then( function(data) { - //console.log(data.dataSetTypes) - - var change_input_fields = function() { - // remove existing input fields - while (input_fields.firstChild) { - input_fields.removeChild(input_fields.firstChild) - } - - // for every property assignment, create an input field. - for (pa of dts[dataset_type.selectedIndex].propertyAssignments) { - //var input_title = document.createTextNode(pa.label + ": ") - var input_field = document.createElement("INPUT") - input_field.type = "text" - input_field.name = pa.code - input_field.placeholder = pa.description ? pa.label + ": " + pa.description : pa.label - input_field.size = 90 - - //input_fields.appendChild(input_title) - input_fields.appendChild(input_field) - input_fields.appendChild(document.createElement("BR")) - } - } - dataset_types.onchange = change_input_fields - - // remove the old and add the new dataset-types - dts = data.dataSetTypes - while (dataset_types.firstChild) { - dataset_types.removeChild(dataset_types.firstChild); - } - for (dt of dts) { - var option = document.createElement("OPTION") - option.value = dt.code - option.textContent = dt.description ? dt.code + ": " + dt.description : dt.code - dataset_types.appendChild(option) - } - // change the input fields, since we just received new datasetTypes - change_input_fields() - - }) - .catch( function(error){ - console.error("Error while parsing dataset types", error) - }) - - } - else { - while (dataset_types.firstChild) { - dataset_types.removeChild(dataset_types.firstChild); - } - } - }) - .catch (function(error) { - console.error("Error while fetching dataset types:", error) - }) - } - - function createFeedback(type, content) { - var close = document.createElement("BUTTON") - close.className = "close" - close.setAttribute("data-dismiss", "alert") - close.setAttribute("aria-label", "Close") - var x = document.createElement("SPAN") - x.setAttribute("aria-hidden", true) - x.innerHTML = "×" - close.appendChild(x) - - var feedbackBox = document.createElement( "DIV" ) - feedbackBox.className = "openbis-feedback alert alert-dismissible alert-" + type - feedbackBox.setAttribute("role","alert") - feedbackBox.innerHTML = content - feedbackBox.prepend(close) - - var nb_container = document.getElementById('notebook-container') - nb_container.prepend(feedbackBox) - } - - function writeMetaData(data) { - var notebook = IPython.notebook - if (typeof notebook.metadata.openbis_connections === 'undefined') { - notebook.metadata.openbis_connections = {} - } - if (typeof notebook.metadata.openbis_connections[data.url] === 'undefined') { - notebook.metadata.openbis_connections[data.url] = {} - } - // store metadata about the downloaded files into the notebook-metadata - if (data.permId) { - notebook.metadata.openbis_connections[data.url][data.permId] = { - "permId": data.permId, - "path": data.path, - "dataStore": data.dataStore, - "location": data.location, - "size": data.size, - "status": data.statusText - } - } - - } - - var fetchDatasetFromOpenBis = { - help: 'Download openBIS datasets to your local harddrive', - icon: 'fa-download', - help_index: '', - handler: function (env) { - conn_table = document.createElement("DIV") - conn_table.id = "openbis_connections" - getOpenBisConnections(env) - .done(function (data) { - show_available_connections(env, data, conn_table) - }) - .fail(function (data) { - alert(data.status) - }) - .always(function () { - showDownloadDialog() - }) - - function showDownloadDialog() { - // This function gets called after loading the openBIS connections - // to make sure we can display the download path provided by the server. - - // show DataSets for Sample identifier/permid - var showDataSets = document.createElement("DIV") - var title = document.createElement("STRONG") - title.textContent = "Sample identfier/permId: " - showDataSets.appendChild(title) - showDataSets.style.marginTop = '10px' - - var sampleIdentifier = document.createElement("INPUT") - sampleIdentifier.type = "text" - sampleIdentifier.name = "sampleIdentifier" - sampleIdentifier.size = 40 - sampleIdentifier.placeholder = "sample identifier or permId" - sampleIdentifier.value = currentSampleIdentifier - - var datasets_table = document.createElement("DIV") - datasets_table.id = "dataset_table" - datasets_table.className = "output output_scroll" - datasets_table.style.maxHeight = "10em" - - var show_datasets_btn = document.createElement("BUTTON") - show_datasets_btn.className = "btn-info btn-xs" - show_datasets_btn.textContent = "show datasets" - - show_datasets_btn.onclick = function() { - var selected_conn = document.querySelector('input[name=connection_name]:checked') - if (!selected_conn) { - alert('Please choose a connection') - return false - } - connection_name = selected_conn.value - - currentConnection = connection_name - currentSampleIdentifier = sampleIdentifier.value - if (!currentSampleIdentifier) { - alert('Please specify a sample identifier/permId') - return false - } - var url = env.notebook.base_url + 'openbis/sample/' + connection_name + '/' + encodeURIComponent(currentSampleIdentifier) - - fetch(url) - .then( function(response) { - if (response.ok) { - response.json() - .then( function(data) { - show_datasets_table(data, datasets_table) - }) - } - else { - response.json() - .then( function(error) { - console.log(error.reason) - alert("Error: " + error.reason) - }) - } - }) - .catch( function(error) { - console.error('A serious network problem occured:', error) - }) - - } - showDataSets.appendChild(sampleIdentifier) - showDataSets.appendChild(show_datasets_btn) - showDataSets.appendChild(datasets_table) - - // dataSetPermId only - var dataset_direct = document.createElement("P") - dataset_direct.style.marginTop='10px' - dataset_direct.innerHTML = '<strong>enter DataSet permId directly: </strong>' - - //var dataset = $('<p>') - // .css('margin-top', '10px') - // .append($('<b>').text('... or enter DataSet permId directly: ')) - var datasetPermId = document.createElement("INPUT") - datasetPermId.type = "text" - datasetPermId.name = "datasetPermId" - datasetPermId.size = "40" - datasetPermId.placeholder = "dataSet permId" - - dataset_direct.appendChild(datasetPermId) - - var downloadPath = document.createElement("INPUT") - downloadPath.type = "text" - downloadPath.name = "downloadPath" - downloadPath.size = "90" - downloadPath.value = currentDownloadPath - - var path = document.createElement("DIV") - path.innerHTML = "<strong>download data to path: </strong>" - path.appendChild(downloadPath) - - var download_dialog_box = document.createElement("DIV") - download_dialog_box.appendChild(conn_table) - download_dialog_box.appendChild(showDataSets) - download_dialog_box.appendChild(dataset_direct) - download_dialog_box.appendChild(path) - - function downloadDataset(selected_conn, selectedPermIds, downloadPath) { - var connection_name = selected_conn.value - - for (permId of selectedPermIds) { - var downloadUrl = env.notebook.base_url + 'openbis/dataset/' - + connection_name + '/' + permId + '/' + encodeURIComponent(downloadPath) - - fetch(downloadUrl) - .then( function(response) { - if (response.ok) { - response.json() - .then( function(data) { - createFeedback('success', data.statusText) - - // successful download: - // write statusText from returned data to notebooks metadata - writeMetaData(data) - - // keep current download path for later use - currentDownloadPath = downloadPath - }) - } - else { - response.json() - .then( function(error) { - console.log(error.reason) - alert("Error: " + error.reason) - }) - } - }) - .catch( function(error) { - console.error('A serious network problem occured:', error) - }) - } - } - - function onDownloadClick() { - var selected_conn = document.querySelector('input[name=connection_name]:checked') - if (! selected_conn) { - alert('please choose a connection') - return false - } - - var selectedPermIds = [] - for (row of document.querySelectorAll('input[name=permId]:checked') ) { - selectedPermIds.push(row.value) - } - if (datasetPermId.value) { - selectedPermIds.push(datasetPermId.value) - } - if (!selectedPermIds) { - alert('please select a dataset or provide a permId') - return false - } - - if (!downloadPath.value) { - alert('Please specify where you would like to download your files!') - return false - } - downloadDataset(selected_conn, selectedPermIds, downloadPath.value) - } - - dialog.modal({ - body: download_dialog_box, - title: 'Download openBIS DataSets', - buttons: { - 'Cancel': {}, - 'Download': { - class: 'btn-primary btn-large', - click: onDownloadClick, - } - }, - notebook: env.notebook, - keyboard_manager: env.notebook.keyboard_manager - }) - } - } - } - - var uploadDatasetsToOpenBis = { - help: 'upload Notebook and Data to openBIS', - icon: 'fa-upload', - help_index: '', - handler: function (env) { - conn_table = document.createElement("DIV") - var dst_title = document.createElement("STRONG") - dst_title.textContent = "DataSet type" - var dataset_types = document.createElement("SELECT") - dataset_types.id = "dataset_type" - dataset_types.className = "form-control select-xs" - - var input_fields = document.createElement("DIV") - conn_table.id = "openbis_connections" - - var onclick_cbf = function() { - currentConnection = this.value - getDatasetTypes(env, this.value, dataset_types, input_fields) - } - - getOpenBisConnections(env) - .done(function (data) { - show_available_connections(env, data, conn_table, onclick_cbf) - }) - .fail(function (data) { - alert(data.status) - }) - - var sample_title = document.createElement("STRONG") - sample_title.textContent = "Sample Identifier" - var sampleIdentifier = document.createElement("INPUT") - sampleIdentifier.type = "text" - sampleIdentifier.name = 'sampleIdentifier' - sampleIdentifier.placeholder = "Sample Identifier or permId" - sampleIdentifier.value = currentSampleIdentifier - sampleIdentifier.size = "90" - - var ds_title = document.createElement("STRONG") - ds_title.textContent = "DataSet files" - var ds_files = document.createElement("INPUT") - ds_files.type = "text" - ds_files.placeholder = "filenames" - ds_files.name = "ds_files" - ds_files.size = "90" - - var inputs = document.createElement("DIV") - inputs.style.marginTop = '10px' - inputs.appendChild(dst_title) - inputs.appendChild(dataset_types) - inputs.appendChild(input_fields) - inputs.appendChild(sample_title) - inputs.appendChild(sampleIdentifier) - inputs.appendChild(ds_title) - inputs.appendChild(ds_files) - - var uploadDialogBox = $('<div/>').append(conn_table).append(inputs) - - - // get the canvas for user feedback - var container = $('#notebook-container') - - function onOk () { - //var connection_name = $('input[name="connection_name"]:checked').val() - var selected_connection = document.querySelector('input[name=connection_name]:checked') - if (!selected_connection) { - alert("No connection selected") - return false - } - var connection_name = selected_connection.value - - var uploadUrl = env.notebook.base_url + 'openbis/dataset/' + connection_name - - var notebook = IPython.notebook - var re = /\/notebooks\/(.*?)$/ - var files = [] - var filepath = window.location.pathname.match(re)[1] - files.push(filepath) - // FIXME - //if (ds_files.val()) { - // files.push(ds_files.value) - //} - - var dataSetInfo = { - "type" : dataset_types.value, - "files" : files, - "sampleIdentifier": sampleIdentifier.value - } - - var settings = { - url: uploadUrl, - processData: false, - type: 'POST', - dataType: 'json', - data: JSON.stringify(dataSetInfo), - contentType: 'application/json', - success: function (data) { - // display feedback to user - createFeedback('success', data.statusText) - - // write statusText from returned data to notebooks metadata - if ( typeof notebook.metadata.openbis === 'undefined') { - notebook.metadata.openbis = {} - } - if ( typeof notebook.metadata.openbis.permIds === 'undefined' ) { - notebook.metadata.openbis.permIds = {} - } - if ( data.permId ) { - notebook.metadata.openbis.permIds[data.permId] = data.statusText - } - - }, - error: function (data) { - // display feedback to user - var feedback = "<strong>Error: </strong>Dataset was not uploaded.<div>" - + data.statusText - + "</div>" - createFeedback('danger', feedback) - } - } - - // display preloader during commit and push - var preloader = '<img class="openbis-feedback" src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.5.8/ajax-loader.gif">' - - // commit and push - utils.ajax(settings) - } - - if (IPython.notebook.dirty === true) { - dialog.modal({ - body: 'Please save the notebook before uploading it to openBIS.', - title: 'Save notebook first', - buttons: { - 'Back': {} - }, - notebook: env.notebook, - keyboard_manager: env.notebook.keyboard_manager - }) - } - else { - dialog.modal({ - body: uploadDialogBox, - title: 'Upload openBIS DataSet', - buttons: { - 'Cancel': {}, - 'Upload': { - class: 'btn-primary btn-large', - click: onOk - } - }, - notebook: env.notebook, - keyboard_manager: env.notebook.keyboard_manager - }) - } - } - } - - var configureOpenBisConnections = { - help: 'configure openBIS connections', - icon: 'fa-sliders', - help_index: '', - handler: function (env) { - conn_table = document.createElement("DIV") - var dst_title = document.createElement("STRONG") - dst_title.textContent = "DataSet type" - var dataset_types = document.createElement("SELECT") - dataset_types.id = "dataset_type" - dataset_types.className = "form-control select-xs" - - var input_fields = document.createElement("DIV") - conn_table.id = "openbis_connections" - - var onclick_cbf = function() { - currentConnection = this.value - getDatasetTypes(env, this.value, dataset_types, input_fields) - } - - getOpenBisConnections(env) - .done(function (data) { - show_available_connections(env, data, conn_table, onclick_cbf) - }) - .fail(function (data) { - alert(data.status) - }) - - var uploadDialogBox = $('<div/>').append(conn_table) - - // get the canvas for user feedback - var container = $('#notebook-container') - - function onOk () { - - } - - dialog.modal({ - body: uploadDialogBox, - title: 'Choose openBIS connection', - buttons: { - 'Cancel': {}, - 'Choose connection': { - class: 'btn-primary btn-large', - click: onOk - } - }, - notebook: env.notebook, - keyboard_manager: env.notebook.keyboard_manager - }) - } - } - - function _onLoad () { - // show connections - var configure_openbis_connections = IPython.keyboard_manager.actions.register( - configureOpenBisConnections, 'openbis-connections', 'jupyter-openBIS') - - // dnownload - var download_datasets = IPython.keyboard_manager.actions.register( - fetchDatasetFromOpenBis, 'openbis-dataset-download', 'jupyter-openBIS') - - // upload - var upload_datasets = IPython.keyboard_manager.actions.register( - uploadDatasetsToOpenBis, 'openbis-dataset-upload', 'jupyter-openBIS') - - // add button for new action - IPython.toolbar.add_buttons_group([configure_openbis_connections, download_datasets, upload_datasets]) - } - - return {load_ipython_extension: _onLoad} - }) diff --git a/jupyter-openbis-extension/static/downloadDialog.js b/jupyter-openbis-extension/static/downloadDialog.js new file mode 100644 index 0000000000000000000000000000000000000000..973ff5e3066bedc3a272f487cc0329f68119e48f --- /dev/null +++ b/jupyter-openbis-extension/static/downloadDialog.js @@ -0,0 +1,242 @@ +define([ + "base/js/dialog", + "./common", + "./state" + ], + function (dialog, common, state) { + + function writeMetaData(data) { + var notebook = IPython.notebook + if (typeof notebook.metadata.openbis_connections === 'undefined') { + notebook.metadata.openbis_connections = {} + } + if (typeof notebook.metadata.openbis_connections[data.url] === 'undefined') { + notebook.metadata.openbis_connections[data.url] = {} + } + // store metadata about the downloaded files into the notebook-metadata + if (data.permId) { + notebook.metadata.openbis_connections[data.url][data.permId] = { + "permId": data.permId, + "path": data.path, + "dataStore": data.dataStore, + "location": data.location, + "size": data.size, + "status": data.statusText + } + } + } + + function show_datasets_table(data, datasets_table) { + var table = document.createElement("TABLE") + table.className = "table-bordered table-striped table-condensed text-nowrap" + var thead = table.createTHead() + var t_row = thead.insertRow() + var titles = ['', 'permId', 'Type', 'Experiment', 'Registration Date', 'Status', 'Size'] + titles.forEach(function (title) { + t_row.insertCell().textContent = title + }) + var tbody = table.createTBody() + + for (dataSet of data.dataSets) { + + var permId = document.createElement("INPUT") + permId.type = "checkbox" + permId.name = "permId" + permId.value = dataSet.permId + + var row = tbody.insertRow() + row.insertCell().appendChild(permId) + row.insertCell().textContent = dataSet.permId + row.insertCell().textContent = dataSet.type + row.insertCell().textContent = dataSet.experiment + row.insertCell().textContent = dataSet.registrationDate + row.insertCell().textContent = dataSet.status + row.insertCell().textContent = dataSet.size + } + + while (datasets_table.firstChild) { + datasets_table.removeChild(datasets_table.firstChild); + } + datasets_table.appendChild(table) + } + + + return { + help: 'Download openBIS datasets to your local harddrive', + icon: 'fa-download', + help_index: '', + handler: function (env) { + conn_table = document.createElement("DIV") + conn_table.id = "openbis_connections" + + var showDataSets = document.createElement("DIV") + var title = document.createElement("STRONG") + title.textContent = "Sample identifier / permId: " + showDataSets.appendChild(title) + showDataSets.style.marginTop = '10px' + + var sampleIdentifier = document.createElement("INPUT") + sampleIdentifier.type = "text" + sampleIdentifier.name = "sampleIdentifier" + sampleIdentifier.size = 40 + sampleIdentifier.placeholder = "sample identifier or permId" + sampleIdentifier.value = '' + + var datasets_table = document.createElement("DIV") + datasets_table.id = "dataset_table" + datasets_table.className = "output output_scroll" + datasets_table.style.maxHeight = "10em" + + var show_datasets_btn = document.createElement("BUTTON") + show_datasets_btn.className = "btn-info btn-xs" + show_datasets_btn.textContent = "show datasets" + + show_datasets_btn.onclick = function () { + var selected_conn = state.connection.name + if (!selected_conn) { + alert('Please choose a connection') + return false + } + connection_name = state.connection.name + + currentConnection = connection_name + currentSampleIdentifier = sampleIdentifier.value + if (!currentSampleIdentifier) { + alert('Please specify a sample identifier/permId') + return false + } + var url = env.notebook.base_url + 'openbis/sample/' + connection_name + '/' + encodeURIComponent(currentSampleIdentifier) + + fetch(url) + .then(function (response) { + if (response.ok) { + response.json() + .then(function (data) { + show_datasets_table(data, datasets_table) + }) + } else { + response.json() + .then(function (error) { + console.log(error.reason) + alert("Error: " + error.reason) + }) + } + }) + .catch(function (error) { + console.error('A serious network problem occured:', error) + }) + + } + showDataSets.appendChild(sampleIdentifier) + showDataSets.appendChild(show_datasets_btn) + showDataSets.appendChild(datasets_table) + + // dataSetPermId only + var dataset_direct = document.createElement("P") + dataset_direct.style.marginTop = '10px' + dataset_direct.innerHTML = '<strong>enter DataSet permId directly: </strong>' + + //var dataset = $('<p>') + // .css('margin-top', '10px') + // .append($('<b>').text('... or enter DataSet permId directly: ')) + var datasetPermId = document.createElement("INPUT") + datasetPermId.type = "text" + datasetPermId.name = "datasetPermId" + datasetPermId.size = "40" + datasetPermId.placeholder = "dataSet permId" + + dataset_direct.appendChild(datasetPermId) + + var downloadPath = document.createElement("INPUT") + downloadPath.type = "text" + downloadPath.name = "downloadPath" + downloadPath.size = "90" + downloadPath.value = '' + + var path = document.createElement("DIV") + path.innerHTML = "<strong>download data to path: </strong>" + path.appendChild(downloadPath) + + var download_dialog_box = document.createElement("DIV") + download_dialog_box.appendChild(conn_table) + download_dialog_box.appendChild(showDataSets) + download_dialog_box.appendChild(dataset_direct) + download_dialog_box.appendChild(path) + + function downloadDataset(connection_name, selectedPermIds, downloadPath) { + + for (permId of selectedPermIds) { + var downloadUrl = env.notebook.base_url + 'openbis/dataset/' + + connection_name + '/' + permId + '/' + encodeURIComponent(downloadPath) + + fetch(downloadUrl) + .then(function (response) { + if (response.ok) { + response.json() + .then(function (data) { + common.createFeedback('success', data.statusText) + + // successful download: + // write statusText from returned data to notebooks metadata + writeMetaData(data) + + // keep current download path for later use + currentDownloadPath = downloadPath + }) + } else { + response.json() + .then(function (error) { + console.log(error.reason) + alert("Error: " + error.reason) + }) + } + }) + .catch(function (error) { + console.error('A serious network problem occured:', error) + }) + } + } + + function onDownloadClick() { + var selected_conn = state.connection.name + if (!selected_conn) { + alert('please choose a connection') + return false + } + + var selectedPermIds = [] + for (row of document.querySelectorAll('input[name=permId]:checked')) { + selectedPermIds.push(row.value) + } + if (datasetPermId.value) { + selectedPermIds.push(datasetPermId.value) + } + if (!selectedPermIds) { + alert('please select a dataset or provide a permId') + return false + } + + if (!downloadPath.value) { + alert('Please specify where you would like to download your files!') + return false + } + downloadDataset(selected_conn, selectedPermIds, downloadPath.value) + } + + dialog.modal({ + body: download_dialog_box, + title: 'Download openBIS DataSets', + buttons: { + 'Cancel': {}, + 'Download': { + class: 'btn-primary btn-large', + click: onDownloadClick, + } + }, + notebook: env.notebook, + keyboard_manager: env.notebook.keyboard_manager + }) + } + } + } +) \ No newline at end of file diff --git a/jupyter-openbis-extension/static/main.js b/jupyter-openbis-extension/static/main.js new file mode 100644 index 0000000000000000000000000000000000000000..777e69d8ab57d2c25566d5425d904d546db115ef --- /dev/null +++ b/jupyter-openbis-extension/static/main.js @@ -0,0 +1,33 @@ +define([ + "base/js/namespace", + "./connectionDialog", + "./uploadDialog", + "./downloadDialog" + ], + function (IPython, connectionDialog, uploadDialog, downloadDialog) { + var ds_type = document.createElement("SELECT") + ds_type.size = '40' + ds_type.className = "select-xs" + ds_type.id = "dataset_type" + + function _onLoad() { + // show connections + var configure_openbis_connections = IPython.keyboard_manager.actions.register( + connectionDialog, 'openbis-connections', 'jupyter-openBIS') + + // dnownload + var download_datasets = IPython.keyboard_manager.actions.register( + downloadDialog, 'openbis-dataset-download', 'jupyter-openBIS') + + // upload + var upload_datasets = IPython.keyboard_manager.actions.register( + uploadDialog, 'openbis-dataset-upload', 'jupyter-openBIS') + + // add button for new action + IPython.toolbar.add_buttons_group([configure_openbis_connections, download_datasets, upload_datasets]) + } + + return { + load_ipython_extension: _onLoad + } + }) \ No newline at end of file diff --git a/jupyter-openbis-extension/static/state.js b/jupyter-openbis-extension/static/state.js new file mode 100644 index 0000000000000000000000000000000000000000..e72d76e13f2952f91369b45130869e27d5c567ec --- /dev/null +++ b/jupyter-openbis-extension/static/state.js @@ -0,0 +1,10 @@ +define([], + function () { + return { + connection: { + name: null, + candidateName: null + } + } + } +) \ No newline at end of file diff --git a/jupyter-openbis-extension/static/uploadDIalog.js b/jupyter-openbis-extension/static/uploadDIalog.js new file mode 100644 index 0000000000000000000000000000000000000000..ff3a51d8a54a15f64513bc5df2eebe2055c86792 --- /dev/null +++ b/jupyter-openbis-extension/static/uploadDIalog.js @@ -0,0 +1,213 @@ +define([ + "base/js/dialog", + "base/js/utils", + "jquery", + "./state", + "./common", + ], + function (dialog, utils, $, state, common) { + + function getDatasetTypes(env, connection_name, dataset_types, input_fields) { + // get all DatasetTypes of a given connection + + var url = env.notebook.base_url + 'openbis/datasetTypes/' + connection_name + fetch(url) + .then(function (response) { + if (response.ok) { + response.json() + .then(function (data) { + //console.log(data.dataSetTypes) + + var change_input_fields = function () { + // remove existing input fields + while (input_fields.firstChild) { + input_fields.removeChild(input_fields.firstChild) + } + + // for every property assignment, create an input field. + for (pa of dts[dataset_types.selectedIndex].propertyAssignments) { + //var input_title = document.createTextNode(pa.label + ": ") + var input_field = document.createElement("INPUT") + input_field.type = "text" + input_field.name = pa.code + input_field.placeholder = pa.description ? pa.label + ": " + pa.description : pa.label + input_field.size = 90 + + //input_fields.appendChild(input_title) + input_fields.appendChild(input_field) + input_fields.appendChild(document.createElement("BR")) + } + } + dataset_types.onchange = change_input_fields + + // remove the old and add the new dataset-types + dts = data.dataSetTypes + while (dataset_types.firstChild) { + dataset_types.removeChild(dataset_types.firstChild); + } + for (dt of dts) { + var option = document.createElement("OPTION") + option.value = dt.code + option.textContent = dt.description ? dt.code + ": " + dt.description : dt.code + dataset_types.appendChild(option) + } + // change the input fields, since we just received new datasetTypes + change_input_fields() + + }) + .catch(function (error) { + console.error("Error while parsing dataset types", error) + }) + + } else { + while (dataset_types.firstChild) { + dataset_types.removeChild(dataset_types.firstChild); + } + } + }) + .catch(function (error) { + console.error("Error while fetching dataset types:", error) + }) + } + + return { + help: 'upload Notebook and Data to openBIS', + icon: 'fa-upload', + help_index: '', + handler: function (env) { + + conn_table = document.createElement("DIV") + var dst_title = document.createElement("STRONG") + dst_title.textContent = "DataSet type" + var dataset_types = document.createElement("SELECT") + dataset_types.id = "dataset_type" + dataset_types.className = "form-control select-xs" + + var input_fields = document.createElement("DIV") + conn_table.id = "openbis_connections" + + getDatasetTypes(env, state.connection.name, dataset_types, input_fields) + + var sample_title = document.createElement("STRONG") + sample_title.textContent = "Sample Identifier" + var sampleIdentifier = document.createElement("INPUT") + sampleIdentifier.type = "text" + sampleIdentifier.name = 'sampleIdentifier' + sampleIdentifier.placeholder = "Sample Identifier or permId" + sampleIdentifier.value = '' + sampleIdentifier.size = "90" + + var ds_title = document.createElement("STRONG") + ds_title.textContent = "DataSet files" + var ds_files = document.createElement("INPUT") + ds_files.type = "text" + ds_files.placeholder = "filenames" + ds_files.name = "ds_files" + ds_files.size = "90" + + var inputs = document.createElement("DIV") + inputs.style.marginTop = '10px' + inputs.appendChild(dst_title) + inputs.appendChild(dataset_types) + inputs.appendChild(input_fields) + inputs.appendChild(sample_title) + inputs.appendChild(sampleIdentifier) + inputs.appendChild(ds_title) + inputs.appendChild(ds_files) + + var uploadDialogBox = $('<div/>').append(conn_table).append(inputs) + + function onOk() { + var connection_name = state.connection.name + + if (!connection_name) { + alert("No connection selected") + return false + } + + var uploadUrl = env.notebook.base_url + 'openbis/dataset/' + connection_name + + var notebook = IPython.notebook + var re = /\/notebooks\/(.*?)$/ + var files = [] + var filepath = window.location.pathname.match(re)[1] + files.push(filepath) + // FIXME + //if (ds_files.val()) { + // files.push(ds_files.value) + //} + + var dataSetInfo = { + "type": dataset_types.value, + "files": files, + "sampleIdentifier": sampleIdentifier.value + } + + var settings = { + url: uploadUrl, + processData: false, + type: 'POST', + dataType: 'json', + data: JSON.stringify(dataSetInfo), + contentType: 'application/json', + success: function (data) { + // display feedback to user + common.createFeedback('success', data.statusText) + + // write statusText from returned data to notebooks metadata + if (typeof notebook.metadata.openbis === 'undefined') { + notebook.metadata.openbis = {} + } + if (typeof notebook.metadata.openbis.permIds === 'undefined') { + notebook.metadata.openbis.permIds = {} + } + if (data.permId) { + notebook.metadata.openbis.permIds[data.permId] = data.statusText + } + + }, + error: function (data) { + // display feedback to user + var feedback = "<strong>Error: </strong>Dataset was not uploaded.<div>" + + data.statusText + + "</div>" + common.createFeedback('danger', feedback) + } + } + + // display preloader during commit and push + var preloader = '<img class="openbis-feedback" src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.5.8/ajax-loader.gif">' + + // commit and push + utils.ajax(settings) + } + + if (IPython.notebook.dirty === true) { + dialog.modal({ + body: 'Please save the notebook before uploading it to openBIS.', + title: 'Save notebook first', + buttons: { + 'Back': {} + }, + notebook: env.notebook, + keyboard_manager: env.notebook.keyboard_manager + }) + } else { + dialog.modal({ + body: uploadDialogBox, + title: 'Upload openBIS DataSet', + buttons: { + 'Cancel': {}, + 'Upload': { + class: 'btn-primary btn-large', + click: onOk + } + }, + notebook: env.notebook, + keyboard_manager: env.notebook.keyboard_manager + }) + } + } + } + } +) \ No newline at end of file diff --git a/openbis-connections.yaml b/openbis-connections.yaml index 4e02d7ff96ecd04da4ee3b7195f6a75e416c719d..754cd1c4c91dfe88e34e94d6368a2e36304705da 100644 --- a/openbis-connections.yaml +++ b/openbis-connections.yaml @@ -1,11 +1,16 @@ connections: - - name : local test openBIS instance - url : https://localhost:8443 - verify_certificates : false - username : username - password : password - - name : productive openBIS instance - url : https://openbis.example.com - verify_certificates : true - username : username - password : password + - name: local test openBIS instance + url: https://localhost:8443 + verify_certificates: false + username: username + password: password + - name: vagrant openBIS instance + url: https://localhost:8122 + verify_certificates: false + username: admin + password: password + - name: productive openBIS instance + url: https://openbis.example.com + verify_certificates: true + username: username + password: password diff --git a/setup.py b/setup.py index 15616447e27ea2606253377bd93f459a8a43d147..5eb05d0e8fa2ed017d3c0c8b07134ecc7377537e 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ setup( data_files=[ # like `jupyter nbextension install --sys-prefix` ("share/jupyter/nbextensions/jupyter-openbis-extension", [ - "jupyter-openbis-extension/static/dialog.js", + "jupyter-openbis-extension/static/main.js", ]), # like `jupyter nbextension enable --sys-prefix` ("etc/jupyter/nbconfig/notebook.d", [ diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile new file mode 100644 index 0000000000000000000000000000000000000000..b21d6f4e6bf9ebc4cf8ecda3d33c32897b9aedf2 --- /dev/null +++ b/vagrant/Vagrantfile @@ -0,0 +1,28 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure("2") do |config| + config.vm.box = "ubuntu/bionic64" + config.vm.box_version = "20180531.0.0" + + config.vm.provider "virtualbox" do |v| + v.memory = 4096 + v.cpus = 4 + v.name = "jupyter-openbis-extension" + end + + # Needs to be unique among VMs + config.notify_forwarder.port = 27282 + + config.vm.synced_folder "..", "/home/vagrant/jupyter-openbis-extension" + config.vm.synced_folder '.', '/vagrant', disabled: true + config.vm.synced_folder "files", "/files", :mount_options => ["ro"] + config.vm.synced_folder "shared", "/shared" + + config.vm.network "forwarded_port", guest: 8122, host: 8122, host_ip: "0.0.0.0" + config.vm.network "forwarded_port", guest: 8123, host: 8123, host_ip: "0.0.0.0" + config.vm.network "forwarded_port", guest: 8888, host: 8888, host_ip: "0.0.0.0" + + config.vm.provision :shell, path: "bootstrap.sh" + config.vm.provision :shell, path: "files/start-services.sh", run: "always", privileged: true +end diff --git a/vagrant/bootstrap.sh b/vagrant/bootstrap.sh new file mode 100644 index 0000000000000000000000000000000000000000..64620765b3dc52e51c25bb8de94bc42f0df97988 --- /dev/null +++ b/vagrant/bootstrap.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +sudo locale-gen en_US.UTF-8 + +echo " +LC_ALL=en_US.UTF-8 +LANG=en_US.UTF-8 +" >> /etc/environment + +apt update +apt install -y python3 python3-pip openjdk-8-jdk postgresql unzip + +cp /files/pg_hba.conf /etc/postgresql/10/main/pg_hba.conf +service postgresql restart +sleep 10 # let the db engine start + +sudo -E -u postgres -H -i /files/setup-postgres.sh +sudo -E -u vagrant -H -i /files/setup-vagrant.sh diff --git a/vagrant/files/console.properties b/vagrant/files/console.properties new file mode 100644 index 0000000000000000000000000000000000000000..faa327b56b38bcb8352771ef160acba48fddf193 --- /dev/null +++ b/vagrant/files/console.properties @@ -0,0 +1,62 @@ +# +# The path where openBIS will be installed. +# +# Example : +# INSTALL_PATH=/home/openbis/ +# +# will result in the following directory structure +# +# + /home/openbis +# + bin/ +# + servers/ +# + core-plugins/ +# + openBIS-server/ +# + datastore_server/ +# +INSTALL_PATH=/home/vagrant/openbis + +# +# The path where openBIS will keep the imported data (e.g. images, analysis files) +# and its incoming folders. +# +DSS_ROOT_DIR=/home/vagrant/dss_root + +# Possible configuration options +# 'local' - if the openBIS servers will only be accessed from this machine +# 'server' - if the installation is meant to be accessible for remote users +INSTALLATION_TYPE=local + +# Path to the file which should replace the current Java key store file +#KEY_STORE_FILE = <path to key store> + +# Password of the key store +KEY_STORE_PASSWORD = changeit + +# Password of the key +KEY_PASSWORD = changeit + +# Standard technology PROTEOMICS is disabled by default +#PROTEOMICS = true + +# Standard technology SCREENING is disabled by default +#SCREENING = true + +# Standard technology ILLUMINA-NGS (ETH BSSE Setup) is disabled by default +#ILLUMINA-NGS = true + +# Standard technology ELN-LIMS is disabled by default +#ELN-LIMS = true + +# Standard technology MICROSCOPY is disabled by default +#MICROSCOPY = true + +# Standard technology FLOW CYTOMETRY is disabled by default +#FLOW = true + +# Full ELN/LIMS master data is enabled by default. This setting is meaningful only if ELN-LIMS is enabled +ELN-LIMS-MASTER-DATA = false + +# +# Comma-separated list of databases to backup. If the list is empty or undefined all databases +# will be backauped. +#DATABASES_TO_BACKUP = \ No newline at end of file diff --git a/vagrant/files/jupyter_notebook_config.py b/vagrant/files/jupyter_notebook_config.py new file mode 100644 index 0000000000000000000000000000000000000000..e137e1661539d2436a296e0332a2219a90ec8fd1 --- /dev/null +++ b/vagrant/files/jupyter_notebook_config.py @@ -0,0 +1,765 @@ +# Configuration file for jupyter-notebook. + +# ------------------------------------------------------------------------------ +# Application(SingletonConfigurable) configuration +# ------------------------------------------------------------------------------ + +# This is an application. + +# The date format used by logging formatters for %(asctime)s +#c.Application.log_datefmt = '%Y-%m-%d %H:%M:%S' + +# The Logging format template +#c.Application.log_format = '[%(name)s]%(highlevel)s %(message)s' + +# Set the log level by value or name. +#c.Application.log_level = 30 + +# ------------------------------------------------------------------------------ +# JupyterApp(Application) configuration +# ------------------------------------------------------------------------------ + +# Base class for Jupyter applications + +# Answer yes to any prompts. +#c.JupyterApp.answer_yes = False + +# Full path of a config file. +#c.JupyterApp.config_file = '' + +# Specify a config file to load. +#c.JupyterApp.config_file_name = '' + +# Generate default config file. +#c.JupyterApp.generate_config = False + +# ------------------------------------------------------------------------------ +# NotebookApp(JupyterApp) configuration +# ------------------------------------------------------------------------------ + +# Set the Access-Control-Allow-Credentials: true header +#c.NotebookApp.allow_credentials = False + +# Set the Access-Control-Allow-Origin header +# +# Use '*' to allow any origin to access your server. +# +# Takes precedence over allow_origin_pat. +c.NotebookApp.allow_origin = '*' + +# Use a regular expression for the Access-Control-Allow-Origin header +# +# Requests from an origin matching the expression will get replies with: +# +# Access-Control-Allow-Origin: origin +# +# where `origin` is the origin of the request. +# +# Ignored if allow_origin is set. +#c.NotebookApp.allow_origin_pat = '' + +# Allow password to be changed at login for the notebook server. +# +# While loggin in with a token, the notebook server UI will give the opportunity +# to the user to enter a new password at the same time that will replace the +# token login mechanism. +# +# This can be set to false to prevent changing password from the UI/API. +#c.NotebookApp.allow_password_change = True + +# Allow requests where the Host header doesn't point to a local server +# +# By default, requests get a 403 forbidden response if the 'Host' header shows +# that the browser thinks it's on a non-local domain. Setting this option to +# True disables this check. +# +# This protects against 'DNS rebinding' attacks, where a remote web server +# serves you a page and then changes its DNS to send later requests to a local +# IP, bypassing same-origin checks. +# +# Local IP addresses (such as 127.0.0.1 and ::1) are allowed as local, along +# with hostnames configured in local_hostnames. +c.NotebookApp.allow_remote_access = True + +# Whether to allow the user to run the notebook as root. +#c.NotebookApp.allow_root = False + +# DEPRECATED use base_url +#c.NotebookApp.base_project_url = '/' + +# The base URL for the notebook server. +# +# Leading and trailing slashes can be omitted, and will automatically be added. +#c.NotebookApp.base_url = '/' + +# Specify what command to use to invoke a web browser when opening the notebook. +# If not specified, the default browser will be determined by the `webbrowser` +# standard library module, which allows setting of the BROWSER environment +# variable to override it. +#c.NotebookApp.browser = '' + +# The full path to an SSL/TLS certificate file. +#c.NotebookApp.certfile = '' + +# The full path to a certificate authority certificate for SSL/TLS client +# authentication. +#c.NotebookApp.client_ca = '' + +# The config manager class to use +#c.NotebookApp.config_manager_class = 'notebook.services.config.manager.ConfigManager' + +# The notebook manager class to use. +#c.NotebookApp.contents_manager_class = 'notebook.services.contents.largefilemanager.LargeFileManager' + +# Extra keyword arguments to pass to `set_secure_cookie`. See tornado's +# set_secure_cookie docs for details. +#c.NotebookApp.cookie_options = {} + +# The random bytes used to secure cookies. By default this is a new random +# number every time you start the Notebook. Set it to a value in a config file +# to enable logins to persist across server sessions. +# +# Note: Cookie secrets should be kept private, do not share config files with +# cookie_secret stored in plaintext (you can read the value from a file). +#c.NotebookApp.cookie_secret = b'' + +# The file where the cookie secret is stored. +#c.NotebookApp.cookie_secret_file = '' + +# Override URL shown to users. +# +# Replace actual URL, including protocol, address, port and base URL, with the +# given value when displaying URL to the users. Do not change the actual +# connection URL. If authentication token is enabled, the token is added to the +# custom URL automatically. +# +# This option is intended to be used when the URL to display to the user cannot +# be determined reliably by the Jupyter notebook server (proxified or +# containerized setups for example). +#c.NotebookApp.custom_display_url = '' + +# The default URL to redirect to from `/` +#c.NotebookApp.default_url = '/tree' + +# Disable cross-site-request-forgery protection +# +# Jupyter notebook 4.3.1 introduces protection from cross-site request +# forgeries, requiring API requests to either: +# +# - originate from pages served by this server (validated with XSRF cookie and +# token), or - authenticate with a token +# +# Some anonymous compute resources still desire the ability to run code, +# completely without authentication. These services can disable all +# authentication and security checks, with the full knowledge of what that +# implies. +c.NotebookApp.disable_check_xsrf = True + +# Whether to enable MathJax for typesetting math/TeX +# +# MathJax is the javascript library Jupyter uses to render math/LaTeX. It is +# very large, so you may want to disable it if you have a slow internet +# connection, or for offline use of the notebook. +# +# When disabled, equations etc. will appear as their untransformed TeX source. +#c.NotebookApp.enable_mathjax = True + +# extra paths to look for Javascript notebook extensions +#c.NotebookApp.extra_nbextensions_path = [] + +# handlers that should be loaded at higher priority than the default services +#c.NotebookApp.extra_services = [] + +# Extra paths to search for serving static files. +# +# This allows adding javascript/css to be available from the notebook server +# machine, or overriding individual files in the IPython +#c.NotebookApp.extra_static_paths = [] + +# Extra paths to search for serving jinja templates. +# +# Can be used to override templates from notebook.templates. +#c.NotebookApp.extra_template_paths = [] + +## +#c.NotebookApp.file_to_run = '' + +# Extra keyword arguments to pass to `get_secure_cookie`. See tornado's +# get_secure_cookie docs for details. +#c.NotebookApp.get_secure_cookie_kwargs = {} + +# Deprecated: Use minified JS file or not, mainly use during dev to avoid JS +# recompilation +#c.NotebookApp.ignore_minified_js = False + +# (bytes/sec) Maximum rate at which stream output can be sent on iopub before +# they are limited. +#c.NotebookApp.iopub_data_rate_limit = 1000000 + +# (msgs/sec) Maximum rate at which messages can be sent on iopub before they are +# limited. +#c.NotebookApp.iopub_msg_rate_limit = 1000 + +# The IP address the notebook server will listen on. +c.NotebookApp.ip = '0.0.0.0' + +# Supply extra arguments that will be passed to Jinja environment. +#c.NotebookApp.jinja_environment_options = {} + +# Extra variables to supply to jinja templates when rendering. +#c.NotebookApp.jinja_template_vars = {} + +# The kernel manager class to use. +#c.NotebookApp.kernel_manager_class = 'notebook.services.kernels.kernelmanager.MappingKernelManager' + +# The kernel spec manager class to use. Should be a subclass of +# `jupyter_client.kernelspec.KernelSpecManager`. +# +# The Api of KernelSpecManager is provisional and might change without warning +# between this version of Jupyter and the next stable one. +#c.NotebookApp.kernel_spec_manager_class = 'jupyter_client.kernelspec.KernelSpecManager' + +# The full path to a private key file for usage with SSL/TLS. +#c.NotebookApp.keyfile = '' + +# Hostnames to allow as local when allow_remote_access is False. +# +# Local IP addresses (such as 127.0.0.1 and ::1) are automatically accepted as +# local as well. +#c.NotebookApp.local_hostnames = ['localhost'] + +# The login handler class to use. +#c.NotebookApp.login_handler_class = 'notebook.auth.login.LoginHandler' + +# The logout handler class to use. +#c.NotebookApp.logout_handler_class = 'notebook.auth.logout.LogoutHandler' + +# The MathJax.js configuration file that is to be used. +#c.NotebookApp.mathjax_config = 'TeX-AMS-MML_HTMLorMML-full,Safe' + +# A custom url for MathJax.js. Should be in the form of a case-sensitive url to +# MathJax, for example: /static/components/MathJax/MathJax.js +#c.NotebookApp.mathjax_url = '' + +# Sets the maximum allowed size of the client request body, specified in the +# Content-Length request header field. If the size in a request exceeds the +# configured value, a malformed HTTP message is returned to the client. +# +# Note: max_body_size is applied even in streaming mode. +#c.NotebookApp.max_body_size = 536870912 + +# Gets or sets the maximum amount of memory, in bytes, that is allocated for +# use by the buffer manager. +#c.NotebookApp.max_buffer_size = 536870912 + +# Dict of Python modules to load as notebook server extensions.Entry values can +# be used to enable and disable the loading ofthe extensions. The extensions +# will be loaded in alphabetical order. +#c.NotebookApp.nbserver_extensions = {} + +# The directory to use for notebooks and kernels. +#c.NotebookApp.notebook_dir = '' + +# Whether to open in a browser after starting. The specific browser used is +# platform dependent and determined by the python standard library `webbrowser` +# module, unless it is overridden using the --browser (NotebookApp.browser) +# configuration option. +c.NotebookApp.open_browser = False + +# Hashed password to use for web authentication. +# +# To generate, type in a python/IPython shell: +# +# from notebook.auth import passwd; passwd() +# +# The string should be of the form type:salt:hashed-password. +#c.NotebookApp.password = '' + +# Forces users to use a password for the Notebook server. This is useful in a +# multi user environment, for instance when everybody in the LAN can access each +# other's machine through ssh. +# +# In such a case, server the notebook server on localhost is not secure since +# any user can connect to the notebook server via ssh. +c.NotebookApp.password_required = False + +# The port the notebook server will listen on. +#c.NotebookApp.port = 8888 + +# The number of additional ports to try if the specified port is not available. +#c.NotebookApp.port_retries = 50 + +# DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib. +#c.NotebookApp.pylab = 'disabled' + +# If True, display a button in the dashboard to quit (shutdown the notebook +# server). +#c.NotebookApp.quit_button = True + +# (sec) Time window used to check the message and data rate limits. +#c.NotebookApp.rate_limit_window = 3 + +# Reraise exceptions encountered loading server extensions? +#c.NotebookApp.reraise_server_extension_failures = False + +# DEPRECATED use the nbserver_extensions dict instead +#c.NotebookApp.server_extensions = [] + +# The session manager class to use. +#c.NotebookApp.session_manager_class = 'notebook.services.sessions.sessionmanager.SessionManager' + +# Shut down the server after N seconds with no kernels or terminals running and +# no activity. This can be used together with culling idle kernels +# (MappingKernelManager.cull_idle_timeout) to shutdown the notebook server when +# it's not in use. This is not precisely timed: it may shut down up to a minute +# later. 0 (the default) disables this automatic shutdown. +#c.NotebookApp.shutdown_no_activity_timeout = 0 + +# Supply SSL options for the tornado HTTPServer. See the tornado docs for +# details. +#c.NotebookApp.ssl_options = {} + +# Supply overrides for terminado. Currently only supports "shell_command". +#c.NotebookApp.terminado_settings = {} + +# Set to False to disable terminals. +# +# This does *not* make the notebook server more secure by itself. Anything the +# user can in a terminal, they can also do in a notebook. +# +# Terminals may also be automatically disabled if the terminado package is not +# available. +#c.NotebookApp.terminals_enabled = True + +# Token used for authenticating first-time connections to the server. +# +# When no password is enabled, the default is to generate a new, random token. +# +# Setting to an empty string disables authentication altogether, which is NOT +# RECOMMENDED. +c.NotebookApp.token = '' + +# Supply overrides for the tornado.web.Application that the Jupyter notebook +# uses. +#c.NotebookApp.tornado_settings = {} + +# Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded- +# For headerssent by the upstream reverse proxy. Necessary if the proxy handles +# SSL +#c.NotebookApp.trust_xheaders = False + +# DEPRECATED, use tornado_settings +#c.NotebookApp.webapp_settings = {} + +# Specify Where to open the notebook on startup. This is the `new` argument +# passed to the standard library method `webbrowser.open`. The behaviour is not +# guaranteed, but depends on browser support. Valid values are: +# +# - 2 opens a new tab, +# - 1 opens a new window, +# - 0 opens in an existing window. +# +# See the `webbrowser.open` documentation for details. +#c.NotebookApp.webbrowser_open_new = 2 + +# Set the tornado compression options for websocket connections. +# +# This value will be returned from +# :meth:`WebSocketHandler.get_compression_options`. None (default) will disable +# compression. A dict (even an empty one) will enable compression. +# +# See the tornado docs for WebSocketHandler.get_compression_options for details. +#c.NotebookApp.websocket_compression_options = None + +# The base URL for websockets, if it differs from the HTTP server (hint: it +# almost certainly doesn't). +# +# Should be in the form of an HTTP origin: ws[s]://hostname[:port] +#c.NotebookApp.websocket_url = '' + +# ------------------------------------------------------------------------------ +# ConnectionFileMixin(LoggingConfigurable) configuration +# ------------------------------------------------------------------------------ + +# Mixin for configurable classes that work with connection files + +# JSON file in which to store connection info [default: kernel-<pid>.json] +# +# This file will contain the IP, ports, and authentication key needed to connect +# clients to this kernel. By default, this file will be created in the security +# dir of the current profile, but can be specified by absolute path. +#c.ConnectionFileMixin.connection_file = '' + +# set the control (ROUTER) port [default: random] +#c.ConnectionFileMixin.control_port = 0 + +# set the heartbeat port [default: random] +#c.ConnectionFileMixin.hb_port = 0 + +# set the iopub (PUB) port [default: random] +#c.ConnectionFileMixin.iopub_port = 0 + +# Set the kernel's IP address [default localhost]. If the IP address is +# something other than localhost, then Consoles on other machines will be able +# to connect to the Kernel, so be careful! +#c.ConnectionFileMixin.ip = '' + +# set the shell (ROUTER) port [default: random] +#c.ConnectionFileMixin.shell_port = 0 + +# set the stdin (ROUTER) port [default: random] +#c.ConnectionFileMixin.stdin_port = 0 + +## +#c.ConnectionFileMixin.transport = 'tcp' + +# ------------------------------------------------------------------------------ +# KernelManager(ConnectionFileMixin) configuration +# ------------------------------------------------------------------------------ + +# Manages a single kernel in a subprocess on this host. +# +# This version starts kernels with Popen. + +# Should we autorestart the kernel if it dies. +#c.KernelManager.autorestart = True + +# DEPRECATED: Use kernel_name instead. +# +# The Popen Command to launch the kernel. Override this if you have a custom +# kernel. If kernel_cmd is specified in a configuration file, Jupyter does not +# pass any arguments to the kernel, because it cannot make any assumptions about +# the arguments that the kernel understands. In particular, this means that the +# kernel does not receive the option --debug if it given on the Jupyter command +# line. +#c.KernelManager.kernel_cmd = [] + +# Time to wait for a kernel to terminate before killing it, in seconds. +#c.KernelManager.shutdown_wait_time = 5.0 + +# ------------------------------------------------------------------------------ +# Session(Configurable) configuration +# ------------------------------------------------------------------------------ + +# Object for handling serialization and sending of messages. +# +# The Session object handles building messages and sending them with ZMQ sockets +# or ZMQStream objects. Objects can communicate with each other over the +# network via Session objects, and only need to work with the dict-based IPython +# message spec. The Session will handle serialization/deserialization, security, +# and metadata. +# +# Sessions support configurable serialization via packer/unpacker traits, and +# signing with HMAC digests via the key/keyfile traits. +# +# Parameters ---------- +# +# debug : bool +# whether to trigger extra debugging statements +# packer/unpacker : str : 'json', 'pickle' or import_string +# importstrings for methods to serialize message parts. If just +# 'json' or 'pickle', predefined JSON and pickle packers will be used. +# Otherwise, the entire importstring must be used. +# +# The functions must accept at least valid JSON input, and output *bytes*. +# +# For example, to use msgpack: +# packer = 'msgpack.packb', unpacker='msgpack.unpackb' +# pack/unpack : callables +# You can also set the pack/unpack callables for serialization directly. +# session : bytes +# the ID of this Session object. The default is to generate a new UUID. +# username : unicode +# username added to message headers. The default is to ask the OS. +# key : bytes +# The key used to initialize an HMAC signature. If unset, messages +# will not be signed or checked. +# keyfile : filepath +# The file containing a key. If this is set, `key` will be initialized +# to the contents of the file. + +# Threshold (in bytes) beyond which an object's buffer should be extracted to +# avoid pickling. +#c.Session.buffer_threshold = 1024 + +# Whether to check PID to protect against calls after fork. +# +# This check can be disabled if fork-safety is handled elsewhere. +#c.Session.check_pid = True + +# Threshold (in bytes) beyond which a buffer should be sent without copying. +#c.Session.copy_threshold = 65536 + +# Debug output in the Session +#c.Session.debug = False + +# The maximum number of digests to remember. +# +# The digest history will be culled when it exceeds this value. +#c.Session.digest_history_size = 65536 + +# The maximum number of items for a container to be introspected for custom +# serialization. Containers larger than this are pickled outright. +#c.Session.item_threshold = 64 + +# execution key, for signing messages. +#c.Session.key = b'' + +# path to file containing execution key. +#c.Session.keyfile = '' + +# Metadata dictionary, which serves as the default top-level metadata dict for +# each message. +#c.Session.metadata = {} + +# The name of the packer for serializing messages. Should be one of 'json', +# 'pickle', or an import name for a custom callable serializer. +#c.Session.packer = 'json' + +# The UUID identifying this session. +#c.Session.session = '' + +# The digest scheme used to construct the message signatures. Must have the form +# 'hmac-HASH'. +#c.Session.signature_scheme = 'hmac-sha256' + +# The name of the unpacker for unserializing messages. Only used with custom +# functions for `packer`. +#c.Session.unpacker = 'json' + +# Username for the Session. Default is your system username. +#c.Session.username = 'vagrant' + +# ------------------------------------------------------------------------------ +# MultiKernelManager(LoggingConfigurable) configuration +# ------------------------------------------------------------------------------ + +# A class for managing multiple kernels. + +# The name of the default kernel to start +#c.MultiKernelManager.default_kernel_name = 'python3' + +# The kernel manager class. This is configurable to allow subclassing of the +# KernelManager for customized behavior. +#c.MultiKernelManager.kernel_manager_class = 'jupyter_client.ioloop.IOLoopKernelManager' + +# ------------------------------------------------------------------------------ +# MappingKernelManager(MultiKernelManager) configuration +# ------------------------------------------------------------------------------ + +# A KernelManager that handles notebook mapping and HTTP error handling + +# Whether messages from kernels whose frontends have disconnected should be +# buffered in-memory. +# +# When True (default), messages are buffered and replayed on reconnect, avoiding +# lost messages due to interrupted connectivity. +# +# Disable if long-running kernels will produce too much output while no +# frontends are connected. +#c.MappingKernelManager.buffer_offline_messages = True + +# Whether to consider culling kernels which are busy. Only effective if +# cull_idle_timeout > 0. +#c.MappingKernelManager.cull_busy = False + +# Whether to consider culling kernels which have one or more connections. Only +# effective if cull_idle_timeout > 0. +#c.MappingKernelManager.cull_connected = False + +# Timeout (in seconds) after which a kernel is considered idle and ready to be +# culled. Values of 0 or lower disable culling. Very short timeouts may result +# in kernels being culled for users with poor network connections. +#c.MappingKernelManager.cull_idle_timeout = 0 + +# The interval (in seconds) on which to check for idle kernels exceeding the +# cull timeout value. +#c.MappingKernelManager.cull_interval = 300 + +# Timeout for giving up on a kernel (in seconds). +# +# On starting and restarting kernels, we check whether the kernel is running and +# responsive by sending kernel_info_requests. This sets the timeout in seconds +# for how long the kernel can take before being presumed dead. This affects the +# MappingKernelManager (which handles kernel restarts) and the +# ZMQChannelsHandler (which handles the startup). +#c.MappingKernelManager.kernel_info_timeout = 60 + +## +#c.MappingKernelManager.root_dir = '' + +# ------------------------------------------------------------------------------ +# ContentsManager(LoggingConfigurable) configuration +# ------------------------------------------------------------------------------ + +# Base class for serving files and directories. +# +# This serves any text or binary file, as well as directories, with special +# handling for JSON notebook documents. +# +# Most APIs take a path argument, which is always an API-style unicode path, and +# always refers to a directory. +# +# - unicode, not url-escaped +# - '/'-separated +# - leading and trailing '/' will be stripped +# - if unspecified, path defaults to '', +# indicating the root path. + +# Allow access to hidden files +#c.ContentsManager.allow_hidden = False + +## +#c.ContentsManager.checkpoints = None + +## +#c.ContentsManager.checkpoints_class = 'notebook.services.contents.checkpoints.Checkpoints' + +## +#c.ContentsManager.checkpoints_kwargs = {} + +# handler class to use when serving raw file requests. +# +# Default is a fallback that talks to the ContentsManager API, which may be +# inefficient, especially for large files. +# +# Local files-based ContentsManagers can use a StaticFileHandler subclass, which +# will be much more efficient. +# +# Access to these files should be Authenticated. +#c.ContentsManager.files_handler_class = 'notebook.files.handlers.FilesHandler' + +# Extra parameters to pass to files_handler_class. +# +# For example, StaticFileHandlers generally expect a `path` argument specifying +# the root directory from which to serve files. +#c.ContentsManager.files_handler_params = {} + +# Glob patterns to hide in file and directory listings. +#c.ContentsManager.hide_globs = ['__pycache__', '*.pyc', '*.pyo', '.DS_Store', '*.so', '*.dylib', '*~'] + +# Python callable or importstring thereof +# +# To be called on a contents model prior to save. +# +# This can be used to process the structure, such as removing notebook outputs +# or other side effects that should not be saved. +# +# It will be called as (all arguments passed by keyword):: +# +# hook(path=path, model=model, contents_manager=self) +# +# - model: the model to be saved. Includes file contents. +# Modifying this dict will affect the file that is stored. +# - path: the API path of the save destination +# - contents_manager: this ContentsManager instance +#c.ContentsManager.pre_save_hook = None + +## +#c.ContentsManager.root_dir = '/' + +# The base name used when creating untitled directories. +#c.ContentsManager.untitled_directory = 'Untitled Folder' + +# The base name used when creating untitled files. +#c.ContentsManager.untitled_file = 'untitled' + +# The base name used when creating untitled notebooks. +#c.ContentsManager.untitled_notebook = 'Untitled' + +# ------------------------------------------------------------------------------ +# FileManagerMixin(Configurable) configuration +# ------------------------------------------------------------------------------ + +# Mixin for ContentsAPI classes that interact with the filesystem. +# +# Provides facilities for reading, writing, and copying both notebooks and +# generic files. +# +# Shared by FileContentsManager and FileCheckpoints. +# +# Note ---- Classes using this mixin must provide the following attributes: +# +# root_dir : unicode +# A directory against against which API-style paths are to be resolved. +# +# log : logging.Logger + +# By default notebooks are saved on disk on a temporary file and then if +# succefully written, it replaces the old ones. This procedure, namely +# 'atomic_writing', causes some bugs on file system whitout operation order +# enforcement (like some networked fs). If set to False, the new notebook is +# written directly on the old one which could fail (eg: full filesystem or quota +# ) +#c.FileManagerMixin.use_atomic_writing = True + +# ------------------------------------------------------------------------------ +# FileContentsManager(FileManagerMixin,ContentsManager) configuration +# ------------------------------------------------------------------------------ + +# If True (default), deleting files will send them to the platform's +# trash/recycle bin, where they can be recovered. If False, deleting files +# really deletes them. +#c.FileContentsManager.delete_to_trash = True + +# Python callable or importstring thereof +# +# to be called on the path of a file just saved. +# +# This can be used to process the file on disk, such as converting the notebook +# to a script or HTML via nbconvert. +# +# It will be called as (all arguments passed by keyword):: +# +# hook(os_path=os_path, model=model, contents_manager=instance) +# +# - path: the filesystem path to the file just written - model: the model +# representing the file - contents_manager: this ContentsManager instance +#c.FileContentsManager.post_save_hook = None + +## +#c.FileContentsManager.root_dir = '' + +# DEPRECATED, use post_save_hook. Will be removed in Notebook 5.0 +#c.FileContentsManager.save_script = False + +# ------------------------------------------------------------------------------ +# NotebookNotary(LoggingConfigurable) configuration +# ------------------------------------------------------------------------------ + +# A class for computing and verifying notebook signatures. + +# The hashing algorithm used to sign notebooks. +#c.NotebookNotary.algorithm = 'sha256' + +# The sqlite file in which to store notebook signatures. By default, this will +# be in your Jupyter data directory. You can set it to ':memory:' to disable +# sqlite writing to the filesystem. +#c.NotebookNotary.db_file = '' + +# The secret key with which notebooks are signed. +#c.NotebookNotary.secret = b'' + +# The file where the secret key is stored. +#c.NotebookNotary.secret_file = '' + +# A callable returning the storage backend for notebook signatures. The default +# uses an SQLite database. +#c.NotebookNotary.store_factory = traitlets.Undefined + +# ------------------------------------------------------------------------------ +# KernelSpecManager(LoggingConfigurable) configuration +# ------------------------------------------------------------------------------ + +# If there is no Python kernelspec registered and the IPython kernel is +# available, ensure it is added to the spec list. +#c.KernelSpecManager.ensure_native_kernel = True + +# The kernel spec class. This is configurable to allow subclassing of the +# KernelSpecManager for customized behavior. +#c.KernelSpecManager.kernel_spec_class = 'jupyter_client.kernelspec.KernelSpec' + +# Whitelist of allowed kernel names. +# +# By default, all installed kernels are allowed. +#c.KernelSpecManager.whitelist = set() diff --git a/vagrant/files/pg_hba.conf b/vagrant/files/pg_hba.conf new file mode 100644 index 0000000000000000000000000000000000000000..5383ee2765aad917cb2c909e743f2a9cf8b335e3 --- /dev/null +++ b/vagrant/files/pg_hba.conf @@ -0,0 +1,99 @@ +# PostgreSQL Client Authentication Configuration File +# =================================================== +# +# Refer to the "Client Authentication" section in the PostgreSQL +# documentation for a complete description of this file. A short +# synopsis follows. +# +# This file controls: which hosts are allowed to connect, how clients +# are authenticated, which PostgreSQL user names they can use, which +# databases they can access. Records take one of these forms: +# +# local DATABASE USER METHOD [OPTIONS] +# host DATABASE USER ADDRESS METHOD [OPTIONS] +# hostssl DATABASE USER ADDRESS METHOD [OPTIONS] +# hostnossl DATABASE USER ADDRESS METHOD [OPTIONS] +# +# (The uppercase items must be replaced by actual values.) +# +# The first field is the connection type: "local" is a Unix-domain +# socket, "host" is either a plain or SSL-encrypted TCP/IP socket, +# "hostssl" is an SSL-encrypted TCP/IP socket, and "hostnossl" is a +# plain TCP/IP socket. +# +# DATABASE can be "all", "sameuser", "samerole", "replication", a +# database name, or a comma-separated list thereof. The "all" +# keyword does not match "replication". Access to replication +# must be enabled in a separate record (see example below). +# +# USER can be "all", a user name, a group name prefixed with "+", or a +# comma-separated list thereof. In both the DATABASE and USER fields +# you can also write a file name prefixed with "@" to include names +# from a separate file. +# +# ADDRESS specifies the set of hosts the record matches. It can be a +# host name, or it is made up of an IP address and a CIDR mask that is +# an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that +# specifies the number of significant bits in the mask. A host name +# that starts with a dot (.) matches a suffix of the actual host name. +# Alternatively, you can write an IP address and netmask in separate +# columns to specify the set of hosts. Instead of a CIDR-address, you +# can write "samehost" to match any of the server's own IP addresses, +# or "samenet" to match any address in any subnet that the server is +# directly connected to. +# +# METHOD can be "trust", "reject", "md5", "password", "scram-sha-256", +# "gss", "sspi", "ident", "peer", "pam", "ldap", "radius" or "cert". +# Note that "password" sends passwords in clear text; "md5" or +# "scram-sha-256" are preferred since they send encrypted passwords. +# +# OPTIONS are a set of options for the authentication in the format +# NAME=VALUE. The available options depend on the different +# authentication methods -- refer to the "Client Authentication" +# section in the documentation for a list of which options are +# available for which authentication methods. +# +# Database and user names containing spaces, commas, quotes and other +# special characters must be quoted. Quoting one of the keywords +# "all", "sameuser", "samerole" or "replication" makes the name lose +# its special character, and just match a database or username with +# that name. +# +# This file is read on server startup and when the server receives a +# SIGHUP signal. If you edit the file on a running system, you have to +# SIGHUP the server for the changes to take effect, run "pg_ctl reload", +# or execute "SELECT pg_reload_conf()". +# +# Put your actual configuration here +# ---------------------------------- +# +# If you want to allow non-local connections, you need to add more +# "host" records. In that case you will also need to make PostgreSQL +# listen on a non-local interface via the listen_addresses +# configuration parameter, or via the -i or -h command line switches. + + + + +# DO NOT DISABLE! +# If you change this first entry you will need to make sure that the +# database superuser can access the database using some other method. +# Noninteractive access to all databases is required during automatic +# maintenance (custom daily cronjobs, replication, and similar tasks). +# +# Database administrative login by Unix domain socket +#local all postgres peer + +# TYPE DATABASE USER ADDRESS METHOD + +# "local" is for Unix domain socket connections only +local all all trust +# IPv4 local connections: +host all all 127.0.0.1/32 trust +# IPv6 local connections: +host all all ::1/128 md5 +# Allow replication connections from localhost, by a user with the +# replication privilege. +#local replication all peer +#host replication all 127.0.0.1/32 md5 +#host replication all ::1/128 md5 \ No newline at end of file diff --git a/vagrant/files/setup-postgres.sh b/vagrant/files/setup-postgres.sh new file mode 100755 index 0000000000000000000000000000000000000000..5e9f9f54c71da14349f5f60cbdca240d55f6b2fa --- /dev/null +++ b/vagrant/files/setup-postgres.sh @@ -0,0 +1,3 @@ +echo " --- setup-postgres.sh ---" + +createuser vagrant diff --git a/vagrant/files/setup-vagrant.sh b/vagrant/files/setup-vagrant.sh new file mode 100755 index 0000000000000000000000000000000000000000..0d0ff35f4ba280a128d5223bc8d2ac448553ad0b --- /dev/null +++ b/vagrant/files/setup-vagrant.sh @@ -0,0 +1,35 @@ +echo " --- setup-vagrant.sh ---" + +rm -rf ~/jupyter-openbis-extension/venv +rm -rf ~/jupyter-openbis-extension/jupyter_openbis_extension.egg-info +rm -rf ~/jupyter-openbis-extension/.ipynb_checkpoints + +build="http://stage-jenkins.ethz.ch:8090/job/installation-18.06/lastSuccessfulBuild" +path=$(curl -s "$build/api/xml?xpath=//relativePath"|sed -e "s/<relativePath>//"|sed -e "s/<\/relativePath>//") +wget -q $build/artifact/$path +archive=$(basename $path) +tar xvfz $archive +directory=$(echo "$archive" | cut -f 1 -d '.') +cp /files/console.properties $directory +export ADMIN_PASSWORD='password' +$directory/run-console.sh + +sed -i "/jetty.ssl.port=/ s/=.*/=8122/" /home/vagrant/openbis/servers/openBIS-server/jetty/start.d/ssl.ini +sed -i "/host-address =/ s/=.*/= https:\/\/localhost/" /home/vagrant/openbis/servers/datastore_server/etc/service.properties +sed -i "/port =/ s/=.*/= 8123/" /home/vagrant/openbis/servers/datastore_server/etc/service.properties +sed -i "/server-url =/ s/=.*/= \${host-address}:8122/" /home/vagrant/openbis/servers/datastore_server/etc/service.properties + +rm -rf ~/openBIS-installation-* + +mkdir ~/.jupyter +cp /files/jupyter_notebook_config.py ~/.jupyter + +cd jupyter-openbis-extension +pip3 install -e . +export PATH=$PATH:/home/vagrant/.local/bin +pip3 install --upgrade pybis +pip3 install 'tornado==5.1.1' --force-reinstall +jupyter serverextension enable --py jupyter-openbis-extension +jupyter nbextension install --py jupyter-openbis-extension --user --symlink +jupyter nbextension enable jupyter-openbis-extension --user --py +cd diff --git a/vagrant/files/start-services-vagrant.sh b/vagrant/files/start-services-vagrant.sh new file mode 100755 index 0000000000000000000000000000000000000000..9bcd30ff0584281f51a4c098b51a0664a0da9c39 --- /dev/null +++ b/vagrant/files/start-services-vagrant.sh @@ -0,0 +1,8 @@ +echo " --- start-services-vagrant.sh ---" +/home/vagrant/openbis/bin/allup.sh + +screen -S dev -t jupyter -Adm bash -c " + cd ~/jupyter-openbis-extension; + jupyter notebook; + bash; +" diff --git a/vagrant/files/start-services.sh b/vagrant/files/start-services.sh new file mode 100755 index 0000000000000000000000000000000000000000..4fd56076ced3de09010ec899d8129d1b1b503621 --- /dev/null +++ b/vagrant/files/start-services.sh @@ -0,0 +1,2 @@ +echo " --- start-services.sh ---" +runuser -l vagrant bash -c /files/start-services-vagrant.sh diff --git a/vagrant/shared/.gitignore b/vagrant/shared/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..c96a04f008ee21e260b28f7701595ed59e2839e3 --- /dev/null +++ b/vagrant/shared/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file