Source code for alab_management.sample_view.sample
"""The definition of the Sample and SamplePosition classes."""
import os
from dataclasses import dataclass, field
from typing import Any, ClassVar
from bson import ObjectId # type: ignore
[docs]
@dataclass(frozen=True)
class Sample:
"""
Basic sample object.
- ``sample_id``: the unique id of a sample in the database
- ``task_id``: the unique id of a task that currently "owns" (is processing) this sample
- ``name``: the name of this sample
- ``position``: current position of the sample in the lab. if None, the sample has not been initialized
in the lab
"""
sample_id: ObjectId
task_id: ObjectId | None
name: str
position: str | None
metadata: dict[str, Any] = field(default_factory=dict)
tags: list[str] = field(default_factory=list)
# When a sample is being physically moved, ``in_transit`` records where it is coming from and
# going to, e.g. {"source": ..., "destination": ..., "started_at": ...}. It is None whenever the
# sample is at rest. compare/hash are disabled so existing Sample equality semantics are preserved.
in_transit: dict[str, Any] | None = field(
default=None, compare=False, hash=False
)
# ``last_position`` always holds the most recently recorded physical location of the sample. Unlike
# ``position`` (which becomes None when the sample leaves the lab / a position), this is never
# cleared once set, so the "last known location" is always available. compare/hash are disabled to
# preserve existing Sample equality semantics.
last_position: str | None = field(default=None, compare=False, hash=False)
[docs]
@dataclass(frozen=True)
class SamplePosition:
"""
A sample position in the lab.
Sample position is a position in the lab that can hold sample,
it is not a geographic coordinate in the lab, but a defined
position in the lab
- ``name``: the name of this sample position, which is the unique
identifier of a sample position
- ``description``: a string that describes the sample position briefly
"""
SEPARATOR: ClassVar[str] = "/"
name: str
number: int = field(default=1)
description: str = field(default="", compare=False, hash=False)
def __post_init__(self):
"""Check if the number of sample position is valid."""
if self.number < 0:
raise ValueError(
f"{self.number} is an invalid number of sample positions! The number of sample position ({self.name}) "
f"must be >= 0."
)
# _standalone_sample_position_registry is used to store all the standalone sample positions that are defined in the __init__.py
# when setting up the lab.
# it is only pooled with new sample positions that are added to the lab during reload.
_standalone_sample_position_registry: dict[str, SamplePosition] = {}
# _current_standalone_sample_position_registry is used to store
# all the standalone sample positions that are defined in the __init__.py
# when alabos setup is called, regardless of whether it is a reload or not.
# this is used to check if a sample position is still in the lab during reload.
# if not, it will be removed from the lab once unoccupied and devices that are related to it are also not occupied.
# this is not visible to the sample_view
_current_standalone_sample_position_registry: dict[str, SamplePosition] = {}
[docs]
def add_standalone_sample_position(position: SamplePosition):
"""Register a device instance."""
if not isinstance(position, SamplePosition):
raise TypeError(
f"The type of position should be SamplePosition, but user provided {type(position)}"
)
if position.name in _standalone_sample_position_registry and not os.environ.get(
"ALABOS_RELOAD", None
):
raise KeyError(f"Duplicated standalone sample position name {position.name}")
_standalone_sample_position_registry[position.name] = position
_current_standalone_sample_position_registry[position.name] = position
[docs]
def get_all_standalone_sample_positions() -> dict[str, SamplePosition]:
"""Get all the sample position names in the registry."""
return _standalone_sample_position_registry.copy()
[docs]
def get_current_standalone_sample_positions() -> dict[str, SamplePosition]:
"""Get all the current standalone sample positions in the registry."""
return _current_standalone_sample_position_registry.copy()
[docs]
def remove_standalone_sample_position(prefix: str):
"""Remove a standalone sample position from the registry."""
_standalone_sample_position_registry.pop(prefix, None)
_current_standalone_sample_position_registry.pop(prefix, None)
[docs]
def reset_current_standalone_sample_position_registry():
"""Reset the current standalone sample position registry."""
_current_standalone_sample_position_registry.clear()