Skip to content

Commit 46b80e8

Browse files
committed
feat(apiclient): add CRUD methods for instantiated entities
1 parent d2dd658 commit 46b80e8

File tree

3 files changed

+97
-7
lines changed

3 files changed

+97
-7
lines changed

.flake8

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[flake8]
2-
ignore = E226,E302,E41,W503
2+
ignore = E226,E302,E41,W503,E704
33
max-line-length = 88
44
exclude = tests/*

.github/workflows/push.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
uses: TrueBrain/actions-flake8@master
2626
with:
2727
max_line_length: 88
28-
extend-ignore: E203
28+
extend-ignore: E203, E704
2929
test:
3030
runs-on: ubuntu-latest
3131
strategy:

cellengine/utils/api_client/APIClient.py

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
from __future__ import annotations
2-
import os
2+
from functools import lru_cache
3+
from getpass import getpass
34
import json
5+
import os
6+
from typing import Any, Dict, List, Optional, TypeVar, Union, overload
7+
48
import pandas
5-
from getpass import getpass
6-
from typing import Any, Dict, List, Union, Optional
7-
from functools import lru_cache
89
from requests_toolbelt.multipart.encoder import MultipartEncoder
910

10-
from cellengine.utils.api_client.BaseAPIClient import BaseAPIClient
11+
from cellengine.utils import converter
1112
from cellengine.utils.api_client.APIError import APIError
13+
from cellengine.utils.api_client.BaseAPIClient import BaseAPIClient
1214
from cellengine.utils.singleton import Singleton
15+
1316
from ...resources.attachment import Attachment
1417
from ...resources.compensation import Compensation
1518
from ...resources.experiment import Experiment
@@ -20,6 +23,14 @@
2023
from ...resources.scaleset import ScaleSet
2124

2225

26+
CE = TypeVar(
27+
"CE",
28+
bound=Union[
29+
Attachment, Compensation, Experiment, FcsFile, Gate, Plot, Population, ScaleSet
30+
],
31+
)
32+
33+
2334
class APIClient(BaseAPIClient, metaclass=Singleton):
2435
_API_NAME = "CellEngine Python Toolkit"
2536

@@ -119,6 +130,85 @@ def update_entity(self, experiment_id, _id, entity_type, body) -> dict:
119130
json=body,
120131
)
121132

133+
def _get_path(self, entity: CE) -> str:
134+
fullpath = f"{self.base_url}/{entity.path}"
135+
return fullpath
136+
137+
# fmt: off
138+
# temporary fix for https://github.com/psf/black/issues/1797
139+
@overload
140+
def create(self, entity: Attachment, **kwargs) -> Attachment: ...
141+
@overload
142+
def create(self, entity: Compensation, **kwargs) -> Compensation: ...
143+
@overload
144+
def create(self, entity: Experiment, **kwargs) -> Experiment: ...
145+
@overload
146+
def create(self, entity: FcsFile, **kwargs) -> FcsFile: ...
147+
@overload
148+
def create(self, entity: Gate, **kwargs) -> Gate: ...
149+
@overload
150+
def create(self, entity: Population, **kwargs) -> Population: ...
151+
@overload
152+
def create(self, entity: ScaleSet, **kwargs) -> ScaleSet: ...
153+
# fmt: on
154+
155+
def create(
156+
self, entity, **kwargs
157+
) -> Union[
158+
Attachment,
159+
Compensation,
160+
Experiment,
161+
FcsFile,
162+
Gate,
163+
Population,
164+
ScaleSet,
165+
]:
166+
"""Create a local entity on CellEngine."""
167+
168+
def unstructure_and_clean(entity, **kwargs):
169+
(cls, path, body) = (
170+
entity.__class__,
171+
entity.path,
172+
converter.unstructure(entity),
173+
)
174+
if body["_id"] == "None" or body["_id"] is None:
175+
del body["_id"] # https://github.com/primitybio/cellengine/issues/5800
176+
return (cls, path, body)
177+
178+
def post_and_restructure(cls, path, body, **kwargs):
179+
res = self._post(f"{self.base_url}/{path}", json=body, params=kwargs)
180+
return converter.structure(res, cls)
181+
182+
if isinstance(entity, list):
183+
body = [unstructure_and_clean(thing) for thing in entity]
184+
# group [(class), (path), (body)]
185+
unzipped = list(zip(*body))
186+
# remove duplicates to ensure all entities are of the same type
187+
(cls, path, body) = (set(unzipped[0]), set(unzipped[1]), unzipped[2])
188+
189+
if len(cls) == 1 and len(path) == 1:
190+
# then post to bulk endpoint
191+
res = post_and_restructure(
192+
List[cls.pop()], path.pop(), list(body) # type: ignore
193+
)
194+
else:
195+
# post each item individually
196+
res = [post_and_restructure(*thing) for thing in body]
197+
else:
198+
body = unstructure_and_clean(entity)
199+
res = post_and_restructure(*body)
200+
return res # type: ignore
201+
202+
def update(self, entity, params: Dict = None):
203+
path = self._get_path(entity)
204+
data = converter.unstructure(entity)
205+
res = self._patch(path, json=data, params=params)
206+
return converter.structure(res, entity.__class__)
207+
208+
def delete(self, entity, params: Dict = None) -> None:
209+
path = self._get_path(entity)
210+
self._delete(path, params=params)
211+
122212
def delete_entity(self, experiment_id, entity_type, _id):
123213
url = f"{self.base_url}/experiments/{experiment_id}/{entity_type}/{_id}"
124214
self._delete(url)

0 commit comments

Comments
 (0)