What to build
Parse per-scene acquisition + instrument metadata from LIF scene XML and surface it in per_scene[i].acquisition (or the block name locked in ADR-0007) in both the convert audit and inspect() output.
Fields to extract
Per scene, mapping to Aperture BigQuery columns:
acquisition_date — timestamp of scene acquisition. LIF stores this in Attachment[TimeStampList] / TimeStamp elements on the scene.
microscope_model — brand + model of the instrument, e.g. "Stellaris", "SP8". LIF HardwareSetting / ATLConfocalSettingDefinition attributes carry this.
microscope_serial — instrument serial number (Aperture will map serial → user-facing microscope_name like "Snouty" on their side). Serial lives in HardwareSetting metadata.
imaging_method — normalised imaging modality (confocal, widefield_fluorescence, spinning_disk_confocal, light_sheet). LIF is nearly always confocal (from ATLConfocalSettingDefinition) but derive it, don't hardcode. Use the OME-conventional token set.
Where the values land
Per ADR-0007, values live under per_scene[i].acquisition (or the block name the ADR chooses) in the convert audit AND in each scene entry of inspect().scenes[i]. Missing values → key omitted from the block (no dummy zeros / empty strings). Missing block entirely → key absent from the scene entry.
Implementation notes
- Add extractors to
src/zarrmony/metadata/ — new module acquisition.py or extension of lif_channels.py depending on module-size preference at the time.
- Call site: the same
_lif_scene_channels code path in api.py walks scene XML for channels — extract acquisition alongside so we only parse once. Thread the returned dict into the scene record.
- Fail-safe like channel + objective extraction: any parse failure returns
None and conversion continues without the block (never crash over metadata).
- Bump
AUDIT_SCHEMA_VERSION.
Acceptance criteria
Blocked by
Follow-up (linked separately)
CZI, ND2, and default (OME-TIFF etc.) reader paths get parallel acquisition + instrument extraction as their own issues, subsuming the earlier #54/#55/#56 objective-only slices.
What to build
Parse per-scene acquisition + instrument metadata from LIF scene XML and surface it in
per_scene[i].acquisition(or the block name locked in ADR-0007) in both the convert audit andinspect()output.Fields to extract
Per scene, mapping to Aperture BigQuery columns:
acquisition_date— timestamp of scene acquisition. LIF stores this inAttachment[TimeStampList]/TimeStampelements on the scene.microscope_model— brand + model of the instrument, e.g."Stellaris","SP8". LIFHardwareSetting/ATLConfocalSettingDefinitionattributes carry this.microscope_serial— instrument serial number (Aperture will map serial → user-facingmicroscope_namelike"Snouty"on their side). Serial lives inHardwareSettingmetadata.imaging_method— normalised imaging modality (confocal,widefield_fluorescence,spinning_disk_confocal,light_sheet). LIF is nearly alwaysconfocal(fromATLConfocalSettingDefinition) but derive it, don't hardcode. Use the OME-conventional token set.Where the values land
Per ADR-0007, values live under
per_scene[i].acquisition(or the block name the ADR chooses) in the convert audit AND in each scene entry ofinspect().scenes[i]. Missing values → key omitted from the block (no dummy zeros / empty strings). Missing block entirely → key absent from the scene entry.Implementation notes
src/zarrmony/metadata/— new moduleacquisition.pyor extension oflif_channels.pydepending on module-size preference at the time._lif_scene_channelscode path inapi.pywalks scene XML for channels — extract acquisition alongside so we only parse once. Thread the returned dict into the scene record.Noneand conversion continues without the block (never crash over metadata).AUDIT_SCHEMA_VERSION.Acceptance criteria
per_scene[i].acquisitionpopulated on a Stellaris LIF fixture with all four fields.inspect().scenes[i].AUDIT_SCHEMA_VERSIONbumped; audit.py docstring updated.Blocked by
Follow-up (linked separately)
CZI, ND2, and default (OME-TIFF etc.) reader paths get parallel acquisition + instrument extraction as their own issues, subsuming the earlier #54/#55/#56 objective-only slices.