@lifeomic/jupyterlab-toolbrowser
Version:
A JupyterLab extension to display, filter and allow download of notebooks from the Precision Health Cloud Tool Registry.
229 lines (228 loc) • 9.64 kB
JavaScript
import { ICommandPalette, MainAreaWidget } from '@jupyterlab/apputils';
import { Widget } from '@lumino/widgets';
import { ContentsManager } from '@jupyterlab/services';
import { IMainMenu } from '@jupyterlab/mainmenu';
let data = new Array();
const content = new Widget();
const downloadedTools = new Array();
let filters = '';
const win = typeof window !== 'undefined' ? window : (global || {}).window;
const fetchDownloadRequest = async (event) => {
const accountStore = JSON.parse(win.localStorage.getItem('@@lifeomic/store/account'));
const account = accountStore['activeAccount'];
const tokenStore = JSON.parse(win.localStorage.getItem('@@lifeomic/store/auth'));
const token = tokenStore['accessToken'];
const host = win.location.host;
if (event.currentTarget.id) {
const toolId = event.currentTarget.id;
const response = await fetch(`https://${host}/api/v1/trs/files/${toolId}/download`, {
headers: {
Authorization: `Bearer ${token}`,
'LifeOmic-Account': account,
},
});
const downloadResponse = (await response.json());
const contents = new ContentsManager();
// We don't want to let the tool download overwrite an existing file so
// we check to see if the file already exists
try {
await contents.get(downloadResponse.fileName);
// no exception thrown, so file exists, don't allow download
alert(`File ${downloadResponse.fileName} already exists`);
return;
}
catch (err) {
// exception was thrown so file doesn't exist, allow the download
console.log(`Ready to download ${downloadResponse.fileName}`);
}
let toolFileContent = '';
const handleReader = (reader) => {
const handleContent = ({ value, done }) => {
const text = new TextDecoder('utf-8').decode(value);
toolFileContent += text;
// if we're not done read the next chunk,
// if we're finished add the file to the
// jupyter contents manager
if (!done) {
reader.read().then(handleContent);
}
else {
const model = {
type: 'file',
content: toolFileContent,
format: 'text',
name: downloadResponse.fileName,
path: downloadResponse.fileName,
};
contents.save(`${downloadResponse.fileName}`, model);
downloadedTools.push({
id: toolId,
fileName: downloadResponse.fileName,
});
const table = generateTable();
content.node.appendChild(table);
}
};
// kick off the initial read
reader.read().then(handleContent);
};
const toolFile = await fetch(downloadResponse.downloadUrl);
const reader = toolFile.body
.getReader();
handleReader(reader);
}
};
const initializeData = async () => {
const accountStore = JSON.parse(win.localStorage.getItem('@@lifeomic/store/account'));
const account = accountStore['activeAccount'];
const tokenStore = JSON.parse(win.localStorage.getItem('@@lifeomic/store/auth'));
const token = tokenStore['accessToken'];
const host = win.location.host;
const response = await fetch(`https://${host}/api/v1/trs/v2/tools?toolClass=Notebook&pageSize=1000`, {
headers: {
Authorization: `Bearer ${token}`,
'LifeOmic-Account': account,
},
});
data = (await response.json());
};
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const onSearch = () => {
const filterValues = document.getElementById('filterEntry');
filters = filterValues.value;
const table = generateTable();
content.node.appendChild(table);
};
const generateTable = () => {
const oldTable = document.getElementById('toolTable');
if (oldTable) {
oldTable.remove();
}
const table = document.createElement('table');
table.id = 'toolTable';
const tr = document.createElement('tr');
const nameHeader = document.createElement('th');
nameHeader.style.border = '1px solid black';
const descriptionHeader = document.createElement('th');
descriptionHeader.style.border = '1px solid black';
const versionHeader = document.createElement('th');
versionHeader.style.border = '1px solid black';
const downloadHeader = document.createElement('th');
downloadHeader.style.border = '1px solid black';
const name = document.createTextNode('Name');
const description = document.createTextNode('Description');
const version = document.createTextNode('Version');
const download = document.createTextNode('');
nameHeader.appendChild(name);
descriptionHeader.appendChild(description);
versionHeader.appendChild(version);
downloadHeader.appendChild(download);
tr.appendChild(nameHeader);
tr.appendChild(descriptionHeader);
tr.appendChild(versionHeader);
tr.appendChild(downloadHeader);
table.appendChild(tr);
const filterArray = filters ? filters.split(',') : new Array();
if (data) {
for (let i = 0; i < data.length; i++) {
const tool = data[i];
if (filterArray.length > 0 &&
tool.labels &&
!tool.labels.some((r) => filterArray.includes(r))) {
continue;
}
const tr = document.createElement('tr');
const name = document.createElement('td');
name.style.border = '1px solid black';
const description = document.createElement('td');
description.style.border = '1px solid black';
const version = document.createElement('td');
version.style.border = '1px solid black';
const download = document.createElement('td');
download.style.border = '1px solid black';
const nameText = document.createTextNode(tool.name);
const descriptionText = document.createTextNode(tool.description);
const versionText = document.createTextNode(tool.meta_version);
name.appendChild(nameText);
description.appendChild(descriptionText);
version.appendChild(versionText);
tr.appendChild(name);
tr.appendChild(description);
tr.appendChild(version);
const downloads = downloadedTools.filter((d) => d.id === tool.id);
if (downloads.length > 0) {
const downloadText = document.createTextNode(`Downloaded: ${downloads[0].fileName}`);
download.style.fontWeight = 'bold';
download.appendChild(downloadText);
}
else {
const button = document.createElement('button');
button.id = tool.id;
button.onclick = fetchDownloadRequest;
button.style.border = '0';
button.style.backgroundColor = 'transparent';
button.style.textDecoration = 'underline';
button.innerText = 'download';
download.appendChild(button);
}
tr.appendChild(download);
table.appendChild(tr);
}
}
table.style.border = '1px solid black';
table.style.marginTop = '5px';
table.style.marginLeft = '30px';
return table;
};
/**
* Initialization data for the toolbrowser extension.
*/
const extension = {
id: 'jupyterlab_toolregistry',
autoStart: true,
requires: [ICommandPalette, IMainMenu],
activate: async (app, palette, mainMenu) => {
// Conntent widget inside of a MainAreaWidget
const widget = new MainAreaWidget({ content });
widget.id = 'toolbrowser-jupyterlab';
widget.title.label = 'Tool Browser';
widget.title.closable = true;
// Add in search box and button
const filter = document.createElement('INPUT');
filter.id = 'filterEntry';
filter.setAttribute('type', 'text');
filter.style.marginTop = '10px';
filter.style.marginLeft = '30px';
filter.style.marginRight = '5px';
content.node.appendChild(filter);
const searchButton = document.createElement('button');
searchButton.onclick = onSearch;
searchButton.innerText = 'Search';
content.node.appendChild(searchButton);
// fetch data from TRS
await initializeData();
// setup initial table
const table = generateTable();
content.node.appendChild(table);
const command = 'tools:list';
app.commands.addCommand(command, {
label: 'Fetch Tools From PHC Registry',
execute: () => {
if (!widget.isAttached) {
// Attach the widget to the main work area if it's not there
app.shell.add(widget, 'main');
}
// Activate the widget
app.shell.activateById(widget.id);
},
});
// Add the command to the palette.
palette.addItem({ command, category: 'File Operations' });
mainMenu.fileMenu.addGroup([
{
command: 'tools:list',
},
], 40 /* rank */);
},
};
export default extension;