temdb-models 2.0.0__py3-none-any.whl

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.
@@ -0,0 +1,153 @@
1
+ from temdb.models.acquisition import (
2
+ AcquisitionBase,
3
+ AcquisitionCreate,
4
+ AcquisitionFullMetadata,
5
+ AcquisitionParams,
6
+ AcquisitionResponse,
7
+ AcquisitionUpdate,
8
+ Calibration,
9
+ HardwareParams,
10
+ LensCorrectionModel,
11
+ StorageLocation,
12
+ StorageLocationCreate,
13
+ )
14
+ from temdb.models.block import (
15
+ BlockBase,
16
+ BlockCreate,
17
+ BlockResponse,
18
+ BlockUpdate,
19
+ )
20
+ from temdb.models.cutting_session import (
21
+ CuttingSessionBase,
22
+ CuttingSessionCreate,
23
+ CuttingSessionResponse,
24
+ CuttingSessionUpdate,
25
+ )
26
+ from temdb.models.enums import (
27
+ AcquisitionStatus,
28
+ AcquisitionTaskStatus,
29
+ SectionQuality,
30
+ )
31
+ from temdb.models.error import APIErrorResponse
32
+ from temdb.models.quality_control import (
33
+ AcquisitionFocusScoresResponse,
34
+ BadFocusTileInfo,
35
+ BadFocusTilesResponse,
36
+ TileFocusScore,
37
+ )
38
+ from temdb.models.roi import (
39
+ ROIBase,
40
+ ROIChildrenResponse,
41
+ ROICreate,
42
+ ROIResponse,
43
+ ROIUpdate,
44
+ )
45
+ from temdb.models.section import (
46
+ SectionBase,
47
+ SectionCreate,
48
+ SectioningRunParameters,
49
+ SectionMetrics,
50
+ SectionResponse,
51
+ SectionUpdate,
52
+ )
53
+ from temdb.models.specimen import (
54
+ SpecimenBase,
55
+ SpecimenCreate,
56
+ SpecimenResponse,
57
+ SpecimenUpdate,
58
+ )
59
+ from temdb.models.substrate import (
60
+ Aperture,
61
+ ReferencePoints,
62
+ SubstrateBase,
63
+ SubstrateCreate,
64
+ SubstrateMetadata,
65
+ SubstrateResponse,
66
+ SubstrateUpdate,
67
+ )
68
+ from temdb.models.task import (
69
+ AcquisitionTaskBase,
70
+ AcquisitionTaskCreate,
71
+ AcquisitionTaskResponse,
72
+ AcquisitionTaskUpdate,
73
+ )
74
+ from temdb.models.tile import (
75
+ Matcher,
76
+ TileBase,
77
+ TileCreate,
78
+ TileResponse,
79
+ TileUpdate,
80
+ )
81
+
82
+ __all__ = [
83
+ # Enums
84
+ "AcquisitionStatus",
85
+ "AcquisitionTaskStatus",
86
+ "SectionQuality",
87
+ # Tile
88
+ "Matcher",
89
+ "TileBase",
90
+ "TileCreate",
91
+ "TileUpdate",
92
+ "TileResponse",
93
+ # Acquisition
94
+ "AcquisitionBase",
95
+ "AcquisitionCreate",
96
+ "AcquisitionUpdate",
97
+ "AcquisitionResponse",
98
+ "AcquisitionFullMetadata",
99
+ "HardwareParams",
100
+ "AcquisitionParams",
101
+ "Calibration",
102
+ "LensCorrectionModel",
103
+ "StorageLocation",
104
+ "StorageLocationCreate",
105
+ # Specimen
106
+ "SpecimenBase",
107
+ "SpecimenCreate",
108
+ "SpecimenUpdate",
109
+ "SpecimenResponse",
110
+ # Block
111
+ "BlockBase",
112
+ "BlockCreate",
113
+ "BlockUpdate",
114
+ "BlockResponse",
115
+ # ROI
116
+ "ROIBase",
117
+ "ROICreate",
118
+ "ROIUpdate",
119
+ "ROIResponse",
120
+ "ROIChildrenResponse",
121
+ # Section
122
+ "SectionBase",
123
+ "SectionCreate",
124
+ "SectionUpdate",
125
+ "SectionResponse",
126
+ "SectionMetrics",
127
+ "SectioningRunParameters",
128
+ # Substrate
129
+ "SubstrateBase",
130
+ "SubstrateCreate",
131
+ "SubstrateUpdate",
132
+ "SubstrateResponse",
133
+ "ReferencePoints",
134
+ "Aperture",
135
+ "SubstrateMetadata",
136
+ # Task
137
+ "AcquisitionTaskBase",
138
+ "AcquisitionTaskCreate",
139
+ "AcquisitionTaskUpdate",
140
+ "AcquisitionTaskResponse",
141
+ # CuttingSession
142
+ "CuttingSessionBase",
143
+ "CuttingSessionCreate",
144
+ "CuttingSessionUpdate",
145
+ "CuttingSessionResponse",
146
+ # Error
147
+ "APIErrorResponse",
148
+ # Quality Control
149
+ "TileFocusScore",
150
+ "AcquisitionFocusScoresResponse",
151
+ "BadFocusTileInfo",
152
+ "BadFocusTilesResponse",
153
+ ]
@@ -0,0 +1,134 @@
1
+ from datetime import datetime
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field
5
+ from temdb.models.enums import AcquisitionStatus
6
+
7
+
8
+ class LensCorrectionModel(BaseModel):
9
+ model_config = ConfigDict(extra="allow")
10
+
11
+ id: int = Field(..., description="ID of lens correction model")
12
+ type: str = Field(
13
+ ...,
14
+ description="Transform type as defined in Render Transform Spec",
15
+ )
16
+ class_name: str = Field(
17
+ ...,
18
+ description="Class name of lens correction model from mpicbg-compatible className",
19
+ )
20
+ data_string: str = Field(
21
+ ...,
22
+ description="Data string of lens correction model from mpicbg-compatible dataString",
23
+ )
24
+
25
+
26
+ class Calibration(BaseModel):
27
+ model_config = ConfigDict(extra="allow")
28
+
29
+ pixel_size: float = Field(..., description="Pixel size in nm")
30
+ rotation_angle: float = Field(..., description="Rotation angle in degrees")
31
+ lens_model: LensCorrectionModel | None = Field(None, description="Lens correction model")
32
+ aperture_centroid: list[float] | None = Field(None, description="Aperture centroid in stage coordinates in nm")
33
+
34
+
35
+ class HardwareParams(BaseModel):
36
+ model_config = ConfigDict(extra="allow")
37
+
38
+ scope_id: str = Field(..., description="ID of microscope")
39
+ camera_model: str = Field(..., description="Model of camera")
40
+ camera_serial: str = Field(..., description="Serial number of camera")
41
+ camera_bit_depth: int = Field(..., description="Native bit depth of camera")
42
+ media_type: str = Field(..., description="Type of substrate in microscope")
43
+
44
+
45
+ class AcquisitionParams(BaseModel):
46
+ model_config = ConfigDict(extra="allow")
47
+
48
+ magnification: int = Field(..., description="Magnification of acquisition")
49
+ spot_size: int = Field(..., description="Spot size of acquisition")
50
+ exposure_time: int = Field(..., description="Exposure time of camera in ms")
51
+ tile_size: list[int] = Field(..., description="Shape of the image tile in pixels")
52
+ tile_overlap: float = Field(..., description="Pixel overlap to neighboring tiles")
53
+ saved_bit_depth: int = Field(..., description="Bit depth of saved image")
54
+
55
+
56
+ class StorageLocation(BaseModel):
57
+ model_config = ConfigDict(extra="allow")
58
+
59
+ location_type: str = Field(..., description="Type of storage location, e.g. local, s3, etc.")
60
+ base_path: str = Field(..., description="Base path of storage location")
61
+ is_current: bool = Field(..., description="Whether this is the current storage location")
62
+ date_added: datetime = Field(..., description="Date storage location was added")
63
+ metadata: dict[str, Any] = Field(..., description="Metadata of storage location")
64
+
65
+
66
+ class StorageLocationCreate(BaseModel):
67
+ model_config = ConfigDict(extra="allow")
68
+
69
+ location_type: str = Field(..., description="Type of storage location, e.g. local, s3, etc.")
70
+ base_path: str = Field(..., description="Base path of storage location")
71
+ metadata: dict[str, Any] = Field(default_factory=dict, description="Metadata of storage location")
72
+
73
+
74
+ class AcquisitionBase(BaseModel):
75
+ model_config = ConfigDict(extra="allow")
76
+
77
+ hardware_settings: HardwareParams | None = None
78
+ acquisition_settings: AcquisitionParams | None = None
79
+ calibration_info: Calibration | None = None
80
+ status: AcquisitionStatus | None = None
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")
84
+ storage_locations: list[StorageLocation] | None = Field(None, description="Storage locations of acquisition")
85
+ montage_set_name: str | None = Field(None, description="Name of montage set")
86
+ sub_region: dict[str, int] | None = Field(None, description="Sub region of acquisition")
87
+ replaces_acquisition_id: int | None = Field(None, description="ID of acquisition this acquisition replaces")
88
+
89
+
90
+ class AcquisitionCreate(AcquisitionBase):
91
+ acquisition_id: str = Field(..., description="Unique acquisition identifier")
92
+ montage_id: str = Field(..., description="Montage identifier")
93
+ roi_id: str = Field(..., description="ROI identifier")
94
+ acquisition_task_id: str = Field(..., description="Parent task identifier")
95
+ hardware_settings: HardwareParams = Field(..., description="Hardware settings of acquisition")
96
+ 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
+ start_time: datetime | None = Field(None, description="Start time of acquisition (defaults to now if not provided)")
101
+
102
+
103
+ class AcquisitionUpdate(AcquisitionBase):
104
+ calibration_info: dict[str, Any] | None = Field(None, description="Calibration information of acquisition")
105
+
106
+
107
+ class AcquisitionResponse(AcquisitionBase):
108
+ acquisition_id: str = Field(..., description="Unique acquisition identifier")
109
+ montage_id: str = Field(..., description="Montage identifier")
110
+ specimen_id: str = Field(..., description="Specimen identifier")
111
+ roi_id: str = Field(..., description="ROI identifier")
112
+ acquisition_task_id: str = Field(..., description="Parent task identifier")
113
+ hardware_settings: HardwareParams = Field(..., description="Hardware settings of acquisition")
114
+ acquisition_settings: AcquisitionParams = Field(..., description="Acquisition settings of acquisition")
115
+ status: AcquisitionStatus = Field(..., description="Status of acquisition")
116
+ start_time: datetime = Field(..., description="Start time of acquisition")
117
+
118
+ created_at: datetime | None = None
119
+ updated_at: datetime | None = None
120
+
121
+
122
+ class AcquisitionFullMetadata(BaseModel):
123
+ """Acquisition with complete hierarchy metadata."""
124
+
125
+ model_config = ConfigDict(extra="allow")
126
+
127
+ acquisition: AcquisitionResponse
128
+ task: dict[str, Any] | None = None
129
+ roi: dict[str, Any] | None = None
130
+ section: dict[str, Any] | None = None
131
+ cutting_session: dict[str, Any] | None = None
132
+ block: dict[str, Any] | None = None
133
+ specimen: dict[str, Any] | None = None
134
+ substrate: dict[str, Any] | None = None
temdb/models/block.py ADDED
@@ -0,0 +1,27 @@
1
+ from datetime import datetime
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field
5
+
6
+
7
+ class BlockBase(BaseModel):
8
+ model_config = ConfigDict(extra="allow")
9
+
10
+ microCT_info: dict[str, Any] | None = Field(None, description="MicroCT information of block")
11
+
12
+
13
+ class BlockCreate(BlockBase):
14
+ block_id: str = Field(..., description="Unique block identifier")
15
+ specimen_id: str = Field(..., description="Parent specimen ID")
16
+
17
+
18
+ class BlockUpdate(BlockBase):
19
+ pass
20
+
21
+
22
+ class BlockResponse(BlockBase):
23
+ block_id: str = Field(..., description="Unique block identifier")
24
+ specimen_id: str = Field(..., description="Parent specimen ID")
25
+
26
+ created_at: datetime | None = None
27
+ updated_at: datetime | None = None
@@ -0,0 +1,47 @@
1
+ from datetime import datetime
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+
6
+ class CuttingSessionBase(BaseModel):
7
+ """Base cutting session fields."""
8
+
9
+ model_config = ConfigDict(extra="allow")
10
+
11
+ start_time: datetime | None = Field(None, description="Time when cutting session started")
12
+ end_time: datetime | None = Field(None, description="Time when cutting session ended")
13
+ operator: str | None = Field(None, description="Operator of cutting session")
14
+ sectioning_device: str | None = Field(None, description="Microtome/Device used for sectioning")
15
+ media_type: str | None = Field(None, description="Type of substrate the sections are placed upon")
16
+ knife_id: str | None = Field(None, description="Identifier for the knife used")
17
+
18
+
19
+ class CuttingSessionCreate(CuttingSessionBase):
20
+ """Schema for creating a cutting session."""
21
+
22
+ cutting_session_id: str = Field(..., description="Unique cutting session identifier")
23
+ block_id: str = Field(..., description="ID of block cutting session is associated with")
24
+ start_time: datetime = Field(..., description="Time when cutting session started")
25
+ sectioning_device: str = Field(..., description="Device used for sectioning")
26
+ media_type: str = Field(..., description="Type of substrate the sections are placed upon")
27
+
28
+
29
+ class CuttingSessionUpdate(CuttingSessionBase):
30
+ """Schema for updating a cutting session."""
31
+
32
+ block_id: str | None = Field(None, description="ID of block")
33
+ specimen_id: str | None = Field(None, description="ID of specimen")
34
+
35
+
36
+ class CuttingSessionResponse(CuttingSessionBase):
37
+ """Schema for cutting session API responses."""
38
+
39
+ cutting_session_id: str = Field(..., description="Unique cutting session identifier")
40
+ specimen_id: str = Field(..., description="ID of specimen")
41
+ block_id: str = Field(..., description="ID of block")
42
+ start_time: datetime = Field(..., description="Time when cutting session started")
43
+ sectioning_device: str = Field(..., description="Device used for sectioning")
44
+ media_type: str = Field(..., description="Type of substrate the sections are placed upon")
45
+
46
+ created_at: datetime | None = None
47
+ updated_at: datetime | None = None
temdb/models/enums.py ADDED
@@ -0,0 +1,27 @@
1
+ from enum import Enum
2
+
3
+
4
+ class SectionQuality(str, Enum):
5
+ GOOD = "good"
6
+ BROKEN = "broken"
7
+ THIN = "thin"
8
+ THICK = "thick"
9
+ EMPTY = "empty"
10
+
11
+
12
+ class AcquisitionTaskStatus(str, Enum):
13
+ PLANNED = "Planned"
14
+ IN_PROGRESS = "In Progress"
15
+ COMPLETED = "Completed"
16
+ FAILED = "Failed"
17
+ ABORTED = "Aborted"
18
+
19
+
20
+ class AcquisitionStatus(str, Enum):
21
+ IMAGING = "imaging"
22
+ ACQUIRED = "acquired"
23
+ ABORTED = "aborted"
24
+ QC_FAILED = "failed"
25
+ QC_PASSED = "qc-passed"
26
+ QC_PENDING = "qc-pending"
27
+ TO_BE_REIMAGED = "to be re-imaged"
temdb/models/error.py ADDED
@@ -0,0 +1,16 @@
1
+ """API error response models."""
2
+
3
+ from typing import Any
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class APIErrorResponse(BaseModel):
9
+ """Standard structure for API error responses."""
10
+
11
+ detail: str = Field(..., description="Human-readable description of the error.")
12
+ error_code: str | None = Field(None, description="Optional machine-readable error code.")
13
+ context: dict[str, Any] | None = Field(
14
+ None,
15
+ description="Optional additional context about the error.",
16
+ )
@@ -0,0 +1,43 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+
4
+ class TileFocusScore(BaseModel):
5
+ """Represents focus score data for a single tile."""
6
+
7
+ tile_id: str = Field(..., description="Unique ID of the tile.")
8
+ raster_index: int = Field(..., description="Sequential index of the tile within the acquisition raster.")
9
+ focus_score: float = Field(..., description="Calculated focus score for the tile.")
10
+
11
+
12
+ class AcquisitionFocusScoresResponse(BaseModel):
13
+ """Response model containing focus scores for an acquisition."""
14
+
15
+ acquisition_id: str = Field(..., description="ID of the acquisition analyzed.")
16
+ tile_count: int = Field(..., description="Total number of tiles found for this acquisition.")
17
+ focus_scores: list[TileFocusScore] = Field(..., description="List of focus scores for each tile.")
18
+ mean_focus: float | None = Field(None, description="Mean focus score across all tiles.")
19
+ median_focus: float | None = Field(None, description="Median focus score across all tiles.")
20
+ stddev_focus: float | None = Field(None, description="Standard deviation of focus scores across all tiles.")
21
+ min_focus: float | None = Field(None, description="Minimum focus score found.")
22
+ max_focus: float | None = Field(None, description="Maximum focus score found.")
23
+
24
+
25
+ class BadFocusTileInfo(BaseModel):
26
+ """Information about a tile with bad focus."""
27
+
28
+ tile_id: str
29
+ acquisition_id: str
30
+ raster_index: int
31
+ focus_score: float
32
+ image_path: str
33
+ stage_position: dict[str, float]
34
+ roi_id: str | None = None
35
+ specimen_id: str | None = None
36
+
37
+
38
+ class BadFocusTilesResponse(BaseModel):
39
+ """Response model for bad focus tiles query."""
40
+
41
+ threshold: float
42
+ count: int
43
+ tiles: list[BadFocusTileInfo]
temdb/models/roi.py ADDED
@@ -0,0 +1,88 @@
1
+ from datetime import datetime
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
5
+
6
+
7
+ class ROIBase(BaseModel):
8
+ model_config = ConfigDict(extra="allow")
9
+
10
+ aperture_width_height: list[float] | None = Field(
11
+ None,
12
+ description="Width and height of aperture in mm calculated from aperture_image",
13
+ )
14
+ aperture_centroid: list[float] | None = Field(
15
+ None,
16
+ description="Center of aperture measured in pixel coordinates from aperture_image",
17
+ )
18
+ aperture_bounding_box: list[float] | None = Field(
19
+ None,
20
+ description="Bounding box of aperture measured in pixel coordinates",
21
+ )
22
+ aperture_image: str | None = Field(None, description="URL of aperture image")
23
+ optical_pixel_size: float | None = Field(None, description="Optical resolution in nm per pixel")
24
+ scale_y: float | None = Field(None, description="Scaling factor for y-axis")
25
+ barcode: int | str | None = Field(None, description="Barcode of ROI if present")
26
+ rois: list[Any] | None = Field(None, description="List of ROIs")
27
+ bucket: str | None = Field(None, description="Bucket of ROI")
28
+ roi_mask: str | None = Field(None, description="URL of ROI mask")
29
+ roi_mask_bucket: str | None = Field(None, description="Bucket of ROI mask")
30
+ corners: dict[str, Any] | None = Field(None, description="Corners of ROI")
31
+ corners_perpendicular: dict[str, Any] | None = Field(None, description="Perpendicular corners of ROI")
32
+ rule: str | None = Field(None, description="Rule for ROI corner detection")
33
+ edits: list[Any] | None = Field(None, description="List of edits to ROI")
34
+ auto_roi: bool | None = Field(None, description="Flag if auto generated ROI was used")
35
+ roi_parameters: dict[str, Any] | None = Field(None, description="Parameters of ROI")
36
+ vertices: list[Any] | None = Field(None, description="Vertices of the ROI polygon")
37
+
38
+
39
+ class ROICreate(ROIBase):
40
+ roi_number: int = Field(..., description="Sequential number for this ROI within its parent context")
41
+ section_id: str = Field(..., description="ID of section")
42
+ specimen_id: str = Field(..., description="ID of specimen")
43
+ block_id: str = Field(..., description="ID of block")
44
+ substrate_media_id: str = Field(..., description="Media ID of the substrate this section is placed on")
45
+ section_number: int | None = Field(None, description="Number of section from collection")
46
+ parent_roi_id: str | None = Field(
47
+ None,
48
+ description="Hierarchical ID of parent ROI (e.g., 'SPEC001.BLK001.SEC001.SUB001.ROI001')",
49
+ )
50
+
51
+ @field_validator("parent_roi_id", mode="after")
52
+ @classmethod
53
+ def validate_parent_roi_id(cls, v: str | None) -> str | None:
54
+ if v is not None:
55
+ if ".ROI" not in v:
56
+ raise ValueError(
57
+ "parent_roi_id must contain .ROI and follow format: SPEC###.BLK###.SEC###.SUB###.ROI###[.ROI###...]"
58
+ )
59
+ parts = v.split(".")
60
+ if len(parts) < 5:
61
+ raise ValueError("parent_roi_id must follow format: SPEC###.BLK###.SEC###.SUB###.ROI###[.ROI###...]")
62
+ if not parts[-1].startswith("ROI"):
63
+ raise ValueError("parent_roi_id must end with a ROI segment (e.g., ROI001)")
64
+ return v
65
+
66
+
67
+ class ROIUpdate(ROIBase):
68
+ pass
69
+
70
+
71
+ class ROIResponse(ROIBase):
72
+ roi_id: str = Field(..., description="Hierarchical ID of ROI")
73
+ roi_number: int = Field(..., description="Sequential number for this ROI within its parent context")
74
+ section_id: str = Field(..., description="ID of section")
75
+ specimen_id: str = Field(..., description="ID of specimen")
76
+ block_id: str = Field(..., description="ID of block")
77
+ substrate_media_id: str = Field(..., description="Media ID of the substrate")
78
+ hierarchy_level: int = Field(..., description="Depth level in ROI hierarchy (1=top-level, 2=child, etc.)")
79
+ is_parent: bool = Field(default=False, description="Whether this ROI has child ROIs")
80
+
81
+ created_at: datetime | None = None
82
+ updated_at: datetime | None = None
83
+
84
+
85
+ class ROIChildrenResponse(BaseModel):
86
+ """Response model for ROI children query."""
87
+
88
+ children: list[ROIResponse]
@@ -0,0 +1,98 @@
1
+ from datetime import datetime
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field
5
+ from temdb.models.enums import SectionQuality
6
+
7
+
8
+ class SectioningRunParameters(BaseModel):
9
+ """Parameters from a sectioning run."""
10
+
11
+ model_config = ConfigDict(extra="allow")
12
+
13
+ cutting_speed_mms: float | None = Field(None, description="Cutting speed in mm/s")
14
+ retract_speed_mms: float | None = Field(None, description="Retract speed in mm/s")
15
+ water_level_mm: float | None = Field(None, description="Water level in boat in mm")
16
+ wafer_set_level: float | None = Field(None, description="Wafer set level value")
17
+ tape_speed: float | None = Field(None, description="Main tape speed value")
18
+ new_tape_speed: float | None = Field(None, description="Temporary tape speed during timePhi")
19
+ tape_cycle: float | None = Field(None, description="Tape cycle duration/value")
20
+ cut_cycle: float | None = Field(None, description="Cut cycle duration/value")
21
+ phiset: float | None = Field(None, description="Phi set value")
22
+ phi_offset: float | None = Field(None, description="Actual phi value during picking")
23
+ time_phi: float | None = Field(None, description="Time associated with phi movement")
24
+ water_added: bool | None = Field(None, description="Flag indicating if water was added during this cycle")
25
+ other_metadata: dict[str, Any] = Field(
26
+ default_factory=dict,
27
+ description="Dictionary for any other arbitrary run parameters",
28
+ )
29
+
30
+
31
+ class SectionMetrics(BaseModel):
32
+ """Metrics and parameters of a section."""
33
+
34
+ model_config = ConfigDict(extra="allow")
35
+
36
+ quality: SectionQuality | None = Field(None, description="Qualitative state of the section (e.g., Good, Broken)")
37
+ thickness_um: float | None = Field(None, description="Measured section thickness in micrometers")
38
+ knife_quality: str | None = Field(None, description="Assessment of the knife condition at the time of cutting")
39
+ tissue_confidence_score: float | None = Field(
40
+ None, description="Confidence score for tissue detection on substrate"
41
+ )
42
+ run_parameters: SectioningRunParameters | None = Field(
43
+ None, description="Detailed parameters from the sectioning run"
44
+ )
45
+
46
+
47
+ class SectionBase(BaseModel):
48
+ """Base section fields."""
49
+
50
+ model_config = ConfigDict(extra="allow")
51
+
52
+ section_number: int | None = Field(None, gt=0, description="Sequential section number within the cutting session")
53
+ timestamp: datetime | None = Field(None, description="Timestamp of section creation/cutting")
54
+ optical_image: dict[str, Any] | None = Field(
55
+ None,
56
+ description="Metadata about optical image collected before imaging",
57
+ )
58
+ aperture_uid: str | None = Field(
59
+ None,
60
+ description="UID of the specific aperture holding this section",
61
+ )
62
+ aperture_index: int | None = Field(
63
+ None,
64
+ description="Index of the specific aperture holding this section",
65
+ )
66
+ barcode: str | None = Field(None, description="Barcode scanned for this section, if any")
67
+ section_metrics: SectionMetrics | None = Field(None, description="Metrics and parameters of the section")
68
+
69
+
70
+ class SectionCreate(SectionBase):
71
+ """Schema for creating a section."""
72
+
73
+ cutting_session_id: str = Field(..., description="ID of the cutting session this section belongs to")
74
+ media_id: str = Field(
75
+ ...,
76
+ description="ID of the substrate (wafer, tape, etc.) this section is placed on",
77
+ )
78
+ section_number: int = Field(..., gt=0, description="Sequential section number")
79
+
80
+
81
+ class SectionUpdate(SectionBase):
82
+ """Schema for updating a section."""
83
+
84
+ pass
85
+
86
+
87
+ class SectionResponse(SectionBase):
88
+ """Schema for section API responses."""
89
+
90
+ section_id: str = Field(..., description="Unique, system-generated ID for the section")
91
+ section_number: int = Field(..., gt=0, description="Sequential section number within the cutting session")
92
+ cutting_session_id: str = Field(..., description="ID of the cutting session")
93
+ block_id: str = Field(..., description="ID of the block")
94
+ specimen_id: str = Field(..., description="ID of the specimen")
95
+ media_id: str = Field(..., description="ID of the substrate")
96
+
97
+ created_at: datetime | None = None
98
+ updated_at: datetime | None = None
@@ -0,0 +1,30 @@
1
+ from datetime import datetime
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field
5
+
6
+
7
+ class SpecimenBase(BaseModel):
8
+ model_config = ConfigDict(extra="allow")
9
+
10
+ description: str | None = Field(None, description="Description of specimen, used for additional notes.")
11
+ specimen_images: set[str] | None = Field(None, description="Images of specimen")
12
+ functional_imaging_metadata: dict[str, Any] | None = Field(
13
+ None,
14
+ description="Functional imaging metadata of specimen, optional links to other datasets",
15
+ )
16
+
17
+
18
+ class SpecimenCreate(SpecimenBase):
19
+ specimen_id: str = Field(..., description="Unique specimen identifier")
20
+
21
+
22
+ class SpecimenUpdate(SpecimenBase):
23
+ pass
24
+
25
+
26
+ class SpecimenResponse(SpecimenBase):
27
+ specimen_id: str = Field(..., description="Unique specimen identifier")
28
+
29
+ created_at: datetime | None = None
30
+ updated_at: datetime | None = None
@@ -0,0 +1,115 @@
1
+ from datetime import datetime
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field
5
+
6
+
7
+ class ReferencePoints(BaseModel):
8
+ """Reference points for substrate calibration."""
9
+
10
+ model_config = ConfigDict(extra="allow")
11
+
12
+ origin: tuple[float, float, float] | None = Field(None, description="Origin point (x, y, z)")
13
+ end: tuple[float, float, float] | None = Field(None, description="End point (x, y, z)")
14
+ ref: tuple[float, float, float] | None = Field(None, description="Reference point (x, y, z)")
15
+
16
+
17
+ class Aperture(BaseModel):
18
+ """Represents a single aperture or slot on a substrate."""
19
+
20
+ model_config = ConfigDict(extra="allow")
21
+
22
+ uid: str = Field(
23
+ ...,
24
+ description="Unique identifier for this aperture within the substrate",
25
+ )
26
+ index: int = Field(..., description="Sequential index of the aperture")
27
+ centroid: tuple[float, float, float] | None = Field(
28
+ None, description="Calculated centroid of the aperture (X, Y, Z)"
29
+ )
30
+ shape: str | None = Field(
31
+ None,
32
+ description="Raw description of the aperture shape",
33
+ )
34
+ shape_type: str | None = None
35
+ shape_params: dict[str, Any] | None = None
36
+ status: str | None = Field(None, description="Status of the aperture (e.g., available, used, damaged)")
37
+ tracking_uid: str | None = Field(
38
+ None,
39
+ alias="tuid",
40
+ description="Tracking UID from source if available",
41
+ )
42
+
43
+
44
+ class SubstrateMetadata(BaseModel):
45
+ """General metadata about a substrate."""
46
+
47
+ model_config = ConfigDict(extra="allow")
48
+
49
+ name: str | None = Field(None, description="User-defined name for the substrate")
50
+ user: str | None = Field(None, description="User associated with substrate creation/calibration")
51
+ created: datetime | None = Field(None, description="Timestamp from source metadata 'created'")
52
+ calibrated: datetime | None = Field(None, description="Timestamp from source metadata 'calibrated'")
53
+ extra: dict[str, Any] = Field(
54
+ default_factory=dict,
55
+ description="Dictionary for any other key-value metadata items",
56
+ )
57
+
58
+
59
+ class SubstrateBase(BaseModel):
60
+ """Base substrate fields."""
61
+
62
+ model_config = ConfigDict(extra="allow")
63
+
64
+ uid: str | None = Field(
65
+ None,
66
+ description="Overall unique identifier for the substrate instance",
67
+ )
68
+ status: str | None = Field(
69
+ None,
70
+ description="Status of the entire substrate (e.g., new, in_use, full, retired)",
71
+ )
72
+ refpoint: ReferencePoints | None = Field(None, description="Reference points in local substrate coordinates")
73
+ refpoint_world: ReferencePoints | None = Field(
74
+ None, description="Reference points mapped to world/stage coordinates"
75
+ )
76
+ source_path: str | None = Field(
77
+ None,
78
+ description="Path or identifier of the source file defining this substrate",
79
+ )
80
+ metadata: SubstrateMetadata | None = Field(None, description="General metadata about the substrate")
81
+ apertures: list[Aperture] | None = Field(
82
+ None,
83
+ description="List of apertures or slots defined on this substrate",
84
+ )
85
+
86
+
87
+ class SubstrateCreate(SubstrateBase):
88
+ """Schema for creating a substrate."""
89
+
90
+ media_id: str = Field(
91
+ ...,
92
+ description="Primary unique identifier for this substrate (e.g., wafer ID, tape reel ID)",
93
+ )
94
+ media_type: str = Field(..., description="Type of substrate (e.g., 'wafer', 'tape', 'stick', 'grid')")
95
+ status: str | None = Field(
96
+ "new",
97
+ description="Status of the entire substrate",
98
+ )
99
+
100
+
101
+ class SubstrateUpdate(SubstrateBase):
102
+ """Schema for updating a substrate."""
103
+
104
+ media_id: str | None = Field(None, description="Primary unique identifier")
105
+ media_type: str | None = Field(None, description="Type of substrate")
106
+
107
+
108
+ class SubstrateResponse(SubstrateBase):
109
+ """Schema for substrate API responses."""
110
+
111
+ media_id: str = Field(..., description="Primary unique identifier")
112
+ media_type: str = Field(..., description="Type of substrate")
113
+
114
+ created_at: datetime | None = None
115
+ updated_at: datetime | None = None
temdb/models/task.py ADDED
@@ -0,0 +1,55 @@
1
+ from datetime import datetime
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field
5
+ from temdb.models.enums import AcquisitionTaskStatus
6
+
7
+
8
+ class AcquisitionTaskBase(BaseModel):
9
+ """Base acquisition task fields."""
10
+
11
+ model_config = ConfigDict(extra="allow")
12
+
13
+ task_type: str | None = Field(None, description="Type of acquisition task")
14
+ version: int | None = Field(None, description="Version number of this task")
15
+ status: AcquisitionTaskStatus | None = Field(None, description="Status of acquisition task")
16
+ error_message: str | None = Field(None, description="Error message if failed")
17
+ started_at: datetime | None = Field(None, description="When task execution began")
18
+ completed_at: datetime | None = Field(None, description="When task finished (success or failure)")
19
+ tags: list[str] | None = Field(None, description="Tags for filtering")
20
+ metadata: dict[str, Any] | None = Field(None, description="Additional metadata")
21
+
22
+
23
+ class AcquisitionTaskCreate(AcquisitionTaskBase):
24
+ """Schema for creating an acquisition task."""
25
+
26
+ task_id: str = Field(..., description="Unique identifier for this task")
27
+ specimen_id: str = Field(..., description="ID of specimen")
28
+ block_id: str = Field(..., description="ID of block")
29
+ roi_id: str = Field(..., description="ID of region of interest to be acquired")
30
+ task_type: str = Field(default="standard_acquisition", description="Type of acquisition task")
31
+ version: int = Field(default=1, description="Version number of this task")
32
+ status: AcquisitionTaskStatus = Field(default=AcquisitionTaskStatus.PLANNED)
33
+ tags: list[str] = Field(default_factory=list, description="Tags for filtering")
34
+ metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
35
+
36
+
37
+ class AcquisitionTaskUpdate(AcquisitionTaskBase):
38
+ """Schema for updating an acquisition task."""
39
+
40
+ pass
41
+
42
+
43
+ class AcquisitionTaskResponse(AcquisitionTaskBase):
44
+ """Schema for acquisition task API responses."""
45
+
46
+ task_id: str = Field(..., description="Unique identifier for this task")
47
+ specimen_id: str = Field(..., description="ID of specimen")
48
+ block_id: str = Field(..., description="ID of block")
49
+ roi_id: str = Field(..., description="ID of region of interest")
50
+ task_type: str = Field(..., description="Type of acquisition task")
51
+ version: int = Field(..., description="Version number of this task")
52
+ status: AcquisitionTaskStatus = Field(..., description="Status of acquisition task")
53
+
54
+ created_at: datetime | None = None
55
+ updated_at: datetime | None = None
temdb/models/tile.py ADDED
@@ -0,0 +1,77 @@
1
+ from datetime import datetime
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+
6
+ class Matcher(BaseModel):
7
+ model_config = ConfigDict(extra="allow")
8
+
9
+ row: int = Field(..., description="Row index of the tile")
10
+ col: int = Field(..., description="Column index of the tile")
11
+ dX: float = Field(..., description="X offset of the tile")
12
+ dY: float = Field(..., description="Y offset of the tile")
13
+ dXsd: float = Field(..., description="X offset standard deviation of the tile")
14
+ dYsd: float = Field(..., description="Y offset standard deviation of the tile")
15
+ distance: float = Field(..., description="Distance between the tiles")
16
+ rotation: float = Field(..., description="Rotation of the tile")
17
+ match_quality: float = Field(..., description="Quality of the match")
18
+ position: int = Field(..., description="Position of the match")
19
+ pX: list[float] = Field(..., description="X position of the points in the template tile")
20
+ pY: list[float] = Field(..., description="Y position of the points in the template tile")
21
+ qX: list[float] = Field(..., description="X position of the points in the reference tile")
22
+ qY: list[float] = Field(..., description="Y position of the points in the reference tile")
23
+
24
+
25
+ class TileBase(BaseModel):
26
+ model_config = ConfigDict(extra="allow")
27
+
28
+ stage_position: dict[str, float] | None = Field(
29
+ None, description="Stage position of the tile in stage coordinates in nm"
30
+ )
31
+ raster_position: dict[str, int] | None = Field(None, description="Row, column raster position of the tile")
32
+ focus_score: float | None = Field(None, description="Focus score of the tile")
33
+ min_value: float | None = Field(None, description="Minimum pixel value of the tile")
34
+ max_value: float | None = Field(None, description="Maximum pixel value of the tile")
35
+ mean_value: float | None = Field(None, description="Mean pixel value of the tile")
36
+ std_value: float | None = Field(None, description="Standard deviation of pixel values of the tile")
37
+ image_path: str | None = Field(None, description="URL to the image of the tile")
38
+ matcher: list[Matcher] | None = Field(None, description="List of matchers for the tile")
39
+ supertile_id: str | None = Field(None, description="ID of the supertile the tile belongs to")
40
+ supertile_raster_position: dict[str, int] | None = Field(
41
+ None, description="Row, column raster position of the supertile"
42
+ )
43
+
44
+
45
+ class TileCreate(TileBase):
46
+ tile_id: str = Field(..., description="Unique tile identifier")
47
+ raster_index: int = Field(..., description="Index of the tile in the raster")
48
+ stage_position: dict[str, float] = Field(..., description="Stage position of the tile in stage coordinates in nm")
49
+ raster_position: dict[str, int] = Field(..., description="Row, column raster position of the tile")
50
+ focus_score: float = Field(..., description="Focus score of the tile")
51
+ min_value: float = Field(..., description="Minimum pixel value of the tile")
52
+ max_value: float = Field(..., description="Maximum pixel value of the tile")
53
+ mean_value: float = Field(..., description="Mean pixel value of the tile")
54
+ std_value: float = Field(..., description="Standard deviation of pixel values of the tile")
55
+ image_path: str = Field(..., description="URL to the image of the tile")
56
+
57
+
58
+ class TileUpdate(TileBase):
59
+ pass
60
+
61
+
62
+ class TileResponse(TileBase):
63
+ tile_id: str = Field(..., description="Unique tile identifier")
64
+ acquisition_id: str = Field(..., description="Parent acquisition ID")
65
+ raster_index: int = Field(..., description="Index of the tile in the raster")
66
+ stage_position: dict[str, float] = Field(..., description="Stage position of the tile in stage coordinates in nm")
67
+ raster_position: dict[str, int] = Field(..., description="Row, column raster position of the tile")
68
+ focus_score: float = Field(..., description="Focus score of the tile")
69
+ min_value: float = Field(..., description="Minimum pixel value of the tile")
70
+ max_value: float = Field(..., description="Maximum pixel value of the tile")
71
+ mean_value: float = Field(..., description="Mean pixel value of the tile")
72
+ std_value: float = Field(..., description="Standard deviation of pixel values of the tile")
73
+ image_path: str = Field(..., description="URL to the image of the tile")
74
+
75
+ created_at: datetime | None = None
76
+ updated_at: datetime | None = None
77
+ version: int | None = Field(None, description="Document version number")
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.4
2
+ Name: temdb-models
3
+ Version: 2.0.0
4
+ Summary: Shared Pydantic models for TEMdb
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: pydantic>=2.0
@@ -0,0 +1,16 @@
1
+ temdb/models/__init__.py,sha256=KufoBfsDXhgFxFa6bSYaBjmrrCsPd7cD-8azmdoUU2w,3227
2
+ temdb/models/acquisition.py,sha256=EpX1xCYAZQELJxK4g7cPRVNi9AWGYxjW9iCf_5963As,6481
3
+ temdb/models/block.py,sha256=nszVnDk7TfLMaUw8MYpFqG7qykDBZjKXMtrMK8p_I2o,740
4
+ temdb/models/cutting_session.py,sha256=qmPhp1yoWhrl3XPAs7snmdzaif6RAbGXKkkDB45kHAI,2206
5
+ temdb/models/enums.py,sha256=soRqd8Oo_900rSNFDFe9mdITJ5kZ-hseOI_tQ2mvYxg,561
6
+ temdb/models/error.py,sha256=Stgs6BHD7Kv6I5or8zI3xIHbr-ElBQtCEyuOrAU66EY,499
7
+ temdb/models/quality_control.py,sha256=lVnexB1WZOlZtVJnc9nrz9RlwJWXJe6TtXkgEqrE1kA,1736
8
+ temdb/models/roi.py,sha256=SZCCGpTbsnPsvj3ToUOjEFIcsudoe0MJ4AphoQ-jyiU,4212
9
+ temdb/models/section.py,sha256=PEINVqDWYq1fI2klP70_brElYnvauWXc62ciydPSRa4,4304
10
+ temdb/models/specimen.py,sha256=k_mJ-Hum0gVVgd7UnLkyr1g_3GTcoI8x5kWSDbX1VFk,914
11
+ temdb/models/substrate.py,sha256=zC2LEAcAQCY4hBTvtRFsMOokRWaQ-Q2zPBBeQ4wpOfQ,4118
12
+ temdb/models/task.py,sha256=h0EDdNE3qjHoBK0eJ5jou_iR4Ibcl11Sd47sIl3FafI,2601
13
+ temdb/models/tile.py,sha256=V_2cWo67qnOmPDHfjA12J1sALtz-OP_EYOC34crzMrk,4494
14
+ temdb_models-2.0.0.dist-info/METADATA,sha256=2o-BIVBqF8BLy43fmN5HB96dx_w0tVLQdYSQG0R3sco,151
15
+ temdb_models-2.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
+ temdb_models-2.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any