Source code for nexusLIMS.extractors.profiles
"""Instrument profile system for customizing extraction behavior.
This module provides a registry for instrument-specific extraction profiles,
enabling easy customization of metadata extraction for different microscopes
without modifying core extractor code.
The profile system is the key extensibility mechanism for NexusLIMS - each
installation has unique instruments, and profiles make it trivial to add
instrument-specific behavior.
"""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from nexusLIMS.db.models import Instrument
from nexusLIMS.extractors.base import InstrumentProfile
_logger = logging.getLogger(__name__)
__all__ = [
"InstrumentProfileRegistry",
"get_profile_registry",
]
[docs]
class InstrumentProfileRegistry:
"""
Registry for instrument-specific extraction profiles.
Manages registration and lookup of InstrumentProfile objects,
which customize extraction behavior for specific microscopes.
This is a singleton - use get_profile_registry() to access.
Examples
--------
Register a profile:
>>> from nexusLIMS.extractors.base import InstrumentProfile
>>> from nexusLIMS.extractors.profiles import get_profile_registry
>>>
>>> titan_profile = InstrumentProfile(
... instrument_id="FEI-Titan-STEM-630901",
... parsers={"microscope": parse_643_titan},
... static_metadata={"nx_meta.Facility": "NIST"}
... )
>>>
>>> registry = get_profile_registry()
>>> registry.register(titan_profile)
Retrieve a profile:
>>> from nexusLIMS.instruments import get_instr_from_filepath
>>> from pathlib import Path
>>>
>>> instrument = get_instr_from_filepath(Path("/path/to/file.dm3"))
>>> profile = registry.get_profile(instrument)
>>> if profile:
... print(f"Using custom profile for {instrument.name}")
"""
def __init__(self):
"""Initialize the profile registry."""
self._profiles: dict[str, InstrumentProfile] = {}
_logger.debug("Initialized InstrumentProfileRegistry")
[docs]
def register(self, profile: InstrumentProfile) -> None:
"""
Register an instrument profile.
Parameters
----------
profile
The profile to register
Raises
------
ValueError
If a profile with the same instrument_id is already registered
Examples
--------
>>> from nexusLIMS.extractors.base import InstrumentProfile
>>> profile = InstrumentProfile(instrument_id="FEI-Quanta-12345")
>>> registry = get_profile_registry()
>>> registry.register(profile)
"""
if profile.instrument_id in self._profiles:
_logger.warning(
"Replacing existing profile for instrument: %s",
profile.instrument_id,
)
self._profiles[profile.instrument_id] = profile
_logger.debug("Registered profile for: %s", profile.instrument_id)
[docs]
def get_profile(self, instrument: Instrument | None) -> InstrumentProfile | None:
"""
Get the profile for a specific instrument.
Parameters
----------
instrument
The instrument to look up, or None
Returns
-------
InstrumentProfile | None
The profile for this instrument, or None if no profile registered
Examples
--------
>>> from nexusLIMS.instruments import get_instr_from_filepath
>>> from pathlib import Path
>>>
>>> instrument = get_instr_from_filepath(Path("/path/to/file.dm3"))
>>> registry = get_profile_registry()
>>> profile = registry.get_profile(instrument)
>>> if profile:
... # Apply custom parsers
... for name, parser_func in profile.parsers.items():
... metadata = parser_func(metadata)
"""
if instrument is None:
return None
instrument_id = instrument.name
return self._profiles.get(instrument_id)
[docs]
def get_all_profiles(self) -> dict[str, InstrumentProfile]:
"""
Get all registered profiles.
Returns
-------
dict[str, InstrumentProfile]
Dictionary mapping instrument IDs to profiles
Examples
--------
>>> registry = get_profile_registry()
>>> all_profiles = registry.get_all_profiles()
>>> for instr_id, profile in all_profiles.items():
... print(f"{instr_id}: {len(profile.parsers)} custom parsers")
"""
return self._profiles.copy()
[docs]
def clear(self) -> None:
"""
Clear all registered profiles.
Primarily used for testing.
Examples
--------
>>> registry = get_profile_registry()
>>> registry.clear()
"""
self._profiles.clear()
_logger.debug("Cleared all instrument profiles")
# Singleton instance
_profile_registry: InstrumentProfileRegistry | None = None
[docs]
def get_profile_registry() -> InstrumentProfileRegistry:
"""
Get the global instrument profile registry (singleton).
Returns
-------
InstrumentProfileRegistry
The global profile registry instance
Examples
--------
>>> from nexusLIMS.extractors.profiles import get_profile_registry
>>> registry = get_profile_registry()
>>> # Always returns the same instance
>>> assert get_profile_registry() is registry
"""
global _profile_registry # noqa: PLW0603
if _profile_registry is None:
_profile_registry = InstrumentProfileRegistry()
return _profile_registry