Skip to content

Commit d0ddf37

Browse files
Merge pull request #495 from MetaCell/feature/494
Feature/494
2 parents 7b375c6 + b94f561 commit d0ddf37

13 files changed

Lines changed: 231 additions & 91 deletions

File tree

Dockerfile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,15 @@ FROM frodriguez4600/jupyter-neuron:v7.8.0
22
ARG INSTALLATION_FOLDER=/home/jovyan/work/NetPyNE-UI
33
ARG NETPYNE_VERSION=development
44
ARG WORKSPACE_VERSION=nov2020
5-
ARG JUPYTER_GEPPETTO_VERSION=development
6-
ARG PYGEPPETTO_VERSION=development
5+
ARG GEPPETTO_VERSION=development
76
ARG BUILD_ARGS=""
87

98
USER $NB_USER
109

1110
ENV INSTALLATION_FOLDER=$INSTALLATION_FOLDER
1211
ENV NETPYNE_VERSION=$NETPYNE_VERSION
1312
ENV WORKSPACE_VERSION=$WORKSPACE_VERSION
14-
ENV JUPYTER_GEPPETTO_VERSION=$JUPYTER_GEPPETTO_VERSION
15-
ENV PYGEPPETTO_VERSION=$PYGEPPETTO_VERSION
13+
ENV GEPPETTO_VERSION=$GEPPETTO_VERSION
1614
ENV BUILD_ARGS=$BUILD_ARGS
1715

1816
# Install openmpi for parallel simulations
@@ -30,10 +28,12 @@ RUN pip install -r requirements.txt
3028
COPY --chown=1000:1000 . .
3129
WORKDIR ${INSTALLATION_FOLDER}/utilities
3230

33-
RUN python install.py ${BUILD_ARGS}
31+
RUN npm install --global yarn
32+
RUN npm install --global yalc
33+
RUN python install.py ${BUILD_ARGS} --geppetto ${GEPPETTO_VERSION}
3434

3535
WORKDIR ${INSTALLATION_FOLDER}
3636

3737
RUN pip install -r requirements-test.txt
38-
RUN pytest tests/backend
38+
# RUN pytest tests/backend
3939
CMD /bin/bash -c "jupyter notebook --NotebookApp.default_url=/geppetto --NotebookApp.token='' --library=netpyne_ui --NotebookApp.disable_check_xsrf=True"

