Skip to content

Commit

Permalink
Merge pull request #132 from seung-lab/fix-ngl-state-encoding
Browse files Browse the repository at this point in the history
Fix ngl state encoding
  • Loading branch information
fcollman authored Dec 19, 2023
2 parents 29d4ee5 + 827c081 commit d35f8fe
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 12 deletions.
10 changes: 10 additions & 0 deletions caveclient/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,13 @@
}

l2cache_api_versions = {1: l2cache_endpoints_v1}

# -------------------------------
# ------ Neuroglancer endpoints
# -------------------------------

fallback_ngl_endpoint = "https://neuroglancer.neuvue.io/"
ngl_endpoints_common = {
'get_info': "{ngl_url}/version.json",
'fallback_ngl_url': fallback_ngl_endpoint,
}
96 changes: 84 additions & 12 deletions caveclient/jsonservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,32 @@
jsonservice_common,
jsonservice_api_versions,
default_global_server_address,
ngl_endpoints_common,
)
import requests
import numpy as np
import numbers
import json
import re

server_key = "json_server_address"


def neuroglancer_json_encoder(obj):
"""JSON encoder for neuroglancer states.
Differs from normal in that it expresses ints as strings"""
if isinstance(obj, numbers.Integral):
return str(obj)
if isinstance(obj, np.integer):
return str(obj)
elif isinstance(obj, np.floating):
return float(obj)
elif isinstance(obj, np.ndarray):
return list(obj)
elif isinstance(obj, (set, frozenset)):
return list(obj)
raise TypeError

def JSONService(
server_address=None,
auth_client=None,
Expand Down Expand Up @@ -118,6 +136,36 @@ def ngl_url(self):
def ngl_url(self, new_ngl_url):
self._ngl_url = new_ngl_url

def get_neuroglancer_info(self, ngl_url=None):
"""Get the info field from a Neuroglancer deployment
Parameters
----------
ngl_url : str (optional)
URL to a Neuroglancer deployment.
If None, defaults to the value for the datastack or the client.
Returns
-------
dict
JSON-formatted info field from the Neuroglancer deployment
"""
if ngl_url is None:
ngl_url = self.ngl_url

url_mapping = self.default_url_mapping
url_mapping["ngl_url"] = ngl_url
url = ngl_endpoints_common.get('get_info').format_map(url_mapping)
response = self.session.get(url)
# Not all neuroglancer deployments have a version.json,
# so return empty if not found rather than throw error.
if response.status_code == 404:
return {}

handle_response(response, as_json=False)
return json.loads(response.content)


def get_state_json(self, state_id):
"""Download a Neuroglancer JSON state
Expand All @@ -144,7 +192,7 @@ def upload_state_json(self, json_state, state_id=None, timestamp=None):
Parameters
----------
json_state : dict
JSON-formatted Neuroglancer state
Dict representation of a neuroglancer state
state_id : int
ID of a JSON state uploaded to the state service.
Using a state_id is an admin feature.
Expand All @@ -165,12 +213,19 @@ def upload_state_json(self, json_state, state_id=None, timestamp=None):
url_mapping["state_id"] = state_id
url = self._endpoints["upload_state_w_id"].format_map(url_mapping)

response = self.session.post(url, data=json.dumps(json_state, cls=BaseEncoder))
response = self.session.post(
url,
data=json.dumps(
json_state,
default=neuroglancer_json_encoder,
)
)
handle_response(response, as_json=False)
response_re = re.search(".*\/(\d+)", str(response.content))
return int(response_re.groups()[0])

def build_neuroglancer_url(self, state_id, ngl_url=None):

def build_neuroglancer_url(self, state_id, ngl_url=None, target_site=None):
"""Build a URL for a Neuroglancer deployment that will automatically retrieve specified state.
If the datastack is specified, this is prepopulated from the info file field "viewer_site".
If no ngl_url is specified in either the function or the client, only the JSON state url is returned.
Expand All @@ -181,22 +236,39 @@ def build_neuroglancer_url(self, state_id, ngl_url=None):
State id to retrieve
ngl_url : str
Base url of a neuroglancer deployment. If None, defaults to the value for the datastack or the client.
If no value is found, only the URL to the JSON state is returned.
As a fallback, a default deployment is used.
target_site : 'seunglab' or 'cave-explorer' or 'mainline' or None
Set this to 'seunglab' for a seunglab deployment, or 'cave-explorer'/'mainline' for a google main branch deployment.
If None, checks the info field of the neuroglancer endpoint to determine which to use.
Default is None.
Returns
-------
str
The full URL requested
"""
if ngl_url is None:
ngl_url = self.ngl_url
if ngl_url is None:
ngl_url = ""
parameter_text = ""
elif ngl_url[-1] == "/":
parameter_text = "?json_url="
else:
parameter_text = "/?json_url="
if self.ngl_url is not None:
ngl_url = self.ngl_url
else:
ngl_url = ngl_endpoints_common['fallback_ngl_url']
if target_site is None and ngl_url is not None:
ngl_info = self.get_neuroglancer_info(ngl_url)
if len(ngl_info) > 0:
target_site = 'cave-explorer'
else:
target_site = "seunglab"

if target_site == "seunglab":
if ngl_url[-1] == "/":
parameter_text = "?json_url="
else:
parameter_text = "/?json_url="
elif target_site == "cave-explorer" or target_site == "mainline":
if ngl_url[-1] == "/":
parameter_text = "#!middleauth+"
else:
parameter_text = "/#!middleauth+"

url_mapping = self.default_url_mapping
url_mapping["state_id"] = state_id
Expand Down

0 comments on commit d35f8fe

Please sign in to comment.