diff --git a/.gitignore b/.gitignore index e3d873d8..db126a77 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ init.py webapp/node_modules webapp/geppetto-client webapp/build +webapp/.yalc workspace netpyne_workspace tests/frontend/e2e/node_modules @@ -25,4 +26,5 @@ utilities/x86_64 *.iml x86_64 .jupyter-config -venv \ No newline at end of file +venv +node_modules diff --git a/netpyne_ui/__init__.py b/netpyne_ui/__init__.py index cd1449c3..359fe02f 100644 --- a/netpyne_ui/__init__.py +++ b/netpyne_ui/__init__.py @@ -1,4 +1,14 @@ from jupyter_geppetto.webapi import RouteManager from netpyne_ui import api +import sentry_sdk + +sentry_sdk.init( + "https://d8bf7e40eec34cb9891f6dd8207b5e83@sentry.metacell.us/6", + + # Set traces_sample_rate to 1.0 to capture 100% + # of transactions for performance monitoring. + # We recommend adjusting this value in production. + traces_sample_rate=1.0, +) RouteManager.add_controller(api.NetPyNEController) diff --git a/netpyne_ui/experiments.py b/netpyne_ui/experiments.py index d94f8377..16abf33e 100644 --- a/netpyne_ui/experiments.py +++ b/netpyne_ui/experiments.py @@ -154,7 +154,7 @@ def get_model_specification(name: str, trial: str) -> dict: """ path = get_trial_output_path(name, trial, fallback=True) if path is None or not os.path.exists(path): - raise ExperimentsError(f"Trial file {path} not found") + raise ExperimentsError(f"Condition file {path} not found") with open(path, "r") as f: trial_output = json.load(f) @@ -273,7 +273,7 @@ def onerror(func, path, exc_info): def _create_base_model_trial() -> model.Trial: - return model.Trial(name="Trial 1", id=BASE_TRIAL_ID) + return model.Trial(name="Condition 1", id=BASE_TRIAL_ID) def _create_trials(experiment: model.Experiment) -> List[model.Trial]: @@ -315,7 +315,7 @@ def _create_trials(experiment: model.Experiment) -> List[model.Trial]: filename = combinations["filenames"][combIdx][1:] indices = combinations["indices"][combIdx] - name = f"Trial {combIdx + 1}" + name = f"Condition {combIdx + 1}" trials.append( model.Trial(name=name, params=params, indices=indices, id=filename) diff --git a/netpyne_ui/netpyne_geppetto.py b/netpyne_ui/netpyne_geppetto.py index 6ec9a098..2f6ad63e 100644 --- a/netpyne_ui/netpyne_geppetto.py +++ b/netpyne_ui/netpyne_geppetto.py @@ -138,7 +138,7 @@ def viewExperimentResult(self, payload: dict): file = experiments.get_trial_output_path(name, trial) if file is None or not os.path.exists(file): - return utils.getJSONError(f"Couldn't find output file of trial. Please take a look at the simulation log.", "") + return utils.getJSONError(f"Couldn't find output file of condition. Please take a look at the simulation log.", "") if self.doIhaveInstOrSimData()['haveInstance']: sim.clearAll() @@ -184,7 +184,7 @@ def instantiateNetPyNEModelInGeppetto(self, args): self.geppetto_model = self.model_interpreter.getGeppettoModel(netpyne_model) return json.loads(GeppettoModelSerializer.serialize(self.geppetto_model)) - except Exception: + except Exception as e: message = "Error while instantiating the NetPyNE model" logging.exception(message) return utils.getJSONError(message, sys.exc_info()) diff --git a/package-lock.json b/package-lock.json index cfdf7274..80914b82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,313 @@ { + "name": "NetPyNE-UI", + "lockfileVersion": 2, "requires": true, - "lockfileVersion": 1, + "packages": { + "": { + "dependencies": { + "@sentry/react": "^6.16.1", + "@sentry/tracing": "^6.16.1" + } + }, + "node_modules/@sentry/browser": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.16.1.tgz", + "integrity": "sha512-F2I5RL7RTLQF9CccMrqt73GRdK3FdqaChED3RulGQX5lH6U3exHGFxwyZxSrY4x6FedfBFYlfXWWCJXpLnFkow==", + "dependencies": { + "@sentry/core": "6.16.1", + "@sentry/types": "6.16.1", + "@sentry/utils": "6.16.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/core": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.16.1.tgz", + "integrity": "sha512-UFI0264CPUc5cR1zJH+S2UPOANpm6dLJOnsvnIGTjsrwzR0h8Hdl6rC2R/GPq+WNbnipo9hkiIwDlqbqvIU5vw==", + "dependencies": { + "@sentry/hub": "6.16.1", + "@sentry/minimal": "6.16.1", + "@sentry/types": "6.16.1", + "@sentry/utils": "6.16.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.16.1.tgz", + "integrity": "sha512-4PGtg6AfpqMkreTpL7ymDeQ/U1uXv03bKUuFdtsSTn/FRf9TLS4JB0KuTZCxfp1IRgAA+iFg6B784dDkT8R9eg==", + "dependencies": { + "@sentry/types": "6.16.1", + "@sentry/utils": "6.16.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.16.1.tgz", + "integrity": "sha512-dq+mI1EQIvUM+zJtGCVgH3/B3Sbx4hKlGf2Usovm9KoqWYA+QpfVBholYDe/H2RXgO7LFEefDLvOdHDkqeJoyA==", + "dependencies": { + "@sentry/hub": "6.16.1", + "@sentry/types": "6.16.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/react": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.16.1.tgz", + "integrity": "sha512-n8fOEKbym4kBi946q3AWXBNy1UKTmABj/hE2nAJbTWhi5IwdM7WBG6QCT2yq7oTHLuTxQrAwgKQc+A6zFTyVHg==", + "dependencies": { + "@sentry/browser": "6.16.1", + "@sentry/minimal": "6.16.1", + "@sentry/types": "6.16.1", + "@sentry/utils": "6.16.1", + "hoist-non-react-statics": "^3.3.2", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "react": "15.x || 16.x || 17.x" + } + }, + "node_modules/@sentry/tracing": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.16.1.tgz", + "integrity": "sha512-MPSbqXX59P+OEeST+U2V/8Hu/8QjpTUxTNeNyTHWIbbchdcMMjDbXTS3etCgajZR6Ro+DHElOz5cdSxH6IBGlA==", + "dependencies": { + "@sentry/hub": "6.16.1", + "@sentry/minimal": "6.16.1", + "@sentry/types": "6.16.1", + "@sentry/utils": "6.16.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/types": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.16.1.tgz", + "integrity": "sha512-Wh354g30UsJ5kYJbercektGX4ZMc9MHU++1NjeN2bTMnbofEcpUDWIiKeulZEY65IC1iU+1zRQQgtYO+/hgCUQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.16.1.tgz", + "integrity": "sha512-7ngq/i4R8JZitJo9Sl8PDnjSbDehOxgr1vsoMmerIsyRZ651C/8B+jVkMhaAPgSdyJ0AlE3O7DKKTP1FXFw9qw==", + "dependencies": { + "@sentry/types": "6.16.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + }, "dependencies": { - "@types/js-base64": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@types/js-base64/-/js-base64-3.3.1.tgz", - "integrity": "sha512-Zw33oQNAvDdAN9b0IE5stH0y2MylYvtU7VVTKEJPxhyM2q57CVaNJhtJW258ah24NRtaiA23tptUmVn3dmTKpw==", - "dev": true, + "@sentry/browser": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.16.1.tgz", + "integrity": "sha512-F2I5RL7RTLQF9CccMrqt73GRdK3FdqaChED3RulGQX5lH6U3exHGFxwyZxSrY4x6FedfBFYlfXWWCJXpLnFkow==", + "requires": { + "@sentry/core": "6.16.1", + "@sentry/types": "6.16.1", + "@sentry/utils": "6.16.1", + "tslib": "^1.9.3" + } + }, + "@sentry/core": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.16.1.tgz", + "integrity": "sha512-UFI0264CPUc5cR1zJH+S2UPOANpm6dLJOnsvnIGTjsrwzR0h8Hdl6rC2R/GPq+WNbnipo9hkiIwDlqbqvIU5vw==", + "requires": { + "@sentry/hub": "6.16.1", + "@sentry/minimal": "6.16.1", + "@sentry/types": "6.16.1", + "@sentry/utils": "6.16.1", + "tslib": "^1.9.3" + } + }, + "@sentry/hub": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.16.1.tgz", + "integrity": "sha512-4PGtg6AfpqMkreTpL7ymDeQ/U1uXv03bKUuFdtsSTn/FRf9TLS4JB0KuTZCxfp1IRgAA+iFg6B784dDkT8R9eg==", + "requires": { + "@sentry/types": "6.16.1", + "@sentry/utils": "6.16.1", + "tslib": "^1.9.3" + } + }, + "@sentry/minimal": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.16.1.tgz", + "integrity": "sha512-dq+mI1EQIvUM+zJtGCVgH3/B3Sbx4hKlGf2Usovm9KoqWYA+QpfVBholYDe/H2RXgO7LFEefDLvOdHDkqeJoyA==", + "requires": { + "@sentry/hub": "6.16.1", + "@sentry/types": "6.16.1", + "tslib": "^1.9.3" + } + }, + "@sentry/react": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.16.1.tgz", + "integrity": "sha512-n8fOEKbym4kBi946q3AWXBNy1UKTmABj/hE2nAJbTWhi5IwdM7WBG6QCT2yq7oTHLuTxQrAwgKQc+A6zFTyVHg==", + "requires": { + "@sentry/browser": "6.16.1", + "@sentry/minimal": "6.16.1", + "@sentry/types": "6.16.1", + "@sentry/utils": "6.16.1", + "hoist-non-react-statics": "^3.3.2", + "tslib": "^1.9.3" + } + }, + "@sentry/tracing": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.16.1.tgz", + "integrity": "sha512-MPSbqXX59P+OEeST+U2V/8Hu/8QjpTUxTNeNyTHWIbbchdcMMjDbXTS3etCgajZR6Ro+DHElOz5cdSxH6IBGlA==", + "requires": { + "@sentry/hub": "6.16.1", + "@sentry/minimal": "6.16.1", + "@sentry/types": "6.16.1", + "@sentry/utils": "6.16.1", + "tslib": "^1.9.3" + } + }, + "@sentry/types": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.16.1.tgz", + "integrity": "sha512-Wh354g30UsJ5kYJbercektGX4ZMc9MHU++1NjeN2bTMnbofEcpUDWIiKeulZEY65IC1iU+1zRQQgtYO+/hgCUQ==" + }, + "@sentry/utils": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.16.1.tgz", + "integrity": "sha512-7ngq/i4R8JZitJo9Sl8PDnjSbDehOxgr1vsoMmerIsyRZ651C/8B+jVkMhaAPgSdyJ0AlE3O7DKKTP1FXFw9qw==", + "requires": { + "@sentry/types": "6.16.1", + "tslib": "^1.9.3" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", "requires": { - "js-base64": "*" + "react-is": "^16.7.0" } }, - "js-base64": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.6.1.tgz", - "integrity": "sha512-Frdq2+tRRGLQUIQOgsIGSCd1VePCS2fsddTG5dTCqR0JHgltXWfsxnY0gIXPoMeRmdom6Oyq+UMOFg5suduOjQ==", - "dev": true + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "peer": true + }, + "react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "peer": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" } } } diff --git a/package.json b/package.json new file mode 100644 index 00000000..17d54363 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "@sentry/react": "^6.16.1", + "@sentry/tracing": "^6.16.1" + } +} diff --git a/requirements.txt b/requirements.txt index 98217d9b..c7448b0f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -83,6 +83,7 @@ ruamel-yaml==0.15.80 ruamel.yaml.clib==0.2.0 scipy==1.4.1 Send2Trash==1.5.0 +sentry_sdk==1.5.2 terminado==0.8.3 testpath==0.4.4 tornado==6.1.0 diff --git a/tests/backend/netypne_model_importer_test.py b/tests/backend/netypne_model_importer_test.py index 73093584..f6361ce3 100644 --- a/tests/backend/netypne_model_importer_test.py +++ b/tests/backend/netypne_model_importer_test.py @@ -36,6 +36,7 @@ def setUpClass(cls): logging.error("Error loading mechanisms", exc_info=True) def test_dict_import_1(self): + assert 0 print("------------------------------------") print("Dictionary transform importModel:") print("------------------------------------") @@ -60,6 +61,40 @@ def test_dict_import_1(self): netpyne = NetPyNEGeppetto() netpyne.importModel(netpyne_info) + def test_netpyne_exported_model_1(self): + print("------------------------------------") + print("Netpyne exported model sim run") + print("------------------------------------") + + params = {} + + HERE = os.path.dirname(os.path.realpath(__file__)) + ROOT = os.path.dirname(HERE) + + params["areModFieldsRequired"] = False + params["compileMod"] = False + params["exploreOnlyDirs"] = False + params["explorerDialogOpen"] = False + params["explorerParameter"] = "" + params["freezeInstance"] = True + params["freezeSimulation"] = True + params["jsonModelFolder"] = ROOT + "/workspace/HHCellNetwork.txt_data.json" + params["jsonPath"] = "" + params["loadNet"] = True + params["loadNetParams"] = True + params["loadSimCfg"] = True + params["loadSimData"] = True + params["modFolder"] = "" + params["modPath"] = "" + params["tab"] = "simulate" + + netpyne = NetPyNEGeppetto() + + netpyne.loadModel(params) + netpyne.instantiateNetPyNEModel() + + return False + if __name__ == '__main__': try: unittest.main() diff --git a/tests/frontend/e2e/.eslintrc.js b/tests/frontend/e2e/.eslintrc.js index 9310b33f..d804684b 100644 --- a/tests/frontend/e2e/.eslintrc.js +++ b/tests/frontend/e2e/.eslintrc.js @@ -1,8 +1,5 @@ module.exports = { - extends: [ - "./node_modules/@geppettoengine/geppetto-client/.eslintrc.js", - "plugin:jest/recommended" - ], + extends: ["plugin:jest/recommended"], rules: { 'multiline-comment-style': 0, }, diff --git a/utilities/install.py b/utilities/install.py index 95ddc5b0..65def579 100644 --- a/utilities/install.py +++ b/utilities/install.py @@ -13,13 +13,16 @@ PYGEPPETTO = 'https://github.com/openworm/pygeppetto.git' NETPYNE = 'https://github.com/Neurosim-lab/netpyne.git' WORKSPACE = 'https://github.com/Neurosim-lab/netpyne_workspace' +META = 'https://github.com/MetaCell/geppetto-meta/' ROOT_DIR = os.path.join(HERE, os.pardir) DEPS_DIR = os.path.join(ROOT_DIR, 'src') WEBAPP_DIR = os.path.join(ROOT_DIR, 'webapp') JUPYTER_DIR = 'jupyter-geppetto' +PYGEPPETTO_DIR = 'pygeppetto' NETPYNE_DIR = 'netpyne' +META_DIR = 'geppetto-meta' WORKSPACE_DIR = 'workspace' @@ -81,7 +84,7 @@ def compile_mod(): execute(['nrnivmodl', os.path.join(WORKSPACE_DIR, 'mod')], cwd=ROOT_DIR) -def main(netpyne_branch, workspace_branch, pygeppetto_branch=None, jupyter_geppetto_branch=None, skipNpm=False, +def main(netpyne_branch, workspace_branch, geppetto_branch=None, skipNpm=False, skipTest=False, development=False): cprint("Installing requirements") execute(cmd=['pip', 'install', '-r', 'requirements.txt'], cwd=ROOT_DIR) @@ -93,28 +96,26 @@ def main(netpyne_branch, workspace_branch, pygeppetto_branch=None, jupyter_geppe if development: os.chdir(DEPS_DIR) + # cloning geppetto meta + cprint("Installing geppetto-meta") + clone(repository=META, + folder=META_DIR, + branch_or_tag=geppetto_branch + ) + + # clone and install netpyne cprint("Installing netpyne") clone(repository=NETPYNE, branch_or_tag=netpyne_branch) execute(cmd=['pip', 'install', '-e', '.'], cwd=os.path.join(DEPS_DIR, NETPYNE_DIR)) - # install pygeppetto + # installing pygeppetto cprint("Installing pygeppetto") - clone(repository=PYGEPPETTO, - folder='pygeppetto', - branch_or_tag=pygeppetto_branch - ) - execute(cmd=['pip', 'install', '-e', '.'], cwd=os.path.join(DEPS_DIR, 'pygeppetto')) - - # install jupyter geppetto - cprint("Installing org.geppetto.frontend.jupyter") - clone(repository=JUPYTER, - folder=JUPYTER_DIR, - branch_or_tag=jupyter_geppetto_branch - ) - - execute(cmd=['pip', 'install', '-e', '.'], cwd=os.path.join(DEPS_DIR, JUPYTER_DIR)) + execute(cmd=['pip', 'install', '-e', '.'], cwd=os.path.join(DEPS_DIR, META_DIR, PYGEPPETTO_DIR)) + # installing jupyter geppetto + cprint("Installing jupyter geppetto") + execute(cmd=['pip', 'install', '-e', '.'], cwd=os.path.join(DEPS_DIR, META_DIR, JUPYTER_DIR)) + # installing core dependencies execute(cmd=['pip', 'install', '-e', '.'], cwd=ROOT_DIR) - else: # install requirements if netpyne_branch and netpyne_branch != 'master': @@ -130,11 +131,11 @@ def main(netpyne_branch, workspace_branch, pygeppetto_branch=None, jupyter_geppe cprint("Compiling workspace modules") compile_mod() - if not skipNpm and os.path.exists(os.path.join(DEPS_DIR, JUPYTER_DIR)): + if not skipNpm and os.path.exists(os.path.join(DEPS_DIR, META_DIR, JUPYTER_DIR)): cprint("Building Jupyter Geppetto extension...") - execute(cmd=['npm', 'ci'], cwd=os.path.join(DEPS_DIR, JUPYTER_DIR, 'js')) + execute(cmd=['npm', 'ci'], cwd=os.path.join(DEPS_DIR, META_DIR, JUPYTER_DIR, 'js')) execute(cmd=['npm', 'run', 'build-dev' if development else 'build'], - cwd=os.path.join(DEPS_DIR, JUPYTER_DIR, 'js')) + cwd=os.path.join(DEPS_DIR, META_DIR, JUPYTER_DIR, 'js')) execute(cmd=['jupyter', 'nbextension', 'install', '--py', '--symlink', '--sys-prefix', 'jupyter_geppetto']) execute(cmd=['jupyter', 'nbextension', 'enable', '--py', '--sys-prefix', 'jupyter_geppetto']) @@ -190,8 +191,20 @@ def main(netpyne_branch, workspace_branch, pygeppetto_branch=None, jupyter_geppe cprint("Installing client packages") if not skipNpm: - execute(cmd=['npm', 'install' if development else 'ci'], cwd=WEBAPP_DIR) - execute(cmd=['npm', 'run', 'build-dev'], cwd=WEBAPP_DIR) + if development: + # install geppetto meta + if os.path.exists(os.path.join(WEBAPP_DIR, '.yalc')): + execute(cmd=['ln', '-s', os.path.expanduser('~') + '/.yalc', '.yalc'], cwd=WEBAPP_DIR) + execute(cmd=['ls'], cwd=WEBAPP_DIR) + execute(cmd=['bash', 'geppetto_ui.sh'], cwd=WEBAPP_DIR) + execute(cmd=['yarn'], cwd=WEBAPP_DIR) + execute(cmd=['yarn', 'run', 'build-dev'], cwd=WEBAPP_DIR) + execute(cmd=['cp', 'package.bak', 'package.json'], cwd=WEBAPP_DIR) + else: + # install jupyter geppetto + cprint("Installing geppetto ui, client and core dependecies") + execute(cmd=['yarn', 'install', '--frozen-lockfile'], cwd=WEBAPP_DIR) + execute(cmd=['yarn', 'run', 'build'], cwd=WEBAPP_DIR) if __name__ == "__main__": @@ -216,20 +229,15 @@ def main(netpyne_branch, workspace_branch, pygeppetto_branch=None, jupyter_geppe default=os.getenv('WORKSPACE_VERSION', 'master'), help='Specify workspace branch or tag.') - parser.add_argument('--pygeppetto', '-vp', dest='pygeppetto_version', action="store", - default=os.getenv('PYGEPPETTO_VERSION', 'development'), + parser.add_argument('--geppetto', '-vp', dest='geppetto_version', action="store", + default=os.getenv('GEPPETTO_VERSION', 'development'), help='Specify Pygeppetto library branch or tag (only for dev build).') - parser.add_argument('--jupyter_geppetto', '-vj', dest='jupyter_geppetto_version', action="store", - default=os.getenv('JUPYTER_GEPPETTO_VERSION', 'development'), - help='Specify Jupyter Geppetto library branch or tag (only for dev build).') - args = parser.parse_args(sys.argv[1:]) print('Install arguments:\n', args) main(skipNpm=args.skipNpm, skipTest=args.skipTest, development=args.development, netpyne_branch=args.netpyne_version, workspace_branch=args.workspace_version, - pygeppetto_branch=args.pygeppetto_version, - jupyter_geppetto_branch=args.jupyter_geppetto_version + geppetto_branch=args.geppetto_version, ) diff --git a/webapp/Main.js b/webapp/Main.js index c48f0ca9..422d4a6f 100644 --- a/webapp/Main.js +++ b/webapp/Main.js @@ -1,39 +1,44 @@ -global.jQuery = require('jquery'); -global.GEPPETTO_CONFIGURATION = require('./GeppettoConfiguration.json'); - -jQuery(() => { - require('geppetto-client-initialization'); - const ReactDOM = require('react-dom'); - const React = require('react'); - const { MuiThemeProvider } = require('@material-ui/core/styles'); - const { NetPyNE } = require('./components'); - - const theme = require('./theme').default; - - const { Provider } = require('react-redux'); - const configureStore = require('./redux/store').default; - - require('./css/netpyne.less'); - require('./css/material.less'); - require('./css/traceback.less'); - require('./css/flexlayout.less'); - require('./css/tree.less'); +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Provider } from 'react-redux'; +import { MuiThemeProvider } from '@material-ui/core/styles'; +import * as Sentry from '@sentry/react'; +import { Integrations } from '@sentry/tracing'; +import { NetPyNE } from './components'; +import theme from './theme'; +import store from './redux/store'; +import '@metacell/geppetto-meta-ui/flex-layout/style/dark.scss'; +import { CaptureConsole } from "@sentry/integrations"; - const store = configureStore(); - - ReactDOM.render( -
- - - - - - -
, - document.querySelector('#mainContainer'), - ); - - GEPPETTO.G.setIdleTimeOut(-1); - GEPPETTO.Resources.COLORS.DEFAULT = '#6f54aa'; - GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, 'Initialising NetPyNE'); +global.GEPPETTO_CONFIGURATION = require('./GeppettoConfiguration.json'); +const { initGeppetto } = require('@metacell/geppetto-meta-client/GEPPETTO'); + +Sentry.init({ + dsn: "https://d8bf7e40eec34cb9891f6dd8207b5e83@sentry.metacell.us/6", + integrations: [ + new CaptureConsole({ + levels: ['error'] + }) + ], + tracesSampleRate: 1.0 }); + +initGeppetto(); +require('./css/netpyne.less'); +require('./css/material.less'); +require('./css/traceback.less'); +require('./css/flexlayout.less'); +require('./css/tree.less'); + +ReactDOM.render( +
+ + + + + +
, + document.querySelector('#mainContainer'), +); + +GEPPETTO.Resources.COLORS.DEFAULT = '#6f54aa'; diff --git a/webapp/Utils.js b/webapp/Utils.js index 3b6adaf5..29b29647 100644 --- a/webapp/Utils.js +++ b/webapp/Utils.js @@ -1,7 +1,9 @@ +import * as Sentry from '@sentry/react'; + import { execPythonMessage, evalPythonMessage, -} from '@geppettoengine/geppetto-client/js/communication/geppettoJupyter/GeppettoJupyterUtils'; +} from './components/general/GeppettoJupyterUtils'; const Utils = { @@ -22,6 +24,11 @@ const Utils = { return id; }, + captureSentryException (e) { + Sentry.captureException(e); + console.error(e); + }, + /** * Retrieves the metadata object for the passed `key`. * diff --git a/webapp/babel.config.js b/webapp/babel.config.js index e7772fc3..0ba8b437 100644 --- a/webapp/babel.config.js +++ b/webapp/babel.config.js @@ -4,6 +4,8 @@ module.exports = { '@babel/preset-env', { targets: { node: 'current' } }, ], + '@babel/preset-typescript', + '@babel/preset-react', ], plugins: [ '@babel/plugin-proposal-class-properties', diff --git a/webapp/components/NetPyNE.js b/webapp/components/NetPyNE.js index fb49cef9..5741361e 100644 --- a/webapp/components/NetPyNE.js +++ b/webapp/components/NetPyNE.js @@ -11,6 +11,7 @@ import { LaunchDialog, } from 'netpyne/components'; +import * as GeppettoActions from '@metacell/geppetto-meta-client/common/actions'; import Utils from '../Utils'; import { EDIT_WIDGETS } from '../constants'; @@ -52,7 +53,6 @@ class NetPyNE extends React.Component { } = this.props; setDefaultWidgets(); - GEPPETTO.on('jupyter_geppetto_extension_ready', (data) => { const project = { id: 1, @@ -63,8 +63,10 @@ class NetPyNE extends React.Component { status: 'DESIGN', }], }; + // to move to redux action, if not working create tech debt card and we do it later. GEPPETTO.Manager.loadProject(project, false); - GEPPETTO.Manager.loadExperiment(1, [], []); + // to remove the experiment. + // GEPPETTO.Manager.loadExperiment(1, [], []); let responded = false; Utils.execPythonMessage('from netpyne_ui.netpyne_geppetto import netpyne_geppetto'); @@ -77,6 +79,7 @@ class NetPyNE extends React.Component { this.addMetadataToWindow(metadata); setWidgets(EDIT_WIDGETS); modelLoaded(); + // GeppettoActions.modelLoaded(); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); setInterval(getExperiments, EXPERIMENT_POLL_INTERVAL); diff --git a/webapp/components/definition/cellRules/NetPyNECellRules.js b/webapp/components/definition/cellRules/NetPyNECellRules.js index 52f9549b..7071a2fd 100644 --- a/webapp/components/definition/cellRules/NetPyNECellRules.js +++ b/webapp/components/definition/cellRules/NetPyNECellRules.js @@ -484,7 +484,7 @@ export default class NetPyNECellRules extends React.Component { if (page === 'main') { event.preventDefault(); this.setState({ anchorEl: event.currentTarget }); - // this.handleNewCellRule({ 'CellType': { 'conds':{}, 'secs':{} } }); + // this.handleNewCellRule({ CellType: { conds: {}, secs: {} } }); } else if (page === 'sections') { this.handleNewSection({ Section: { diff --git a/webapp/components/definition/stimulationTargets/NetPyNEStimulationTarget.js b/webapp/components/definition/stimulationTargets/NetPyNEStimulationTarget.js index 590a1828..6af83d98 100644 --- a/webapp/components/definition/stimulationTargets/NetPyNEStimulationTarget.js +++ b/webapp/components/definition/stimulationTargets/NetPyNEStimulationTarget.js @@ -44,6 +44,17 @@ export default class NetPyNEStimulationTarget extends React.Component { this.isStimSourceTypeNetStim(); } + // eslint-disable-next-line camelcase + UNSAFE_componentWillReceiveProps (nextProps) { + if (this.state.currentName != nextProps.name) { + this.setState({ + currentName: nextProps.name, + selectedIndex: 0, + sectionId: 'General', + }); + } + } + componentDidUpdate (prevProps, prevState) { if ( this.props.name !== prevProps.name @@ -91,15 +102,7 @@ export default class NetPyNEStimulationTarget extends React.Component { } } - UNSAFE_componentWillReceiveProps (nextProps) { - if (this.state.currentName != nextProps.name) { - this.setState({ - currentName: nextProps.name, - selectedIndex: 0, - sectionId: 'General', - }); - } - } + handleRenameChange = (event) => { const storedValue = this.props.name; diff --git a/webapp/components/drawer/Drawer.js b/webapp/components/drawer/Drawer.js index bb1d0bd6..13364886 100644 --- a/webapp/components/drawer/Drawer.js +++ b/webapp/components/drawer/Drawer.js @@ -9,7 +9,8 @@ import ListItemText from '@material-ui/core/ListItemText'; import Typography from '@material-ui/core/Typography'; import Divider from '@material-ui/core/Divider'; import { withStyles } from '@material-ui/core/styles'; -import { WidgetStatus } from '../layout/model'; +import { getLayoutManagerInstance } from '@metacell/geppetto-meta-client/common/layout/LayoutManager'; +import { WidgetStatus } from '@metacell/geppetto-meta-client/common/layout/model'; import { EDIT_WIDGETS, DEFAULT_NETWORK_WIDGETS, TOP_PANEL, TOOLS_LIST, SIDEBAR_HEADINGS, @@ -91,6 +92,7 @@ const drawerStyles = ({ spacing }) => ({ const DrawerList = ({ newWidget, editMode, + widgets, activateWidget, updateWidget, classes, @@ -101,8 +103,7 @@ const DrawerList = ({ width: expand ? drawerOpenWidth : drawerCloseWidth, expand, }); - const layoutManager = require('../layout/LayoutManager') - .getLayoutManagerInstance(); + const layoutManager = getLayoutManagerInstance(); function createOrFocusWidget (widgetId) { const widget = { ...layoutManager.getWidget(widgetId) }; @@ -136,7 +137,8 @@ const DrawerList = ({ function getMenu () { const [modelDrawerItems, toolsDrawerItems] = [[], []]; - layoutManager.getWidgets().sort((w1, w2) => w1.pos - w2.pos).filter((widget) => { + // eslint-disable-next-line array-callback-return + Object.values(widgets).sort((w1, w2) => w1.pos - w2.pos).filter((widget) => { widget.specification !== TOOLS_LIST ? modelDrawerItems.push(widget) : toolsDrawerItems.push(widget); }); @@ -147,8 +149,8 @@ const DrawerList = ({ name, id, }) => { - const widget = layoutManager.getWidget(id); - const status = layoutManager.getWidgetStatus(id); + const widget = widgets[id]; + const status = widget.status; return ( { const [experimentName, setExperimentName] = useState(''); const [experimentNameError, setExperimentNameError] = useState(''); const [selectionParams, setSelectionParams] = useState([]); + const [trialNumberErrorDialogOpen, setTrialNumberErrorDialogOpen] = useState({ condition: false, number: 1 }); // Existing Experiment. const [experiment, setExperiment] = useState(null); const experiments = useSelector((state) => state.experiments.experiments); + let numberOfTrials = 1; + const validateParameter = (param) => { let updatedParam = param; if (param.type === LIST) { @@ -339,7 +344,19 @@ const ExperimentEdit = (props) => { params, }; - if (editState) { + numberOfTrials = 1; + + params.forEach((param) => { + if (param.type === LIST) { + numberOfTrials *= param.values.length; + } else if (param.type === RANGE) { + numberOfTrials *= Math.ceil((param.max - param.min) / param.step); + } + }); + + if (numberOfTrials > MAX_TRIALS) { + setTrialNumberErrorDialogOpen({ condition: true, number: numberOfTrials }); + } else if (editState) { ExperimentsApi.editExperiment(experiment?.name, newExperimentDetails) .then(() => { setView(EXPERIMENT_VIEWS.list); @@ -467,34 +484,35 @@ const ExperimentEdit = (props) => { }; return ( - - - - setView(EXPERIMENT_VIEWS.list)} /> - {!editState ? 'New Experiment' : 'Edit Experiment'} - - -
- setExperimentNameInfo(e.target.value)} - error={experimentNameError !== ''} - helperText={experimentNameError} - /> - - - + <> + + + + setView(EXPERIMENT_VIEWS.list)} /> + {!editState ? 'New Experiment' : 'Edit Experiment'} - - - - - Parameters + +
+ setExperimentNameInfo(e.target.value)} + error={experimentNameError !== ''} + helperText={experimentNameError} + /> + + + - {groupParameters.length > 0 && ( +
+ + + + Parameters + + {groupParameters.length > 0 && ( Grouped Parameters @@ -512,8 +530,8 @@ const ExperimentEdit = (props) => { )} - )} - {selectionParams.length > 0 && ( + )} + {selectionParams.length > 0 && ( {parameters.map((parameter, index) => ( ParameterRow(parameter, index, handleParamSelection, handleChange, @@ -521,34 +539,44 @@ const ExperimentEdit = (props) => { removeParameter, selectionParams, classes) ))} - )} + )} + + + + + + Add parameter +
- - - - Add parameter - - -
- - - - - + + + + + + - -
+
+ setTrialNumberErrorDialogOpen({ condition: false, number: 1 })} + textForDialog={{ + heading: 'Error - Number of conditions is too large', + content: `Please change your exploration parameters to + reduce the number of experimental conditions to less than 100. Last number of conditions: ${trialNumberErrorDialogOpen.number}`, + }} + /> + ); }; diff --git a/webapp/components/experiments/ExperimentView.js b/webapp/components/experiments/ExperimentView.js index f2dde7d0..ebc7718b 100644 --- a/webapp/components/experiments/ExperimentView.js +++ b/webapp/components/experiments/ExperimentView.js @@ -269,7 +269,7 @@ function EnhancedTableHead (props) { id: 'name', numeric: false, disablePadding: true, - label: 'CONDITION NAME', + label: 'EXPERIMENTAL CONDITIONS', }, ...paramHeaders, ]; @@ -564,7 +564,7 @@ const ExperimentView = (props) => { experimentFinished={experimentFinished} /> - {stableSort(filteredRows, getComparator(order, orderBy)) + {stableSort(filteredRows, getComparator(order, 'indices')) .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) .map((row) => ( diff --git a/webapp/components/general/FileBrowser.js b/webapp/components/general/FileBrowser.js index cc45be66..c9df9fa9 100644 --- a/webapp/components/general/FileBrowser.js +++ b/webapp/components/general/FileBrowser.js @@ -1,5 +1,4 @@ import React from 'react'; -import Tree from '@geppettoengine/geppetto-client/js/components/interface/tree/Tree'; import Button from '@material-ui/core/Button'; import { changeNodeAtPath, walk } from 'react-sortable-tree'; import Dialog from '@material-ui/core/Dialog'; @@ -10,6 +9,7 @@ import DialogContent from '@material-ui/core/DialogContent'; import { Tooltip } from 'netpyne/components'; import IconButton from '@material-ui/core/IconButton'; import Icon from '@material-ui/core/Icon'; +import Tree from './tree/Tree'; import Utils from '../../Utils'; import { bgLight, fontColor } from '../../theme'; diff --git a/webapp/components/general/GeppettoJupyterUtils.js b/webapp/components/general/GeppettoJupyterUtils.js index 114a9f2c..f2b9e5ee 100644 --- a/webapp/components/general/GeppettoJupyterUtils.js +++ b/webapp/components/general/GeppettoJupyterUtils.js @@ -2,15 +2,14 @@ const handle_output = function (data) { // data is the object passed to the callback from the kernel execution switch (data.msg_type) { case 'error': - GEPPETTO.CommandController.log('ERROR while executing a Python command:'); - GEPPETTO.CommandController.log(data.content.evalue.trim()); + console.log('ERROR while executing a Python command:'); + console.log(data.content.evalue.trim()); console.error('ERROR while executing a Python command:'); console.error(data.content.traceback); GEPPETTO.trigger(GEPPETTO.Events.Error_while_exec_python_command, data.content); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); break; case 'execute_result': - GEPPETTO.CommandController.log(data.content.data['text/plain'].trim(), true); try { var response = JSON.parse(data.content.data['text/plain'].replace(/^'(.*)'$/, '$1')); } catch (error) { @@ -22,18 +21,17 @@ const handle_output = function (data) { // FIXME break; default: - GEPPETTO.CommandController.log(data.content.text.trim(), true); + console.log(data.content.text.trim(), true); } }; const execPythonMessage = function (command, callback = handle_output) { - GEPPETTO.CommandController.log(`Executing Python command: ${command}`, true); const { kernel } = IPython.notebook; const messageID = kernel.execute(command, { iopub: { output: callback } }, { silent: false, stop_on_error: true, store_history: true }); return new Promise((resolve, reject) => GEPPETTO.on(GEPPETTO.Events.Receive_Python_Message, (data) => { - if (data.id == messageID) { - resolve(data.response); + if (data.data.id == messageID) { + resolve(data.data.response); } })); }; diff --git a/webapp/components/general/HTMLViewer.js b/webapp/components/general/HTMLViewer.js index 63738554..d5f32b5e 100644 --- a/webapp/components/general/HTMLViewer.js +++ b/webapp/components/general/HTMLViewer.js @@ -1,7 +1,6 @@ import React, { Component, createRef } from 'react'; -import HTMLViewer - from '@geppettoengine/geppetto-client/js/components/interface/htmlViewer/HTMLViewer'; +import HTMLViewer from '@metacell/geppetto-meta-ui/html-viewer/HTMLViewer'; import { withStyles } from '@material-ui/core/styles'; diff --git a/webapp/components/general/NetPyNEPythonConsole.js b/webapp/components/general/NetPyNEPythonConsole.js index ffe17bae..b8f4de25 100644 --- a/webapp/components/general/NetPyNEPythonConsole.js +++ b/webapp/components/general/NetPyNEPythonConsole.js @@ -1,13 +1,15 @@ import React, { Component } from 'react'; -import PythonConsole - from '@geppettoengine/geppetto-client/js/components/interface/pythonConsole/PythonConsole'; +import { PythonConsole } from '@metacell/geppetto-meta-ui/python-console/PythonConsole'; export class NetPyNEPythonConsole extends Component { componentDidMount () { } - shouldComponentUpdate () { + shouldComponentUpdate (nextProps) { + if (this.props.extensionLoaded !== nextProps.extensionLoaded) { + return true; + } return false; } @@ -16,7 +18,7 @@ export class NetPyNEPythonConsole extends Component { } render () { - return ; + return ; } } diff --git a/webapp/components/general/PlotViewer.js b/webapp/components/general/PlotViewer.js new file mode 100644 index 00000000..dd62144c --- /dev/null +++ b/webapp/components/general/PlotViewer.js @@ -0,0 +1,32 @@ +import React from 'react'; +import HTMLViewer from './HTMLViewer'; + +const PlotViewer = ({ key, id, method }) => { + const data = window.plotCache[id]; + + if (method.plotMethod.startsWith('iplot')) { + return ( +
+