Newer
Older
from notebook.utils import url_path_join
from notebook.base.handlers import IPythonHandler
from pybis import Openbis
import numpy as np
Swen Vermeul
committed
from urllib.parse import unquote
import yaml
openbis_connections = {}
def _jupyter_server_extension_paths():
return [{'module': 'jupyter-openbis-extension.server'}]
Swen Vermeul
committed
def _load_configuration(paths, filename='openbis-connections.yaml'):
if paths is None:
paths = []
home = os.path.expanduser("~")
paths.append(os.path.join(home, '.jupyter'))
# look in all config file paths of jupyter
# for openbis connection files and load them
connections = []
for path in paths:
abs_filename = os.path.join(path, filename)
if os.path.isfile(abs_filename):
with open(abs_filename, 'r') as stream:
try:
config = yaml.safe_load(stream)
for connection in config['connections']:
connections.append(connection)
Swen Vermeul
committed
except yaml.YAMLexception as e:
print(e)
Swen Vermeul
committed
return None
return connections
def load_jupyter_server_extension(nb_server_app):
"""Call when the extension is loaded.
:param nb_server_app: Handle to the Notebook webserver instance.
"""
# load the configuration file
Swen Vermeul
committed
# and register the openBIS connections.
# If username and password is available, try to connect to the server
connections = _load_configuration(
paths = nb_server_app.config_file_paths,
filename = 'openbis-connections.yaml'
)
Swen Vermeul
committed
for connection_info in connections:
conn = register_connection(connection_info)
print("Registered: {}".format(conn.url))
if conn.username and conn.password:
try:
conn.login()
print("Successfully connected to: {}".format(conn.url))
except ValueError:
print("Incorrect username or password for: {}".format(conn.url))
except Exception:
print("Cannot establish connection to: {}".format(conn.url))
# Add URL handlers to our web_app
# see Tornado documentation: https://www.tornadoweb.org
web_app = nb_server_app.web_app
host_pattern = '.*$'
base_url = web_app.settings['base_url']
Swen Vermeul
committed
# get the file list
web_app.add_handlers(
host_pattern,
[(url_path_join( base_url, '/general/filelist'),
FileListHandler
)]
)
web_app.add_handlers(
host_pattern,
[(url_path_join(
base_url,
'/openbis/dataset/(?P<connection_name>.*)?/(?P<permId>.*)?/(?P<downloadPath>.*)'),
DataSetDownloadHandler
)]
Swen Vermeul
committed
)
# DataSet upload
web_app.add_handlers( host_pattern, [(
Swen Vermeul
committed
url_path_join( base_url, '/openbis/dataset/(?P<connection_name>.*)' ),
DataSetUploadHandler
Swen Vermeul
committed
)
# DataSet-Types
web_app.add_handlers(
host_pattern,
[(
Swen Vermeul
committed
url_path_join( base_url, '/openbis/datasetTypes/(?P<connection_name>.*)'
Swen Vermeul
committed
),
DataSetTypesHandler
)]
)
# DataSets for Sample identifier/permId
[(
url_path_join(
base_url,
'/openbis/sample/(?P<connection_name>.*)?/(?P<permId>.*)'
),
Swen Vermeul
committed
)
# OpenBIS connections
web_app.add_handlers(
host_pattern,
[(
url_path_join(
base_url,
'/openbis/conns'
),
OpenBISConnections
)]
)
# Modify / reconnect to a connection
[(
Swen Vermeul
committed
url_path_join(
base_url,
'/openbis/conn/(?P<connection_name>.*)'
),
OpenBISConnectionHandler
)]
)
print("pybis loaded: {}".format(Openbis))
def register_connection(connection_info):
Swen Vermeul
committed
Swen Vermeul
committed
name = connection_info.get('name'),
url = connection_info.get('url'),
verify_certificates = connection_info.get('verify_certificates', False),
username = connection_info.get('username'),
password = connection_info.get('password'),
status = 'not connected',
Swen Vermeul
committed
return conn
class OpenBISConnection:
"""register an openBIS connection
"""
def __init__(self, **kwargs):
Swen Vermeul
committed
for needed_key in ['name', 'url']:
if needed_key not in kwargs:
raise KeyError("{} is missing".format(needed_key))
for key in kwargs:
setattr(self, key, kwargs[key])
Swen Vermeul
committed
openbis = Openbis(
url = self.url,
verify_certificates = self.verify_certificates
)
self.openbis = openbis
self.status = "not connected"
def is_session_active(self):
return self.openbis.is_session_active()
Swen Vermeul
committed
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
def check_status(self):
if self.openbis.is_session_active():
self.status = "connected"
else:
self.status = "not connected"
def login(self, username=None, password=None):
if username is None:
username=self.username
if password is None:
password=self.password
self.openbis.login(
username = username,
password = password
)
# store username and password in memory
self.username = username
self.password = password
self.status = 'connected'
def get_info(self):
return {
'name' : self.name,
'url' : self.url,
'status' : self.status,
'username': self.username,
'password': self.password,
}
class OpenBISConnections(IPythonHandler):
def post(self):
"""create a new connection
:return: a new connection object
"""
data = self.get_json_body()
conn = register_connection(data)
if conn.username and conn.password:
try:
conn.login()
except Exception:
pass
self.get()
return
def get(self):
"""returns all available openBIS connections
"""
Swen Vermeul
committed
connections= []
for conn in openbis_connections.values():
Swen Vermeul
committed
conn.check_status()
connections.append(conn.get_info())
self.write({
'status' : 200,
'connections': connections,
'cwd' : os.getcwd()
})
return
class OpenBISConnectionHandler(IPythonHandler):
"""Handle the requests to /openbis/conn
"""
def put(self, connection_name):
"""reconnect to a current connection
:return: an updated connection object
"""
data = self.get_json_body()
Swen Vermeul
committed
try:
conn = openbis_connections[connection_name]
except KeyError:
self.set_status(404)
self.write({
"reason" : 'No such connection: {}'.format(data)
Swen Vermeul
committed
return
try:
conn.login(data.get('username'), data.get('password'))
except ConnectionError:
self.set_status(500)
self.write({
"reason": "Could not establish connection to {}".format(connection_name)
})
return
except ValueError:
self.set_status(401)
self.write({
"reason": "Incorrect username or password for {}".format(connection_name)
})
return
'status' : 200,
Swen Vermeul
committed
'connection': conn.get_info(),
'cwd' : os.getcwd()
})
def get(self, connection_name):
"""returns information about a connection name
"""
try:
conn = openbis_connections[connection_name]
except KeyError:
self.set_status(404)
self.write({
"reason" : 'No such connection: {}'.format(data)
})
return
conn.check_status()
self.write({
'status' : 200,
'connection': conn.get_info(),
'cwd' : os.getcwd()
Swen Vermeul
committed
class SampleHandler(IPythonHandler):
"""Handle the requests for /openbis/sample/connection/permId"""
def get_datasets(self, conn, permId):
if not conn.is_session_active():
try:
Swen Vermeul
committed
conn.login()
except Exception as exc:
self.write({
"reason" : 'connection to {} could not be established: {}'.format(conn.name, exc)
})
sample = None
try:
sample = conn.openbis.get_sample(permId)
except Exception as exc:
self.set_status(404)
self.write({
"reason" : 'No such sample: {}'.format(permId)
})
if sample is None:
return
datasets.replace({np.nan:None}, inplace=True) # replace NaN with None, otherwise we cannot convert it correctly
return datasets.to_dict(orient='records') # is too stupid to handle NaN
def get(self, **params):
"""Handle a request to /openbis/sample/connection_name/permId
download the data and return a message
"""
try:
conn = openbis_connections[params['connection_name']]
except KeyError:
self.write({
"reason" : 'connection {} was not found'.format(
params['connection_name']
)
})
Swen Vermeul
committed
return
datasets = self.get_datasets(conn, params['permId'])
if datasets is not None:
self.set_status(200)
self.write({
"dataSets": datasets
})
Swen Vermeul
committed
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
class FileListHandler(IPythonHandler):
def get(self, **params):
"""
Returns the file list of the current working directory
:param params:
:return: dictionary of files, key is the fully qualified name,
value is the relative name (for display)
"""
cwd = os.getcwd()
files = {}
for (dirpath, dirnames, filenames) in os.walk(cwd):
if filenames:
for filename in filenames:
# ignore hidden files
if filename.startswith('.'):
continue
# ignore hidden folders
if os.path.relpath(dirpath) != '.' \
and os.path.relpath(dirpath).startswith('.'):
continue
fqn = os.path.join(dirpath, filename)
files[fqn] = os.path.relpath(fqn, cwd)
self.set_status(200)
self.write({
"files": files
})
class DataSetDownloadHandler(IPythonHandler):
"""Handle the requests for /openbis/dataset/connection/permId"""
def download_data(self, conn, permId, downloadPath=None):
Swen Vermeul
committed
conn.login()
Swen Vermeul
committed
self.set_status(500)
self.write({
"reason" : 'connection to {} could not be established: {}'.format(conn.name, exc)
})
return
try:
dataset = conn.openbis.get_dataset(permId)
except Exception as exc:
self.set_status(404)
self.write({
"reason" : 'No such dataSet found: {}'.format(permId)
})
return
# dataset was found, download the data to the disk
try:
destination = dataset.download(destination=downloadPath)
Swen Vermeul
committed
self.set_status(500)
self.write({
"reason": 'Data for DataSet {} could not be downloaded: {}'.format(permId, exc)
})
path = os.path.join(downloadPath, dataset.permId)
Swen Vermeul
committed
'url' : conn.url,
'path' : path,
'dataStore' : dataset.dataStore,
'location' : dataset.physicalData.location,
'size' : dataset.physicalData.size,
'statusText': 'Data for DataSet {} was successfully downloaded to: {}.'.format(dataset.permId, path)
})
def get(self, **params):
"""Handle a request to /openbis/dataset/connection_name/permId
download the data and return a message
"""
try:
conn = openbis_connections[params['connection_name']]
except KeyError:
Swen Vermeul
committed
self.set_status(404)
self.write({
"reason":'connection {} was not found'.format(params['connection_name'])
})
results = self.download_data(conn=conn, permId=params['permId'], downloadPath=params['downloadPath'])
Swen Vermeul
committed
class DataSetTypesHandler(IPythonHandler):
def get(self, **params):
"""Handle a request to /openbis/datasetTypes/connection_name
"""
try:
conn = openbis_connections[params['connection_name']]
except KeyError:
self.set_status(404)
self.write({
"reason":'connection {} was not found'.format(params['connection_name'])
})
return
try:
dataset_types = conn.openbis.get_dataset_types()
dts = dataset_types.df.to_dict(orient='records')
# get property assignments for every dataset-type
# and add it to the dataset collection
for dt in dts:
dataset_type = conn.openbis.get_dataset_type(dt['code'])
Swen Vermeul
committed
pa = dataset_type.get_propertyAssignments(including_vocabulary=True)
pa_dicts = pa.to_dict(orient='records')
for pa_dict in pa_dicts:
if pa_dict['dataType'] == 'CONTROLLEDVOCABULARY':
terms = conn.openbis.get_terms(pa_dict['vocabulary']['code'])
pa_dict['terms'] = terms.df[['code','label','description','official','ordinal']].to_dict(orient='records')
dt['propertyAssignments'] = pa_dicts
Swen Vermeul
committed
self.write({
"dataSetTypes": dts
})
return
except Exception as e:
Swen Vermeul
committed
print(e)
Swen Vermeul
committed
self.set_status(500)
self.write({
"reason":'Could not fetch dataset-types: {}'.format(e)
})
return
class DataSetUploadHandler(IPythonHandler):
Swen Vermeul
committed
"""Handle the POST requests for /openbis/dataset/connection_name"""
def upload_data(self, conn, data):
if not conn.is_session_active():
try:
Swen Vermeul
committed
conn.login()
Swen Vermeul
committed
except Exception as e:
print(e)
self.set_status(500)
self.write({
Swen Vermeul
committed
"reason": 'connection to {} could not be established: {}'.format(conn.name, e)
})
return
Swen Vermeul
committed
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
errors = []
sample = None
experiment = None
if (data.get('sampleIdentifier')):
try:
sample = conn.openbis.get_sample(data.get('sampleIdentifier'))
except Exception as e:
print(e)
errors.append(
{"sampleIdentifier" : 'No such sample: {}'.format(data.get('sampleIdentifier')) }
)
else:
errors.append(
{"sampleIdentifier": "please provide a sample identifier"}
)
print("--------------_HERE_-----------1")
if (data.get('experimentIdentifier')):
try:
experiment = conn.openbis.get_experiment(data.get('experimentIdentifier'))
except Exception as e:
print(e)
errors.append(
{"experimentIdentifier" : 'No such experiment: {}'.format(data.get('experimentIdentifier')) }
)
print("--------------_HERE_-----------2")
filenames = []
for filename in data.get('files'):
filename = unquote(filename)
Swen Vermeul
committed
if os.path.isfile(filename):
filenames.append(filename)
else:
errors.append({
"file": "File not found: {}".format(filename)
})
print("--------------_HERE_-----------3")
Swen Vermeul
committed
try:
dataset = conn.openbis.new_dataset(
type = data.get('type'),
sample = sample,
experiment = experiment,
files = filenames,
)
except Exception as e:
print(e)
errors.append({
"create": 'Error while creating the dataset: {}'.format(e)
})
Swen Vermeul
committed
print("--------------_HERE_-----------4")
# try to set the properties
if (data.get('props')):
props = data.get('props')
Swen Vermeul
committed
for prop, value in props.items():
try:
setattr(dataset.props, prop.lower(), value)
Swen Vermeul
committed
except Exception as e:
errors.append({
"prop."+prop : str(e)
Swen Vermeul
committed
})
print("--------------_HERE_-----------5")
# write errors back if already occured
if errors:
self.set_status(500)
self.write({ "errors": errors })
return
Swen Vermeul
committed
print("--------------_HERE_-----------6")
try:
Swen Vermeul
committed
dataset.save()
except Exception as e:
errors.append({
"save": 'Error while saving the dataset: {}'.format(e)
})
print("--------------_HERE_-----------7")
# write errors back if they occured
if errors:
self.set_status(500)
self.write({ "errors": errors })
else:
# ...or return a success message
self.write({
Swen Vermeul
committed
'status': 200,
'statusText': 'Jupyter Notebook was successfully uploaded to: {} with permId: {}'.format(conn.name, dataset.permId)
})
Swen Vermeul
committed
print("--------------_HERE_-----------8")
def post(self, **params):
"""Handle a request to /openbis/dataset/connection_name/permId
download the data and return a message
"""
try:
conn = openbis_connections[params['connection_name']]
except KeyError:
self.write({
"reason": 'connection {} was not found'.format(params['connection_name'])
})
return
data = self.get_json_body()
Swen Vermeul
committed
print("Received DATA")
print(data)
self.upload_data(conn=conn,data=data)