imaging-server-kit 0.0.4__tar.gz → 0.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/Dockerfile +1 -2
  2. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/Dockerfile-GPU +1 -1
  3. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/PKG-INFO +15 -9
  4. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/README.md +6 -5
  5. imaging_server_kit-0.0.6/main.py +10 -0
  6. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/pyproject.toml +6 -2
  7. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit/__init__.py +10 -2
  8. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit/_version.py +9 -4
  9. imaging_server_kit-0.0.4/src/imaging_server_kit/registry.py → imaging_server_kit-0.0.6/src/imaging_server_kit/algorithm_hub.py +1 -1
  10. imaging_server_kit-0.0.6/src/imaging_server_kit/authenticated_server.py +249 -0
  11. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit/client.py +30 -4
  12. imaging_server_kit-0.0.6/src/imaging_server_kit/geometry.py +248 -0
  13. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit/serialization.py +20 -4
  14. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit/server.py +66 -27
  15. imaging_server_kit-0.0.6/src/imaging_server_kit/static/js/login_script.js +24 -0
  16. imaging_server_kit-0.0.6/src/imaging_server_kit/static/js/register_script.js +15 -0
  17. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit/templates/index.html +1 -1
  18. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit/templates/info.html +1 -1
  19. imaging_server_kit-0.0.6/src/imaging_server_kit/templates/login.html +27 -0
  20. imaging_server_kit-0.0.6/src/imaging_server_kit/templates/register.html +27 -0
  21. imaging_server_kit-0.0.6/src/imaging_server_kit/users_utils/__init__.py +3 -0
  22. imaging_server_kit-0.0.6/src/imaging_server_kit/users_utils/db.py +34 -0
  23. imaging_server_kit-0.0.6/src/imaging_server_kit/users_utils/schemas.py +15 -0
  24. imaging_server_kit-0.0.6/src/imaging_server_kit/users_utils/users.py +55 -0
  25. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit/web_demo.py +6 -6
  26. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit.egg-info/PKG-INFO +15 -9
  27. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit.egg-info/SOURCES.txt +14 -5
  28. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit.egg-info/requires.txt +4 -0
  29. imaging_server_kit-0.0.6/start_algorithm_hub.py +8 -0
  30. imaging_server_kit-0.0.4/main.py +0 -8
  31. imaging_server_kit-0.0.4/src/imaging_server_kit/geometry.py +0 -145
  32. imaging_server_kit-0.0.4/start_registry.py +0 -8
  33. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/.dockerignore +0 -0
  34. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/.gitattributes +0 -0
  35. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/.github/workflows/publish.yml +0 -0
  36. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/.gitignore +0 -0
  37. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/LICENSE +0 -0
  38. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/MANIFEST.in +0 -0
  39. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/build.sh +0 -0
  40. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/examples/.gitkeep +0 -0
  41. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/setup.cfg +0 -0
  42. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit/assets/style.css +0 -0
  43. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit/encoding.py +0 -0
  44. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit/errors.py +0 -0
  45. {imaging_server_kit-0.0.4/src/imaging_server_kit/static → imaging_server_kit-0.0.6/src/imaging_server_kit/static/css}/index.css +0 -0
  46. {imaging_server_kit-0.0.4/src/imaging_server_kit/static → imaging_server_kit-0.0.6/src/imaging_server_kit/static/css}/info.css +0 -0
  47. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit.egg-info/dependency_links.txt +0 -0
  48. {imaging_server_kit-0.0.4 → imaging_server_kit-0.0.6}/src/imaging_server_kit.egg-info/top_level.txt +0 -0
@@ -12,5 +12,4 @@ RUN python -m pip install -e .
12
12
 
13
13
  EXPOSE 8000
14
14
 
15
- CMD ["python3", "main.py"]
16
- # CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
15
+ CMD ["python3", "main.py"]
@@ -11,4 +11,4 @@ RUN python -m pip install -e .
11
11
 
12
12
  EXPOSE 8000
13
13
 
14
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
14
+ CMD ["python3", "main.py"]
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: imaging-server-kit
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: Deploy image processing algorithms in FastAPI servers.
5
5
  Author-email: Mallory Wittwer <mallory.wittwer@epfl.ch>
