diff --git a/catmaid_interface.py b/catmaid_interface.py index 0974d08..b4ffe01 100644 --- a/catmaid_interface.py +++ b/catmaid_interface.py @@ -44,9 +44,185 @@ def get_catmaid_url( proj_opts, xyz, nodeid=None, skid=None, tool='tracingtool', return '&'.join(strs) +class CatmaidAPI(object): + def __init__(self, baseurl, project_id, token, authname=None, authpass=None): + self.baseurl = baseurl + self.project_id = project_id + self.token = token + self.authname = authname + self.authpass = authpass + + self.auth_token = catmaid_auth_token(self.token, self.authname, self.authpass) + + @property + def proj_opts(self): + return { + 'baseurl': self.baseurl, + 'project_id': self.project_id, + 'token': self.token, + 'authname': self.authname, + 'authpass': self.authpass + } + + def __getitem__(self, key): + return self.proj_opts[key] + + def __str__(self): + return json.dumps(self.proj_opts, sort_keys=True) + + def get(self, relative_url, params=None, raw=False): + """ + Get data from a running instance of CATMAID. + + Parameters + ---------- + relative_url : str + URL to send the request to, relative to the baseurl + params : dict or str + JSON-like key/value data to be included in the get URL + raw : bool + Whether to return the response as a string (defaults to returning a dict) + + Returns + ------- + dict or str + Data returned from CATMAID: type depends on the 'raw' parameter. + """ + url = requests.compat.urljoin(self.baseurl, relative_url) + response = requests.get(url, params=dict(params), auth=self.auth_token) + return response.json() if not raw else response.text + + def post(self, relative_url, data=None, raw=False): + """ + Post data to a running instance of CATMAID. + + Parameters + ---------- + relative_url : str + URL to send the request to, relative to the baseurl + params : dict or str + JSON-like key/value data to be included in the request as a payload + raw : bool + Whether to return the response as a string (defaults to returning a dict) + + Returns + ------- + dict or str + Data returned from CATMAID: type depends on the 'raw' parameter. + """ + url = requests.compat.urljoin(self.baseurl, relative_url) + response = requests.post(url, data=dict(data), auth=self.auth_token) + return response.json() if not raw else response.text + + def fetch(self, relative_url, method='GET', data=None, raw=False): + """ + Interact with the CATMAID server in a manner very similar to the javascript CATMAID.fetch API. + + Parameters + ---------- + relative_url : str + URL to send the request to, relative to the baseurl + method : str + 'GET' or 'POST' (default 'GET') + data : dict or str + JSON-like key/value data to be included in the request as a payload + raw : bool + Whether to return the response as a string (defaults to returning a dict) + + Returns + ------- + dict or str + Data returned from CATMAID: type depends on the 'raw' parameter. + """ + return {'POST': self.post, 'GET': self.get}[method.upper()](relative_url, data, raw) + + def get_catmaid_url(self, xyz, nodeid=None, skid=None, tool='tracingtool',zoomlevel=0): + return get_catmaid_url(self.proj_opts, xyz, nodeid, skid, tool, zoomlevel) + + def get_neuron_name(self, skeleton_id): + """ + Given a skeleton ID, fetch the neuron name + """ + return get_neuron_name(skeleton_id, self.proj_opts) + + def get_skeleton_json(self, skeleton_id, withtags=True): + """ + skeleton_object: Fetch full JSON info (plus a few other useful things) for a skeleton + sk[0] is the list of skeleton nodes + sk[1] is the list of connectors + sk[2] is tags + sk[3] is the skeleton id + sk[4] is the neuron name + sk[5] is the number of postsynaptic targets for each presynaptic connector + """ + return get_skeleton_json(skeleton_id, self.proj_opts, withtags) + + def get_treenode_and_connector_geometry(self, skeleton_id): + """ + Return a dict of the same form as the JSON returned by the 'Treenode and connector geometry' option in the + export widget. + See CATMAID code for original js implementation: + http://github.com/catmaid/CATMAID/blob/master/django/applications/catmaid/static/js/widgets/export-widget.js#L449 + """ + return get_treenode_and_connector_geometry(skeleton_id, self.proj_opts) + def get_connector_info(self, connector_id): + return get_connector_info(connector_id, self.proj_opts) + def add_annotation(self, annotation_list, id_list): + """ + Add a single annotation to a list of skeleton IDs. + """ + return add_annotation(annotation_list, id_list, self.proj_opts) + + def get_ids_from_annotation(self, annotation_id_list): + """ + Given an annotation id, get all skeleton ids with that annotation + """ + return get_ids_from_annotation(annotation_id_list, self.proj_opts) + + def get_ids_by_nodecount(self, min_nodes): + """ + Ids of all skeletons with more nodes than min_nodes + """ + return get_ids_by_nodecount(min_nodes, self.proj_opts) + + def get_connected_skeleton_info(self, id_list): + """ + Get skeleton ids, nodes, and number of synapses connected upstream/downstream for a list of ids. + """ + return get_connected_skeleton_info(id_list, self.proj_opts) + + def increase_id_list(self, id_list, min_pre=0, min_post=0, hops=1): + """ + Grow a list of skeleton ids along the connectivity network + """ + return increase_id_list(id_list, self.proj_opts, min_pre, min_post, hops) + + def post_synaptic_count(self, connector_list): + """ + Count how many postsynaptic targets are associated with each connector in a list of connectors + """ + return post_synaptic_count(connector_list, self.proj_opts) + + def write_skeletons_from_list(self, id_list): + """ + pull JSON files (plus key details) for + """ + return write_skeletons_from_list(id_list, self.proj_opts) + + def get_connectivity_graph(self, id_list): + return get_connectivity_graph(id_list, self.proj_opts) + + def get_skeleton_statistics(self, skid): + """ + Retrieve basic statistics about skeletons like number of synapses and total length + """ + return get_skeleton_statistics(skid, self.proj_opts) + + +# IN FUTURE, DEFINE API CALLS AS INSTANCE METHODS ON THE OBJECT; THE BELOW ARE FOR BACKWARDS COMPATIBILITY # get_neuron_name: Given a skeleton ID, fetch the neuron name def get_neuron_name( skeleton_id, proj_opts ): @@ -79,6 +255,44 @@ def get_skeleton_json( skeleton_id, proj_opts, withtags = True): d.append([]) return d +def get_treenode_and_connector_geometry(skeleton_id, proj_opts): + """ + See CATMAID code for original js implementation: + http://github.com/catmaid/CATMAID/blob/master/django/applications/catmaid/static/js/widgets/export-widget.js#L449 + """ + url = proj_opts['baseurl'] + '/{}/{}/1/0/compact-skeleton'.format( proj_opts['project_id'], skeleton_id) + data = requests.get( + url, auth=catmaid_auth_token(proj_opts['token'], proj_opts['authname'], proj_opts['authpass']) + ).json() + + skeleton = { + 'treenodes': dict(), + 'connectors': dict() + } + + for treenode in data[0]: + skeleton['treenodes'][treenode[0]] = { + 'location': treenode[3:6], + 'parent_id': treenode[1] + } + + for connector in data[1]: + if connector[2] not in [0, 1]: + continue + + conn_id = connector[1] + if conn_id not in skeleton['connectors']: + skeleton['connectors'][conn_id] = { + 'presynaptic_to': [], + 'postsynaptic_to': [] + } + + skeleton['connectors'][conn_id]['location'] = connector[3:6] + relation = 'postsynaptic_to' if connector[2] == 1 else 'presynaptic_to' + skeleton['connectors'][conn_id][relation].append(connector[0]) + + return skeleton + def get_connector_info( connector_id, proj_opts): url = proj_opts['baseurl'] + '/{}/connectors/{}/'.format( proj_opts['project_id'], connector_id ) d = requests.get( url, auth = catmaid_auth_token( proj_opts['token'], proj_opts['authname'], proj_opts['authpass'] ) ).json()