sectra-image-analysis-api 1.5.1__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.
- sectra_image_analysis_api-1.5.1/.github/workflows/docs.yml +25 -0
- sectra_image_analysis_api-1.5.1/.github/workflows/publish.yaml +35 -0
- sectra_image_analysis_api-1.5.1/.gitignore +53 -0
- sectra_image_analysis_api-1.5.1/LICENSE +21 -0
- sectra_image_analysis_api-1.5.1/PKG-INFO +143 -0
- sectra_image_analysis_api-1.5.1/README.md +88 -0
- sectra_image_analysis_api-1.5.1/docs/api/client.md +137 -0
- sectra_image_analysis_api-1.5.1/docs/api/mock-server.md +119 -0
- sectra_image_analysis_api-1.5.1/docs/api/schemas.md +171 -0
- sectra_image_analysis_api-1.5.1/docs/index.md +39 -0
- sectra_image_analysis_api-1.5.1/docs/quickstart.md +103 -0
- sectra_image_analysis_api-1.5.1/examples/fetch_and_cast_results.py +32 -0
- sectra_image_analysis_api-1.5.1/examples/local_dev.py +181 -0
- sectra_image_analysis_api-1.5.1/examples/patch_result.py +122 -0
- sectra_image_analysis_api-1.5.1/examples/primitive_result.py +58 -0
- sectra_image_analysis_api-1.5.1/mkdocs.yml +30 -0
- sectra_image_analysis_api-1.5.1/pyproject.toml +68 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/__init__.py +3 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/client.py +310 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/mock_server.py +468 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/__init__.py +96 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/common.py +46 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/image.py +88 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/info.py +8 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/invocation.py +92 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/quality_control.py +26 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/registration.py +29 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/results.py +111 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/utils/__init__.py +0 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/utils/decode_images.py +17 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/utils/errors.py +10 -0
- sectra_image_analysis_api-1.5.1/src/sectra_client/utils/helpers.py +32 -0
- sectra_image_analysis_api-1.5.1/uv.lock +1405 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Deploy docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- docs/**
|
|
8
|
+
- mkdocs.yml
|
|
9
|
+
tags:
|
|
10
|
+
- "v*.*.*"
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
deploy:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
cache: pip
|
|
24
|
+
- run: pip install mkdocs-material
|
|
25
|
+
- run: mkdocs gh-deploy --force
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
name: Build and publish
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
environment: pypi
|
|
13
|
+
permissions:
|
|
14
|
+
id-token: write # required for OIDC trusted publisher
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0 # needed for hatch-vcs to resolve the tag
|
|
20
|
+
|
|
21
|
+
- name: Ensure tag is on main
|
|
22
|
+
run: |
|
|
23
|
+
git fetch origin main
|
|
24
|
+
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
|
|
25
|
+
echo "Tagged commit is not on main. Aborting."
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
- uses: astral-sh/setup-uv@v5
|
|
30
|
+
|
|
31
|
+
- name: Build
|
|
32
|
+
run: uv build
|
|
33
|
+
|
|
34
|
+
- name: Publish to PyPI
|
|
35
|
+
run: uv publish
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# These are some examples of commonly ignored file patterns.
|
|
2
|
+
# You should customize this list as applicable to your project.
|
|
3
|
+
# Learn more about .gitignore:
|
|
4
|
+
# https://www.atlassian.com/git/tutorials/saving-changes/gitignore
|
|
5
|
+
|
|
6
|
+
# Node artifact files
|
|
7
|
+
.vscode/
|
|
8
|
+
node_modules/
|
|
9
|
+
dist/
|
|
10
|
+
|
|
11
|
+
# Compiled Java class files
|
|
12
|
+
*.class
|
|
13
|
+
|
|
14
|
+
# Compiled Python bytecode
|
|
15
|
+
*.py[cod]
|
|
16
|
+
|
|
17
|
+
# Log files
|
|
18
|
+
*.log
|
|
19
|
+
|
|
20
|
+
# Package files
|
|
21
|
+
*.jar
|
|
22
|
+
|
|
23
|
+
# Maven
|
|
24
|
+
target/
|
|
25
|
+
dist/
|
|
26
|
+
|
|
27
|
+
# JetBrains IDE
|
|
28
|
+
.idea/
|
|
29
|
+
|
|
30
|
+
# Unit test reports
|
|
31
|
+
TEST*.xml
|
|
32
|
+
|
|
33
|
+
# Generated by MacOS
|
|
34
|
+
.DS_Store
|
|
35
|
+
|
|
36
|
+
# Generated by Windows
|
|
37
|
+
Thumbs.db
|
|
38
|
+
|
|
39
|
+
# Applications
|
|
40
|
+
*.app
|
|
41
|
+
*.exe
|
|
42
|
+
*.war
|
|
43
|
+
|
|
44
|
+
# Large media files
|
|
45
|
+
*.mp4
|
|
46
|
+
*.tiff
|
|
47
|
+
*.avi
|
|
48
|
+
*.flv
|
|
49
|
+
*.mov
|
|
50
|
+
*.wmv
|
|
51
|
+
|
|
52
|
+
**/*.egg-info
|
|
53
|
+
**/*build
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) [year] [fullname]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sectra-image-analysis-api
|
|
3
|
+
Version: 1.5.1
|
|
4
|
+
Summary: A wrapper for the Sectra Image Analysis API.
|
|
5
|
+
Project-URL: Homepage, https://github.com/amspath/sectra-image-analysis-api
|
|
6
|
+
Project-URL: Repository, https://github.com/amspath/sectra-image-analysis-api
|
|
7
|
+
Author-email: Robin van Hoorn <r.d.vanhoorn@amsterdamumc.nl>
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) [year] [fullname]
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Keywords: WSI,digital pathology,medical imaging,whole slide image
|
|
31
|
+
Classifier: Development Status :: 4 - Beta
|
|
32
|
+
Classifier: Intended Audience :: Science/Research
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Operating System :: OS Independent
|
|
35
|
+
Classifier: Programming Language :: Python :: 3
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
41
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
42
|
+
Requires-Python: >=3.11
|
|
43
|
+
Requires-Dist: hl7>=0.4.5
|
|
44
|
+
Requires-Dist: pillow>=10.0.0
|
|
45
|
+
Requires-Dist: pydantic>=2.0.0
|
|
46
|
+
Requires-Dist: requests-toolbelt>=1.0.0
|
|
47
|
+
Requires-Dist: requests>=2.28.0
|
|
48
|
+
Provides-Extra: dev
|
|
49
|
+
Requires-Dist: fastapi[standard]>=0.115.0; extra == 'dev'
|
|
50
|
+
Requires-Dist: honcho>=2.0.0; extra == 'dev'
|
|
51
|
+
Requires-Dist: ruff>=0.12.5; extra == 'dev'
|
|
52
|
+
Requires-Dist: ty>=0.0.10; extra == 'dev'
|
|
53
|
+
Requires-Dist: types-requests>=2.32.4; extra == 'dev'
|
|
54
|
+
Description-Content-Type: text/markdown
|
|
55
|
+
|
|
56
|
+
# Python Client for Sectra
|
|
57
|
+
|
|
58
|
+
This python package aims to facilite the development of AI applications for Sectra PACS.
|
|
59
|
+
|
|
60
|
+
## Version
|
|
61
|
+
The library currently supports functionality up to do version 3.4 (Dec 2023). However, we will update to 5.0 in Juli, after which this library will be adjusted to the newest version as well.
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
To install sectra_client:
|
|
66
|
+
```
|
|
67
|
+
pip install sectra-image-analysis-api
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Usage
|
|
71
|
+
|
|
72
|
+
Before using the client, make sure you have access to a valid authentication token, and url, sent in the analysis requests.
|
|
73
|
+
|
|
74
|
+
### Example 1: Retrieve image information
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from sectra_client import SectraClient
|
|
78
|
+
|
|
79
|
+
# Info, sent by Sectra in the request
|
|
80
|
+
callback_url = "http://sectraweb.*.*.*/SectraPathologyServer/external/imageanalysis/v1"
|
|
81
|
+
callback_token = "abcde"
|
|
82
|
+
slide_id = "fghij"
|
|
83
|
+
|
|
84
|
+
# Use the context manager
|
|
85
|
+
with SectraClient(
|
|
86
|
+
url=callback_url,
|
|
87
|
+
token=callback_token
|
|
88
|
+
) as client:
|
|
89
|
+
# Returns the image info with extended and personal health information data
|
|
90
|
+
image_info = client.get_image_metadata(slide_id, extended=True, phi=True)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Example 2: Download WSI
|
|
94
|
+
```python
|
|
95
|
+
from sectra_client import SectraClient
|
|
96
|
+
|
|
97
|
+
# Info, sent by Sectra in the request
|
|
98
|
+
...
|
|
99
|
+
|
|
100
|
+
# Use the context manager
|
|
101
|
+
with SectraClient(
|
|
102
|
+
url=callback_url,
|
|
103
|
+
token=callback_token
|
|
104
|
+
) as client:
|
|
105
|
+
# Download the file(s) to an output directory, and returns the file paths of the file(s).
|
|
106
|
+
file_paths = client.download_slide_files(slide_id, output_dir="./path/to/output/dir/")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Example 3: Without context manager
|
|
110
|
+
It is also possible to use the SectraClient without the context manager
|
|
111
|
+
```python
|
|
112
|
+
from sectra_client import SectraClient
|
|
113
|
+
|
|
114
|
+
# Info, sent by Setra in the request
|
|
115
|
+
...
|
|
116
|
+
|
|
117
|
+
# Open context manager
|
|
118
|
+
client = SectraClient(url=callback_url, token=callback_token)
|
|
119
|
+
|
|
120
|
+
# Perform API interactions
|
|
121
|
+
image_info = client.get_image_metadata(slide_id, extended=True, phi=False)
|
|
122
|
+
|
|
123
|
+
# Make sure to close the connection
|
|
124
|
+
client.close()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Local development with the mock server
|
|
128
|
+
`MockSectraServer` simulates a Sectra PACS instance in memory so you can develop and test without a real server.
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from sectra_client.mock_server import MockSectraServer
|
|
132
|
+
|
|
133
|
+
server = MockSectraServer(token="dev-token")
|
|
134
|
+
with server.run(port=8001):
|
|
135
|
+
server.trigger(
|
|
136
|
+
webhook_url="http://localhost:8000/sectra/hook",
|
|
137
|
+
application_id="my-app",
|
|
138
|
+
slide_id="slide-001",
|
|
139
|
+
)
|
|
140
|
+
results = server.get_results(app_id="my-app", slide_id="slide-001")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
See `examples/local_dev.py` for a complete round-trip: both servers started from a single script, a fake invocation fired, and results read back.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Python Client for Sectra
|
|
2
|
+
|
|
3
|
+
This python package aims to facilite the development of AI applications for Sectra PACS.
|
|
4
|
+
|
|
5
|
+
## Version
|
|
6
|
+
The library currently supports functionality up to do version 3.4 (Dec 2023). However, we will update to 5.0 in Juli, after which this library will be adjusted to the newest version as well.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
To install sectra_client:
|
|
11
|
+
```
|
|
12
|
+
pip install sectra-image-analysis-api
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Before using the client, make sure you have access to a valid authentication token, and url, sent in the analysis requests.
|
|
18
|
+
|
|
19
|
+
### Example 1: Retrieve image information
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from sectra_client import SectraClient
|
|
23
|
+
|
|
24
|
+
# Info, sent by Sectra in the request
|
|
25
|
+
callback_url = "http://sectraweb.*.*.*/SectraPathologyServer/external/imageanalysis/v1"
|
|
26
|
+
callback_token = "abcde"
|
|
27
|
+
slide_id = "fghij"
|
|
28
|
+
|
|
29
|
+
# Use the context manager
|
|
30
|
+
with SectraClient(
|
|
31
|
+
url=callback_url,
|
|
32
|
+
token=callback_token
|
|
33
|
+
) as client:
|
|
34
|
+
# Returns the image info with extended and personal health information data
|
|
35
|
+
image_info = client.get_image_metadata(slide_id, extended=True, phi=True)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Example 2: Download WSI
|
|
39
|
+
```python
|
|
40
|
+
from sectra_client import SectraClient
|
|
41
|
+
|
|
42
|
+
# Info, sent by Sectra in the request
|
|
43
|
+
...
|
|
44
|
+
|
|
45
|
+
# Use the context manager
|
|
46
|
+
with SectraClient(
|
|
47
|
+
url=callback_url,
|
|
48
|
+
token=callback_token
|
|
49
|
+
) as client:
|
|
50
|
+
# Download the file(s) to an output directory, and returns the file paths of the file(s).
|
|
51
|
+
file_paths = client.download_slide_files(slide_id, output_dir="./path/to/output/dir/")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Example 3: Without context manager
|
|
55
|
+
It is also possible to use the SectraClient without the context manager
|
|
56
|
+
```python
|
|
57
|
+
from sectra_client import SectraClient
|
|
58
|
+
|
|
59
|
+
# Info, sent by Setra in the request
|
|
60
|
+
...
|
|
61
|
+
|
|
62
|
+
# Open context manager
|
|
63
|
+
client = SectraClient(url=callback_url, token=callback_token)
|
|
64
|
+
|
|
65
|
+
# Perform API interactions
|
|
66
|
+
image_info = client.get_image_metadata(slide_id, extended=True, phi=False)
|
|
67
|
+
|
|
68
|
+
# Make sure to close the connection
|
|
69
|
+
client.close()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Local development with the mock server
|
|
73
|
+
`MockSectraServer` simulates a Sectra PACS instance in memory so you can develop and test without a real server.
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from sectra_client.mock_server import MockSectraServer
|
|
77
|
+
|
|
78
|
+
server = MockSectraServer(token="dev-token")
|
|
79
|
+
with server.run(port=8001):
|
|
80
|
+
server.trigger(
|
|
81
|
+
webhook_url="http://localhost:8000/sectra/hook",
|
|
82
|
+
application_id="my-app",
|
|
83
|
+
slide_id="slide-001",
|
|
84
|
+
)
|
|
85
|
+
results = server.get_results(app_id="my-app", slide_id="slide-001")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
See `examples/local_dev.py` for a complete round-trip: both servers started from a single script, a fake invocation fired, and results read back.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# SectraClient
|
|
2
|
+
|
|
3
|
+
```python
|
|
4
|
+
from sectra_client import SectraClient
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
The primary interface for communicating with a Sectra PACS instance. Supports use as a context manager (recommended) or manually via `close()`.
|
|
8
|
+
|
|
9
|
+
## Constructor
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
SectraClient(url: str, token: str, _allow_http: bool = False)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Parameter | Description |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `url` | Base URL of the Sectra Image Analysis API (from `callbackInfo.url`) |
|
|
18
|
+
| `token` | Auth token (from `callbackInfo.token`) |
|
|
19
|
+
| `_allow_http` | Whether to allow plain HTTP during communication with Sectra (useful for `MockSectraServer`) |
|
|
20
|
+
|
|
21
|
+
## Properties
|
|
22
|
+
|
|
23
|
+
| Property | Type | Description |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `version_info` | `ApplicationInfo` | API and software version reported by the server |
|
|
26
|
+
| `response_headers` | `dict` | Headers to forward in your webhook response (required by Sectra) |
|
|
27
|
+
|
|
28
|
+
## Image metadata
|
|
29
|
+
|
|
30
|
+
### `get_image_metadata`
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
client.get_image_metadata(slide_id: str, extended: bool = False, phi: bool = False) -> ImageMetadata
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Retrieves slide metadata including dimensions, focal planes, optical paths, and staining info. Set `extended=True` for additional fields and `phi=True` to include protected health information.
|
|
37
|
+
|
|
38
|
+
### `get_image_infos_in_case`
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
client.get_image_infos_in_case(
|
|
42
|
+
accession_number: str,
|
|
43
|
+
phi: bool = False,
|
|
44
|
+
accession_number_issuer_id: str | None = None,
|
|
45
|
+
) -> list[CaseImageInfo]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Returns all slides belonging to a case, looked up by accession number. Requires IA-API 1.9+.
|
|
49
|
+
|
|
50
|
+
### `get_image_infos_in_case_by_slide_id`
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
client.get_image_infos_in_case_by_slide_id(
|
|
54
|
+
slide_id: str,
|
|
55
|
+
phi: bool = False,
|
|
56
|
+
accession_number_issuer_id: str | None = None,
|
|
57
|
+
) -> list[CaseImageInfo]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Same as above but resolves the case from a known `slide_id`. Requires IA-API 1.9+.
|
|
61
|
+
|
|
62
|
+
## File operations
|
|
63
|
+
|
|
64
|
+
### `get_label_image`
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
client.get_label_image(slide_id: str) -> LabelImage
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Retrieves the slide's label/thumbnail image. Call `.convert_to_pil()` on the result to get a Pillow `Image`.
|
|
71
|
+
|
|
72
|
+
### `download_slide_files`
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
client.download_slide_files(slide_id: str, output_dir: pathlib.Path | str) -> list[pathlib.Path]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Downloads all WSI files for a slide into `output_dir` (created if absent). Returns the paths of the saved files. Handles multipart responses transparently.
|
|
79
|
+
|
|
80
|
+
## Results
|
|
81
|
+
|
|
82
|
+
### `create_results`
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
client.create_results(app_id: str, results: Result) -> ResultResponse
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Submits analysis results to Sectra. Returns the stored result including its assigned `id`.
|
|
89
|
+
|
|
90
|
+
### `get_result_by_result_id`
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
client.get_result_by_result_id(app_id: str, result_id: int) -> ResultResponse
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Fetches a single stored result by ID.
|
|
97
|
+
|
|
98
|
+
### `get_all_results`
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
client.get_all_results(wsi_id: str, app_id: str) -> list[ResultResponse]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Returns all results stored for a slide/application pair.
|
|
105
|
+
|
|
106
|
+
### `update_results`
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
client.update_results(app_id: str, result_id: int, result: AdaptedResult) -> ResultResponse
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Updates an existing result. `AdaptedResult` requires the current `versionId` (optimistic concurrency).
|
|
113
|
+
|
|
114
|
+
## Quality control
|
|
115
|
+
|
|
116
|
+
### `set_quality_control`
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
client.set_quality_control(slide_id: str, quality_control: QualityControl) -> None
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Sets the QC status for a slide. Requires IA-API 1.10+.
|
|
123
|
+
|
|
124
|
+
## Errors
|
|
125
|
+
|
|
126
|
+
`SectraRequestError` is raised when Sectra returns a non-2xx response.
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from sectra_client.utils.errors import SectraRequestError
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
client.get_image_metadata(slide_id)
|
|
133
|
+
except SectraRequestError as e:
|
|
134
|
+
print(e.status_code, e.path, e.text)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Failed requests are retried up to 5 times with exponential backoff on connection errors.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# MockSectraServer
|
|
2
|
+
|
|
3
|
+
```python
|
|
4
|
+
from sectra_client.mock_server import MockSectraServer
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
An in-memory Sectra PACS simulation for local development and testing. Exposes the same HTTP API as a real Sectra server, so your analysis app needs no code changes when switching between mock and production.
|
|
8
|
+
|
|
9
|
+
## Constructor
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
MockSectraServer(
|
|
13
|
+
api_version: str = "1.10",
|
|
14
|
+
software_version: str = "4.2.0.0",
|
|
15
|
+
token: str = "dev-token",
|
|
16
|
+
slides: dict[str, ImageMetadata] | None = None,
|
|
17
|
+
slide_files: dict[str, pathlib.Path | list[pathlib.Path]] | None = None,
|
|
18
|
+
)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| Parameter | Description |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `token` | Auth token your analysis app must present |
|
|
24
|
+
| `slides` | Pre-populate slide metadata (keyed by slide ID) |
|
|
25
|
+
| `slide_files` | Pre-populate real WSI files to serve (keyed by slide ID) |
|
|
26
|
+
|
|
27
|
+
## Starting the server
|
|
28
|
+
|
|
29
|
+
Use `run()` as a context manager — it starts the server in a background thread and stops it on exit:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
mock = MockSectraServer(token="dev-token")
|
|
33
|
+
with mock.run(port=8001):
|
|
34
|
+
# server is running at http://localhost:8001
|
|
35
|
+
...
|
|
36
|
+
# server is stopped
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
You can also call `start()` / `stop()` manually.
|
|
40
|
+
|
|
41
|
+
## Registering slides
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
mock.add_slide(metadata: ImageMetadata) -> None
|
|
45
|
+
mock.add_slide_files(slide_id: str, files: pathlib.Path | list[pathlib.Path]) -> None
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Use these before (or after) starting the server to register slide metadata and real WSI files. Auto-generated metadata is used for any slide ID not explicitly registered.
|
|
49
|
+
|
|
50
|
+
## Triggering invocations
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
mock.trigger(webhook_url: str, invocation: Invocation) -> dict
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Sends a fake invocation from the mock Sectra server to your app's webhook. Use this to drive an end-to-end test without clicking through a real PACS UI.
|
|
57
|
+
|
|
58
|
+
## Reading results
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
mock.get_results(app_id: str, slide_id: str) -> list[ResultResponse]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Returns results stored in the mock server's memory — useful for asserting your app submitted the expected output.
|
|
65
|
+
|
|
66
|
+
## Full round-trip example
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
import threading, time
|
|
70
|
+
import uvicorn
|
|
71
|
+
from sectra_client.mock_server import MockSectraServer
|
|
72
|
+
from sectra_client.schemas import CallbackInfo, CreateInvocation, WholeSlideInput
|
|
73
|
+
|
|
74
|
+
MOCK_PORT = 8001
|
|
75
|
+
APP_PORT = 8000
|
|
76
|
+
SLIDE_ID = "slide-001"
|
|
77
|
+
|
|
78
|
+
mock = MockSectraServer(token="dev-token")
|
|
79
|
+
with mock.run(port=MOCK_PORT):
|
|
80
|
+
# Start your analysis app in the background
|
|
81
|
+
app_config = uvicorn.Config(my_fastapi_app, host="localhost", port=APP_PORT, log_level="warning")
|
|
82
|
+
app_server = uvicorn.Server(app_config)
|
|
83
|
+
thread = threading.Thread(target=app_server.run, daemon=True)
|
|
84
|
+
thread.start()
|
|
85
|
+
time.sleep(1) # wait for app to be ready
|
|
86
|
+
|
|
87
|
+
# Fire a fake invocation
|
|
88
|
+
mock.trigger(
|
|
89
|
+
webhook_url=f"http://localhost:{APP_PORT}/sectra/hook",
|
|
90
|
+
invocation=CreateInvocation(
|
|
91
|
+
applicationId="my-app",
|
|
92
|
+
slideId=SLIDE_ID,
|
|
93
|
+
callbackInfo=CallbackInfo(
|
|
94
|
+
url=f"http://localhost:{MOCK_PORT}",
|
|
95
|
+
token="dev-token",
|
|
96
|
+
),
|
|
97
|
+
cancellationToken="tok-001",
|
|
98
|
+
input=WholeSlideInput(),
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Poll until the result arrives
|
|
103
|
+
for _ in range(30):
|
|
104
|
+
results = mock.get_results(app_id="my-app", slide_id=SLIDE_ID)
|
|
105
|
+
if results:
|
|
106
|
+
break
|
|
107
|
+
time.sleep(1)
|
|
108
|
+
|
|
109
|
+
print(results[0].displayResult)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
See `examples/local_dev.py` in the repository for the complete, runnable version.
|
|
113
|
+
|
|
114
|
+
!!! note
|
|
115
|
+
When connecting `SectraClient` to a `MockSectraServer`, pass `_allow_http=True` because the mock runs on plain HTTP.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
SectraClient(url="http://localhost:8001", token="dev-token", _allow_http=True)
|
|
119
|
+
```
|