data-steward 0.6.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.
Files changed (29) hide show
  1. data_steward-0.6.0/LICENSE +21 -0
  2. data_steward-0.6.0/PKG-INFO +347 -0
  3. data_steward-0.6.0/README.md +319 -0
  4. data_steward-0.6.0/pyproject.toml +55 -0
  5. data_steward-0.6.0/setup.cfg +4 -0
  6. data_steward-0.6.0/src/data_steward/__init__.py +3 -0
  7. data_steward-0.6.0/src/data_steward/__main__.py +5 -0
  8. data_steward-0.6.0/src/data_steward/checksum.py +99 -0
  9. data_steward-0.6.0/src/data_steward/cli.py +2280 -0
  10. data_steward-0.6.0/src/data_steward/config.py +173 -0
  11. data_steward-0.6.0/src/data_steward/contents.py +553 -0
  12. data_steward-0.6.0/src/data_steward/db.py +690 -0
  13. data_steward-0.6.0/src/data_steward/detect.py +368 -0
  14. data_steward-0.6.0/src/data_steward/fetch.py +121 -0
  15. data_steward-0.6.0/src/data_steward/provenance.py +97 -0
  16. data_steward-0.6.0/src/data_steward/sources.py +533 -0
  17. data_steward-0.6.0/src/data_steward/util.py +99 -0
  18. data_steward-0.6.0/src/data_steward/vocab.py +209 -0
  19. data_steward-0.6.0/src/data_steward/web.py +276 -0
  20. data_steward-0.6.0/src/data_steward.egg-info/PKG-INFO +347 -0
  21. data_steward-0.6.0/src/data_steward.egg-info/SOURCES.txt +27 -0
  22. data_steward-0.6.0/src/data_steward.egg-info/dependency_links.txt +1 -0
  23. data_steward-0.6.0/src/data_steward.egg-info/entry_points.txt +2 -0
  24. data_steward-0.6.0/src/data_steward.egg-info/requires.txt +8 -0
  25. data_steward-0.6.0/src/data_steward.egg-info/top_level.txt +1 -0
  26. data_steward-0.6.0/tests/test_config.py +99 -0
  27. data_steward-0.6.0/tests/test_features.py +396 -0
  28. data_steward-0.6.0/tests/test_layout.py +357 -0
  29. data_steward-0.6.0/tests/test_stew.py +360 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ho Seok Lee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,347 @@
