Skip to content

Commit 075d397

Browse files
Merge branch 'codefresh/tests' of https://github.com/MetaCell/NetPyNE-UI into codefresh/tests
2 parents f80b3ef + 255759d commit 075d397

32 files changed

Lines changed: 2740 additions & 1248 deletions

Dockerfile

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
FROM node:14 as jsbuild
22

33
ENV FOLDER=netpyne
4-
RUN echo "no-cache 2023-7-14"
54

65
WORKDIR $FOLDER/webapp
76
COPY webapp/package.json .
@@ -12,10 +11,6 @@ RUN yarn install --network-timeout 1000000000
1211
COPY webapp .
1312
RUN yarn build-dev
1413

15-
RUN rm -Rf node_modules/*
16-
17-
18-
1914
###
2015
FROM jupyter/base-notebook:hub-1.5.0
2116
ENV NB_UID=jovyan

k8s/cf_values.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ hub:
1414
c.Spawner.notebook_dir = '/opt/workspace'
1515
c.Spawner.default_url = '/geppetto'
1616
spawner: >-
17-
c.Spawner.args = ["--library=netpyne_ui"]
17+
c.Spawner.args = ["--library=netpyne_ui", "--NotebookApp.default_url=/geppetto"]
1818
singleuser:
1919
storage:
2020
type: none

netpyne_ui/netpyne_geppetto.py

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,40 @@
4747
os.chdir(constants.NETPYNE_WORKDIR_PATH)
4848

4949

50+
class NetpyneValidationError(Exception):
51+
...
52+
53+
54+
def deepcopy_wout_empty(d, memo=None):
55+
def is_empty(x):
56+
return x == '' or x == [] or x == () or x == set() or x == {} or x is None
57+
58+
# if is_empty(d):
59+
# return None
60+
memo = {} if memo is None else memo
61+
if id(d) in memo:
62+
return memo[id(d)]
63+
if isinstance(d, dict):
64+
cpy = {}
65+
for k, v in d.items():
66+
v_cpy = deepcopy_wout_empty(v, memo=memo)
67+
if is_empty(v_cpy):
68+
continue
69+
cpy[k] = memo.setdefault(id(v), v_cpy)
70+
return cpy
71+
elif isinstance(d, (list, set, tuple)):
72+
return d.__class__(memo.setdefault(id(v), deepcopy_wout_empty(v, memo=memo)) for v in d if not is_empty(v))
73+
elif hasattr(d, '__dict__'):
74+
cpy = d.__new__(d.__class__) # We skip the initializer, in case it needs some arguments
75+
for k, v in d.__dict__.items():
76+
v_cpy = deepcopy_wout_empty(v, memo=memo)
77+
# if is_empty(v_cpy): #
78+
# continue
79+
cpy.__dict__[k] = memo.setdefault(id(v), v_cpy)
80+
return cpy
81+
return d
82+
83+
5084
class NetPyNEGeppetto:
5185

5286
def __init__(self):
@@ -185,6 +219,11 @@ def instantiateNetPyNEModelInGeppetto(self, args):
185219
self.geppetto_model = self.model_interpreter.getGeppettoModel(netpyne_model)
186220

187221
return json.loads(GeppettoModelSerializer.serialize(self.geppetto_model))
222+
except NetpyneValidationError as e:
223+
message = ("Error while validating the NetPyNE model before instantiation.\n"
224+
"One or more components in your model have issues, see details below:")
225+
logging.exception(message)
226+
return utils.getJSONError(message, '\n'.join(e.args))
188227
except Exception as e:
189228
message = "Error while instantiating the NetPyNE model"
190229
logging.exception(message)
@@ -256,6 +295,26 @@ def simulate_single_model(self, experiment: model.Experiment = None, use_prev_in
256295
response = json.loads(GeppettoModelSerializer.serialize(self.geppetto_model))
257296
return response
258297

298+
299+
def validate_netParams(self):
300+
cpy = deepcopy_wout_empty(self.netParams)
301+
_, failed = sim.validator.validateNetParams(cpy)
302+
if failed:
303+
message = ""
304+
components_error = {}
305+
for entry in failed:
306+
components_error.setdefault(entry.component, []).append((entry.keyPath, entry.summary))
307+
for component, details in components_error.items():
308+
message = message + f"* Error validating {component}\n"
309+
for (keyPath, summary) in details:
310+
path = ' -> '.join(f"{key}" for key in keyPath)
311+
message = message + f" Error in {path}\n"
312+
for line in summary:
313+
message = message + f" {line}\n"
314+
message = message + "\n"
315+
raise NetpyneValidationError(message)
316+
317+
259318
def simulateNetPyNEModelInGeppetto(self, args):
260319
""" Starts simulation of the currently loaded NetPyNe model.
261320
@@ -270,8 +329,9 @@ def simulateNetPyNEModelInGeppetto(self, args):
270329
allTrials = args.get('allTrials', True)
271330
use_prev_inst = args.get('usePrevInst', False)
272331
sim_id = args.get('simId', 0)
273-
274332
try:
333+
self.validate_netParams()
334+
275335
experiment = experiments.get_current()
276336
if experiment:
277337
if self.experiments.any_in_state([model.ExperimentState.PENDING, model.ExperimentState.SIMULATING]):
@@ -300,6 +360,11 @@ def simulateNetPyNEModelInGeppetto(self, args):
300360

301361
else:
302362
return self.simulate_single_model(use_prev_inst=use_prev_inst)
363+
except NetpyneValidationError as e:
364+
message = (f"Error while validating the NetPyNE model before simulation {sim_id}.\n"
365+
"One or more components in your model have issues, see details below:")
366+
logging.exception(message)
367+
return utils.getJSONError(message, '\n'.join(e.args))
303368

304369
except Exception as e :
305370
message = f"Error while simulating the NetPyNE model: {e}. SimulationId {sim_id}"
@@ -740,6 +805,7 @@ def deleteModel(self, modelParams):
740805
return utils.getJSONReply()
741806

742807
def instantiateNetPyNEModel(self):
808+
self.validate_netParams()
743809
with redirect_stdout(sys.__stdout__):
744810
saveData = sim.allSimData if hasattr(sim, 'allSimData') and 'spkt' in sim.allSimData.keys() and len(
745811
sim.allSimData['spkt']) > 0 else False
@@ -774,7 +840,8 @@ def doIhaveInstOrSimData(self):
774840
return {'haveInstance': out[0], 'haveSimData': out[1]}
775841

776842
def rename(self, path, oldValue, newValue):
777-
command = 'sim.rename(self.' + path + ',"' + oldValue + '","' + newValue + '")'
843+
# command = 'sim.rename(self.' + path + ',"' + oldValue + '","' + newValue + '")'
844+
command = f'sim.rename(self.{path}, {oldValue!r}, {newValue!r})'
778845
logging.debug('renaming ' + command)
779846

780847
eval(command)
@@ -912,11 +979,15 @@ def getAvailableCellModels(self):
912979
cell_models.add(cm)
913980
return list(cell_models)
914981

915-
def getAvailableCellTypes(self):
916-
cell_types = set([])
917-
for p in self.netParams.cellParams:
918-
cell_types.add(p)
919-
return list(cell_types)
982+
def getAvailableCellModels(self):
983+
return ["", "VecStim", "NetStim", "IntFire1"]
984+
985+
def getAvailableStimulationDistribution(self):
986+
return ["normal", "uniform"]
987+
988+
def getAvailableStimulationPattern(self):
989+
# self.netParams.popParams[name]['spikePattern'] = {}
990+
return ["", "rhythmic", "evoked", "poisson", "gauss"]
920991

921992
def getAvailableSections(self):
922993
sections = {}

utilities/install.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ def main(netpyne_branch, workspace_branch, geppetto_branch=None, skipNpm=False,
171171
_ = config['NotebookApp']['tornado_settings']
172172
except KeyError:
173173
config['NotebookApp']['tornado_settings'] = {}
174+
config['NotebookApp']['tornado_settings']['headers'] = { 'Content-Security-Policy': "frame-ancestors 'self' http: https: http://localhost:8081 *" }
174175
config['NotebookApp']['tornado_settings']['gzip'] = True
175176
f.seek(0)
176177
json.dump(config, f, indent=4, sort_keys=True)

webapp/Main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ require('./css/flexlayout.less');
3434
require('./css/tree.less');
3535

3636
ReactDOM.render(
37-
<div>
37+
<div id="iframefix">
3838
<MuiThemeProvider theme={theme}>
3939
<Provider store={store}>
4040
<LoadingSpinner />

webapp/Utils.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,13 @@ const Utils = {
155155
return modelFields;
156156
},
157157

158+
asEscapedString (value) {
159+
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
160+
},
161+
158162
renameKey (path, oldValue, newValue, callback) {
163+
oldValue = oldValue.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
164+
newValue = newValue.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
159165
this.execPythonMessage(`netpyne_geppetto.rename("${path}","${oldValue}","${newValue}")`)
160166
.then((response) => {
161167
callback(response, newValue);

webapp/components/NetPyNE.js

Lines changed: 114 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import {
1010
Dialog,
1111
ConfirmationDialog,
1212
LaunchDialog,
13-
TutorialObserver
13+
TutorialObserver,
1414
} from 'netpyne/components';
15-
import { loadModel } from '../redux/actions/general';
15+
import { loadModel, openDialog } from '../redux/actions/general';
16+
// import { execPythonMessage } from './general/GeppettoJupyterUtils';
17+
import { replayAll } from './general/CommandRecorder';
1618

1719
const styles = ({ zIndex }) => ({
1820
root: {
@@ -46,6 +48,11 @@ class NetPyNE extends React.Component {
4648
super(props);
4749
this.openPythonCallDialog = this.openPythonCallDialog.bind(this);
4850
this.loaded = false;
51+
this.kernelRestartState = {
52+
state: "idle",
53+
kernelID: undefined,
54+
crashLoop: false
55+
}
4956
}
5057

5158
componentDidMount () {
@@ -66,7 +73,7 @@ class NetPyNE extends React.Component {
6673
return;
6774
}
6875
this.loaded = true;
69-
console.log('Netpyne is ready');
76+
console.log('NetPyNE-UI component is ready');
7077
if (window !== window.parent) {
7178
window.parent.postMessage({
7279
type: 'APP_READY',
@@ -84,7 +91,107 @@ class NetPyNE extends React.Component {
8491
};
8592
// A message from the parent frame can specify the file to load
8693
window.addEventListener('message', loadFromEvent);
87-
// window.load = loadFromEvent
94+
95+
// Logic for kernel reinit
96+
const handleKernelRestart = ({ detail: { kernel, type } }) => {
97+
switch (this.kernelRestartState.state) {
98+
case "restarting":
99+
if (type === "kernel_ready" || type === "kernel_autorestarting") {
100+
console.log("Replaying all commands since the beginning of the session")
101+
replayAll(this.kernelRestartState.kernelID)
102+
this.kernelRestartState = {
103+
...this.kernelRestartState,
104+
state: "idle",
105+
kernelID: undefined,
106+
}
107+
}
108+
case "idle":
109+
if (type === "kernel_connected") {
110+
console.log("Kernel is connecting/starting, being init")
111+
this.kernelRestartState = {
112+
...this.kernelRestartState,
113+
state: "init",
114+
kernelID: kernel.id
115+
}
116+
}
117+
else if (type === "kernel_autorestarting") {
118+
console.log("Kernel restart event caught, trying to re-init the current model")
119+
this.kernelRestartState = {
120+
...this.kernelRestartState,
121+
state: "restarting",
122+
kernelID: kernel.id
123+
}
124+
if (!this.kernelRestartState.crashLoop) {
125+
this.props.dispatchAction(openDialog({
126+
title: "Kernel restart",
127+
message: "An action occured that made the kernel restart. We are reloading your model and all the actions you applied on it."
128+
}))
129+
}
130+
}
131+
else if (type === "kernel_restarting") {
132+
console.log("Kernel restart, perhaps it's a special restart?")
133+
this.kernelRestartState = {
134+
...this.kernelRestartState,
135+
state: "special_restart",
136+
kernelID: kernel.id
137+
}
138+
}
139+
case "init":
140+
if (type === "kernel_ready") {
141+
console.log("Kernel properly initialized")
142+
this.kernelRestartState = {
143+
...this.kernelRestartState,
144+
state: "idle",
145+
kernelID: undefined,
146+
}
147+
}
148+
case "special_restart":
149+
if (type == "kernel_autorestarting") {
150+
console.log("Kernel autorestart after a start, we might not have the ready event, we force it then")
151+
replayAll(this.kernelRestartState.kernelID)
152+
this.kernelRestartState = {
153+
...this.kernelRestartState,
154+
state: 'restarting',
155+
kernelID: kernel.id,
156+
}
157+
} else {
158+
console.log("Regular restart detected")
159+
this.kernelRestartState = {
160+
...this.kernelRestartState,
161+
state: 'idle',
162+
kernelID: undefined,
163+
}
164+
}
165+
}
166+
}
167+
window.addEventListener('kernelstatus', handleKernelRestart)
168+
169+
// Dedicated code to handle crash loops
170+
const kernelRestartLoopHandler = ( ) => {
171+
if (!this.kernelRestartState.crashLoop) {
172+
this.props.dispatchAction(openDialog({
173+
title: "Kernel restart loop stabilization",
174+
message: "One of your actions triggered a kernel restart loop. We are trying to identify the faulty command and to restore your model until this point. Close this window and wait for the kernel stabilization notification."
175+
}))
176+
} else {
177+
clearTimeout(this.kernelRestartState.crashLoop)
178+
}
179+
const taskID = setTimeout((_this) => {
180+
this.props.dispatchAction(openDialog({
181+
title: "Kernel restart loop stabilized",
182+
message: "The kernel is now stabilized."
183+
}));
184+
this.kernelRestartState = {
185+
...this.kernelRestartState,
186+
crashLoop: false
187+
}
188+
}, 8000, this)
189+
this.kernelRestartState = {
190+
...this.kernelRestartState,
191+
crashLoop: taskID
192+
}
193+
}
194+
window.addEventListener('kernelRestartLoop', kernelRestartLoopHandler)
88195
}
89196

90197
componentWillUnmount () {
@@ -115,6 +222,9 @@ class NetPyNE extends React.Component {
115222
<div className={classes.container}>
116223
<div className={classes.topbar}>
117224
<Topbar />
225+
{/* <button onClick={() => {
226+
execPythonMessage("utils.convertToJS(netpyne_geppetto.importCellTemplate(utils.convertToPython('{\"cellArgs\":{},\"fileName\":\"/home/vince/git-repository/metacell/NetPyNE-UI/workspace/cells/FScell.hoc\",\"cellName\":\"FScell\",\"label\":\"CellType1\",\"modFolder\":\"/home/vince/git-repository/metacell/NetPyNE-UI/workspace/mod\",\"importSynMechs\":false,\"compileMod\":false}')))")
227+
}}>CRASH ME</button> */}
118228
</div>
119229
<Box p={1} flex={1} display="flex" alignItems="stretch">
120230
<Grid container spacing={1} className={classes.content} alignItems="stretch">

0 commit comments

Comments
 (0)