Skip to content

Commit

Permalink
Merge pull request #147 from FZJ-INM1-BDA/feat_compoundFt
Browse files Browse the repository at this point in the history
feat: add gene endpoint
  • Loading branch information
xgui3783 authored Jan 17, 2024
2 parents a445457 + da22096 commit f9fcead
Show file tree
Hide file tree
Showing 19 changed files with 385 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .helm/siibra-api/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
replicaCount: 1

sapiVersion: "0.3.15" # "latest" or "0.3.15"
sapiWorkerQueues: ["core", "features", "volumes", "compounds"]
sapiWorkerQueues: ["core", "features", "volumes", "compounds", "vocabularies"]
sapiFlavor: "prod" # could be prod, rc, latest, etc

image:
Expand Down
1 change: 1 addition & 0 deletions api/common/data_handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from . import features
from . import volumes
from . import compounds
from . import vocabularies

from api.siibra_api_config import ROLE

Expand Down
1 change: 1 addition & 0 deletions api/common/data_handlers/vocabularies/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import gene
24 changes: 24 additions & 0 deletions api/common/data_handlers/vocabularies/gene.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 api.serialization.util.siibra 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]
2 changes: 1 addition & 1 deletion api/models/_commons.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pydantic import Field, BaseModel
from abc import ABC

SIIBRA_PYTHON_VERSION = "0.4"
SIIBRA_PYTHON_VERSION = "1.0"

ignore_cls=(
"BrainAtlasVersionModel",
Expand Down
Empty file.
7 changes: 7 additions & 0 deletions api/models/vocabularies/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from api.models._commons import (
ConfigBaseModel,
)
from abc import ABC

class _VocabBaseModel(ConfigBaseModel, ABC, type="vocabulary"):
...
5 changes: 5 additions & 0 deletions api/models/vocabularies/genes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .base import _VocabBaseModel

class GeneModel(_VocabBaseModel, type="gene"):
symbol: str
description: str
1 change: 1 addition & 0 deletions api/serialization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
_retrieval,
_common,
locations,
# vocabularies, # For now, there are no serialization to be done on vocabolaries
)

2 changes: 2 additions & 0 deletions api/serialization/util/siibra.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@

from siibra import parcellations, spaces, atlases

from siibra.vocabularies import GENE_NAMES

import siibra
2 changes: 2 additions & 0 deletions api/server/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .volumes import prefixed_routers as volume_prefixed_routers
from .compounds import prefixed_routers as compound_prefixed_routers
from .features import router as feature_router
from .volcabularies import router as vocabolaries_router
from .metrics import prom_metrics_resp, on_startup as metrics_on_startup, on_terminate as metrics_on_terminate
from .code_snippet import get_sourcecode

Expand All @@ -37,6 +38,7 @@
siibra_api.include_router(prefix_router.router, prefix=prefix_router.prefix)

siibra_api.include_router(feature_router, prefix="/feature")
siibra_api.include_router(vocabolaries_router, prefix="/vocabularies")

add_pagination(siibra_api)

Expand Down
25 changes: 25 additions & 0 deletions api/server/volcabularies/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from fastapi import APIRouter, HTTPException
from fastapi_pagination import paginate, Page
from fastapi_versioning import version

from api.server.util import SapiCustomRoute
from api.server import FASTAPI_VERSION
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

TAGS= ["vocabularies"]
"""HTTP vocabularies tags"""

router = APIRouter(route_class=SapiCustomRoute, tags=TAGS)
"""HTTP vocabularies router"""

@router.get("/genes", response_model=Page[GeneModel])
@version(*FASTAPI_VERSION)
@router_decorator(ROLE, func=get_genes)
def get_genes(find:str=None, func=None):
"""HTTP get (filtered) genes"""
if func is None:
raise HTTPException(500, "func: None passed")
return paginate(func(find=find))
1 change: 1 addition & 0 deletions api/siibra_api_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def get_config_dir_short_hash(path_to_config: str):
"features",
"volumes",
"compounds",
"vocabularies",
]

class CELERY_CONFIG:
Expand Down
24 changes: 24 additions & 0 deletions bla.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
feature_types = [
((), "FunctionalConnectivity",)
((), "ReceptorDensityFingerprint",)
((), "ReceptorDensityProfile",)
((), "MRIVolumeOfInterest",)
((), "BlockfaceVolumeOfInterest",)
((), "RegionalBOLD",)
((), "PLIVolumeOfInterest",)
((), "DTIVolumeOfInterest",)
((), "TracingConnectivity",)
((), "StreamlineLengths",)
((), "StreamlineCounts",)
((), "AnatomoFunctionalConnectivity",)
]