1
+ Metadata-Version: 2.4
2
+ Name: data-steward
3
+ Version: 0.6.0
4
+ Summary: A command-line catalogue for a lab's datasets: find them, download public data once, link instead of copy
5
+ Author: Ho Seok Lee
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/hoseok-lee/data-steward
8
+ Project-URL: Issues, https://github.com/hoseok-lee/data-steward/issues
9
+ Keywords: bioinformatics,single-cell,spatial transcriptomics,data management,catalogue,GEO,SRA
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Provides-Extra: hdf5
22
+ Requires-Dist: h5py; extra == "hdf5"
23
+ Provides-Extra: test
24
+ Requires-Dist: pytest; extra == "test"
25
+ Requires-Dist: h5py; extra == "test"
26
+ Requires-Dist: numpy; extra == "test"
27
+ Dynamic: license-file
28
+
29
+ # data-steward (`stew`)
30
+
31
+ [![test](https://github.com/hoseok-lee/data-steward/actions/workflows/test.yml/badge.svg)](https://github.com/hoseok-lee/data-steward/actions/workflows/test.yml)
32
+
33
+ A command-line catalogue for a lab's datasets: single-cell, multi-omics, spatial
34
+ transcriptomics, raw reads and slide images, kept in one shared data folder. `stew`
35
+ records what each dataset is (organ, species, modality, platform, cell counts read
36
+ from the files), where it came from (GEO, SRA, Zenodo, ...) and who owns it; answers
37
+ filtered queries; downloads public data only when it isn't already there; and hands
38
+ out symlinks so everyone works from one copy instead of many.
39
+
40
+ ```console
41
+ $ stew ls --organ lung -m rna -m spatial
42
+ ID NAME PLATFORM MODALITIES ORGAN SPECIES SIZE OWNER
43
+ 21 human-lung-cancer-ffpe-rep1 xenium rna,spatial lung human 10.5 GiB alice
44
+ 22 human-lung-cancer-ffpe-rep2 xenium rna,spatial lung human 10.5 GiB alice
45
+
46
+ $ stew download GSE274673
47
+ error: GSE274673 is already downloaded and catalogued:
48
+ #86 gse274673 /data/shared/public/geo/GSE274673
49
+ Use `stew get gse274673` to link it into your folder.
50
+
51
+ $ stew get human-lung-cancer-ffpe-rep1 --to ~/project/data
52
+ ```
53
+
54
+ ## Install
55
+
56
+ ```sh
57
+ pipx install "data-steward[hdf5]" # or: pip install "data-steward[hdf5]"
58
+ nix profile install github:hoseok-lee/data-steward # or with Nix
59
+ ```
60
+
61
+ Python 3.11+. The `hdf5` extra (h5py) lets stew read cell counts, organism and tissue
62
+ from inside `.h5ad`, `.h5mu`, `.loom` and 10x `.h5` files; without it stew still works.
63
+
64
+ ## Quick start
65
+
66
+ ```sh
67
+ stew init /data/shared --group lab --set-default # a data folder with an empty catalogue
68
+ stew add ~/old/project/data --dry-run # how stew would split and place it
69
+ stew add ~/old/project/data # register it (and move it in, leaving links)
70
+ stew download GSE123456 # fetch public data, unless it is already here
71
+ stew ls -m spatial --species human # find
72
+ stew get gse123456 --to ~/analysis/inputs # link, don't copy
73
+ ```
74
+
75
+ ## Configuration
76
+
77
+ `stew init <root>` creates `<root>/.data-steward/` holding `catalog.db` and `config.toml`:
78
+
79
+ ```toml
80
+ move_on_add = true # move added datasets into <root>, leaving a symlink behind
81
+ group = "lab" # group for folders stew creates
82
+ folder_mode = "3775" # group-writable, inherited group, only owners delete
83
+
84
+ [layout] # where datasets go, relative to <root>
85
+ public = "public/{kind}/{accession}" # with a source, e.g. public/geo/GSE123456
86
+ reference = "references/{ref_kind}" # genomes and annotations: 10x or annotations
87
+ project = "projects" # everything else
88
+ ```
89
+
90
+ stew finds the catalogue, in order, from: `--catalog`; `$STEW_ROOT` (or
91
+ `$DATA_STEWARD_CATALOG`); the current folder or one above it holding `.data-steward/`
92
+ (like git); `~/.config/stew/config.toml`; `/etc/stew/config.toml`; and a `site.toml`
93
+ built into the package. The config files hold one line, `root = "/data/shared"`.
94
+ For a shared server, an admin writes `/etc/stew/config.toml` once and everyone finds the
95
+ catalogue; or build the package with a `site.toml` next to `flake.nix`. `stew config`
96
+ shows which catalogue is in use and how it was found.
97
+
98
+ ## Commands
99
+
100
+ `stew` on its own, `stew --help` or `stew help <command>` show help with examples.
101
+ A dataset can be named by its name, its numeric id, or its path.
102
+
103
+ | command | what it does |
104
+ |---|---|
105
+ | `ls` (`list`) | list datasets; filter with `--organ`, `--species`, `-m/--modality`, `--platform`, `--format`, `--tag`, `--owner`, `--study`, `--source`, `--name`, `--under`, `--text`; `-l` for cells, path, source and tags; `--paths`, `--json` or `--csv` for scripts; `--save NAME [--shared]` to keep the query, `-q NAME` to rerun it |
106
+ | `search <words>` | datasets whose name, description, study, source, path or file contents (cell types, tissues, ...) contain all the words; takes the `ls` filters too |
107
+ | `saved [--rm NAME]` | your saved queries and shared ones |
108
+ | `info <dataset>` | everything known about one dataset: contents, relations, checksum, whether you can read it |
109
+ | `serve [--port 8765]` | read-only web page of the catalogue (see below) |
110
+ | `get <dataset> [--to DIR] [--as NAME]` | make a symlink to the dataset in your folder and record it |
111
+ | `links [--all] [--released] [--prune]` | links handed out (yours by default); `--prune` releases links whose symlink was deleted by hand |
112
+ | `release <link>` | delete a link made by `get` |
113
+ | `download <source>` | download from GEO, SRA/ENA, CELLxGENE, ArrayExpress, HCA, figshare, Zenodo or a URL, unless it is already here; `--background` runs it in tmux (see below) |
114
+ | `downloads` | downloads in progress, finished and failed |
115
+ | `add <path>` (`scan`) | register a file, a folder stew recognises as one dataset, or every dataset found inside any other folder (scripts and loose files stay; nothing found = the folder as one); moved into the data folder with a symlink left behind. `--whole`, `--each`, `--to DIR`, `--in-place`, `--dry-run` |
116
+ | `set <dataset> ...` | edit metadata: `--organ`, `--species`, `-m` (replace), `--add-modality`, `--rm-modality`, `--tag`, `--untag`, `--study`, `--source`, `--desc`, `--steward`, `--name` |
117
+ | `rm <dataset>` | remove from the catalogue; files are never touched |
118
+ | `inspect <dataset…> \| --all` | read cell/feature counts, organism, tissue, assay and modalities from inside the files |
119
+ | `relate <child> <parent> [--kind]`, `relate --suggest [--apply]` | record that a dataset is `derived-from` (default), `part-of` or the `same-as` another; `unrelate` removes it |
120
+ | `checksum <dataset…> \| --all [--quick]` | record a hash of every file; `verify` re-checks them; `duplicates` lists datasets and files stored more than once |
121
+ | `mv <dataset\|folder> <dest>` | move on disk (a rename on the same filesystem), update the catalogue, repoint links; `--leave-link` keeps the old path working; `dest/` means into that folder |
122
+ | `refresh [dataset]` | re-check paths, sizes and owners; flags missing datasets |
123
+ | `report` | storage report: space by area, owner and modality; largest datasets; never-linked, unchanged ones |
124
+ | `check` / `backup` | nightly health check (present, checksums match) and catalogue backup |
125
+ | `summary` | counts by modality, organ, species and platform |
126
+ | `vocab` | the controlled vocabulary and accepted aliases |
127
+ | `log` | recent changes, with who made them |
128
+ | `init [--group lab]` | create a new, empty catalogue |
129
+
130
+ ### Filters
131
+
132
+ Repeating a filter for organ, species, platform or format means *any of*
133
+ (`--organ lung --organ liver`); repeating a modality or tag means *all of*
134
+ (`-m rna -m spatial` finds spatial transcriptomics). Aliases are understood:
135
+ `--organ lungs`, `--organ pdac` (pancreas), `-m scrna` (rna), `--species mm10`
136
+ (mouse). See `stew vocab`.
137
+
138
+ ### Sources
139
+
140
+ Every dataset can record where it came from: a GEO accession (`GSE…`, `GSM…`), a
141
+ Zenodo record (`zenodo:123456`) or any URL. Give it with `--source` to `add`, `scan`
142
+ or `set`; `download` records it automatically. Different spellings of the same
143
+ source are recognised as one: `GSE274673`, its GEO page URL and its FTP folder are
144
+ the same, as are `zenodo:123`, `10.5281/zenodo.123`, `https://doi.org/10.5281/zenodo.123`
145
+ and `https://zenodo.org/records/123`. `stew ls --source GSE274673` finds it.
146
+
147
+ ### Downloading
148
+
149
+ ```sh
150
+ stew download GSE274673 --dry-run # checks, file list and plan; nothing fetched
151
+ stew download GSM8454446 # one GEO sample's processed files
152
+ stew download GSE84133 --raw # its FASTQs, via SRA/ENA
153
+ stew download SRP000001 --files 'SRR000003_*'
154
+ stew download E-MTAB-5061 # ArrayExpress / BioStudies
155
+ stew download cellxgene:6f6d381a-7701-4781-935c-db10d30de293 --files '*core*'
156
+ stew download hca:74b6d569-3b11-42ef-b6b1-a0454522b4a0 # add --raw for FASTQs
157
+ stew download figshare:5968960 --files metadata_droplet.csv
158
+ stew download zenodo:5979761 --organ lung --tag tracking --background
159
+ stew download https://example.org/atlas.h5ad --name my-atlas --derived-from hlca-core
160
+ ```
161
+
162
+ | source | how stew gets the files |
163
+ |---|---|
164
+ | GEO `GSE…`/`GSM…` | supplementary (processed) files; `--raw` follows GEO's SRA/BioProject link to the FASTQs |
165
+ | SRA/ENA `SRP/SRX/SRR/PRJNA/ERP/PRJEB…` | FASTQs from ENA's mirror, md5-checked (submitted BAMs if there are no FASTQs); runs ENA doesn't mirror come as `.sra` files from NCBI's AWS open-data copy (convert with `fasterq-dump`) |
166
+ | ArrayExpress/BioStudies `E-MTAB-…` | the study's files (processed data, SDRF/IDF); raw reads via their ENA accession |
167
+ | CELLxGENE `cellxgene:<collection>` | one H5AD per dataset in the collection |
168
+ | HCA `hca:<project>` | processed files (matrices, loom, h5ad); `--raw` for FASTQs; sha256-checked |
169
+ | figshare, Zenodo | all files of the record, md5-checked |
170
+ | any URL | the one file |
171
+
172
+ Before fetching anything, `download` checks that the data isn't already here:
173
+
174
+ 1. a catalogued dataset already has this source, or the GEO series / Zenodo record
175
+ that contains it (a GSM's series, a file URL's record), or the SRA study that a
176
+ GEO series' raw reads point to;
177
+ 2. a catalogued dataset mentions the accession (or a containing series) in its
178
+ study, name or path, which usually means it was registered before sources were
179
+ recorded;
180
+ 3. someone is downloading it right now;
181
+ 4. the destination folder already exists without being a stew download.
182
+
183
+ Checks 1 and 3 always stop the download; 2 and 4 can be overridden with `--force`.
184
+ The first round of checks uses only the catalogue and the disk, so a repeat
185
+ request never reaches the network.
186
+
187
+ Files go to `<data root>/downloads/<kind>/<id>/` (the data root is the folder
188
+ holding `.data-steward/`; override with `--to` or `$STEW_DOWNLOAD_DIR`). Downloads
189
+ resume if interrupted (re-run the same command), published checksums are verified,
190
+ and free space is checked first. Finished downloads are registered with their
191
+ source, title and study, the files are read for cell counts, organism and tissue,
192
+ and recognisable datasets inside a download (e.g. each H5AD of a CELLxGENE
193
+ collection) are registered too, as `part-of` the download.
194
+
195
+ With `--background`, stew does the checks and asks its questions, then hands the
196
+ download to a tmux session (`stew-dl-…`) and returns. `stew downloads` shows
197
+ progress and the log file, and your next `stew` command tells you when it finished
198
+ or failed.
199
+
200
+ ### References, project folders and provenance
201
+
202
+ Genome references are catalogued as `kind = reference`, separate from datasets:
203
+ 10x reference packages (a folder with `reference.json` and `fasta/`) and folders
204
+ holding only annotation files (`.gtf`, `.bed`, `.chrom.sizes`, `.fa`, ...).
205
+ `stew ls` shows datasets only and says how many references matched;
206
+ `stew ls --kind reference` (or `--kind all`) lists them.
207
+
208
+ Some folders are one dataset as a whole even though stew would find pieces
209
+ inside, such as an analysis project of R objects and scripts. Either register
210
+ each subfolder whole with `stew add <parent> --each`, or put a `.stew-dataset`
211
+ file in the folder; it may hold JSON metadata (`name`, `organ`, `species`,
212
+ `modalities`, `study`, `source`, `description`, `tags`, `steward`, `kind`) that
213
+ stew uses when it registers the folder. Either way, later scans skip what is
214
+ inside a catalogued dataset.
215
+
216
+ 10x pipeline outputs (cellranger, cellranger-arc, cellranger-atac, spaceranger)
217
+ record the command they ran in `<id>/_cmdline`. stew reads it, follows
218
+ `--fastqs`, `--libraries` CSVs and `--reference`, and records `derived-from`
219
+ relations to the catalogued FASTQ folders and a `uses` relation to the reference.
220
+ FASTQ folders then take the species and organ of what was made from them.
221
+
222
+ cellranger-atac outputs and folders of scATAC fragments (`fragments.tsv.gz` with
223
+ `barcodes.tsv`) are recognised, with cell counts; for barnyard references
224
+ (GRCh38-and-mm10) the species is set only when at least 90% of cells map to one genome.
225
+
226
+ ### Moving data
227
+
228
+ `stew mv <dataset or folder> <destination>` renames on disk (same filesystem only,
229
+ so it is instant), updates the path of every catalogued dataset at or below the
230
+ old path, and repoints the symlinks handed out by `stew get`. `--leave-link`
231
+ leaves a symlink at the old path so scripts that use it keep working; `--dry-run`
232
+ shows the plan. A destination ending in `/` is a folder to move into (`--parents`
233
+ creates it). Moving a messy existing folder into a layout is a sequence of these,
234
+ with a link left at every old name so nothing that uses the old paths breaks.
235
+
236
+ ### What is inside a dataset
237
+
238
+ `scan`, `add` and `download` read the files themselves, and `stew inspect` does it
239
+ again later. From `.h5ad`, `.h5mu`, `.loom`, 10x `.h5`, AnnData/SpatialData `.zarr`,
240
+ Xenium bundles (`experiment.xenium`) and Cell/Space Ranger outputs
241
+ (`metrics_summary.csv`) stew records the number of cells (or spots) and features,
242
+ and where present the organism, tissues, assay, diseases and cell types
243
+ (CELLxGENE-style `obs` columns), feature types (gene expression, antibody capture,
244
+ peaks), obsm keys and layers. Species falls back to the gene names (`ACTB` vs
245
+ `Actb`, `ENSG…` vs `ENSMUSG…`) and the reference genome. Only headers, indices and
246
+ small annotation columns are read, never expression matrices. File contents beat
247
+ guesses from folder names; values you set yourself are kept unless you run
248
+ `stew inspect --overwrite`.
249
+
250
+ ### Relations, checksums, duplicates
251
+
252
+ `stew relate filtered raw` records that `filtered` was derived from `raw`; `--kind
253
+ part-of` and `--kind same-as` are the other kinds. `stew info` shows both
254
+ directions, and loops are refused.
255
+
256
+ `stew checksum` stores every file's size, mtime and BLAKE2b hash plus a fingerprint
257
+ of the whole dataset; `--quick` hashes only the first and last MiB of each file,
258
+ which is enough to find copies in seconds. `stew verify` re-hashes and reports
259
+ changed, missing and new files (exit status 1 if anything differs). `stew
260
+ duplicates` lists whole datasets with the same fingerprint and individual files
261
+ stored in more than one dataset, with the space that could be saved.
262
+
263
+ ### Web page
264
+
265
+ `stew serve` serves a read-only page: a sortable table with search, organ/species/
266
+ modality/platform filters, a detail panel (contents, relations, checksum, the `stew
267
+ get` command) and CSV export. It opens the catalogue read-only and never serves data
268
+ files. By default it listens on the server's localhost only; people reach it through
269
+ an SSH tunnel from their own computer, which needs no root and no open port:
270
+
271
+ ```sh
272
+ stew serve --port 8765 # on the server, e.g. in tmux
273
+ ssh -N -L 8765:localhost:8765 <you>@<server> # on your computer; open http://localhost:8765/
274
+ ```
275
+
276
+ To serve it on the network instead, use `--host 0.0.0.0` behind your firewall, or put
277
+ it behind a reverse proxy with a login.
278
+
279
+ ### What `scan` recognises
280
+
281
+ | layout | platform / format |
282
+ |---|---|
283
+ | folder with `experiment.xenium` (organism and tissue read from it) | xenium / xenium-bundle |
284
+ | `outs/spatial/tissue_positions*` | visium / spaceranger |
285
+ | `outs/binned_outputs/` | visium-hd / spaceranger |
286
+ | `*exprMat_file.csv` | cosmx |
287
+ | `cell_by_gene.csv` + `detected_transcripts.csv` | merscope |
288
+ | `outs/filtered_feature_bc_matrix*` (+ `atac_fragments.tsv.gz`) | 10x-chromium / cellranger (10x-multiome / cellranger-arc) |
289
+ | `*.zarr` | spatialdata, anndata-zarr or zarr |
290
+ | `*.h5ad`, `*.h5mu`, `*.loom`, `*.rds`, `*.h5seurat`, `*feature_bc_matrix.h5` | analysis objects |
291
+ | folders of `*.fastq.gz` / `*.bam` | fastq / bam (one dataset per folder) |
292
+ | `*.svs`, `*.ndpi`, `*.mrxs`, `*.ome.tif` | slide images |
293
+
294
+ Organ, species, platform and extra modalities are guessed from words in the path
295
+ (`human_lung_cancer`, `mouse_brain`, `pdac`, `visium_hd`, ...). Anything not
296
+ guessed is shown as `?` before registering, and can be filled in during review or
297
+ later with `set`.
298
+
299
+ ## The shared catalogue
300
+
301
+ The catalogue is a single SQLite file in `<root>/.data-steward/` (see
302
+ [Configuration](#configuration) for how stew finds it).
303
+
304
+ `init --download-root DIR` sets a separate folder for downloads (by default they follow the `public` layout).
305
+ `init --group lab` makes the catalogue folder group-owned and group-writable, so lab
306
+ members can register datasets, download, and record their links; everyone else can
307
+ still read it. Every change is logged with the user who made it (`log`). Newer
308
+ versions of stew upgrade an older catalogue in place the first time they open it.
309
+
310
+ Optionally, stewards (set with `init --steward` or `stew stewards --add/--remove`) are
311
+ the only users who may register, edit, remove, move, inspect, checksum or download
312
+ datasets; everyone else can list, search, `get`/`release` links and keep their own
313
+ saved queries. Without stewards (the default) anyone who can write the catalogue may change it.
314
+
315
+ "Access" here means tracking, not granting: `get` only links datasets you can
316
+ already read. If you can't, it tells you who owns the data and whom to ask.
317
+
318
+ ## Nightly checks
319
+
320
+ `stew backup` copies the catalogue safely (even while in use) to
321
+ `.data-steward/backups/`, keeping the newest 30; `stew check` confirms every dataset is
322
+ present and unchanged since its checksum and ends with `RESULT: OK` or the problems.
323
+ `scripts/nightly.sh` runs both and logs to `.data-steward/logs/`. Schedule it with cron
324
+ (`0 3 * * * /path/to/nightly.sh`), a systemd timer, or, where neither is available,
325
+ `scripts/scheduler.sh` in tmux. `stew report` summarises storage: space by area, owner
326
+ and modality, the largest datasets, and the ones nobody links to.
327
+
328
+ For lab members, [docs/USER_GUIDE.md](docs/USER_GUIDE.md) is a one-page guide you can
329
+ adapt (replace `/data/shared` and the steward's name).
330
+
331
+ ## Development
332
+
333
+ ```sh
334
+ nix develop # python 3.12 with pytest, h5py, numpy; ruff
335
+ python -m pytest # tests (also run by `nix build` and CI)
336
+ ruff check src tests scripts
337
+ python -m data_steward --help # run from the checkout
338
+ nix build # result/bin/stew
339
+ python scripts/make-dummy-data.py /tmp/dummy # fake tree: real small HDF5 files + sparse big ones,
340
+ # plus mirror/: a typical lab data folder in miniature
341
+ ```
342
+
343
+ Releases: bump `version` in `pyproject.toml` (and `__init__.py`), add a CHANGELOG entry, then
344
+ create a GitHub release tagged `v<version>`; `.github/workflows/publish.yml` tests, builds and
345
+ uploads it to PyPI.
346
+
347
+ The flake is pinned to nixpkgs 25.05 so it also evaluates on old Nix releases (2.11).