Skip to content

Add MIAPPE metadata with an editor and legacy migration - #116

Merged
jlegrand62 merged 31 commits into
devfrom
feature/metadata
Sep 24, 2026
Merged

jlegrand62 merged 31 commits into
devfrom
feature/metadata

Conversation

@jlegrand62

@jlegrand62 jlegrand62 commented Sep 7, 2026 •

Copy link
Copy Markdown
Member

Summary

Adds MIAPPE-aligned biological metadata support to PlantDB: a write-time schema/validator in the commons layer, CLI migration for legacy scans, and a web UI in the metadata editor to view, edit, and migrate scan metadata.

Changes

  • commons: MIAPPE biological metadata schema (investigation, study, biological material, observed variable) with type-checked write-time validation.
  • CLI: legacy metadata migration (fsdb_migrate_metadata, now with backup / --no-backup) and images.json cleanup commands.
  • server: scan info now exposes MIAPPE-aligned biological metadata.
  • client: new metadata editor web UI — load an FSDB, migrate legacy scans (with streamed progress), bulk or single-scan edit with per-field MIAPPE tooltips, plus Help/About modals.
  • docs: added developers/miappe_metadata.md (schema mapping & design) and wired it into the mkdocs nav.

What's new since the last update

  • fsdb_migrate_metadata writes a .bak backup by default and gained a --no-backup option.
  • Editor I/O now routes through the FSDB Scan API (get_metadata/set_metadata) instead of raw path manipulation, so it transparently handles both flat and nested (timelapse) scans.
  • Scan metadata is loaded before filesets so nested timelapse scan paths resolve correctly.
  • Cleaned up duplicated metadata-loading utilities; fixed load_database to return 7 values.

Notes

  • Legacy pre-MIAPPE scans are detected and migrated from the UI before editing; the migration is idempotent.
  • Docs reference updated from metadata_miappe_plan.md → miappe_metadata.md.

…alidation

Define the canonical MIAPPE-aligned biological metadata structure for Scan
and validate it at the create and metadata-write boundaries (metadata.py,
core.py). The block is optional; when present, known fields are type-checked.
…CLIs

Add fsdb_migrate_metadata (one-time conversion of legacy Metadata.object to
the MIAPPE tree, idempotent) and fsdb_clean_images_metadata (removes the
now-redundant biological/hardware entries duplicated in images.json). Drop
the legacy images.json fallback read in _load_scan_metadata.
get_scan_info now reads the canonical biologicalMaterial/study structure for
species, plant and environment instead of the legacy Metadata.object block.
Update scan API tests accordingly.
Add a Dash web UI to view, edit and bulk-fill MIAPPE-aligned biological
metadata of local FSDB scans, with per-field specs, flatten/unflatten helpers
and write-back with schema validation. Wire up the metadata_app CLI entry
point.
Drop support for loose scan directories; a database root must now be a proper
ROMI DB. Surface a clearer NotAnFSDBError message from FSDB.connect.
…r UI

Auto-load the DB from --db-path, flag scans still on the legacy object
schema, and add a one-click 'Migrate now' action in the editor. Add
scan_needs_migration/migratable_scans/migrate_scans helpers in db_ops.
diskcache backs Dash background callbacks (used by the metadata editor for
streaming migration progress). Ignore the .dashcache runtime directory.
Wrap list_scans in try/finally so the database connection is released even on
error, matching other call sites.
Cache connected FSDB instances per database path (connect is expensive) with
thread-safe access, close_db to switch databases, and atexit cleanup. Add
migrate_scans_progress to report per-scan progress to a caller.
…gration modal

