TEMdb-client 1.0.0__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 (49) hide show
  1. temdb_client-1.0.0/PKG-INFO +10 -0
  2. temdb_client-1.0.0/README.md +2 -0
  3. temdb_client-1.0.0/TEMdb_client.egg-info/PKG-INFO +10 -0
  4. temdb_client-1.0.0/TEMdb_client.egg-info/SOURCES.txt +47 -0
  5. temdb_client-1.0.0/TEMdb_client.egg-info/dependency_links.txt +1 -0
  6. temdb_client-1.0.0/TEMdb_client.egg-info/requires.txt +7 -0
  7. temdb_client-1.0.0/TEMdb_client.egg-info/top_level.txt +1 -0
  8. temdb_client-1.0.0/pyproject.toml +21 -0
  9. temdb_client-1.0.0/setup.cfg +4 -0
  10. temdb_client-1.0.0/temdb_client/__init__.py +11 -0
  11. temdb_client-1.0.0/temdb_client/client.py +347 -0
  12. temdb_client-1.0.0/temdb_client/exceptions.py +5 -0
  13. temdb_client-1.0.0/temdb_client/models/__init__.py +0 -0
  14. temdb_client-1.0.0/temdb_client/models/acquisition.py +122 -0
  15. temdb_client-1.0.0/temdb_client/models/block.py +40 -0
  16. temdb_client-1.0.0/temdb_client/models/cutting_session.py +45 -0
  17. temdb_client-1.0.0/temdb_client/models/enum_schemas.py +26 -0
  18. temdb_client-1.0.0/temdb_client/models/roi.py +59 -0
  19. temdb_client-1.0.0/temdb_client/models/section.py +77 -0
  20. temdb_client-1.0.0/temdb_client/models/specimen.py +47 -0
  21. temdb_client-1.0.0/temdb_client/models/substrate.py +63 -0
  22. temdb_client-1.0.0/temdb_client/models/task.py +54 -0
  23. temdb_client-1.0.0/temdb_client/models/tile.py +80 -0
  24. temdb_client-1.0.0/temdb_client/resources/__init__.py +0 -0
  25. temdb_client-1.0.0/temdb_client/resources/acquisition.py +166 -0
  26. temdb_client-1.0.0/temdb_client/resources/base.py +25 -0
  27. temdb_client-1.0.0/temdb_client/resources/block.py +138 -0
  28. temdb_client-1.0.0/temdb_client/resources/cutting_session.py +194 -0
  29. temdb_client-1.0.0/temdb_client/resources/roi.py +86 -0
  30. temdb_client-1.0.0/temdb_client/resources/section.py +241 -0
  31. temdb_client-1.0.0/temdb_client/resources/specimen.py +145 -0
  32. temdb_client-1.0.0/temdb_client/resources/substrate.py +124 -0
  33. temdb_client-1.0.0/temdb_client/resources/sync_wrappers/__init__.py +19 -0
  34. temdb_client-1.0.0/temdb_client/resources/sync_wrappers/acquisition.py +142 -0
  35. temdb_client-1.0.0/temdb_client/resources/sync_wrappers/block.py +69 -0
  36. temdb_client-1.0.0/temdb_client/resources/sync_wrappers/cutting_session.py +99 -0
  37. temdb_client-1.0.0/temdb_client/resources/sync_wrappers/roi.py +73 -0
  38. temdb_client-1.0.0/temdb_client/resources/sync_wrappers/section.py +119 -0
  39. temdb_client-1.0.0/temdb_client/resources/sync_wrappers/specimen.py +59 -0
  40. temdb_client-1.0.0/temdb_client/resources/sync_wrappers/substrate.py +68 -0
  41. temdb_client-1.0.0/temdb_client/resources/sync_wrappers/task.py +87 -0
  42. temdb_client-1.0.0/temdb_client/resources/task.py +112 -0
  43. temdb_client-1.0.0/tests/test_acquisition.py +45 -0
  44. temdb_client-1.0.0/tests/test_block.py +46 -0
  45. temdb_client-1.0.0/tests/test_client.py +17 -0
  46. temdb_client-1.0.0/tests/test_cutting_session.py +48 -0
  47. temdb_client-1.0.0/tests/test_imaging_session.py +43 -0
  48. temdb_client-1.0.0/tests/test_roi.py +54 -0
  49. temdb_client-1.0.0/tests/test_specimen.py +45 -0
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: TEMdb_client
3
+ Version: 1.0.0
4
+ Author-email: Derrick Brittain <derrickb@alleninstitute.org>
5
+ Requires-Dist: httpx
6
+ Requires-Dist: tenacity
7
+ Requires-Dist: pydantic
8
+ Provides-Extra: test
9
+ Requires-Dist: pytest; extra == "test"
10
+ Requires-Dist: pytest-asyncio; extra == "test"
@@ -0,0 +1,2 @@
1
+ # TEMdb_client
2
+ A REST client to interface with TEMdb
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: TEMdb_client
3
+ Version: 1.0.0
4
+ Author-email: Derrick Brittain <derrickb@alleninstitute.org>
5
+ Requires-Dist: httpx
6
+ Requires-Dist: tenacity
7
+ Requires-Dist: pydantic
8
+ Provides-Extra: test
9
+ Requires-Dist: pytest; extra == "test"
10
+ Requires-Dist: pytest-asyncio; extra == "test"
@@ -0,0 +1,47 @@
1
+ README.md
2
+ pyproject.toml
3
+ TEMdb_client.egg-info/PKG-INFO
4
+ TEMdb_client.egg-info/SOURCES.txt
5
+ TEMdb_client.egg-info/dependency_links.txt
6
+ TEMdb_client.egg-info/requires.txt
7
+ TEMdb_client.egg-info/top_level.txt
8
+ temdb_client/__init__.py
9
+ temdb_client/client.py
10
+ temdb_client/exceptions.py
11
+ temdb_client/models/__init__.py
12
+ temdb_client/models/acquisition.py
13
+ temdb_client/models/block.py
14
+ temdb_client/models/cutting_session.py
15
+ temdb_client/models/enum_schemas.py
16
+ temdb_client/models/roi.py
17
+ temdb_client/models/section.py
18
+ temdb_client/models/specimen.py
19
+ temdb_client/models/substrate.py
20
+ temdb_client/models/task.py
21
+ temdb_client/models/tile.py
22
+ temdb_client/resources/__init__.py
23
+ temdb_client/resources/acquisition.py
24
+ temdb_client/resources/base.py
25
+ temdb_client/resources/block.py
26
+ temdb_client/resources/cutting_session.py
27
+ temdb_client/resources/roi.py
28
+ temdb_client/resources/section.py
29
+ temdb_client/resources/specimen.py
30
+ temdb_client/resources/substrate.py
31
+ temdb_client/resources/task.py
32
+ temdb_client/resources/sync_wrappers/__init__.py
33
+ temdb_client/resources/sync_wrappers/acquisition.py
34
+ temdb_client/resources/sync_wrappers/block.py
35
+ temdb_client/resources/sync_wrappers/cutting_session.py
36
+ temdb_client/resources/sync_wrappers/roi.py
37
+ temdb_client/resources/sync_wrappers/section.py
38
+ temdb_client/resources/sync_wrappers/specimen.py
39
+ temdb_client/resources/sync_wrappers/substrate.py
40
+ temdb_client/resources/sync_wrappers/task.py
41
+ tests/test_acquisition.py
42
+ tests/test_block.py
43
+ tests/test_client.py
44
+ tests/test_cutting_session.py
45
+ tests/test_imaging_session.py
46
+ tests/test_roi.py
47
+ tests/test_specimen.py
@@ -0,0 +1,7 @@
1
+ httpx
2
+ tenacity
3
+ pydantic
4
+
5
+ [test]
6
+ pytest
7
+ pytest-asyncio
@@ -0,0 +1 @@
1
+ temdb_client
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "TEMdb_client"
3
+ version = "1.0.0"
4
+ authors = [
5
+ { name="Derrick Brittain", email="derrickb@alleninstitute.org" }
6
+ ]
7
+ dependencies = [
8
+ "httpx",
9
+ "tenacity",
10
+ "pydantic",
11
+ ]
12
+
13
+ [project.optional-dependencies]
14
+ test = [
15
+ "pytest",
16
+ "pytest-asyncio"
17
+ ]
18
+
19
+ [build-system]
20
+ requires = ["setuptools", "wheel"]
21
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,11 @@
1
+ from .client import create_client, AsyncTEMdbClient, SyncTEMdbClient
2
+ from .exceptions import TEMdbClientError, NotFoundError
3
+
4
+ __all__ = [
5
+ "create_client",
6
+ "SyncTEMdbClient",
7
+ "AsyncTEMdbClient",
8
+ "TEMdbClientError",
9
+ "NotFoundError",
10
+ ]
11
+ __version__ = "0.1.0"
@@ -0,0 +1,347 @@
1
+ import httpx
2
+ import logging
3
+ import asyncio
4
+ from typing import Dict, Any, Optional, Union, List, cast
5
+
6
+ from tenacity import (
7
+ retry,
8
+ stop_after_attempt,
9
+ wait_exponential,
10
+ retry_if_exception_type,
11
+ )
12
+
13
+ from .resources.specimen import SpecimenResource
14
+ from .resources.block import BlockResource
15
+ from .resources.cutting_session import CuttingSessionResource
16
+ from .resources.substrate import SubstrateResource
17
+ from .resources.task import AcquisitionTaskResource
18
+ from .resources.roi import ROIResource
19
+ from .resources.acquisition import AcquisitionResource
20
+ from .resources.section import SectionResource
21
+
22
+ from .resources.sync_wrappers import (
23
+ SyncAcquisitionResourceWrapper,
24
+ SyncSpecimenResourceWrapper,
25
+ SyncBlockResourceWrapper,
26
+ SyncCuttingSessionResourceWrapper,
27
+ SyncSubstrateResourceWrapper,
28
+ SyncAcquisitionTaskResourceWrapper,
29
+ SyncROIResourceWrapper,
30
+ SyncSectionResourceWrapper,
31
+ )
32
+
33
+ from .exceptions import TEMdbClientError, NotFoundError
34
+
35
+
36
+ class AsyncTEMdbClient:
37
+ """Asynchronous client for interacting with the TEMdb API."""
38
+
39
+ def __init__(
40
+ self,
41
+ base_url: str,
42
+ api_version: str = "v2",
43
+ api_key: Optional[str] = None,
44
+ timeout: float = 30.0,
45
+ debug: bool = False,
46
+ ):
47
+ """Initialize the async client."""
48
+ self.raw_base_url = base_url
49
+ self.api_version = api_version
50
+ self.api_url = f"{base_url}/api/{api_version}"
51
+ self.api_key = api_key
52
+ self.timeout = timeout
53
+
54
+ self.logger = logging.getLogger("temdb_client.async")
55
+ level = logging.DEBUG if debug else logging.INFO
56
+ self.logger.setLevel(level)
57
+
58
+ headers = {}
59
+ if api_key:
60
+ headers["Authorization"] = f"Bearer {api_key}"
61
+
62
+ self._http_client = httpx.AsyncClient(
63
+ base_url=self.api_url,
64
+ headers=headers,
65
+ timeout=timeout,
66
+ )
67
+
68
+ self.logger.info(
69
+ f"Async TEMdb client initialized: {base_url} (API v{api_version})"
70
+ )
71
+
72
+ self._specimen = SpecimenResource(self._async_request, self.api_url)
73
+ self._block = BlockResource(self._async_request, self.api_url)
74
+ self._cutting_session = CuttingSessionResource(
75
+ self._async_request, self.api_url
76
+ )
77
+ self._substrate = SubstrateResource(self._async_request, self.api_url)
78
+ self._acquisition_task = AcquisitionTaskResource(
79
+ self._async_request, self.api_url
80
+ )
81
+ self._roi = ROIResource(self._async_request, self.api_url)
82
+ self._acquisition = AcquisitionResource(self._async_request, self.api_url)
83
+ self._section = SectionResource(self._async_request, self.api_url)
84
+
85
+ @property
86
+ def specimen(self) -> SpecimenResource:
87
+ """Access specimen-related async operations."""
88
+ return self._specimen
89
+
90
+ @property
91
+ def block(self) -> BlockResource:
92
+ """Access block-related async operations."""
93
+ return self._block
94
+
95
+ @property
96
+ def cutting_session(self) -> CuttingSessionResource:
97
+ """Access cutting session-related async operations."""
98
+ return self._cutting_session
99
+
100
+ @property
101
+ def substrate(self) -> SubstrateResource:
102
+ """Access substrate-related async operations."""
103
+ return self._substrate
104
+
105
+ @property
106
+ def acquisition_task(self) -> AcquisitionTaskResource: # Renamed property
107
+ """Access acquisition task-related async operations."""
108
+ return self._acquisition_task
109
+
110
+ @property
111
+ def roi(self) -> ROIResource:
112
+ """Access ROI-related async operations."""
113
+ return self._roi
114
+
115
+ @property
116
+ def acquisition(self) -> AcquisitionResource:
117
+ """Access acquisition-related async operations."""
118
+ return self._acquisition
119
+
120
+ @property
121
+ def section(self) -> SectionResource:
122
+ """Access section-related async operations."""
123
+ return self._section
124
+
125
+ @retry(
126
+ stop=stop_after_attempt(3),
127
+ wait=wait_exponential(multiplier=1, min=4, max=10),
128
+ retry=retry_if_exception_type(httpx.RequestError),
129
+ )
130
+ async def _async_request(
131
+ self, method: str, endpoint: str, **kwargs
132
+ ) -> Union[Dict[str, Any], List[Any]]:
133
+ """Make an async HTTP request to the API."""
134
+ self.logger.debug(f"Async Request: {method} {endpoint}")
135
+ try:
136
+ response = await self._http_client.request(method, endpoint, **kwargs)
137
+ response.raise_for_status()
138
+ if response.status_code == 204:
139
+ return {}
140
+ return response.json()
141
+ except httpx.HTTPStatusError as e:
142
+ self.logger.error(
143
+ f"HTTP error: {e.response.status_code} - {e.response.text}"
144
+ )
145
+ if e.response.status_code == 404:
146
+ raise NotFoundError(f"Resource not found: {endpoint}") from e
147
+ raise TEMdbClientError(
148
+ f"HTTP {e.response.status_code}: {e.response.text}"
149
+ ) from e
150
+ except httpx.RequestError as e:
151
+ self.logger.error(f"Request failed: {str(e)}")
152
+ raise TEMdbClientError(f"Request failed: {str(e)}") from e
153
+ except Exception as e:
154
+ self.logger.exception(
155
+ f"An unexpected error occurred during request to {endpoint}"
156
+ )
157
+ raise TEMdbClientError(f"Unexpected error: {str(e)}") from e
158
+
159
+ async def health_check(self) -> Dict[str, Any]:
160
+ """Check if the API is available."""
161
+ try:
162
+ result = await self._async_request("GET", "/health")
163
+ self.logger.info(f"Async Health check: {result.get('status', 'unknown')}")
164
+ return cast(Dict[str, Any], result)
165
+ except Exception as e:
166
+ self.logger.error(f"Async Health check failed: {str(e)}")
167
+ raise
168
+
169
+ async def get_api_info(self) -> Dict[str, Any]:
170
+ """Get API information."""
171
+ result = await self._async_request("GET", "/")
172
+ return cast(Dict[str, Any], result)
173
+
174
+ async def close(self) -> None:
175
+ """Close the async client and release resources."""
176
+ self.logger.info("Closing async TEMdb client")
177
+ await self._http_client.aclose()
178
+
179
+ async def __aenter__(self) -> "AsyncTEMdbClient":
180
+ """Allow using client as async context manager."""
181
+ return self
182
+
183
+ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
184
+ """Close client when exiting async context manager."""
185
+ await self.close()
186
+
187
+
188
+ class SyncTEMdbClient:
189
+ """Synchronous client for interacting with the TEMdb API (wraps AsyncTEMdbClient)."""
190
+
191
+ def __init__(
192
+ self,
193
+ base_url: str,
194
+ api_version: str = "v2",
195
+ api_key: Optional[str] = None,
196
+ timeout: float = 30.0,
197
+ debug: bool = False,
198
+ ):
199
+ """Initialize the sync client."""
200
+ self._async_client = AsyncTEMdbClient(
201
+ base_url, api_version, api_key, timeout, debug
202
+ )
203
+ self.logger = logging.getLogger("temdb_client.sync")
204
+ level = logging.DEBUG if debug else logging.INFO
205
+ self.logger.setLevel(level)
206
+ self.logger.info(
207
+ f"Sync TEMdb client initialized (wrapping async): {base_url} (API v{api_version})"
208
+ )
209
+
210
+ self._acquisition = SyncAcquisitionResourceWrapper(
211
+ self._async_client.acquisition
212
+ )
213
+ self._specimen = SyncSpecimenResourceWrapper(self._async_client.specimen)
214
+ self._block = SyncBlockResourceWrapper(self._async_client.block)
215
+ self._cutting_session = SyncCuttingSessionResourceWrapper(
216
+ self._async_client.cutting_session
217
+ )
218
+ self._substrate = SyncSubstrateResourceWrapper(self._async_client.substrate)
219
+ self._acquisition_task = SyncAcquisitionTaskResourceWrapper(
220
+ self._async_client.acquisition_task
221
+ )
222
+ self._roi = SyncROIResourceWrapper(self._async_client.roi)
223
+ self._section = SyncSectionResourceWrapper(self._async_client.section)
224
+
225
+ @property
226
+ def acquisition(self) -> SyncAcquisitionResourceWrapper:
227
+ """Access acquisition-related sync operations."""
228
+ return self._acquisition
229
+
230
+ @property
231
+ def specimen(self) -> SyncSpecimenResourceWrapper:
232
+ """Access specimen-related sync operations."""
233
+ return self._specimen
234
+
235
+ @property
236
+ def block(self) -> SyncBlockResourceWrapper:
237
+ """Access block-related sync operations."""
238
+ return self._block
239
+
240
+ @property
241
+ def cutting_session(self) -> SyncCuttingSessionResourceWrapper:
242
+ """Access cutting session-related sync operations."""
243
+ return self._cutting_session
244
+
245
+ @property
246
+ def substrate(self) -> SyncSubstrateResourceWrapper:
247
+ """Access substrate-related sync operations."""
248
+ return self._substrate
249
+
250
+ @property
251
+ def acquisition_task(self) -> SyncAcquisitionTaskResourceWrapper:
252
+ """Access acquisition task-related sync operations."""
253
+ return self._acquisition_task
254
+
255
+ @property
256
+ def roi(self) -> SyncROIResourceWrapper:
257
+ """Access ROI-related sync operations."""
258
+ return self._roi
259
+
260
+ @property
261
+ def section(self) -> SyncSectionResourceWrapper:
262
+ """Access section-related sync operations."""
263
+ return self._section
264
+
265
+ def health_check(self) -> Dict[str, Any]:
266
+ """Check if the API is available."""
267
+ self.logger.info("Running sync health check...")
268
+ try:
269
+ return asyncio.run(self._async_client.health_check())
270
+ except Exception as e:
271
+ self.logger.error(f"Sync Health check failed: {str(e)}")
272
+ raise
273
+
274
+ def get_api_info(self) -> Dict[str, Any]:
275
+ """Get API information."""
276
+ self.logger.info("Getting sync API info...")
277
+ return asyncio.run(self._async_client.get_api_info())
278
+
279
+ def close(self) -> None:
280
+ """Close the client and release resources."""
281
+ self.logger.info("Closing sync TEMdb client (and underlying async client)")
282
+ asyncio.run(self._async_client.close())
283
+
284
+ def __enter__(self) -> "SyncTEMdbClient":
285
+ """Allow using client as sync context manager."""
286
+ return self
287
+
288
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
289
+ """Close client when exiting sync context manager."""
290
+ self.close()
291
+
292
+
293
+ def create_client(
294
+ base_url: str,
295
+ api_version: str = "v2",
296
+ api_key: Optional[str] = None,
297
+ timeout: float = 30.0,
298
+ debug: bool = False,
299
+ async_mode: bool = True,
300
+ ) -> Union[AsyncTEMdbClient, SyncTEMdbClient]:
301
+ """
302
+ Factory function to create either an async or sync TEMdb client.
303
+
304
+ Args:
305
+ base_url: Base URL of the TEMdb API (e.g., "http://localhost:8000").
306
+ api_version: API version string (default: "v2").
307
+ api_key: Optional API key for authentication.
308
+ timeout: Request timeout in seconds.
309
+ debug: Enable debug logging for the client.
310
+ async_mode: If True (default), returns AsyncTEMdbClient. If False, returns SyncTEMdbClient.
311
+
312
+ Returns:
313
+ An instance of AsyncTEMdbClient or SyncTEMdbClient.
314
+ """
315
+ if async_mode:
316
+ return AsyncTEMdbClient(base_url, api_version, api_key, timeout, debug)
317
+ else:
318
+ return SyncTEMdbClient(base_url, api_version, api_key, timeout, debug)
319
+
320
+
321
+ if __name__ == "__main__":
322
+ # Example usage
323
+ client = create_client("http://localhost:8000", async_mode=False)
324
+ try:
325
+ info = client.get_api_info()
326
+ print("API Info:", info)
327
+ finally:
328
+ client.close()
329
+
330
+ # with context manager
331
+ with create_client("http://localhost:8000", async_mode=False) as client:
332
+ try:
333
+ info = client.get_api_info()
334
+ print("API Info:", info)
335
+ except Exception as e:
336
+ print(f"Error: {e}")
337
+
338
+ # Example usage of async client
339
+ async def main():
340
+ async with create_client("http://localhost:8000", async_mode=True) as client:
341
+ try:
342
+ info = await client.get_api_info()
343
+ print("API Info:", info)
344
+ except Exception as e:
345
+ print(f"Error: {e}")
346
+
347
+ asyncio.run(main())
@@ -0,0 +1,5 @@
1
+ class TEMdbClientError(Exception):
2
+ pass
3
+
4
+ class NotFoundError(TEMdbClientError):
5
+ pass
File without changes
@@ -0,0 +1,122 @@
1
+ from typing import Dict, List, Optional, Any
2
+ from datetime import datetime
3
+ from pydantic import BaseModel, Field
4
+
5
+ from .enum_schemas import AcquisitionStatus
6
+
7
+
8
+ class StorageLocation(BaseModel):
9
+ location_type: str
10
+ base_path: str
11
+ is_current: bool
12
+ date_added: datetime
13
+ metadata: Dict[str, Any]
14
+
15
+ class Config:
16
+ extra = "allow"
17
+
18
+
19
+ class StorageLocationCreate(BaseModel):
20
+ location_type: str
21
+ base_path: str
22
+ metadata: Dict[str, Any]
23
+
24
+ class Config:
25
+ extra = "allow"
26
+
27
+
28
+ class LensCorrectionModel(BaseModel):
29
+ id: int
30
+ type: str
31
+ class_name: str
32
+ data_string: str
33
+
34
+ class Config:
35
+ extra = "allow"
36
+
37
+
38
+ class Calibration(BaseModel):
39
+ pixel_size: float
40
+ rotation_angle: float
41
+ lens_model: Optional[LensCorrectionModel] = None
42
+ aperture_centroid: Optional[List[float]] = None
43
+
44
+ class Config:
45
+ extra = "allow"
46
+
47
+
48
+ class HardwareParams(BaseModel):
49
+ scope_id: str
50
+ camera_model: str
51
+ camera_serial: str
52
+ camera_bit_depth: int
53
+ media_type: str
54
+
55
+ class Config:
56
+ extra = "allow"
57
+
58
+
59
+ class AcquisitionParams(BaseModel):
60
+ magnification: int
61
+ spot_size: int
62
+ exposure_time: int
63
+ tile_size: List[int]
64
+ tile_overlap: float
65
+ saved_bit_depth: int
66
+
67
+ class Config:
68
+ extra = "allow"
69
+
70
+
71
+ class AcquisitionBase(BaseModel):
72
+ hardware_settings: Optional[HardwareParams] = None
73
+ acquisition_settings: Optional[AcquisitionParams] = None
74
+ calibration_info: Optional[Calibration] = None
75
+ status: Optional[AcquisitionStatus] = None
76
+ tilt_angle: Optional[float] = None
77
+ lens_correction: Optional[bool] = None
78
+ end_time: Optional[datetime] = None
79
+ storage_locations: Optional[List[StorageLocation]] = None
80
+ montage_set_name: Optional[str] = None
81
+ sub_region: Optional[Dict[str, int]] = None
82
+ replaces_acquisition_id: Optional[int] = None
83
+ montage_id: Optional[str] = None
84
+ acquisition_id: Optional[str] = None
85
+ roi_id: Optional[int] = None
86
+ acquisition_task_id: Optional[str] = None
87
+ specimen_id: Optional[str] = None
88
+
89
+ class Config:
90
+ extra = "allow"
91
+
92
+
93
+ class AcquisitionCreate(AcquisitionBase):
94
+ montage_id: str = Field(...)
95
+ acquisition_id: str = Field(...)
96
+ roi_id: int = Field(...)
97
+ acquisition_task_id: str = Field(...)
98
+ hardware_settings: HardwareParams = Field(...)
99
+ acquisition_settings: AcquisitionParams = Field(...)
100
+ tilt_angle: float = Field(...)
101
+ lens_correction: bool = Field(...)
102
+
103
+ status: AcquisitionStatus = AcquisitionStatus.IMAGING
104
+
105
+
106
+ class AcquisitionUpdate(AcquisitionBase):
107
+ calibration_info: Optional[Dict[str, Any]] = None
108
+
109
+
110
+ class AcquisitionResponse(AcquisitionBase):
111
+ acquisition_id: str = Field(...)
112
+ montage_id: str = Field(...)
113
+ specimen_id: str = Field(...)
114
+ roi_id: int = Field(...)
115
+ acquisition_task_id: str = Field(...)
116
+ hardware_settings: HardwareParams
117
+ acquisition_settings: AcquisitionParams
118
+ status: AcquisitionStatus
119
+ start_time: datetime
120
+
121
+ created_at: Optional[datetime] = None
122
+ updated_at: Optional[datetime] = None
@@ -0,0 +1,40 @@
1
+ from typing import Optional, Dict, Any
2
+ from pydantic import BaseModel, Field
3
+ from datetime import datetime
4
+
5
+
6
+ class BlockBase(BaseModel):
7
+ microCT_info: Optional[Dict[str, Any]] = Field(
8
+ None, description="MicroCT information of block"
9
+ )
10
+
11
+ class Config:
12
+ extra = "allow"
13
+
14
+
15
+ class BlockCreate(BlockBase):
16
+ block_id: str = Field(
17
+ ..., description="Unique human-readable identifier for the block"
18
+ )
19
+ specimen_id: str = Field(
20
+ ..., description="Human-readable identifier of the parent specimen"
21
+ )
22
+ microCT_info: Dict[str, Any] = Field(
23
+ ..., description="MicroCT information of block"
24
+ )
25
+
26
+
27
+ class BlockUpdate(BlockBase):
28
+ pass
29
+
30
+
31
+ class BlockResponse(BlockBase):
32
+
33
+ block_id: str = Field(
34
+ ..., description="Unique human-readable identifier for the block"
35
+ )
36
+ specimen_id: str = Field(
37
+ ..., description="Human-readable identifier of the parent specimen"
38
+ )
39
+ created_at: Optional[datetime] = None
40
+ updated_at: Optional[datetime] = None
@@ -0,0 +1,45 @@
1
+ from typing import Optional
2
+ from pydantic import BaseModel, Field
3
+ from datetime import datetime
4
+
5
+
6
+ class CuttingSessionBase(BaseModel):
7
+ start_time: Optional[datetime] = None
8
+ end_time: Optional[datetime] = None
9
+ operator: Optional[str] = None
10
+ sectioning_device: Optional[str] = None
11
+ media_type: Optional[str] = None
12
+ knife_id: Optional[str] = None
13
+ block_id: Optional[str] = None
14
+ specimen_id: Optional[str] = None
15
+
16
+ class Config:
17
+ extra = "allow"
18
+
19
+
20
+ class CuttingSessionCreate(CuttingSessionBase):
21
+ cutting_session_id: str = Field(..., description="ID of cutting session")
22
+ start_time: datetime = Field(..., description="Time when cutting session started")
23
+ sectioning_device: str = Field(..., description="Device used for sectioning")
24
+ media_type: str = Field(
25
+ ..., description="Type of substrate the sections are placed upon"
26
+ )
27
+ block_id: str = Field(
28
+ ..., description="ID of block cutting session is associated with"
29
+ )
30
+
31
+
32
+ class CuttingSessionUpdate(CuttingSessionBase):
33
+ pass
34
+
35
+
36
+ class CuttingSessionResponse(CuttingSessionBase):
37
+ cutting_session_id: str = Field(..., description="Unique ID of cutting session")
38
+ specimen_id: str = Field(..., description="Human-readable ID of specimen")
39
+ block_id: str = Field(..., description="Human-readable ID of block")
40
+ start_time: datetime
41
+ sectioning_device: str
42
+ media_type: str
43
+
44
+ created_at: datetime
45
+ updated_at: Optional[datetime] = None