6
6
  License: Copyright (c) 2024, EPFL
@@ -31,8 +31,8 @@ License: Copyright (c) 2024, EPFL
31
31
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32
32
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
33
 
34
- Project-URL: homepage, https://gitlab.com/epfl-center-for-imaging/imaging-server-kit
35
- Project-URL: repository, https://gitlab.com/epfl-center-for-imaging/imaging-server-kit
34
+ Project-URL: homepage, https://github.com/Imaging-Server-Kit/imaging-server-kit
35
+ Project-URL: repository, https://github.com/Imaging-Server-Kit/imaging-server-kit
36
36
  Classifier: Development Status :: 2 - Pre-Alpha
37
37
  Classifier: Intended Audience :: Developers
38
38
  Classifier: License :: OSI Approved :: BSD License
@@ -50,9 +50,13 @@ License-File: LICENSE
50
50
  Requires-Dist: fastapi
51
51
  Requires-Dist: uvicorn[standard]
52
52
  Requires-Dist: python-multipart
53
+ Requires-Dist: fastapi-users[sqlalchemy]
54
+ Requires-Dist: aiosqlite
53
55
  Requires-Dist: tifffile
54
56
  Requires-Dist: numpy
55
57
  Requires-Dist: scikit-image
58
+ Requires-Dist: opencv-contrib-python-headless
59
+ Requires-Dist: imantics
56
60
  Requires-Dist: geojson
57
61
  Requires-Dist: jinja2
58
62
  Requires-Dist: pyyaml
@@ -60,6 +64,7 @@ Requires-Dist: dash
60
64
  Requires-Dist: dash-bootstrap-components
61
65
  Requires-Dist: a2wsgi
62
66
  Requires-Dist: pytest
67
+ Dynamic: license-file
63
68
 