Restructure the layout (FSDB location + About on top, scan selection beside a
single field-edit form), merging the separate per-scan and bulk-edit flows into
one form driven by the edit-accordion mode. Move migration behind a modal with
a streaming progress bar via a DiskCache background callback, auto-closing on
completion. Close cached DBs when switching databases.
Add Bootstrap icons to card headers, accordion and MIAPPE section titles, move
field tooltips to a question-mark icon on each row, and add app.css to size the
field labels consistently.
@jlegrand62 jlegrand62 self-assigned this Sep 7, 2026
@jlegrand62 jlegrand62 added the enhancement New feature or request label Sep 7, 2026
Comment thread src/commons/plantdb/commons/fsdb/metadata.py
Comment thread src/client/plantdb/client/metadata_app/app.py Outdated
Comment thread src/commons/plantdb/commons/cli/fsdb_migrate_metadata.py
- Remove duplicated `_load_fileset_metadata` implementation and relocate it after scan helper.
- Simplify `_load_scan_metadata` to directly return `_load_metadata`.
- Update `file_ops` to use `_load_scan_metadata` and `_fileset_metadata_path` for images metadata.
Stop reading and writing metadata.json via raw path manipulation in the
editor helpers. Go through the Scan API (get_metadata/set_metadata) so the
editor transparently handles both flat and nested (timelapse) scans.

- replace load_db with all_scan_metadata (get_scans + get_metadata)
- read/write_scan_metadata now take a Scan; write uses _scan_metadata_path
  and set_metadata, keeping the .bak backup and MIAPPE validation
- apply_bulk, scan_needs_migration, migratable_scans use the Scan API
- drop the _SCAN_METADATA_REL constant and the dead migrate_scans wrapper
- remove now-unused read_scan_metadata and the json import
- fix a NameError in apply_edit by importing _connect in app.py
- update tests for the new API and drop the migration-wrapper test
…imelapse scans

In _load_scan_at, populate scan.metadata before loading filesets so
_scan_path can resolve nested (timelapse) scan paths from the timelapse id.
Also import Fileset locally for the legacy images.json metadata merge.

Rewrite test_dual_read_scans to build a member scan through the timelapse
API (create_timelapse/create_scan/set_metadata) instead of hand-crafting
the on-disk layout.
Remove a stray leading space before the >>> prompt so the doctest runs.
- New requirement for Open3D.
- Updated `docker/Dockerfile` to install `libusb-1.0-0` alongside existing Open3D system dependencies (`libegl1`, `libgl1`, `libgomp1`) in both the runtime and build stages.
- Update `metadata.py` to call `_fileset_metadata_json_path` instead of `_fileset_metadata_path` when loading fileset metadata.
- In `file_ops.py`, extract possible `timelapse_id` from the scan path and pass it to `Scan`.
- Extend `Scan.__init__` in `core.py` to accept `timelapse_id=None`, auto‑create the timelapse if needed, and store it in metadata.
- Update doctest examples in `core.py` to showcase initializing a `Scan` with a `timelapse_id`.
- Add safe fallback when the path does not contain a timelapse component.
… tests

- Add `test_fsdb_reload_timelapse_scan` in `src/commons/tests/test_fsdb.py` to verify `FSDB.reload()` correctly loads a scan nested inside a timelapse container without raising an exception.
- Add `test_scan_timelapse_id_parameter` to ensure `Scan(timelapse_id=…)` creates the timelapse, registers it in the DB, and stores the correct metadata.
- Add `test_scan_timelapse_id_none` to confirm that creating a `Scan` without a `timelapse_id` results in empty timelapse metadata.
- Update `src/commons/plantdb/commons/fsdb/file_ops.py`:
  - Refactor directory listing for readability.
  - Store scans in the result dict by `scan.id` instead of directory name.
  - Adjust error reporting to use actual child directory names.
  - Remove the now‑unused `scan_id` argument from `_load_scan_at` and update its signature.
  - Resolve scan paths with `Path(...).resolve()` and extract `timelapse_id`/`scan_id` from the path relative to the DB root.
- Add unit tests in `src/commons/tests/test_file_ops.py`:
  - `test_load_scan_at_success` verifies that a scan can be loaded from a valid explicit path and that its `id` matches.
  - `test_load_scan_at_invalid_path` ensures that loading from a non‑scan path returns `None`.
- Replace `self.set_metadata("timelapse", {"id": timelapse_id})` with `self.metadata["timelapse"] = {"id": timelapse_id}` in `src/commons/plantdb/commons/fsdb/core.py`.
@jlegrand62
jlegrand62 merged commit d6d73a5 into dev Sep 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants