Skip to content

Commit

Permalink
revert: use old worker for all but /assign endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
xgui3783 committed Oct 4, 2024
1 parent 8f9cb79 commit 55c7a44
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 11 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.3.19
0.3.20
1 change: 1 addition & 0 deletions api/common/data_handlers/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from . import space
from . import region
from . import parcellation
from . import misc
216 changes: 216 additions & 0 deletions api/common/data_handlers/core/misc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
from api.common import data_decorator, get_filename, NotFound
from api.models.volumes.volume import MapType
from api.siibra_api_config import ROLE
from typing import Union, Dict, Tuple

@data_decorator(ROLE)
def get_map(parcellation_id: str, space_id: str, maptype: Union[MapType, str]) -> Dict:
"""Get a map instance, based on specification
Args:
parcellation_id: lookup id of the parcellation of the map
space_id: lookup id of the space of the map
maptype: maptype, either LABELLED or STATISTICAL
Returns:
Requested map instance, serialized into dict
Raises:
AssertionError: if the supplied maptype is invalid type
NotFound: Map with the specification not found
"""
import siibra
from api.serialization.util import instance_to_model

maptype_string = None
# check maptype name and value both matches
if isinstance(maptype, MapType):
assert maptype.name == maptype.value, f"str enum, expecting .name and .value to equal"
maptype_string = maptype.name
if isinstance(maptype, str):
maptype_string = maptype

assert maptype_string is not None, f"maptype is neither MapType nor str"

siibra_maptype = siibra.MapType[maptype_string]
assert siibra_maptype.name == maptype_string, f"Expecting maptype.name to match"

returned_map = siibra.get_map(parcellation_id, space_id, siibra_maptype)

if returned_map is None:
raise NotFound

return instance_to_model(
returned_map
).dict()


def cache_region_statistic_map(parcellation_id: str, region_id: str, space_id: str) -> Tuple[str, bool]:
"""Retrieve and save regional statistical map (if necessary), and then return the path of the map.
Args:
parcellation_id: lookup id of the parcellation of the map
region_id: lookup id of the region of the map
space_id: lookup id of the space of the map
Returns:
path to statistical map, if a cached file is returned
"""
import os
full_filename = get_filename("statistical_map", parcellation_id, region_id, space_id, ext=".nii.gz")
if os.path.isfile(full_filename):
return full_filename, True

import siibra
import nibabel as nib
error_text = f"Map with parc id '{parcellation_id}', space id '{space_id}'"

stat_map = siibra.get_map(parcellation_id, space_id, siibra.MapType.STATISTICAL)
assert stat_map is not None, f"{error_text} returns None"

volume_data = stat_map.fetch(region=region_id)

error_text = f"{error_text}, with region_id '{region_id}'"
assert isinstance(volume_data, nib.Nifti1Image), f"{error_text}, volume provided is not of type Nifti1Image"

nib.save(volume_data, full_filename)
import json
import time
with open(f"{full_filename}.{str(int(time.time()))}.json", "w") as fp:
json.dump({
"prefix": "statistical_map",
"parcellation_id": parcellation_id,
"region_id": region_id,
"space_id": space_id,
}, fp=fp, indent="\t")
return full_filename, False

@data_decorator(ROLE)
def get_region_statistic_map(parcellation_id: str, region_id: str, space_id: str):
"""Retrieve and save regional statistical map (if necessary), and then return the path of the map.
Args:
parcellation_id: lookup id of the parcellation of the map
region_id: lookup id of the region of the map
space_id: lookup id of the space of the map
Returns:
path to statistical map, if a cached file is returned
"""
return cache_region_statistic_map(parcellation_id, region_id, space_id)

@data_decorator(ROLE)
def get_region_statistic_map_info(parcellation_id: str, region_id: str, space_id: str):
"""Retrieve and save regional statistical map (if necessary), and then return the path of the map.
Args:
parcellation_id: lookup id of the parcellation of the map
region_id: lookup id of the region of the map
space_id: lookup id of the space of the map
Returns:
dict of min an max of the statistical map
"""
full_filename, _cache_flag = cache_region_statistic_map(parcellation_id, region_id, space_id)

import nibabel as nib
import numpy as np

nii = nib.load(full_filename)
data = nii.get_fdata()
return {
"min": np.min(data),
"max": np.max(data),
}

@data_decorator(ROLE)
def get_parcellation_labelled_map(parcellation_id: str, space_id: str, region_id:str=None):
"""Retrieve and save labelled map / regional mask (if necessary), and then return the path of the map.
Args:
parcellation_id: lookup id of the parcellation of the map
region_id: lookup id of the region of the map
space_id: lookup id of the space of the map
Returns:
path to labelled map/regional mask, if a cached file is returned
"""
import os
full_filename = get_filename("labelled_map", parcellation_id, space_id, region_id if region_id else "", ext=".nii.gz")
if os.path.isfile(full_filename):
return full_filename, True

import siibra
import nibabel as nib
error_text = f"Map with parc id '{parcellation_id}', space id '{space_id}'"

volume_data = None
if region_id is not None:
region = siibra.get_region(parcellation_id, region_id)
volume_data = region.fetch_regional_map(space_id, siibra.MapType.LABELLED)
else:
labelled_map = siibra.get_map(parcellation_id, space_id, siibra.MapType.LABELLED)
assert labelled_map is not None, f"{error_text} returns None"
volume_data = labelled_map.fetch()

assert isinstance(volume_data, nib.Nifti1Image), f"{error_text}, volume provided is not of type Nifti1Image"

nib.save(volume_data, full_filename)
import json
import time
with open(f"{full_filename}.{str(int(time.time()))}.json", "w") as fp:
json.dump({
"prefix": "labelled_map",
"parcellation_id": parcellation_id,
"space_id": space_id,
"region_id": region_id,
}, fp=fp, indent="\t")
return full_filename, False

# @data_decorator(ROLE)
# def assign_point(parcellation_id: str, space_id: str, point: str, assignment_type: str, sigma_mm: float):
# import siibra
# from api.serialization.util import instance_to_model
# m = siibra.get_map(parcellation_id, space_id, assignment_type)
# p = siibra.Point(point, space=space_id, sigma_mm=sigma_mm)
# df = m.assign(p)

# try:
# return instance_to_model(df, detail=True).dict()
# except Exception as e:
# raise e

@data_decorator(ROLE)
def get_resampled_map(parcellation_id: str, space_id: str):
"""Retrieve and save a labelled map, resampled in space (if necessary), and then return the path of the map.
Args:
parcellation_id: lookup id of the parcellation of the map
space_id: lookup id of the target space of the sampled map
Returns:
path to statistical map, if a cached file is returned
"""
import os
full_filename = get_filename("resampled_map", parcellation_id, space_id, ext=".nii.gz")
if os.path.isfile(full_filename):
return full_filename, True

import siibra
import nibabel as nib
parcellation: siibra.core.parcellation.Parcellation = siibra.parcellations[parcellation_id]
parcellation_map = parcellation.get_map(siibra.spaces[space_id], siibra.MapType.LABELLED)
nii = parcellation_map.get_resampled_template()

assert isinstance(nii, nib.Nifti1Image), f"resample failed... returned not of type nii"

import time
import json
nib.save(nii, full_filename)
with open(f"{full_filename}.{str(int(time.time()))}.json", "w") as fp:
json.dump({
"prefix": "resampled_map",
"parcellation_id": parcellation_id,
"space_id": space_id,
}, indent="\t", fp=fp)
return full_filename, False
2 changes: 1 addition & 1 deletion api/common/data_handlers/features/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from . import types
from . import types, misc
24 changes: 24 additions & 0 deletions api/common/data_handlers/features/misc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from api.common import data_decorator
from api.siibra_api_config import ROLE
from api.models.vocabularies.genes import GeneModel

@data_decorator(ROLE)
def get_genes(find:str=None):
"""Get all genes
Args:
string to find in vocabularies
Returns:
List of the genes."""

from siibra.vocabularies import GENE_NAMES
if find == None:
return_list = [v for v in GENE_NAMES]
else:
return_list = GENE_NAMES.find(find)

return [GeneModel(
symbol=v.get("symbol"),
description=v.get("description")
).dict() for v in return_list]
3 changes: 2 additions & 1 deletion api/server/volcabularies/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from api.siibra_api_config import ROLE
from api.common import router_decorator
from api.models.vocabularies.genes import GeneModel
from api.common.data_handlers.vocabularies.gene import get_genes
# from api.common.data_handlers.vocabularies.gene import get_genes
from api.common.data_handlers.features.misc import get_genes

TAGS= ["vocabularies"]
"""HTTP vocabularies tags"""
Expand Down
28 changes: 21 additions & 7 deletions api/server/volumes/parcellationmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
from api.models.volumes.volume import MapType
from api.models._commons import DataFrameModel
from api.common import router_decorator, get_filename, logger, NotFound
from api.common.data_handlers.core.misc import (
get_map as old_get_map,
get_resampled_map as old_get_resampled_map,
get_parcellation_labelled_map as old_get_parcellation_labelled_map,
get_region_statistic_map as old_get_region_statistic_map,
get_region_statistic_map_info as old_get_region_statistic_map_info
)
from new_api.v3.data_handlers.map import assign, get_map, statistical_map_info_json, statistical_map_nii_gz, labelled_map_nii_gz, resampled_template
from api.server.util import SapiCustomRoute
import os
Expand All @@ -20,20 +27,23 @@
router = APIRouter(route_class=SapiCustomRoute, tags=TAGS)
"""HTTP map router"""

# still use the old worker. New worker not stable (?)
@router.get("", response_model=MapModel)
@version(*FASTAPI_VERSION)
@router_decorator(ROLE, func=get_map)
@router_decorator(ROLE, func=old_get_map)
def get_siibra_map(parcellation_id: str, space_id: str, map_type: MapType, name: str= "", *, func):
"""Get map according to specification"""
if func is None:
raise HTTPException(500, f"func: None passsed")
return func(parcellation_id, space_id, map_type, name)


# still use the old worker. New worker not stable (?)
@router.get("/resampled_template", response_class=FileResponse, tags=TAGS, description="""
Return a resampled template volume, based on labelled parcellation map.
""")
@version(*FASTAPI_VERSION)
@router_decorator(ROLE, func=resampled_template)
@router_decorator(ROLE, func=old_get_resampled_map)
def get_resampled_map(parcellation_id: str, space_id: str, *, func):
"""Get resampled map according to specification"""
if func is None:
Expand All @@ -50,6 +60,7 @@ def get_resampled_map(parcellation_id: str, space_id: str, *, func):
return FileResponse(full_filename, headers=headers)


# still use the old worker. New worker not stable (?)
@router.get("/labelled_map.nii.gz", response_class=FileResponse, tags=TAGS, description="""
Returns a labelled map if region_id is not provided.
Expand All @@ -58,7 +69,7 @@ def get_resampled_map(parcellation_id: str, space_id: str, *, func):
region_id MAY refer to ANY region on the region hierarchy, and a combined mask will be returned.
""")
@version(*FASTAPI_VERSION)
@router_decorator(ROLE, func=labelled_map_nii_gz)
@router_decorator(ROLE, func=old_get_parcellation_labelled_map)
def get_parcellation_labelled_map(parcellation_id: str, space_id: str, region_id: str=None, *, func):
"""Get labelled map according to specification"""
if func is None:
Expand All @@ -76,13 +87,14 @@ def get_parcellation_labelled_map(parcellation_id: str, space_id: str, region_id
return FileResponse(full_filename, headers=headers)


# still use the old worker. New worker not stable (?)
@router.get("/statistical_map.nii.gz", response_class=FileResponse, tags=TAGS, description="""
Returns a statistic map.
region_id MUST refer to leaf region on the region hierarchy.
""")
@version(*FASTAPI_VERSION)
@router_decorator(ROLE, func=statistical_map_nii_gz)
@router_decorator(ROLE, func=old_get_region_statistic_map)
def get_region_statistical_map(parcellation_id: str, region_id: str, space_id: str, name: str="", *, func):
"""Get statistical map according to specification"""
if func is None:
Expand All @@ -92,7 +104,7 @@ def get_region_statistical_map(parcellation_id: str, region_id: str, space_id: s
"content-type": "application/octet-stream",
"content-disposition": f'attachment; filename="statistical_map.nii.gz"'
}
full_filename, cache_flag = func(parcellation_id=parcellation_id, region_id=region_id, space_id=space_id, name=name)
full_filename, cache_flag = func(parcellation_id=parcellation_id, region_id=region_id, space_id=space_id)
if cache_flag:
headers[cache_header] = "hit"
assert os.path.isfile(full_filename), f"file saved incorrectly"
Expand All @@ -102,15 +114,17 @@ class StatisticModelInfo(BaseModel):
min: float
max: float


# still use the old worker. New worker not stable (?)
@router.get("/statistical_map.info.json", response_model=StatisticModelInfo, tags=TAGS)
@version(*FASTAPI_VERSION)
@router_decorator(ROLE, func=statistical_map_info_json)
@router_decorator(ROLE, func=old_get_region_statistic_map_info)
def get_region_statistical_map_metadata(parcellation_id: str, region_id: str, space_id: str, name: str="", *, func):
"""Get metadata of statistical map according to specification"""
if func is None:
raise HTTPException(500, f"func: None passsed")

data = func(parcellation_id=parcellation_id, region_id=region_id, space_id=space_id, name=name)
data = func(parcellation_id=parcellation_id, region_id=region_id, space_id=space_id)
return StatisticModelInfo(**data)

@router.get("/assign", response_model=DataFrameModel, tags=TAGS)
Expand Down
2 changes: 1 addition & 1 deletion api/siibra_api_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def get_config_dir_short_hash(path_to_config: str):
"features",
"volumes",
"compounds",
"vocabularies",
# "vocabularies",
]

class CELERY_CONFIG:
Expand Down

0 comments on commit 55c7a44

Please sign in to comment.