temdb-models 2.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-2.0.0/.gitignore +155 -0
- temdb_models-2.0.0/PKG-INFO +6 -0
- temdb_models-2.0.0/pyproject.toml +15 -0
- temdb_models-2.0.0/src/temdb/models/__init__.py +153 -0
- temdb_models-2.0.0/src/temdb/models/acquisition.py +134 -0
- temdb_models-2.0.0/src/temdb/models/block.py +27 -0
- temdb_models-2.0.0/src/temdb/models/cutting_session.py +47 -0
- temdb_models-2.0.0/src/temdb/models/enums.py +27 -0
- temdb_models-2.0.0/src/temdb/models/error.py +16 -0
- temdb_models-2.0.0/src/temdb/models/quality_control.py +43 -0
- temdb_models-2.0.0/src/temdb/models/roi.py +88 -0
- temdb_models-2.0.0/src/temdb/models/section.py +98 -0
- temdb_models-2.0.0/src/temdb/models/specimen.py +30 -0
- temdb_models-2.0.0/src/temdb/models/substrate.py +115 -0
- temdb_models-2.0.0/src/temdb/models/task.py +55 -0
- temdb_models-2.0.0/src/temdb/models/tile.py +77 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# poetry
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
102
|
+
#poetry.lock
|
|
103
|
+
|
|
104
|
+
# pdm
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
106
|
+
#pdm.lock
|
|
107
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
108
|
+
# in version control.
|
|
109
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
110
|
+
.pdm.toml
|
|
111
|
+
.pdm-python
|
|
112
|
+
.pdm-build/
|
|
113
|
+
|
|
114
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
115
|
+
__pypackages__/
|
|
116
|
+
|
|
117
|
+
# Celery stuff
|
|
118
|
+
celerybeat-schedule
|
|
119
|
+
celerybeat.pid
|
|
120
|
+
|
|
121
|
+
# SageMath parsed files
|
|
122
|
+
*.sage.py
|
|
123
|
+
|
|
124
|
+
# Environments
|
|
125
|
+
.env
|
|
126
|
+
.venv
|
|
127
|
+
env/
|
|
128
|
+
venv/
|
|
129
|
+
ENV/
|
|
130
|
+
env.bak/
|
|
131
|
+
venv.bak/
|
|
132
|
+
|
|
133
|
+
# Spyder project settings
|
|
134
|
+
.spyderproject
|
|
135
|
+
.spyproject
|
|
136
|
+
|
|
137
|
+
# Rope project settings
|
|
138
|
+
.ropeproject
|
|
139
|
+
|
|
140
|
+
# mkdocs documentation
|
|
141
|
+
/site
|
|
142
|
+
|
|
143
|
+
# mypy
|
|
144
|
+
.mypy_cache/
|
|
145
|
+
.dmypy.json
|
|
146
|
+
dmypy.json
|
|
147
|
+
|
|
148
|
+
# Pyre type checker
|
|
149
|
+
.pyre/
|
|
150
|
+
|
|
151
|
+
# pytype static type analyzer
|
|
152
|
+
.pytype/
|
|
153
|
+
|
|
154
|
+
# Cython debug symbols
|
|
155
|
+
cython_debug/
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "temdb-models"
|
|
3
|
+
version = "2.0.0"
|
|
4
|
+
description = "Shared Pydantic models for TEMdb"
|
|
5
|
+
requires-python = ">=3.10"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"pydantic>=2.0",
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
[build-system]
|
|
11
|
+
requires = ["hatchling"]
|
|
12
|
+
build-backend = "hatchling.build"
|
|
13
|
+
|
|
14
|
+
[tool.hatch.build.targets.wheel]
|
|
15
|
+
packages = ["src/temdb"]
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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"
|
|
@@ -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]
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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")
|