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.
Files changed (33) hide show
  1. sectra_image_analysis_api-1.5.1/.github/workflows/docs.yml +25 -0
  2. sectra_image_analysis_api-1.5.1/.github/workflows/publish.yaml +35 -0
  3. sectra_image_analysis_api-1.5.1/.gitignore +53 -0
  4. sectra_image_analysis_api-1.5.1/LICENSE +21 -0
  5. sectra_image_analysis_api-1.5.1/PKG-INFO +143 -0
  6. sectra_image_analysis_api-1.5.1/README.md +88 -0
  7. sectra_image_analysis_api-1.5.1/docs/api/client.md +137 -0
  8. sectra_image_analysis_api-1.5.1/docs/api/mock-server.md +119 -0
  9. sectra_image_analysis_api-1.5.1/docs/api/schemas.md +171 -0
  10. sectra_image_analysis_api-1.5.1/docs/index.md +39 -0
  11. sectra_image_analysis_api-1.5.1/docs/quickstart.md +103 -0
  12. sectra_image_analysis_api-1.5.1/examples/fetch_and_cast_results.py +32 -0
  13. sectra_image_analysis_api-1.5.1/examples/local_dev.py +181 -0
  14. sectra_image_analysis_api-1.5.1/examples/patch_result.py +122 -0
  15. sectra_image_analysis_api-1.5.1/examples/primitive_result.py +58 -0
  16. sectra_image_analysis_api-1.5.1/mkdocs.yml +30 -0
  17. sectra_image_analysis_api-1.5.1/pyproject.toml +68 -0
  18. sectra_image_analysis_api-1.5.1/src/sectra_client/__init__.py +3 -0
  19. sectra_image_analysis_api-1.5.1/src/sectra_client/client.py +310 -0
  20. sectra_image_analysis_api-1.5.1/src/sectra_client/mock_server.py +468 -0
  21. sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/__init__.py +96 -0
  22. sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/common.py +46 -0
  23. sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/image.py +88 -0
  24. sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/info.py +8 -0
  25. sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/invocation.py +92 -0
  26. sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/quality_control.py +26 -0
  27. sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/registration.py +29 -0
  28. sectra_image_analysis_api-1.5.1/src/sectra_client/schemas/results.py +111 -0
  29. sectra_image_analysis_api-1.5.1/src/sectra_client/utils/__init__.py +0 -0
  30. sectra_image_analysis_api-1.5.1/src/sectra_client/utils/decode_images.py +17 -0
  31. sectra_image_analysis_api-1.5.1/src/sectra_client/utils/errors.py +10 -0
  32. sectra_image_analysis_api-1.5.1/src/sectra_client/utils/helpers.py +32 -0
  33. 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
+ ```