From 93c7d8a6d1a2cddb47e2d87a859705768c690c5e Mon Sep 17 00:00:00 2001 From: Adrien Berchet Date: Wed, 15 Mar 2023 10:53:17 +0100 Subject: [PATCH] NeuroMorpho: Can use neuron names as file names (#27) --- morphapi/api/mouselight.py | 4 ++-- morphapi/api/neuromorphorg.py | 22 ++++++++++++++++---- morphapi/paths_manager.py | 2 +- tests/test_download.py | 39 ++++++++++++++++++++++++++++------- 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/morphapi/api/mouselight.py b/morphapi/api/mouselight.py index 353e1f4..b65e076 100644 --- a/morphapi/api/mouselight.py +++ b/morphapi/api/mouselight.py @@ -17,9 +17,9 @@ """ - Collections of functions to query http://ml-neuronbrowser.janelia.org/ and get data about either the status of the API, + Collections of functions to query https://ml-neuronbrowser.janelia.org/ and get data about either the status of the API, the brain regions or the neurons available. - Queries are sent by sending POST requests to http://ml-neuronbrowser.janelia.org/graphql + Queries are sent by sending POST requests to https://ml-neuronbrowser.janelia.org/graphql with a string query. """ diff --git a/morphapi/api/neuromorphorg.py b/morphapi/api/neuromorphorg.py index 20e9b60..ab672fa 100644 --- a/morphapi/api/neuromorphorg.py +++ b/morphapi/api/neuromorphorg.py @@ -129,16 +129,25 @@ def build_filepath(self, neuron_id): return os.path.join(self.neuromorphorg_cache, f"{neuron_id}.swc") def download_neurons( - self, neurons, _name=None, load_neurons=True, **kwargs + self, + neurons, + _name=None, + load_neurons=True, + use_neuron_names=False, + **kwargs, ): """ Downloads neuronal morphological data and saves it to .swc files. It then returns a list of Neuron instances with morphological data for each neuron. :param neurons: list of neurons metadata (as returned by one of the functions - used to fetch metadata) + used to fetch metadata) :param _name: used internally to save cached neurons with a different prefix when the - class is used to download neurons for other APIs + class is used to download neurons for other APIs + :param load_neurons: if set to True, the neurons are loaded into a + `morphapi.morphology.morphology.Neuron` object and returned + :param use_neuron_names: if set to True, the filenames use the names of the neurons instead + of their IDs """ if not isinstance(neurons, (list, tuple)): neurons = [neurons] @@ -154,7 +163,12 @@ class is used to download neurons for other APIs except KeyError: pass - filepath = self.build_filepath(neuron["neuron_id"]) + if use_neuron_names: + filepath = self.build_filepath( + neuron.get("neuron_name", neuron["neuron_id"]) + ) + else: + filepath = self.build_filepath(neuron["neuron_id"]) load_current_neuron = load_neurons if not os.path.isfile(filepath): diff --git a/morphapi/paths_manager.py b/morphapi/paths_manager.py index 332d6a6..7116482 100644 --- a/morphapi/paths_manager.py +++ b/morphapi/paths_manager.py @@ -31,7 +31,7 @@ def __init__(self, base_dir=None, **kwargs): if base_dir is None: self.base_dir = Path.home() / ".morphapi" else: - self.base_dir = base_dir + self.base_dir = Path(base_dir) self.base_dir.mkdir(exist_ok=True) diff --git a/tests/test_download.py b/tests/test_download.py index 66521a4..9cd0a55 100644 --- a/tests/test_download.py +++ b/tests/test_download.py @@ -1,10 +1,14 @@ +from pathlib import Path + from morphapi.api.allenmorphology import AllenMorphology from morphapi.api.mouselight import MouseLightAPI from morphapi.api.neuromorphorg import NeuroMorpOrgAPI -def test_neuromorpho_download(): - api = NeuroMorpOrgAPI() +def test_neuromorpho_download(tmpdir): + api = NeuroMorpOrgAPI(base_dir=tmpdir) + cache_path = Path(api.neuromorphorg_cache) + metadata, _ = api.get_neurons_metadata( size=2, # Can get the metadata for up to 500 neurons at the time species="mouse", @@ -15,15 +19,34 @@ def test_neuromorpho_download(): if len(metadata) != 2: raise ValueError("Incorrect metadata length") + assert len(list(cache_path.iterdir())) == 0 neurons = api.download_neurons(metadata) - if len(neurons) != len(metadata): - raise ValueError + assert len(neurons) == len(metadata) + assert len(list(cache_path.iterdir())) == 2 neurons = [neuron.create_mesh()[1] for neuron in neurons] # Test no load assert api.download_neurons(metadata, load_neurons=False)[0].points is None + assert len(list(cache_path.iterdir())) == 2 + + # Test using neuron names + assert ( + api.download_neurons( + metadata, load_neurons=False, use_neuron_names=True + )[0].points + is None + ) + assert len(list(cache_path.iterdir())) == 4 + + cache_files = list(cache_path.iterdir()) + assert sorted([i.name for i in cache_files]) == [ + "10075.swc", + "10076.swc", + "C1q-Cell7-40x.swc", + "Ctrl-Cell3-40x.swc", + ] # Test failure metadata[0]["neuron_id"] = "BAD ID" @@ -34,8 +57,8 @@ def test_neuromorpho_download(): assert neurons[0].points is None -def test_mouselight_download(): - mlapi = MouseLightAPI() +def test_mouselight_download(tmpdir): + mlapi = MouseLightAPI(base_dir=tmpdir) neurons_metadata = mlapi.fetch_neurons_metadata( filterby="soma", filter_regions=["MOs"] @@ -87,8 +110,8 @@ def test_mouselight_download(): assert filtered_neurons_metadata == filtered_neurons_metadata_atlas -def test_allen_morphology_download(): - am = AllenMorphology() +def test_allen_morphology_download(tmpdir): + am = AllenMorphology(base_dir=tmpdir) # Select some mouse neurons in the primary visual cortex neurons_df = am.neurons.loc[