canvod-store-metadata 0.2.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.
- canvod_store_metadata-0.2.0/PKG-INFO +376 -0
- canvod_store_metadata-0.2.0/README.md +354 -0
- canvod_store_metadata-0.2.0/pyproject.toml +70 -0
- canvod_store_metadata-0.2.0/src/canvod/store_metadata/__init__.py +39 -0
- canvod_store_metadata-0.2.0/src/canvod/store_metadata/collectors.py +359 -0
- canvod_store_metadata-0.2.0/src/canvod/store_metadata/inventory.py +301 -0
- canvod_store_metadata-0.2.0/src/canvod/store_metadata/io.py +111 -0
- canvod_store_metadata-0.2.0/src/canvod/store_metadata/schema.py +231 -0
- canvod_store_metadata-0.2.0/src/canvod/store_metadata/show.py +463 -0
- canvod_store_metadata-0.2.0/src/canvod/store_metadata/validate.py +201 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: canvod-store-metadata
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Rich metadata lifecycle for canvod Icechunk stores
|
|
5
|
+
Keywords: GNSS,VOD,metadata,icechunk,zarr,DataCite,ACDD,STAC
|
|
6
|
+
Author: Nicolas François Bader
|
|
7
|
+
Author-email: Nicolas François Bader <nicolas.bader@geo.tuwien.ac.at>
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Dist: pydantic>=2.0
|
|
14
|
+
Requires-Dist: icechunk>=1.1.5
|
|
15
|
+
Requires-Dist: zarr>=3.1.2
|
|
16
|
+
Requires-Dist: polars>=1.0.0
|
|
17
|
+
Requires-Dist: canvod-utils>=0.1.0
|
|
18
|
+
Maintainer: Climate and Environmental Remote Sensing (CLIMERS)
|
|
19
|
+
Maintainer-email: Climate and Environmental Remote Sensing (CLIMERS) <nicolas.bader@geo.tuwien.ac.at>
|
|
20
|
+
Requires-Python: >=3.14
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# canvod-store-metadata
|
|
24
|
+
|
|
25
|
+
Rich metadata lifecycle for canvod Icechunk stores. Defines, collects, writes, reads,
|
|
26
|
+
validates, and catalogs metadata aligned with **DataCite 4.5**, **ACDD 1.3**, and
|
|
27
|
+
**STAC 1.1**.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv add canvod-store-metadata
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Naming: "store metadata" vs "file registry"
|
|
36
|
+
|
|
37
|
+
canvod has two distinct concepts that both involve "metadata":
|
|
38
|
+
|
|
39
|
+
| Concept | Package | Location in store | What it tracks |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| **Store metadata** | `canvod-store-metadata` (this package) | Zarr root attrs (`canvod_metadata` key) | *Who/what/when/how* — identity, creator, provenance, environment, compliance |
|
|
42
|
+
| **File registry** | `canvod-store` (`MyIcechunkStore`) | `{group}/metadata/table` Zarr arrays | *Which files* went in — per-file hash, temporal range, filename, path |
|
|
43
|
+
|
|
44
|
+
In short: **store metadata** describes the store as a whole (for catalogs, reproducibility,
|
|
45
|
+
and compliance), while the **file registry** is an internal ingest ledger that tracks
|
|
46
|
+
individual source files for deduplication and auditing.
|
|
47
|
+
|
|
48
|
+
## Quick start
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from pathlib import Path
|
|
52
|
+
from canvod.store_metadata import (
|
|
53
|
+
write_metadata, read_metadata, format_metadata, validate_all,
|
|
54
|
+
extract_env, write_stac_collection, scan_stores,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Write metadata to an existing Icechunk store
|
|
58
|
+
write_metadata(store_path, metadata)
|
|
59
|
+
|
|
60
|
+
# Read it back
|
|
61
|
+
meta = read_metadata(store_path)
|
|
62
|
+
|
|
63
|
+
# Human-readable report
|
|
64
|
+
print(format_metadata(meta))
|
|
65
|
+
|
|
66
|
+
# Query a single section
|
|
67
|
+
print(format_metadata(meta, section="env"))
|
|
68
|
+
|
|
69
|
+
# Validate against DataCite/ACDD/STAC
|
|
70
|
+
issues = validate_all(meta)
|
|
71
|
+
|
|
72
|
+
# Export STAC Collection JSON
|
|
73
|
+
write_stac_collection(store_path)
|
|
74
|
+
|
|
75
|
+
# Reproduce the exact Python environment
|
|
76
|
+
extract_env(store_path, Path("repro_env/"))
|
|
77
|
+
# then: cd repro_env && uv sync --frozen
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## CLI
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
python -m canvod.store_metadata.show /path/to/store # full report
|
|
84
|
+
python -m canvod.store_metadata.show /path/to/store identity # one section
|
|
85
|
+
python -m canvod.store_metadata.show /path/to/store reproduce # env reproduction
|
|
86
|
+
python -m canvod.store_metadata.show /path/to/store uv # raw uv.lock
|
|
87
|
+
python -m canvod.store_metadata.show /path/to/store toml # raw pyproject.toml
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## What we track (11 sections, ~90 fields)
|
|
93
|
+
|
|
94
|
+
All metadata is stored as a single JSON blob in the Zarr root attributes under the
|
|
95
|
+
key `canvod_metadata`. Each section is described below.
|
|
96
|
+
|
|
97
|
+
### 1. Identity & Discovery
|
|
98
|
+
|
|
99
|
+
Identifies the store and enables discovery in catalogs.
|
|
100
|
+
|
|
101
|
+
| Field | Type | Description | Standard |
|
|
102
|
+
|---|---|---|---|
|
|
103
|
+
| `id` | str | Unique store identifier, e.g. `Rosalia/rinex_store` | DataCite, STAC |
|
|
104
|
+
| `title` | str | Human-readable title | DataCite, ACDD, STAC |
|
|
105
|
+
| `description` | str? | Free-text description | ACDD, STAC |
|
|
106
|
+
| `store_type` | str | `rinex_store`, `vod_store`, etc. | DataCite (resourceType) |
|
|
107
|
+
| `source_format` | str | `rinex3`, `rinex2`, `sbf` | — |
|
|
108
|
+
| `keywords` | list[str] | Discovery keywords | ACDD, STAC |
|
|
109
|
+
| `conventions` | str | Metadata conventions followed (default `ACDD-1.3`) | ACDD |
|
|
110
|
+
| `naming_authority` | str? | Naming authority URI, e.g. `at.ac.tuwien` | ACDD |
|
|
111
|
+
| `persistent_identifier` | str? | DOI or other persistent identifier | FAIR F1, DataCite |
|
|
112
|
+
| `standard_name_vocabulary` | str? | Vocabulary for standard_name attributes | ACDD |
|
|
113
|
+
|
|
114
|
+
### 2. Creator
|
|
115
|
+
|
|
116
|
+
The person or team who produced the data.
|
|
117
|
+
|
|
118
|
+
| Field | Type | Description | Standard |
|
|
119
|
+
|---|---|---|---|
|
|
120
|
+
| `name` | str | Full name | DataCite, ACDD |
|
|
121
|
+
| `email` | str | Contact email | ACDD |
|
|
122
|
+
| `orcid` | str? | ORCID identifier | DataCite |
|
|
123
|
+
| `type` | str | `Personal` or `Organizational` | DataCite |
|
|
124
|
+
| `institution` | str | Institution name | DataCite, ACDD |
|
|
125
|
+
| `institution_ror` | str? | ROR identifier | DataCite |
|
|
126
|
+
| `department` | str? | Department or faculty | — |
|
|
127
|
+
| `research_group` | str? | Research group name | — |
|
|
128
|
+
| `website` | str? | Lab/group URL | — |
|
|
129
|
+
|
|
130
|
+
### 3. Publisher & Rights
|
|
131
|
+
|
|
132
|
+
Licensing and publication info for repository deposits.
|
|
133
|
+
|
|
134
|
+
| Field | Type | Description | Standard |
|
|
135
|
+
|---|---|---|---|
|
|
136
|
+
| `name` | str? | Publisher name | DataCite |
|
|
137
|
+
| `type` | str | Publisher type (default `Institutional`) | DataCite |
|
|
138
|
+
| `url` | str? | Publisher URL | — |
|
|
139
|
+
| `license` | str? | SPDX license identifier, e.g. `CC-BY-4.0` | STAC |
|
|
140
|
+
| `license_uri` | str? | Full license URL | DataCite |
|
|
141
|
+
|
|
142
|
+
### 4. Temporal Extent
|
|
143
|
+
|
|
144
|
+
When the store was created and what time period the data covers.
|
|
145
|
+
|
|
146
|
+
| Field | Type | Description | Standard |
|
|
147
|
+
|---|---|---|---|
|
|
148
|
+
| `created` | str | Store creation timestamp (ISO 8601) | DataCite |
|
|
149
|
+
| `updated` | str | Last update timestamp (ISO 8601) | — |
|
|
150
|
+
| `collected_start` | str? | Earliest data epoch | ACDD |
|
|
151
|
+
| `collected_end` | str? | Latest data epoch | ACDD |
|
|
152
|
+
| `time_coverage_start` | str? | ACDD time_coverage_start | ACDD |
|
|
153
|
+
| `time_coverage_end` | str? | ACDD time_coverage_end | ACDD |
|
|
154
|
+
| `time_coverage_duration` | str? | ISO 8601 duration, e.g. `P1D` | ACDD |
|
|
155
|
+
| `time_coverage_resolution` | str? | ISO 8601 duration, e.g. `PT5S` | ACDD |
|
|
156
|
+
|
|
157
|
+
### 5. Spatial Extent & Site
|
|
158
|
+
|
|
159
|
+
Geographic location of the GNSS site.
|
|
160
|
+
|
|
161
|
+
| Field | Type | Description | Standard |
|
|
162
|
+
|---|---|---|---|
|
|
163
|
+
| `site.name` | str | Site name, e.g. `Rosalia` | — |
|
|
164
|
+
| `site.description` | str? | Site description | — |
|
|
165
|
+
| `site.country` | str? | ISO 3166-1 alpha-2, e.g. `AT` | — |
|
|
166
|
+
| `geospatial_lat` | float? | WGS84 latitude (degrees) | ACDD |
|
|
167
|
+
| `geospatial_lon` | float? | WGS84 longitude (degrees) | ACDD |
|
|
168
|
+
| `geospatial_alt_m` | float? | Altitude in meters | — |
|
|
169
|
+
| `geospatial_lat_min/max` | float? | Latitude bounds | ACDD |
|
|
170
|
+
| `geospatial_lon_min/max` | float? | Longitude bounds | ACDD |
|
|
171
|
+
| `geospatial_vertical_crs` | str | Vertical CRS (default `EPSG:4979`) | — |
|
|
172
|
+
| `bbox` | list[float]? | STAC bounding box `[W, S, E, N]` | STAC |
|
|
173
|
+
| `extent_temporal_interval` | list? | STAC temporal interval | STAC |
|
|
174
|
+
|
|
175
|
+
### 6. Instruments & Receivers
|
|
176
|
+
|
|
177
|
+
GNSS equipment and per-receiver ingestion stats.
|
|
178
|
+
|
|
179
|
+
| Field | Type | Description | Standard |
|
|
180
|
+
|---|---|---|---|
|
|
181
|
+
| `platform` | str | Platform type (default `ground-based GNSS`) | ACDD |
|
|
182
|
+
| `instruments` | list[str] | Instrument names | ACDD |
|
|
183
|
+
| `receivers` | dict | Per-receiver info (see below) | — |
|
|
184
|
+
|
|
185
|
+
**Per receiver:**
|
|
186
|
+
|
|
187
|
+
| Field | Type | Description |
|
|
188
|
+
|---|---|---|
|
|
189
|
+
| `type` | str | `reference` or `canopy` |
|
|
190
|
+
| `directory` | str | Data subdirectory |
|
|
191
|
+
| `reader_format` | str | Reader format (default `auto`) |
|
|
192
|
+
| `description` | str? | Receiver description |
|
|
193
|
+
| `recipe` | str? | Naming recipe used |
|
|
194
|
+
| `epochs` | int? | Number of epochs ingested |
|
|
195
|
+
| `sids` | int? | Number of satellite IDs observed |
|
|
196
|
+
| `variables` | list[str]? | Variable names in the store group |
|
|
197
|
+
| `temporal_range` | list[str]? | `[start, end]` ISO timestamps |
|
|
198
|
+
| `metadata` | dict? | Freeform receiver metadata |
|
|
199
|
+
|
|
200
|
+
### 7. Software Provenance
|
|
201
|
+
|
|
202
|
+
Exact software stack used to create the store.
|
|
203
|
+
|
|
204
|
+
| Field | Type | Description | Standard |
|
|
205
|
+
|---|---|---|---|
|
|
206
|
+
| `software` | dict[str, str] | Package name → version | W3C PROV |
|
|
207
|
+
| `python` | str? | Python version + implementation | W3C PROV |
|
|
208
|
+
| `uv_version` | str? | `uv` package manager version | — |
|
|
209
|
+
| `level` | str | Processing level: `L1` (obs store) or `L2` (VOD store) | — |
|
|
210
|
+
| `lineage` | str? | Processing lineage description | W3C PROV |
|
|
211
|
+
| `facility` | str? | Hostname of the processing machine | W3C PROV |
|
|
212
|
+
| `datetime` | str? | Processing timestamp | W3C PROV |
|
|
213
|
+
|
|
214
|
+
Tracked packages: `canvodpy`, `canvod-readers`, `canvod-store`, `canvod-store-metadata`,
|
|
215
|
+
`canvod-utils`, `icechunk`, `zarr`, `xarray`, `dask`, `numpy`, `polars`, `pydantic`.
|
|
216
|
+
|
|
217
|
+
### 8. Environment
|
|
218
|
+
|
|
219
|
+
Hardware and runtime environment for reproducibility.
|
|
220
|
+
|
|
221
|
+
| Field | Type | Description |
|
|
222
|
+
|---|---|---|
|
|
223
|
+
| `hostname` | str? | Machine hostname |
|
|
224
|
+
| `os` | str? | OS platform string (e.g. `macOS-15.4-arm64-arm-64bit`) |
|
|
225
|
+
| `arch` | str? | CPU architecture (e.g. `arm64`) |
|
|
226
|
+
| `cpu_count` | int? | Number of CPU cores |
|
|
227
|
+
| `memory_gb` | float? | Total system RAM in GB |
|
|
228
|
+
| `disk_free_gb` | float? | Free disk at store path in GB |
|
|
229
|
+
| `dask_workers` | int? | Dask worker count used |
|
|
230
|
+
| `dask_threads_per_worker` | int? | Threads per Dask worker |
|
|
231
|
+
| `uv_lock_hash` | str? | SHA-256 of `uv.lock` for quick comparison |
|
|
232
|
+
| `pyproject_toml_text` | str? | **Raw monorepo `pyproject.toml`** (full text) |
|
|
233
|
+
| `uv_lock_text` | str? | **Raw monorepo `uv.lock`** (full text) |
|
|
234
|
+
|
|
235
|
+
The raw `pyproject.toml` and `uv.lock` are captured from the **monorepo root** (not
|
|
236
|
+
the individual package). This means you can recreate the exact virtual environment
|
|
237
|
+
that produced the store:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Extract and recreate
|
|
241
|
+
from canvod.store_metadata.show import extract_env
|
|
242
|
+
extract_env(Path("/path/to/store"), Path("repro_env/"))
|
|
243
|
+
|
|
244
|
+
cd repro_env
|
|
245
|
+
uv sync --frozen # exact same environment
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 9. Config Snapshot
|
|
249
|
+
|
|
250
|
+
Serialized processing configuration + integrity hash.
|
|
251
|
+
|
|
252
|
+
| Field | Type | Description |
|
|
253
|
+
|---|---|---|
|
|
254
|
+
| `processing` | dict? | Processing config section |
|
|
255
|
+
| `preprocessing` | dict? | Preprocessing config |
|
|
256
|
+
| `aux_data` | dict? | Auxiliary data config |
|
|
257
|
+
| `compression` | dict? | Compression settings |
|
|
258
|
+
| `icechunk` | dict? | Icechunk store config |
|
|
259
|
+
| `sids` | dict? | SID filtering/selection config |
|
|
260
|
+
| `config_hash` | str? | SHA-256 of serialized config (for quick comparison) |
|
|
261
|
+
|
|
262
|
+
### 10. References
|
|
263
|
+
|
|
264
|
+
Citations, funding, and related resources.
|
|
265
|
+
|
|
266
|
+
| Field | Type | Description | Standard |
|
|
267
|
+
|---|---|---|---|
|
|
268
|
+
| `software_repository` | str? | Source code URL | — |
|
|
269
|
+
| `documentation` | str? | Documentation URL | — |
|
|
270
|
+
| `access_url` | str? | URL where data is accessible (FAIR A1) | FAIR |
|
|
271
|
+
| `related_stores` | list[str] | IDs of related stores | DataCite |
|
|
272
|
+
| `publications` | list | DOI + citation pairs | DataCite |
|
|
273
|
+
| `funding` | list | Funder, ROR, grant number, award title | DataCite |
|
|
274
|
+
|
|
275
|
+
### 11. Summaries
|
|
276
|
+
|
|
277
|
+
Aggregate statistics about the store contents.
|
|
278
|
+
|
|
279
|
+
| Field | Type | Description |
|
|
280
|
+
|---|---|---|
|
|
281
|
+
| `total_epochs` | int? | Total number of time epochs |
|
|
282
|
+
| `total_sids` | int? | Total satellite IDs across all constellations |
|
|
283
|
+
| `constellations` | list[str]? | GNSS constellations present (G, R, E, C, ...) |
|
|
284
|
+
| `variables` | list[str]? | Data variable names |
|
|
285
|
+
| `temporal_resolution_s` | float? | Temporal resolution in seconds |
|
|
286
|
+
| `file_count` | int? | Number of source files ingested |
|
|
287
|
+
| `store_size_mb` | float? | Store size on disk in MB |
|
|
288
|
+
| `history` | list[str] | Timestamped processing log entries |
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## FAIR data principles
|
|
293
|
+
|
|
294
|
+
The schema is designed around the [FAIR principles](https://doi.org/10.1038/sdata.2016.18)
|
|
295
|
+
(Findable, Accessible, Interoperable, Reusable). The table below maps each
|
|
296
|
+
sub-principle to the fields and features that satisfy it:
|
|
297
|
+
|
|
298
|
+
| Principle | Requirement | How we satisfy it |
|
|
299
|
+
|---|---|---|
|
|
300
|
+
| **F1** | Globally unique persistent identifier | `identity.id` (local) + `identity.persistent_identifier` (DOI) |
|
|
301
|
+
| **F2** | Rich metadata | 11 sections, ~90 fields |
|
|
302
|
+
| **F3** | Metadata includes data identifier | `identity.id` is part of the metadata blob |
|
|
303
|
+
| **F4** | Registered in searchable resource | `scan_stores()`, STAC catalog export |
|
|
304
|
+
| **A1** | Retrievable by standard protocol | Zarr/Icechunk (local, S3, HTTP); `references.access_url` |
|
|
305
|
+
| **A1.1** | Protocol is open and free | Zarr v3 + Icechunk — open specifications |
|
|
306
|
+
| **A1.2** | Protocol allows authentication | Icechunk supports S3/GCS credentials |
|
|
307
|
+
| **A2** | Metadata accessible if data gone | Metadata in root attrs (coupled); STAC JSON is independent |
|
|
308
|
+
| **I1** | Formal knowledge representation | JSON with Pydantic v2 schema |
|
|
309
|
+
| **I2** | FAIR-compliant vocabularies | ACDD conventions, SPDX licenses |
|
|
310
|
+
| **I3** | Qualified cross-references | `references.related_stores`, `references.publications` |
|
|
311
|
+
| **R1.1** | Clear data usage license | `publisher.license` (SPDX identifier) |
|
|
312
|
+
| **R1.2** | Detailed provenance | `processing` (software), `environment` (hardware), `config` (settings) |
|
|
313
|
+
| **R1.3** | Domain-relevant community standards | DataCite 4.5, ACDD 1.3, STAC 1.1 |
|
|
314
|
+
|
|
315
|
+
## Standards compliance
|
|
316
|
+
|
|
317
|
+
Metadata is validated against four standards:
|
|
318
|
+
|
|
319
|
+
| Standard | What it checks |
|
|
320
|
+
|---|---|
|
|
321
|
+
| **FAIR** | All sub-principles: persistent ID, rich metadata, license, provenance, access URL, cross-references |
|
|
322
|
+
| **DataCite 4.5** | Mandatory fields for TU Wien Repositum deposits: id, title, creator name/institution, publication date, resource type, publisher |
|
|
323
|
+
| **ACDD 1.3** | Highly recommended (title, id, conventions, creator, date) + recommended (keywords, description, geospatial) |
|
|
324
|
+
| **STAC 1.1** | Collection required fields: id, title, description, bbox, temporal interval, license |
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
results = validate_all(meta)
|
|
328
|
+
for standard, issues in results.items():
|
|
329
|
+
print(f"{standard}: {'PASS' if not issues else f'{len(issues)} issues'}")
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## STAC JSON export
|
|
333
|
+
|
|
334
|
+
Each store can be exported as a standalone STAC Collection JSON file:
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
from canvod.store_metadata import write_stac_collection, write_stac_catalog
|
|
338
|
+
|
|
339
|
+
# Single store → collection.json
|
|
340
|
+
write_stac_collection(store_path) # writes store_path/collection.json
|
|
341
|
+
write_stac_collection(store_path, Path("out.json")) # custom path
|
|
342
|
+
|
|
343
|
+
# Scan directory → catalog.json + per-store collection.json files
|
|
344
|
+
write_stac_catalog(root_dir)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
The catalog output is a valid STAC Catalog with child links pointing to each
|
|
348
|
+
store's `collection.json`.
|
|
349
|
+
|
|
350
|
+
## Store inventory
|
|
351
|
+
|
|
352
|
+
Scan a directory tree for all Icechunk stores and build a Polars DataFrame catalog:
|
|
353
|
+
|
|
354
|
+
```python
|
|
355
|
+
from canvod.store_metadata import scan_stores
|
|
356
|
+
|
|
357
|
+
df = scan_stores(Path("/Volumes/ExtremePro/stores/Rosalia/"))
|
|
358
|
+
print(df)
|
|
359
|
+
# ┌─────────────────────┬───────────────┬─────────┬─────────────┐
|
|
360
|
+
# │ id ┆ store_type ┆ site ┆ total_epochs│
|
|
361
|
+
# ╞═════════════════════╪═══════════════╪═════════╪═════════════╡
|
|
362
|
+
# │ Rosalia/rinex_store ┆ rinex_store ┆ Rosalia ┆ 86400 │
|
|
363
|
+
# │ Rosalia/sbf_store ┆ sbf_store ┆ Rosalia ┆ 86400 │
|
|
364
|
+
# └─────────────────────┴───────────────┴─────────┴─────────────┘
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## Modules
|
|
368
|
+
|
|
369
|
+
| Module | Purpose |
|
|
370
|
+
|---|---|
|
|
371
|
+
| `schema.py` | Pydantic v2 models for all 11 sections |
|
|
372
|
+
| `collectors.py` | Pure functions that gather runtime info |
|
|
373
|
+
| `io.py` | Write/read/update metadata to Icechunk root attrs |
|
|
374
|
+
| `validate.py` | Compliance checking (DataCite, ACDD, STAC) |
|
|
375
|
+
| `inventory.py` | Directory scanning, catalog building, STAC export |
|
|
376
|
+
| `show.py` | Human-readable formatting, env extraction, CLI |
|