brainstem-nwb 0.1.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 (116) hide show
  1. brainstem_nwb-0.1.0/.gitignore +18 -0
  2. brainstem_nwb-0.1.0/CHANGELOG.md +99 -0
  3. brainstem_nwb-0.1.0/LICENSE +22 -0
  4. brainstem_nwb-0.1.0/PKG-INFO +158 -0
  5. brainstem_nwb-0.1.0/README.md +116 -0
  6. brainstem_nwb-0.1.0/docs/cli.md +115 -0
  7. brainstem_nwb-0.1.0/docs/design/architecture.md +51 -0
  8. brainstem_nwb-0.1.0/docs/design/decisions.md +92 -0
  9. brainstem_nwb-0.1.0/docs/design/export-schemas.md +104 -0
  10. brainstem_nwb-0.1.0/docs/design/ndx-brainstem.md +201 -0
  11. brainstem_nwb-0.1.0/docs/design/phase4-raw-data.md +89 -0
  12. brainstem_nwb-0.1.0/docs/extensions.md +131 -0
  13. brainstem_nwb-0.1.0/docs/index.md +12 -0
  14. brainstem_nwb-0.1.0/docs/install.md +65 -0
  15. brainstem_nwb-0.1.0/docs/manifest.md +69 -0
  16. brainstem_nwb-0.1.0/docs/mapping-spec.md +284 -0
  17. brainstem_nwb-0.1.0/docs/modalities.md +41 -0
  18. brainstem_nwb-0.1.0/docs/python-api.md +115 -0
  19. brainstem_nwb-0.1.0/docs/troubleshooting.md +61 -0
  20. brainstem_nwb-0.1.0/docs/validation.md +48 -0
  21. brainstem_nwb-0.1.0/docs/warnings.md +59 -0
  22. brainstem_nwb-0.1.0/examples/synthetic-ephys/README.md +57 -0
  23. brainstem_nwb-0.1.0/examples/synthetic-ephys/brainstem-export.json +1418 -0
  24. brainstem_nwb-0.1.0/examples/synthetic-ephys/brainstem-nwb.toml +9 -0
  25. brainstem_nwb-0.1.0/pyproject.toml +84 -0
  26. brainstem_nwb-0.1.0/schemas/manifest-1.0.0.schema.json +2355 -0
  27. brainstem_nwb-0.1.0/schemas/validation-report-1.0.0.schema.json +162 -0
  28. brainstem_nwb-0.1.0/scripts/generate_docs.py +26 -0
  29. brainstem_nwb-0.1.0/scripts/make_contract_export.py +229 -0
  30. brainstem_nwb-0.1.0/src/brainstem_nwb/__init__.py +34 -0
  31. brainstem_nwb-0.1.0/src/brainstem_nwb/_version.py +21 -0
  32. brainstem_nwb-0.1.0/src/brainstem_nwb/cli.py +308 -0
  33. brainstem_nwb-0.1.0/src/brainstem_nwb/config.py +109 -0
  34. brainstem_nwb-0.1.0/src/brainstem_nwb/errors.py +42 -0
  35. brainstem_nwb-0.1.0/src/brainstem_nwb/export.py +539 -0
  36. brainstem_nwb-0.1.0/src/brainstem_nwb/manifest/__init__.py +6 -0
  37. brainstem_nwb-0.1.0/src/brainstem_nwb/manifest/io.py +38 -0
  38. brainstem_nwb-0.1.0/src/brainstem_nwb/manifest/models.py +447 -0
  39. brainstem_nwb-0.1.0/src/brainstem_nwb/manifest/schema.py +33 -0
  40. brainstem_nwb-0.1.0/src/brainstem_nwb/manifest/warnings.py +180 -0
  41. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/__init__.py +0 -0
  42. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/acquisitions.py +98 -0
  43. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/context.py +111 -0
  44. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/describe.py +249 -0
  45. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/devices.py +344 -0
  46. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/docs.py +102 -0
  47. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/epochs.py +94 -0
  48. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/extension.py +330 -0
  49. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/herd.py +108 -0
  50. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/localization.py +116 -0
  51. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/manipulations.py +88 -0
  52. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/notes.py +175 -0
  53. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/pipeline.py +228 -0
  54. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/procedures.py +169 -0
  55. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/registry.py +281 -0
  56. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/session.py +203 -0
  57. brainstem_nwb-0.1.0/src/brainstem_nwb/mapping/subject.py +204 -0
  58. brainstem_nwb-0.1.0/src/brainstem_nwb/normalize/__init__.py +0 -0
  59. brainstem_nwb-0.1.0/src/brainstem_nwb/normalize/identifiers.py +60 -0
  60. brainstem_nwb-0.1.0/src/brainstem_nwb/normalize/kv.py +53 -0
  61. brainstem_nwb-0.1.0/src/brainstem_nwb/normalize/quantities.py +73 -0
  62. brainstem_nwb-0.1.0/src/brainstem_nwb/normalize/schemas.py +101 -0
  63. brainstem_nwb-0.1.0/src/brainstem_nwb/normalize/text.py +67 -0
  64. brainstem_nwb-0.1.0/src/brainstem_nwb/normalize/timeutil.py +110 -0
  65. brainstem_nwb-0.1.0/src/brainstem_nwb/normalize/units.py +630 -0
  66. brainstem_nwb-0.1.0/src/brainstem_nwb/nwb/__init__.py +0 -0
  67. brainstem_nwb-0.1.0/src/brainstem_nwb/nwb/canonical.py +94 -0
  68. brainstem_nwb-0.1.0/src/brainstem_nwb/nwb/writer.py +295 -0
  69. brainstem_nwb-0.1.0/src/brainstem_nwb/py.typed +0 -0
  70. brainstem_nwb-0.1.0/src/brainstem_nwb/raw/__init__.py +0 -0
  71. brainstem_nwb-0.1.0/src/brainstem_nwb/raw/base.py +49 -0
  72. brainstem_nwb-0.1.0/src/brainstem_nwb/raw/metadata.py +31 -0
  73. brainstem_nwb-0.1.0/src/brainstem_nwb/raw/paths.py +74 -0
  74. brainstem_nwb-0.1.0/src/brainstem_nwb/raw/registry.py +38 -0
  75. brainstem_nwb-0.1.0/src/brainstem_nwb/retrieval/__init__.py +0 -0
  76. brainstem_nwb-0.1.0/src/brainstem_nwb/retrieval/api.py +137 -0
  77. brainstem_nwb-0.1.0/src/brainstem_nwb/retrieval/loader.py +182 -0
  78. brainstem_nwb-0.1.0/src/brainstem_nwb/retrieval/schemas.py +52 -0
  79. brainstem_nwb-0.1.0/src/brainstem_nwb/store/__init__.py +0 -0
  80. brainstem_nwb-0.1.0/src/brainstem_nwb/store/datamodel.py +299 -0
  81. brainstem_nwb-0.1.0/src/brainstem_nwb/store/graph.py +180 -0
  82. brainstem_nwb-0.1.0/src/brainstem_nwb/store/inventory.py +95 -0
  83. brainstem_nwb-0.1.0/src/brainstem_nwb/validation/__init__.py +36 -0
  84. brainstem_nwb-0.1.0/src/brainstem_nwb/validation/checks.py +167 -0
  85. brainstem_nwb-0.1.0/src/brainstem_nwb/validation/report.py +82 -0
  86. brainstem_nwb-0.1.0/tests/__init__.py +0 -0
  87. brainstem_nwb-0.1.0/tests/conftest.py +74 -0
  88. brainstem_nwb-0.1.0/tests/fixtures/__init__.py +0 -0
  89. brainstem_nwb-0.1.0/tests/fixtures/builder.py +140 -0
  90. brainstem_nwb-0.1.0/tests/fixtures/exports/behavior.json +472 -0
  91. brainstem_nwb-0.1.0/tests/fixtures/exports/edge_cases.json +492 -0
  92. brainstem_nwb-0.1.0/tests/fixtures/exports/ephys.json +1418 -0
  93. brainstem_nwb-0.1.0/tests/fixtures/exports/exvivo_specimen.json +516 -0
  94. brainstem_nwb-0.1.0/tests/fixtures/exports/multisession.json +454 -0
  95. brainstem_nwb-0.1.0/tests/fixtures/exports/multisubject.json +555 -0
  96. brainstem_nwb-0.1.0/tests/fixtures/exports/optogenetics.json +758 -0
  97. brainstem_nwb-0.1.0/tests/fixtures/exports/server_contract_2_1.json +840 -0
  98. brainstem_nwb-0.1.0/tests/fixtures/exports/server_contract_2_1_session.json +858 -0
  99. brainstem_nwb-0.1.0/tests/fixtures/exports/session_export.json +396 -0
  100. brainstem_nwb-0.1.0/tests/fixtures/regen.py +39 -0
  101. brainstem_nwb-0.1.0/tests/fixtures/scenarios.py +358 -0
  102. brainstem_nwb-0.1.0/tests/integration/__init__.py +0 -0
  103. brainstem_nwb-0.1.0/tests/integration/test_modalities.py +49 -0
  104. brainstem_nwb-0.1.0/tests/test_cli_fetch.py +207 -0
  105. brainstem_nwb-0.1.0/tests/test_contract.py +96 -0
  106. brainstem_nwb-0.1.0/tests/test_datamodel.py +158 -0
  107. brainstem_nwb-0.1.0/tests/test_export.py +208 -0
  108. brainstem_nwb-0.1.0/tests/test_extension.py +221 -0
  109. brainstem_nwb-0.1.0/tests/test_loader_store.py +131 -0
  110. brainstem_nwb-0.1.0/tests/test_mapping.py +293 -0
  111. brainstem_nwb-0.1.0/tests/test_normalize.py +156 -0
  112. brainstem_nwb-0.1.0/tests/test_raw.py +57 -0
  113. brainstem_nwb-0.1.0/tests/test_spec.py +75 -0
  114. brainstem_nwb-0.1.0/tests/test_tool_alignment.py +34 -0
  115. brainstem_nwb-0.1.0/tests/test_units.py +139 -0
  116. brainstem_nwb-0.1.0/tests/test_writer_validation.py +120 -0
