Netpyne-44 Add mechanism to be able to react on kernel status change from the by aranega · Pull Request #773 · MetaCell/NetPyNE-UI · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 114 additions & 4 deletions webapp/components/NetPyNE.js
103 changes: 103 additions & 0 deletions webapp/components/general/CommandRecorder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { KERNEL_HANDLING } from '../../constants';
import { store } from '../../redux/actiondomainStore'
import { recordCommand, dropLastCommand, dropFromIndex } from '../../redux/actions/actiondomain';
import { execPythonMessage, execPythonMessageWithoutRecording } from './GeppettoJupyterUtils';


const registerKernelListeners = () => {
try {
if(IPython.notebook.kernel == null) {
console.warn("Kernel not initialized. Waiting to register kernel event listeners");
setTimeout(registerKernelListeners, 500);
return;
}
} catch (error) {
console.warn("IPython not initialized. Waiting to register kernel event listeners");
setTimeout(registerKernelListeners, 500);
return
}

const notebook = IPython.notebook;
const handleKernelStatusChange = (event, data) => {
const kernelStatusEvent = new CustomEvent("kernelstatus", {
detail: {
"type": event.type,
...data
},
});
window.dispatchEvent(kernelStatusEvent);
};

const handleExecutionRequest = (event, data) => {
if (data.content.netpyne_ui_triggered) {
return
}
const { kernel, content } = data;
record(kernel.id, content.code);
}

// Kernel lifecycle requests
notebook.events.on('kernel_created.Kernel', handleKernelStatusChange);
notebook.events.on('kernel_reconnecting.Kernel', handleKernelStatusChange);
notebook.events.on('kernel_connected.Kernel', handleKernelStatusChange);
notebook.events.on('kernel_starting.Kernel', handleKernelStatusChange);
notebook.events.on('kernel_restarting.Kernel', handleKernelStatusChange);
notebook.events.on('kernel_autorestarting.Kernel', handleKernelStatusChange);
notebook.events.on('kernel_interrupting.Kernel', handleKernelStatusChange);
notebook.events.on('kernel_disconnected.Kernel', handleKernelStatusChange);
notebook.events.on('kernel_ready.Kernel', handleKernelStatusChange);
notebook.events.on('kernel_killed.Kernel', handleKernelStatusChange);
notebook.events.on('kernel_dead.Kernel', handleKernelStatusChange);

// Execution requests
notebook.events.on('execution_request.Kernel', handleExecutionRequest);
}
registerKernelListeners();


const record = (kernelID, command) => {
store.dispatch(recordCommand(kernelID, command))
}

const TIMEFRAME = 10 * 1000; // 10s
let lastReplayTime = 0

const getCommands = (kernelID) => {
return [
"from jupyter_geppetto import jupyter_geppetto",
"from jupyter_geppetto import utils",
"from netpyne_ui.netpyne_geppetto import netpyne_geppetto",
"netpyne_geppetto.deleteModel({})",
`netpyne_geppetto.loadFromIndexFile("${KERNEL_HANDLING.tmpModelPath}")`,
...store.getState()[kernelID]
]
}

const replayAll = (kernelID, fromRec = false) => {
const currentTimestamp = Date.now();
const commands = getCommands(kernelID);

if (!fromRec && currentTimestamp - lastReplayTime < TIMEFRAME) {
const restartLoop = new CustomEvent("kernelRestartLoop", {
detail: {
"kernel": kernelID,
"state": "looping"
}
});
window.dispatchEvent(restartLoop);
store.dispatch(dropLastCommand(kernelID))
replayAll(kernelID, true)
return
}

const lastCommand = commands.pop() // we drop the last command which is probably the faulty one
const script = commands.join('\n')
console.log("Playing", script)
console.log("Skipping last command", lastCommand)
lastReplayTime = currentTimestamp
execPythonMessageWithoutRecording(script).then(() => {
store.dispatch(dropLastCommand(kernelID))
})
}