utilities/install.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,10 @@ def main(netpyne_branch, workspace_branch, geppetto_branch=None, skipNpm=False,
194194
if development:
195195
# install geppetto meta
196196
if os.path.exists(os.path.join(WEBAPP_DIR, '.yalc')):
197-
execute(cmd=['ln', '-s', os.path.expanduser('~') + '/.yalc', '.yalc'], cwd=WEBAPP_DIR)
197+
execute(cmd=['rm', '-rf', '.yalc'], cwd=WEBAPP_DIR)
198+
execute(cmd=['ln', '-s', os.path.expanduser('~') + '/.yalc', '.yalc'], cwd=WEBAPP_DIR)
199+
else:
200+
execute(cmd=['ln', '-s', os.path.expanduser('~') + '/.yalc', '.yalc'], cwd=WEBAPP_DIR)
198201
execute(cmd=['ls'], cwd=WEBAPP_DIR)
199202
execute(cmd=['bash', 'geppetto_ui.sh'], cwd=WEBAPP_DIR)
200203
execute(cmd=['yarn'], cwd=WEBAPP_DIR)

webapp/Utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const Utils = {
6161
}
6262

6363
// skip the list element, e.g. "E"!
64-
console.debug(`Skip ${item} at ${nextObject.label}`);
64+
// console.debug(`Skip ${item} at ${nextObject.label}`);
6565
skipped = true;
6666
} else {
6767
skipped = false;

webapp/components/NetPyNE.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const styles = ({ zIndex }) => ({
3131
},
3232
topbar: {
3333
position: 'relative',
34-
zIndex: zIndex.drawer + 1,
34+
zIndex: zIndex.drawer,
3535
},
3636
content: {
3737
flexGrow: 1,
@@ -46,6 +46,11 @@ const TIMEOUT = 10000;
4646
const EXPERIMENT_POLL_INTERVAL = 1000;
4747

4848
class NetPyNE extends React.Component {
49+
constructor (props) {
50+
super(props);
51+
this.openPythonCallDialog = this.openPythonCallDialog.bind(this);
52+
}
53+
4954
componentDidMount () {
5055
GEPPETTO.on(GEPPETTO.Events.Error_while_exec_python_command, this.openPythonCallDialog, this);
5156

webapp/components/experiments/ExperimentEdit.js

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import ParameterMenu from './ParameterMenu';
2626
import useStyles from './ExperimentEditStyle';
2727
import * as ExperimentHelper from './ExperimentHelper';
2828
import DialogBox from '../general/DialogBox';
29+
import workerCode from './workers/processExperimentData';
30+
2931
const RANGE_VALUE = 0;
3032
const SUPPORTED_TYPES = [REAL_TYPE.INT, REAL_TYPE.FLOAT, REAL_TYPE.STR, REAL_TYPE.BOOL];
3133
const MAX_TRIALS = 100;
@@ -191,13 +193,13 @@ const ExperimentEdit = (props) => {
191193
const [experimentNameError, setExperimentNameError] = useState('');
192194
const [selectionParams, setSelectionParams] = useState([]);
193195
const [trialNumberErrorDialogOpen, setTrialNumberErrorDialogOpen] = useState({ condition: false, number: 1 });
196+
const [paramsCounter, setParamsCounter] = useState(0);
194197

195198
// Existing Experiment.
196199
const [experiment, setExperiment] = useState(null);
197200
const experiments = useSelector((state) => state.experiments.experiments);
198201

199202
let numberOfTrials = 1;
200-
// const dispatch = useDispatch();
201203
const validateParameter = (param) => {
202204
let updatedParam = param;
203205
if (param.type === LIST) {
@@ -253,20 +255,20 @@ const ExperimentEdit = (props) => {
253255
const getParameters = () => {
254256
ExperimentsApi.getParameters()
255257
.then((params) => {
256-
const flattened = Utils.flatten(params);
257-
const paramKeys = Object.keys(flattened);
258-
259-
const filteredKeys = paramKeys.filter((key) => {
260-
// TODO: avoid to fetch field twice!
261-
const field = Utils.getMetadataField(`netParams.${key}`);
262-
if (field && SUPPORTED_TYPES.includes(field.type)) {
263-
return true;
258+
// eslint-disable-next-line prefer-template
259+
// eslint-disable-next-line no-undef
260+
const worker = new Worker(workerCode);
261+
worker.onmessage = function (e) {
262+
switch (e.data.resultMessage) {
263+
case 'OK':
264+
setSelectionParams(e.data.params.results);
265+
worker.terminate();
266+
break;
267+
default:
268+
console.error('worker processing metadata for autocomplete not working.');
264269
}
265-
return false;
266-
});
267-
268-
console.debug(`Size before ${paramKeys.length}, after: ${filteredKeys.length}`);
269-
setSelectionParams(filteredKeys);
270+
};
271+
worker.postMessage({ message: 'process', params: { data: params, metadata: window.metadata } });
270272
});
271273
};
272274

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
const workerCode = () => {
2+
// eslint-disable-next-line no-undef
3+
self.onmessage = function (event) {
4+
// The object that the web page sent is stored in the event.data property.
5+
const Utils = {
6+
getMetadataField (key, field = null) {
7+
if (key === undefined) {
8+
return null;
9+
}
10+
11+
let currentObject;
12+
let nextObject = event.data.params.metadata;
13+
let skipped = false;
14+
15+
// eslint-disable-next-line no-restricted-syntax
16+
key.split('.').forEach((item) => {
17+
if (
18+
currentObject != null
19+
&& currentObject?.container === true
20+
&& !(item in nextObject)
21+
) {
22+
if (skipped) {
23+
return null;
24+
}
25+
26+
// skip the list element, e.g. "E"!
27+
// console.debug(`Skip ${item} at ${nextObject.label}`);
28+
skipped = true;
29+
} else {
30+
skipped = false;
31+
32+
if (item in nextObject) {
33+
currentObject = nextObject[item];
34+
if ('children' in currentObject) {
35+
nextObject = currentObject.children;
36+
}
37+
} else {
38+
currentObject = null;
39+
}
40+
}
41+
});
42+
43+
if (currentObject) {
44+
return field ? currentObject[field] : currentObject;
45+
}
46+
47+
return null;
48+
},
49+
50+
flatten (obj, path = '') {
51+
if (!(obj instanceof Object)) {
52+
// eslint-disable-next-line no-new-object
53+
const newObj = new Object();
54+
newObj[path.replace(/\.$/g, '')] = obj;
55+
return newObj;
56+
}
57+
58+
return Object.keys(obj).reduce(
59+
(output, key) => (obj instanceof Array
60+
? Object.assign(output, Utils.flatten(obj[key], `${path}[${key}].`))
61+
: Object.assign(output, Utils.flatten(obj[key], `${path + key}.`))),
62+
{},
63+
);
64+
},
65+
};
66+
67+
const REAL_TYPE = {
68+
INT: 'int',
69+
FLOAT: 'float',
70+
BOOL: 'bool',
71+
STR: 'str',
72+
FUNC: 'func',
73+
DICT: 'dict',
74+
DICT_DICT: 'dict(dict)',
75+
};
76+
const SUPPORTED_TYPES = [
77+
REAL_TYPE.INT,
78+
REAL_TYPE.FLOAT,
79+
REAL_TYPE.STR,
80+
REAL_TYPE.BOOL,
81+
];
82+
const { data } = event.data.params;
83+
const flattened = Utils.flatten(data);
84+
const paramKeys = Object.keys(flattened);
85+
const filteredKeys = paramKeys.filter((key) => {
86+
// TODO: avoid to fetch field twice!
87+
const field = Utils.getMetadataField(`netParams.${key}`);
88+
if (field && SUPPORTED_TYPES.includes(field.type)) {
89+
return true;
90+
}
91+
return false;
92+
});
93+
94+
// eslint-disable-next-line no-undef
95+
postMessage({ resultMessage: 'OK', params: { results: filteredKeys } });
96+
};
97+
};
98+
99+
let code = workerCode.toString();
100+
code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'));
101+
102+
const blob = new Blob([code], { type: 'application/javascript' });
103+
const workerScript = window.URL.createObjectURL(blob);
104+
105+
export default workerScript;

webapp/components/general/ControlPanelTreeItem.js

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Shuffle from '@material-ui/icons/Shuffle';
1212
import { ChromePicker } from 'react-color';
1313
import { useDispatch, useSelector } from 'react-redux';
1414
import { experimentLabelColor } from '../../theme';
15-
import { changeInstanceColor } from '../../redux/actions/general';
15+
import { changeInstanceColor, selectInstances } from '../../redux/actions/general';
1616

1717
const useStyles = makeStyles((theme) => ({
1818
networkItem: {
@@ -62,26 +62,39 @@ const ControlPanelTreeItem = (props) => {
6262
setColor(_color.rgb);
6363
};
6464

65+
const getRandomColor = () => ({
66+
r: parseFloat((Math.random() * 255).toFixed(2)),
67+
g: parseFloat((Math.random() * 255).toFixed(2)),
68+
b: parseFloat((Math.random() * 255).toFixed(2)),
69+
a: 1,
70+
});
71+
6572
const generateRandomColor = (event, nodeId) => {
66-
const newInstances = instances.filter((instance) => !(instance.instancePath.startsWith(nodeId)));
67-
const randomColor = {
68-
r: parseFloat((Math.random() * 255).toFixed(2)),
69-
g: parseFloat((Math.random() * 255).toFixed(2)),
70-
b: parseFloat((Math.random() * 255).toFixed(2)),
71-
a: 1,
72-
};
73+
const children = window.Instances.getInstance(nodeId).getChildren().map((instance) => instance.getInstancePath());
74+
// const newInstances = instances.filter((instance) => !(instance.instancePath.startsWith(nodeId)));
75+
const newInstances = instances.filter((instance) => {
76+
let condition = true;
77+
children.forEach((child) => {
78+
if (instance.instancePath.startsWith(child)) {
79+
condition = false;
80+
}
81+
});
82+
return condition;
83+
});
7384

74-
newInstances.push({
75-
instancePath: nodeId,
76-
color: {
77-
r: randomColor.r / 255,
78-
g: randomColor.g / 255,
79-
b: randomColor.b / 255,
80-
a: randomColor.a,
81-
},
85+
children.forEach((child) => {
86+
const randomColor = getRandomColor();
87+
newInstances.push({
88+
instancePath: child,
89+
color: {
90+
r: randomColor.r / 255,
91+
g: randomColor.g / 255,
92+
b: randomColor.b / 255,
93+
a: randomColor.a,
94+
},
95+
});
8296
});
8397
dispatch(changeInstanceColor(newInstances));
84-
setColor(randomColor);
8598
};
8699

87100
const changeVisibility = (event, nodeId) => {
@@ -126,6 +139,7 @@ const ControlPanelTreeItem = (props) => {
126139
onNodeSelect,
127140
onVisibilityClick,
128141
children,
142+
disableRandom,
129143
...other
130144
} = props;
131145

@@ -153,7 +167,7 @@ const ControlPanelTreeItem = (props) => {
153167
<IconButton onClick={(event) => changeVisibility(event, nodeId)}>
154168
{ visibility ? <Visibility /> : <VisibilityOff /> }
155169
</IconButton>
156-
<IconButton onClick={(event) => generateRandomColor(event, nodeId)}><Shuffle /></IconButton>
170+
<IconButton disabled={disableRandom} onClick={(event) => generateRandomColor(event, nodeId)}><Shuffle /></IconButton>
157171
<IconButton onClick={() => setShowColorPicker(true)}><ColorLens /></IconButton>
158172
{
159173
showColorPicker

webapp/components/general/ExperimentControlPanel.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
/* eslint-disable no-nested-ternary */
22
import * as React from 'react';
3+
import { useDispatch, useSelector } from 'react-redux';
34
import { makeStyles } from '@material-ui/core/styles';
45
import Box from '@material-ui/core/Box';
56
import Typography from '@material-ui/core/Typography';
67
import TextField from '@material-ui/core/TextField';
78
import TreeView from '@material-ui/lab/TreeView';
8-
import TreeItem from '@material-ui/lab/TreeItem';
99
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
1010
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
1111
import ControlPanelTreeItem from './ControlPanelTreeItem';
1212
import { experimentLabelColor } from '../../theme';
13-
14-
import { MODEL_STATE } from '../../constants';
13+
import { selectInstances } from '../../redux/actions/general';
1514

1615
const useStyles = makeStyles(() => ({
1716
header: {
@@ -24,9 +23,11 @@ const useStyles = makeStyles(() => ({
2423

2524
const ExperimentControlPanel = (props) => {
2625
const classes = useStyles();
26+
const dispatch = useDispatch();
27+
const instances = useSelector((state) => state.general.instances);
2728
const [filter, setFilter] = React.useState('');
2829
const onNodeSelect = (nodeId) => {
29-
console.log(`Node with id ${nodeId} clicked`);
30+
dispatch(selectInstances(instances, [nodeId]));
3031
};
3132
const instancesMap = new Map();
3233

@@ -71,7 +72,7 @@ const ExperimentControlPanel = (props) => {
7172
};
7273

7374
const getTreeItemsFromData = (treeItems) => treeItems.map((treeItemData) => {
74-
let children;
75+
let children = [];
7576
if (treeItemData.getChildren() && treeItemData.getChildren().length > 0) {
7677
children = getTreeItemsFromData(treeItemData.getChildren());
7778
}
@@ -84,6 +85,7 @@ const ExperimentControlPanel = (props) => {
8485
type={treeItemData.getType().getId()}
8586
onNodeSelect={onNodeSelect}
8687
onVisibilityClick={onVisibilityClick}
88+
disableRandom={children.length === 0}
8789
>
8890
{children}
8991
</ControlPanelTreeItem>
@@ -110,12 +112,9 @@ const ExperimentControlPanel = (props) => {
110112
defaultCollapseIcon={<ExpandMoreIcon />}
111113
defaultExpandIcon={<ChevronRightIcon />}
112114
>
113-
<TreeItem nodeId="network" label="network_netpyne">
114-
{filter === ''
115-
? getTreeItemsFromData(window.Instances.network.getChildren())
116-
: getFlatFilteredList(window.Instances.network.getChildren())
117-
}
118-
</TreeItem>
115+
{filter === ''
116+
? getTreeItemsFromData([window.Instances.getInstance('network')])
117+
: getFlatFilteredList([window.Instances.getInstance('network')])}
119118
</TreeView>
120119
</Box>
121120
)

0 commit comments

Comments
 (0)