@@ -0,0 +1,18 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ build/
6
+ dist/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .coverage
12
+ coverage.xml
13
+ htmlcov/
14
+ site/
15
+ *.nwb
16
+ !tests/fixtures/**/*.nwb
17
+ .DS_Store
18
+ /out/
@@ -0,0 +1,99 @@
1
+ # Changelog
2
+
3
+ All notable changes to brainstem-nwb are documented here. The project follows semantic versioning. The mapping version, which tracks changes to what is written to NWB files, is listed separately.
4
+
5
+ ## 0.1.0 (2026-11-07)
6
+
7
+ ### The BrainSTEM data model is read from the server (mapping version 1.3.0)
8
+
9
+ - brainstem-nwb no longer ships a copy of the BrainSTEM data model. It reads the record types, fields, links and type vocabularies from the server's reference documents (`/reference/data/`), caches them per server for an hour, and uses the cached copy when the server cannot be reached (`DATA_MODEL_CACHED`). `BRAINSTEM_NWB_DATA_MODEL` names a directory with the documents for offline machines; `brainstem-nwb data-model` fetches and shows the model.
10
+ - The manifest's `data_model` names the server, the data model generation, the export format and a checksum of the model used. A generation this release was not written for is reported as `DATA_MODEL_UNSUPPORTED`.
11
+ - Follows the production data model (`specimen-1`): data acquisitions and manipulations link procedures only through their interfaces, as the server's session export does; specimens record `laterality` (no `hemisphere` or `baseline_conditions`).
12
+ - Planned and cancelled sessions get no file (`SESSION_NOT_PERFORMED`); planned and cancelled procedures and specimen procedures are left out of the file and kept in the manifest (`PROCEDURE_NOT_PERFORMED`). Records without a status are performed.
13
+ - The code is MIT. The BrainSTEM data model and schemas are © BrainSTEM, all rights reserved; embedded schemas carry BrainSTEM's notice in the `schemas` table description.
14
+
15
+ ### NWB extensions (mapping version 1.2.0)
16
+
17
+ - `--extension brainstem` writes every included BrainSTEM record, with every field and the links between them, as `ndx-brainstem` tables (pre-release extension, 0.2.x; install with `pip install "brainstem-nwb[extensions]"`).
18
+ - With `--extension brainstem`, coordinates of procedures and interfaces in stereotaxic absolute and Allen CCF systems are also written as `ndx-anatomical-localization` tables, linked to their `ndx-brainstem` rows (`--extension localization` is the same option).
19
+ - The `ndx-brainstem` schema table (extension 0.2.0) lists every BrainSTEM JSON schema the file's tables name, with its stable URL. `--embed-schemas` fetches the schemas from the server's public schema service and embeds them. Coordinates get their own `coordinates_schema` column.
20
+ - Materials link directly to their catalog consumable (`catalog_entry`), also when the usage names a stock. Stocks keep only their typed details (batch, titer, volume, aliquots).
21
+ - `--herd` writes ontology terms for species, strains, regions and device models as a HERD zip next to each file.
22
+ - Licenses, data storages and inventories are no longer written to NWB files (NWBFile.protocol lists protocols only); they stay in the manifest.
23
+ - An NWB Inspector check that crashes is reported as a warning, not an error.
24
+
25
+ ### Export format 2.1 (mapping version 1.1.0)
26
+
27
+ - **Session exports.** Reads session exports (`export_info.scope`, `session_id`, `project_ids`). `export --session UUID` fetches one session's export through `BrainstemClient.export_session` when the API tools provide it. A session in several projects lists all of them in `experiment_description` and `keywords`.
28
+ - **Catalog records the export now includes:**
29
+ - Brain regions and atlases: electrode-group and stimulus-site `location` from region acronyms, with full names and atlas in descriptions and procedure text.
30
+ - Suppliers: `DeviceModel` with manufacturer and model number for hardware devices and probe designs; suppliers in material descriptions.
31
+ - Publications: `related_publications` DOIs.
32
+ - User names: who performed each procedure, episode personnel, and opt-in `experimenter_source = "session_creator"`.
33
+ - Log entries: `Subject.weight` from the latest weighing before the session.
34
+ - Behavioral paradigms and categories, setup types, regulatory authorities.
35
+ - **Restricted lab resources.** Resources the exporting user cannot read (`export_info.restricted`) are named in the text and reported as `RESTRICTED_LAB_RESOURCES`.
36
+ - **Contract fixtures.** Regenerated from the server's export builder, as a project export and a session export.
37
+
38
+ ### First version
39
+
40
+ First release: metadata-only export. Mapping version 1.0.0. Manifest and validation report schemas 1.0.0.
41
+
42
+ #### Input
43
+
44
+ - Reads BrainSTEM project exports (format 2.x), from a downloaded file (`.json` or `.json.gz`) or fetched with `BrainstemClient.export_project()`.
45
+
46
+ #### Export units
47
+
48
+ - One NWB file per session and subject, named `sub-<subject>/sub-<subject>_ses-<session>.nwb`.
49
+ - Each record is attributed to the subject it belongs to, and every decision is documented.
50
+ - Specimen (ex vivo) sessions are supported.
51
+
52
+ #### What is written
53
+
54
+ **Session and subject**
55
+
56
+ - Session fields and project context.
57
+ - `Subject`: species, optionally as an NCBI Taxonomy IRI; strain; sex; date of birth; age; genotype.
58
+
59
+ **Procedures and manipulations**
60
+
61
+ - Surgery, virus and pharmacology narratives, from procedures and their materials.
62
+ - Stimulus notes, from manipulations.
63
+
64
+ **Devices**
65
+
66
+ - Devices from equipment and experimental interfaces.
67
+ - Electrode groups.
68
+ - Optogenetic stimulus sites.
69
+
70
+ **Other**
71
+
72
+ - The epochs table.
73
+ - Ex vivo slices.
74
+ - Behaviors in the notes.
75
+ - A provenance block.
76
+
77
+ #### Manifest and validation
78
+
79
+ - **Conversion manifest:** a value state for every NWB destination, coded warnings, raw-data references, and the records used.
80
+ - **Validation:**
81
+ - pynwb schema validation.
82
+ - NWB Inspector, with the DANDI configuration.
83
+ - BrainSTEM semantic checks.
84
+ - Reports in JSON and text.
85
+
86
+ #### Tools
87
+
88
+ - **CLI:** `export`, `manifest`, `validate`, `fetch`, `mapping`, `schema`.
89
+ - **Python API:** `export_project`, `export_session`, `build_manifest`, `validate_output`, `load_export`, `fetch_export`.
90
+
91
+ #### Reproducibility
92
+
93
+ - Deterministic NWB identifiers.
94
+ - `--reproducible` mode.
95
+ - Idempotent re-runs: files that are already up to date are kept.
96
+
97
+ #### Raw data (Phase 4)
98
+
99
+ - Designed only: the converter protocol, registry, data-path file and NeuroConv metadata hand-off.
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Peter C. Petersen - BrainSTEM Team
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.
22
+
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.5
2
+ Name: brainstem-nwb
3
+ Version: 0.1.0
4
+ Summary: Export BrainSTEM experimental metadata to Neurodata Without Borders (NWB) files
5
+ Project-URL: Homepage, https://github.com/brainstem-org/brainstem-nwb
6
+ Project-URL: Documentation, https://github.com/brainstem-org/brainstem-nwb/tree/main/docs
7
+ Project-URL: Issues, https://github.com/brainstem-org/brainstem-nwb/issues
8
+ Author: BrainSTEM Team
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: brainstem,dandi,electronic lab notebook,metadata,neuroscience,nwb
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: brainstem-python-api-tools>=2.1
23
+ Requires-Dist: click>=8.1
24
+ Requires-Dist: nwbinspector>=0.7
25
+ Requires-Dist: pydantic>=2.6
26
+ Requires-Dist: pynwb<5,>=4.0
27
+ Requires-Dist: requests>=2.28
28
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
29
+ Provides-Extra: dev
30
+ Requires-Dist: jsonschema>=4.18; extra == 'dev'
31
+ Requires-Dist: mypy>=1.11; extra == 'dev'
32
+ Requires-Dist: pytest-cov>=5; extra == 'dev'
33
+ Requires-Dist: pytest>=8; extra == 'dev'
34
+ Requires-Dist: ruff>=0.6; extra == 'dev'
35
+ Provides-Extra: docs
36
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
37
+ Provides-Extra: extensions
38
+ Requires-Dist: ndx-brainstem<0.3,>=0.2; extra == 'extensions'
39
+ Provides-Extra: neuroconv
40
+ Requires-Dist: neuroconv[ecephys-minimal]>=0.10; extra == 'neuroconv'
41
+ Description-Content-Type: text/markdown
42
+
43
+ # brainstem-nwb
44
+
45
+ Export experimental metadata from [BrainSTEM](https://www.brainstem.org) to
46
+ [Neurodata Without Borders (NWB)](https://www.nwb.org) files.
47
+
48
+ `brainstem-nwb` reads a BrainSTEM project export and writes one NWB file per session and subject,
49
+ following NWB and DANDI conventions. Each file holds what BrainSTEM knows about the experiment:
50
+ - session and subject
51
+ - surgeries, viruses and drugs
52
+ - devices, electrode groups and optogenetic sites
53
+ - acquisitions, behaviors, manipulations and epochs
54
+
55
+ Alongside the files it writes a versioned **conversion manifest** and a **validation report**.
56
+
57
+ This release exports **metadata only**. Measured data (recordings, video, imaging) is not converted
58
+ yet; the files describe it and point to it. Raw-data conversion with
59
+ [NeuroConv](https://github.com/catalystneuro/neuroconv), starting with flat binary extracellular
60
+ recordings, is designed and planned (see [docs/design/phase4-raw-data.md](docs/design/phase4-raw-data.md)).
61
+
62
+ ## Install
63
+
64
+ ```bash
65
+ pip install brainstem-nwb
66
+ # the development version:
67
+ pip install git+https://github.com/brainstem-org/brainstem-nwb
68
+ ```
69
+
70
+ Python 3.10 or newer. brainstem-nwb reads the BrainSTEM data model from the server at run time, so the
71
+ first export needs network access to www.brainstem.org (or your `--url`); the model is then cached (see
72
+ [installation](docs/install.md#the-brainstem-data-model)). Installing also installs [pynwb](https://github.com/NeurodataWithoutBorders/pynwb),
73
+ [NWB Inspector](https://github.com/NeurodataWithoutBorders/nwbinspector) and the
74
+ [BrainSTEM Python API tools](https://github.com/brainstem-org/brainstem_python_api_tools).
75
+
76
+ ## Quick start
77
+
78
+ From a project export downloaded in BrainSTEM (Project → Download → JSON):
79
+
80
+ ```bash
81
+ brainstem-nwb export --export-file brainstem-export.json.gz --output ./nwb/
82
+ ```
83
+
84
+ Or fetch the export directly (needs read access to the project):
85
+
86
+ ```bash
87
+ brainstem login # once, from the BrainSTEM API tools
88
+ brainstem-nwb export --project PROJECT_UUID --output ./nwb/
89
+ brainstem-nwb export --session SESSION_UUID --output ./nwb/ # one session's export
90
+ ```
91
+
92
+ From Python:
93
+
94
+ ```python
95
+ from brainstem_nwb import export_project
96
+
97
+ result = export_project(project_id="PROJECT_UUID", output_dir="./nwb")
98
+ # or: export_project(export_file="brainstem-export.json.gz", output_dir="./nwb")
99
+ print(result.exit_code, result.manifest.summary)
100
+ ```
101
+
102
+ The output directory then contains:
103
+
104
+ ```
105
+ nwb/
106
+ sub-<subject>/sub-<subject>_ses-<session>.nwb
107
+ brainstem-nwb-manifest.json what was exported, from which records, with every warning
108
+ validation-report.json / .txt pynwb, NWB Inspector (DANDI configuration) and BrainSTEM checks
109
+ source/brainstem-export-….json.gz the export the files were built from (when fetched)
110
+ ```
111
+
112
+ Other commands:
113
+ - **Dry run:** `brainstem-nwb manifest --export-file … -o manifest.json` writes only the manifest, so you can inspect what would be written.
114
+ - **One session:** `brainstem-nwb export … --session NAME`.
115
+ - **Validate existing files:** `brainstem-nwb validate ./nwb/`.
116
+
117
+ Some NWB fields are not in a BrainSTEM export, so they are never guessed: experimenter, lab and institution. Supply them in a configuration file:
118
+
119
+ ```toml
120
+ # brainstem-nwb.toml (use with --config brainstem-nwb.toml)
121
+ [nwbfile]
122
+ experimenter = ["Last, First"]
123
+ lab = "My Lab"
124
+ institution = "My University"
125
+ ```
126
+
127
+ A fully reproducible example is in [examples/synthetic-ephys](examples/synthetic-ephys/).
128
+
129
+ ## Principles
130
+
131
+ - **Nothing is invented.** No time series, electrode positions or other observations are generated from metadata. Fields BrainSTEM does not have stay empty, and the manifest records why. Each value is marked present, missing, unknown, not applicable or not exported.
132
+ - **Nothing is silently dropped.** Every field of every exported record is accounted for:
133
+ - mapped to NWB,
134
+ - used to link records,
135
+ - or kept in the manifest, with the reason.
136
+
137
+ The [mapping specification](docs/mapping-spec.md) is generated from the code.
138
+ - **Traceable.** Every file carries a provenance block naming its BrainSTEM records, the exporter and mapping versions, and the checksum of the source export. Its NWB identifier is derived from the content, so re-exporting unchanged records gives the same identifier.
139
+ - **Core NWB by default.** With `--extension brainstem`, every BrainSTEM record is also stored losslessly in the file as `ndx-brainstem` tables. See [extensions](docs/extensions.md).
140
+
141
+ ## Documentation
142
+
143
+ - [Installation and authentication](docs/install.md)
144
+ - [Quick start and CLI](docs/cli.md) · [Python API](docs/python-api.md)
145
+ - [Mapping specification](docs/mapping-spec.md) · [Warning codes](docs/warnings.md)
146
+ - [Conversion manifest](docs/manifest.md) · [Validation](docs/validation.md)
147
+ - [Supported modalities](docs/modalities.md) · [Troubleshooting](docs/troubleshooting.md)
148
+ - [Architecture](docs/design/architecture.md) · [Design decisions](docs/design/decisions.md)
149
+ - [Contributing](CONTRIBUTING.md) · [Changelog](CHANGELOG.md)
150
+
151
+ ## License
152
+
153
+ The code is released under the [MIT License](LICENSE).
154
+
155
+ The BrainSTEM data model and its JSON schemas are not part of this package and are not covered by that
156
+ license: they are © BrainSTEM, all rights reserved ([terms](https://www.brainstem.org/reference/license.md)).
157
+ brainstem-nwb reads them from the BrainSTEM server as an API client. `--embed-schemas` embeds the schemas of a
158
+ file's records unchanged, as a BrainSTEM export does, with BrainSTEM's notice next to them.
@@ -0,0 +1,116 @@
1
+ # brainstem-nwb
2
+
3
+ Export experimental metadata from [BrainSTEM](https://www.brainstem.org) to
4
+ [Neurodata Without Borders (NWB)](https://www.nwb.org) files.
5
+
6
+ `brainstem-nwb` reads a BrainSTEM project export and writes one NWB file per session and subject,
7
+ following NWB and DANDI conventions. Each file holds what BrainSTEM knows about the experiment:
8
+ - session and subject
9
+ - surgeries, viruses and drugs
10
+ - devices, electrode groups and optogenetic sites
11
+ - acquisitions, behaviors, manipulations and epochs
12
+
13
+ Alongside the files it writes a versioned **conversion manifest** and a **validation report**.
14
+
15
+ This release exports **metadata only**. Measured data (recordings, video, imaging) is not converted
16
+ yet; the files describe it and point to it. Raw-data conversion with
17
+ [NeuroConv](https://github.com/catalystneuro/neuroconv), starting with flat binary extracellular
18
+ recordings, is designed and planned (see [docs/design/phase4-raw-data.md](docs/design/phase4-raw-data.md)).
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pip install brainstem-nwb
24
+ # the development version:
25
+ pip install git+https://github.com/brainstem-org/brainstem-nwb
26
+ ```
27
+
28
+ Python 3.10 or newer. brainstem-nwb reads the BrainSTEM data model from the server at run time, so the
29
+ first export needs network access to www.brainstem.org (or your `--url`); the model is then cached (see
30
+ [installation](docs/install.md#the-brainstem-data-model)). Installing also installs [pynwb](https://github.com/NeurodataWithoutBorders/pynwb),
31
+ [NWB Inspector](https://github.com/NeurodataWithoutBorders/nwbinspector) and the
32
+ [BrainSTEM Python API tools](https://github.com/brainstem-org/brainstem_python_api_tools).
33
+
34
+ ## Quick start
35
+
36
+ From a project export downloaded in BrainSTEM (Project → Download → JSON):
37
+
38
+ ```bash
39
+ brainstem-nwb export --export-file brainstem-export.json.gz --output ./nwb/
40
+ ```
41
+
42
+ Or fetch the export directly (needs read access to the project):
43
+
44
+ ```bash
45
+ brainstem login # once, from the BrainSTEM API tools
46
+ brainstem-nwb export --project PROJECT_UUID --output ./nwb/
47
+ brainstem-nwb export --session SESSION_UUID --output ./nwb/ # one session's export
48
+ ```
49
+
50
+ From Python:
51
+
52
+ ```python
53
+ from brainstem_nwb import export_project
54
+
55
+ result = export_project(project_id="PROJECT_UUID", output_dir="./nwb")
56
+ # or: export_project(export_file="brainstem-export.json.gz", output_dir="./nwb")
57
+ print(result.exit_code, result.manifest.summary)
58
+ ```
59
+
60
+ The output directory then contains:
61
+
62
+ ```
63
+ nwb/
64
+ sub-<subject>/sub-<subject>_ses-<session>.nwb
65
+ brainstem-nwb-manifest.json what was exported, from which records, with every warning
66
+ validation-report.json / .txt pynwb, NWB Inspector (DANDI configuration) and BrainSTEM checks
67
+ source/brainstem-export-….json.gz the export the files were built from (when fetched)
68
+ ```
69
+
70
+ Other commands:
71
+ - **Dry run:** `brainstem-nwb manifest --export-file … -o manifest.json` writes only the manifest, so you can inspect what would be written.
72
+ - **One session:** `brainstem-nwb export … --session NAME`.
73
+ - **Validate existing files:** `brainstem-nwb validate ./nwb/`.
74
+
75
+ Some NWB fields are not in a BrainSTEM export, so they are never guessed: experimenter, lab and institution. Supply them in a configuration file:
76
+
77
+ ```toml
78
+ # brainstem-nwb.toml (use with --config brainstem-nwb.toml)
79
+ [nwbfile]
80
+ experimenter = ["Last, First"]
81
+ lab = "My Lab"
82
+ institution = "My University"
83
+ ```
84
+
85
+ A fully reproducible example is in [examples/synthetic-ephys](examples/synthetic-ephys/).
86
+
87
+ ## Principles
88
+
89
+ - **Nothing is invented.** No time series, electrode positions or other observations are generated from metadata. Fields BrainSTEM does not have stay empty, and the manifest records why. Each value is marked present, missing, unknown, not applicable or not exported.
90
+ - **Nothing is silently dropped.** Every field of every exported record is accounted for:
91
+ - mapped to NWB,
92
+ - used to link records,
93
+ - or kept in the manifest, with the reason.
94
+
95
+ The [mapping specification](docs/mapping-spec.md) is generated from the code.
96
+ - **Traceable.** Every file carries a provenance block naming its BrainSTEM records, the exporter and mapping versions, and the checksum of the source export. Its NWB identifier is derived from the content, so re-exporting unchanged records gives the same identifier.
97
+ - **Core NWB by default.** With `--extension brainstem`, every BrainSTEM record is also stored losslessly in the file as `ndx-brainstem` tables. See [extensions](docs/extensions.md).
98
+
99
+ ## Documentation
100
+
101
+ - [Installation and authentication](docs/install.md)
102
+ - [Quick start and CLI](docs/cli.md) · [Python API](docs/python-api.md)
103
+ - [Mapping specification](docs/mapping-spec.md) · [Warning codes](docs/warnings.md)
104
+ - [Conversion manifest](docs/manifest.md) · [Validation](docs/validation.md)
105
+ - [Supported modalities](docs/modalities.md) · [Troubleshooting](docs/troubleshooting.md)
106
+ - [Architecture](docs/design/architecture.md) · [Design decisions](docs/design/decisions.md)
107
+ - [Contributing](CONTRIBUTING.md) · [Changelog](CHANGELOG.md)
108
+
109
+ ## License
110
+
111
+ The code is released under the [MIT License](LICENSE).
112
+
113
+ The BrainSTEM data model and its JSON schemas are not part of this package and are not covered by that
114
+ license: they are © BrainSTEM, all rights reserved ([terms](https://www.brainstem.org/reference/license.md)).
115
+ brainstem-nwb reads them from the BrainSTEM server as an API client. `--embed-schemas` embeds the schemas of a
116
+ file's records unchanged, as a BrainSTEM export does, with BrainSTEM's notice next to them.
@@ -0,0 +1,115 @@
1
+ # Command-line interface
2
+
3
+ This page is the reference for every `brainstem-nwb` command and option. `brainstem-nwb --help` and `brainstem-nwb COMMAND --help` show the same options.
4
+
5
+ ## `export`
6
+
7
+ Converts a whole project, or some of its sessions, into metadata-only NWB files.
8
+
9
+ ```bash
10
+ brainstem-nwb export (--project UUID | --export-file FILE) --output DIR [options]
11
+ ```
12
+
13
+ ### Where the records come from
14
+
15
+ | Option | Meaning |
16
+ |---|---|
17
+ | `--project UUID` | Fetch the project's export from BrainSTEM |
18
+ | `--export-file FILE` | Use a downloaded export (`.json` or `.json.gz`, project or session export) |
19
+ | `--session ID\|NAME` | Export only this session; repeat for more. A single `--session UUID` without `--project` or `--export-file` fetches that session's export |
20
+ | `--token`, `--url`, `--headless`, `--no-device-flow`, `--no-background` | Fetching and authentication; see [install.md](install.md) |
21
+ | `--no-save-export` | Do not keep a copy of the fetched export in `DIR/source/` |
22
+
23
+ ### What is written
24
+
25
+ | Option | Meaning |
26
+ |---|---|
27
+ | `--config FILE.toml` | Experimenter, lab, institution and subject options ([configuration](#configuration)) |
28
+ | `--dry-run` | Write only `brainstem-nwb-manifest.json` |
29
+ | `--allow-no-subject` | Export sessions that have no subject, without an NWB Subject |
30
+ | `--no-infer-subjects` | When a session lists no subjects, do not take them from its behaviors, procedures and interfaces |
31
+ | `--overwrite` | Replace existing files whose content differs. Files that are identical are kept and reported as `up_to_date` |
32
+ | `--reproducible` | Use the export time as the creation time, so reruns give identical manifests |
33
+ | `--extension brainstem\|localization` | Also write NWB extensions: every BrainSTEM record as `ndx-brainstem` tables, and coordinates as `ndx-anatomical-localization` tables. Repeatable. See [extensions](extensions.md) |
34
+ | `--embed-schemas` | With `--extension brainstem`: fetch the BrainSTEM JSON schemas the records use from the server's public schema service (`--url`) and embed them |
35
+ | `--herd` | Also write ontology terms (NCBITaxon, RRIDs, atlas regions) as a HERD zip next to each file |
36
+ | `--fail-fast` | Stop at the first unit that fails. By default the remaining units are still exported |
37
+
38
+ ### Validation and output
39
+
40
+ | Option | Meaning |
41
+ |---|---|
42
+ | `--no-validate` | Skip validation |
43
+ | `--inspector-config dandi\|none` | NWB Inspector configuration. Default: `dandi` |
44
+ | `--fail-on error\|warning` | Which validation severity gives exit code 4 |
45
+ | `--json` | Print the summary as JSON |
46
+
47
+ ## `manifest`
48
+
49
+ The same as `export --dry-run`, but the manifest can be written to a single named file:
50
+
51
+ ```bash
52
+ brainstem-nwb manifest --export-file export.json -o manifest.json
53
+ ```
54
+
55
+ ## `validate`
56
+
57
+ Validates NWB files, or a directory of them. When the files are in an export directory, they are also checked against the manifest there.
58
+
59
+ ```bash
60
+ brainstem-nwb validate ./nwb/ [--manifest FILE] [--report-dir DIR] [--inspector-config dandi|none] [--fail-on warning]
61
+ ```
62
+
63
+ ## `fetch`
64
+
65
+ Downloads a project or session export to a file, so you can convert it later or offline.
66
+
67
+ ```bash
68
+ brainstem-nwb fetch --project UUID -o export.json.gz
69
+ brainstem-nwb fetch --session UUID -o session-export.json.gz
70
+ ```
71
+
72
+ ## `data-model`
73
+
74
+ ```bash
75
+ brainstem-nwb data-model [--url URL] [--refresh]
76
+ ```
77
+
78
+ Reads the BrainSTEM data model from the server (or the fresh cached copy), caches it, and shows the server, the data model generation and whether this release supports it, the export format, its checksum and the cache directory. Run it once before working offline. See [the BrainSTEM data model](install.md#the-brainstem-data-model).
79
+
80
+ ## `mapping` and `schema`
81
+
82
+ | Command | Prints |
83
+ |---|---|
84
+ | `brainstem-nwb mapping` | The mapping specification (Markdown). `--format json` prints the rules instead; `--warnings` prints the warning codes |
85
+ | `brainstem-nwb schema manifest` | The JSON Schema of the manifest |
86
+ | `brainstem-nwb schema report` | The JSON Schema of the validation report |
87
+
88
+ ## Exit codes
89
+
90
+ | Code | Meaning |
91
+ |---|---|
92
+ | 0 | Everything was exported and validated without errors |
93
+ | 1 | Partial export: some sessions could not be exported, or some files failed (see the manifest) |
94
+ | 2 | Usage error, invalid configuration, or unreadable or unsupported export file |
95
+ | 3 | Authentication or retrieval failure, or the data model could not be read and is not cached |
96
+ | 4 | Files were written, but validation found errors (or warnings, with `--fail-on warning`) |
97
+
98
+ If several apply, the highest code is returned.
99
+
100
+ ## Configuration
101
+
102
+ Some NWB fields have no source in a BrainSTEM export. They can be set in a TOML file:
103
+
104
+ ```toml
105
+ [nwbfile]
106
+ experimenter = ["Last, First", "Other, Person"]
107
+ experimenter_source = "config" # or "session_creator": the user who created the session record, when no
108
+ # experimenter is configured (BrainSTEM records who created a session, not who ran it)
109
+ lab = "My Lab"
110
+ institution = "My University"
111
+
112
+ [subject]
113
+ label_field = "name" # or "name_used_in_storage": used for subject_id and file names
114
+ species_format = "binomial" # or "ncbitaxon": NCBI Taxonomy IRI, preferred by DANDI
115
+ ```
@@ -0,0 +1,51 @@
1
+ # Architecture
2
+
3
+ This page is for contributors and for anyone embedding the exporter. It explains how `brainstem-nwb` is organized and how data moves through it. To change a mapping, see [Contributing](https://github.com/brainstem-org/brainstem-nwb/blob/main/CONTRIBUTING.md); the reasons behind the main choices are in [decisions.md](decisions.md).
4
+
5
+ ## Layers
6
+
7
+ The exporter has four layers, and each knows only its neighbours:
8
+
9
+ ```
10
+ retrieval ─► normalization ─► manifest (NwbPlan) ─► NWB writing ─► validation
11
+ ```
12
+
13
+ | Layer | Package | Input → output | Knows about |
14
+ |---|---|---|---|
15
+ | Retrieval | `retrieval/` | project id or file → `ProjectExport` | the BrainSTEM API tools, the export format |
16
+ | Normalization | `store/`, `normalize/` | `ProjectExport` → `RecordStore`, `ExportUnit`s | BrainSTEM records and links |
17
+ | Mapping | `mapping/` | `ExportUnit` → manifest `Unit` with an `NwbPlan` | BrainSTEM and NWB concepts, not HTTP or HDF5 |
18
+ | NWB writing | `nwb/` | `NwbPlan` → `.nwb` file | pynwb only, not BrainSTEM |
19
+ | Validation | `validation/` | `.nwb` (+ manifest unit) → report | pynwb, NWB Inspector, the manifest |
20
+
21
+ `export.py` puts the layers together. `cli.py` and the functions in `__init__.py` are thin wrappers around it.
22
+
23
+ ## Data flow
24
+
25
+ 1. **Retrieval.** `load_export()` reads a project export (format 2.x) from a file or a dict. `fetch_export()` gets one through `BrainstemClient.export_project()`. The export is the **only** source of BrainSTEM records. This keeps an export reproducible: the export file, the exporter version and the BrainSTEM data model (whose checksum the manifest records) determine the result.
26
+ 2. **Record store.** `RecordStore` indexes records by model label and primary key, and builds a reverse index of every foreign key and many-to-many link. The export does not record which model a link points to. That comes from the **inventory**: the BrainSTEM data model, read at run time from the server's reference documents (`store/datamodel.py`) and cached per server; brainstem-nwb ships no copy of it. A link to a record that is not in the export resolves to `Ref(reason="not_in_export")`; it is not an error.
27
+ 3. **Units.** `plan_units()` decides which files to write. An NWB file holds one subject, so each session gives one unit per subject. Every record of the session is attributed to the units it belongs to, using documented rules. Each decision is recorded as an `Attribution`, and sessions that cannot be exported become `unit_errors`.
28
+ 4. **Mapping.** `map_unit()` runs the mapper modules in order: session, project, people, protocols, subject, procedures, manipulations, acquisitions, devices, electrode groups, optogenetic sites, epochs and notes. Each mapper does two things:
29
+ - fills the `NwbPlan`;
30
+ - records every intended destination as a `FieldMapping` with a value state, and emits coded warnings.
31
+
32
+ Mapper functions declare their `MappingRule`s with `@rules(...)`. The registry generates the mapping specification and backs the coverage test.
33
+ 5. **Manifest.** The per-unit results, the source description, coverage and summary make up the `Manifest`. It is written as canonical JSON (`brainstem-nwb-manifest.json`), and a dry run stops here.
34
+ 6. **Writing.** `build_nwbfile(plan)` builds a pynwb `NWBFile` from the plan alone. `write_nwbfile()` writes it to a temporary name and moves it into place.
35
+ 7. **Validation.** Each file goes through `pynwb.validate`, NWB Inspector (DANDI configuration by default) and the BrainSTEM semantic checks, which compare the file with its manifest unit. Results go to `validation-report.json` and `.txt`.
36
+
37
+ ## Identity and reproducibility
38
+
39
+ - The NWB `identifier` is `uuid5(namespace, session id | subject id | mapping version | sha256(plan content))`.
40
+ - It is stable across re-exports of unchanged records.
41
+ - It changes when any written content changes.
42
+ - It ignores when the export was made.
43
+ - `--reproducible` (or `SOURCE_DATE_EPOCH`) takes the creation times from the export, which makes the manifest byte-identical across runs.
44
+ - HDF5 files themselves are not byte-identical (HDF5 object ids are random). Reproducibility tests therefore compare a canonical summary of each file (`nwb/canonical.py`).
45
+
46
+ ## Extension points
47
+
48
+ - **New mapping.** Add a mapper function with `@rules(...)` and call it from `mapping/pipeline.py`. The coverage test lists the BrainSTEM fields still unaccounted for.
49
+ - **New BrainSTEM fields or models.** The data model comes from the server, so new fields appear on their own; `tests/test_spec.py` lists the ones the mapping does not account for yet. New record types go into `EXPORT_LABELS`.
50
+ - **Raw data (Phase 4).** Implement `raw.base.RawDataConverter` and register it, either built in or through the `brainstem_nwb.raw_converters` entry-point group. See [phase4-raw-data.md](phase4-raw-data.md).
51
+ - **Web integration (Phase 5).** The server can call `build_manifest(export_dict)` and `export_project(export=export_dict, ...)` directly with the payload of `build_project_export`, with no HTTP involved.