# Allen Institute Software License - This software license is the 2-clause BSD
# license plus a third clause that prohibits redistribution for commercial
# purposes without further permission.
#
# Copyright 2015-2016. Allen Institute. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Redistributions for commercial purposes are not permitted without the
# Allen Institute's written permission.
# For purposes of this license, commercial purposes is the incorporation of the
# Allen Institute's software into anything for which you will charge fees or
# other compensation. Contact terms@alleninstitute.org for commercial licensing
# opportunities.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
from .rma_api import RmaApi
from ..cache import cacheable
from allensdk.config.manifest import Manifest
from allensdk.api.cache import Cache
from allensdk.deprecated import deprecated
[docs]class CellTypesApi(RmaApi):
NWB_FILE_TYPE = 'NWBDownload'
SWC_FILE_TYPE = '3DNeuronReconstruction'
MARKER_FILE_TYPE = '3DNeuronMarker'
MOUSE = 'Mus musculus'
HUMAN = 'Homo Sapiens'
def __init__(self, base_uri=None):
super(CellTypesApi, self).__init__(base_uri)
[docs] @cacheable()
def list_cells_api(self,
id=None,
require_morphology=False,
require_reconstruction=False,
reporter_status=None,
species=None):
criteria = None
if id:
criteria = "[specimen__id$eq%d]" % id
cells = self.model_query(
'ApiCellTypesSpecimenDetail', criteria=criteria, num_rows='all')
return cells
[docs] @deprecated("please use list_cells_api instead")
def list_cells(self,
id=None,
require_morphology=False,
require_reconstruction=False,
reporter_status=None,
species=None):
"""
Query the API for a list of all cells in the Cell Types Database.
Parameters
----------
id: int
ID of a cell. If not provided returns all matching cells.
require_morphology: boolean
Only return cells that have morphology images.
require_reconstruction: boolean
Only return cells that have morphological reconstructions.
reporter_status: list
Return cells that have a particular cell reporter status.
species: list
Filter for cells that belong to one or more species. If None, return all.
Must be one of [ CellTypesApi.MOUSE, CellTypesApi.HUMAN ].
Returns
-------
list
Meta data for all cells.
"""
if id:
criteria = "[id$eq'%d']" % id
else:
criteria = "[is_cell_specimen$eq'true'],products[name$in'Mouse Cell Types','Human Cell Types'],ephys_result[failed$eqfalse]"
include = ('structure,cortex_layer,donor(transgenic_lines,organism,conditions),specimen_tags,cell_soma_locations,' +
'ephys_features,data_sets,neuron_reconstructions,cell_reporter')
cells = self.model_query(
'Specimen', criteria=criteria, include=include, num_rows='all')
for cell in cells:
# specimen tags
for tag in cell['specimen_tags']:
tag_name, tag_value = tag['name'].split(' - ')
tag_name = tag_name.replace(' ', '_')
cell[tag_name] = tag_value
# morphology and reconstuction
cell['has_reconstruction'] = len(
cell['neuron_reconstructions']) > 0
cell['has_morphology'] = len(cell['data_sets']) > 0
# transgenic line
cell['transgenic_line'] = None
for tl in cell['donor']['transgenic_lines']:
if tl['transgenic_line_type_name'] == 'driver':
cell['transgenic_line'] = tl['name']
# cell reporter status
cell['reporter_status'] = cell.get('cell_reporter', {}).get('name', None)
# species
cell['species'] = cell.get('donor',{}).get('organism',{}).get('name', None)
# conditions (whitelist)
condition_types = [ 'disease categories' ]
condition_keys = dict(zip(condition_types,
[ ct.replace(' ', '_') for ct in condition_types ]))
for ct, ck in condition_keys.items():
cell[ck] = []
conditions = cell.get('donor',{}).get('conditions', [])
for condition in conditions:
c_type, c_val = condition['name'].split(' - ')
if c_type in condition_keys:
cell[condition_keys[c_type]].append(c_val)
result = self.filter_cells(cells, require_morphology, require_reconstruction, reporter_status, species)
return result
[docs] def get_cell(self, id):
'''
Query the API for a one cells in the Cell Types Database.
Returns
-------
list
Meta data for one cell.
'''
cells = self.list_cells_api(id=id)
cell = None if not cells else cells[0]
return cell
[docs] @cacheable()
def get_ephys_sweeps(self, specimen_id):
"""
Query the API for a list of sweeps for a particular cell in the Cell Types Database.
Parameters
----------
specimen_id: int
Specimen ID of a cell.
Returns
-------
list: List of sweep dictionaries belonging to a cell
"""
criteria = "[specimen_id$eq%d]" % specimen_id
sweeps = self.model_query(
'EphysSweep', criteria=criteria, num_rows='all')
return sorted(sweeps, key=lambda x: x['sweep_number'])
[docs] @deprecated("please use filter_cells_api")
def filter_cells(self, cells, require_morphology, require_reconstruction, reporter_status, species):
"""
Filter a list of cell specimens to those that optionally have morphologies
or have morphological reconstructions.
Parameters
----------
cells: list
List of cell metadata dictionaries to be filtered
require_morphology: boolean
Filter out cells that have no morphological images.
require_reconstruction: boolean
Filter out cells that have no morphological reconstructions.
reporter_status: list
Filter for cells that have a particular cell reporter status
species: list
Filter for cells that belong to one or more species. If None, return all.
Must be one of [ CellTypesApi.MOUSE, CellTypesApi.HUMAN ].
"""
if require_morphology:
cells = [c for c in cells if c['has_morphology']]
if require_reconstruction:
cells = [c for c in cells if c['has_reconstruction']]
if reporter_status:
cells = [c for c in cells if c[
'reporter_status'] in reporter_status]
if species:
species_lower = [ s.lower() for s in species ]
cells = [c for c in cells if c['donor']['organism']['name'].lower() in species_lower]
return cells
[docs] def filter_cells_api(self, cells,
require_morphology=False,
require_reconstruction=False,
reporter_status=None,
species=None,
simple=True):
"""
"""
if require_morphology or require_reconstruction:
cells = [c for c in cells if c.get('nr__reconstruction_type') is not None]
if reporter_status:
cells = [c for c in cells if c.get('cell_reporter_status') in reporter_status]
if species:
species_lower = [ s.lower() for s in species ]
cells = [c for c in cells if c.get('donor__species',"").lower() in species_lower]
if simple:
cells = self.simplify_cells_api(cells)
return cells
[docs] def simplify_cells_api(self, cells):
return [{
'reporter_status': cell['cell_reporter_status'],
'cell_soma_location': [ cell['csl__x'], cell['csl__y'], cell['csl__z'] ],
'species': cell['donor__species'],
'id': cell['specimen__id'],
'name': cell['specimen__name'],
'structure_layer_name': cell['structure__layer'],
'structure_area_id': cell['structure_parent__id'],
'structure_area_abbrev': cell['structure_parent__acronym'],
'transgenic_line': cell['line_name'],
'dendrite_type': cell['tag__dendrite_type'],
'apical': cell['tag__apical'],
'reconstruction_type': cell['nr__reconstruction_type'],
'disease_state': cell['donor__disease_state']
} for cell in cells ]
[docs] @cacheable()
def get_ephys_features(self):
"""
Query the API for the full table of EphysFeatures for all cells.
"""
return self.model_query(
'EphysFeature',
criteria='specimen(ephys_result[failed$eqfalse])',
num_rows='all')
[docs] @cacheable()
def get_morphology_features(self):
"""
Query the API for the full table of morphology features for all cells
Notes
-----
by default the tags column is removed because it isn't useful
"""
return self.model_query(
'NeuronReconstruction',
criteria="specimen(ephys_result[failed$eqfalse])",
excpt='tags',
num_rows='all')
[docs] @cacheable(strategy='create',
pathfinder=Cache.pathfinder(file_name_position=2,
path_keyword='file_name'))
def save_ephys_data(self, specimen_id, file_name):
"""
Save the electrophysology recordings for a cell as an NWB file.
Parameters
----------
specimen_id: int
ID of the specimen, from the Specimens database model in the Allen Institute API.
file_name: str
Path to save the NWB file.
"""
criteria = '[id$eq%d],ephys_result(well_known_files(well_known_file_type[name$eq%s]))' % (
specimen_id, self.NWB_FILE_TYPE)
includes = 'ephys_result(well_known_files(well_known_file_type))'
results = self.model_query('Specimen',
criteria=criteria,
include=includes,
num_rows='all')
try:
file_url = results[0]['ephys_result'][
'well_known_files'][0]['download_link']
except Exception as _:
raise Exception("Specimen %d has no ephys data" % specimen_id)
self.retrieve_file_over_http(self.api_url + file_url, file_name)
[docs] def save_reconstruction(self, specimen_id, file_name):
"""
Save the morphological reconstruction of a cell as an SWC file.
Parameters
----------
specimen_id: int
ID of the specimen, from the Specimens database model in the Allen Institute API.
file_name: str
Path to save the SWC file.
"""
Manifest.safe_make_parent_dirs(file_name)
criteria = '[id$eq%d],neuron_reconstructions(well_known_files)' % specimen_id
includes = 'neuron_reconstructions(well_known_files(well_known_file_type[name$eq\'%s\']))' % self.SWC_FILE_TYPE
results = self.model_query('Specimen',
criteria=criteria,
include=includes,
num_rows='all')
try:
file_url = results[0]['neuron_reconstructions'][
0]['well_known_files'][0]['download_link']
except:
raise Exception("Specimen %d has no reconstruction" % specimen_id)
self.retrieve_file_over_http(self.api_url + file_url, file_name)
[docs] def save_reconstruction_markers(self, specimen_id, file_name):
"""
Save the marker file for the morphological reconstruction of a cell. These are
comma-delimited files indicating points of interest in a reconstruction (truncation
points, early tracing termination, etc).
Parameters
----------
specimen_id: int
ID of the specimen, from the Specimens database model in the Allen Institute API.
file_name: str
Path to save the marker file.
"""
Manifest.safe_make_parent_dirs(file_name)
criteria = '[id$eq%d],neuron_reconstructions(well_known_files)' % specimen_id
includes = 'neuron_reconstructions(well_known_files(well_known_file_type[name$eq\'%s\']))' % self.MARKER_FILE_TYPE
results = self.model_query('Specimen',
criteria=criteria,
include=includes,
num_rows='all')
try:
file_url = results[0]['neuron_reconstructions'][
0]['well_known_files'][0]['download_link']
except:
raise LookupError("Specimen %d has no marker file" % specimen_id)
self.retrieve_file_over_http(self.api_url + file_url, file_name)