Skip to content

Commit e9f7d5e

Browse files
Merge pull request #738 from MetaCell/release/1.0.0
Release/1.0.0
2 parents 0668bb1 + a259bce commit e9f7d5e

72 files changed

Lines changed: 4797 additions & 2542 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Dockerfile

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:13.14 as jsbuild
1+
FROM node:14.21.3-bullseye as jsbuild
22

33
WORKDIR /app
44

@@ -18,10 +18,10 @@ RUN rm -Rf node_modules/*
1818
FROM jupyter/base-notebook:hub-1.5.0
1919
ENV NB_UID=jovyan
2020
ENV FOLDER=netpyne
21-
ARG GEPPETTO_VERSION=development
2221
ARG BUILD_ARGS=""
23-
ARG NETPYNE_VERSION=master
2422
ARG WORKSPACE_VERSION=master
23+
# ARG GEPPETTO_VERSION=development
24+
# ARG NETPYNE_VERSION=master
2525

2626
ENV FOLDER=/home/jovyan/work/NetPyNE-UI
2727

@@ -32,10 +32,9 @@ RUN apt-get update -qq &&\
3232
apt-get install python3-tk vim nano unzip git make libtool g++ -qq pkg-config libfreetype6-dev libpng-dev libopenmpi-dev openjdk-11-jre-headless -y -y
3333
RUN conda install python=3.7 -y
3434

35-
3635
WORKDIR $FOLDER
3736
COPY --chown=1000:1000 requirements.txt requirements.txt
38-
RUN pip install -r requirements.txt --no-cache-dir --prefer-binary
37+
RUN --mount=type=cache,target=/root/.cache python -m pip install --upgrade pip && pip install -r requirements.txt --prefer-binary
3938

4039
COPY --chown=$NB_UID:1000 . .
4140
COPY --from=jsbuild --chown=$NB_UID:1000 /app webapp
@@ -46,7 +45,7 @@ RUN jupyter nbextension enable --py --sys-prefix jupyter_geppetto
4645
RUN jupyter nbextension enable --py --sys-prefix widgetsnbextension
4746
RUN jupyter serverextension enable --py --sys-prefix jupyter_geppetto
4847

49-
RUN python utilities/install.py ${BUILD_ARGS} --geppetto ${GEPPETTO_VERSION} --netpyne $NETPYNE_VERSION --workspace WORKSPACE_VERSION --npm-skip
48+
RUN python utilities/install.py ${BUILD_ARGS} --workspace $WORKSPACE_VERSION
5049

5150
RUN jupyter labextension disable @jupyterlab/hub-extension
5251

@@ -58,7 +57,8 @@ ENV NEURON_HOME=/opt/conda
5857
# For lfpykit 0.4
5958
# RUN wget -P $(pip show LFPykit | grep "Location:" | awk '{print $2"/lfpykit"}') https://www.parralab.org/nyhead/sa_nyhead.mat
6059
# For lpfykit 0.5
61-
RUN wget --no-check-certificate -P ${FOLDER}/workspace https://www.parralab.org/nyhead/sa_nyhead.mat
60+
ENV NP_LFPYKIT_HEAD_FILE=/home/jovyan/nyhead.mat
61+
RUN wget --no-check-certificate -O $NP_LFPYKIT_HEAD_FILE https://www.parralab.org/nyhead/sa_nyhead.mat
6262

6363
USER $NB_UID
6464

netpyne_ui/__init__.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,23 @@
1111
"https://d8bf7e40eec34cb9891f6dd8207b5e83@sentry.metacell.us/6"
1212
)
1313

14+
HEAD_MODEL_FILE = os.getenv("NP_LFPYKIT_HEAD_FILE", "sa_nyhead.mat")
15+
1416
def init_eeg():
1517
import sys
1618
from netpyne_ui.constants import HERE
1719
# FIXES library asking for input to download
1820
sys.stdin = open(os.path.join(HERE, "resources/stdin.txt"),'r')
1921
from lfpykit.eegmegcalc import NYHeadModel
2022
try:
21-
NYHeadModel() # Downloads the model if does not exist
23+
NYHeadModel(HEAD_MODEL_FILE) # Downloads the model if does not exist
2224
except:
2325
logging.error("Error initializing the EEG head model", exc_info=True)
2426

25-
from multiprocessing import Process
26-
thread = Process(target = init_eeg)
27-
thread.start()
28-
27+
if not os.path.exists(HEAD_MODEL_FILE):
28+
from multiprocessing import Process
29+
thread = Process(target = init_eeg)
30+
thread.start()
2931

3032
RouteManager.add_controller(api.NetPyNEController)
3133

netpyne_ui/mod_utils.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import logging
12
import shutil
23
import subprocess
34
import os
@@ -26,19 +27,30 @@ def is_loaded_mechanisms():
2627
return True
2728

2829

29-
def loadModMechFiles(compileMod, modFolder):
30+
def loadModMechFiles(compileMod, modFolder, forceRecompile=False):
3031
# Create Symbolic link
31-
if compileMod:
32-
modPath = os.path.join(str(modFolder), "x86_64")
3332

34-
if os.path.exists(modPath):
35-
shutil.rmtree(modPath)
36-
37-
os.chdir(modFolder)
38-
subprocess.call(["nrnivmodl"])
39-
os.chdir('..')
40-
41-
try:
42-
neuron.load_mechanisms(str(modFolder))
43-
except:
44-
raise
33+
try:
34+
35+
owd = os.getcwd()
36+
if compileMod:
37+
38+
compiledModPath = os.path.join(str(modFolder), "x86_64")
39+
40+
if os.path.exists(compiledModPath) and forceRecompile:
41+
logging.info("Forcing mod files to recompile in %s" % modFolder)
42+
shutil.rmtree(compiledModPath)
43+
44+
if not os.path.exists(compiledModPath):
45+
logging.info("Compiling mod files in %s" % modFolder)
46+
os.chdir(modFolder)
47+
subprocess.call(["nrnivmodl"])
48+
os.chdir('..')
49+
50+
try:
51+
neuron.load_mechanisms(str(modFolder))
52+
except:
53+
logging.exception("Error loading mechanisms")
54+
raise
55+
finally:
56+
os.chdir(owd)

netpyne_ui/netpyne_geppetto.py

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import json
1414
import logging
1515
import os
16+
from pathlib import Path
1617
import pprint
1718
import re
1819
import sys
@@ -45,7 +46,6 @@
4546

4647
os.chdir(constants.NETPYNE_WORKDIR_PATH)
4748

48-
neuron.nrn_dll_loaded.append(os.path.join(NETPYNE_WORKDIR_PATH, 'mod')) # Avoids to load workspace modfiles twice
4949

5050
class NetPyNEGeppetto:
5151

@@ -79,17 +79,6 @@ def __init__(self):
7979
if not simulations.local.is_running():
8080
[experiments.set_to_error(e) for e in running_exps]
8181

82-
# sys.stdin = open(os.path.join(constants.HERE, "stdin.txt"),'r') # FIXES library asking for input to download -- eg lfpykit models
83-
84-
# from ipykernel import kernelbase
85-
86-
# def raw_input(self, prompt=''):
87-
# return "y"
88-
# kernelbase.Kernel.raw_input = raw_input
89-
90-
# from lfpykit.eegmegcalc import NYHeadModel
91-
# NYHeadModel()
92-
9382
def getData(self):
9483
return {
9584
"metadata": metadata,
@@ -511,7 +500,32 @@ def _create3D_shapes(self, json_path: str):
511500
# Load again because gatherData removed simData
512501
sim.loadSimData(json_path)
513502

503+
def loadFromIndexFile(self, json_path: str):
504+
cfg, netParams = sim.loadModel(json_path, loadMechs=True, ignoreMechAlreadyExistsError=True)
505+
self.simConfig = cfg
506+
self.netParams = netParams
507+
508+
if isinstance(self.netParams, dict):
509+
self.netParams = specs.NetParams(self.netParams)
510+
511+
if isinstance(self.simConfig, dict):
512+
self.simConfig = specs.SimConfig(self.simConfig)
514513

514+
for key, value in self.netParams.cellParams.items():
515+
if hasattr(value, 'todict'):
516+
self.netParams.cellParams[key] = value.todict()
517+
518+
# TODO: when should sim.initialize be called?
519+
# Only on import or better before every simulation or network instantiation?
520+
sim.initialize()
521+
522+
def saveToIndexFile(self, srcPath, dstPath, exportNetParamsAsPython, exportSimConfigAsPython):
523+
sim.saveModel(netParams=self.netParams,
524+
simConfig=self.simConfig,
525+
srcPath=srcPath,
526+
dstPath=dstPath,
527+
exportNetParamsAsPython=exportNetParamsAsPython,
528+
exportSimConfigAsPython=exportSimConfigAsPython)
515529

516530
def importModel(self, modelParameters):
517531
""" Imports a model stored in form of Python files.
@@ -524,7 +538,7 @@ def importModel(self, modelParameters):
524538
# Shouldn't be specific to Import
525539
sim.clearAll()
526540
try:
527-
loadModMechFiles(modelParameters['compileMod'], modelParameters['modFolder'])
541+
loadModMechFiles(modelParameters['compileMod'], modelParameters['modFolder'], modelParameters.get("forceRecompile", True))
528542
except Exception:
529543
message = "Error while importing/compiling mods"
530544
logging.exception(message)
@@ -566,7 +580,7 @@ def importModel(self, modelParameters):
566580
# Only on import or better before every simulation or network instantiation?
567581
sim.initialize()
568582
return utils.getJSONReply()
569-
except Exception:
583+
except:
570584
message = "Error while importing the NetPyNE model"
571585
logging.exception(message)
572586
return utils.getJSONError(message, sys.exc_info())
@@ -575,7 +589,7 @@ def importModel(self, modelParameters):
575589

576590
def importNeuroML(self, modelParameters):
577591
from netpyne_ui.helpers import neuroml
578-
592+
579593

580594
try:
581595
# Get Current dir
@@ -584,9 +598,9 @@ def importNeuroML(self, modelParameters):
584598
with redirect_stdout(sys.__stdout__):
585599
# NetParams
586600
filename = str(modelParameters["fileName"])
587-
601+
588602
json_fname = neuroml.convertNeuroML2(filename, compileMod=modelParameters["compileMod"])
589-
603+
590604
return self.loadModel(args=dict(
591605
compileMod=True,
592606
modFolder=os.path.dirname(json_fname),
@@ -605,7 +619,7 @@ def importNeuroML(self, modelParameters):
605619

606620
def importLEMS(self, modelParameters):
607621
from netpyne_ui.helpers import neuroml
608-
622+
609623

610624
try:
611625
# Get Current dir
@@ -614,7 +628,7 @@ def importLEMS(self, modelParameters):
614628
with redirect_stdout(sys.__stdout__):
615629
# NetParams
616630
filename = str(modelParameters["fileName"])
617-
631+
618632
json_fname = neuroml.convertLEMSSimulation(filename)
619633

620634
return self.loadModel(args=dict(
@@ -789,10 +803,21 @@ def getPlotSettings(self, plot_name):
789803
return self.simConfig.analysis[plot_name]
790804
return {}
791805

792-
def getDirList(self, dir=None, onlyDirs=False, filterFiles=False):
793-
# Get Current dir
806+
def checkFileExists(self, path):
807+
path = Path(path or '')
808+
return path.exists()
809+
810+
def getFullPath(self, dir, subDir):
794811
if dir is None or dir == '':
795-
dir = os.path.join(os.getcwd(), constants.NETPYNE_WORKDIR_PATH)
812+
base = constants.NETPYNE_WORKDIR_PATH
813+
if subDir:
814+
base = os.path.join(base, subDir)
815+
dir = os.path.join(os.getcwd(), base)
816+
return dir
817+
818+
def getDirList(self, dir=None, onlyDirs=False, filterFiles=False, subDir=None):
819+
# Get Current dir
820+
dir = self.getFullPath(dir, subDir)
796821
dir_list = []
797822
file_list = []
798823
for f in sorted(os.listdir(str(dir)), key=str.lower):
@@ -810,6 +835,13 @@ def checkAvailablePlots(self):
810835
def getPlot(self, plotName, LFPflavour, theme='gui'):
811836
try:
812837
with redirect_stdout(sys.__stdout__):
838+
availablePlots = self.checkAvailablePlots()
839+
checkCondition = availablePlots.get(plotName.replace('iplot', 'plot'), False)
840+
841+
if checkCondition is False:
842+
logging.info("Plot " + plotName + " not available")
843+
return -1
844+
813845
args = self.getPlotSettings(plotName)
814846
if LFPflavour:
815847
args['plots'] = [LFPflavour]
@@ -899,10 +931,10 @@ def getAvailableCellTypes(self):
899931
cell_types.add(p)
900932
return sorted(cell_types)
901933

902-
def getAvailableRxDSections(self, selectedRegion):
934+
def getAvailableRxDSections(self, selectedRegion = None):
903935
sections = set([])
904936
sections.add('all')
905-
if selectedRegion in self.netParams.rxdParams.regions and self.netParams.rxdParams.regions[selectedRegion].get('cells'):
937+
if selectedRegion and selectedRegion in self.netParams.rxdParams.regions and self.netParams.rxdParams.regions[selectedRegion].get('cells'):
906938
if 'all' in self.netParams.rxdParams.regions[selectedRegion]['cells']:
907939
for cellRule in self.netParams.cellParams:
908940
for cellSect in self.netParams.cellParams[cellRule]['secs']:

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jupyterthemes==0.20.0
3434
kiwisolver==1.2.0
3535
lesscpy==0.14.0
3636
libNeuroML==0.4.0
37-
lfpykit==0.5
37+
lfpykit==0.5.1
3838
lxml==4.5.1
3939
Mako==1.1.0
4040
MarkupSafe==1.1.1
@@ -44,7 +44,7 @@ mistune==0.8.4
4444
multimethod==1.3
4545
nbconvert==5.6.1
4646
nbformat==5.0.6
47-
netpyne==1.0.3.1
47+
netpyne==1.0.4.1
4848
NEURON==8.2.2
4949
numpy==1.18.5
5050
oauthlib==3.0.1

setup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
setuptools.setup(
2020
name="netpyne_ui",
21-
version="0.9.1",
21+
version="1.0.0",
2222
url="https://github.com/MetaCell/NetPyNE-UI",
2323
author="MetaCell",
2424
author_email="info@metacell.us",
@@ -41,8 +41,8 @@
4141
],
4242
install_requires=[
4343
'jupyter-geppetto>=1.0.0',
44-
'NEURON>=8.0.2',
45-
'netpyne>=1.0.3.1',
44+
'NEURON>=8.2.2',
45+
'netpyne>=1.0.4.1',
4646
'neuromllite==0.5.1',
4747
'pyNeuroML>=0.7.1',
4848
'sentry_sdk>=1.5.2',

tests/frontend/e2e/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "NetPyNe UI tests",
55
"license": "unlicensed",
66
"scripts": {
7-
"test": "jest --verbose ",
7+
"test": "jest --verbose",
88
"EEG_Dipole_test": "jest --verbose EEG_and_Dipole_Tut#1 ",
99
"Experiment_Manager_test": "jest --verbose ExperimentManager_Tut#1 ",
1010
"Tutorial_1_test":"jest --verbose Tut#1_smoke ",

tests/frontend/e2e/tests/ControlPanel.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as selectors from './selectors'
99

1010

1111
//PAGE INFO:
12-
const baseURL = process.env.url || 'https://stage.netpyne.metacell.us/'
12+
const baseURL = process.env.url || 'https://test.netpyne.metacell.us/'
1313
const PAGE_WAIT = 3000;
1414
const TIMEOUT = 60000;
1515

tests/frontend/e2e/tests/EEG_and_Dipole_Tut#1.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as selectors from './selectors'
99

1010

1111
//PAGE INFO:
12-
const baseURL = process.env.url || 'https://stage.netpyne.metacell.us/'
12+
const baseURL = process.env.url || 'https://test.netpyne.metacell.us/'
1313
const PAGE_WAIT = 3000;
1414
const TIMEOUT = 60000;
1515

@@ -60,7 +60,7 @@ beforeAll(async () => {
6060

6161
describe('EEG and Dipole Plot Test using Tutorial#1', () => {
6262

63-
it('Open new page', async () => {
63+
it.skip('Open new page', async () => {
6464

6565
console.log('Opening a new NetPyNE page')
6666

@@ -120,7 +120,7 @@ describe('EEG and Dipole Plot Test using Tutorial#1', () => {
120120
await page.waitForSelector(selectors.TRACES_TO_RECORD_SELECTOR)
121121
await page.waitForTimeout(PAGE_WAIT)
122122
await page.waitForSelector(selectors.DIPOLE_LFPYKIT_SELECTOR)
123-
await expect(page).toClick(selectors.DIPOLE_LFPYKIT_SELECTOR)
123+
// await expect(page).toClick(selectors.DIPOLE_LFPYKIT_SELECTOR)
124124
await page.waitForTimeout(PAGE_WAIT)
125125
await page.click(selectors.DIPOLE_LFPYKIT_SELECTOR)
126126
await page.waitForTimeout(PAGE_WAIT)

tests/frontend/e2e/tests/ExperimentManager_Tut#1.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as selectors from './selectors'
99

1010

1111
//PAGE INFO:
12-
const baseURL = process.env.url || 'https://stage.netpyne.metacell.us/'
12+
const baseURL = process.env.url || 'https://test.netpyne.metacell.us/'
1313
const PAGE_WAIT = 3000;
1414
const TIMEOUT = 60000;
1515

0 commit comments

Comments
 (0)