temdb-models 2.0.0__tar.gz → 3.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.
- temdb_models-3.0.0/LICENSE +11 -0
- temdb_models-3.0.0/PKG-INFO +12 -0
- {temdb_models-2.0.0 → temdb_models-3.0.0}/pyproject.toml +9 -3
- {temdb_models-2.0.0 → temdb_models-3.0.0}/src/temdb/models/__init__.py +53 -6
- {temdb_models-2.0.0 → temdb_models-3.0.0}/src/temdb/models/acquisition.py +86 -36
- temdb_models-3.0.0/src/temdb/models/base.py +9 -0
- {temdb_models-2.0.0 → temdb_models-3.0.0}/src/temdb/models/block.py +8 -3
- {temdb_models-2.0.0 → temdb_models-3.0.0}/src/temdb/models/cutting_session.py +8 -4
- temdb_models-3.0.0/src/temdb/models/dataset.py +46 -0
- temdb_models-3.0.0/src/temdb/models/enums.py +27 -0
- {temdb_models-2.0.0 → temdb_models-3.0.0}/src/temdb/models/error.py +4 -2
- temdb_models-3.0.0/src/temdb/models/lens_correction.py +39 -0
- temdb_models-3.0.0/src/temdb/models/microscope.py +35 -0
- {temdb_models-2.0.0 → temdb_models-3.0.0}/src/temdb/models/quality_control.py +7 -5
- {temdb_models-2.0.0 → temdb_models-3.0.0}/src/temdb/models/roi.py +27 -11
- temdb_models-3.0.0/src/temdb/models/section.py +154 -0
- {temdb_models-2.0.0 → temdb_models-3.0.0}/src/temdb/models/specimen.py +11 -4
- {temdb_models-2.0.0 → temdb_models-3.0.0}/src/temdb/models/substrate.py +20 -14
- temdb_models-3.0.0/src/temdb/models/task.py +78 -0
- {temdb_models-2.0.0 → temdb_models-3.0.0}/src/temdb/models/tile.py +15 -10
- temdb_models-3.0.0/src/temdb/models/utils/uri.py +222 -0
- temdb_models-2.0.0/PKG-INFO +0 -6
- temdb_models-2.0.0/src/temdb/models/enums.py +0 -27
- temdb_models-2.0.0/src/temdb/models/section.py +0 -98
- temdb_models-2.0.0/src/temdb/models/task.py +0 -55
- {temdb_models-2.0.0 → temdb_models-3.0.0}/.gitignore +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Copyright 2026 Allen Institute
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
4
|
+
|
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
6
|
+
|
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
8
|
+
|
|
9
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
10
|
+
|
|
11
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: temdb-models
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: Shared Pydantic models for TEMdb
|
|
5
|
+
License-Expression: BSD-3-Clause
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: pathlib-abc>=0.5
|
|
9
|
+
Requires-Dist: platformdirs>=4.10
|
|
10
|
+
Requires-Dist: pydantic>=2.13
|
|
11
|
+
Requires-Dist: pyyaml>=6.0
|
|
12
|
+
Requires-Dist: smart-open[s3]>=7.6
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "temdb-models"
|
|
3
|
-
version = "
|
|
3
|
+
version = "3.0.0"
|
|
4
4
|
description = "Shared Pydantic models for TEMdb"
|
|
5
|
+
license = "BSD-3-Clause"
|
|
6
|
+
license-files = ["LICENSE"]
|
|
5
7
|
requires-python = ">=3.10"
|
|
6
8
|
dependencies = [
|
|
7
|
-
"
|
|
9
|
+
"pathlib-abc>=0.5",
|
|
10
|
+
"platformdirs>=4.10",
|
|
11
|
+
"pydantic>=2.13",
|
|
12
|
+
"pyyaml>=6.0",
|
|
13
|
+
"smart-open[s3]>=7.6",
|
|
8
14
|
]
|
|
9
15
|
|
|
10
16
|
[build-system]
|
|
11
|
-
requires = ["hatchling"]
|
|
17
|
+
requires = ["hatchling>=1.27"]
|
|
12
18
|
build-backend = "hatchling.build"
|
|
13
19
|
|
|
14
20
|
[tool.hatch.build.targets.wheel]
|
|
@@ -23,12 +23,34 @@ from temdb.models.cutting_session import (
|
|
|
23
23
|
CuttingSessionResponse,
|
|
24
24
|
CuttingSessionUpdate,
|
|
25
25
|
)
|
|
26
|
+
from temdb.models.dataset import (
|
|
27
|
+
DatasetBase,
|
|
28
|
+
DatasetCreate,
|
|
29
|
+
DatasetResponse,
|
|
30
|
+
DatasetUpdate,
|
|
31
|
+
)
|
|
26
32
|
from temdb.models.enums import (
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
QC_STATES,
|
|
34
|
+
RUN_STATUSES,
|
|
35
|
+
SECTION_CONDITIONS,
|
|
36
|
+
TASK_KINDS,
|
|
37
|
+
TRANSFER_STATES,
|
|
38
|
+
AcquisitionStatusFilter,
|
|
29
39
|
SectionQuality,
|
|
30
40
|
)
|
|
31
41
|
from temdb.models.error import APIErrorResponse
|
|
42
|
+
from temdb.models.lens_correction import (
|
|
43
|
+
LensCorrectionBase,
|
|
44
|
+
LensCorrectionCreate,
|
|
45
|
+
LensCorrectionResponse,
|
|
46
|
+
LensCorrectionUpdate,
|
|
47
|
+
)
|
|
48
|
+
from temdb.models.microscope import (
|
|
49
|
+
MicroscopeBase,
|
|
50
|
+
MicroscopeCreate,
|
|
51
|
+
MicroscopeResponse,
|
|
52
|
+
MicroscopeUpdate,
|
|
53
|
+
)
|
|
32
54
|
from temdb.models.quality_control import (
|
|
33
55
|
AcquisitionFocusScoresResponse,
|
|
34
56
|
BadFocusTileInfo,
|
|
@@ -36,9 +58,9 @@ from temdb.models.quality_control import (
|
|
|
36
58
|
TileFocusScore,
|
|
37
59
|
)
|
|
38
60
|
from temdb.models.roi import (
|
|
39
|
-
ROIBase,
|
|
40
61
|
ROIChildrenResponse,
|
|
41
62
|
ROICreate,
|
|
63
|
+
ROIPayload,
|
|
42
64
|
ROIResponse,
|
|
43
65
|
ROIUpdate,
|
|
44
66
|
)
|
|
@@ -46,6 +68,7 @@ from temdb.models.section import (
|
|
|
46
68
|
SectionBase,
|
|
47
69
|
SectionCreate,
|
|
48
70
|
SectioningRunParameters,
|
|
71
|
+
SectionMetric,
|
|
49
72
|
SectionMetrics,
|
|
50
73
|
SectionResponse,
|
|
51
74
|
SectionUpdate,
|
|
@@ -78,12 +101,18 @@ from temdb.models.tile import (
|
|
|
78
101
|
TileResponse,
|
|
79
102
|
TileUpdate,
|
|
80
103
|
)
|
|
104
|
+
from temdb.models.utils.uri import URI
|
|
81
105
|
|
|
82
106
|
__all__ = [
|
|
83
107
|
# Enums
|
|
84
|
-
"
|
|
85
|
-
"AcquisitionTaskStatus",
|
|
108
|
+
"AcquisitionStatusFilter",
|
|
86
109
|
"SectionQuality",
|
|
110
|
+
# Vocab constants
|
|
111
|
+
"RUN_STATUSES",
|
|
112
|
+
"QC_STATES",
|
|
113
|
+
"TRANSFER_STATES",
|
|
114
|
+
"SECTION_CONDITIONS",
|
|
115
|
+
"TASK_KINDS",
|
|
87
116
|
# Tile
|
|
88
117
|
"Matcher",
|
|
89
118
|
"TileBase",
|
|
@@ -113,7 +142,7 @@ __all__ = [
|
|
|
113
142
|
"BlockUpdate",
|
|
114
143
|
"BlockResponse",
|
|
115
144
|
# ROI
|
|
116
|
-
"
|
|
145
|
+
"ROIPayload",
|
|
117
146
|
"ROICreate",
|
|
118
147
|
"ROIUpdate",
|
|
119
148
|
"ROIResponse",
|
|
@@ -123,6 +152,7 @@ __all__ = [
|
|
|
123
152
|
"SectionCreate",
|
|
124
153
|
"SectionUpdate",
|
|
125
154
|
"SectionResponse",
|
|
155
|
+
"SectionMetric",
|
|
126
156
|
"SectionMetrics",
|
|
127
157
|
"SectioningRunParameters",
|
|
128
158
|
# Substrate
|
|
@@ -143,6 +173,21 @@ __all__ = [
|
|
|
143
173
|
"CuttingSessionCreate",
|
|
144
174
|
"CuttingSessionUpdate",
|
|
145
175
|
"CuttingSessionResponse",
|
|
176
|
+
# Dataset
|
|
177
|
+
"DatasetBase",
|
|
178
|
+
"DatasetCreate",
|
|
179
|
+
"DatasetUpdate",
|
|
180
|
+
"DatasetResponse",
|
|
181
|
+
# Microscope
|
|
182
|
+
"MicroscopeBase",
|
|
183
|
+
"MicroscopeCreate",
|
|
184
|
+
"MicroscopeUpdate",
|
|
185
|
+
"MicroscopeResponse",
|
|
186
|
+
# Lens Correction
|
|
187
|
+
"LensCorrectionBase",
|
|
188
|
+
"LensCorrectionCreate",
|
|
189
|
+
"LensCorrectionUpdate",
|
|
190
|
+
"LensCorrectionResponse",
|
|
146
191
|
# Error
|
|
147
192
|
"APIErrorResponse",
|
|
148
193
|
# Quality Control
|
|
@@ -150,4 +195,6 @@ __all__ = [
|
|
|
150
195
|
"AcquisitionFocusScoresResponse",
|
|
151
196
|
"BadFocusTileInfo",
|
|
152
197
|
"BadFocusTilesResponse",
|
|
198
|
+
# Utils
|
|
199
|
+
"URI",
|
|
153
200
|
]
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import uuid
|
|
1
2
|
from datetime import datetime
|
|
2
3
|
from typing import Any
|
|
3
4
|
|
|
4
|
-
from pydantic import
|
|
5
|
-
from
|
|
5
|
+
from pydantic import ConfigDict, Field, field_validator
|
|
6
|
+
from pydantic_core import PydanticCustomError
|
|
6
7
|
|
|
8
|
+
from temdb.models.base import TEMDBModel
|
|
9
|
+
from temdb.models.enums import QC_STATES, RUN_STATUSES, TASK_KINDS, TRANSFER_STATES
|
|
7
10
|
|
|
8
|
-
class LensCorrectionModel(BaseModel):
|
|
9
|
-
model_config = ConfigDict(extra="allow")
|
|
10
11
|
|
|
12
|
+
class LensCorrectionModel(TEMDBModel):
|
|
11
13
|
id: int = Field(..., description="ID of lens correction model")
|
|
12
14
|
type: str = Field(
|
|
13
15
|
...,
|
|
@@ -23,18 +25,14 @@ class LensCorrectionModel(BaseModel):
|
|
|
23
25
|
)
|
|
24
26
|
|
|
25
27
|
|
|
26
|
-
class Calibration(
|
|
27
|
-
model_config = ConfigDict(extra="allow")
|
|
28
|
-
|
|
28
|
+
class Calibration(TEMDBModel):
|
|
29
29
|
pixel_size: float = Field(..., description="Pixel size in nm")
|
|
30
30
|
rotation_angle: float = Field(..., description="Rotation angle in degrees")
|
|
31
31
|
lens_model: LensCorrectionModel | None = Field(None, description="Lens correction model")
|
|
32
32
|
aperture_centroid: list[float] | None = Field(None, description="Aperture centroid in stage coordinates in nm")
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
class HardwareParams(
|
|
36
|
-
model_config = ConfigDict(extra="allow")
|
|
37
|
-
|
|
35
|
+
class HardwareParams(TEMDBModel):
|
|
38
36
|
scope_id: str = Field(..., description="ID of microscope")
|
|
39
37
|
camera_model: str = Field(..., description="Model of camera")
|
|
40
38
|
camera_serial: str = Field(..., description="Serial number of camera")
|
|
@@ -42,9 +40,7 @@ class HardwareParams(BaseModel):
|
|
|
42
40
|
media_type: str = Field(..., description="Type of substrate in microscope")
|
|
43
41
|
|
|
44
42
|
|
|
45
|
-
class AcquisitionParams(
|
|
46
|
-
model_config = ConfigDict(extra="allow")
|
|
47
|
-
|
|
43
|
+
class AcquisitionParams(TEMDBModel):
|
|
48
44
|
magnification: int = Field(..., description="Magnification of acquisition")
|
|
49
45
|
spot_size: int = Field(..., description="Spot size of acquisition")
|
|
50
46
|
exposure_time: int = Field(..., description="Exposure time of camera in ms")
|
|
@@ -53,9 +49,7 @@ class AcquisitionParams(BaseModel):
|
|
|
53
49
|
saved_bit_depth: int = Field(..., description="Bit depth of saved image")
|
|
54
50
|
|
|
55
51
|
|
|
56
|
-
class StorageLocation(
|
|
57
|
-
model_config = ConfigDict(extra="allow")
|
|
58
|
-
|
|
52
|
+
class StorageLocation(TEMDBModel):
|
|
59
53
|
location_type: str = Field(..., description="Type of storage location, e.g. local, s3, etc.")
|
|
60
54
|
base_path: str = Field(..., description="Base path of storage location")
|
|
61
55
|
is_current: bool = Field(..., description="Whether this is the current storage location")
|
|
@@ -63,24 +57,17 @@ class StorageLocation(BaseModel):
|
|
|
63
57
|
metadata: dict[str, Any] = Field(..., description="Metadata of storage location")
|
|
64
58
|
|
|
65
59
|
|
|
66
|
-
class StorageLocationCreate(
|
|
67
|
-
model_config = ConfigDict(extra="allow")
|
|
68
|
-
|
|
60
|
+
class StorageLocationCreate(TEMDBModel):
|
|
69
61
|
location_type: str = Field(..., description="Type of storage location, e.g. local, s3, etc.")
|
|
70
62
|
base_path: str = Field(..., description="Base path of storage location")
|
|
71
63
|
metadata: dict[str, Any] = Field(default_factory=dict, description="Metadata of storage location")
|
|
72
64
|
|
|
73
65
|
|
|
74
|
-
class AcquisitionBase(
|
|
75
|
-
model_config = ConfigDict(extra="allow")
|
|
76
|
-
|
|
66
|
+
class AcquisitionBase(TEMDBModel):
|
|
77
67
|
hardware_settings: HardwareParams | None = None
|
|
78
68
|
acquisition_settings: AcquisitionParams | None = None
|
|
79
69
|
calibration_info: Calibration | None = None
|
|
80
|
-
|
|
81
|
-
tilt_angle: float | None = Field(None, description="Tilt angle of acquisition in degrees")
|
|
82
|
-
lens_correction: bool | None = Field(None, description="Whether this acquisition is a lens correction calibration")
|
|
83
|
-
end_time: datetime | None = Field(None, description="End time of acquisition")
|
|
70
|
+
tilt_angle_deg: float | None = Field(None, description="Tilt angle of acquisition in degrees")
|
|
84
71
|
storage_locations: list[StorageLocation] | None = Field(None, description="Storage locations of acquisition")
|
|
85
72
|
montage_set_name: str | None = Field(None, description="Name of montage set")
|
|
86
73
|
sub_region: dict[str, int] | None = Field(None, description="Sub region of acquisition")
|
|
@@ -90,42 +77,105 @@ class AcquisitionBase(BaseModel):
|
|
|
90
77
|
class AcquisitionCreate(AcquisitionBase):
|
|
91
78
|
acquisition_id: str = Field(..., description="Unique acquisition identifier")
|
|
92
79
|
montage_id: str = Field(..., description="Montage identifier")
|
|
93
|
-
roi_id: str = Field(
|
|
80
|
+
roi_id: str | None = Field(None, description="ROI identifier (required for montage runs)")
|
|
94
81
|
acquisition_task_id: str = Field(..., description="Parent task identifier")
|
|
82
|
+
microscope_id: uuid.UUID = Field(..., description="Microscope that executes this run")
|
|
83
|
+
dataset_id: str | None = Field(None, description="Dataset this acquisition belongs to (UUIDv7)")
|
|
84
|
+
kind: str = Field("montage", description="Run kind: montage or lens_correction")
|
|
85
|
+
lc_id: uuid.UUID | None = Field(None, description="Lens correction applied to this run")
|
|
95
86
|
hardware_settings: HardwareParams = Field(..., description="Hardware settings of acquisition")
|
|
96
87
|
acquisition_settings: AcquisitionParams = Field(..., description="Acquisition settings of acquisition")
|
|
97
|
-
tilt_angle: float = Field(..., description="Tilt angle of acquisition in degrees")
|
|
98
|
-
lens_correction: bool = Field(..., description="Whether this acquisition is a lens correction calibration")
|
|
99
|
-
status: AcquisitionStatus = Field(default=AcquisitionStatus.IMAGING, description="Status of acquisition")
|
|
100
88
|
start_time: datetime | None = Field(None, description="Start time of acquisition (defaults to now if not provided)")
|
|
89
|
+
created_at: datetime | None = Field(None, description="Creation timestamp; server-generated if omitted")
|
|
90
|
+
|
|
91
|
+
@field_validator("kind")
|
|
92
|
+
@classmethod
|
|
93
|
+
def _kind_vocab(cls, v: str) -> str:
|
|
94
|
+
if v not in TASK_KINDS:
|
|
95
|
+
raise PydanticCustomError("value_error", f"kind must be one of {TASK_KINDS}")
|
|
96
|
+
return v
|
|
101
97
|
|
|
102
98
|
|
|
103
99
|
class AcquisitionUpdate(AcquisitionBase):
|
|
104
100
|
calibration_info: dict[str, Any] | None = Field(None, description="Calibration information of acquisition")
|
|
101
|
+
status: str | None = Field(None, description="Terminal run status (write-once): complete, aborted, or failed")
|
|
102
|
+
end_time: datetime | None = Field(None, description="End time of acquisition (set with terminal status)")
|
|
103
|
+
error_message: str | None = Field(None, description="Failure detail for terminal status")
|
|
104
|
+
qc_state: str | None = Field(None, description="QC axis state")
|
|
105
|
+
transfer_state: str | None = Field(None, description="Transfer axis state")
|
|
106
|
+
updated_by: str | None = Field(None, description="Actor stamped on qc_state/transfer_state changes")
|
|
107
|
+
tile_count: int | None = Field(None, description="Rollup: total tiles")
|
|
108
|
+
avg_focus_score: float | None = Field(None, description="Rollup: mean tile focus score")
|
|
109
|
+
failed_tile_count: int | None = Field(None, description="Rollup: failed tiles")
|
|
110
|
+
median_match_quality: float | None = Field(None, description="Rollup: median tile match quality")
|
|
111
|
+
|
|
112
|
+
@field_validator("status")
|
|
113
|
+
@classmethod
|
|
114
|
+
def _status_terminal(cls, v: str | None) -> str:
|
|
115
|
+
if v not in RUN_STATUSES:
|
|
116
|
+
raise PydanticCustomError("value_error", f"status must be one of {RUN_STATUSES}")
|
|
117
|
+
return v
|
|
118
|
+
|
|
119
|
+
@field_validator("qc_state")
|
|
120
|
+
@classmethod
|
|
121
|
+
def _qc_state_vocab(cls, v: str | None) -> str:
|
|
122
|
+
if v not in QC_STATES:
|
|
123
|
+
raise PydanticCustomError("value_error", f"qc_state must be one of {QC_STATES}")
|
|
124
|
+
return v
|
|
125
|
+
|
|
126
|
+
@field_validator("transfer_state")
|
|
127
|
+
@classmethod
|
|
128
|
+
def _transfer_state_vocab(cls, v: str | None) -> str:
|
|
129
|
+
if v not in TRANSFER_STATES:
|
|
130
|
+
raise PydanticCustomError("value_error", f"transfer_state must be one of {TRANSFER_STATES}")
|
|
131
|
+
return v
|
|
105
132
|
|
|
106
133
|
|
|
107
134
|
class AcquisitionResponse(AcquisitionBase):
|
|
135
|
+
model_config = ConfigDict(from_attributes=True, extra="ignore")
|
|
136
|
+
|
|
137
|
+
id: int = Field(..., description="Internal database ID")
|
|
108
138
|
acquisition_id: str = Field(..., description="Unique acquisition identifier")
|
|
139
|
+
run_id: uuid.UUID = Field(..., description="DB-minted UUIDv7 execution key")
|
|
109
140
|
montage_id: str = Field(..., description="Montage identifier")
|
|
110
|
-
specimen_id: str = Field(
|
|
111
|
-
roi_id: str = Field(
|
|
141
|
+
specimen_id: str | None = Field(None, description="Specimen identifier")
|
|
142
|
+
roi_id: str | None = Field(None, description="ROI identifier")
|
|
112
143
|
acquisition_task_id: str = Field(..., description="Parent task identifier")
|
|
144
|
+
microscope_id: uuid.UUID = Field(..., description="Microscope that executed this run")
|
|
145
|
+
dataset_id: uuid.UUID | None = Field(None, description="Dataset this acquisition belongs to (UUIDv7)")
|
|
146
|
+
kind: str = Field(..., description="Run kind: montage or lens_correction")
|
|
147
|
+
lc_id: uuid.UUID | None = Field(None, description="Lens correction applied to this run")
|
|
113
148
|
hardware_settings: HardwareParams = Field(..., description="Hardware settings of acquisition")
|
|
114
149
|
acquisition_settings: AcquisitionParams = Field(..., description="Acquisition settings of acquisition")
|
|
115
|
-
|
|
150
|
+
replaces_acquisition_id: str | None = Field(
|
|
151
|
+
None, description="Business ID (acquisition_id) of acquisition this acquisition replaces"
|
|
152
|
+
)
|
|
153
|
+
status: str | None = Field(None, description="Terminal run status; null while in flight")
|
|
154
|
+
error_message: str | None = Field(None, description="Failure detail for terminal status")
|
|
155
|
+
qc_state: str = Field(..., description="QC axis state")
|
|
156
|
+
qc_state_updated_at: datetime | None = None
|
|
157
|
+
qc_state_updated_by: str | None = None
|
|
158
|
+
transfer_state: str = Field(..., description="Transfer axis state")
|
|
159
|
+
transfer_state_updated_at: datetime | None = None
|
|
160
|
+
transfer_state_updated_by: str | None = None
|
|
161
|
+
tile_count: int | None = None
|
|
162
|
+
avg_focus_score: float | None = None
|
|
163
|
+
failed_tile_count: int | None = None
|
|
164
|
+
median_match_quality: float | None = None
|
|
116
165
|
start_time: datetime = Field(..., description="Start time of acquisition")
|
|
166
|
+
end_time: datetime | None = Field(None, description="End time of acquisition")
|
|
117
167
|
|
|
118
168
|
created_at: datetime | None = None
|
|
119
169
|
updated_at: datetime | None = None
|
|
120
170
|
|
|
121
171
|
|
|
122
|
-
class AcquisitionFullMetadata(
|
|
172
|
+
class AcquisitionFullMetadata(TEMDBModel):
|
|
123
173
|
"""Acquisition with complete hierarchy metadata."""
|
|
124
174
|
|
|
125
|
-
model_config = ConfigDict(extra="
|
|
175
|
+
model_config = ConfigDict(from_attributes=True, extra="ignore", populate_by_name=True)
|
|
126
176
|
|
|
127
177
|
acquisition: AcquisitionResponse
|
|
128
|
-
task: dict[str, Any] | None = None
|
|
178
|
+
task: dict[str, Any] | None = Field(None, alias="acquisition_task")
|
|
129
179
|
roi: dict[str, Any] | None = None
|
|
130
180
|
section: dict[str, Any] | None = None
|
|
131
181
|
cutting_session: dict[str, Any] | None = None
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
|
-
from pydantic import
|
|
4
|
+
from pydantic import ConfigDict, Field
|
|
5
5
|
|
|
6
|
+
from .base import TEMDBModel
|
|
6
7
|
|
|
7
|
-
class BlockBase(BaseModel):
|
|
8
|
-
model_config = ConfigDict(extra="allow")
|
|
9
8
|
|
|
9
|
+
class BlockBase(TEMDBModel):
|
|
10
10
|
microCT_info: dict[str, Any] | None = Field(None, description="MicroCT information of block")
|
|
11
|
+
description: str | None = Field(None, description="Description of block")
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class BlockCreate(BlockBase):
|
|
14
15
|
block_id: str = Field(..., description="Unique block identifier")
|
|
15
16
|
specimen_id: str = Field(..., description="Parent specimen ID")
|
|
17
|
+
created_at: datetime | None = Field(None, description="Creation timestamp; server-generated if omitted")
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
class BlockUpdate(BlockBase):
|
|
@@ -20,6 +22,9 @@ class BlockUpdate(BlockBase):
|
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
class BlockResponse(BlockBase):
|
|
25
|
+
model_config = ConfigDict(from_attributes=True, extra="ignore")
|
|
26
|
+
|
|
27
|
+
id: int
|
|
23
28
|
block_id: str = Field(..., description="Unique block identifier")
|
|
24
29
|
specimen_id: str = Field(..., description="Parent specimen ID")
|
|
25
30
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
|
|
3
|
-
from pydantic import
|
|
3
|
+
from pydantic import ConfigDict, Field
|
|
4
4
|
|
|
5
|
+
from .base import TEMDBModel
|
|
5
6
|
|
|
6
|
-
class CuttingSessionBase(BaseModel):
|
|
7
|
-
"""Base cutting session fields."""
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
class CuttingSessionBase(TEMDBModel):
|
|
9
|
+
"""Base cutting session fields."""
|
|
10
10
|
|
|
11
11
|
start_time: datetime | None = Field(None, description="Time when cutting session started")
|
|
12
12
|
end_time: datetime | None = Field(None, description="Time when cutting session ended")
|
|
@@ -24,6 +24,7 @@ class CuttingSessionCreate(CuttingSessionBase):
|
|
|
24
24
|
start_time: datetime = Field(..., description="Time when cutting session started")
|
|
25
25
|
sectioning_device: str = Field(..., description="Device used for sectioning")
|
|
26
26
|
media_type: str = Field(..., description="Type of substrate the sections are placed upon")
|
|
27
|
+
created_at: datetime | None = Field(None, description="Creation timestamp; server-generated if omitted")
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
class CuttingSessionUpdate(CuttingSessionBase):
|
|
@@ -36,6 +37,9 @@ class CuttingSessionUpdate(CuttingSessionBase):
|
|
|
36
37
|
class CuttingSessionResponse(CuttingSessionBase):
|
|
37
38
|
"""Schema for cutting session API responses."""
|
|
38
39
|
|
|
40
|
+
model_config = ConfigDict(from_attributes=True, extra="ignore")
|
|
41
|
+
|
|
42
|
+
id: int
|
|
39
43
|
cutting_session_id: str = Field(..., description="Unique cutting session identifier")
|
|
40
44
|
specimen_id: str = Field(..., description="ID of specimen")
|
|
41
45
|
block_id: str = Field(..., description="ID of block")
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import ConfigDict, Field
|
|
6
|
+
|
|
7
|
+
from .base import TEMDBModel
|
|
8
|
+
|
|
9
|
+
SizeClass = Literal["small", "medium", "large", "xlarge"]
|
|
10
|
+
DatasetStatus = Literal["collecting", "collected", "archived"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DatasetBase(TEMDBModel):
|
|
14
|
+
description: str | None = Field(None, description="Human description of the dataset")
|
|
15
|
+
specimen_id: str | None = Field(None, description="Specimen this dataset belongs to")
|
|
16
|
+
size_class: SizeClass | None = Field(None, description="Planner size class; drives tile partitioning")
|
|
17
|
+
estimated_tile_count: int | None = Field(None, description="Estimated total tiles; resolves size_class when set")
|
|
18
|
+
metadata_json: dict[str, Any] | None = Field(None, description="Free-form metadata")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DatasetCreate(DatasetBase):
|
|
22
|
+
name: str = Field(..., description="Unique human-readable dataset name")
|
|
23
|
+
parent_dataset_id: uuid.UUID | None = Field(None, description="Optional parent dataset (one level only)")
|
|
24
|
+
created_at: datetime | None = Field(None, description="Creation timestamp; server-generated if omitted")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DatasetUpdate(TEMDBModel):
|
|
28
|
+
description: str | None = None
|
|
29
|
+
status: DatasetStatus | None = None
|
|
30
|
+
size_class: SizeClass | None = None
|
|
31
|
+
metadata_json: dict[str, Any] | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DatasetResponse(DatasetBase):
|
|
35
|
+
model_config = ConfigDict(from_attributes=True, extra="ignore")
|
|
36
|
+
|
|
37
|
+
dataset_id: uuid.UUID = Field(..., description="UUIDv7 primary key")
|
|
38
|
+
name: str = Field(..., description="Unique human-readable dataset name")
|
|
39
|
+
parent_dataset_id: uuid.UUID | None = Field(None, description="Parent dataset if this is a child (one level only)")
|
|
40
|
+
status: DatasetStatus = Field(..., description="Lifecycle status")
|
|
41
|
+
tile_hash_modulus: int | None = Field(None, description="System-managed frozen partition modulus")
|
|
42
|
+
estimated_tile_count: int | None = Field(None, description="Estimated total tiles recorded at creation")
|
|
43
|
+
collected_at: datetime | None = None
|
|
44
|
+
archived_at: datetime | None = None
|
|
45
|
+
created_at: datetime | None = None
|
|
46
|
+
updated_at: datetime | None = None
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SectionQuality(str, Enum):
|
|
6
|
+
GOOD = "good"
|
|
7
|
+
BROKEN = "broken"
|
|
8
|
+
THIN = "thin"
|
|
9
|
+
THICK = "thick"
|
|
10
|
+
EMPTY = "empty"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MatchPosition(str, Enum):
|
|
14
|
+
INVALID = "invalid"
|
|
15
|
+
CENTER = "center"
|
|
16
|
+
LEFT = "left"
|
|
17
|
+
TOP = "top"
|
|
18
|
+
RIGHT = "right"
|
|
19
|
+
BOTTOM = "bottom"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
RUN_STATUSES = ("complete", "aborted", "failed")
|
|
23
|
+
AcquisitionStatusFilter = Literal["complete", "aborted", "failed", "in_flight"]
|
|
24
|
+
QC_STATES = ("pending", "qc_pass", "qc_fail", "needs_review")
|
|
25
|
+
TRANSFER_STATES = ("not_started", "in_progress", "complete", "error")
|
|
26
|
+
SECTION_CONDITIONS = ("ok", "damaged", "destroyed", "contaminated", "lost")
|
|
27
|
+
TASK_KINDS = ("montage", "lens_correction")
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
-
from pydantic import
|
|
5
|
+
from pydantic import Field
|
|
6
6
|
|
|
7
|
+
from .base import TEMDBModel
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
class APIErrorResponse(TEMDBModel):
|
|
9
11
|
"""Standard structure for API error responses."""
|
|
10
12
|
|
|
11
13
|
detail: str = Field(..., description="Human-readable description of the error.")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import ConfigDict, Field
|
|
6
|
+
|
|
7
|
+
from .base import TEMDBModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LensCorrectionBase(TEMDBModel):
|
|
11
|
+
microscope_id: uuid.UUID
|
|
12
|
+
magnification: int
|
|
13
|
+
started_at: datetime
|
|
14
|
+
source_run_id: uuid.UUID | None = None
|
|
15
|
+
source_dataset_id: uuid.UUID | None = None
|
|
16
|
+
shared_transform: dict[str, Any] | None = None
|
|
17
|
+
correction_x_uri: str | None = None
|
|
18
|
+
correction_y_uri: str | None = None
|
|
19
|
+
solver_params: dict[str, Any] | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LensCorrectionCreate(LensCorrectionBase):
|
|
23
|
+
lc_id: uuid.UUID | None = Field(None, description="Optional client-supplied UUIDv7")
|
|
24
|
+
created_at: datetime | None = Field(None, description="Creation timestamp; server-generated if omitted")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LensCorrectionUpdate(TEMDBModel):
|
|
28
|
+
shared_transform: dict[str, Any] | None = None
|
|
29
|
+
correction_x_uri: str | None = None
|
|
30
|
+
correction_y_uri: str | None = None
|
|
31
|
+
solver_params: dict[str, Any] | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class LensCorrectionResponse(LensCorrectionBase):
|
|
35
|
+
model_config = ConfigDict(from_attributes=True, extra="ignore")
|
|
36
|
+
|
|
37
|
+
lc_id: uuid.UUID
|
|
38
|
+
created_at: datetime
|
|
39
|
+
updated_at: datetime | None = None
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
from pydantic import ConfigDict, Field
|
|
5
|
+
|
|
6
|
+
from .base import TEMDBModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MicroscopeBase(TEMDBModel):
|
|
10
|
+
microscope_type: str = Field("TEM", description="Instrument type")
|
|
11
|
+
model: str | None = Field(None, description="Manufacturer model")
|
|
12
|
+
location: str | None = Field(None, description="Physical location")
|
|
13
|
+
notes: str | None = Field(None, description="Free-form notes")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MicroscopeCreate(MicroscopeBase):
|
|
17
|
+
label: str = Field(..., description="Unique human handle, e.g. 'TEM-01'")
|
|
18
|
+
microscope_id: uuid.UUID | None = Field(None, description="Optional client-supplied UUIDv7")
|
|
19
|
+
created_at: datetime | None = Field(None, description="Creation timestamp; server-generated if omitted")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MicroscopeUpdate(TEMDBModel):
|
|
23
|
+
microscope_type: str | None = None
|
|
24
|
+
model: str | None = None
|
|
25
|
+
location: str | None = None
|
|
26
|
+
notes: str | None = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MicroscopeResponse(MicroscopeBase):
|
|
30
|
+
model_config = ConfigDict(from_attributes=True, extra="ignore")
|
|
31
|
+
|
|
32
|
+
microscope_id: uuid.UUID
|
|
33
|
+
label: str = Field(..., description="Unique human handle, e.g. 'TEM-01'")
|
|
34
|
+
created_at: datetime
|
|
35
|
+
updated_at: datetime | None = None
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
from pydantic import
|
|
1
|
+
from pydantic import Field
|
|
2
2
|
|
|
3
|
+
from .base import TEMDBModel
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
class TileFocusScore(TEMDBModel):
|
|
5
7
|
"""Represents focus score data for a single tile."""
|
|
6
8
|
|
|
7
9
|
tile_id: str = Field(..., description="Unique ID of the tile.")
|
|
@@ -9,7 +11,7 @@ class TileFocusScore(BaseModel):
|
|
|
9
11
|
focus_score: float = Field(..., description="Calculated focus score for the tile.")
|
|
10
12
|
|
|
11
13
|
|
|
12
|
-
class AcquisitionFocusScoresResponse(
|
|
14
|
+
class AcquisitionFocusScoresResponse(TEMDBModel):
|
|
13
15
|
"""Response model containing focus scores for an acquisition."""
|
|
14
16
|
|
|
15
17
|
acquisition_id: str = Field(..., description="ID of the acquisition analyzed.")
|
|
@@ -22,7 +24,7 @@ class AcquisitionFocusScoresResponse(BaseModel):
|
|
|
22
24
|
max_focus: float | None = Field(None, description="Maximum focus score found.")
|
|
23
25
|
|
|
24
26
|
|
|
25
|
-
class BadFocusTileInfo(
|
|
27
|
+
class BadFocusTileInfo(TEMDBModel):
|
|
26
28
|
"""Information about a tile with bad focus."""
|
|
27
29
|
|
|
28
30
|
tile_id: str
|
|
@@ -35,7 +37,7 @@ class BadFocusTileInfo(BaseModel):
|
|
|
35
37
|
specimen_id: str | None = None
|
|
36
38
|
|
|
37
39
|
|
|
38
|
-
class BadFocusTilesResponse(
|
|
40
|
+
class BadFocusTilesResponse(TEMDBModel):
|
|
39
41
|
"""Response model for bad focus tiles query."""
|
|
40
42
|
|
|
41
43
|
threshold: float
|