Skip to content

Commit

Permalink
read notebook location from plugin metadata; and some linting
Browse files Browse the repository at this point in the history
  • Loading branch information
emlys committed Oct 18, 2024
1 parent 073e9e8 commit 40954a8
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 42 deletions.
79 changes: 44 additions & 35 deletions workbench/src/main/createPythonFlaskProcess.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,43 @@ export function setupServerProcessHandlers(subprocess) {
pidToSubprocess[subprocess.pid] = subprocess;
}

export async function createJupyterProcess(notebookDir, _port = undefined) {
/**
* Find out if the Jupyter server is online, waiting until it is.
* @param {number} port - the port where the jupyter server is running
* @param {number} i - the number or previous tries
* @param {number} retries - number of recursive calls this function is allowed.
* @returns { Promise } resolves text indicating success.
*/
export async function getJupyterIsReady(port, { i = 0, retries = 41 } = {}) {
try {
logger.debug(`${HOSTNAME}:${port}/?token=${process.env.JUPYTER_TOKEN}`);
await fetch(`${HOSTNAME}:${port}`, {
method: 'get',
});
} catch (error) {
if (error.code === 'ECONNREFUSED') {
while (i < retries) {
i++;
// Try every X ms, usually takes a couple seconds to startup.
await new Promise((resolve) => setTimeout(resolve, 300));
logger.debug(`retry # ${i}`);
return getJupyterIsReady(port, { i: i, retries: retries });
}
logger.error(`Not able to connect to server after ${retries} tries.`);
}
logger.error(error);
throw error;
}
}

/**
* Launch the voila server running a jupyter notebook for a given plugin.
* @param {string} pluginID - id of the plugin
* @param {number} _port - port to launch the voila server. If undefined,
* an available port will be chosen.
* @returns {Array} [subprocess, port]
*/
export async function createJupyterProcess(pluginID, _port = undefined) {
let port = _port;
if (port === undefined) {
port = await getFreePort();
Expand All @@ -90,20 +126,21 @@ export async function createJupyterProcess(notebookDir, _port = undefined) {
logger.debug('creating voila server process');

const mamba = settingsStore.get('mamba');
const modelEnvPath = settingsStore.get('plugins.schistosomiasis.env');
const modelEnvPath = settingsStore.get(`plugins.${pluginID}.env`);
const relativeNotebookPath = settingsStore.get(`plugins.${pluginID}.notebook_path`);

// Extract the location of the installed plugin from `pip show`
// The notebook should be available there as package data
const pipShow = execSync(
`mamba run --prefix "${modelEnvPath}" pip show schistosomiasis`,
`mamba run --prefix "${modelEnvPath}" pip show ${pluginID}`,
{ windowsHide: true }
).toString();
logger.info(pipShow);
const pluginLocation = pipShow.match(/Location: (.+)/)[1];
logger.info('location', pluginLocation);
const notebookPath = `${pluginLocation}/notebooks/ipyleaflet.ipynb`;
const notebookPath = `${pluginLocation}/${relativeNotebookPath}`;

const args = [
'run', '--prefix', `"${modelEnvPath}"`,
'voila', notebookPath, '--debug', '--no-browser', '--port', port
'voila', notebookPath, '--debug', '--no-browser', '--port', port,
];
logger.debug('spawning command:', mamba, args);

Expand Down Expand Up @@ -203,34 +240,6 @@ export async function createPluginServerProcess(modelName, _port = undefined) {
return pythonServerProcess.pid;
}

/** Find out if the Jupyter server is online, waiting until it is.
*
* @param {number} i - the number or previous tries
* @param {number} retries - number of recursive calls this function is allowed.
* @returns { Promise } resolves text indicating success.
*/
export async function getJupyterIsReady(port = undefined, { i = 0, retries = 41 } = {}) {
try {
logger.debug(`${HOSTNAME}:${port}/?token=${process.env.JUPYTER_TOKEN}`)
await fetch(`${HOSTNAME}:${port}`, {
method: 'get',
});
} catch (error) {
if (error.code === 'ECONNREFUSED') {
while (i < retries) {
i++;
// Try every X ms, usually takes a couple seconds to startup.
await new Promise((resolve) => setTimeout(resolve, 300));
logger.debug(`retry # ${i}`);
return getJupyterIsReady(port, { i: i, retries: retries });
}
logger.error(`Not able to connect to server after ${retries} tries.`);
}
logger.error(error);
throw error;
}
}

/**
* Kill the process running the Flask app
* @param {number} pid - process ID of the child process to shut down
Expand Down
8 changes: 3 additions & 5 deletions workbench/src/main/setupJupyter.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,17 @@ function createWindow(parentWindow, isDevMode) {
}

function serveWorkspace(dir) {
console.log('SERVING', dir)
console.log('SERVING', dir);
const app = connect();
app.use(serveStatic(dir));
return http.createServer(app).listen(8080);
}

export default function setupJupyter(parentWindow, isDevMode) {
ipcMain.on(
ipcMainChannels.OPEN_JUPYTER, async (event, filepath) => {
ipcMainChannels.OPEN_JUPYTER, async (event, filepath, pluginID) => {
const httpServer = serveWorkspace(path.dirname(filepath));
let labDir = `${process.resourcesPath}/notebooks`;
if (isDevMode) { labDir = 'resources/notebooks'; }
const [subprocess, port] = await createJupyterProcess(labDir);
const [subprocess, port] = await createJupyterProcess(pluginID);
const child = createWindow(parentWindow, isDevMode);
child.loadURL(`http://localhost:${port}/?token=${process.env.JUPYTER_TOKEN}`);
child.on('close', async () => {
Expand Down
4 changes: 2 additions & 2 deletions workbench/src/renderer/components/InvestTab/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function handleOpenWorkspace(logfile) {
}

function handleViewResults(logfile) {
console.log('View Results')
console.log('View Results');
ipcRenderer.send(ipcMainChannels.OPEN_JUPYTER, logfile);
}

Expand Down Expand Up @@ -278,7 +278,7 @@ class InvestTab extends React.Component {
<ModelStatusAlert
status={status}
handleOpenWorkspace={() => handleOpenWorkspace(logfile)}
handleViewResults={() => handleViewResults(logfile)}
handleViewResults={() => handleViewResults(logfile, modelSpec.model_id)}
terminateInvestProcess={this.terminateInvestProcess}
/>
)
Expand Down

0 comments on commit 40954a8

Please sign in to comment.