import os
from subprocess import run
MONITOR_FIRSTLVL_DIR = os.getenv("MONITOR_FIRSTLVL_DIR")
dirs = os.listdir(MONITOR_FIRSTLVL_DIR)
for dir in dirs:
result = run(["du", "-s", f"{MONITOR_FIRSTLVL_DIR}/{dir}"], capture_output=True, text=True)
size_b, *_ = result.stdout.split("\t")
print(size_b)
print("dir", size_b, int(size_b))
88 changes: 69 additions & 19 deletions docs/develop.example.adding_serialization.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
siibra v0.5 introduced compound features. New serialization strategies and models need to be introduced in order to take full advantage of the introduction of compound features.

See the [commit](https://github.com/FZJ-INM1-BDA/siibra-api/commit/6723cfa3e612de66022150b6d5e645caddbc3e7a)

## Import the new class

`api.serialization.util.siibra` Serves as the one and single entrypoint to siibra package. To access the new class `CompoundFeature`, import it here.

```python
```diff
# in api.serialization.util.siibra

# ... trimmed for brevity

from siibra.feature.feature import CompoundFeature
+ from siibra.feature.feature import CompoundFeature
```

## Adding serialization model
Expand All @@ -20,38 +22,86 @@ Since `CompoundFeature` subclasses `Feature`, it is quite natural to have the mo

Here, we also added the property `subfeature_keys`, which is the additional property of `CompoundFeatureModel` compared to `FeatureModel` see [more detail](../api.models/#api.models._commons.ConfigBaseModel.__init_subclass__)

```python
```diff
# in api.models.features._basetypes.feature

# ... trimmed for brevity

class CompoundFeatureModel(_FeatureModel, type="compound_feature"):
"""CompoundFeatureModel"""
subfeature_keys: List[str]
from api.models.features.anchor import SiibraAnchorModel
- from typing import List, Optional
+ from api.models.locations.point import CoordinatePointModel
+ from typing import List, Optional, Union
from abc import ABC

# ... trimmed for brevity


+ class SubfeatureModel(ConfigBaseModel):
+ id: str
+ index: Union[str, CoordinatePointModel]
+ name: str
+
+ class CompoundFeatureModel(_FeatureModel, type="compoundfeature"):
+ indices: List[SubfeatureModel]
```

## Adding serialization strategy

We then add the logic of serialization.

```python
```diff
# in api.serialization.features.feature

from api.serialization.util.siibra import CompoundFeature
from api.models.features._basetypes.feature import CompoundFeatureModel
# ... trimmed for brevity

- from api.serialization.util.siibra import Feature
+ from api.serialization.util.siibra import Feature, CompoundFeature
from api.serialization.util import serialize, instance_to_model
- from api.models.features._basetypes.feature import FeatureModel
+ from api.models.features._basetypes.feature import FeatureModel, CompoundFeatureModel, SubfeatureModel
+
+ @serialize(CompoundFeature, pass_super_model=True)
+ def cmpdfeature_to_model(cf: CompoundFeature, detail: bool=False, super_model_dict={}, **kwargs):
+ return CompoundFeatureModel(
+ **super_model_dict,
+ indices=[SubfeatureModel(id=ft.id, index=instance_to_model(idx), name=ft.name) for (idx, ft) in zip(cf.indices, cf.elements)]
+ )
+


```

## Updating response models

Whilst the update to serve compound feature does not require the addition of additional REST endpoints, several existing endpoints needs to be updated, as their response would contain compound feature model *in addition* to the previous models.

```diff
# api.server.features.__init__

# ... trimmed for brevity

+ from api.models.features._basetypes.feature import (
+ CompoundFeatureModel
+ )

# ... trimmed for brevity

# Regional Connectivity
- RegionalConnectivityModels = SiibraRegionalConnectivityModel
+ RegionalConnectivityModels = Union[SiibraRegionalConnectivityModel, CompoundFeatureModel]

# ... trimmed for brevity

# Cortical Profiles
- CortialProfileModels = SiibraCorticalProfileModel
+ CortialProfileModels = Union[SiibraCorticalProfileModel, CompoundFeatureModel]

# ... trimmed for brevity

from api.serialization.util import (
serialize,
instance_to_model
)
# Tabular
- TabularModels = Union[SiibraCorticalProfileModel, SiibraReceptorDensityFp, SiibraTabularModel]
+ TabularModels = Union[CompoundFeatureModel, SiibraCorticalProfileModel, SiibraReceptorDensityFp, SiibraTabularModel]

@serialize(CompoundFeature, pass_super_model=True)
def serialize_cf(cf: CompoundFeature, detail=False, super_model_dict={}, **kwargs) -> CompoundFeatureModel:
return CompoundFeatureModel(
**super_model_dict,
subfeature_keys=cf.subfeature_keys
)

```

Expand Down
Loading

0 comments on commit f9fcead

Please sign in to comment.