export { record, replayAll }
26 changes: 16 additions & 10 deletions webapp/components/general/Dialog.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import React from 'react';
import Button from '@material-ui/core/Button';
import MuiDialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import Typography from '@material-ui/core/Typography';
import DialogTitle from '@material-ui/core/DialogTitle';
import Paper from '@material-ui/core/Paper';
import Box from '@material-ui/core/Box';
import Link from '@material-ui/core/Link';
import Icon from '@material-ui/core/Icon';
import { Button,DialogActions, DialogContent, DialogTitle, DialogContentText } from '@material-ui/core';
import { Typography, Paper, Box, Link, Icon } from '@material-ui/core';
import { withStyles } from '@material-ui/core/styles';
import { secondaryColor, bgLight } from '../../theme';
import logoNetpyne from '../../static/netpyne-logo_white.png';
Expand Down Expand Up @@ -135,18 +128,31 @@ const ContributeContent = withStyles(styles)(({ classes }) => (
</Paper>
));


const titleContentMapping = {
Contribute: <ContributeContent />,
About: <AboutContent />
}

export default function Dialog ({
open,
title,
message,
handleClose,
}) {
const selectMessageContent = () => {
if (title in titleContentMapping) {
return titleContentMapping[title]
}
return <DialogContentText>{message}</DialogContentText>
}
return (
<div>
<MuiDialog fullWidth maxWidth="sm" open={open} onClose={handleClose}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
{title === 'Contribute' ? <ContributeContent /> : <AboutContent />}
{/* {title === 'Contribute' ? <ContributeContent /> : <AboutContent />} */}
{selectMessageContent()}
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary" autoFocus>
Expand Down
31 changes: 26 additions & 5 deletions webapp/components/general/GeppettoJupyterUtils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { record as recordCommand } from './CommandRecorder';


const handle_output = function (data) {
// data is the object passed to the callback from the kernel execution
switch (data.msg_type) {
Expand Down Expand Up @@ -29,9 +32,23 @@ const handle_output = function (data) {
}
};

const execPythonMessage = function (command, callback = handle_output) {
const execPythonMessage = function (command, callback = handle_output, record = true) {
const { kernel } = IPython.notebook;
const messageID = kernel.execute(command, { iopub: { output: callback } }, { silent: false, stop_on_error: true, store_history: true });
if (record) {
recordCommand(kernel.id, command)
}
const messageID = kernel.execute(
command,
{
iopub: { output: callback }
},
{
silent: false,
stop_on_error: true,
store_history: true,
netpyne_ui_triggered: true
});


return new Promise((resolve, reject) => GEPPETTO.on(GEPPETTO.Events.Receive_Python_Message, (data) => {
if (data.data.id == messageID) {
Expand All @@ -40,11 +57,15 @@ const execPythonMessage = function (command, callback = handle_output) {
}));
};

const execPythonMessageWithoutRecording = function (command, callback = handle_output) {
return execPythonMessage(command, callback, false)
}

const addslashes = function (str) {
return (str + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0');
}

const evalPythonMessage = function (command, parameters, parse = true) {
const evalPythonMessage = function (command, parameters, parse = true, record = true) {
let parametersString = '';
if (parameters) {
if (parameters.length > 0) {
Expand All @@ -58,7 +79,7 @@ const evalPythonMessage = function (command, parameters, parse = true) {
if (parse) {
finalCommand = `utils.convertToJS(${finalCommand})`;
}
return execPythonMessage(finalCommand, handle_output);
return execPythonMessage(finalCommand, handle_output, record);
};

export { execPythonMessage, evalPythonMessage };
export { execPythonMessage, evalPythonMessage, execPythonMessageWithoutRecording };
5 changes: 4 additions & 1 deletion webapp/components/topbar/dialogs/ActionDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,13 @@ class ActionDialog extends React.Component {
}
}
}
this.setState({ hide: true });
if (this.props.onAction) {
this.props.onAction();
}
this.setState({ hide: true });
if (this.props.onRequestClose) {
this.props.onRequestClose();
}
};

clearErrorDialogBox () {
Expand Down
3 changes: 3 additions & 0 deletions webapp/components/topbar/dialogs/ActionValidationDialog.js
Loading