grdb 0.9.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- grdb-0.9.1/PKG-INFO +146 -0
- grdb-0.9.1/README.md +126 -0
- grdb-0.9.1/grdb/__init__.py +23 -0
- grdb-0.9.1/grdb/core.py +27 -0
- grdb-0.9.1/grdb/crud.py +477 -0
- grdb-0.9.1/grdb/devtools.py +363 -0
- grdb-0.9.1/grdb/migrations.py +206 -0
- grdb-0.9.1/grdb/models.py +490 -0
- grdb-0.9.1/grdb/py.typed +0 -0
- grdb-0.9.1/grdb.egg-info/PKG-INFO +146 -0
- grdb-0.9.1/grdb.egg-info/SOURCES.txt +17 -0
- grdb-0.9.1/grdb.egg-info/dependency_links.txt +1 -0
- grdb-0.9.1/grdb.egg-info/requires.txt +14 -0
- grdb-0.9.1/grdb.egg-info/top_level.txt +1 -0
- grdb-0.9.1/pyproject.toml +92 -0
- grdb-0.9.1/setup.cfg +4 -0
- grdb-0.9.1/tests/test_crud.py +476 -0
- grdb-0.9.1/tests/test_models.py +164 -0
- grdb-0.9.1/tests/test_readme.py +48 -0
grdb-0.9.1/PKG-INFO
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: grdb
|
|
3
|
+
Version: 0.9.1
|
|
4
|
+
Summary: README.md
|
|
5
|
+
Author-email: Bjørn Mølvig <bjoernmoelvig@gmail.com>, Mads Ehrhorn <mads.ehrhorn@gmail.com>
|
|
6
|
+
Requires-Python: <=3.13,>=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: sqlmodel>=0.0.24
|
|
9
|
+
Requires-Dist: pydantic>=2.11.3
|
|
10
|
+
Provides-Extra: build
|
|
11
|
+
Requires-Dist: build>=1.2.1; extra == "build"
|
|
12
|
+
Requires-Dist: twine>=5.1.1; extra == "build"
|
|
13
|
+
Requires-Dist: bumpver>=2024.1130; extra == "build"
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: ruff>=0.11.6; extra == "dev"
|
|
16
|
+
Requires-Dist: pytest>=7.0.1; extra == "dev"
|
|
17
|
+
Requires-Dist: ty>=0.0.9; extra == "dev"
|
|
18
|
+
Requires-Dist: bumpver>=2024.1130; extra == "dev"
|
|
19
|
+
Requires-Dist: pytest-cov>=6.1.1; extra == "dev"
|
|
20
|
+
|
|
21
|
+
# grdb
|
|
22
|
+
|
|
23
|
+
A Python library for creating and managing SQLite databases of THz pulse measurement data.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install grdb
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
|
|
35
|
+
### Loading Pulses
|
|
36
|
+
|
|
37
|
+
**Load sample pulses:**
|
|
38
|
+
```python
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
from grdb import load_pulses
|
|
41
|
+
|
|
42
|
+
db_path = Path("raster.grf")
|
|
43
|
+
|
|
44
|
+
# Load first 100 sample pulses
|
|
45
|
+
samples = load_pulses(db_path, offset=0, limit=100, variant="sample")
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Load reference pulses:**
|
|
49
|
+
```python
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
from grdb import load_pulses
|
|
52
|
+
|
|
53
|
+
db_path = Path("raster.grf")
|
|
54
|
+
|
|
55
|
+
# Load first 50 reference pulses
|
|
56
|
+
references = load_pulses(db_path, offset=0, limit=50, variant="reference")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Load all pulses (both samples and references):**
|
|
60
|
+
```python
|
|
61
|
+
from pathlib import Path
|
|
62
|
+
from grdb import load_pulses
|
|
63
|
+
|
|
64
|
+
db_path = Path("raster.grf")
|
|
65
|
+
|
|
66
|
+
# Load all pulse types (variant=None)
|
|
67
|
+
all_pulses = load_pulses(db_path, offset=0, limit=1000, variant=None)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Loading Metadata
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from pathlib import Path
|
|
74
|
+
from grdb import load_metadata
|
|
75
|
+
|
|
76
|
+
db_path = Path("raster.grf")
|
|
77
|
+
|
|
78
|
+
raster_config, device_metadata, raster_metadata, n_refs, n_samples = load_metadata(db_path)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- `raster_config`: Acquisition settings such as raster `patterns`, scan `stepsize`, optional `reference_point`, how often to `acquire_ref_every`, and any multi-pass `repetitions_config`.
|
|
82
|
+
- `device_metadata`: Instrument identifiers including the serial number and firmware version captured at acquisition time.
|
|
83
|
+
- `raster_metadata`: Session-level context like the GRDB app version, optional `raster_id`, acquisition `timestamp`, user-supplied `annotations`, raw `device_configuration`, and any saved coordinate transform in `user_coordinates`.
|
|
84
|
+
- `user_coordinates` (within `raster_metadata`): A `CoordinateTransform` that captures how user coordinates map to machine space, including the UUID and `name` of the transform, the XYZ `offset` applied, the axis/sign remapping in `mapping`, the `last_used` timestamp, and optional operator `notes`.
|
|
85
|
+
- `n_refs` and `n_samples`: Counts of final reference and sample pulses stored in the database (stitched sources are excluded so the numbers reflect the user-facing traces).
|
|
86
|
+
|
|
87
|
+
### Understanding the Measurement Data Structure
|
|
88
|
+
|
|
89
|
+
The `load_pulses` function returns a list of `Measurement` objects. Each `Measurement` represents a single pulse measurement with its associated metadata and spatial information.
|
|
90
|
+
|
|
91
|
+
#### Measurement Fields
|
|
92
|
+
|
|
93
|
+
A `Measurement` object contains the following fields:
|
|
94
|
+
|
|
95
|
+
- **`pulse`** (`Trace`): The actual pulse data containing:
|
|
96
|
+
- `time` (list[float]): Time values for the pulse waveform
|
|
97
|
+
- `signal` (list[float]): Signal amplitude values corresponding to each time point
|
|
98
|
+
- `uuid` (UUID): Unique identifier for this pulse
|
|
99
|
+
- `timestamp` (int): Unix timestamp in milliseconds when the pulse was acquired
|
|
100
|
+
- `derived_from` (list[PulseComposition] | None): If this pulse was created by stitching multiple pulses together, this contains information about the source pulses
|
|
101
|
+
- `averaged_from` (list[Trace] | None): If this pulse was created by averaging multiple pulses, this lists the contributing source traces (which may themselves be stitched)
|
|
102
|
+
|
|
103
|
+
- **`point`** (`Point3D`): The 3D spatial coordinates where this pulse was measured:
|
|
104
|
+
- `x` (float | None): X coordinate
|
|
105
|
+
- `y` (float | None): Y coordinate
|
|
106
|
+
- `z` (float | None): Z coordinate
|
|
107
|
+
|
|
108
|
+
- **`variant`** (str): The type of measurement, one of:
|
|
109
|
+
- `"reference"`: Reference measurement taken at a known location
|
|
110
|
+
- `"sample"`: Sample measurement taken on the object being scanned
|
|
111
|
+
- `"noise"`: Noise measurement for baseline correction
|
|
112
|
+
- `"other"`: Other types of measurements
|
|
113
|
+
|
|
114
|
+
- **`reference`** (UUID | None): If this is a sample measurement, this field may contain the UUID of the associated reference pulse
|
|
115
|
+
|
|
116
|
+
- **`annotations`** (list[KVPair] | None): Optional key-value pairs for additional metadata
|
|
117
|
+
|
|
118
|
+
#### Example: Accessing Measurement Data
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from pathlib import Path
|
|
122
|
+
from grdb import load_pulses
|
|
123
|
+
|
|
124
|
+
db_path = Path("raster.grf")
|
|
125
|
+
|
|
126
|
+
# Load sample pulses
|
|
127
|
+
samples = load_pulses(db_path, offset=0, limit=10, variant="sample")
|
|
128
|
+
|
|
129
|
+
# Access the first measurement
|
|
130
|
+
first_measurement = samples[0]
|
|
131
|
+
|
|
132
|
+
# Access pulse waveform data
|
|
133
|
+
time_values = first_measurement.pulse.time
|
|
134
|
+
signal_values = first_measurement.pulse.signal
|
|
135
|
+
|
|
136
|
+
# Access spatial coordinates
|
|
137
|
+
x_position = first_measurement.point.x
|
|
138
|
+
y_position = first_measurement.point.y
|
|
139
|
+
z_position = first_measurement.point.z
|
|
140
|
+
|
|
141
|
+
# Check the measurement type
|
|
142
|
+
measurement_type = first_measurement.variant
|
|
143
|
+
|
|
144
|
+
# Get the pulse UUID
|
|
145
|
+
pulse_id = first_measurement.pulse.uuid
|
|
146
|
+
```
|
grdb-0.9.1/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# grdb
|
|
2
|
+
|
|
3
|
+
A Python library for creating and managing SQLite databases of THz pulse measurement data.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install grdb
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### Loading Pulses
|
|
16
|
+
|
|
17
|
+
**Load sample pulses:**
|
|
18
|
+
```python
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from grdb import load_pulses
|
|
21
|
+
|
|
22
|
+
db_path = Path("raster.grf")
|
|
23
|
+
|
|
24
|
+
# Load first 100 sample pulses
|
|
25
|
+
samples = load_pulses(db_path, offset=0, limit=100, variant="sample")
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Load reference pulses:**
|
|
29
|
+
```python
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from grdb import load_pulses
|
|
32
|
+
|
|
33
|
+
db_path = Path("raster.grf")
|
|
34
|
+
|
|
35
|
+
# Load first 50 reference pulses
|
|
36
|
+
references = load_pulses(db_path, offset=0, limit=50, variant="reference")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Load all pulses (both samples and references):**
|
|
40
|
+
```python
|
|
41
|
+
from pathlib import Path
|
|
42
|
+
from grdb import load_pulses
|
|
43
|
+
|
|
44
|
+
db_path = Path("raster.grf")
|
|
45
|
+
|
|
46
|
+
# Load all pulse types (variant=None)
|
|
47
|
+
all_pulses = load_pulses(db_path, offset=0, limit=1000, variant=None)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Loading Metadata
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from pathlib import Path
|
|
54
|
+
from grdb import load_metadata
|
|
55
|
+
|
|
56
|
+
db_path = Path("raster.grf")
|
|
57
|
+
|
|
58
|
+
raster_config, device_metadata, raster_metadata, n_refs, n_samples = load_metadata(db_path)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- `raster_config`: Acquisition settings such as raster `patterns`, scan `stepsize`, optional `reference_point`, how often to `acquire_ref_every`, and any multi-pass `repetitions_config`.
|
|
62
|
+
- `device_metadata`: Instrument identifiers including the serial number and firmware version captured at acquisition time.
|
|
63
|
+
- `raster_metadata`: Session-level context like the GRDB app version, optional `raster_id`, acquisition `timestamp`, user-supplied `annotations`, raw `device_configuration`, and any saved coordinate transform in `user_coordinates`.
|
|
64
|
+
- `user_coordinates` (within `raster_metadata`): A `CoordinateTransform` that captures how user coordinates map to machine space, including the UUID and `name` of the transform, the XYZ `offset` applied, the axis/sign remapping in `mapping`, the `last_used` timestamp, and optional operator `notes`.
|
|
65
|
+
- `n_refs` and `n_samples`: Counts of final reference and sample pulses stored in the database (stitched sources are excluded so the numbers reflect the user-facing traces).
|
|
66
|
+
|
|
67
|
+
### Understanding the Measurement Data Structure
|
|
68
|
+
|
|
69
|
+
The `load_pulses` function returns a list of `Measurement` objects. Each `Measurement` represents a single pulse measurement with its associated metadata and spatial information.
|
|
70
|
+
|
|
71
|
+
#### Measurement Fields
|
|
72
|
+
|
|
73
|
+
A `Measurement` object contains the following fields:
|
|
74
|
+
|
|
75
|
+
- **`pulse`** (`Trace`): The actual pulse data containing:
|
|
76
|
+
- `time` (list[float]): Time values for the pulse waveform
|
|
77
|
+
- `signal` (list[float]): Signal amplitude values corresponding to each time point
|
|
78
|
+
- `uuid` (UUID): Unique identifier for this pulse
|
|
79
|
+
- `timestamp` (int): Unix timestamp in milliseconds when the pulse was acquired
|
|
80
|
+
- `derived_from` (list[PulseComposition] | None): If this pulse was created by stitching multiple pulses together, this contains information about the source pulses
|
|
81
|
+
- `averaged_from` (list[Trace] | None): If this pulse was created by averaging multiple pulses, this lists the contributing source traces (which may themselves be stitched)
|
|
82
|
+
|
|
83
|
+
- **`point`** (`Point3D`): The 3D spatial coordinates where this pulse was measured:
|
|
84
|
+
- `x` (float | None): X coordinate
|
|
85
|
+
- `y` (float | None): Y coordinate
|
|
86
|
+
- `z` (float | None): Z coordinate
|
|
87
|
+
|
|
88
|
+
- **`variant`** (str): The type of measurement, one of:
|
|
89
|
+
- `"reference"`: Reference measurement taken at a known location
|
|
90
|
+
- `"sample"`: Sample measurement taken on the object being scanned
|
|
91
|
+
- `"noise"`: Noise measurement for baseline correction
|
|
92
|
+
- `"other"`: Other types of measurements
|
|
93
|
+
|
|
94
|
+
- **`reference`** (UUID | None): If this is a sample measurement, this field may contain the UUID of the associated reference pulse
|
|
95
|
+
|
|
96
|
+
- **`annotations`** (list[KVPair] | None): Optional key-value pairs for additional metadata
|
|
97
|
+
|
|
98
|
+
#### Example: Accessing Measurement Data
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from pathlib import Path
|
|
102
|
+
from grdb import load_pulses
|
|
103
|
+
|
|
104
|
+
db_path = Path("raster.grf")
|
|
105
|
+
|
|
106
|
+
# Load sample pulses
|
|
107
|
+
samples = load_pulses(db_path, offset=0, limit=10, variant="sample")
|
|
108
|
+
|
|
109
|
+
# Access the first measurement
|
|
110
|
+
first_measurement = samples[0]
|
|
111
|
+
|
|
112
|
+
# Access pulse waveform data
|
|
113
|
+
time_values = first_measurement.pulse.time
|
|
114
|
+
signal_values = first_measurement.pulse.signal
|
|
115
|
+
|
|
116
|
+
# Access spatial coordinates
|
|
117
|
+
x_position = first_measurement.point.x
|
|
118
|
+
y_position = first_measurement.point.y
|
|
119
|
+
z_position = first_measurement.point.z
|
|
120
|
+
|
|
121
|
+
# Check the measurement type
|
|
122
|
+
measurement_type = first_measurement.variant
|
|
123
|
+
|
|
124
|
+
# Get the pulse UUID
|
|
125
|
+
pulse_id = first_measurement.pulse.uuid
|
|
126
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
__version__ = "0.9.1"
|
|
2
|
+
|
|
3
|
+
# Re-export public API functions for convenient imports, e.g.:
|
|
4
|
+
from .crud import (
|
|
5
|
+
add_pulses,
|
|
6
|
+
create_db,
|
|
7
|
+
load_metadata,
|
|
8
|
+
load_pulses,
|
|
9
|
+
update_annotations,
|
|
10
|
+
update_references,
|
|
11
|
+
)
|
|
12
|
+
from .devtools import make_dummy_database
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"__version__",
|
|
16
|
+
"add_pulses",
|
|
17
|
+
"create_db",
|
|
18
|
+
"load_metadata",
|
|
19
|
+
"load_pulses",
|
|
20
|
+
"make_dummy_database",
|
|
21
|
+
"update_annotations",
|
|
22
|
+
"update_references",
|
|
23
|
+
]
|
grdb-0.9.1/grdb/core.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from sqlalchemy import Engine, Table
|
|
2
|
+
from sqlmodel import SQLModel
|
|
3
|
+
|
|
4
|
+
from grdb.models import (
|
|
5
|
+
PulseCompositionTable,
|
|
6
|
+
PulseDB,
|
|
7
|
+
RasterInfoDB,
|
|
8
|
+
SchemaVersion,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_tables(engine: Engine) -> None:
|
|
13
|
+
"""Create the necessary tables in the database."""
|
|
14
|
+
SQLModel.metadata.create_all(engine, tables=_get_tables())
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_tables() -> list[Table]:
|
|
18
|
+
"""Get the names of all tables in the database."""
|
|
19
|
+
return [
|
|
20
|
+
SQLModel.metadata.tables[table_name]
|
|
21
|
+
for table_name in [
|
|
22
|
+
RasterInfoDB.__tablename__,
|
|
23
|
+
PulseDB.__tablename__,
|
|
24
|
+
PulseCompositionTable.__tablename__,
|
|
25
|
+
SchemaVersion.__tablename__,
|
|
26
|
+
]
|
|
27
|
+
]
|