From 73c20d78d8f06924861d233b37c758b66b72f419 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 16 Jan 2023 13:52:05 +0100 Subject: [PATCH 01/54] add evo_aspirate() and evo_aspirate_well() --- robotools/evotools/__init__.py | 111 +++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 6c6a9fa..50d118a 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -511,6 +511,79 @@ def aspirate_well( ) return + def evo_aspirate_well( + self, + labware: liquidhandling.Labware, + wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], + *, + labware_position: tuple[int, int], + volume: typing.Union[float, list[float]], + liquid_class: str, + tips: typing.Union[typing.List[Tip], typing.List[int]] + ) -> None: + """Command for aspirating with the EvoWARE aspirate command. As many wells in one column may be selected as your liquid handling arm has pipettes. + This method generates the full command (as can be observed when opening a .esc file with an editor) and calls upon other functions to create the code string + specifying the target wells. + + Parameters + ---------- + labware : liquidhandling.Labware + Source labware + wells : list of str + List with target well ID(s) + labware_position : tuple + Grid position of the target labware on the robotic deck and site position on its carrier, e.g. labware on grid 38, site 2 -> (38,2) + volume : float or list + Volume in microliters (will be rounded to 2 decimal places); if several tips are used, these tips may aspirate individual volumes -> use list in these cases + liquid_class : str, optional + Overwrites the liquid class for this step (max 32 characters) + tips : list + Tip(s) that will be selected; use either a list with integers from 1 - 8 or with tip.T1 - tip.T8 + """ + # perform consistency checks + args = ( + labware, + wells, + labware_position, + volume, + liquid_class, + tips, + ) + ( + labware, + wells, + labware_position, + volume, + liquid_class, + tips, + ) = _prepare_evo_aspirate_dispense_parameters(*args, max_volume=self.max_volume) + + # calculate tip_selection based on tips argument + tip_selection = 0 + for tip in tips: + tip_selection += tip.value + + # prepare volume section (handle both the case of one volume as int and a list of individual volumes per tip) + tip_volumes = "" + if type(volume) == list: + volume_dict = dict(zip(tips,volume)) + for tip in tips: + if tip.value in [1,2,4,8,16,32,64,128]: + if type(volume) == float: + tip_volumes += f"\"{volume}\"," + elif type(volume) == list: + tip_volumes += f"\"{volume_dict[tip]}\"," + else: + tip_volumes += "0," + + # convert selection from list of well ids to numpy array with same dimensions as target labware (1: well is selected, 0: well is not selected) + selected = evo_make_selection_array(labware.n_rows, labware.n_columns, wells) + # create code string containing information about target well(s) + code_string = evo_get_selection(labware.n_rows, labware.n_columns, selected) + self.append( + f"B;Aspirate({tip_selection},\"{liquid_class}\",{tip_volumes}0,0,0,0,{labware_position[0]},{labware_position[1]},1,\"{code_string}\",0,0);" + ) + return def dispense_well( self, rack_label: str, @@ -734,6 +807,44 @@ def aspirate( self.aspirate_well(labware.name, labware.positions[well], volume, **kwargs) return + def evo_aspirate( + self, + labware: liquidhandling.Labware, + wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], + labware_position: tuple[int, int], + tips: typing.Union[typing.List[Tip], typing.List[int]], + volumes: typing.Union[float, typing.Sequence[float], numpy.ndarray], + liquid_class: str, + *, + label: typing.Optional[str] = None, + # **kwargs, + ) -> None: + """Performs aspiration from the provided labware. Is identical to the aspirate command inside the EvoWARE. + Thus, several wells in a single column can be targeted. + + Parameters + ---------- + labware : liquidhandling.Labware + Source labware + labware_position : tuple + Grid position of the target labware on the robotic deck and site position on its carrier, e.g. labware on grid 38, site 2 -> (38,2) + wells : list of str or iterable + List with target well ID(s) + tips : list + Tip(s) that will be selected; use either a list with integers from 1 - 8 or with tip.T1 - tip.T8 + volumes : float or iterable + Volume(s) in microliters (will be rounded to 2 decimal places); if several tips are used, these tips may aspirate individual volumes -> use list in these cases + liquid_class : str, optional + Overwrites the liquid class for this step (max 32 characters) + """ + wells = numpy.array(wells).flatten("F") + volumes = numpy.array(volumes).flatten("F") + if len(volumes) == 1: + volumes = numpy.repeat(volumes, len(wells)) + labware.remove(wells, volumes, label) + self.comment(label) + self.evo_aspirate_well(labware, wells, labware_position, volumes, liquid_class, tips) + return def dispense( self, labware: liquidhandling.Labware, From 3a149473f8e7b05157c3b40940e2937b462a42da Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 16 Jan 2023 13:53:01 +0100 Subject: [PATCH 02/54] add evo_dispense() and evo_dispense_well() --- robotools/evotools/__init__.py | 203 +++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 50d118a..6e276c7 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -651,6 +651,171 @@ def dispense_well( f"D;{rack_label};{rack_id};{rack_type};{position};{tube_id};{volume};{liquid_class};{tip_type};{tip};{forced_rack_type}" ) return + + def evo_dispense_well( + self, + labware: liquidhandling.Labware, + wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], + labware_position: tuple[int, int], + volume: typing.Union[float, list[float]], + liquid_class: str, + tips: typing.Union[typing.List[Tip], typing.List[int]] + ) -> None: + """Command for dispensing using the EvoWARE dispense command. As many wells in one column may be selected as your liquid handling arm has pipettes. + This method generates the full command (as can be observed when opening a .esc file with an editor) and calls upon other functions to create the code string + specifying the target wells. + + Parameters + ---------- + labware : liquidhandling.Labware + Source labware + wells : list of str + List with target well ID(s) + labware_position : tuple + Grid position of the target labware on the robotic deck and site position on its carrier, e.g. labware on grid 38, site 2 -> (38,2) + volume : float or list + Volume in microliters (will be rounded to 2 decimal places); if several tips are used, these tips may aspirate individual volumes -> use list in these cases + liquid_class : str, optional + Overwrites the liquid class for this step (max 32 characters) + tips : list + Tip(s) that will be selected; use either a list with integers from 1 - 8 or with tip.T1 - tip.T8 + """ + # perform consistency checks + args = ( + labware, + wells, + labware_position, + volume, + liquid_class, + tips, + ) + ( + labware, + wells, + labware_position, + volume, + liquid_class, + tips, + ) = _prepare_evo_aspirate_dispense_parameters(*args, max_volume=self.max_volume) + + # calculate tip_selection based on tips argument + tip_selection = 0 + for tip in tips: + tip_selection += tip.value + + # prepare volume section (handle both the case of one volume as int and a list of individual volumes per tip) + tip_volumes = "" + if type(volume) == list: + volume_dict = dict(zip(tips,volume)) + for tip in tips: + if tip.value in [1,2,4,8,16,32,64,128]: + if type(volume) == float: + tip_volumes += f"\"{volume}\"," + elif type(volume) == list: + tip_volumes += f"\"{volume_dict[tip]}\"," + else: + tip_volumes += "0," + + # convert selection from list of well ids to numpy array with same dimensions as target labware (1: well is selected, 0: well is not selected) + selected = evo_make_selection_array(labware.n_rows, labware.n_columns, wells) + # create code string containing information about target well(s) + code_string = evo_get_selection(labware.n_rows, labware.n_columns, selected) + self.append( + f"B;Dispense({tip_selection},\"{liquid_class}\",{tip_volumes}0,0,0,0,{labware_position[0]},{labware_position[1]},1,\"{code_string}\",0,0);" + ) + return + + def evo_wash( + self, + *, + tips: typing.Union[typing.List[Tip], typing.List[int]], + waste_location: tuple[int, int], + cleaner_location: tuple[int, int], + arm: int = 0, + waste_vol: float = 3.0, + waste_delay: int = 500, + cleaner_vol: float = 4.0, + cleaner_delay: int = 500, + airgap: float = 10, + airgap_speed: int = 70, + retract_speed: int = 30, + fastwash: int = 1, + low_volume: int = 0, + ) -> None: + """Command for aspirating with the EvoWARE aspirate command. As many wells in one column may be selected as your liquid handling arm has pipettes. + This method generates the full command (as can be observed when opening a .esc file with an editor) and calls upon other functions to create the code string + specifying the target wells. + + Parameters + ---------- + tips : list + Tip(s) that will be selected; use either a list with integers from 1 - 8 or with tip.T1 - tip.T8 + waste_location : tuple + Tuple with grid position (1-67) and site number (0-127) of waste as integers + cleaner_location : tuple + Tuple with grid position (1-67) and site number (0-127) of cleaner as integers + arm : int + number of the LiHa performing the action: 0 = LiHa 1, 1 = LiHa 2 + waste_vol: float + Volume in waste in mL (0-100) + waste_delay : int + Delay before closing valves in waste in ms (0-1000) + cleaner_vol: float + Volume in cleaner in mL (0-100) + cleaner_delay : int + Delay before closing valves in cleaner in ms (0-1000) + airgap : float + Volume of airgap in µL which is aspirated after washing the tips (system trailing airgap) (0-100) + airgap_speed : int + Speed of airgap aspiration in µL/s (1-1000) + retract_speed : int + Retract speed in mm/s (1-100) + fastwash : int + Use fast-wash module = 1, don't use it = 0 + low_volume : int + Use pinch valves = 1, don't use them = 0 + """ + + # perform consistency checks + args = ( + tips, + waste_location, + cleaner_location, + arm, + waste_vol, + waste_delay, + cleaner_vol, + cleaner_delay, + airgap, + airgap_speed, + retract_speed, + fastwash, + low_volume, + ) + ( + tips, + waste_location, + cleaner_location, + arm, + waste_vol, + waste_delay, + cleaner_vol, + cleaner_delay, + airgap, + airgap_speed, + retract_speed, + fastwash, + low_volume, + ) = _prepare_evo_wash_parameters(*args) + # calculate tip_selection based on tips argument + tip_selection = 0 + for tip in tips: + tip_selection += tip.value + + self.append( + f"B;Wash({tip_selection},{waste_location[0]},{waste_location[1]},{cleaner_location[0]},{cleaner_location[1]},\"{waste_vol}\",{waste_delay},\"{cleaner_vol}\",{cleaner_delay},{airgap},{airgap_speed},{retract_speed},{fastwash},{low_volume},1000,{arm});" + ) + return def reagent_distribution( self, @@ -884,6 +1049,44 @@ def dispense( if volume > 0: self.dispense_well(labware.name, labware.positions[well], volume, **kwargs) return + def evo_dispense( + self, + labware: liquidhandling.Labware, + wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], + labware_position: tuple[int, int], + tips: typing.Union[typing.List[Tip], typing.List[int]], + volumes: typing.Union[float, typing.Sequence[float], numpy.ndarray], + liquid_class: str, + *, + label: typing.Optional[str] = None, + # **kwargs, + ) -> None: + """Performs dispensation from the provided labware. Is identical to the dispense command inside the EvoWARE. + Thus, several wells in a single column can be targeted. + + Parameters + ---------- + labware : liquidhandling.Labware + Source labware + labware_position : tuple + Grid position of the target labware on the robotic deck and site position on its carrier, e.g. labware on grid 38, site 2 -> (38,2) + wells : list of str or iterable + List with target well ID(s) + tips : list + Tip(s) that will be selected; use either a list with integers from 1 - 8 or with tip.T1 - tip.T8 + volumes : float or iterable + Volume(s) in microliters (will be rounded to 2 decimal places); if several tips are used, these tips may aspirate individual volumes -> use list in these cases + liquid_class : str, optional + Overwrites the liquid class for this step (max 32 characters) + """ + wells = numpy.array(wells).flatten("F") + volumes = numpy.array(volumes).flatten("F") + if len(volumes) == 1: + volumes = numpy.repeat(volumes, len(wells)) + labware.remove(wells, volumes, label) + self.comment(label) + self.evo_dispense_well(labware, wells, labware_position, volumes, liquid_class, tips) + return def transfer( self, From 1ede648a142d4c1c954a1c7e365a6ecfe1c0db7e Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 16 Jan 2023 13:54:18 +0100 Subject: [PATCH 03/54] add _prepare_evo_aspirate_dispense_parameters( --- robotools/evotools/__init__.py | 142 +++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 6e276c7..d0248b6 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -180,6 +180,146 @@ def _int_to_tip(tip_int: int): tip = "" if tip == -1 else tip return rack_label, position, volume, liquid_class, tip, rack_id, tube_id, rack_type, forced_rack_type +def _prepare_evo_aspirate_dispense_parameters( + labware: liquidhandling.Labware, + wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], + *, + labware_position: typing.Tuple[int, int], + volume: typing.Union[float, typing.List[float]], + liquid_class: str, + tips: typing.Union[typing.List[Tip], typing.List[int]], + max_volume: typing.Optional[int] = None, +) -> typing.Tuple[str, list, tuple, float, str, list]: + """Validates and prepares aspirate/dispense parameters. + + Parameters + ---------- + labware : liquidhandling.Labware + Source labware + wells : list of str + List with target well ID(s) + labware_position : tuple + Grid position of the target labware on the robotic deck and site position on its carrier, e.g. labware on grid 38, site 2 -> (38,2) + volume : float or list + Volume in microliters (will be rounded to 2 decimal places); if several tips are used, these tips may aspirate individual volumes -> use list in these cases + liquid_class : str, optional + Overwrites the liquid class for this step (max 32 characters) + tips : list of int + Tip(s) that will be selected (out of tips 1-8) + max_volume : int, optional + Maximum allowed volume + + Returns + ------- + labware : liquidhandling.Labware + Source labware + wells : list of str + List with target well ID(s) + labware_position : tuple + Grid position of the target labware on the robotic deck and site position on its carrier, e.g. labware on grid 38, site 2 -> (38,2) + volume : float or list + Volume in microliters (will be rounded to 2 decimal places); if several tips are used, these tips may aspirate individual volumes -> use list in these cases + liquid_class : str, optional + Overwrites the liquid class for this step (max 32 characters) + tips : list of int + Tip(s) that will be selected (out of tips 1-8) + """ + # required parameters + if labware is None: + raise ValueError("Missing required paramter: labware") + if not isinstance(labware, liquidhandling.Labware): + raise ValueError(f"Invalid labware: {labware}") + + if labware_position is None: + raise ValueError("Missing required paramter: position") + if not all(isinstance(position, int) for position in labware_position) or any(position < 0 for position in labware_position): + raise ValueError(f"Invalid position: {labware_position}") + + if volume is None: + raise ValueError("Missing required parameter: volume") + if type(volume) == list: + for vol in volume: + try: + vol = float(vol) + except: + raise ValueError(f"Invalid volume: {vol}") + if vol < 0 or vol > 7158278 or numpy.isnan(vol): + raise ValueError(f"Invalid volume: {vol}") + if max_volume is not None and vol > max_volume: + raise InvalidOperationError(f"Volume of {vol} exceeds max_volume.") + if not len(vol) == len(tips): + raise Exception(f"Tips and volume lists have different lengths ({len(tips)} and {len(vol)}, respectively).") + else: + try: + volume = float(volume) + except: + raise ValueError(f"Invalid volume: {volume}") + if volume < 0 or volume > 7158278 or numpy.isnan(volume): + raise ValueError(f"Invalid volume: {volume}") + if max_volume is not None and volume > max_volume: + raise InvalidOperationError(f"Volume of {volume} exceeds max_volume.") + + # optional parameters + if not isinstance(liquid_class, str) or ";" in liquid_class: + raise ValueError(f"Invalid liquid_class: {liquid_class}") + + def _int_to_tip(tip_int: int): + """Asserts a Tecan Tip class to an int between 1 and 8.""" + if not 1 <= tip_int <= 8: + raise ValueError( + f"Tip is {tip} with type {type(tip)}, but should be an int between 1 and 8 for _int_to_tip conversion." + ) + if tip_int == 1: + return Tip.T1 + elif tip_int == 2: + return Tip.T2 + elif tip_int == 3: + return Tip.T3 + elif tip_int == 4: + return Tip.T4 + elif tip_int == 5: + return Tip.T5 + elif tip_int == 6: + return Tip.T6 + elif tip_int == 7: + return Tip.T7 + elif tip_int == 8: + return Tip.T8 + + tecan_tips = [] + for tip in tips: + if isinstance(tip, int) and not isinstance(tip, Tip): + # User-specified integers from 1-8 need to be converted to Tecan logic + tip = _int_to_tip(tip) + tecan_tips.append(tip) + + if isinstance(tip, collections.abc.Iterable): + tips = [] + for element in tip: + if isinstance(element, int) and not isinstance(element, Tip): + tips.append(_int_to_tip(element)) + elif isinstance(element, Tip): + if element == -1: + raise ValueError( + "When Iterables are used, no Tip.Any elements are allowed. Pass just one Tip.Any instead." + ) + tips.append(element) + else: + raise ValueError( + f"If tip is an Iterable, it may only contain int or Tip values, not {type(element)}." + ) + tip = sum(set(tips)) + elif not isinstance(tip, Tip): + raise ValueError(f"tip must be an int between 1 and 8, Tip or Iterable, but was {type(tip)}.") + + # apply rounding and corrections for the right string formatting + volume = f"{numpy.round(volume, decimals=2):.2f}" + tips = ["" if tip == -1 else tip for tip in tips] + if tecan_tips: + return labware, wells, labware_position, volume, liquid_class, tecan_tips + else: + return labware, wells, labware_position, volume, liquid_class, tips + def _optimize_partition_by( source: liquidhandling.Labware, @@ -1010,6 +1150,7 @@ def evo_aspirate( self.comment(label) self.evo_aspirate_well(labware, wells, labware_position, volumes, liquid_class, tips) return + def dispense( self, labware: liquidhandling.Labware, @@ -1049,6 +1190,7 @@ def dispense( if volume > 0: self.dispense_well(labware.name, labware.positions[well], volume, **kwargs) return + def evo_dispense( self, labware: liquidhandling.Labware, From b8ad4dc2765c0e505bc9c8147bf889ed98b068bb Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 16 Jan 2023 13:54:46 +0100 Subject: [PATCH 04/54] add Evoware pippeting command code string generation, co-authored by Martin Beiss --- robotools/evotools/__init__.py | 85 ++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index d0248b6..d297481 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -448,6 +448,91 @@ def _partition_by_column( ) return column_groups +def toHex( + dec : int +): + """Method from stackoverflow to convert decimal to hex. + """ + digits = "0123456789ABCDEF" + x = (dec % 16) + rest = dec // 16 + if (rest == 0): + return digits[x] + return toHex(rest) + digits[x] + +def evo_make_selection_array( + rows:int, + columns:int, + wells: numpy.ndarray +): + """Translate well IDs to a numpy array with 1s (selected) and 0s (not selected). + + Parameters + ---------- + rows : int + Number of rows of target labware object + cols : int + Number of columns of target labware object + wells : List[str] + Selected wells by well IDs as strings (e.g. ["A01", "B01"]) + + Returns + ------- + selection_array : numpy.ndarray + Numpy array in labware dimensions with selected wells as 1 and others as 0 + """ + # create array with a shape beffiting the labware dimensions + selection_array = numpy.zeros((rows, columns)) + # get a dictionary with the "coordinates" of well IDs (A01, B01 etc) as tuples + well_index_dict = transform.make_well_index_dict(rows, columns) + # insert 1s for all selected wells + for well in wells: + selection_array[well_index_dict[well]] = 1 + return selection_array + +def evo_get_selection(rows:int, cols:int, selected): + """Function to generate the code string for the well selection of pipetting actions in EvoWare scripts (.esc). + Adapted from the C++ function detailed in the EvoWare manual to Python by Martin Beyß (except the test at the end). + + Parameters + ---------- + selected : numpy.ndarray + Numpy array in labware dimensions with selected wells as 1 and others as 0 (from evo_make_selection_array) + rows : int + Number of rows of target labware object + cols : int + Number of columns of target labware object + + Returns + ------- + selection : str + Code string for well selection of pipetting actions in EvoWare scripts (.esc) + """ + # apply bit mask with 7 bits, adapted from function detailed in EvoWare manual + selection = f"0{toHex(cols)}{rows:02d}" + bit_counter = 0 + bit_mask = 0 + for x in range(cols): + for y in range(rows): + if selected[y,x] == 1: + bit_mask |= 1< 6: + selection += chr(bit_mask + 48) + bit_counter = 0 + bit_mask = 0 + if bit_counter > 0: + selection += chr(bit_mask + 48) + + # check if wells from more than one column are selected and raise Exception if so + check = 0 + for column in selected.transpose(): + if sum(column) >= 1: + check+=1 + if check >= 2: + raise Exception("Wells from more than one column are selected.\nSelect only wells from one column per pipetting action.") + + return selection class Worklist(list): """Context manager for the creation of Worklists.""" From 811b2df6651edee975a6bc4eb9f92da6bccf7c4a Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 16 Jan 2023 13:56:20 +0100 Subject: [PATCH 05/54] change encoding of save() --- robotools/evotools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index d297481..87269b0 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -577,7 +577,7 @@ def save(self, filepath: str) -> None: assert ".gwl" in filepath.lower(), "The filename did not contain the .gwl extension." if os.path.exists(filepath): os.remove(filepath) - with open(filepath, "w", newline="\r\n", encoding="latin-1") as file: + with open(filepath, "w", newline="\r\n", encoding="latin_1") as file: file.write("\n".join(self)) return From 539c4573ba04e4c5719706f65e03d06365b9d045 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 16 Jan 2023 13:57:13 +0100 Subject: [PATCH 06/54] add _prepare_evo_wash_parameters() --- robotools/evotools/__init__.py | 189 +++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 87269b0..55b5339 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -320,6 +320,194 @@ def _int_to_tip(tip_int: int): else: return labware, wells, labware_position, volume, liquid_class, tips +def _prepare_evo_wash_parameters( + tips: typing.Union[typing.List[Tip], typing.List[int]], + waste_location: tuple[int, int], + cleaner_location: tuple[int, int], + arm: int, + waste_vol: float, + waste_delay: int, + cleaner_vol: float, + cleaner_delay: int, + airgap: float, + airgap_speed: int, + retract_speed: int, + fastwash: int, + low_volume: int, +) -> typing.Tuple[list, tuple, tuple, int, float, int, float, int, float, int, int, int, int]: + """Validates and prepares aspirate/dispense parameters. + + Parameters + ---------- + tips : list + Tip(s) that will be selected; use either a list with integers from 1 - 8 or with tip.T1 - tip.T8 + waste_location : tuple + Tuple with grid position (1-67) and site number (0-127) of waste as integers + cleaner_location : tuple + Tuple with grid position (1-67) and site number (0-127) of cleaner as integers + arm : int + number of the LiHa performing the action: 0 = LiHa 1, 1 = LiHa 2 + waste_vol: float + Volume in waste in mL (0-100) + waste_delay : int + Delay before closing valves in waste in ms (0-1000) + cleaner_vol: float + Volume in cleaner in mL (0-100) + cleaner_delay : int + Delay before closing valves in cleaner in ms (0-1000) + airgap : float + Volume of airgap in µL which is aspirated after washing the tips (system trailing airgap) (0-100) + airgap_speed : int + Speed of airgap aspiration in µL/s (1-1000) + retract_speed : int + Retract speed in mm/s (1-100) + fastwash : int + Use fast-wash module = 1, don't use it = 0 + low_volume : int + Use pinch valves = 1, don't use them = 0 + + Returns + ------- + tips : list + Tip(s) that will be selected; have been converted to tip.T1 - tip.T8 here if they weren't originally formatted that way + waste_location : tuple + Tuple with grid position (1-67) and site number (0-127) of waste as integers + cleaner_location : tuple + Tuple with grid position (1-67) and site number (0-127) of cleaner as integers + arm : int + number of the LiHa performing the action: 0 = LiHa 1, 1 = LiHa 2 + waste_vol: float + Volume in waste in mL (0-100) + waste_delay : int + Delay before closing valves in waste in ms (0-1000) + cleaner_vol: float + Volume in cleaner in mL (0-100) + cleaner_delay : int + Delay before closing valves in cleaner in ms (0-1000) + airgap : float + Volume of airgap in µL which is aspirated after washing the tips (system trailing airgap) (0-100) + airgap_speed : int + Speed of airgap aspiration in µL/s (1-1000) + retract_speed : int + Retract speed in mm/s (1-100) + fastwash : int + Use fast-wash module = 1, don't use it = 0 + low_volume : int + Use pinch valves = 1, don't use them = 0 + """ + if tips is None: + raise ValueError("Missing required parameter: tips") + + def _int_to_tip(tip_int: int): + """Asserts a Tecan Tip class to an int between 1 and 8.""" + if not 1 <= tip_int <= 8: + raise ValueError( + f"Tip is {tip} with type {type(tip)}, but should be an int between 1 and 8 for _int_to_tip conversion." + ) + if tip_int == 1: + return Tip.T1 + elif tip_int == 2: + return Tip.T2 + elif tip_int == 3: + return Tip.T3 + elif tip_int == 4: + return Tip.T4 + elif tip_int == 5: + return Tip.T5 + elif tip_int == 6: + return Tip.T6 + elif tip_int == 7: + return Tip.T7 + elif tip_int == 8: + return Tip.T8 + + tecan_tips = [] + for tip in tips: + if isinstance(tip, int) and not isinstance(tip, Tip): + # User-specified integers from 1-8 need to be converted to Tecan logic + tip = _int_to_tip(tip) + tecan_tips.append(tip) + + if waste_location is None: + raise ValueError("Missing required parameter: waste_location") + for grid, site in waste_location: + if not isinstance(grid, int) or not 1 <= grid <= 67: + raise ValueError("Grid (first number in waste_location tuple) has to be an int from 1 - 67.") + if not isinstance(site, int) or not 0 <= site <= 127: + raise ValueError("Site (second number in waste_location tuple) has to be an int from 0 - 127.") + + if cleaner_location is None: + raise ValueError("Missing required parameter: cleaner_location") + for grid, site in cleaner_location: + if not isinstance(grid, int) or not 1 <= grid <= 67: + raise ValueError("Grid (first number in cleaner_location tuple) has to be an int from 1 - 67.") + if not isinstance(site, int) or not 0 <= site <= 127: + raise ValueError("Site (second number in cleaner_location tuple) has to be an int from 0 - 127.") + + if arm is None: + raise ValueError("Missing required paramter: arm") + if not isinstance(arm, int): + raise ValueError("Parameter arm is not int.") + if not arm == 0 and not arm == 1: + raise ValueError("Parameter arm has to be 0 (LiHa 1) or 1 (LiHa 2).") + + if waste_vol is None: + raise ValueError("Missing required parameter: waste_vol") + if not isinstance(waste_vol, float) or not 0 <= waste_vol <= 100: + raise ValueError("waste_vol has to be a float from 0 - 100.") + # round waste_vol to the first decimal (pre-requisite for Tecan's wash command) + waste_vol = numpy.round(waste_vol,1) + + if waste_delay is None: + raise ValueError("Missing required parameter: waste_delay") + if not isinstance(waste_delay, int) or not 0 <= waste_delay <= 1000: + raise ValueError("waste_delay has to be a float from 0 - 1000.") + + if cleaner_vol is None: + raise ValueError("Missing required parameter: cleaner_vol") + if not isinstance(cleaner_vol, float) or not 0 <= cleaner_vol <= 100: + raise ValueError("cleaner_vol has to be a float from 0 - 100.") + # round cleaner_vol to the first decimal (pre-requisite for Tecan's wash command) + cleaner_vol = numpy.round(cleaner_vol,1) + + if cleaner_delay is None: + raise ValueError("Missing required parameter: cleaner_delay") + if not isinstance(cleaner_delay, int) or not 0 <= cleaner_delay <= 1000: + raise ValueError("cleaner_delay has to be a float from 0 - 1000.") + + if airgap is None: + raise ValueError("Missing required parameter: airgap") + if not isinstance(airgap, float) or not 0 <= airgap <= 100: + raise ValueError("airgap has to be a float from 0 - 100.") + + if airgap_speed is None: + raise ValueError("Missing required parameter: airgap_speed") + if not isinstance(airgap_speed, int) or not 1 <= airgap_speed <= 1000: + raise ValueError("airgap_speed has to be a float from 1 - 1000.") + + if retract_speed is None: + raise ValueError("Missing required parameter: retract_speed") + if not isinstance(retract_speed, int) or not 1 <= retract_speed <= 100: + raise ValueError("retract_speed has to be a float from 1 - 100.") + + if fastwash is None: + raise ValueError("Missing required paramter: fastwash") + if not isinstance(fastwash, int): + raise ValueError("Parameter fastwash is not int.") + if not fastwash == 0 and not fastwash == 1: + raise ValueError("Parameter fastwash has to be 0 (no fast-wash) or 1 (use fast-wash).") + + if low_volume is None: + raise ValueError("Missing required paramter: low_volume") + if not isinstance(low_volume, int): + raise ValueError("Parameter low_volume is not int.") + if not low_volume == 0 and not low_volume == 1: + raise ValueError("Parameter low_volume has to be 0 (no fast-wash) or 1 (use fast-wash).") + + if tecan_tips: + return tecan_tips, waste_location, cleaner_location, arm, waste_vol, waste_delay, cleaner_vol, cleaner_delay, airgap, airgap_speed, retract_speed, fastwash, low_volume + else: + return tips, waste_location, cleaner_location, arm, waste_vol, waste_delay, cleaner_vol, cleaner_delay, airgap, airgap_speed, retract_speed, fastwash, low_volume def _optimize_partition_by( source: liquidhandling.Labware, @@ -809,6 +997,7 @@ def evo_aspirate_well( f"B;Aspirate({tip_selection},\"{liquid_class}\",{tip_volumes}0,0,0,0,{labware_position[0]},{labware_position[1]},1,\"{code_string}\",0,0);" ) return + def dispense_well( self, rack_label: str, From 324a89cc37ba60f4b1d476719753d7515d4097bd Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 16 Jan 2023 13:57:47 +0100 Subject: [PATCH 07/54] add import transform --- robotools/evotools/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 55b5339..bf501ff 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -10,6 +10,7 @@ import numpy from .. import liquidhandling +from .. import transform logger = logging.getLogger("evotools") From 5a774deb9ad3dc1af278fbea59c701da96efc362 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 16 Jan 2023 18:26:22 +0100 Subject: [PATCH 08/54] apply pre-commit changes --- robotools/evotools/__init__.py | 280 +++++++++++++++++++++++++-------- 1 file changed, 218 insertions(+), 62 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index bf501ff..d4267d5 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -9,8 +9,7 @@ import numpy -from .. import liquidhandling -from .. import transform +from .. import liquidhandling, transform logger = logging.getLogger("evotools") @@ -181,6 +180,7 @@ def _int_to_tip(tip_int: int): tip = "" if tip == -1 else tip return rack_label, position, volume, liquid_class, tip, rack_id, tube_id, rack_type, forced_rack_type + def _prepare_evo_aspirate_dispense_parameters( labware: liquidhandling.Labware, wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], @@ -233,7 +233,9 @@ def _prepare_evo_aspirate_dispense_parameters( if labware_position is None: raise ValueError("Missing required paramter: position") - if not all(isinstance(position, int) for position in labware_position) or any(position < 0 for position in labware_position): + if not all(isinstance(position, int) for position in labware_position) or any( + position < 0 for position in labware_position + ): raise ValueError(f"Invalid position: {labware_position}") if volume is None: @@ -249,7 +251,9 @@ def _prepare_evo_aspirate_dispense_parameters( if max_volume is not None and vol > max_volume: raise InvalidOperationError(f"Volume of {vol} exceeds max_volume.") if not len(vol) == len(tips): - raise Exception(f"Tips and volume lists have different lengths ({len(tips)} and {len(vol)}, respectively).") + raise Exception( + f"Tips and volume lists have different lengths ({len(tips)} and {len(vol)}, respectively)." + ) else: try: volume = float(volume) @@ -286,7 +290,7 @@ def _int_to_tip(tip_int: int): return Tip.T7 elif tip_int == 8: return Tip.T8 - + tecan_tips = [] for tip in tips: if isinstance(tip, int) and not isinstance(tip, Tip): @@ -321,6 +325,7 @@ def _int_to_tip(tip_int: int): else: return labware, wells, labware_position, volume, liquid_class, tips + def _prepare_evo_wash_parameters( tips: typing.Union[typing.List[Tip], typing.List[int]], waste_location: tuple[int, int], @@ -398,7 +403,7 @@ def _prepare_evo_wash_parameters( """ if tips is None: raise ValueError("Missing required parameter: tips") - + def _int_to_tip(tip_int: int): """Asserts a Tecan Tip class to an int between 1 and 8.""" if not 1 <= tip_int <= 8: @@ -436,7 +441,7 @@ def _int_to_tip(tip_int: int): raise ValueError("Grid (first number in waste_location tuple) has to be an int from 1 - 67.") if not isinstance(site, int) or not 0 <= site <= 127: raise ValueError("Site (second number in waste_location tuple) has to be an int from 0 - 127.") - + if cleaner_location is None: raise ValueError("Missing required parameter: cleaner_location") for grid, site in cleaner_location: @@ -444,7 +449,7 @@ def _int_to_tip(tip_int: int): raise ValueError("Grid (first number in cleaner_location tuple) has to be an int from 1 - 67.") if not isinstance(site, int) or not 0 <= site <= 127: raise ValueError("Site (second number in cleaner_location tuple) has to be an int from 0 - 127.") - + if arm is None: raise ValueError("Missing required paramter: arm") if not isinstance(arm, int): @@ -457,7 +462,7 @@ def _int_to_tip(tip_int: int): if not isinstance(waste_vol, float) or not 0 <= waste_vol <= 100: raise ValueError("waste_vol has to be a float from 0 - 100.") # round waste_vol to the first decimal (pre-requisite for Tecan's wash command) - waste_vol = numpy.round(waste_vol,1) + waste_vol = numpy.round(waste_vol, 1) if waste_delay is None: raise ValueError("Missing required parameter: waste_delay") @@ -469,13 +474,13 @@ def _int_to_tip(tip_int: int): if not isinstance(cleaner_vol, float) or not 0 <= cleaner_vol <= 100: raise ValueError("cleaner_vol has to be a float from 0 - 100.") # round cleaner_vol to the first decimal (pre-requisite for Tecan's wash command) - cleaner_vol = numpy.round(cleaner_vol,1) + cleaner_vol = numpy.round(cleaner_vol, 1) if cleaner_delay is None: raise ValueError("Missing required parameter: cleaner_delay") if not isinstance(cleaner_delay, int) or not 0 <= cleaner_delay <= 1000: raise ValueError("cleaner_delay has to be a float from 0 - 1000.") - + if airgap is None: raise ValueError("Missing required parameter: airgap") if not isinstance(airgap, float) or not 0 <= airgap <= 100: @@ -490,7 +495,7 @@ def _int_to_tip(tip_int: int): raise ValueError("Missing required parameter: retract_speed") if not isinstance(retract_speed, int) or not 1 <= retract_speed <= 100: raise ValueError("retract_speed has to be a float from 1 - 100.") - + if fastwash is None: raise ValueError("Missing required paramter: fastwash") if not isinstance(fastwash, int): @@ -504,11 +509,40 @@ def _int_to_tip(tip_int: int): raise ValueError("Parameter low_volume is not int.") if not low_volume == 0 and not low_volume == 1: raise ValueError("Parameter low_volume has to be 0 (no fast-wash) or 1 (use fast-wash).") - + if tecan_tips: - return tecan_tips, waste_location, cleaner_location, arm, waste_vol, waste_delay, cleaner_vol, cleaner_delay, airgap, airgap_speed, retract_speed, fastwash, low_volume + return ( + tecan_tips, + waste_location, + cleaner_location, + arm, + waste_vol, + waste_delay, + cleaner_vol, + cleaner_delay, + airgap, + airgap_speed, + retract_speed, + fastwash, + low_volume, + ) else: - return tips, waste_location, cleaner_location, arm, waste_vol, waste_delay, cleaner_vol, cleaner_delay, airgap, airgap_speed, retract_speed, fastwash, low_volume + return ( + tips, + waste_location, + cleaner_location, + arm, + waste_vol, + waste_delay, + cleaner_vol, + cleaner_delay, + airgap, + airgap_speed, + retract_speed, + fastwash, + low_volume, + ) + def _optimize_partition_by( source: liquidhandling.Labware, @@ -637,28 +671,23 @@ def _partition_by_column( ) return column_groups -def toHex( - dec : int -): - """Method from stackoverflow to convert decimal to hex. - """ + +def toHex(dec: int): + """Method from stackoverflow to convert decimal to hex.""" digits = "0123456789ABCDEF" - x = (dec % 16) + x = dec % 16 rest = dec // 16 - if (rest == 0): + if rest == 0: return digits[x] return toHex(rest) + digits[x] -def evo_make_selection_array( - rows:int, - columns:int, - wells: numpy.ndarray -): + +def evo_make_selection_array(rows: int, columns: int, wells: numpy.ndarray): """Translate well IDs to a numpy array with 1s (selected) and 0s (not selected). Parameters ---------- - rows : int + rows : int Number of rows of target labware object cols : int Number of columns of target labware object @@ -679,19 +708,20 @@ def evo_make_selection_array( selection_array[well_index_dict[well]] = 1 return selection_array -def evo_get_selection(rows:int, cols:int, selected): - """Function to generate the code string for the well selection of pipetting actions in EvoWare scripts (.esc). + +def evo_get_selection(rows: int, cols: int, selected): + """Function to generate the code string for the well selection of pipetting actions in EvoWare scripts (.esc). Adapted from the C++ function detailed in the EvoWare manual to Python by Martin Beyß (except the test at the end). Parameters ---------- selected : numpy.ndarray Numpy array in labware dimensions with selected wells as 1 and others as 0 (from evo_make_selection_array) - rows : int + rows : int Number of rows of target labware object cols : int Number of columns of target labware object - + Returns ------- selection : str @@ -703,8 +733,8 @@ def evo_get_selection(rows:int, cols:int, selected): bit_mask = 0 for x in range(cols): for y in range(rows): - if selected[y,x] == 1: - bit_mask |= 1< 6: selection += chr(bit_mask + 48) @@ -717,12 +747,15 @@ def evo_get_selection(rows:int, cols:int, selected): check = 0 for column in selected.transpose(): if sum(column) >= 1: - check+=1 + check += 1 if check >= 2: - raise Exception("Wells from more than one column are selected.\nSelect only wells from one column per pipetting action.") + raise Exception( + "Wells from more than one column are selected.\nSelect only wells from one column per pipetting action." + ) return selection + class Worklist(list): """Context manager for the creation of Worklists.""" @@ -933,10 +966,10 @@ def evo_aspirate_well( labware_position: tuple[int, int], volume: typing.Union[float, list[float]], liquid_class: str, - tips: typing.Union[typing.List[Tip], typing.List[int]] + tips: typing.Union[typing.List[Tip], typing.List[int]], ) -> None: """Command for aspirating with the EvoWARE aspirate command. As many wells in one column may be selected as your liquid handling arm has pipettes. - This method generates the full command (as can be observed when opening a .esc file with an editor) and calls upon other functions to create the code string + This method generates the full command (as can be observed when opening a .esc file with an editor) and calls upon other functions to create the code string specifying the target wells. Parameters @@ -976,17 +1009,17 @@ def evo_aspirate_well( tip_selection = 0 for tip in tips: tip_selection += tip.value - + # prepare volume section (handle both the case of one volume as int and a list of individual volumes per tip) - tip_volumes = "" + tip_volumes = "" if type(volume) == list: - volume_dict = dict(zip(tips,volume)) + volume_dict = dict(zip(tips, volume)) for tip in tips: - if tip.value in [1,2,4,8,16,32,64,128]: + if tip.value in [1, 2, 4, 8, 16, 32, 64, 128]: if type(volume) == float: - tip_volumes += f"\"{volume}\"," + tip_volumes += f'"{volume}",' elif type(volume) == list: - tip_volumes += f"\"{volume_dict[tip]}\"," + tip_volumes += f'"{volume_dict[tip]}",' else: tip_volumes += "0," @@ -995,7 +1028,7 @@ def evo_aspirate_well( # create code string containing information about target well(s) code_string = evo_get_selection(labware.n_rows, labware.n_columns, selected) self.append( - f"B;Aspirate({tip_selection},\"{liquid_class}\",{tip_volumes}0,0,0,0,{labware_position[0]},{labware_position[1]},1,\"{code_string}\",0,0);" + f'B;Aspirate({tip_selection},"{liquid_class}",{tip_volumes}0,0,0,0,{labware_position[0]},{labware_position[1]},1,"{code_string}",0,0);' ) return @@ -1066,7 +1099,7 @@ def dispense_well( f"D;{rack_label};{rack_id};{rack_type};{position};{tube_id};{volume};{liquid_class};{tip_type};{tip};{forced_rack_type}" ) return - + def evo_dispense_well( self, labware: liquidhandling.Labware, @@ -1074,10 +1107,10 @@ def evo_dispense_well( labware_position: tuple[int, int], volume: typing.Union[float, list[float]], liquid_class: str, - tips: typing.Union[typing.List[Tip], typing.List[int]] + tips: typing.Union[typing.List[Tip], typing.List[int]], ) -> None: """Command for dispensing using the EvoWARE dispense command. As many wells in one column may be selected as your liquid handling arm has pipettes. - This method generates the full command (as can be observed when opening a .esc file with an editor) and calls upon other functions to create the code string + This method generates the full command (as can be observed when opening a .esc file with an editor) and calls upon other functions to create the code string specifying the target wells. Parameters @@ -1117,17 +1150,17 @@ def evo_dispense_well( tip_selection = 0 for tip in tips: tip_selection += tip.value - + # prepare volume section (handle both the case of one volume as int and a list of individual volumes per tip) - tip_volumes = "" + tip_volumes = "" if type(volume) == list: - volume_dict = dict(zip(tips,volume)) + volume_dict = dict(zip(tips, volume)) for tip in tips: - if tip.value in [1,2,4,8,16,32,64,128]: + if tip.value in [1, 2, 4, 8, 16, 32, 64, 128]: if type(volume) == float: - tip_volumes += f"\"{volume}\"," + tip_volumes += f'"{volume}",' elif type(volume) == list: - tip_volumes += f"\"{volume_dict[tip]}\"," + tip_volumes += f'"{volume_dict[tip]}",' else: tip_volumes += "0," @@ -1136,10 +1169,10 @@ def evo_dispense_well( # create code string containing information about target well(s) code_string = evo_get_selection(labware.n_rows, labware.n_columns, selected) self.append( - f"B;Dispense({tip_selection},\"{liquid_class}\",{tip_volumes}0,0,0,0,{labware_position[0]},{labware_position[1]},1,\"{code_string}\",0,0);" + f'B;Dispense({tip_selection},"{liquid_class}",{tip_volumes}0,0,0,0,{labware_position[0]},{labware_position[1]},1,"{code_string}",0,0);' ) return - + def evo_wash( self, *, @@ -1158,7 +1191,7 @@ def evo_wash( low_volume: int = 0, ) -> None: """Command for aspirating with the EvoWARE aspirate command. As many wells in one column may be selected as your liquid handling arm has pipettes. - This method generates the full command (as can be observed when opening a .esc file with an editor) and calls upon other functions to create the code string + This method generates the full command (as can be observed when opening a .esc file with an editor) and calls upon other functions to create the code string specifying the target wells. Parameters @@ -1226,9 +1259,9 @@ def evo_wash( tip_selection = 0 for tip in tips: tip_selection += tip.value - + self.append( - f"B;Wash({tip_selection},{waste_location[0]},{waste_location[1]},{cleaner_location[0]},{cleaner_location[1]},\"{waste_vol}\",{waste_delay},\"{cleaner_vol}\",{cleaner_delay},{airgap},{airgap_speed},{retract_speed},{fastwash},{low_volume},1000,{arm});" + f'B;Wash({tip_selection},{waste_location[0]},{waste_location[1]},{cleaner_location[0]},{cleaner_location[1]},"{waste_vol}",{waste_delay},"{cleaner_vol}",{cleaner_delay},{airgap},{airgap_speed},{retract_speed},{fastwash},{low_volume},1000,{arm});' ) return @@ -1401,7 +1434,7 @@ def evo_aspirate( ) -> None: """Performs aspiration from the provided labware. Is identical to the aspirate command inside the EvoWARE. Thus, several wells in a single column can be targeted. - + Parameters ---------- labware : liquidhandling.Labware @@ -1465,7 +1498,7 @@ def dispense( if volume > 0: self.dispense_well(labware.name, labware.positions[well], volume, **kwargs) return - + def evo_dispense( self, labware: liquidhandling.Labware, @@ -1480,7 +1513,7 @@ def evo_dispense( ) -> None: """Performs dispensation from the provided labware. Is identical to the dispense command inside the EvoWARE. Thus, several wells in a single column can be targeted. - + Parameters ---------- labware : liquidhandling.Labware @@ -1505,6 +1538,129 @@ def evo_dispense( self.evo_dispense_well(labware, wells, labware_position, volumes, liquid_class, tips) return + def evo_transfer( + self, + source: liquidhandling.Labware, + source_wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], + source_position: tuple[int, int], + destination: liquidhandling.Labware, + destination_wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], + destination_position: tuple[int, int], + volumes: typing.Union[float, typing.Sequence[float], numpy.ndarray], + tips: typing.Union[typing.List[Tip], typing.List[int]], + liquid_class: str, + *, + label: typing.Optional[str] = None, + # wash_scheme: int = 1, + partition_by: str = "auto", + **kwargs, + ) -> None: + """Transfer operation between two labwares. + + Parameters + ---------- + source : liquidhandling.Labware + Source labware + source_wells : str or iterable + List of source well ids + source_position : tuple + Tuple with position of source labware (grid and site as integers) + destination : liquidhandling.Labware + Destination labware + destination_wells : str or iterable + List of destination well ids + destination_position : tuple + Tuple with position of destination labware (grid and site as integers) + volumes : float or iterable + Volume(s) to transfer + tips: list + Tips with which to perform the transfer + liquid_class : str + Liquid class to use for aspirate and dispense. + label : str + Label of the operation to log into labware history + partition_by : str + one of 'auto' (default), 'source' or 'destination' + 'auto': partitioning by source unless the source is a Trough + 'source': partitioning by source columns + 'destination': partitioning by destination columns + kwargs + Additional keyword arguments to pass to aspirate and dispense. + Take a look at `Worklist.aspirate_well` for the full list of options. + """ + # reformat the convenience parameters + source_wells = numpy.array(source_wells).flatten("F") + destination_wells = numpy.array(destination_wells).flatten("F") + volumes = numpy.array(volumes).flatten("F") + nmax = max((len(source_wells), len(destination_wells), len(volumes))) + + if len(source_wells) == 1: + source_wells = numpy.repeat(source_wells, nmax) + if len(destination_wells) == 1: + destination_wells = numpy.repeat(destination_wells, nmax) + if len(volumes) == 1: + volumes = numpy.repeat(volumes, nmax) + lengths = (len(source_wells), len(destination_wells), len(volumes)) + assert ( + len(set(lengths)) == 1 + ), f"Number of source/destination/volumes must be equal. They were {lengths}" + + # automatic partitioning + partition_by = _optimize_partition_by(source, destination, partition_by, label) + + # the label applies to the entire transfer operation and is not logged at individual aspirate/dispense steps + self.comment(label) + nsteps = 0 + lvh_extra = 0 + + for srcs, dsts, vols in _partition_by_column(source_wells, destination_wells, volumes, partition_by): + # make vector of volumes into vector of volume-lists + vols = [ + _partition_volume(float(v), max_volume=self.max_volume) if self.auto_split else [v] + for v in vols + ] + # transfer from this source column until all wells are done + npartitions = max(map(len, vols)) + # Count only the extra steps created by LVH + lvh_extra += sum([len(vs) - 1 for vs in vols]) + for p in range(npartitions): + naccessed = 0 + # iterate the rows + for s, d, vs in zip(srcs, dsts, vols): + # transfer the next volume-fraction for this well + if len(vs) > p: + v = vs[p] + if v > 0: + self.evo_aspirate(source, s, source_position, v, liquid_class, tips, label=None) + self.evo_dispense( + destination, d, destination_position, v, liquid_class, tips, label=None + ) + nsteps += 1 + # if wash_scheme is not None: + # self.wash(scheme=wash_scheme) + naccessed += 1 + # LVH: if multiple wells are accessed, don't group across partitions + if npartitions > 1 and naccessed > 1 and not p == npartitions - 1: + self.commit() + # LVH: don't group across columns + if npartitions > 1: + self.commit() + + # Condense the labware logs into one operation + # after the transfer operation completed to facilitate debugging. + # Also include the number of extra steps because of LVH if applicable. + if lvh_extra: + if label: + label = f"{label} ({lvh_extra} LVH steps)" + else: + label = f"{lvh_extra} LVH steps" + if destination == source: + source.condense_log(nsteps * 2, label=label) + else: + source.condense_log(nsteps, label=label) + destination.condense_log(nsteps, label=label) + return + def transfer( self, source: liquidhandling.Labware, From df2bbe82efa49ebc4d0988b18065b01f1154f26c Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 16 Jan 2023 18:32:18 +0100 Subject: [PATCH 09/54] fix typing.Tuple[] error --- robotools/evotools/__init__.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index d4267d5..7026bf6 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -328,8 +328,8 @@ def _int_to_tip(tip_int: int): def _prepare_evo_wash_parameters( tips: typing.Union[typing.List[Tip], typing.List[int]], - waste_location: tuple[int, int], - cleaner_location: tuple[int, int], + waste_location: typing.Tuple[int, int], + cleaner_location: typing.Tuple[int, int], arm: int, waste_vol: float, waste_delay: int, @@ -963,7 +963,7 @@ def evo_aspirate_well( labware: liquidhandling.Labware, wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], *, - labware_position: tuple[int, int], + labware_position: typing.Tuple[int, int], volume: typing.Union[float, list[float]], liquid_class: str, tips: typing.Union[typing.List[Tip], typing.List[int]], @@ -1104,7 +1104,7 @@ def evo_dispense_well( self, labware: liquidhandling.Labware, wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], - labware_position: tuple[int, int], + labware_position: typing.Tuple[int, int], volume: typing.Union[float, list[float]], liquid_class: str, tips: typing.Union[typing.List[Tip], typing.List[int]], @@ -1177,8 +1177,8 @@ def evo_wash( self, *, tips: typing.Union[typing.List[Tip], typing.List[int]], - waste_location: tuple[int, int], - cleaner_location: tuple[int, int], + waste_location: typing.Tuple[int, int], + cleaner_location: typing.Tuple[int, int], arm: int = 0, waste_vol: float = 3.0, waste_delay: int = 500, @@ -1424,7 +1424,7 @@ def evo_aspirate( self, labware: liquidhandling.Labware, wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], - labware_position: tuple[int, int], + labware_position: typing.Tuple[int, int], tips: typing.Union[typing.List[Tip], typing.List[int]], volumes: typing.Union[float, typing.Sequence[float], numpy.ndarray], liquid_class: str, @@ -1503,7 +1503,7 @@ def evo_dispense( self, labware: liquidhandling.Labware, wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], - labware_position: tuple[int, int], + labware_position: typing.Tuple[int, int], tips: typing.Union[typing.List[Tip], typing.List[int]], volumes: typing.Union[float, typing.Sequence[float], numpy.ndarray], liquid_class: str, @@ -1542,10 +1542,10 @@ def evo_transfer( self, source: liquidhandling.Labware, source_wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], - source_position: tuple[int, int], + source_position: typing.Tuple[int, int], destination: liquidhandling.Labware, destination_wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], - destination_position: tuple[int, int], + destination_position: typing.Tuple[int, int], volumes: typing.Union[float, typing.Sequence[float], numpy.ndarray], tips: typing.Union[typing.List[Tip], typing.List[int]], liquid_class: str, From 90d08606400ed6019f3cb72cf277b8a74e061d21 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 16 Jan 2023 18:37:36 +0100 Subject: [PATCH 10/54] fix typing.List[] errors --- robotools/evotools/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 7026bf6..7367190 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -964,7 +964,7 @@ def evo_aspirate_well( wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], *, labware_position: typing.Tuple[int, int], - volume: typing.Union[float, list[float]], + volume: typing.Union[float, typing.List[float]], liquid_class: str, tips: typing.Union[typing.List[Tip], typing.List[int]], ) -> None: @@ -1105,7 +1105,7 @@ def evo_dispense_well( labware: liquidhandling.Labware, wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], labware_position: typing.Tuple[int, int], - volume: typing.Union[float, list[float]], + volume: typing.Union[float, typing.List[float]], liquid_class: str, tips: typing.Union[typing.List[Tip], typing.List[int]], ) -> None: From 4242079776aef8dcd49ad0f5a9ea94fdcec624cc Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Tue, 17 Jan 2023 15:06:33 +0100 Subject: [PATCH 11/54] extract _int_to_tip() method --- robotools/evotools/__init__.py | 91 ++++++++-------------------------- 1 file changed, 22 insertions(+), 69 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 7367190..eb09fcd 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -35,6 +35,28 @@ class Tip(enum.IntEnum): class InvalidOperationError(Exception): pass +def _int_to_tip(tip_int: int): + """Asserts a Tecan Tip class to an int between 1 and 8.""" + if not 1 <= tip_int <= 8: + raise ValueError( + f"Tip is {tip_int} with type {type(tip_int)}, but should be an int between 1 and 8 for _int_to_tip conversion." + ) + if tip_int == 1: + return Tip.T1 + elif tip_int == 2: + return Tip.T2 + elif tip_int == 3: + return Tip.T3 + elif tip_int == 4: + return Tip.T4 + elif tip_int == 5: + return Tip.T5 + elif tip_int == 6: + return Tip.T6 + elif tip_int == 7: + return Tip.T7 + elif tip_int == 8: + return Tip.T8 def _prepare_aspirate_dispense_parameters( rack_label: str, @@ -122,29 +144,6 @@ def _prepare_aspirate_dispense_parameters( if not isinstance(liquid_class, str) or ";" in liquid_class: raise ValueError(f"Invalid liquid_class: {liquid_class}") - def _int_to_tip(tip_int: int): - """Asserts a Tecan Tip class to an int between 1 and 8.""" - if not 1 <= tip_int <= 8: - raise ValueError( - f"Tip is {tip} with type {type(tip)}, but should be an int between 1 and 8 for _int_to_tip conversion." - ) - if tip_int == 1: - return Tip.T1 - elif tip_int == 2: - return Tip.T2 - elif tip_int == 3: - return Tip.T3 - elif tip_int == 4: - return Tip.T4 - elif tip_int == 5: - return Tip.T5 - elif tip_int == 6: - return Tip.T6 - elif tip_int == 7: - return Tip.T7 - elif tip_int == 8: - return Tip.T8 - if isinstance(tip, int) and not isinstance(tip, Tip): # User-specified integers from 1-8 need to be converted to Tecan logic tip = _int_to_tip(tip) @@ -268,29 +267,6 @@ def _prepare_evo_aspirate_dispense_parameters( if not isinstance(liquid_class, str) or ";" in liquid_class: raise ValueError(f"Invalid liquid_class: {liquid_class}") - def _int_to_tip(tip_int: int): - """Asserts a Tecan Tip class to an int between 1 and 8.""" - if not 1 <= tip_int <= 8: - raise ValueError( - f"Tip is {tip} with type {type(tip)}, but should be an int between 1 and 8 for _int_to_tip conversion." - ) - if tip_int == 1: - return Tip.T1 - elif tip_int == 2: - return Tip.T2 - elif tip_int == 3: - return Tip.T3 - elif tip_int == 4: - return Tip.T4 - elif tip_int == 5: - return Tip.T5 - elif tip_int == 6: - return Tip.T6 - elif tip_int == 7: - return Tip.T7 - elif tip_int == 8: - return Tip.T8 - tecan_tips = [] for tip in tips: if isinstance(tip, int) and not isinstance(tip, Tip): @@ -404,29 +380,6 @@ def _prepare_evo_wash_parameters( if tips is None: raise ValueError("Missing required parameter: tips") - def _int_to_tip(tip_int: int): - """Asserts a Tecan Tip class to an int between 1 and 8.""" - if not 1 <= tip_int <= 8: - raise ValueError( - f"Tip is {tip} with type {type(tip)}, but should be an int between 1 and 8 for _int_to_tip conversion." - ) - if tip_int == 1: - return Tip.T1 - elif tip_int == 2: - return Tip.T2 - elif tip_int == 3: - return Tip.T3 - elif tip_int == 4: - return Tip.T4 - elif tip_int == 5: - return Tip.T5 - elif tip_int == 6: - return Tip.T6 - elif tip_int == 7: - return Tip.T7 - elif tip_int == 8: - return Tip.T8 - tecan_tips = [] for tip in tips: if isinstance(tip, int) and not isinstance(tip, Tip): From 2c6fc50ae921baf5ee0eb3439d2fa5f25a1d447a Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Tue, 17 Jan 2023 15:08:33 +0100 Subject: [PATCH 12/54] remove evo_transfer() for the time being --- robotools/evotools/__init__.py | 123 --------------------------------- 1 file changed, 123 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index eb09fcd..a41c432 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -1491,129 +1491,6 @@ def evo_dispense( self.evo_dispense_well(labware, wells, labware_position, volumes, liquid_class, tips) return - def evo_transfer( - self, - source: liquidhandling.Labware, - source_wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], - source_position: typing.Tuple[int, int], - destination: liquidhandling.Labware, - destination_wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], - destination_position: typing.Tuple[int, int], - volumes: typing.Union[float, typing.Sequence[float], numpy.ndarray], - tips: typing.Union[typing.List[Tip], typing.List[int]], - liquid_class: str, - *, - label: typing.Optional[str] = None, - # wash_scheme: int = 1, - partition_by: str = "auto", - **kwargs, - ) -> None: - """Transfer operation between two labwares. - - Parameters - ---------- - source : liquidhandling.Labware - Source labware - source_wells : str or iterable - List of source well ids - source_position : tuple - Tuple with position of source labware (grid and site as integers) - destination : liquidhandling.Labware - Destination labware - destination_wells : str or iterable - List of destination well ids - destination_position : tuple - Tuple with position of destination labware (grid and site as integers) - volumes : float or iterable - Volume(s) to transfer - tips: list - Tips with which to perform the transfer - liquid_class : str - Liquid class to use for aspirate and dispense. - label : str - Label of the operation to log into labware history - partition_by : str - one of 'auto' (default), 'source' or 'destination' - 'auto': partitioning by source unless the source is a Trough - 'source': partitioning by source columns - 'destination': partitioning by destination columns - kwargs - Additional keyword arguments to pass to aspirate and dispense. - Take a look at `Worklist.aspirate_well` for the full list of options. - """ - # reformat the convenience parameters - source_wells = numpy.array(source_wells).flatten("F") - destination_wells = numpy.array(destination_wells).flatten("F") - volumes = numpy.array(volumes).flatten("F") - nmax = max((len(source_wells), len(destination_wells), len(volumes))) - - if len(source_wells) == 1: - source_wells = numpy.repeat(source_wells, nmax) - if len(destination_wells) == 1: - destination_wells = numpy.repeat(destination_wells, nmax) - if len(volumes) == 1: - volumes = numpy.repeat(volumes, nmax) - lengths = (len(source_wells), len(destination_wells), len(volumes)) - assert ( - len(set(lengths)) == 1 - ), f"Number of source/destination/volumes must be equal. They were {lengths}" - - # automatic partitioning - partition_by = _optimize_partition_by(source, destination, partition_by, label) - - # the label applies to the entire transfer operation and is not logged at individual aspirate/dispense steps - self.comment(label) - nsteps = 0 - lvh_extra = 0 - - for srcs, dsts, vols in _partition_by_column(source_wells, destination_wells, volumes, partition_by): - # make vector of volumes into vector of volume-lists - vols = [ - _partition_volume(float(v), max_volume=self.max_volume) if self.auto_split else [v] - for v in vols - ] - # transfer from this source column until all wells are done - npartitions = max(map(len, vols)) - # Count only the extra steps created by LVH - lvh_extra += sum([len(vs) - 1 for vs in vols]) - for p in range(npartitions): - naccessed = 0 - # iterate the rows - for s, d, vs in zip(srcs, dsts, vols): - # transfer the next volume-fraction for this well - if len(vs) > p: - v = vs[p] - if v > 0: - self.evo_aspirate(source, s, source_position, v, liquid_class, tips, label=None) - self.evo_dispense( - destination, d, destination_position, v, liquid_class, tips, label=None - ) - nsteps += 1 - # if wash_scheme is not None: - # self.wash(scheme=wash_scheme) - naccessed += 1 - # LVH: if multiple wells are accessed, don't group across partitions - if npartitions > 1 and naccessed > 1 and not p == npartitions - 1: - self.commit() - # LVH: don't group across columns - if npartitions > 1: - self.commit() - - # Condense the labware logs into one operation - # after the transfer operation completed to facilitate debugging. - # Also include the number of extra steps because of LVH if applicable. - if lvh_extra: - if label: - label = f"{label} ({lvh_extra} LVH steps)" - else: - label = f"{lvh_extra} LVH steps" - if destination == source: - source.condense_log(nsteps * 2, label=label) - else: - source.condense_log(nsteps, label=label) - destination.condense_log(nsteps, label=label) - return - def transfer( self, source: liquidhandling.Labware, From 524b8c7e87bfae8025d734acac1ad6c00abd9268 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Tue, 17 Jan 2023 15:09:16 +0100 Subject: [PATCH 13/54] apply pre-commit changes again --- robotools/evotools/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index a41c432..1b1bf93 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -35,6 +35,7 @@ class Tip(enum.IntEnum): class InvalidOperationError(Exception): pass + def _int_to_tip(tip_int: int): """Asserts a Tecan Tip class to an int between 1 and 8.""" if not 1 <= tip_int <= 8: @@ -58,6 +59,7 @@ def _int_to_tip(tip_int: int): elif tip_int == 8: return Tip.T8 + def _prepare_aspirate_dispense_parameters( rack_label: str, position: int, From 2f40fc76828599371eeb5ad91038c69bdbc03d7c Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Tue, 17 Jan 2023 15:15:04 +0100 Subject: [PATCH 14/54] to_hex changes --- robotools/evotools/__init__.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 1b1bf93..5bc839c 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -627,14 +627,17 @@ def _partition_by_column( return column_groups -def toHex(dec: int): - """Method from stackoverflow to convert decimal to hex.""" +def to_hex(dec: int): + """Method from stackoverflow to convert decimal to hex. + Link: https://stackoverflow.com/questions/5796238/python-convert-decimal-to-hex + Solution posted by user "Chunghee Kim" on 21.11.2020. + """ digits = "0123456789ABCDEF" x = dec % 16 rest = dec // 16 if rest == 0: return digits[x] - return toHex(rest) + digits[x] + return to_hex(rest) + digits[x] def evo_make_selection_array(rows: int, columns: int, wells: numpy.ndarray): @@ -683,7 +686,7 @@ def evo_get_selection(rows: int, cols: int, selected): Code string for well selection of pipetting actions in EvoWare scripts (.esc) """ # apply bit mask with 7 bits, adapted from function detailed in EvoWare manual - selection = f"0{toHex(cols)}{rows:02d}" + selection = f"0{to_hex(cols)}{rows:02d}" bit_counter = 0 bit_mask = 0 for x in range(cols): From 953e54ada8fca7fbcec7c1c331cf5fbd4509514a Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Tue, 17 Jan 2023 15:17:55 +0100 Subject: [PATCH 15/54] fix evo_get_selection() doc string --- robotools/evotools/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 5bc839c..0b5b2f5 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -667,18 +667,18 @@ def evo_make_selection_array(rows: int, columns: int, wells: numpy.ndarray): return selection_array -def evo_get_selection(rows: int, cols: int, selected): +def evo_get_selection(rows: int, cols: int, selected: numpy.ndarray): """Function to generate the code string for the well selection of pipetting actions in EvoWare scripts (.esc). Adapted from the C++ function detailed in the EvoWare manual to Python by Martin Beyß (except the test at the end). Parameters ---------- - selected : numpy.ndarray - Numpy array in labware dimensions with selected wells as 1 and others as 0 (from evo_make_selection_array) rows : int Number of rows of target labware object cols : int Number of columns of target labware object + selected : numpy.ndarray + Numpy array in labware dimensions with selected wells as 1 and others as 0 (from evo_make_selection_array) Returns ------- From fe4e738634986dda68d7fb52f0745316df6e17bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20Nie=C3=9Fer?= <104903134+Y0dler@users.noreply.github.com> Date: Tue, 17 Jan 2023 15:11:31 +0100 Subject: [PATCH 16/54] Update robotools/evotools/__init__.py Co-authored-by: Michael Osthege --- robotools/evotools/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 0b5b2f5..541e03a 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -1467,7 +1467,6 @@ def evo_dispense( liquid_class: str, *, label: typing.Optional[str] = None, - # **kwargs, ) -> None: """Performs dispensation from the provided labware. Is identical to the dispense command inside the EvoWARE. Thus, several wells in a single column can be targeted. From 3d7726661287dde58ae87e4f2925f9f4517a3a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20Nie=C3=9Fer?= <104903134+Y0dler@users.noreply.github.com> Date: Tue, 17 Jan 2023 15:19:07 +0100 Subject: [PATCH 17/54] Update robotools/evotools/__init__.py Co-authored-by: Michael Osthege --- robotools/evotools/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 541e03a..a40318e 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -1388,7 +1388,6 @@ def evo_aspirate( liquid_class: str, *, label: typing.Optional[str] = None, - # **kwargs, ) -> None: """Performs aspiration from the provided labware. Is identical to the aspirate command inside the EvoWARE. Thus, several wells in a single column can be targeted. From d8ab5cf03c97a012d6783dbc9ce0736b0b8ac6cf Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Tue, 17 Jan 2023 16:37:27 +0100 Subject: [PATCH 18/54] add more criteria to _prepare_aspirate_dispense_parameters() --- robotools/evotools/__init__.py | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index a40318e..05b48fc 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -266,9 +266,15 @@ def _prepare_evo_aspirate_dispense_parameters( raise InvalidOperationError(f"Volume of {volume} exceeds max_volume.") # optional parameters + if liquid_class is None: + raise ValueError(f"Missing required parameter: liquid_class") if not isinstance(liquid_class, str) or ";" in liquid_class: raise ValueError(f"Invalid liquid_class: {liquid_class}") + if tips is None: + raise ValueError(f"Missing required parameter: tips") + if type(tips) is not int and type(tips) is not Tip: + raise ValueError(f"Invalid type of tips: {tips}. Has to be int or Tip.") tecan_tips = [] for tip in tips: if isinstance(tip, int) and not isinstance(tip, Tip): @@ -276,28 +282,8 @@ def _prepare_evo_aspirate_dispense_parameters( tip = _int_to_tip(tip) tecan_tips.append(tip) - if isinstance(tip, collections.abc.Iterable): - tips = [] - for element in tip: - if isinstance(element, int) and not isinstance(element, Tip): - tips.append(_int_to_tip(element)) - elif isinstance(element, Tip): - if element == -1: - raise ValueError( - "When Iterables are used, no Tip.Any elements are allowed. Pass just one Tip.Any instead." - ) - tips.append(element) - else: - raise ValueError( - f"If tip is an Iterable, it may only contain int or Tip values, not {type(element)}." - ) - tip = sum(set(tips)) - elif not isinstance(tip, Tip): - raise ValueError(f"tip must be an int between 1 and 8, Tip or Iterable, but was {type(tip)}.") - # apply rounding and corrections for the right string formatting volume = f"{numpy.round(volume, decimals=2):.2f}" - tips = ["" if tip == -1 else tip for tip in tips] if tecan_tips: return labware, wells, labware_position, volume, liquid_class, tecan_tips else: From 81f44b0fe359e54154e72cb86edf8dcc67602829 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Tue, 17 Jan 2023 16:38:10 +0100 Subject: [PATCH 19/54] add tests of _prepare_evo_aspirate_dispense_parameters() --- robotools/tests.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/robotools/tests.py b/robotools/tests.py index d5498dd..9ece305 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -465,6 +465,46 @@ def test_parameter_validation(self) -> None: evotools._prepare_aspirate_dispense_parameters( rack_label="WaterTrough", position=1, volume=15, forced_rack_type="valid forced rack type" ) + + # define a labware correctly for testing purposes + plate = liquidhandling.Labware('DWP', 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) + # test labware argument checks + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=None, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware="wrong_labware_type", wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + # test labware_position argument checks + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=None, volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,-1), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=("a",2), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + # test liquid_class argument checks + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class=None, tips=[1,2]) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class=["Water_DispZmax-1_AspZmax-1"], tips=[1,2]) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class="Water;DispZmax-1;AspZmax-1", tips=[1,2]) + # test tips argument checks + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=None) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,"2"]) + _, _, _, _, _, tips = evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + if type(tips) is not evotools.Tip: + raise TypeError(f"Even after completing the _prepare_evo_aspirate_dispense_parameters method, tips are type {type(tips)}.") + # test volume argument checks + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=None, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume="volume", liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=-10, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=7158279, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + return def test_comment(self) -> None: From b9ca7cd6ea0bb4d22320f5f3a84fadd59c0099f7 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Tue, 17 Jan 2023 16:39:10 +0100 Subject: [PATCH 20/54] pre-commit --- robotools/tests.py | 143 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 125 insertions(+), 18 deletions(-) diff --git a/robotools/tests.py b/robotools/tests.py index 9ece305..ab71a30 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -467,44 +467,151 @@ def test_parameter_validation(self) -> None: ) # define a labware correctly for testing purposes - plate = liquidhandling.Labware('DWP', 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) + plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) # test labware argument checks with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=None, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + evotools._prepare_evo_aspirate_dispense_parameters( + labware=None, + wells=["A01", "B01"], + labware_position=(38, 2), + volume=15, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, 2], + ) with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware="wrong_labware_type", wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + evotools._prepare_evo_aspirate_dispense_parameters( + labware="wrong_labware_type", + wells=["A01", "B01"], + labware_position=(38, 2), + volume=15, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, 2], + ) # test labware_position argument checks with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=None, volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=None, + volume=15, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, 2], + ) with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,-1), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=(38, -1), + volume=15, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, 2], + ) with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=("a",2), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=("a", 2), + volume=15, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, 2], + ) # test liquid_class argument checks with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class=None, tips=[1,2]) + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=(38, 2), + volume=15, + liquid_class=None, + tips=[1, 2], + ) with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class=["Water_DispZmax-1_AspZmax-1"], tips=[1,2]) + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=(38, 2), + volume=15, + liquid_class=["Water_DispZmax-1_AspZmax-1"], + tips=[1, 2], + ) with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class="Water;DispZmax-1;AspZmax-1", tips=[1,2]) + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=(38, 2), + volume=15, + liquid_class="Water;DispZmax-1;AspZmax-1", + tips=[1, 2], + ) # test tips argument checks with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=None) + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=(38, 2), + volume=15, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=None, + ) with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,"2"]) - _, _, _, _, _, tips = evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=15, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=(38, 2), + volume=15, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, "2"], + ) + _, _, _, _, _, tips = evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=(38, 2), + volume=15, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, 2], + ) if type(tips) is not evotools.Tip: - raise TypeError(f"Even after completing the _prepare_evo_aspirate_dispense_parameters method, tips are type {type(tips)}.") + raise TypeError( + f"Even after completing the _prepare_evo_aspirate_dispense_parameters method, tips are type {type(tips)}." + ) # test volume argument checks with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=None, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=(38, 2), + volume=None, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, 2], + ) with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume="volume", liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=(38, 2), + volume="volume", + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, 2], + ) with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=-10, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=(38, 2), + volume=-10, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, 2], + ) with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters(labware=plate, wells=["A01", "B01"], labware_position=(38,2), volume=7158279, liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1,2]) - + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["A01", "B01"], + labware_position=(38, 2), + volume=7158279, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, 2], + ) + return def test_comment(self) -> None: From 0916e114d17ace8c87a81a455ac8c6f0202047c4 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Wed, 18 Jan 2023 11:52:33 +0100 Subject: [PATCH 21/54] fix controlling type of tips error --- robotools/evotools/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 05b48fc..2e7bef0 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -273,8 +273,9 @@ def _prepare_evo_aspirate_dispense_parameters( if tips is None: raise ValueError(f"Missing required parameter: tips") - if type(tips) is not int and type(tips) is not Tip: - raise ValueError(f"Invalid type of tips: {tips}. Has to be int or Tip.") + for tip in tips: + if type(tip) is not int and type(tip) is not Tip: + raise ValueError(f"Invalid type of tips: {type(tip)}. Has to be int or Tip.") tecan_tips = [] for tip in tips: if isinstance(tip, int) and not isinstance(tip, Tip): From 7193f2ebb5bf3d2e9de004d297360aa92463f4b9 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Wed, 18 Jan 2023 12:02:50 +0100 Subject: [PATCH 22/54] fix next tip type testing error --- robotools/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/robotools/tests.py b/robotools/tests.py index ab71a30..ee9ea17 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -570,9 +570,9 @@ def test_parameter_validation(self) -> None: liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1, 2], ) - if type(tips) is not evotools.Tip: + if not all(isinstance(n, evotools.Tip) for n in tips): raise TypeError( - f"Even after completing the _prepare_evo_aspirate_dispense_parameters method, tips are type {type(tips)}." + f"Even after completing the _prepare_evo_aspirate_dispense_parameters method, not all tips are type Tip." ) # test volume argument checks with self.assertRaises(ValueError): From c54d397f8a4e9fca6e0e08da9822540d7dd131af Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 11:09:10 +0100 Subject: [PATCH 23/54] add test for wells in _prepare_evo_aspirate_dispense_parameters() --- robotools/evotools/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 2e7bef0..c4b0018 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -226,11 +226,15 @@ def _prepare_evo_aspirate_dispense_parameters( tips : list of int Tip(s) that will be selected (out of tips 1-8) """ - # required parameters if labware is None: raise ValueError("Missing required paramter: labware") if not isinstance(labware, liquidhandling.Labware): raise ValueError(f"Invalid labware: {labware}") + + if wells is None: + raise ValueError("Missing required paramter: wells") + if not isinstance(wells, typing.Union[str, typing.Sequence[str], numpy.ndarray]): + raise ValueError(f"Invalid wells: {wells}") if labware_position is None: raise ValueError("Missing required paramter: position") From 2f07e92b5b77dada58d15d42f2d530f1aeb28d35 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 11:09:48 +0100 Subject: [PATCH 24/54] force keyword arguments in _prepare_evo_wash_parameters() --- robotools/evotools/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index c4b0018..c347f84 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -296,6 +296,7 @@ def _prepare_evo_aspirate_dispense_parameters( def _prepare_evo_wash_parameters( + *, tips: typing.Union[typing.List[Tip], typing.List[int]], waste_location: typing.Tuple[int, int], cleaner_location: typing.Tuple[int, int], From 1db713550471bf7108dd4931539cf7c8b35b8fdc Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 11:10:24 +0100 Subject: [PATCH 25/54] correct error message --- robotools/evotools/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index c347f84..74010a3 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -414,7 +414,7 @@ def _prepare_evo_wash_parameters( if waste_delay is None: raise ValueError("Missing required parameter: waste_delay") if not isinstance(waste_delay, int) or not 0 <= waste_delay <= 1000: - raise ValueError("waste_delay has to be a float from 0 - 1000.") + raise ValueError("waste_delay has to be an int from 0 - 1000.") if cleaner_vol is None: raise ValueError("Missing required parameter: cleaner_vol") @@ -426,7 +426,7 @@ def _prepare_evo_wash_parameters( if cleaner_delay is None: raise ValueError("Missing required parameter: cleaner_delay") if not isinstance(cleaner_delay, int) or not 0 <= cleaner_delay <= 1000: - raise ValueError("cleaner_delay has to be a float from 0 - 1000.") + raise ValueError("cleaner_delay has to be an int from 0 - 1000.") if airgap is None: raise ValueError("Missing required parameter: airgap") From e31169febe097788316d44e0dc1e34f75812d1d7 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 11:10:54 +0100 Subject: [PATCH 26/54] add test for well argument check --- robotools/tests.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/robotools/tests.py b/robotools/tests.py index ee9ea17..c9f8e8b 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -487,6 +487,25 @@ def test_parameter_validation(self) -> None: liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1, 2], ) + # test wells argument checks + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=None, + labware_position=(38, 2), + volume=15, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, 2], + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells="A01", + labware_position=(38, 2), + volume=15, + liquid_class="Water_DispZmax-1_AspZmax-1", + tips=[1, 2], + ) # test labware_position argument checks with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( From 6b18b7e35fd0e72509e8982c36c01ca38f529e75 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 11:11:09 +0100 Subject: [PATCH 27/54] add comment --- robotools/tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/robotools/tests.py b/robotools/tests.py index c9f8e8b..0720e61 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -466,6 +466,7 @@ def test_parameter_validation(self) -> None: rack_label="WaterTrough", position=1, volume=15, forced_rack_type="valid forced rack type" ) + # test _prepare_evo_aspirate_dispense_parameters # define a labware correctly for testing purposes plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) # test labware argument checks From 932a029d32fbb40c7b80c058e6995797d0de5b1d Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 11:11:47 +0100 Subject: [PATCH 28/54] add numerous tests --- robotools/tests.py | 451 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 451 insertions(+) diff --git a/robotools/tests.py b/robotools/tests.py index 0720e61..b93df9f 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -632,6 +632,457 @@ def test_parameter_validation(self) -> None: tips=[1, 2], ) + # test complete _prepare_evo_aspirate_dispense_parameters() command + labware, wells, labware_position, volume, liquid_class, tips = evotools._prepare_evo_aspirate_dispense_parameters( + labware=plate, + wells=["E01","F01","G01"], + labware_position=(38, 2), + volume=750, + liquid_class="Water_DispZmax_AspZmax", + tips=[5,6,7], + ) + self.assertEqual([labware, wells, labware_position, volume, liquid_class, tips],[plate, ["E01","F01","G01"], 750, "Water_DispZmax_AspZmax", [evotools.Tip.T5,evotools.Tip.T6,evotools.Tip.T7]]) + + # test _prepare_evo_wash_parameters + # test tips argument checks + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=None, + waste_location = (52,1), + cleaner_location = (52,0), + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, "2"], + waste_location = (52,1), + cleaner_location = (52,0), + ) + tips, _, _, _, _, _, _, _, _, _, _, _, _ = evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + ) + if not all(isinstance(n, evotools.Tip) for n in tips): + raise TypeError( + f"Even after completing the _prepare_evo_aspirate_dispense_parameters method, not all tips are type Tip." + ) + + # test waste_location argument checks + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = None, + cleaner_location = (52,0), + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (68, 1), + cleaner_location = (52,0), + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (0,1), + cleaner_location = (52,0), + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (1.7,1), + cleaner_location = (52,0), + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (52,-1), + cleaner_location = (52,0), + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (52,128), + cleaner_location = (52,0), + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (52,1.7), + cleaner_location = (52,0), + ) + + # test cleaner_location argument checks + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (52,1), + cleaner_location = None, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (52,1), + cleaner_location = (68, 1), + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (52,1), + cleaner_location = (0,1), + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (52,1), + cleaner_location = (1.7,1), + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (52,1), + cleaner_location = (52,-1), + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (52,1), + cleaner_location = (52,128), + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1,2], + waste_location = (52,1), + cleaner_location = (52,1.7), + ) + + # test arm argument check + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + arm=None, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + arm=2, + ) + + # test waste_vol argument check + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + waste_vol=None, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + waste_vol=-1.0, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + waste_vol=101.0, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + waste_vol=1, + ) + + # test waste_delay argument check + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + waste_delay=None, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + waste_delay=-1, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + waste_delay=1001, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + waste_delay=10.0, + ) + + # test cleaner_vol argument check + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + cleaner_vol=None, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + cleaner_vol=-1.0, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + cleaner_vol=101.0, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + cleaner_vol=1, + ) + + # test cleaner_delay argument check + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + cleaner_delay=None, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + cleaner_delay=-1, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + cleaner_delay=1001, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + cleaner_delay=10.0, + ) + + # test airgap argument check + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + airgap=None, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + airgap=-1.0, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + airgap=101.0, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + airgap=10, + ) + + # test airgap_speed argument check + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + airgap_speed=None, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + airgap_speed=0, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + airgap_speed=1001, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + airgap_speed=10.0, + ) + + # test retract_speed argument check + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + retract_speed=None, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + retract_speed=0, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + retract_speed=101, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + retract_speed=10.0, + ) + + # test fastwash argument check + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + fastwash=None, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + fastwash=2, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + fastwash=1.0, + ) + + # test low_volume argument check + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + low_volume=None, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + low_volume=2, + ) + with self.assertRaises(ValueError): + evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2], + waste_location = (52,1), + cleaner_location = (52,0), + low_volume=1.0, + ) + + # test complete _prepare_evo_wash_parameters() command + tips, waste_location, cleaner_location, arm, waste_vol, waste_delay, cleaner_vol, cleaner_delay, airgap, airgap_speed, retract_speed, fastwash, low_volume = evotools._prepare_evo_aspirate_dispense_parameters( + tips=[1, 2, 3, 4, 5, 6, 7, 8], + waste_location = (52,1), + cleaner_location = (52,0), + ) + self.assertEqual([tips, waste_location, cleaner_location, arm, waste_vol, waste_delay, cleaner_vol, cleaner_delay, airgap, airgap_speed, retract_speed, fastwash, low_volume],[[evotools.Tip.T1,evotools.Tip.T2,evotools.Tip.T3,evotools.Tip.T4,evotools.Tip.T5,evotools.Tip.T6,evotools.Tip.T7,evotools.Tip.T8],(52,1),(52,0),0,3.0,500,4.0,500,10,70,30,1,0]) + + return + + # test complete evo_aspirate() command + def test_evo_aspirate(self) -> None: + plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) + with evotools.Worklist() as wl: + wl.evo_aspirate( + labware=plate, + wells=["E01","F01","G01"], + labware_position=(38,2), + tips=[5,6,7], + volumes=750, + liquid_class="Water_DispZmax_AspZmax", + ) + self.assertEqual( + wl, ['B;Aspirate(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","750","750",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);'] + ) + return + + # test complete evo_dispense() command + def test_evo_dispense(self) -> None: + plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) + with evotools.Worklist() as wl: + wl.evo_dispense( + labware=plate, + wells=["E01","F01","G01"], + labware_position=(38,2), + tips=[5,6,7], + volumes=750, + liquid_class="Water_DispZmax_AspZmax", + ) + self.assertEqual( + wl, ['B;Dispense(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","750","750",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);'] + ) + return + + # test complete evo_wash() command + def test_evo_wash(self) -> None: + plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) + with evotools.Worklist() as wl: + wl.evo_wash( + tips=[1, 2, 3, 4, 5, 6, 7, 8], + waste_location = (52,1), + cleaner_location = (52,0), + ) + self.assertEqual( + wl, ['B;Wash(255,52,1,52,0,"3.0",500,"4.0",500,10,70,30,1,0,1000,0);'] + ) return def test_comment(self) -> None: From 30f3e3a876f9ad91c92a4ce168723b693030a666 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 11:12:35 +0100 Subject: [PATCH 29/54] minor changes --- robotools/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/robotools/tests.py b/robotools/tests.py index b93df9f..23beb90 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -631,7 +631,7 @@ def test_parameter_validation(self) -> None: liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1, 2], ) - + # test complete _prepare_evo_aspirate_dispense_parameters() command labware, wells, labware_position, volume, liquid_class, tips = evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, @@ -1084,7 +1084,7 @@ def test_evo_wash(self) -> None: wl, ['B;Wash(255,52,1,52,0,"3.0",500,"4.0",500,10,70,30,1,0,1000,0);'] ) return - + def test_comment(self) -> None: with evotools.Worklist() as wl: # empty and None comments should be ignored From 00f3243f3803d2af6056867d21f0494dfdfde2dc Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 11:13:29 +0100 Subject: [PATCH 30/54] apply pre-commit changes --- robotools/evotools/__init__.py | 2 +- robotools/tests.py | 386 ++++++++++++++++++++------------- 2 files changed, 231 insertions(+), 157 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 74010a3..3a7a053 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -230,7 +230,7 @@ def _prepare_evo_aspirate_dispense_parameters( raise ValueError("Missing required paramter: labware") if not isinstance(labware, liquidhandling.Labware): raise ValueError(f"Invalid labware: {labware}") - + if wells is None: raise ValueError("Missing required paramter: wells") if not isinstance(wells, typing.Union[str, typing.Sequence[str], numpy.ndarray]): diff --git a/robotools/tests.py b/robotools/tests.py index 23beb90..fdfa8c0 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -631,203 +631,219 @@ def test_parameter_validation(self) -> None: liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1, 2], ) - + # test complete _prepare_evo_aspirate_dispense_parameters() command - labware, wells, labware_position, volume, liquid_class, tips = evotools._prepare_evo_aspirate_dispense_parameters( + ( + labware, + wells, + labware_position, + volume, + liquid_class, + tips, + ) = evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, - wells=["E01","F01","G01"], + wells=["E01", "F01", "G01"], labware_position=(38, 2), volume=750, liquid_class="Water_DispZmax_AspZmax", - tips=[5,6,7], + tips=[5, 6, 7], + ) + self.assertEqual( + [labware, wells, labware_position, volume, liquid_class, tips], + [ + plate, + ["E01", "F01", "G01"], + 750, + "Water_DispZmax_AspZmax", + [evotools.Tip.T5, evotools.Tip.T6, evotools.Tip.T7], + ], ) - self.assertEqual([labware, wells, labware_position, volume, liquid_class, tips],[plate, ["E01","F01","G01"], 750, "Water_DispZmax_AspZmax", [evotools.Tip.T5,evotools.Tip.T6,evotools.Tip.T7]]) # test _prepare_evo_wash_parameters # test tips argument checks with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=None, - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, "2"], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), ) tips, _, _, _, _, _, _, _, _, _, _, _, _ = evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), ) if not all(isinstance(n, evotools.Tip) for n in tips): raise TypeError( f"Even after completing the _prepare_evo_aspirate_dispense_parameters method, not all tips are type Tip." ) - + # test waste_location argument checks with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = None, - cleaner_location = (52,0), + tips=[1, 2], + waste_location=None, + cleaner_location=(52, 0), ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (68, 1), - cleaner_location = (52,0), + tips=[1, 2], + waste_location=(68, 1), + cleaner_location=(52, 0), ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (0,1), - cleaner_location = (52,0), + tips=[1, 2], + waste_location=(0, 1), + cleaner_location=(52, 0), ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (1.7,1), - cleaner_location = (52,0), + tips=[1, 2], + waste_location=(1.7, 1), + cleaner_location=(52, 0), ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (52,-1), - cleaner_location = (52,0), + tips=[1, 2], + waste_location=(52, -1), + cleaner_location=(52, 0), ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (52,128), - cleaner_location = (52,0), + tips=[1, 2], + waste_location=(52, 128), + cleaner_location=(52, 0), ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (52,1.7), - cleaner_location = (52,0), + tips=[1, 2], + waste_location=(52, 1.7), + cleaner_location=(52, 0), ) - + # test cleaner_location argument checks with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (52,1), - cleaner_location = None, + tips=[1, 2], + waste_location=(52, 1), + cleaner_location=None, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (52,1), - cleaner_location = (68, 1), + tips=[1, 2], + waste_location=(52, 1), + cleaner_location=(68, 1), ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (52,1), - cleaner_location = (0,1), + tips=[1, 2], + waste_location=(52, 1), + cleaner_location=(0, 1), ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (52,1), - cleaner_location = (1.7,1), + tips=[1, 2], + waste_location=(52, 1), + cleaner_location=(1.7, 1), ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (52,1), - cleaner_location = (52,-1), + tips=[1, 2], + waste_location=(52, 1), + cleaner_location=(52, -1), ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (52,1), - cleaner_location = (52,128), + tips=[1, 2], + waste_location=(52, 1), + cleaner_location=(52, 128), ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1,2], - waste_location = (52,1), - cleaner_location = (52,1.7), + tips=[1, 2], + waste_location=(52, 1), + cleaner_location=(52, 1.7), ) - + # test arm argument check with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), arm=None, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), arm=2, ) - + # test waste_vol argument check with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), waste_vol=None, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), waste_vol=-1.0, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), waste_vol=101.0, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), waste_vol=1, ) - + # test waste_delay argument check with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), waste_delay=None, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), waste_delay=-1, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), waste_delay=1001, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), waste_delay=10.0, ) @@ -835,29 +851,29 @@ def test_parameter_validation(self) -> None: with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), cleaner_vol=None, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), cleaner_vol=-1.0, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), cleaner_vol=101.0, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), cleaner_vol=1, ) @@ -865,226 +881,284 @@ def test_parameter_validation(self) -> None: with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), cleaner_delay=None, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), cleaner_delay=-1, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), cleaner_delay=1001, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), cleaner_delay=10.0, ) - + # test airgap argument check with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), airgap=None, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), airgap=-1.0, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), airgap=101.0, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), airgap=10, ) - + # test airgap_speed argument check with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), airgap_speed=None, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), airgap_speed=0, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), airgap_speed=1001, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), airgap_speed=10.0, ) - + # test retract_speed argument check with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), retract_speed=None, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), retract_speed=0, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), retract_speed=101, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), retract_speed=10.0, ) - + # test fastwash argument check with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), fastwash=None, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), fastwash=2, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), fastwash=1.0, ) - + # test low_volume argument check with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), low_volume=None, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), low_volume=2, ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), low_volume=1.0, ) # test complete _prepare_evo_wash_parameters() command - tips, waste_location, cleaner_location, arm, waste_vol, waste_delay, cleaner_vol, cleaner_delay, airgap, airgap_speed, retract_speed, fastwash, low_volume = evotools._prepare_evo_aspirate_dispense_parameters( + ( + tips, + waste_location, + cleaner_location, + arm, + waste_vol, + waste_delay, + cleaner_vol, + cleaner_delay, + airgap, + airgap_speed, + retract_speed, + fastwash, + low_volume, + ) = evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2, 3, 4, 5, 6, 7, 8], - waste_location = (52,1), - cleaner_location = (52,0), + waste_location=(52, 1), + cleaner_location=(52, 0), + ) + self.assertEqual( + [ + tips, + waste_location, + cleaner_location, + arm, + waste_vol, + waste_delay, + cleaner_vol, + cleaner_delay, + airgap, + airgap_speed, + retract_speed, + fastwash, + low_volume, + ], + [ + [ + evotools.Tip.T1, + evotools.Tip.T2, + evotools.Tip.T3, + evotools.Tip.T4, + evotools.Tip.T5, + evotools.Tip.T6, + evotools.Tip.T7, + evotools.Tip.T8, + ], + (52, 1), + (52, 0), + 0, + 3.0, + 500, + 4.0, + 500, + 10, + 70, + 30, + 1, + 0, + ], ) - self.assertEqual([tips, waste_location, cleaner_location, arm, waste_vol, waste_delay, cleaner_vol, cleaner_delay, airgap, airgap_speed, retract_speed, fastwash, low_volume],[[evotools.Tip.T1,evotools.Tip.T2,evotools.Tip.T3,evotools.Tip.T4,evotools.Tip.T5,evotools.Tip.T6,evotools.Tip.T7,evotools.Tip.T8],(52,1),(52,0),0,3.0,500,4.0,500,10,70,30,1,0]) return - + # test complete evo_aspirate() command def test_evo_aspirate(self) -> None: plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) with evotools.Worklist() as wl: wl.evo_aspirate( labware=plate, - wells=["E01","F01","G01"], - labware_position=(38,2), - tips=[5,6,7], + wells=["E01", "F01", "G01"], + labware_position=(38, 2), + tips=[5, 6, 7], volumes=750, liquid_class="Water_DispZmax_AspZmax", ) self.assertEqual( - wl, ['B;Aspirate(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","750","750",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);'] + wl, + [ + 'B;Aspirate(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","750","750",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' + ], ) return - + # test complete evo_dispense() command def test_evo_dispense(self) -> None: plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) with evotools.Worklist() as wl: wl.evo_dispense( labware=plate, - wells=["E01","F01","G01"], - labware_position=(38,2), - tips=[5,6,7], + wells=["E01", "F01", "G01"], + labware_position=(38, 2), + tips=[5, 6, 7], volumes=750, liquid_class="Water_DispZmax_AspZmax", ) self.assertEqual( - wl, ['B;Dispense(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","750","750",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);'] + wl, + [ + 'B;Dispense(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","750","750",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' + ], ) return - + # test complete evo_wash() command def test_evo_wash(self) -> None: plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) with evotools.Worklist() as wl: wl.evo_wash( tips=[1, 2, 3, 4, 5, 6, 7, 8], - waste_location = (52,1), - cleaner_location = (52,0), - ) - self.assertEqual( - wl, ['B;Wash(255,52,1,52,0,"3.0",500,"4.0",500,10,70,30,1,0,1000,0);'] + waste_location=(52, 1), + cleaner_location=(52, 0), ) + self.assertEqual(wl, ['B;Wash(255,52,1,52,0,"3.0",500,"4.0",500,10,70,30,1,0,1000,0);']) return - + def test_comment(self) -> None: with evotools.Worklist() as wl: # empty and None comments should be ignored From 45d87b3fa6b3dd304fde6d609d45ab1f41c34fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20Nie=C3=9Fer?= <104903134+Y0dler@users.noreply.github.com> Date: Thu, 19 Jan 2023 11:22:48 +0100 Subject: [PATCH 31/54] Update robotools/evotools/__init__.py Co-authored-by: Michael Osthege --- robotools/evotools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 3a7a053..f3116ab 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -233,7 +233,7 @@ def _prepare_evo_aspirate_dispense_parameters( if wells is None: raise ValueError("Missing required paramter: wells") - if not isinstance(wells, typing.Union[str, typing.Sequence[str], numpy.ndarray]): + if not isinstance(wells, (str, list, tuple, numpy.ndarray)): raise ValueError(f"Invalid wells: {wells}") if labware_position is None: From 9114334730fcbb154464eacb22f0abea755b4c10 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 11:28:18 +0100 Subject: [PATCH 32/54] remove None tests --- robotools/tests.py | 142 --------------------------------------------- 1 file changed, 142 deletions(-) diff --git a/robotools/tests.py b/robotools/tests.py index fdfa8c0..0620214 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -470,15 +470,6 @@ def test_parameter_validation(self) -> None: # define a labware correctly for testing purposes plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) # test labware argument checks - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - labware=None, - wells=["A01", "B01"], - labware_position=(38, 2), - volume=15, - liquid_class="Water_DispZmax-1_AspZmax-1", - tips=[1, 2], - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( labware="wrong_labware_type", @@ -489,15 +480,6 @@ def test_parameter_validation(self) -> None: tips=[1, 2], ) # test wells argument checks - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - labware=plate, - wells=None, - labware_position=(38, 2), - volume=15, - liquid_class="Water_DispZmax-1_AspZmax-1", - tips=[1, 2], - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, @@ -508,15 +490,6 @@ def test_parameter_validation(self) -> None: tips=[1, 2], ) # test labware_position argument checks - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - labware=plate, - wells=["A01", "B01"], - labware_position=None, - volume=15, - liquid_class="Water_DispZmax-1_AspZmax-1", - tips=[1, 2], - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, @@ -536,15 +509,6 @@ def test_parameter_validation(self) -> None: tips=[1, 2], ) # test liquid_class argument checks - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - labware=plate, - wells=["A01", "B01"], - labware_position=(38, 2), - volume=15, - liquid_class=None, - tips=[1, 2], - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, @@ -564,15 +528,6 @@ def test_parameter_validation(self) -> None: tips=[1, 2], ) # test tips argument checks - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - labware=plate, - wells=["A01", "B01"], - labware_position=(38, 2), - volume=15, - liquid_class="Water_DispZmax-1_AspZmax-1", - tips=None, - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, @@ -595,15 +550,6 @@ def test_parameter_validation(self) -> None: f"Even after completing the _prepare_evo_aspirate_dispense_parameters method, not all tips are type Tip." ) # test volume argument checks - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - labware=plate, - wells=["A01", "B01"], - labware_position=(38, 2), - volume=None, - liquid_class="Water_DispZmax-1_AspZmax-1", - tips=[1, 2], - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, @@ -661,12 +607,6 @@ def test_parameter_validation(self) -> None: # test _prepare_evo_wash_parameters # test tips argument checks - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=None, - waste_location=(52, 1), - cleaner_location=(52, 0), - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, "2"], @@ -684,12 +624,6 @@ def test_parameter_validation(self) -> None: ) # test waste_location argument checks - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, 2], - waste_location=None, - cleaner_location=(52, 0), - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], @@ -728,12 +662,6 @@ def test_parameter_validation(self) -> None: ) # test cleaner_location argument checks - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, 2], - waste_location=(52, 1), - cleaner_location=None, - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], @@ -772,13 +700,6 @@ def test_parameter_validation(self) -> None: ) # test arm argument check - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, 2], - waste_location=(52, 1), - cleaner_location=(52, 0), - arm=None, - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], @@ -788,13 +709,6 @@ def test_parameter_validation(self) -> None: ) # test waste_vol argument check - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, 2], - waste_location=(52, 1), - cleaner_location=(52, 0), - waste_vol=None, - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], @@ -818,13 +732,6 @@ def test_parameter_validation(self) -> None: ) # test waste_delay argument check - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, 2], - waste_location=(52, 1), - cleaner_location=(52, 0), - waste_delay=None, - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], @@ -848,13 +755,6 @@ def test_parameter_validation(self) -> None: ) # test cleaner_vol argument check - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, 2], - waste_location=(52, 1), - cleaner_location=(52, 0), - cleaner_vol=None, - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], @@ -878,13 +778,6 @@ def test_parameter_validation(self) -> None: ) # test cleaner_delay argument check - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, 2], - waste_location=(52, 1), - cleaner_location=(52, 0), - cleaner_delay=None, - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], @@ -908,13 +801,6 @@ def test_parameter_validation(self) -> None: ) # test airgap argument check - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, 2], - waste_location=(52, 1), - cleaner_location=(52, 0), - airgap=None, - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], @@ -938,13 +824,6 @@ def test_parameter_validation(self) -> None: ) # test airgap_speed argument check - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, 2], - waste_location=(52, 1), - cleaner_location=(52, 0), - airgap_speed=None, - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], @@ -968,13 +847,6 @@ def test_parameter_validation(self) -> None: ) # test retract_speed argument check - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, 2], - waste_location=(52, 1), - cleaner_location=(52, 0), - retract_speed=None, - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], @@ -998,13 +870,6 @@ def test_parameter_validation(self) -> None: ) # test fastwash argument check - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, 2], - waste_location=(52, 1), - cleaner_location=(52, 0), - fastwash=None, - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], @@ -1021,13 +886,6 @@ def test_parameter_validation(self) -> None: ) # test low_volume argument check - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, 2], - waste_location=(52, 1), - cleaner_location=(52, 0), - low_volume=None, - ) with self.assertRaises(ValueError): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], From 49d724d3cfa8fb5d711a3be226600af4be6b889f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20Nie=C3=9Fer?= <104903134+Y0dler@users.noreply.github.com> Date: Thu, 19 Jan 2023 11:30:49 +0100 Subject: [PATCH 33/54] Update robotools/tests.py Co-authored-by: Michael Osthege --- robotools/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/robotools/tests.py b/robotools/tests.py index 0620214..30f96cc 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -470,7 +470,7 @@ def test_parameter_validation(self) -> None: # define a labware correctly for testing purposes plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) # test labware argument checks - with self.assertRaises(ValueError): + with self.assertRaises(ValueError, matches="required parameter: labware"): evotools._prepare_evo_aspirate_dispense_parameters( labware="wrong_labware_type", wells=["A01", "B01"], From 82cfe022b1390fd449de127d351f7de3014395fa Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 13:54:32 +0100 Subject: [PATCH 34/54] use pytest to match specific error messages --- robotools/tests.py | 102 +++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 54 deletions(-) diff --git a/robotools/tests.py b/robotools/tests.py index 30f96cc..94c30bf 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -470,7 +470,7 @@ def test_parameter_validation(self) -> None: # define a labware correctly for testing purposes plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) # test labware argument checks - with self.assertRaises(ValueError, matches="required parameter: labware"): + with pytest.raises(ValueError, match="Invalid labware:"): evotools._prepare_evo_aspirate_dispense_parameters( labware="wrong_labware_type", wells=["A01", "B01"], @@ -480,7 +480,7 @@ def test_parameter_validation(self) -> None: tips=[1, 2], ) # test wells argument checks - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Invalid wells:"): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, wells="A01", @@ -490,7 +490,7 @@ def test_parameter_validation(self) -> None: tips=[1, 2], ) # test labware_position argument checks - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Invalid position:"): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, wells=["A01", "B01"], @@ -499,7 +499,7 @@ def test_parameter_validation(self) -> None: liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1, 2], ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Invalid position:"): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, wells=["A01", "B01"], @@ -509,7 +509,7 @@ def test_parameter_validation(self) -> None: tips=[1, 2], ) # test liquid_class argument checks - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Invalid liquid_class:"): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, wells=["A01", "B01"], @@ -518,7 +518,7 @@ def test_parameter_validation(self) -> None: liquid_class=["Water_DispZmax-1_AspZmax-1"], tips=[1, 2], ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Invalid liquid_class:"): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, wells=["A01", "B01"], @@ -528,7 +528,7 @@ def test_parameter_validation(self) -> None: tips=[1, 2], ) # test tips argument checks - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Invalid type of tips:"): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, wells=["A01", "B01"], @@ -550,7 +550,7 @@ def test_parameter_validation(self) -> None: f"Even after completing the _prepare_evo_aspirate_dispense_parameters method, not all tips are type Tip." ) # test volume argument checks - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Invalid volume:"): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, wells=["A01", "B01"], @@ -559,7 +559,7 @@ def test_parameter_validation(self) -> None: liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1, 2], ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Invalid volume:"): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, wells=["A01", "B01"], @@ -568,7 +568,7 @@ def test_parameter_validation(self) -> None: liquid_class="Water_DispZmax-1_AspZmax-1", tips=[1, 2], ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Invalid volume:"): evotools._prepare_evo_aspirate_dispense_parameters( labware=plate, wells=["A01", "B01"], @@ -607,12 +607,6 @@ def test_parameter_validation(self) -> None: # test _prepare_evo_wash_parameters # test tips argument checks - with self.assertRaises(ValueError): - evotools._prepare_evo_aspirate_dispense_parameters( - tips=[1, "2"], - waste_location=(52, 1), - cleaner_location=(52, 0), - ) tips, _, _, _, _, _, _, _, _, _, _, _, _ = evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), @@ -624,37 +618,37 @@ def test_parameter_validation(self) -> None: ) # test waste_location argument checks - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Grid (first number in waste_location tuple)"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(68, 1), cleaner_location=(52, 0), ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Grid (first number in waste_location tuple)"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(0, 1), cleaner_location=(52, 0), ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Grid (first number in waste_location tuple)"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(1.7, 1), cleaner_location=(52, 0), ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Site (second number in waste_location tuple)"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, -1), cleaner_location=(52, 0), ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Site (second number in waste_location tuple)"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 128), cleaner_location=(52, 0), ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Site (second number in waste_location tuple)"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1.7), @@ -662,37 +656,37 @@ def test_parameter_validation(self) -> None: ) # test cleaner_location argument checks - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Grid (first number in cleaner_location tuple)"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(68, 1), ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Grid (first number in cleaner_location tuple)"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(0, 1), ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Grid (first number in cleaner_location tuple)"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(1.7, 1), ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Site (second number in cleaner_location tuple)"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, -1), ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Site (second number in cleaner_location tuple)"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 128), ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Site (second number in cleaner_location tuple)"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), @@ -700,7 +694,7 @@ def test_parameter_validation(self) -> None: ) # test arm argument check - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Parameter arm"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), @@ -709,21 +703,21 @@ def test_parameter_validation(self) -> None: ) # test waste_vol argument check - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="waste_vol has to be a float"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), waste_vol=-1.0, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="waste_vol has to be a float"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), waste_vol=101.0, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="waste_vol has to be a float"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), @@ -732,21 +726,21 @@ def test_parameter_validation(self) -> None: ) # test waste_delay argument check - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="waste_delay has to be an int"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), waste_delay=-1, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="waste_delay has to be an int"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), waste_delay=1001, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="waste_delay has to be an int"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), @@ -755,21 +749,21 @@ def test_parameter_validation(self) -> None: ) # test cleaner_vol argument check - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="cleaner_vol has to be a float"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), cleaner_vol=-1.0, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="cleaner_vol has to be a float"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), cleaner_vol=101.0, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="cleaner_vol has to be a float"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), @@ -778,21 +772,21 @@ def test_parameter_validation(self) -> None: ) # test cleaner_delay argument check - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="cleaner_delay has to be an int"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), cleaner_delay=-1, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="cleaner_delay has to be an int"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), cleaner_delay=1001, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="cleaner_delay has to be an int"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), @@ -801,21 +795,21 @@ def test_parameter_validation(self) -> None: ) # test airgap argument check - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="airgap has to be a float"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), airgap=-1.0, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="airgap has to be a float"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), airgap=101.0, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="airgap has to be a float"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), @@ -824,21 +818,21 @@ def test_parameter_validation(self) -> None: ) # test airgap_speed argument check - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="airgap_speed has to be an int"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), airgap_speed=0, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="airgap_speed has to be an int"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), airgap_speed=1001, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="airgap_speed has to be an int"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), @@ -847,21 +841,21 @@ def test_parameter_validation(self) -> None: ) # test retract_speed argument check - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="retract_speed has to be an int"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), retract_speed=0, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="retract_speed has to be an int"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), retract_speed=101, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="retract_speed has to be an int"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), @@ -870,14 +864,14 @@ def test_parameter_validation(self) -> None: ) # test fastwash argument check - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Parameter fastwash"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), fastwash=2, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Parameter fastwash"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), @@ -886,14 +880,14 @@ def test_parameter_validation(self) -> None: ) # test low_volume argument check - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Parameter low_volume"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), low_volume=2, ) - with self.assertRaises(ValueError): + with pytest.raises(ValueError, match="Parameter low_volume"): evotools._prepare_evo_aspirate_dispense_parameters( tips=[1, 2], waste_location=(52, 1), From 1947ef6047dc922dc03821c9ca75164bb3a72ad5 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 13:54:43 +0100 Subject: [PATCH 35/54] fix some error messages etc --- robotools/evotools/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index f3116ab..921aa7c 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -254,10 +254,10 @@ def _prepare_evo_aspirate_dispense_parameters( if vol < 0 or vol > 7158278 or numpy.isnan(vol): raise ValueError(f"Invalid volume: {vol}") if max_volume is not None and vol > max_volume: - raise InvalidOperationError(f"Volume of {vol} exceeds max_volume.") + raise InvalidOperationError(f"Invalid volume: volume of {vol} exceeds max_volume.") if not len(vol) == len(tips): raise Exception( - f"Tips and volume lists have different lengths ({len(tips)} and {len(vol)}, respectively)." + f"Invalid volume: Tips and volume lists have different lengths ({len(tips)} and {len(vol)}, respectively)." ) else: try: @@ -436,12 +436,12 @@ def _prepare_evo_wash_parameters( if airgap_speed is None: raise ValueError("Missing required parameter: airgap_speed") if not isinstance(airgap_speed, int) or not 1 <= airgap_speed <= 1000: - raise ValueError("airgap_speed has to be a float from 1 - 1000.") + raise ValueError("airgap_speed has to be an int from 1 - 1000.") if retract_speed is None: raise ValueError("Missing required parameter: retract_speed") if not isinstance(retract_speed, int) or not 1 <= retract_speed <= 100: - raise ValueError("retract_speed has to be a float from 1 - 100.") + raise ValueError("retract_speed has to be an int from 1 - 100.") if fastwash is None: raise ValueError("Missing required paramter: fastwash") From 62c6612d5de49f6b32e514269afcbe60c15e29c7 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 14:23:24 +0100 Subject: [PATCH 36/54] exchange args for kwargs --- robotools/evotools/__init__.py | 62 +++++++++++++++++----------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 921aa7c..a9f4f2c 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -938,13 +938,13 @@ def evo_aspirate_well( Tip(s) that will be selected; use either a list with integers from 1 - 8 or with tip.T1 - tip.T8 """ # perform consistency checks - args = ( - labware, - wells, - labware_position, - volume, - liquid_class, - tips, + kwargs = dict( + labware=labware, + wells=wells, + labware_position=labware_position, + volume=volume, + liquid_class=liquid_class, + tips=tips, ) ( labware, @@ -953,7 +953,7 @@ def evo_aspirate_well( volume, liquid_class, tips, - ) = _prepare_evo_aspirate_dispense_parameters(*args, max_volume=self.max_volume) + ) = _prepare_evo_aspirate_dispense_parameters(**kwargs, max_volume=self.max_volume) # calculate tip_selection based on tips argument tip_selection = 0 @@ -1079,13 +1079,13 @@ def evo_dispense_well( Tip(s) that will be selected; use either a list with integers from 1 - 8 or with tip.T1 - tip.T8 """ # perform consistency checks - args = ( - labware, - wells, - labware_position, - volume, - liquid_class, - tips, + kwargs = dict( + labware=labware, + wells=wells, + labware_position=labware_position, + volume=volume, + liquid_class=liquid_class, + tips=tips, ) ( labware, @@ -1094,7 +1094,7 @@ def evo_dispense_well( volume, liquid_class, tips, - ) = _prepare_evo_aspirate_dispense_parameters(*args, max_volume=self.max_volume) + ) = _prepare_evo_aspirate_dispense_parameters(**kwargs, max_volume=self.max_volume) # calculate tip_selection based on tips argument tip_selection = 0 @@ -1175,20 +1175,20 @@ def evo_wash( """ # perform consistency checks - args = ( - tips, - waste_location, - cleaner_location, - arm, - waste_vol, - waste_delay, - cleaner_vol, - cleaner_delay, - airgap, - airgap_speed, - retract_speed, - fastwash, - low_volume, + kwargs = dict( + tips=tips, + waste_location=waste_location, + cleaner_location=cleaner_location, + arm=arm, + waste_vol=waste_vol, + waste_delay=waste_delay, + cleaner_vol=cleaner_vol, + cleaner_delay=cleaner_delay, + airgap=airgap, + airgap_speed=airgap_speed, + retract_speed=retract_speed, + fastwash=fastwash, + low_volume=low_volume, ) ( tips, @@ -1204,7 +1204,7 @@ def evo_wash( retract_speed, fastwash, low_volume, - ) = _prepare_evo_wash_parameters(*args) + ) = _prepare_evo_wash_parameters(**kwargs) # calculate tip_selection based on tips argument tip_selection = 0 for tip in tips: From 8bc36da010c788cf713986ed6eacbc9a3d6ebcdf Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 14:23:58 +0100 Subject: [PATCH 37/54] increase starting volume of plate --- robotools/tests.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/robotools/tests.py b/robotools/tests.py index 94c30bf..6e6417f 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -468,7 +468,7 @@ def test_parameter_validation(self) -> None: # test _prepare_evo_aspirate_dispense_parameters # define a labware correctly for testing purposes - plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) + plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=1000) # test labware argument checks with pytest.raises(ValueError, match="Invalid labware:"): evotools._prepare_evo_aspirate_dispense_parameters( @@ -961,7 +961,7 @@ def test_parameter_validation(self) -> None: # test complete evo_aspirate() command def test_evo_aspirate(self) -> None: - plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) + plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=1000) with evotools.Worklist() as wl: wl.evo_aspirate( labware=plate, @@ -981,7 +981,7 @@ def test_evo_aspirate(self) -> None: # test complete evo_dispense() command def test_evo_dispense(self) -> None: - plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) + plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=1000) with evotools.Worklist() as wl: wl.evo_dispense( labware=plate, @@ -1001,7 +1001,6 @@ def test_evo_dispense(self) -> None: # test complete evo_wash() command def test_evo_wash(self) -> None: - plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=100) with evotools.Worklist() as wl: wl.evo_wash( tips=[1, 2, 3, 4, 5, 6, 7, 8], From 4259e535f7fc3fc314bd52e49410f64683fc913f Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 14:46:15 +0100 Subject: [PATCH 38/54] fix evo_aspirate() and evo_dispense() regarding wells and volumes --- robotools/evotools/__init__.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index a9f4f2c..b6ef66d 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -911,7 +911,7 @@ def aspirate_well( def evo_aspirate_well( self, labware: liquidhandling.Labware, - wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], + wells: typing.Union[str, typing.List[str]], *, labware_position: typing.Tuple[int, int], volume: typing.Union[float, typing.List[float]], @@ -1053,7 +1053,7 @@ def dispense_well( def evo_dispense_well( self, labware: liquidhandling.Labware, - wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], + wells: typing.Union[str, typing.List[str]], labware_position: typing.Tuple[int, int], volume: typing.Union[float, typing.List[float]], liquid_class: str, @@ -1373,10 +1373,10 @@ def aspirate( def evo_aspirate( self, labware: liquidhandling.Labware, - wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], + wells: typing.Union[str, typing.List[str]], labware_position: typing.Tuple[int, int], tips: typing.Union[typing.List[Tip], typing.List[int]], - volumes: typing.Union[float, typing.Sequence[float], numpy.ndarray], + volumes: typing.Union[float, typing.List[float]], liquid_class: str, *, label: typing.Optional[str] = None, @@ -1399,11 +1399,12 @@ def evo_aspirate( liquid_class : str, optional Overwrites the liquid class for this step (max 32 characters) """ - wells = numpy.array(wells).flatten("F") - volumes = numpy.array(volumes).flatten("F") - if len(volumes) == 1: - volumes = numpy.repeat(volumes, len(wells)) - labware.remove(wells, volumes, label) + # diferentiate between what is needed for volume calculation and for pipetting commands + wells_calc = numpy.array(wells).flatten("F") + volumes_calc = numpy.array(volumes).flatten("F") + if len(volumes_calc) == 1: + volumes_calc = numpy.repeat(volumes_calc, len(wells_calc)) + labware.remove(wells_calc, volumes_calc, label) self.comment(label) self.evo_aspirate_well(labware, wells, labware_position, volumes, liquid_class, tips) return @@ -1451,10 +1452,10 @@ def dispense( def evo_dispense( self, labware: liquidhandling.Labware, - wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], + wells: typing.Union[str, typing.List[str]], labware_position: typing.Tuple[int, int], tips: typing.Union[typing.List[Tip], typing.List[int]], - volumes: typing.Union[float, typing.Sequence[float], numpy.ndarray], + volumes: typing.Union[float, typing.List[float]], liquid_class: str, *, label: typing.Optional[str] = None, @@ -1477,11 +1478,12 @@ def evo_dispense( liquid_class : str, optional Overwrites the liquid class for this step (max 32 characters) """ - wells = numpy.array(wells).flatten("F") - volumes = numpy.array(volumes).flatten("F") - if len(volumes) == 1: - volumes = numpy.repeat(volumes, len(wells)) - labware.remove(wells, volumes, label) + # diferentiate between what is needed for volume calculation and for pipetting commands + wells_calc = numpy.array(wells).flatten("F") + volumes_calc = numpy.array(volumes).flatten("F") + if len(volumes_calc) == 1: + volumes_calc = numpy.repeat(volumes_calc, len(wells_calc)) + labware.remove(wells_calc, volumes_calc, label) self.comment(label) self.evo_dispense_well(labware, wells, labware_position, volumes, liquid_class, tips) return From fc7f5df6568b7e8bf93046660b4b2c8feb24275f Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 14:49:12 +0100 Subject: [PATCH 39/54] fix kwargs of evo_aspirate_wells and dispense --- robotools/evotools/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index b6ef66d..177fda6 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -910,9 +910,9 @@ def aspirate_well( def evo_aspirate_well( self, + *, labware: liquidhandling.Labware, wells: typing.Union[str, typing.List[str]], - *, labware_position: typing.Tuple[int, int], volume: typing.Union[float, typing.List[float]], liquid_class: str, @@ -1052,6 +1052,7 @@ def dispense_well( def evo_dispense_well( self, + *, labware: liquidhandling.Labware, wells: typing.Union[str, typing.List[str]], labware_position: typing.Tuple[int, int], From 40bca7a31e8803491742be790935674e749bac50 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 14:55:06 +0100 Subject: [PATCH 40/54] exchange positional for keyword arguments --- robotools/evotools/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 177fda6..482f285 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -1407,7 +1407,7 @@ def evo_aspirate( volumes_calc = numpy.repeat(volumes_calc, len(wells_calc)) labware.remove(wells_calc, volumes_calc, label) self.comment(label) - self.evo_aspirate_well(labware, wells, labware_position, volumes, liquid_class, tips) + self.evo_aspirate_well(labware=labware, wells=wells, labware_position=labware_position, volumes=volumes, liquid_class=liquid_class, tips=tips) return def dispense( @@ -1486,7 +1486,7 @@ def evo_dispense( volumes_calc = numpy.repeat(volumes_calc, len(wells_calc)) labware.remove(wells_calc, volumes_calc, label) self.comment(label) - self.evo_dispense_well(labware, wells, labware_position, volumes, liquid_class, tips) + self.evo_dispense_well(labware=labware, wells=wells, labware_position=labware_position, volumes=volumes, liquid_class=liquid_class, tips=tips) return def transfer( From 88dca517721e0e27e43bda163f7d04b4a7adaf8e Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 15:37:25 +0100 Subject: [PATCH 41/54] fix wrong keyword denomination --- robotools/evotools/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 482f285..1e31c72 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -1407,7 +1407,7 @@ def evo_aspirate( volumes_calc = numpy.repeat(volumes_calc, len(wells_calc)) labware.remove(wells_calc, volumes_calc, label) self.comment(label) - self.evo_aspirate_well(labware=labware, wells=wells, labware_position=labware_position, volumes=volumes, liquid_class=liquid_class, tips=tips) + self.evo_aspirate_well(labware=labware, wells=wells, labware_position=labware_position, volume=volumes, liquid_class=liquid_class, tips=tips) return def dispense( @@ -1486,7 +1486,7 @@ def evo_dispense( volumes_calc = numpy.repeat(volumes_calc, len(wells_calc)) labware.remove(wells_calc, volumes_calc, label) self.comment(label) - self.evo_dispense_well(labware=labware, wells=wells, labware_position=labware_position, volumes=volumes, liquid_class=liquid_class, tips=tips) + self.evo_dispense_well(labware=labware, wells=wells, labware_position=labware_position, volume=volumes, liquid_class=liquid_class, tips=tips) return def transfer( From 81a3d5356967f8e53f83f4a89ed36d0a861a592c Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 15:47:07 +0100 Subject: [PATCH 42/54] apply pre-commit --- robotools/evotools/__init__.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 1e31c72..6602f04 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -1407,7 +1407,14 @@ def evo_aspirate( volumes_calc = numpy.repeat(volumes_calc, len(wells_calc)) labware.remove(wells_calc, volumes_calc, label) self.comment(label) - self.evo_aspirate_well(labware=labware, wells=wells, labware_position=labware_position, volume=volumes, liquid_class=liquid_class, tips=tips) + self.evo_aspirate_well( + labware=labware, + wells=wells, + labware_position=labware_position, + volume=volumes, + liquid_class=liquid_class, + tips=tips, + ) return def dispense( @@ -1486,7 +1493,14 @@ def evo_dispense( volumes_calc = numpy.repeat(volumes_calc, len(wells_calc)) labware.remove(wells_calc, volumes_calc, label) self.comment(label) - self.evo_dispense_well(labware=labware, wells=wells, labware_position=labware_position, volume=volumes, liquid_class=liquid_class, tips=tips) + self.evo_dispense_well( + labware=labware, + wells=wells, + labware_position=labware_position, + volume=volumes, + liquid_class=liquid_class, + tips=tips, + ) return def transfer( From ed61b04145c31ddb96f6f9036f4281f2e4750bc0 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Thu, 19 Jan 2023 16:11:11 +0100 Subject: [PATCH 43/54] fix iterate over tuple error --- robotools/evotools/__init__.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 6602f04..ead162f 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -383,19 +383,19 @@ def _prepare_evo_wash_parameters( if waste_location is None: raise ValueError("Missing required parameter: waste_location") - for grid, site in waste_location: - if not isinstance(grid, int) or not 1 <= grid <= 67: - raise ValueError("Grid (first number in waste_location tuple) has to be an int from 1 - 67.") - if not isinstance(site, int) or not 0 <= site <= 127: - raise ValueError("Site (second number in waste_location tuple) has to be an int from 0 - 127.") + grid, site = waste_location + if not isinstance(grid, int) or not 1 <= grid <= 67: + raise ValueError("Grid (first number in waste_location tuple) has to be an int from 1 - 67.") + if not isinstance(site, int) or not 0 <= site <= 127: + raise ValueError("Site (second number in waste_location tuple) has to be an int from 0 - 127.") if cleaner_location is None: raise ValueError("Missing required parameter: cleaner_location") - for grid, site in cleaner_location: - if not isinstance(grid, int) or not 1 <= grid <= 67: - raise ValueError("Grid (first number in cleaner_location tuple) has to be an int from 1 - 67.") - if not isinstance(site, int) or not 0 <= site <= 127: - raise ValueError("Site (second number in cleaner_location tuple) has to be an int from 0 - 127.") + grid, site = cleaner_location + if not isinstance(grid, int) or not 1 <= grid <= 67: + raise ValueError("Grid (first number in cleaner_location tuple) has to be an int from 1 - 67.") + if not isinstance(site, int) or not 0 <= site <= 127: + raise ValueError("Site (second number in cleaner_location tuple) has to be an int from 0 - 127.") if arm is None: raise ValueError("Missing required paramter: arm") From 66ffa49710035c7521c7fa2f2711ce2d1e2d9824 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Fri, 20 Jan 2023 08:55:28 +0100 Subject: [PATCH 44/54] fix volume calculation in evo_aspirate_well and dispense --- robotools/evotools/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index ead162f..d782d83 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -964,8 +964,8 @@ def evo_aspirate_well( tip_volumes = "" if type(volume) == list: volume_dict = dict(zip(tips, volume)) - for tip in tips: - if tip.value in [1, 2, 4, 8, 16, 32, 64, 128]: + for tip in [1, 2, 4, 8, 16, 32, 64, 128]: + if tip.value in tips: if type(volume) == float: tip_volumes += f'"{volume}",' elif type(volume) == list: @@ -1103,17 +1103,17 @@ def evo_dispense_well( tip_selection += tip.value # prepare volume section (handle both the case of one volume as int and a list of individual volumes per tip) - tip_volumes = "" + tip_volumes = '' if type(volume) == list: volume_dict = dict(zip(tips, volume)) - for tip in tips: - if tip.value in [1, 2, 4, 8, 16, 32, 64, 128]: + for tip in [1, 2, 4, 8, 16, 32, 64, 128]: + if tip.value in tips: if type(volume) == float: - tip_volumes += f'"{volume}",' + tip_volumes += f'\"{volume}\",' elif type(volume) == list: - tip_volumes += f'"{volume_dict[tip]}",' + tip_volumes += f'\"{volume_dict[tip]}\",' else: - tip_volumes += "0," + tip_volumes += '0,' # convert selection from list of well ids to numpy array with same dimensions as target labware (1: well is selected, 0: well is not selected) selected = evo_make_selection_array(labware.n_rows, labware.n_columns, wells) From 94265e53d9bf81bd47e45bee765a9e4e4942d9d4 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Fri, 20 Jan 2023 08:55:58 +0100 Subject: [PATCH 45/54] fix comparison of test_evo_aspirate and dispense --- robotools/tests.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/robotools/tests.py b/robotools/tests.py index 6e6417f..6df5ea9 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -992,10 +992,8 @@ def test_evo_dispense(self) -> None: liquid_class="Water_DispZmax_AspZmax", ) self.assertEqual( - wl, - [ - 'B;Dispense(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","750","750",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' - ], + wl[0], + 'B;Dispense(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","750","750",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' ) return From 8e753ec967d61cc619d17f1f5881373fad3cf01a Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Fri, 20 Jan 2023 12:07:40 +0100 Subject: [PATCH 46/54] fixing evo_aspirate and dispense as well as their tests --- robotools/evotools/__init__.py | 62 +++++++++++++++------------------- robotools/tests.py | 49 ++++++++++++++++++++++----- 2 files changed, 68 insertions(+), 43 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index d782d83..07e23d1 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -235,7 +235,8 @@ def _prepare_evo_aspirate_dispense_parameters( raise ValueError("Missing required paramter: wells") if not isinstance(wells, (str, list, tuple, numpy.ndarray)): raise ValueError(f"Invalid wells: {wells}") - + if not len(wells) == len(tips): + raise ValueError(f"wells and tips need to have the same length.") if labware_position is None: raise ValueError("Missing required paramter: position") if not all(isinstance(position, int) for position in labware_position) or any( @@ -255,21 +256,22 @@ def _prepare_evo_aspirate_dispense_parameters( raise ValueError(f"Invalid volume: {vol}") if max_volume is not None and vol > max_volume: raise InvalidOperationError(f"Invalid volume: volume of {vol} exceeds max_volume.") - if not len(vol) == len(tips): - raise Exception( - f"Invalid volume: Tips and volume lists have different lengths ({len(tips)} and {len(vol)}, respectively)." - ) - else: - try: - volume = float(volume) - except: - raise ValueError(f"Invalid volume: {volume}") + if not len(volume) == len(tips) == len(wells): + raise Exception( + f"Invalid volume: Tips, wells, and volume lists have different lengths ({len(tips)}, {len(wells)} and {len(volume)}, respectively)." + ) + elif type(volume) == float or type(volume) == int: + # test volume like in the list section if volume < 0 or volume > 7158278 or numpy.isnan(volume): - raise ValueError(f"Invalid volume: {volume}") + raise ValueError(f"Invalid volume: {volume}") if max_volume is not None and volume > max_volume: - raise InvalidOperationError(f"Volume of {volume} exceeds max_volume.") + raise InvalidOperationError(f"Invalid volume: volume of {volume} exceeds max_volume.") + # convert volume to list and multiply list to reach identical length as wells + volume = [float(volume)] + volume = volume * len(wells) + else: + raise ValueError(f"Invalid volume: {volume}") - # optional parameters if liquid_class is None: raise ValueError(f"Missing required parameter: liquid_class") if not isinstance(liquid_class, str) or ";" in liquid_class: @@ -287,8 +289,6 @@ def _prepare_evo_aspirate_dispense_parameters( tip = _int_to_tip(tip) tecan_tips.append(tip) - # apply rounding and corrections for the right string formatting - volume = f"{numpy.round(volume, decimals=2):.2f}" if tecan_tips: return labware, wells, labware_position, volume, liquid_class, tecan_tips else: @@ -955,21 +955,17 @@ def evo_aspirate_well( tips, ) = _prepare_evo_aspirate_dispense_parameters(**kwargs, max_volume=self.max_volume) - # calculate tip_selection based on tips argument + # calculate tip_selection based on tips argument (tips are converted to evotools.Tip in _prepare_evo_aspirate_dispense_parameters) tip_selection = 0 for tip in tips: tip_selection += tip.value - # prepare volume section (handle both the case of one volume as int and a list of individual volumes per tip) + # prepare volume section (volume is converted to list in _prepare_evo_aspirate_dispense_parameters) tip_volumes = "" - if type(volume) == list: - volume_dict = dict(zip(tips, volume)) for tip in [1, 2, 4, 8, 16, 32, 64, 128]: - if tip.value in tips: - if type(volume) == float: - tip_volumes += f'"{volume}",' - elif type(volume) == list: - tip_volumes += f'"{volume_dict[tip]}",' + if tip in [tecantip.value for tecantip in tips]: + tip_volumes += f'"{volume[0]}",' + volume.pop(0) else: tip_volumes += "0," @@ -1097,23 +1093,19 @@ def evo_dispense_well( tips, ) = _prepare_evo_aspirate_dispense_parameters(**kwargs, max_volume=self.max_volume) - # calculate tip_selection based on tips argument + # calculate tip_selection based on tips argument (tips are converted to evotools.Tip in _prepare_evo_aspirate_dispense_parameters) tip_selection = 0 for tip in tips: tip_selection += tip.value - # prepare volume section (handle both the case of one volume as int and a list of individual volumes per tip) - tip_volumes = '' - if type(volume) == list: - volume_dict = dict(zip(tips, volume)) + # prepare volume section (volume is converted to list in _prepare_evo_aspirate_dispense_parameters) + tip_volumes = "" for tip in [1, 2, 4, 8, 16, 32, 64, 128]: - if tip.value in tips: - if type(volume) == float: - tip_volumes += f'\"{volume}\",' - elif type(volume) == list: - tip_volumes += f'\"{volume_dict[tip]}\",' + if tip in [tecantip.value for tecantip in tips]: + tip_volumes += f'"{volume[0]}",' + volume.pop(0) else: - tip_volumes += '0,' + tip_volumes += "0," # convert selection from list of well ids to numpy array with same dimensions as target labware (1: well is selected, 0: well is not selected) selected = evo_make_selection_array(labware.n_rows, labware.n_columns, wells) diff --git a/robotools/tests.py b/robotools/tests.py index 6df5ea9..2e7e145 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -960,7 +960,7 @@ def test_parameter_validation(self) -> None: return # test complete evo_aspirate() command - def test_evo_aspirate(self) -> None: + def test_evo_aspirate1(self) -> None: plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=1000) with evotools.Worklist() as wl: wl.evo_aspirate( @@ -972,15 +972,30 @@ def test_evo_aspirate(self) -> None: liquid_class="Water_DispZmax_AspZmax", ) self.assertEqual( - wl, - [ - 'B;Aspirate(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","750","750",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' - ], + wl[0], + 'B;Aspirate(112,"Water_DispZmax_AspZmax",0,0,0,0,"750.0","750.0","750.0",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' + ) + return + + def test_evo_aspirate2(self) -> None: + plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=1000) + with evotools.Worklist() as wl: + wl.evo_aspirate( + labware=plate, + wells=["E01", "F01", "G01"], + labware_position=(38, 2), + tips=[5, 6, 7], + volumes=[750,730,710], + liquid_class="Water_DispZmax_AspZmax", + ) + self.assertEqual( + wl[0], + 'B;Aspirate(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","730","710",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' ) return # test complete evo_dispense() command - def test_evo_dispense(self) -> None: + def test_evo_dispense1(self) -> None: plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=1000) with evotools.Worklist() as wl: wl.evo_dispense( @@ -993,7 +1008,25 @@ def test_evo_dispense(self) -> None: ) self.assertEqual( wl[0], - 'B;Dispense(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","750","750",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' + 'B;Dispense(112,"Water_DispZmax_AspZmax",0,0,0,0,"750.0","750.0","750.0",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' + ) + return + + # test complete evo_dispense() command + def test_evo_dispense2(self) -> None: + plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=1000) + with evotools.Worklist() as wl: + wl.evo_dispense( + labware=plate, + wells=["E01", "F01", "G01"], + labware_position=(38, 2), + tips=[5, 6, 7], + volumes=[750,730,710], + liquid_class="Water_DispZmax_AspZmax", + ) + self.assertEqual( + wl[0], + 'B;Dispense(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","730","710",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' ) return @@ -1005,7 +1038,7 @@ def test_evo_wash(self) -> None: waste_location=(52, 1), cleaner_location=(52, 0), ) - self.assertEqual(wl, ['B;Wash(255,52,1,52,0,"3.0",500,"4.0",500,10,70,30,1,0,1000,0);']) + self.assertEqual(wl[0], ['B;Wash(255,52,1,52,0,"3.0",500,"4.0",500,10,70,30,1,0,1000,0);']) return def test_comment(self) -> None: From 5708c869f521f22f56a64efc5644f207226859b0 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Fri, 20 Jan 2023 12:09:06 +0100 Subject: [PATCH 47/54] pre-commit --- robotools/evotools/__init__.py | 4 ++-- robotools/tests.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 07e23d1..ccd8171 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -263,12 +263,12 @@ def _prepare_evo_aspirate_dispense_parameters( elif type(volume) == float or type(volume) == int: # test volume like in the list section if volume < 0 or volume > 7158278 or numpy.isnan(volume): - raise ValueError(f"Invalid volume: {volume}") + raise ValueError(f"Invalid volume: {volume}") if max_volume is not None and volume > max_volume: raise InvalidOperationError(f"Invalid volume: volume of {volume} exceeds max_volume.") # convert volume to list and multiply list to reach identical length as wells volume = [float(volume)] - volume = volume * len(wells) + volume = volume * len(wells) else: raise ValueError(f"Invalid volume: {volume}") diff --git a/robotools/tests.py b/robotools/tests.py index 2e7e145..4633bee 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -973,10 +973,10 @@ def test_evo_aspirate1(self) -> None: ) self.assertEqual( wl[0], - 'B;Aspirate(112,"Water_DispZmax_AspZmax",0,0,0,0,"750.0","750.0","750.0",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' + 'B;Aspirate(112,"Water_DispZmax_AspZmax",0,0,0,0,"750.0","750.0","750.0",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);', ) return - + def test_evo_aspirate2(self) -> None: plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=1000) with evotools.Worklist() as wl: @@ -985,12 +985,12 @@ def test_evo_aspirate2(self) -> None: wells=["E01", "F01", "G01"], labware_position=(38, 2), tips=[5, 6, 7], - volumes=[750,730,710], + volumes=[750, 730, 710], liquid_class="Water_DispZmax_AspZmax", ) self.assertEqual( wl[0], - 'B;Aspirate(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","730","710",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' + 'B;Aspirate(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","730","710",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);', ) return @@ -1008,7 +1008,7 @@ def test_evo_dispense1(self) -> None: ) self.assertEqual( wl[0], - 'B;Dispense(112,"Water_DispZmax_AspZmax",0,0,0,0,"750.0","750.0","750.0",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' + 'B;Dispense(112,"Water_DispZmax_AspZmax",0,0,0,0,"750.0","750.0","750.0",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);', ) return @@ -1021,12 +1021,12 @@ def test_evo_dispense2(self) -> None: wells=["E01", "F01", "G01"], labware_position=(38, 2), tips=[5, 6, 7], - volumes=[750,730,710], + volumes=[750, 730, 710], liquid_class="Water_DispZmax_AspZmax", ) self.assertEqual( wl[0], - 'B;Dispense(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","730","710",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);' + 'B;Dispense(112,"Water_DispZmax_AspZmax",0,0,0,0,"750","730","710",0,0,0,0,0,38,2,1,"0C08\xa00000000000000",0,0);', ) return From 7ac2a3d6c32aa12e16f9651a1a0934a451ff79c8 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 23 Jan 2023 10:38:16 +0100 Subject: [PATCH 48/54] fix further errors in tests --- robotools/evotools/__init__.py | 56 +++++++-------- robotools/tests.py | 124 +++++++++++++++++---------------- 2 files changed, 92 insertions(+), 88 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index ccd8171..bc4625e 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -187,7 +187,7 @@ def _prepare_evo_aspirate_dispense_parameters( wells: typing.Union[str, typing.Sequence[str], numpy.ndarray], *, labware_position: typing.Tuple[int, int], - volume: typing.Union[float, typing.List[float]], + volume: typing.Union[float, typing.List[float], int], liquid_class: str, tips: typing.Union[typing.List[Tip], typing.List[int]], max_volume: typing.Optional[int] = None, @@ -202,7 +202,7 @@ def _prepare_evo_aspirate_dispense_parameters( List with target well ID(s) labware_position : tuple Grid position of the target labware on the robotic deck and site position on its carrier, e.g. labware on grid 38, site 2 -> (38,2) - volume : float or list + volume : int, float or list Volume in microliters (will be rounded to 2 decimal places); if several tips are used, these tips may aspirate individual volumes -> use list in these cases liquid_class : str, optional Overwrites the liquid class for this step (max 32 characters) @@ -219,7 +219,7 @@ def _prepare_evo_aspirate_dispense_parameters( List with target well ID(s) labware_position : tuple Grid position of the target labware on the robotic deck and site position on its carrier, e.g. labware on grid 38, site 2 -> (38,2) - volume : float or list + volume : list Volume in microliters (will be rounded to 2 decimal places); if several tips are used, these tips may aspirate individual volumes -> use list in these cases liquid_class : str, optional Overwrites the liquid class for this step (max 32 characters) @@ -227,18 +227,18 @@ def _prepare_evo_aspirate_dispense_parameters( Tip(s) that will be selected (out of tips 1-8) """ if labware is None: - raise ValueError("Missing required paramter: labware") + raise ValueError("Missing required parameter: labware") if not isinstance(labware, liquidhandling.Labware): raise ValueError(f"Invalid labware: {labware}") if wells is None: - raise ValueError("Missing required paramter: wells") + raise ValueError("Missing required parameter: wells") if not isinstance(wells, (str, list, tuple, numpy.ndarray)): raise ValueError(f"Invalid wells: {wells}") if not len(wells) == len(tips): - raise ValueError(f"wells and tips need to have the same length.") + raise ValueError(f"Invalid wells: wells and tips need to have the same length.") if labware_position is None: - raise ValueError("Missing required paramter: position") + raise ValueError("Missing required parameter: position") if not all(isinstance(position, int) for position in labware_position) or any( position < 0 for position in labware_position ): @@ -300,17 +300,17 @@ def _prepare_evo_wash_parameters( tips: typing.Union[typing.List[Tip], typing.List[int]], waste_location: typing.Tuple[int, int], cleaner_location: typing.Tuple[int, int], - arm: int, - waste_vol: float, - waste_delay: int, - cleaner_vol: float, - cleaner_delay: int, - airgap: float, - airgap_speed: int, - retract_speed: int, - fastwash: int, - low_volume: int, -) -> typing.Tuple[list, tuple, tuple, int, float, int, float, int, float, int, int, int, int]: + arm: int = 0, + waste_vol: float = 3.0, + waste_delay: int = 500, + cleaner_vol: float = 4.0, + cleaner_delay: int = 500, + airgap: int = 10, + airgap_speed: int = 70, + retract_speed: int = 30, + fastwash: int = 1, + low_volume: int = 0, +) -> typing.Tuple[list, tuple, tuple, int, float, int, float, int, int, int, int, int, int]: """Validates and prepares aspirate/dispense parameters. Parameters @@ -331,7 +331,7 @@ def _prepare_evo_wash_parameters( Volume in cleaner in mL (0-100) cleaner_delay : int Delay before closing valves in cleaner in ms (0-1000) - airgap : float + airgap : int Volume of airgap in µL which is aspirated after washing the tips (system trailing airgap) (0-100) airgap_speed : int Speed of airgap aspiration in µL/s (1-1000) @@ -360,7 +360,7 @@ def _prepare_evo_wash_parameters( Volume in cleaner in mL (0-100) cleaner_delay : int Delay before closing valves in cleaner in ms (0-1000) - airgap : float + airgap : int Volume of airgap in µL which is aspirated after washing the tips (system trailing airgap) (0-100) airgap_speed : int Speed of airgap aspiration in µL/s (1-1000) @@ -430,8 +430,8 @@ def _prepare_evo_wash_parameters( if airgap is None: raise ValueError("Missing required parameter: airgap") - if not isinstance(airgap, float) or not 0 <= airgap <= 100: - raise ValueError("airgap has to be a float from 0 - 100.") + if not isinstance(airgap, int) or not 0 <= airgap <= 100: + raise ValueError("airgap has to be an int from 0 - 100.") if airgap_speed is None: raise ValueError("Missing required parameter: airgap_speed") @@ -914,7 +914,7 @@ def evo_aspirate_well( labware: liquidhandling.Labware, wells: typing.Union[str, typing.List[str]], labware_position: typing.Tuple[int, int], - volume: typing.Union[float, typing.List[float]], + volume: typing.Union[float, typing.List[float], int], liquid_class: str, tips: typing.Union[typing.List[Tip], typing.List[int]], ) -> None: @@ -930,7 +930,7 @@ def evo_aspirate_well( List with target well ID(s) labware_position : tuple Grid position of the target labware on the robotic deck and site position on its carrier, e.g. labware on grid 38, site 2 -> (38,2) - volume : float or list + volume : int, float or list Volume in microliters (will be rounded to 2 decimal places); if several tips are used, these tips may aspirate individual volumes -> use list in these cases liquid_class : str, optional Overwrites the liquid class for this step (max 32 characters) @@ -1052,7 +1052,7 @@ def evo_dispense_well( labware: liquidhandling.Labware, wells: typing.Union[str, typing.List[str]], labware_position: typing.Tuple[int, int], - volume: typing.Union[float, typing.List[float]], + volume: typing.Union[float, typing.List[float], int], liquid_class: str, tips: typing.Union[typing.List[Tip], typing.List[int]], ) -> None: @@ -1068,7 +1068,7 @@ def evo_dispense_well( List with target well ID(s) labware_position : tuple Grid position of the target labware on the robotic deck and site position on its carrier, e.g. labware on grid 38, site 2 -> (38,2) - volume : float or list + volume : int, float or list Volume in microliters (will be rounded to 2 decimal places); if several tips are used, these tips may aspirate individual volumes -> use list in these cases liquid_class : str, optional Overwrites the liquid class for this step (max 32 characters) @@ -1127,7 +1127,7 @@ def evo_wash( waste_delay: int = 500, cleaner_vol: float = 4.0, cleaner_delay: int = 500, - airgap: float = 10, + airgap: int = 10, airgap_speed: int = 70, retract_speed: int = 30, fastwash: int = 1, @@ -1155,7 +1155,7 @@ def evo_wash( Volume in cleaner in mL (0-100) cleaner_delay : int Delay before closing valves in cleaner in ms (0-1000) - airgap : float + airgap : int Volume of airgap in µL which is aspirated after washing the tips (system trailing airgap) (0-100) airgap_speed : int Speed of airgap aspiration in µL/s (1-1000) diff --git a/robotools/tests.py b/robotools/tests.py index 4633bee..c2bd041 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -599,7 +599,8 @@ def test_parameter_validation(self) -> None: [ plate, ["E01", "F01", "G01"], - 750, + (38, 2), + [750.0, 750.0, 750.0], "Water_DispZmax_AspZmax", [evotools.Tip.T5, evotools.Tip.T6, evotools.Tip.T7], ], @@ -607,7 +608,7 @@ def test_parameter_validation(self) -> None: # test _prepare_evo_wash_parameters # test tips argument checks - tips, _, _, _, _, _, _, _, _, _, _, _, _ = evotools._prepare_evo_aspirate_dispense_parameters( + tips, _, _, _, _, _, _, _, _, _, _, _, _ = evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), @@ -618,76 +619,76 @@ def test_parameter_validation(self) -> None: ) # test waste_location argument checks - with pytest.raises(ValueError, match="Grid (first number in waste_location tuple)"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="Grid \\(first number in waste_location tuple\\)"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(68, 1), cleaner_location=(52, 0), ) - with pytest.raises(ValueError, match="Grid (first number in waste_location tuple)"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="Grid \\(first number in waste_location tuple\\)"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(0, 1), cleaner_location=(52, 0), ) - with pytest.raises(ValueError, match="Grid (first number in waste_location tuple)"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="Grid \\(first number in waste_location tuple\\)"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(1.7, 1), cleaner_location=(52, 0), ) - with pytest.raises(ValueError, match="Site (second number in waste_location tuple)"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="Site \\(second number in waste_location tuple\\)"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, -1), cleaner_location=(52, 0), ) - with pytest.raises(ValueError, match="Site (second number in waste_location tuple)"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="Site \\(second number in waste_location tuple\\)"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 128), cleaner_location=(52, 0), ) - with pytest.raises(ValueError, match="Site (second number in waste_location tuple)"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="Site \\(second number in waste_location tuple\\)"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1.7), cleaner_location=(52, 0), ) # test cleaner_location argument checks - with pytest.raises(ValueError, match="Grid (first number in cleaner_location tuple)"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="Grid \\(first number in cleaner_location tuple\\)"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(68, 1), ) - with pytest.raises(ValueError, match="Grid (first number in cleaner_location tuple)"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="Grid \\(first number in cleaner_location tuple\\)"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(0, 1), ) - with pytest.raises(ValueError, match="Grid (first number in cleaner_location tuple)"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="Grid \\(first number in cleaner_location tuple\\)"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(1.7, 1), ) - with pytest.raises(ValueError, match="Site (second number in cleaner_location tuple)"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="Site \\(second number in cleaner_location tuple\\)"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, -1), ) - with pytest.raises(ValueError, match="Site (second number in cleaner_location tuple)"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="Site \\(second number in cleaner_location tuple\\)"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 128), ) - with pytest.raises(ValueError, match="Site (second number in cleaner_location tuple)"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="Site \\(second number in cleaner_location tuple\\)"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 1.7), @@ -695,7 +696,7 @@ def test_parameter_validation(self) -> None: # test arm argument check with pytest.raises(ValueError, match="Parameter arm"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), @@ -704,21 +705,21 @@ def test_parameter_validation(self) -> None: # test waste_vol argument check with pytest.raises(ValueError, match="waste_vol has to be a float"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), waste_vol=-1.0, ) with pytest.raises(ValueError, match="waste_vol has to be a float"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), waste_vol=101.0, ) with pytest.raises(ValueError, match="waste_vol has to be a float"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), @@ -727,21 +728,21 @@ def test_parameter_validation(self) -> None: # test waste_delay argument check with pytest.raises(ValueError, match="waste_delay has to be an int"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), waste_delay=-1, ) with pytest.raises(ValueError, match="waste_delay has to be an int"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), waste_delay=1001, ) with pytest.raises(ValueError, match="waste_delay has to be an int"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), @@ -750,21 +751,21 @@ def test_parameter_validation(self) -> None: # test cleaner_vol argument check with pytest.raises(ValueError, match="cleaner_vol has to be a float"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), cleaner_vol=-1.0, ) with pytest.raises(ValueError, match="cleaner_vol has to be a float"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), cleaner_vol=101.0, ) with pytest.raises(ValueError, match="cleaner_vol has to be a float"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), @@ -773,21 +774,21 @@ def test_parameter_validation(self) -> None: # test cleaner_delay argument check with pytest.raises(ValueError, match="cleaner_delay has to be an int"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), cleaner_delay=-1, ) with pytest.raises(ValueError, match="cleaner_delay has to be an int"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), cleaner_delay=1001, ) with pytest.raises(ValueError, match="cleaner_delay has to be an int"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), @@ -795,45 +796,45 @@ def test_parameter_validation(self) -> None: ) # test airgap argument check - with pytest.raises(ValueError, match="airgap has to be a float"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="airgap has to be an int"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), - airgap=-1.0, + airgap=-1, ) - with pytest.raises(ValueError, match="airgap has to be a float"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="airgap has to be an int"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), - airgap=101.0, + airgap=101, ) - with pytest.raises(ValueError, match="airgap has to be a float"): - evotools._prepare_evo_aspirate_dispense_parameters( + with pytest.raises(ValueError, match="airgap has to be an int"): + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), - airgap=10, + airgap=10.0, ) # test airgap_speed argument check with pytest.raises(ValueError, match="airgap_speed has to be an int"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), airgap_speed=0, ) with pytest.raises(ValueError, match="airgap_speed has to be an int"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), airgap_speed=1001, ) with pytest.raises(ValueError, match="airgap_speed has to be an int"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), @@ -842,21 +843,21 @@ def test_parameter_validation(self) -> None: # test retract_speed argument check with pytest.raises(ValueError, match="retract_speed has to be an int"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), retract_speed=0, ) with pytest.raises(ValueError, match="retract_speed has to be an int"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), retract_speed=101, ) with pytest.raises(ValueError, match="retract_speed has to be an int"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), @@ -865,14 +866,14 @@ def test_parameter_validation(self) -> None: # test fastwash argument check with pytest.raises(ValueError, match="Parameter fastwash"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), fastwash=2, ) with pytest.raises(ValueError, match="Parameter fastwash"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), @@ -881,14 +882,14 @@ def test_parameter_validation(self) -> None: # test low_volume argument check with pytest.raises(ValueError, match="Parameter low_volume"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), low_volume=2, ) with pytest.raises(ValueError, match="Parameter low_volume"): - evotools._prepare_evo_aspirate_dispense_parameters( + evotools._prepare_evo_wash_parameters( tips=[1, 2], waste_location=(52, 1), cleaner_location=(52, 0), @@ -910,7 +911,7 @@ def test_parameter_validation(self) -> None: retract_speed, fastwash, low_volume, - ) = evotools._prepare_evo_aspirate_dispense_parameters( + ) = evotools._prepare_evo_wash_parameters( tips=[1, 2, 3, 4, 5, 6, 7, 8], waste_location=(52, 1), cleaner_location=(52, 0), @@ -1038,7 +1039,10 @@ def test_evo_wash(self) -> None: waste_location=(52, 1), cleaner_location=(52, 0), ) - self.assertEqual(wl[0], ['B;Wash(255,52,1,52,0,"3.0",500,"4.0",500,10,70,30,1,0,1000,0);']) + self.assertEqual( + wl[0], + 'B;Wash(255,52,1,52,0,"3.0",500,"4.0",500,10,70,30,1,0,1000,0);' + ) return def test_comment(self) -> None: From d0821ec8d13f6cc70134281e6c03d4b00ddb7c6e Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 23 Jan 2023 10:38:56 +0100 Subject: [PATCH 49/54] pre-commit --- robotools/tests.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/robotools/tests.py b/robotools/tests.py index c2bd041..dd1b85a 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -1039,10 +1039,7 @@ def test_evo_wash(self) -> None: waste_location=(52, 1), cleaner_location=(52, 0), ) - self.assertEqual( - wl[0], - 'B;Wash(255,52,1,52,0,"3.0",500,"4.0",500,10,70,30,1,0,1000,0);' - ) + self.assertEqual(wl[0], 'B;Wash(255,52,1,52,0,"3.0",500,"4.0",500,10,70,30,1,0,1000,0);') return def test_comment(self) -> None: From ac14c31affd2fecfd01185dba882b65e8c2d9a30 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 23 Jan 2023 15:45:38 +0100 Subject: [PATCH 50/54] apply rounding to volume of aspirate/dispense --- robotools/evotools/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index bc4625e..6d0f15f 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -271,6 +271,9 @@ def _prepare_evo_aspirate_dispense_parameters( volume = volume * len(wells) else: raise ValueError(f"Invalid volume: {volume}") + + # apply rounding and corrections for the right string formatting + volume = [numpy.round(vol, decimals=2) for vol in volume] if liquid_class is None: raise ValueError(f"Missing required parameter: liquid_class") From b89997fdf1b312f0fb0a6ad3dc82fae9c45885b3 Mon Sep 17 00:00:00 2001 From: "j.niesser" Date: Mon, 23 Jan 2023 15:46:24 +0100 Subject: [PATCH 51/54] pre-commit --- robotools/evotools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 6d0f15f..53c76b5 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -271,7 +271,7 @@ def _prepare_evo_aspirate_dispense_parameters( volume = volume * len(wells) else: raise ValueError(f"Invalid volume: {volume}") - + # apply rounding and corrections for the right string formatting volume = [numpy.round(vol, decimals=2) for vol in volume] From dfb39a6523f35de3303e1a2447839bd5abc63b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20Nie=C3=9Fer?= <104903134+Y0dler@users.noreply.github.com> Date: Tue, 24 Jan 2023 09:59:14 +0100 Subject: [PATCH 52/54] Update robotools/evotools/__init__.py Co-authored-by: Michael Osthege --- robotools/evotools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 53c76b5..b08ce85 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -246,7 +246,7 @@ def _prepare_evo_aspirate_dispense_parameters( if volume is None: raise ValueError("Missing required parameter: volume") - if type(volume) == list: + if isinstance(volume, list): for vol in volume: try: vol = float(vol) From 1d2c0347e8b6dd42707e8db106dd4c74b821bcd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20Nie=C3=9Fer?= <104903134+Y0dler@users.noreply.github.com> Date: Tue, 24 Jan 2023 10:00:57 +0100 Subject: [PATCH 53/54] Apply suggestions from code review Co-authored-by: Michael Osthege --- robotools/evotools/__init__.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index b08ce85..4ec69b2 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -260,20 +260,19 @@ def _prepare_evo_aspirate_dispense_parameters( raise Exception( f"Invalid volume: Tips, wells, and volume lists have different lengths ({len(tips)}, {len(wells)} and {len(volume)}, respectively)." ) - elif type(volume) == float or type(volume) == int: + elif isinstance(volume, (float, int)): # test volume like in the list section if volume < 0 or volume > 7158278 or numpy.isnan(volume): raise ValueError(f"Invalid volume: {volume}") if max_volume is not None and volume > max_volume: raise InvalidOperationError(f"Invalid volume: volume of {volume} exceeds max_volume.") # convert volume to list and multiply list to reach identical length as wells - volume = [float(volume)] - volume = volume * len(wells) + volume = [float(volume)] * len(wells) else: raise ValueError(f"Invalid volume: {volume}") # apply rounding and corrections for the right string formatting - volume = [numpy.round(vol, decimals=2) for vol in volume] + volume = numpy.round(volume, decimals=2).tolist() if liquid_class is None: raise ValueError(f"Missing required parameter: liquid_class") @@ -283,7 +282,7 @@ def _prepare_evo_aspirate_dispense_parameters( if tips is None: raise ValueError(f"Missing required parameter: tips") for tip in tips: - if type(tip) is not int and type(tip) is not Tip: + if not isinstance(tip, (int, Tip)): raise ValueError(f"Invalid type of tips: {type(tip)}. Has to be int or Tip.") tecan_tips = [] for tip in tips: From 91cfd047df953ef921c2fe94d0046aaff95843dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20Nie=C3=9Fer?= <104903134+Y0dler@users.noreply.github.com> Date: Tue, 24 Jan 2023 10:04:41 +0100 Subject: [PATCH 54/54] Apply suggestions from code review Co-authored-by: Michael Osthege --- robotools/evotools/__init__.py | 52 +++++++++++----------------------- robotools/tests.py | 4 --- 2 files changed, 16 insertions(+), 40 deletions(-) diff --git a/robotools/evotools/__init__.py b/robotools/evotools/__init__.py index 4ec69b2..df65075 100644 --- a/robotools/evotools/__init__.py +++ b/robotools/evotools/__init__.py @@ -291,10 +291,7 @@ def _prepare_evo_aspirate_dispense_parameters( tip = _int_to_tip(tip) tecan_tips.append(tip) - if tecan_tips: - return labware, wells, labware_position, volume, liquid_class, tecan_tips - else: - return labware, wells, labware_position, volume, liquid_class, tips + return labware, wells, labware_position, volume, liquid_class, tecan_tips def _prepare_evo_wash_parameters( @@ -459,38 +456,21 @@ def _prepare_evo_wash_parameters( if not low_volume == 0 and not low_volume == 1: raise ValueError("Parameter low_volume has to be 0 (no fast-wash) or 1 (use fast-wash).") - if tecan_tips: - return ( - tecan_tips, - waste_location, - cleaner_location, - arm, - waste_vol, - waste_delay, - cleaner_vol, - cleaner_delay, - airgap, - airgap_speed, - retract_speed, - fastwash, - low_volume, - ) - else: - return ( - tips, - waste_location, - cleaner_location, - arm, - waste_vol, - waste_delay, - cleaner_vol, - cleaner_delay, - airgap, - airgap_speed, - retract_speed, - fastwash, - low_volume, - ) + return ( + tecan_tips, + waste_location, + cleaner_location, + arm, + waste_vol, + waste_delay, + cleaner_vol, + cleaner_delay, + airgap, + airgap_speed, + retract_speed, + fastwash, + low_volume, + ) def _optimize_partition_by( diff --git a/robotools/tests.py b/robotools/tests.py index dd1b85a..c411604 100644 --- a/robotools/tests.py +++ b/robotools/tests.py @@ -960,7 +960,6 @@ def test_parameter_validation(self) -> None: return - # test complete evo_aspirate() command def test_evo_aspirate1(self) -> None: plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=1000) with evotools.Worklist() as wl: @@ -995,7 +994,6 @@ def test_evo_aspirate2(self) -> None: ) return - # test complete evo_dispense() command def test_evo_dispense1(self) -> None: plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=1000) with evotools.Worklist() as wl: @@ -1013,7 +1011,6 @@ def test_evo_dispense1(self) -> None: ) return - # test complete evo_dispense() command def test_evo_dispense2(self) -> None: plate = liquidhandling.Labware("DWP", 8, 12, min_volume=0, max_volume=2000, initial_volumes=1000) with evotools.Worklist() as wl: @@ -1031,7 +1028,6 @@ def test_evo_dispense2(self) -> None: ) return - # test complete evo_wash() command def test_evo_wash(self) -> None: with evotools.Worklist() as wl: wl.evo_wash(