UNPKG

@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
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;