+ }
+
+ const { currentName } = this.state
+ const { cellRule, section, } = this.props
+ let tag = `netParams.cellParams['${cellRule}']['secs']['${section}']['mechs']['${currentName}']`
+
+ return this.state.mechFields.map((name, i) => (
+
+
+
+
+ ))
+
+ };
+
+ render () {
+ var content = []
+ if (this.state.currentName != undefined && this.state.currentName != '') {
+ Utils
+ .evalPythonMessage("netpyne_geppetto.getMechParams", [this.state.currentName])
+ .then(response => {
+ if (JSON.stringify(this.state.mechFields) != JSON.stringify(response)) {
+ this.setState({ mechFields: response })
+ }
+ })
+ content.push(this.renderMechFields())
+ }
+
+ return (
+
+
+
+
+
+ {content}
+
+ );
+ }
+}
diff --git a/webapp/components/definition/cellRules/sections/mechanisms/NetPyNENewMechanism.js b/webapp/components/definition/cellRules/sections/mechanisms/NetPyNENewMechanism.js
new file mode 100644
index 00000000..5be18744
--- /dev/null
+++ b/webapp/components/definition/cellRules/sections/mechanisms/NetPyNENewMechanism.js
@@ -0,0 +1,132 @@
+import React from 'react';
+import Menu from '@material-ui/core/Menu';
+import MenuItem from '@material-ui/core/MenuItem';
+import NavigationMoreHoriz from '@material-ui/icons/MoreHoriz';
+import { withStyles } from '@material-ui/core/styles'
+import Utils from '../../../../../Utils';
+import ContentAdd from '@material-ui/icons/Add'
+import { MechIcon } from '../../../../general/NetPyNEIcons'
+import Tooltip from '../../../../general/Tooltip'
+const fontSize = 40
+const styles = ({ spacing, palette }) => ({
+ icon : { color: palette.primary.main, cursor: 'pointer' },
+ disabledIcon : { color: '#d1d1d1', cursor: 'auto' },
+ iconContent: { position: 'absolute', color: 'white' },
+ cogIconContent: {
+ width: fontSize,
+ height: fontSize,
+ position: 'absolute',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center'
+ },
+ container: {
+ position: 'relative',
+ width: fontSize - 2,
+ height: fontSize - 2
+ },
+ cogIcon: { width: fontSize, height:fontSize, position: 'absolute' }
+
+})
+
+class NetPyNENewMechanism extends React.Component {
+
+ constructor (props) {
+ super(props);
+ this.state = {
+ open: false,
+ mechanisms: []
+ };
+ }
+
+ componentDidMount () {
+ Utils.evalPythonMessage("netpyne_geppetto.getAvailableMechs", [])
+ .then(response => {
+ this.setState({ mechanisms: response })
+ })
+ }
+
+ handleClick = event => {
+ this.setState({ open: false });
+ this.props.handleClick(event.target.innerText);
+ };
+
+ handleButtonClick = anchor => {
+ const { blockButton, handleHierarchyClick } = this.props;
+ if (!blockButton) {
+ this.setState({ open: true, anchorEl: anchor })
+ }
+ handleHierarchyClick();
+ };
+
+ createTooltip (){
+ const { disabled, blockButton } = this.props;
+ if (disabled) {
+ return ""
+ } else {
+ if (blockButton) {
+ return "Explore mechanisms"
+ } else {
+ return "Create new mechanism"
+ }
+ }
+ }
+
+ createLabel (classes){
+ const { disabled, blockButton } = this.props;
+ if (disabled) {
+ return ""
+ } else {
+ if (blockButton) {
+ return
+ } else {
+ return
+ }
+ }
+ }
+ render () {
+ const { disabled, classes, className } = this.props;
+ const { open, anchorEl, mechanisms } = this.state;
+ const tooltip = disabled ? '' : this.createTooltip()
+ return (
+
+
+
+
+
+
+
+
+ )
+ }
+}
+
+export default withStyles(styles)(NetPyNENewMechanism)
\ No newline at end of file
diff --git a/webapp/components/definition/configuration/NetPyNESimConfig.js b/webapp/components/definition/configuration/NetPyNESimConfig.js
new file mode 100644
index 00000000..f18b915a
--- /dev/null
+++ b/webapp/components/definition/configuration/NetPyNESimConfig.js
@@ -0,0 +1,316 @@
+import React, { Component } from 'react';
+import FontIcon from '@material-ui/core/Icon';
+import { BottomNavigation, BottomNavigationAction } from '@material-ui/core';
+import { withStyles } from '@material-ui/core/styles'
+import Paper from '@material-ui/core/Paper'
+import { bgDark } from '../../../theme'
+import {
+ NetPyNEField,
+ NetPyNECheckbox,
+ NetPyNETextField,
+ SelectField,
+ ListComponent,
+ GridLayout
+} from 'netpyne/components';
+
+class NetPyNESimConfig extends React.Component {
+
+ constructor (props) {
+ super(props);
+ this.state = {
+ model: props.model,
+ selectedIndex: 0,
+ sectionId: "General"
+ };
+ }
+
+ UNSAFE_componentWillReceiveProps (nextProps) {
+ this.setState({ model: nextProps.model });
+ }
+
+ select = (index, sectionId) => this.setState({ selectedIndex: index, sectionId: sectionId });
+
+ render () {
+ var contentLeft = ;
+ var contentRight = ;
+ if (this.state.sectionId == 'General') {
+ contentLeft = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ contentRight = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+
+ } else if (this.state.sectionId == 'SaveConfiguration') {
+ contentLeft = (
+
+
+
+
+
+ {
+ !window.isDocker
+ &&
+
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ contentRight = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/*
+
+
+
+
+
+
+
+
+
+
+
+
+
+ */}
+
+
+
+
+
+ )
+ } else if (this.state.sectionId == 'Record') {
+ contentLeft = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ contentRight = (
+
+
+
+
+
+
+
+
+
+ )
+ } else if (this.state.sectionId == 'netParams') {
+ var contentLeft = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ contentRight = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ }
+ const { classes } = this.props
+ return (
+
+
+ } onClick={() => this.select(0, 'General')} />
+ } onClick={() => this.select(1, 'Record')} />
+ } onClick={() => this.select(2, 'SaveConfiguration')} />
+ } onClick={() => this.select(3, 'netParams')} />
+
+
+
+ {contentLeft}
+ {contentRight}
+
+
+
+ );
+ }
+}
+
+const styles = ({ shape, spacing }) => ({
+ root: { height: `calc(100% - 56px - ${spacing(1)}px)`, flexDirection: 'column' },
+ bottomNav: { margin: spacing(2) },
+ layout: { height: "100%", display: 'flex' }
+
+})
+
+export default withStyles(styles)(NetPyNESimConfig)
\ No newline at end of file
diff --git a/webapp/components/definition/connectivity/NetPyNEConnectivityRule.js b/webapp/components/definition/connectivity/NetPyNEConnectivityRule.js
new file mode 100644
index 00000000..1b2f8b87
--- /dev/null
+++ b/webapp/components/definition/connectivity/NetPyNEConnectivityRule.js
@@ -0,0 +1,406 @@
+import React from "react";
+import TextField from '@material-ui/core/TextField';
+import FontIcon from "@material-ui/core/Icon";
+import { BottomNavigation, BottomNavigationAction } from "@material-ui/core";
+import Dialog from "@material-ui/core/Dialog/Dialog";
+import Button from "@material-ui/core/Button";
+import DialogActions from "@material-ui/core/DialogActions";
+import DialogContent from "@material-ui/core/DialogContent";
+import DialogContentText from "@material-ui/core/DialogContentText";
+import DialogTitle from "@material-ui/core/DialogTitle";
+import MenuItem from "@material-ui/core/MenuItem";
+import Paper from '@material-ui/core/Paper'
+import Box from '@material-ui/core/Box';
+
+import {
+ NetPyNESelectField,
+ NetPyNEField,
+ NetPyNETextField,
+ ListComponent,
+ NetPyNECoordsRange
+} from "netpyne/components";
+import Utils from "../../../Utils";
+
+export default class NetPyNEConnectivityRule extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = {
+ currentName: props.name,
+ selectedIndex: 0,
+ sectionId: "General",
+ errorMessage: undefined,
+ errorDetails: undefined
+ };
+ }
+
+ handleRenameChange = event => {
+ var storedValue = this.props.name;
+ var newValue = Utils.nameValidation(event.target.value);
+ var updateCondition = this.props.renameHandler(newValue);
+ var triggerCondition = Utils.handleUpdate(
+ updateCondition,
+ newValue,
+ event.target.value,
+ this,
+ "ConnectionRule"
+ );
+
+ if (triggerCondition) {
+ this.triggerUpdate(() => {
+ // Rename the population in Python
+ Utils.renameKey(
+ "netParams.connParams",
+ storedValue,
+ newValue,
+ (response, newValue) => {
+ this.renaming = false;
+ }
+ );
+ this.renaming = true;
+ });
+ }
+ };
+
+ triggerUpdate (updateMethod) {
+ // common strategy when triggering processing of a value change, delay it, every time there is a change we reset
+ if (this.updateTimer != undefined) {
+ clearTimeout(this.updateTimer);
+ }
+ this.updateTimer = setTimeout(updateMethod, 1000);
+ }
+
+ select = (index, sectionId) =>
+ this.setState({ selectedIndex: index, sectionId: sectionId });
+
+ getBottomNavigationAction (index, sectionId, label, icon, id) {
+ return (
+ }
+ onClick={() => this.select(index, sectionId)}
+ />
+ );
+ }
+
+ postProcessMenuItems (pythonData, selected) {
+ return pythonData.map(name => (
+
+ ));
+ }
+
+ UNSAFE_componentWillReceiveProps (nextProps) {
+ this.setState({ currentName: nextProps.name });
+ }
+
+ render () {
+ const dialogPop = this.state.errorMessage != undefined ? (
+
+ ) : (
+ undefined
+ );
+
+ if (this.state.sectionId == "General") {
+ var content = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ pythonData.map(name => (
+
+ ))
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {dialogPop}
+
+ );
+ } else if (this.state.sectionId == "Pre Conditions") {
+ var content = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ } else if (this.state.sectionId == "Post Conditions") {
+ var content = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ // Generate Menu
+ var index = 0;
+ var bottomNavigationItems = [];
+ bottomNavigationItems.push(
+ this.getBottomNavigationAction(
+ index++,
+ "General",
+ "General",
+ "fa-bars",
+ "generalConnTab"
+ )
+ );
+ bottomNavigationItems.push(
+ this.getBottomNavigationAction(
+ index++,
+ "Pre Conditions",
+ "Pre-synaptic cells conditions",
+ "fa-caret-square-o-left",
+ "preCondsConnTab"
+ )
+ );
+ bottomNavigationItems.push(
+ this.getBottomNavigationAction(
+ index++,
+ "Post Conditions",
+ "Post-synaptic cells conditions",
+ "fa-caret-square-o-right",
+ "postCondsConnTab"
+ )
+ );
+
+ return (
+
+
+ {bottomNavigationItems}
+
+ {content}
+
+ );
+ }
+
+ handleChange = (event, index, values) => this.setState({ values });
+}
diff --git a/webapp/components/definition/connectivity/NetPyNEConnectivityRules.js b/webapp/components/definition/connectivity/NetPyNEConnectivityRules.js
new file mode 100644
index 00000000..67627911
--- /dev/null
+++ b/webapp/components/definition/connectivity/NetPyNEConnectivityRules.js
@@ -0,0 +1,252 @@
+import React, { Component } from 'react';
+import Dialog from '@material-ui/core/Dialog/Dialog';
+
+import Button from '@material-ui/core/Button';
+import Utils from '../../../Utils';
+import NetPyNEHome from '../../general/NetPyNEHome';
+import NetPyNEAddNew from '../../general/NetPyNEAddNew';
+
+import NetPyNEConnectivityRule from './NetPyNEConnectivityRule';
+import { NetPyNEThumbnail, GridLayout, Filter } from 'netpyne/components'
+
+import RulePath from '../../general/RulePath'
+import Accordion from '../../general/ExpansionPanel'
+import Divider from '@material-ui/core/Divider';
+export default class NetPyNEConnectivityRules extends Component {
+
+ constructor (props) {
+ super(props);
+ this.state = {
+ drawerOpen: false,
+ selectedConnectivityRule: undefined,
+ deletedConnectivityRule: undefined,
+ page: "main",
+ errorMessage: undefined,
+ errorDetails: undefined,
+ filterValue: null
+ };
+
+ this.selectPage = this.selectPage.bind(this);
+
+ this.selectConnectivityRule = this.selectConnectivityRule.bind(this);
+ this.handleNewConnectivityRule = this.handleNewConnectivityRule.bind(this);
+
+ this.handleRenameChildren = this.handleRenameChildren.bind(this);
+ }
+
+ handleToggle = () => this.setState({ drawerOpen: !this.state.drawerOpen });
+
+
+ selectPage (page) {
+ this.setState({ page: page });
+ }
+
+ /* Method that handles button click */
+ selectConnectivityRule (connectivityRule) {
+ this.setState({ selectedConnectivityRule: connectivityRule });
+ }
+
+ handleNewConnectivityRule () {
+ var defaultConnectivityRules = {
+ 'ConnectivityRule': {
+ 'preConds': {},
+ 'postConds': {}
+ }
+ };
+ // Get Key and Value
+ var key = Object.keys(defaultConnectivityRules)[0];
+ var value = defaultConnectivityRules[key];
+ const model = { ...this.state.value }
+
+ // Get New Available ID
+ var connectivityRuleId = Utils.getAvailableKey(model, key);
+ var newConnectivityRule = Object.assign({ name: connectivityRuleId }, value);
+ // Create Cell Rule Client side
+ Utils.execPythonMessage('netpyne_geppetto.netParams.connParams["' + connectivityRuleId + '"] = ' + JSON.stringify(value));
+ model[connectivityRuleId] = newConnectivityRule;
+ // Update state
+ this.setState({
+ value: model,
+ selectedConnectivityRule: connectivityRuleId
+ });
+ }
+
+
+ hasSelectedConnectivityRuleBeenRenamed (prevState, currentState) {
+ var currentModel = prevState.value;
+ var model = currentState.value;
+ // deal with rename
+ if (currentModel != undefined && model != undefined) {
+ var oldP = Object.keys(currentModel);
+ var newP = Object.keys(model);
+ if (oldP.length == newP.length) {
+ // if it's the same lenght there could be a rename
+ for (var i = 0; i < oldP.length; i++) {
+ if (oldP[i] != newP[i]) {
+ if (prevState.selectedConnectivityRule != undefined) {
+ if (oldP[i] == prevState.selectedConnectivityRule) {
+ return newP[i];
+ }
+ }
+ }
+ }
+ }
+ }
+ return undefined;
+ }
+
+
+ componentDidUpdate (prevProps, prevState) {
+ // we need to check if any of the three entities have been renamed and if that's the case change the state for the selection variable
+ var newConnectivityRuleName = this.hasSelectedConnectivityRuleBeenRenamed(prevState, this.state);
+ if (newConnectivityRuleName !== undefined) {
+ this.setState({ selectedConnectivityRule: newConnectivityRuleName, deletedConnectivityRule: undefined });
+ } else if ((prevState.value !== undefined) && (Object.keys(prevState.value).length !== Object.keys(this.state.value).length)) {
+ /*
+ * logic into this if to check if the user added a new object from the python backend and
+ * if the name convention pass the checks, differently rename this and open dialog to inform.
+ */
+ var model = this.state.value;
+ for (var m in model) {
+ if ((prevState.value !== "") && (!(m in prevState.value))) {
+ var newValue = Utils.nameValidation(m);
+ if (newValue != m) {
+ newValue = Utils.getAvailableKey(model, newValue);
+ model[newValue] = model[m];
+ delete model[m];
+ this.setState({
+ value: model,
+ errorMessage: "Error",
+ errorDetails: "Leading digits or whitespaces are not allowed in ConnectivityRule names.\n"
+ + m + " has been renamed " + newValue
+ },
+ function () {
+ Utils.renameKey('netParams.connParams', m, newValue, (response, newValue) => {});
+ }.bind(this));
+ }
+ }
+ }
+ }
+ }
+
+ shouldComponentUpdate (nextProps, nextState) {
+ var itemRenamed = this.hasSelectedConnectivityRuleBeenRenamed(this.state, nextState) !== undefined;
+ var newItemCreated = false;
+ var selectionChanged = this.state.selectedConnectivityRule != nextState.selectedConnectivityRule;
+ var pageChanged = this.state.page != nextState.page;
+ var newModel = this.state.value == undefined;
+ if (!newModel) {
+ newItemCreated = ((Object.keys(this.state.value).length != Object.keys(nextState.value).length));
+ }
+ var errorDialogOpen = (this.state.errorDetails !== nextState.errorDetails);
+ const filterValueChanged = nextState.filterValue !== this.state.filterValue
+ return filterValueChanged || newModel || newItemCreated || itemRenamed || selectionChanged || pageChanged || errorDialogOpen;
+ }
+
+ handleRenameChildren (childName) {
+ childName = childName.replace(/\s*$/,"");
+ var childrenList = Object.keys(this.state.value);
+ for (var i = 0 ; childrenList.length > i ; i++) {
+ if (childName === childrenList[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ getCopyPath (){
+ const { value: model, selectedConnectivityRule } = this.state
+ return model && model[selectedConnectivityRule] && `netParams.connParams["${selectedConnectivityRule}"]`
+ }
+
+ render () {
+ var actions = [
+