Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add segment props #176

Merged
merged 8 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 5.18.0
current_version = 5.19.0
commit = True
tag = True

Expand Down
2 changes: 1 addition & 1 deletion caveclient/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "5.18.0"
__version__ = "5.19.0"

from .frameworkclient import CAVEclient

Expand Down
36 changes: 22 additions & 14 deletions caveclient/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ def __init__(
self._api_version = api_version
self._endpoints = endpoints
self._fc = over_client
self._server_version = self._get_version()

@property
def fc(self):
Expand All @@ -234,7 +235,11 @@ def api_version(self):

def _get_version(self) -> Optional[Version]:
endpoint_mapping = self.default_url_mapping
url = self._endpoints.get("get_version", None).format_map(endpoint_mapping)
endpoint = self._endpoints.get("get_version", None)
if endpoint is None:
return None

url = endpoint.format_map(endpoint_mapping)
response = self.session.get(url)
if response.status_code == 404: # server doesn't have this endpoint yet
return None
Expand Down Expand Up @@ -349,8 +354,11 @@ def _version_fails_constraint(version: Version, constraint: str = None):
if constraint is None:
return False
else:
specifier = SpecifierSet(constraint)
return version not in specifier
if version is None:
return True
else:
specifier = SpecifierSet(constraint)
return version not in specifier


@parametrized
Expand Down Expand Up @@ -399,17 +407,17 @@ def wrapper(*args, **kwargs):
)

raise ServerIncompatibilityError(msg)

for kwarg, kwarg_constraint in kwarg_use_constraints.items():
if _version_fails_constraint(self.server_version, kwarg_constraint):
msg = (
f"Use of keyword argument `{kwarg}` in `{method.__name__}` "
"is only permitted "
f"for server version {kwarg_constraint}, your server "
f"version is {self.server_version}. Contact your system "
"administrator to update the server version."
)
raise ServerIncompatibilityError(msg)
if kwarg_use_constraints is not None:
for kwarg, kwarg_constraint in kwarg_use_constraints.items():
if _version_fails_constraint(self.server_version, kwarg_constraint):
msg = (
f"Use of keyword argument `{kwarg}` in `{method.__name__}` "
"is only permitted "
f"for server version {kwarg_constraint}, your server "
f"version is {self.server_version}. Contact your system "
"administrator to update the server version."
)
raise ServerIncompatibilityError(msg)

out = method(*args, **kwargs)
return out
Expand Down
1 change: 0 additions & 1 deletion caveclient/chunkedgraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ def __init__(
self._default_timestamp = timestamp
self._table_name = table_name
self._segmentation_info = None
self._server_version = self._get_version()

@property
def default_url_mapping(self):
Expand Down
5 changes: 5 additions & 0 deletions caveclient/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@
"upload_state_w_id": json_v1 + "/post/{state_id}",
"get_state": json_v1 + "/{state_id}",
"get_state_raw": json_v1 + "/raw/{state_id}",
"get_properties": json_v1 + "/property/{state_id}/info",
"upload_properties": json_v1 + "/property/post",
"get_properties_raw": json_v1 + "/property/raw/{state_id}",
"upload_properties_w_id": json_v1 + "/property/post/{state_id}",
"get_version": json_v1 + "/version",
}

json_legacy = "{json_server_address}/nglstate"
Expand Down
73 changes: 72 additions & 1 deletion caveclient/jsonservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .base import (
ClientBase,
_api_endpoints,
_check_version_compatibility,
handle_response,
)
from .endpoints import (
Expand Down Expand Up @@ -186,6 +187,27 @@ def get_state_json(self, state_id):
handle_response(response, as_json=False)
return json.loads(response.content)

@_check_version_compatibility(method_constraint=">=0.4.0")
def get_property_json(self, state_id):
"""Download a Neuroglancer JSON state

Parameters
----------
state_id : int
ID of a JSON state uploaded to the state service.

Returns
-------
dict
JSON specifying a Neuroglancer state.
"""
url_mapping = self.default_url_mapping
url_mapping["state_id"] = state_id
url = self._endpoints["get_property"].format_map(url_mapping)
response = self.session.get(url)
handle_response(response, as_json=False)
return json.loads(response.content)

def upload_state_json(self, json_state, state_id=None, timestamp=None):
"""Upload a Neuroglancer JSON state

Expand Down Expand Up @@ -224,6 +246,45 @@ def upload_state_json(self, json_state, state_id=None, timestamp=None):
response_re = re.search(".*\/(\d+)", str(response.content))
return int(response_re.groups()[0])

@_check_version_compatibility(">=0.4.0")
def upload_property_json(self, property_json, state_id=None, timestamp=None):
"""Upload a Neuroglancer JSON state

Parameters
----------
propery_json : dict
Dict representation of a neuroglancer segment properties json
state_id : int
ID of a JSON state uploaded to the state service.
Using a state_id is an admin feature.
timestamp: time.time
Timestamp for json state date. Requires state_id.

Returns
-------
int
state_id of the uploaded JSON state
"""
url_mapping = self.default_url_mapping

if state_id is None:
url = self._endpoints["upload_properties"].format_map(url_mapping)
else:
url_mapping = self.default_url_mapping
url_mapping["state_id"] = state_id
url = self._endpoints["upload_properties_w_id"].format_map(url_mapping)

response = self.session.post(
url,
data=json.dumps(
property_json,
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 save_state_json_local(self, json_state, filename, overwrite=False):
"""Save a Neuroglancer JSON state to a JSON file locally.

Expand Down Expand Up @@ -251,6 +312,7 @@ def build_neuroglancer_url(
ngl_url=None,
target_site=None,
static_url=False,
format_propeties=False,
):
"""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".
Expand All @@ -269,7 +331,8 @@ def build_neuroglancer_url(
Default is None.
static_url : bool
If True, treats "state_id" as a static URL directly to the JSON and does not use the state service.

format_propeties : bool
If True, formats the url as a segment_properties info file
Returns
-------
str
Expand Down Expand Up @@ -304,6 +367,14 @@ def build_neuroglancer_url(
target_site_error = "A specified target_site must be one of 'seunglab', 'cave-explorer' or 'mainline'"
raise ValueError(target_site_error)

if format_propeties:
url_mapping = self.default_url_mapping
url_mapping["state_id"] = state_id
get_state_url = self._endpoints["get_properties"][:-5].format_map(
url_mapping
)
url = "precomputed://" + auth_text + get_state_url
return url
if static_url:
url = ngl_url + parameter_text + state_id
else:
Expand Down