From c2177a736a1d46ed0b7651eb60b1d4820947fb08 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Mon, 9 Jun 2025 17:10:25 -0700 Subject: [PATCH 1/2] use OrderedDict for ordering --- pylabrobot/resources/itemized_resource.py | 27 ++++++++++++----------- pylabrobot/resources/plate.py | 3 ++- pylabrobot/resources/tip_rack.py | 5 +++-- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/pylabrobot/resources/itemized_resource.py b/pylabrobot/resources/itemized_resource.py index 2e07aa2d06d..eaee79c97a6 100644 --- a/pylabrobot/resources/itemized_resource.py +++ b/pylabrobot/resources/itemized_resource.py @@ -1,3 +1,4 @@ +from collections import OrderedDict import sys from abc import ABCMeta from string import ascii_uppercase as LETTERS @@ -47,7 +48,7 @@ def __init__( size_y: float, size_z: float, ordered_items: Optional[Dict[str, T]] = None, - ordering: Optional[List[str]] = None, + ordering: Optional[OrderedDict[str, str]] = None, category: Optional[str] = None, model: Optional[str] = None, ): @@ -62,8 +63,8 @@ def __init__( :func:`pylabrobot.resources.create_ordered_items_2d`. If this is specified, `ordering` must be `None`. Keys must be in transposed MS Excel style notation, e.g. "A1" for the first item, "B1" for the item below that, "A2" for the item to the right, etc. - ordering: The order of the items on the resource. This is a list of identifiers. If this is - specified, `ordered_items` must be `None`. See `ordered_items` for the format of the + ordering: The order of the items on the resource. This is a dict of item identifier <> item name. + If this is specified, `ordered_items` must be `None`. See `ordered_items` for the format of the identifiers. category: The category of the resource. @@ -95,7 +96,7 @@ def __init__( raise ValueError("Item location must be specified if supplied at initialization.") item.name = f"{self.name}_{item.name}" # prefix item name with resource name self.assign_child_resource(item, location=item.location) - self._ordering = list(ordered_items.keys()) + self._ordering = OrderedDict((identifier, item.name) for identifier, item in ordered_items.items()) else: if ordering is None: raise ValueError("Must specify either `ordered_items` or `ordering`.") @@ -155,11 +156,11 @@ def __getitem__( if isinstance(identifier, (slice, range)): start, stop = identifier.start, identifier.stop if isinstance(identifier.start, str): - start = self._ordering.index(identifier.start) + start = list(self._ordering.keys()).index(identifier.start) elif identifier.start is None: start = 0 if isinstance(identifier.stop, str): - stop = self._ordering.index(identifier.stop) + stop = list(self._ordering.keys()).index(identifier.stop) elif identifier.stop is None: stop = self.num_items identifier = list(range(start, stop, identifier.step or 1)) @@ -188,7 +189,7 @@ def get_item(self, identifier: Union[str, int, Tuple[int, int]]) -> T: identifier = LETTERS[row] + str(column + 1) # standard transposed-Excel style notation if isinstance(identifier, str): try: - identifier = self._ordering.index(identifier) + identifier = list(self._ordering.keys()).index(identifier) except ValueError as e: raise IndexError( f"Item with identifier '{identifier}' does not exist on " f"resource '{self.name}'." @@ -413,17 +414,17 @@ def serialize(self) -> dict: def index_of_item(self, item: T) -> Optional[int]: """Return the index of the given item in the resource, or `None` if not found.""" - for i, i_item in enumerate(self.children): - if i_item == item: + for i, i_item_name in enumerate(self._ordering.values()): + if i_item_name == item.name: return i return None def get_child_identifier(self, item: T) -> str: """Get the identifier of the item.""" - index = self.index_of_item(item) - if index is None: - raise ValueError(f"Item {item} not found in resource.") - return self._ordering[index] + for identifier, i_item_name in self._ordering.items(): + if i_item_name == item.name: + return identifier + raise ValueError(f"Item {item} not found in resource.") def get_all_items(self) -> List[T]: """Get all items in the resource. Items are in a 1D list, starting from the top left and going diff --git a/pylabrobot/resources/plate.py b/pylabrobot/resources/plate.py index dc50bcafa4f..a7dfa6eb23b 100644 --- a/pylabrobot/resources/plate.py +++ b/pylabrobot/resources/plate.py @@ -1,5 +1,6 @@ from __future__ import annotations +from collections import OrderedDict from typing import ( TYPE_CHECKING, Dict, @@ -73,7 +74,7 @@ def __init__( size_y: float, size_z: float, ordered_items: Optional[Dict[str, "Well"]] = None, - ordering: Optional[List[str]] = None, + ordering: Optional[OrderedDict[str, str]] = None, category: str = "plate", lid: Optional[Lid] = None, model: Optional[str] = None, diff --git a/pylabrobot/resources/tip_rack.py b/pylabrobot/resources/tip_rack.py index 11566102feb..8f49e379755 100644 --- a/pylabrobot/resources/tip_rack.py +++ b/pylabrobot/resources/tip_rack.py @@ -1,6 +1,7 @@ from __future__ import annotations from abc import ABCMeta +from collections import OrderedDict from typing import Any, Dict, List, Optional, Sequence, Union, cast from pylabrobot.resources.coordinate import Coordinate @@ -120,7 +121,7 @@ def __init__( size_y: float, size_z: float, ordered_items: Optional[Dict[str, TipSpot]] = None, - ordering: Optional[List[str]] = None, + ordering: Optional[OrderedDict[str, str]] = None, category: str = "tip_rack", model: Optional[str] = None, with_tips: bool = True, @@ -230,7 +231,7 @@ def __init__( size_z: float, stacking_z_height: float, ordered_items: Optional[Dict[str, TipSpot]] = None, - ordering: Optional[List[str]] = None, + ordering: Optional[OrderedDict[str, str]] = None, category: str = "tip_rack", model: Optional[str] = None, with_tips: bool = True, From b3715224c1583f5e30dd49af7409a2de33e74550 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Mon, 9 Jun 2025 17:13:49 -0700 Subject: [PATCH 2/2] format --- pylabrobot/resources/itemized_resource.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pylabrobot/resources/itemized_resource.py b/pylabrobot/resources/itemized_resource.py index eaee79c97a6..b8b4b7f1db6 100644 --- a/pylabrobot/resources/itemized_resource.py +++ b/pylabrobot/resources/itemized_resource.py @@ -1,6 +1,6 @@ -from collections import OrderedDict import sys from abc import ABCMeta +from collections import OrderedDict from string import ascii_uppercase as LETTERS from typing import ( Dict, @@ -96,7 +96,9 @@ def __init__( raise ValueError("Item location must be specified if supplied at initialization.") item.name = f"{self.name}_{item.name}" # prefix item name with resource name self.assign_child_resource(item, location=item.location) - self._ordering = OrderedDict((identifier, item.name) for identifier, item in ordered_items.items()) + self._ordering = OrderedDict( + (identifier, item.name) for identifier, item in ordered_items.items() + ) else: if ordering is None: raise ValueError("Must specify either `ordered_items` or `ordering`.")