64
69
  ![EPFL Center for Imaging logo](https://imaging.epfl.ch/resources/logo-for-gitlab.svg)
65
70
  # 🪐 Imaging Server Kit
@@ -89,18 +94,19 @@ All of these algorithm servers can be installed locally or built and run with do
89
94
 
90
95
  ```{docker-compose.yml}
91
96
  services:
92
- servers_registry:
93
- image: mallorywittwerepfl/imaging-server-kit:registry
97
+ algorithm_hub:
98
+ image: mallorywittwerepfl/imaging-server-kit:latest
94
99
  ports:
95
100
  - "8000:8000"
101
+ command: python3 start_algorithm_hub.py
96
102
  rembg:
97
103
  image: registry.rcp.epfl.ch/imaging-server-kit/serverkit-rembg:latest
98
104
  depends_on:
99
- - servers_registry
105
+ - algorithm_hub
100
106
  skimage-log:
101
107
  image: registry.rcp.epfl.ch/imaging-server-kit/serverkit-skimage-log:latest
102
108
  depends_on:
103
- - servers_registry
109
+ - algorithm_hub
104
110
  ```
105
111
 
106
112
  Start the server with
@@ -170,7 +176,7 @@ Build the `imaging-server-kit` docker image for a specific Python version:
170
176
  docker build --build-arg PYTHON_VERSION=3.9 -t imaging-server-kit:3.9 .
171
177
  ```
172
178
 
173
- Run [build.sh](./build.sh) to build docker images for Python 3.9, 3.10, 3.11, GPU and the server kit registry:
179
+ Run [build.sh](./build.sh) to build docker images for Python 3.9, 3.10, 3.11, GPU and the algorithm hub:
174
180
 
175
181
  ```
176
182
  bash ./build.sh
@@ -26,18 +26,19 @@ All of these algorithm servers can be installed locally or built and run with do
26
26
 
27
27
  ```{docker-compose.yml}
28
28
  services:
29
- servers_registry:
30
- image: mallorywittwerepfl/imaging-server-kit:registry
29
+ algorithm_hub:
30
+ image: mallorywittwerepfl/imaging-server-kit:latest
31
31
  ports:
32
32
  - "8000:8000"
33
+ command: python3 start_algorithm_hub.py
33
34
  rembg:
34
35
  image: registry.rcp.epfl.ch/imaging-server-kit/serverkit-rembg:latest
35
36
  depends_on:
36
- - servers_registry
37
+ - algorithm_hub
37
38
  skimage-log:
38
39
  image: registry.rcp.epfl.ch/imaging-server-kit/serverkit-skimage-log:latest
39
40
  depends_on:
40
- - servers_registry
41
+ - algorithm_hub
41
42
  ```
42
43
 
43
44
  Start the server with
@@ -107,7 +108,7 @@ Build the `imaging-server-kit` docker image for a specific Python version:
107
108
  docker build --build-arg PYTHON_VERSION=3.9 -t imaging-server-kit:3.9 .
108
109
  ```
109
110
 
110
- Run [build.sh](./build.sh) to build docker images for Python 3.9, 3.10, 3.11, GPU and the server kit registry:
111
+ Run [build.sh](./build.sh) to build docker images for Python 3.9, 3.10, 3.11, GPU and the algorithm hub:
111
112
 
112
113
  ```
113
114
  bash ./build.sh
@@ -0,0 +1,10 @@
1
+ import uvicorn
2
+ import imaging_server_kit as serverkit
3
+
4
+ server = serverkit.AlgorithmServer(
5
+ algorithm_name="foo", parameters_model=serverkit.Parameters
6
+ )
7
+ app = server.app
8
+
9
+ if __name__ == "__main__":
10
+ uvicorn.run("main:app", host="0.0.0.0", port=8000)
@@ -25,9 +25,13 @@ dependencies = [
25
25
  "fastapi",
26
26
  "uvicorn[standard]",
27
27
  "python-multipart",
28
+ "fastapi-users[sqlalchemy]",
29
+ "aiosqlite",
28
30
  "tifffile",
29
31
  "numpy",
30
32
  "scikit-image",
33
+ "opencv-contrib-python-headless",
34
+ "imantics",
31
35
  "geojson",
32
36
  "jinja2", # For the /info route
33
37
  "pyyaml", # For the /info route
@@ -38,8 +42,8 @@ dependencies = [
38
42
  ]
39
43
 
40
44
  [project.urls]
41
- homepage = "https://gitlab.com/epfl-center-for-imaging/imaging-server-kit"
42
- repository = "https://gitlab.com/epfl-center-for-imaging/imaging-server-kit"
45
+ homepage = "https://github.com/Imaging-Server-Kit/imaging-server-kit"
46
+ repository = "https://github.com/Imaging-Server-Kit/imaging-server-kit"
43
47
 
44
48
  [build-system]
45
49
  requires = ["setuptools>=42.0.0", "wheel", "setuptools_scm"]
@@ -1,12 +1,14 @@
1
1
  from ._version import version as __version__
2
+
2
3
  try:
3
4
  from ._version import version as __version__
4
5
  except ImportError:
5
6
  __version__ = "unknown"
6
7
 
7
8
  from imaging_server_kit.client import Client
8
- from imaging_server_kit.server import Server, Parameters
9
- from imaging_server_kit.registry import Registry
9
+ from imaging_server_kit.server import AlgorithmServer, Parameters
10
+ from imaging_server_kit.authenticated_server import AuthenticatedAlgorithmServer
11
+ from imaging_server_kit.algorithm_hub import AlgorithmHub
10
12
  from imaging_server_kit.encoding import encode_contents, decode_contents
11
13
  from imaging_server_kit.serialization import (
12
14
  serialize_result_tuple,
@@ -14,7 +16,13 @@ from imaging_server_kit.serialization import (
14
16
  )
15
17
  from imaging_server_kit.geometry import (
16
18
  mask2features,
19
+ instance_mask2features,
17
20
  features2mask,
21
+ features2instance_mask,
22
+ features2mask_3d,
23
+ mask2features_3d,
24
+ instance_mask2features_3d,
25
+ features2instance_mask_3d,
18
26
  boxes2features,
19
27
  features2boxes,
20
28
  points2features,
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '0.0.4'
16
- __version_tuple__ = version_tuple = (0, 0, 4)
20
+ __version__ = version = '0.0.6'
21
+ __version_tuple__ = version_tuple = (0, 0, 6)
@@ -11,7 +11,7 @@ static_dir = importlib.resources.files("imaging_server_kit").joinpath("static")
11
11
  templates = Jinja2Templates(directory=str(templates_dir))
12
12
 
13
13
 
14
- class Registry:
14
+ class AlgorithmHub:
15
15
  def __init__(self) -> None:
16
16
  self.app = FastAPI()
17
17
  self.app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
@@ -0,0 +1,249 @@
1
+ import importlib.resources
2
+ import os
3
+ from typing import List, Tuple, Type
4
+
5
+ import imaging_server_kit as serverkit
6
+ import requests
7
+ import yaml
8
+ from fastapi import FastAPI, Request, status, HTTPException, Depends
9
+ from fastapi.responses import HTMLResponse
10
+ from fastapi.staticfiles import StaticFiles
11
+ from fastapi.templating import Jinja2Templates
12
+ from imaging_server_kit.web_demo import generate_dash_app
13
+ from pydantic import BaseModel, ConfigDict
14
+ from a2wsgi import WSGIMiddleware
15
+ from contextlib import asynccontextmanager
16
+ import asyncio
17
+
18
+ templates_dir = importlib.resources.files("imaging_server_kit").joinpath("templates")
19
+ static_dir = importlib.resources.files("imaging_server_kit").joinpath("static")
20
+
21
+ templates = Jinja2Templates(directory=str(templates_dir))
22
+
23
+ PROCESS_TIMEOUT_SEC = 3600
24
+
25
+ from imaging_server_kit.users_utils import (
26
+ create_db_and_tables,
27
+ fastapi_users,
28
+ auth_backend,
29
+ UserRead,
30
+ UserUpdate,
31
+ UserCreate,
32
+ User,
33
+ current_active_user,
34
+ )
35
+
36
+
37
+ def load_from_yaml(file_path: str):
38
+ with open(file_path, "r") as file:
39
+ data = yaml.safe_load(file)
40
+ return data
41
+
42
+
43
+ def parse_algo_params_schema(algo_params_schema):
44
+ algo_params = algo_params_schema.get("properties")
45
+ required_params = algo_params_schema.get("required")
46
+ for param in algo_params.keys():
47
+ algo_params[param]["required"] = param in required_params
48
+ return algo_params
49
+
50
+
51
+ ALGORITHM_HUB_URL = os.getenv("ALGORITHM_HUB_URL", "http://algorithm_hub:8000")
52
+
53
+
54
+ class Parameters(BaseModel):
55
+ ...
56
+
57
+ model_config = ConfigDict(extra="forbid")
58
+
59
+
60
+ class AuthenticatedAlgorithmServer:
61
+ def __init__(
62
+ self,
63
+ algorithm_name: str,
64
+ parameters_model: Type[BaseModel],
65
+ metadata_file: str = None,
66
+ ):
67
+ self.algorithm_name = algorithm_name
68
+ self.parameters_model = parameters_model
69
+
70
+ if metadata_file is None:
71
+ self.metadata_file = "metadata.yaml"
72
+ else:
73
+ self.metadata_file = metadata_file
74
+
75
+ self.app = FastAPI(title=algorithm_name, lifespan=self.lifespan)
76
+
77
+ # Users
78
+ self.app.include_router(
79
+ fastapi_users.get_auth_router(auth_backend),
80
+ prefix="/auth/jwt",
81
+ tags=["auth"],
82
+ )
83
+ self.app.include_router(
84
+ fastapi_users.get_register_router(UserRead, UserCreate),
85
+ prefix="/auth",
86
+ tags=["auth"],
87
+ )
88
+ self.app.include_router(
89
+ fastapi_users.get_reset_password_router(),
90
+ prefix="/auth",
91
+ tags=["auth"],
92
+ )
93
+ self.app.include_router(
94
+ fastapi_users.get_verify_router(UserRead),
95
+ prefix="/auth",
96
+ tags=["auth"],
97
+ )
98
+ self.app.include_router(
99
+ fastapi_users.get_users_router(UserRead, UserUpdate),
100
+ prefix="/users",
101
+ tags=["users"],
102
+ )
103
+
104
+ # Info HTML
105
+ self.app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
106
+
107
+ # Web demo app
108
+ algo_params = parameters_model.model_json_schema().get("properties")
109
+
110
+ dash_app = generate_dash_app(
111
+ algorithm_name,
112
+ algo_params,
113
+ run_fnct=self.run_algorithm,
114
+ sample_image_fnct=self.load_sample_images,
115
+ prefix=f"/{algorithm_name}/demo/",
116
+ )
117
+
118
+ self.app.mount(f"/{algorithm_name}/demo", WSGIMiddleware(dash_app.server))
119
+
120
+ self.register_routes()
121
+
122
+ self.services = [self.algorithm_name]
123
+
124
+ @asynccontextmanager
125
+ async def lifespan(self, app: FastAPI):
126
+ await self.register_with_algohub()
127
+ await create_db_and_tables()
128
+ yield
129
+ await self.deregister_from_algohub()
130
+
131
+ async def register_with_algohub(self):
132
+ try:
133
+ response = requests.get(f"{ALGORITHM_HUB_URL}/")
134
+ except Exception:
135
+ print("Algorithm hub unavailable.")
136
+ return
137
+
138
+ response = requests.post(
139
+ f"{ALGORITHM_HUB_URL}/register",
140
+ json={
141
+ "name": self.algorithm_name,
142
+ "url": f"http://{self.algorithm_name}:8000",
143
+ },
144
+ )
145
+ if response.status_code == 201:
146
+ print(f"Service {self.algorithm_name} registered successfully.")
147
+ else:
148
+ print(f"Failed to register {self.algorithm_name}: {response.json()}")
149
+
150
+ async def deregister_from_algohub(self):
151
+ deregister_url = f"{ALGORITHM_HUB_URL}/deregister"
152
+ response = requests.post(deregister_url, json={"name": self.algorithm_name})
153
+ if response.status_code == 201:
154
+ print(f"Service {self.algorithm_name} deregistered.")
155
+ else:
156
+ print(f"Failed to deregister {self.algorithm_name}: {response.json()}")
157
+
158
+ def register_routes(self):
159
+ @self.app.get("/")
160
+ def home():
161
+ return list_services()
162
+
163
+ @self.app.get("/services")
164
+ def list_services():
165
+ return {"services": self.services}
166
+
167
+ @self.app.get("/version")
168
+ def get_version():
169
+ return serverkit.__version__
170
+
171
+ @self.app.get(f"/{self.algorithm_name}/info", response_class=HTMLResponse)
172
+ def info(request: Request):
173
+ algo_info = load_from_yaml(self.metadata_file)
174
+ algo_params_schema = get_algo_params()
175
+ algo_params = parse_algo_params_schema(algo_params_schema)
176
+ return templates.TemplateResponse(
177
+ "info.html",
178
+ {
179
+ "request": request,
180
+ "algo_info": algo_info,
181
+ "algo_params": algo_params,
182
+ },
183
+ )
184
+
185
+ @self.app.get(f"/register", response_class=HTMLResponse)
186
+ def register(request: Request):
187
+ return templates.TemplateResponse("register.html", {"request": request})
188
+
189
+ @self.app.get(f"/login", response_class=HTMLResponse)
190
+ def login(request: Request):
191
+ return templates.TemplateResponse("login.html", {"request": request})
192
+
193
+ @self.app.post(
194
+ f"/{self.algorithm_name}/process", status_code=status.HTTP_201_CREATED
195
+ )
196
+ async def run_algo(
197
+ algo_params: self.parameters_model,
198
+ user: User = Depends(current_active_user),
199
+ ):
200
+ try:
201
+ result_data_tuple = await asyncio.wait_for(
202
+ self._run_algorithm(**algo_params.dict()),
203
+ timeout=PROCESS_TIMEOUT_SEC,
204
+ )
205
+ except (
206
+ asyncio.TimeoutError
207
+ ): # This works but doesn't actually kill the process... we need something else to terminate it
208
+ raise HTTPException(
209
+ status_code=504, detail="Request timeout during processing."
210
+ )
211
+ try:
212
+ serialized_results = await asyncio.wait_for(
213
+ self._serialize_result_tuple(result_data_tuple),
214
+ timeout=PROCESS_TIMEOUT_SEC,
215
+ )
216
+ except asyncio.TimeoutError:
217
+ raise HTTPException(
218
+ status_code=504, detail="Request timeout during serialization."
219
+ )
220
+
221
+ return serialized_results
222
+
223
+ @self.app.get(f"/{self.algorithm_name}/parameters", response_model=dict)
224
+ def get_algo_params():
225
+ return self.parameters_model.model_json_schema()
226
+
227
+ @self.app.get(f"/{self.algorithm_name}/sample_images", response_model=dict)
228
+ def get_sample_images():
229
+ images = self.load_sample_images()
230
+ encoded_images = [
231
+ {"sample_image": serverkit.encode_contents(image)} for image in images
232
+ ]
233
+ return {"sample_images": encoded_images}
234
+
235
+ async def _serialize_result_tuple(self, result_data_tuple):
236
+ serialized_results = await asyncio.to_thread(
237
+ serverkit.serialize_result_tuple, result_data_tuple
238
+ )
239
+ return serialized_results
240
+
241
+ async def _run_algorithm(self, **algo_params):
242
+ result_data_tuple = await asyncio.to_thread(self.run_algorithm, **algo_params)
243
+ return result_data_tuple
244
+
245
+ def load_sample_images(self) -> List["np.ndarray"]:
246
+ raise NotImplementedError("Subclasses should implement this method")
247
+
248
+ def run_algorithm(self, **algo_params) -> List[Tuple]:
249
+ raise NotImplementedError("Subclasses should implement this method")
@@ -1,4 +1,5 @@
1
1
  import requests
2
+ import webbrowser
2
3
  from typing import List, Dict, Tuple
3
4
  import numpy as np
4
5
  import imaging_server_kit as serverkit
@@ -17,6 +18,8 @@ class Client:
17
18
  self._algorithms = {}
18
19
  if server_url != "":
19
20
  self.connect(server_url)
21
+
22
+ self.token = None
20
23
 
21
24
  def connect(self, server_url: str) -> None:
22
25
  self.server_url = server_url
@@ -31,6 +34,31 @@ class Client:
31
34
  self.algorithms = services
32
35
  else:
33
36
  raise AlgorithmServerError(response.status_code, response.text)
37
+
38
+ # def browser_login(self):
39
+ # webbrowser.open(f"{self.server_url}/login")
40
+ # # How to retreive the access_token when the user logs in via the web page?
41
+ # # self.token = [...]
42
+ # # This might be complicated...
43
+
44
+ def login(self, username, password):
45
+ endpoint = f"{self.server_url}/auth/jwt/login"
46
+ try:
47
+ response = requests.post(
48
+ endpoint,
49
+ data={
50
+ "username": username,
51
+ "password": password,
52
+ },
53
+ )
54
+ except requests.exceptions.RequestException as e:
55
+ raise ServerRequestError(endpoint, e)
56
+
57
+ if response.status_code == 200:
58
+ token = response.json().get("access_token")
59
+ self.token = token
60
+ else:
61
+ raise AlgorithmServerError(response.status_code, response.text)
34
62
 
35
63
  @property
36
64
  def server_url(self) -> str:
@@ -60,6 +88,7 @@ class Client:
60
88
  headers={
61
89
  "Content-Type": "application/json",
62
90
  "accept": "application/json",
91
+ "Authorization": f"Bearer {self.token}",
63
92
  },
64
93
  )
65
94
  except requests.exceptions.RequestException as e:
@@ -111,10 +140,7 @@ class Client:
111
140
  return image
112
141
  return images
113
142
  else:
114
- print(
115
- f"Error status while attempting to get algorithm sample images: {response.status_code}"
116
- )
117
- return -1
143
+ raise AlgorithmServerError(response.status_code, response.text)
118
144
 
119
145
  def _validate_algorithm(self, algorithm=None) -> str:
120
146
  if algorithm is None: