From 0a94fac6fcbfdc91d449f46d4e6609274b31cb74 Mon Sep 17 00:00:00 2001 From: Chris Barnes Date: Thu, 9 Mar 2017 17:44:12 -0500 Subject: [PATCH 1/3] Add method replicating skeleton export Method to return a dictionary of the same format as the 'Treenode and connector geometry' option from the skeleton export widget. --- catmaid_interface.py | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/catmaid_interface.py b/catmaid_interface.py index 0974d08..0f28e48 100644 --- a/catmaid_interface.py +++ b/catmaid_interface.py @@ -79,6 +79,55 @@ 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 + + Parameters + ---------- + skeleton_id + proj_opts + + Returns + ------- + dict + dict of the same form as the JSON file used by the 'Treenode and connector geometry' option in the CATMAID + Export widget. + """ + 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() From ddc29c290efe09a2ea7b4c1d458acbdf856026f1 Mon Sep 17 00:00:00 2001 From: Chris Barnes Date: Thu, 9 Mar 2017 18:35:58 -0500 Subject: [PATCH 2/3] Add CatmaidAPI object Rather than faffing with the proj_opts dict and remembering where it goes in the function call, you just instantiate a CatmaidAPI and call all of the functions are instance methods without the proj_opts argument. The CatmaidAPI instance can also be used as a drop-in replacement for a proj_opts dict, and its string representation is the JSON identical to a proj_opts dict. Future methods could (should?) be defined straight on the object, but for backwards compatibility the existing methods can be called either way. Also, added docstrings which IDEs will recognise. --- catmaid_interface.py | 123 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 110 insertions(+), 13 deletions(-) diff --git a/catmaid_interface.py b/catmaid_interface.py index 0f28e48..8cc4457 100644 --- a/catmaid_interface.py +++ b/catmaid_interface.py @@ -44,9 +44,117 @@ 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 + + @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_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 ): @@ -83,17 +191,6 @@ 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 - - Parameters - ---------- - skeleton_id - proj_opts - - Returns - ------- - dict - dict of the same form as the JSON file used by the 'Treenode and connector geometry' option in the CATMAID - Export widget. """ url = proj_opts['baseurl'] + '/{}/{}/1/0/compact-skeleton'.format( proj_opts['project_id'], skeleton_id) data = requests.get( From 9db96cb7d51dc5ca0afe8f8ae8c577c158631c0a Mon Sep 17 00:00:00 2001 From: Chris Barnes Date: Thu, 9 Mar 2017 19:19:58 -0500 Subject: [PATCH 3/3] Methods to make get/post requests easier Also a fetch method which behaves very similarly to the javascript API. --- catmaid_interface.py | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/catmaid_interface.py b/catmaid_interface.py index 8cc4457..b4ffe01 100644 --- a/catmaid_interface.py +++ b/catmaid_interface.py @@ -52,6 +52,8 @@ def __init__(self, baseurl, project_id, token, authname=None, authpass=None): self.authname = authname self.authpass = authpass + self.auth_token = catmaid_auth_token(self.token, self.authname, self.authpass) + @property def proj_opts(self): return { @@ -68,6 +70,72 @@ def __getitem__(self, 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)