pychemstation.utils.tray_types
1from __future__ import annotations 2 3from dataclasses import dataclass 4from enum import Enum 5from typing import Union 6 7 8class Num(Enum): 9 ONE = 1 10 TWO = 2 11 THREE = 3 12 FOUR = 4 13 FIVE = 5 14 SIX = 6 15 SEVEN = 7 16 EIGHT = 8 17 NINE = 9 18 19 @classmethod 20 def from_num(cls, num: int): 21 num_mapping = { 22 1: Num.ONE, 23 2: Num.TWO, 24 3: Num.THREE, 25 4: Num.FOUR, 26 5: Num.FIVE, 27 6: Num.SIX, 28 7: Num.SEVEN, 29 8: Num.EIGHT, 30 9: Num.NINE, 31 } 32 33 if num in num_mapping: 34 return num_mapping[num] 35 else: 36 raise ValueError("Num must be between 1 and 9") 37 38 39class Plate(Enum): 40 ONE = -96 41 TWO = 4000 42 43 @classmethod 44 def from_num(cls, plate: int) -> Plate: 45 if 1 <= plate <= 2: 46 return Plate.ONE if plate == 1 else Plate.TWO 47 raise ValueError("Plate is one or 1 or 2") 48 49 50class Letter(Enum): 51 A = 4191 52 B = 4255 53 C = 4319 54 D = 4383 55 E = 4447 56 F = 4511 57 58 @classmethod 59 def from_str(cls, let: str) -> Letter: 60 letter_mapping = { 61 "A": Letter.A, 62 "B": Letter.B, 63 "C": Letter.C, 64 "D": Letter.D, 65 "E": Letter.E, 66 "F": Letter.F, 67 } 68 69 if let in letter_mapping: 70 return letter_mapping[let] 71 else: 72 raise ValueError("Letter must be one of A to F") 73 74 @classmethod 75 def from_int(cls, num: int) -> Letter: 76 letter_mapping = { 77 "A": Letter.A, 78 "B": Letter.B, 79 "C": Letter.C, 80 "D": Letter.D, 81 "E": Letter.E, 82 "F": Letter.F, 83 } 84 85 if num <= len(letter_mapping): 86 return list(letter_mapping.values())[num] 87 else: 88 raise ValueError("Letter must be one of A to F") 89 90 91@dataclass 92class FiftyFourVialPlate: 93 """Class to represent the 54 vial tray. 94 95 :param plate: one of the two, (P1 or P2) 96 :param letter: one of the rows, (A B C D E F) 97 :param num: one of the columns, (1 2 3 4 5 6 7 8 9) 98 99 Examples: 100 >>> from pychemstation.utils.tray_types import FiftyFourVialPlate 101 >>> FiftyFourVialPlate.from_str("P1-A2") 102 >>> FiftyFourVialPlate(plate=Plate.TWO, letter=Letter.A, num=Num.THREE) 103 """ 104 105 plate: Plate 106 letter: Letter 107 num: Num 108 109 def value(self) -> int: 110 return self.plate.value + self.letter.value + self.num.value 111 112 @classmethod 113 def from_tray_row_col(cls, tray: int, row: int, col: int): 114 try: 115 return FiftyFourVialPlate( 116 plate=Plate.from_num(tray), 117 letter=Letter.from_int(row), 118 num=Num.from_num(col), 119 ) 120 except Exception: 121 raise ValueError("Could not parse tray location.") 122 123 @classmethod 124 def from_str(cls, loc: str): 125 """Converts a string representing the vial location into numerical representation for Chemstation. 126 127 :param loc: vial location 128 :return: `FiftyFourVialPlate` object representing the vial location 129 :raises: ValueError if string is invalid tray location 130 131 Examples: 132 >>> from pychemstation.utils.tray_types import FiftyFourVialPlate 133 >>> vial_location = FiftyFourVialPlate.from_str("P2-F4") 134 """ 135 if len(loc) != 5: 136 raise ValueError( 137 "Plate locations must be PX-LY, where X is either 1 or 2 and Y is 1 to 9" 138 ) 139 try: 140 plate = int(loc[1]) 141 letter = loc[3] 142 num = int(loc[4]) 143 return FiftyFourVialPlate( 144 plate=Plate.from_num(plate), 145 letter=Letter.from_str(letter), 146 num=Num.from_num(num), 147 ) 148 except Exception: 149 raise ValueError( 150 "Plate locations must be PX-LY, where X is either 1 or 2 and Y is 1 to 9" 151 ) 152 153 @classmethod 154 def from_int(cls, num: int) -> Tray: 155 """Converts an integer representation of a vial location to a `FiftyFourVialPlate` object 156 157 :param num: numerical representation of a vial location 158 :return: the proper vial location object 159 :raises: ValueError no matching can be made 160 161 Examples: 162 >>> vial_location = FiftyFourVialPlate.from_int(4097) 163 """ 164 if num in range(1, 11): 165 return VialBar(num) 166 167 row_starts = [ 168 # plate 1 169 FiftyFourVialPlate.from_str("P1-F1"), 170 FiftyFourVialPlate.from_str("P1-E1"), 171 FiftyFourVialPlate.from_str("P1-D1"), 172 FiftyFourVialPlate.from_str("P1-C1"), 173 FiftyFourVialPlate.from_str("P1-B1"), 174 FiftyFourVialPlate.from_str("P1-A1"), 175 # plate 2 176 FiftyFourVialPlate.from_str("P2-F1"), 177 FiftyFourVialPlate.from_str("P2-E1"), 178 FiftyFourVialPlate.from_str("P2-D1"), 179 FiftyFourVialPlate.from_str("P2-C1"), 180 FiftyFourVialPlate.from_str("P2-B1"), 181 FiftyFourVialPlate.from_str("P2-A1"), 182 ] 183 184 # find which row 185 possible_row = None 186 for i in range(0, 6): 187 p1_val = row_starts[i].value() 188 p2_val = row_starts[6 + i].value() 189 if num >= p2_val: 190 possible_row = row_starts[6 + i] 191 elif p1_val <= num < row_starts[-1].value(): 192 possible_row = row_starts[i] 193 if possible_row: 194 break 195 196 # determine which num 197 if possible_row: 198 starting_loc = possible_row 199 base_val = starting_loc.plate.value + starting_loc.letter.value 200 for i in range(1, 10): 201 if num - i == base_val: 202 return FiftyFourVialPlate( 203 plate=starting_loc.plate, 204 letter=starting_loc.letter, 205 num=Num.from_num(i), 206 ) 207 raise ValueError("Number didn't match any location. " + str(num)) 208 209 210class VialBar(Enum): 211 """Class to represent the vial bar, has 10 locations. 212 213 Examples: 214 >>> vial_bar_2 = VialBar(2) 215 >>> vial_bar_10 = VialBar.TEN 216 """ 217 218 ONE = 1 219 TWO = 2 220 THREE = 3 221 FOUR = 4 222 FIVE = 5 223 SIX = 6 224 SEVEN = 7 225 EIGHT = 8 226 NINE = 9 227 TEN = 10 228 229 230@dataclass 231class LocationPlus: 232 """Not sure what location refers to, but part the `Draw` function for specifying an `InjectorTable`""" 233 234 unit: int 235 tray: int 236 row: int 237 col: int 238 239 240Tray = Union[FiftyFourVialPlate, VialBar, LocationPlus]
class
Num(enum.Enum):
9class Num(Enum): 10 ONE = 1 11 TWO = 2 12 THREE = 3 13 FOUR = 4 14 FIVE = 5 15 SIX = 6 16 SEVEN = 7 17 EIGHT = 8 18 NINE = 9 19 20 @classmethod 21 def from_num(cls, num: int): 22 num_mapping = { 23 1: Num.ONE, 24 2: Num.TWO, 25 3: Num.THREE, 26 4: Num.FOUR, 27 5: Num.FIVE, 28 6: Num.SIX, 29 7: Num.SEVEN, 30 8: Num.EIGHT, 31 9: Num.NINE, 32 } 33 34 if num in num_mapping: 35 return num_mapping[num] 36 else: 37 raise ValueError("Num must be between 1 and 9")
ONE =
<Num.ONE: 1>
TWO =
<Num.TWO: 2>
THREE =
<Num.THREE: 3>
FOUR =
<Num.FOUR: 4>
FIVE =
<Num.FIVE: 5>
SIX =
<Num.SIX: 6>
SEVEN =
<Num.SEVEN: 7>
EIGHT =
<Num.EIGHT: 8>
NINE =
<Num.NINE: 9>
@classmethod
def
from_num(cls, num: int):
20 @classmethod 21 def from_num(cls, num: int): 22 num_mapping = { 23 1: Num.ONE, 24 2: Num.TWO, 25 3: Num.THREE, 26 4: Num.FOUR, 27 5: Num.FIVE, 28 6: Num.SIX, 29 7: Num.SEVEN, 30 8: Num.EIGHT, 31 9: Num.NINE, 32 } 33 34 if num in num_mapping: 35 return num_mapping[num] 36 else: 37 raise ValueError("Num must be between 1 and 9")
class
Plate(enum.Enum):
40class Plate(Enum): 41 ONE = -96 42 TWO = 4000 43 44 @classmethod 45 def from_num(cls, plate: int) -> Plate: 46 if 1 <= plate <= 2: 47 return Plate.ONE if plate == 1 else Plate.TWO 48 raise ValueError("Plate is one or 1 or 2")
ONE =
<Plate.ONE: -96>
TWO =
<Plate.TWO: 4000>
class
Letter(enum.Enum):
51class Letter(Enum): 52 A = 4191 53 B = 4255 54 C = 4319 55 D = 4383 56 E = 4447 57 F = 4511 58 59 @classmethod 60 def from_str(cls, let: str) -> Letter: 61 letter_mapping = { 62 "A": Letter.A, 63 "B": Letter.B, 64 "C": Letter.C, 65 "D": Letter.D, 66 "E": Letter.E, 67 "F": Letter.F, 68 } 69 70 if let in letter_mapping: 71 return letter_mapping[let] 72 else: 73 raise ValueError("Letter must be one of A to F") 74 75 @classmethod 76 def from_int(cls, num: int) -> Letter: 77 letter_mapping = { 78 "A": Letter.A, 79 "B": Letter.B, 80 "C": Letter.C, 81 "D": Letter.D, 82 "E": Letter.E, 83 "F": Letter.F, 84 } 85 86 if num <= len(letter_mapping): 87 return list(letter_mapping.values())[num] 88 else: 89 raise ValueError("Letter must be one of A to F")
A =
<Letter.A: 4191>
B =
<Letter.B: 4255>
C =
<Letter.C: 4319>
D =
<Letter.D: 4383>
E =
<Letter.E: 4447>
F =
<Letter.F: 4511>
59 @classmethod 60 def from_str(cls, let: str) -> Letter: 61 letter_mapping = { 62 "A": Letter.A, 63 "B": Letter.B, 64 "C": Letter.C, 65 "D": Letter.D, 66 "E": Letter.E, 67 "F": Letter.F, 68 } 69 70 if let in letter_mapping: 71 return letter_mapping[let] 72 else: 73 raise ValueError("Letter must be one of A to F")
75 @classmethod 76 def from_int(cls, num: int) -> Letter: 77 letter_mapping = { 78 "A": Letter.A, 79 "B": Letter.B, 80 "C": Letter.C, 81 "D": Letter.D, 82 "E": Letter.E, 83 "F": Letter.F, 84 } 85 86 if num <= len(letter_mapping): 87 return list(letter_mapping.values())[num] 88 else: 89 raise ValueError("Letter must be one of A to F")
@dataclass
class
FiftyFourVialPlate:
92@dataclass 93class FiftyFourVialPlate: 94 """Class to represent the 54 vial tray. 95 96 :param plate: one of the two, (P1 or P2) 97 :param letter: one of the rows, (A B C D E F) 98 :param num: one of the columns, (1 2 3 4 5 6 7 8 9) 99 100 Examples: 101 >>> from pychemstation.utils.tray_types import FiftyFourVialPlate 102 >>> FiftyFourVialPlate.from_str("P1-A2") 103 >>> FiftyFourVialPlate(plate=Plate.TWO, letter=Letter.A, num=Num.THREE) 104 """ 105 106 plate: Plate 107 letter: Letter 108 num: Num 109 110 def value(self) -> int: 111 return self.plate.value + self.letter.value + self.num.value 112 113 @classmethod 114 def from_tray_row_col(cls, tray: int, row: int, col: int): 115 try: 116 return FiftyFourVialPlate( 117 plate=Plate.from_num(tray), 118 letter=Letter.from_int(row), 119 num=Num.from_num(col), 120 ) 121 except Exception: 122 raise ValueError("Could not parse tray location.") 123 124 @classmethod 125 def from_str(cls, loc: str): 126 """Converts a string representing the vial location into numerical representation for Chemstation. 127 128 :param loc: vial location 129 :return: `FiftyFourVialPlate` object representing the vial location 130 :raises: ValueError if string is invalid tray location 131 132 Examples: 133 >>> from pychemstation.utils.tray_types import FiftyFourVialPlate 134 >>> vial_location = FiftyFourVialPlate.from_str("P2-F4") 135 """ 136 if len(loc) != 5: 137 raise ValueError( 138 "Plate locations must be PX-LY, where X is either 1 or 2 and Y is 1 to 9" 139 ) 140 try: 141 plate = int(loc[1]) 142 letter = loc[3] 143 num = int(loc[4]) 144 return FiftyFourVialPlate( 145 plate=Plate.from_num(plate), 146 letter=Letter.from_str(letter), 147 num=Num.from_num(num), 148 ) 149 except Exception: 150 raise ValueError( 151 "Plate locations must be PX-LY, where X is either 1 or 2 and Y is 1 to 9" 152 ) 153 154 @classmethod 155 def from_int(cls, num: int) -> Tray: 156 """Converts an integer representation of a vial location to a `FiftyFourVialPlate` object 157 158 :param num: numerical representation of a vial location 159 :return: the proper vial location object 160 :raises: ValueError no matching can be made 161 162 Examples: 163 >>> vial_location = FiftyFourVialPlate.from_int(4097) 164 """ 165 if num in range(1, 11): 166 return VialBar(num) 167 168 row_starts = [ 169 # plate 1 170 FiftyFourVialPlate.from_str("P1-F1"), 171 FiftyFourVialPlate.from_str("P1-E1"), 172 FiftyFourVialPlate.from_str("P1-D1"), 173 FiftyFourVialPlate.from_str("P1-C1"), 174 FiftyFourVialPlate.from_str("P1-B1"), 175 FiftyFourVialPlate.from_str("P1-A1"), 176 # plate 2 177 FiftyFourVialPlate.from_str("P2-F1"), 178 FiftyFourVialPlate.from_str("P2-E1"), 179 FiftyFourVialPlate.from_str("P2-D1"), 180 FiftyFourVialPlate.from_str("P2-C1"), 181 FiftyFourVialPlate.from_str("P2-B1"), 182 FiftyFourVialPlate.from_str("P2-A1"), 183 ] 184 185 # find which row 186 possible_row = None 187 for i in range(0, 6): 188 p1_val = row_starts[i].value() 189 p2_val = row_starts[6 + i].value() 190 if num >= p2_val: 191 possible_row = row_starts[6 + i] 192 elif p1_val <= num < row_starts[-1].value(): 193 possible_row = row_starts[i] 194 if possible_row: 195 break 196 197 # determine which num 198 if possible_row: 199 starting_loc = possible_row 200 base_val = starting_loc.plate.value + starting_loc.letter.value 201 for i in range(1, 10): 202 if num - i == base_val: 203 return FiftyFourVialPlate( 204 plate=starting_loc.plate, 205 letter=starting_loc.letter, 206 num=Num.from_num(i), 207 ) 208 raise ValueError("Number didn't match any location. " + str(num))
Class to represent the 54 vial tray.
Parameters
- plate: one of the two, (P1 or P2)
- letter: one of the rows, (A B C D E F)
- num: one of the columns, (1 2 3 4 5 6 7 8 9)
Examples:
>>> from pychemstation.utils.tray_types import FiftyFourVialPlate
>>> FiftyFourVialPlate.from_str("P1-A2")
>>> FiftyFourVialPlate(plate=Plate.TWO, letter=Letter.A, num=Num.THREE)
plate: Plate
letter: Letter
num: Num
@classmethod
def
from_str(cls, loc: str):
124 @classmethod 125 def from_str(cls, loc: str): 126 """Converts a string representing the vial location into numerical representation for Chemstation. 127 128 :param loc: vial location 129 :return: `FiftyFourVialPlate` object representing the vial location 130 :raises: ValueError if string is invalid tray location 131 132 Examples: 133 >>> from pychemstation.utils.tray_types import FiftyFourVialPlate 134 >>> vial_location = FiftyFourVialPlate.from_str("P2-F4") 135 """ 136 if len(loc) != 5: 137 raise ValueError( 138 "Plate locations must be PX-LY, where X is either 1 or 2 and Y is 1 to 9" 139 ) 140 try: 141 plate = int(loc[1]) 142 letter = loc[3] 143 num = int(loc[4]) 144 return FiftyFourVialPlate( 145 plate=Plate.from_num(plate), 146 letter=Letter.from_str(letter), 147 num=Num.from_num(num), 148 ) 149 except Exception: 150 raise ValueError( 151 "Plate locations must be PX-LY, where X is either 1 or 2 and Y is 1 to 9" 152 )
Converts a string representing the vial location into numerical representation for Chemstation.
Parameters
- loc: vial location
Returns
FiftyFourVialPlateobject representing the vial location
Raises
- ValueError if string is invalid tray location
Examples:
>>> from pychemstation.utils.tray_types import FiftyFourVialPlate
>>> vial_location = FiftyFourVialPlate.from_str("P2-F4")
154 @classmethod 155 def from_int(cls, num: int) -> Tray: 156 """Converts an integer representation of a vial location to a `FiftyFourVialPlate` object 157 158 :param num: numerical representation of a vial location 159 :return: the proper vial location object 160 :raises: ValueError no matching can be made 161 162 Examples: 163 >>> vial_location = FiftyFourVialPlate.from_int(4097) 164 """ 165 if num in range(1, 11): 166 return VialBar(num) 167 168 row_starts = [ 169 # plate 1 170 FiftyFourVialPlate.from_str("P1-F1"), 171 FiftyFourVialPlate.from_str("P1-E1"), 172 FiftyFourVialPlate.from_str("P1-D1"), 173 FiftyFourVialPlate.from_str("P1-C1"), 174 FiftyFourVialPlate.from_str("P1-B1"), 175 FiftyFourVialPlate.from_str("P1-A1"), 176 # plate 2 177 FiftyFourVialPlate.from_str("P2-F1"), 178 FiftyFourVialPlate.from_str("P2-E1"), 179 FiftyFourVialPlate.from_str("P2-D1"), 180 FiftyFourVialPlate.from_str("P2-C1"), 181 FiftyFourVialPlate.from_str("P2-B1"), 182 FiftyFourVialPlate.from_str("P2-A1"), 183 ] 184 185 # find which row 186 possible_row = None 187 for i in range(0, 6): 188 p1_val = row_starts[i].value() 189 p2_val = row_starts[6 + i].value() 190 if num >= p2_val: 191 possible_row = row_starts[6 + i] 192 elif p1_val <= num < row_starts[-1].value(): 193 possible_row = row_starts[i] 194 if possible_row: 195 break 196 197 # determine which num 198 if possible_row: 199 starting_loc = possible_row 200 base_val = starting_loc.plate.value + starting_loc.letter.value 201 for i in range(1, 10): 202 if num - i == base_val: 203 return FiftyFourVialPlate( 204 plate=starting_loc.plate, 205 letter=starting_loc.letter, 206 num=Num.from_num(i), 207 ) 208 raise ValueError("Number didn't match any location. " + str(num))
Converts an integer representation of a vial location to a FiftyFourVialPlate object
Parameters
- num: numerical representation of a vial location
Returns
the proper vial location object
Raises
- ValueError no matching can be made
Examples:
>>> vial_location = FiftyFourVialPlate.from_int(4097)
class
VialBar(enum.Enum):
211class VialBar(Enum): 212 """Class to represent the vial bar, has 10 locations. 213 214 Examples: 215 >>> vial_bar_2 = VialBar(2) 216 >>> vial_bar_10 = VialBar.TEN 217 """ 218 219 ONE = 1 220 TWO = 2 221 THREE = 3 222 FOUR = 4 223 FIVE = 5 224 SIX = 6 225 SEVEN = 7 226 EIGHT = 8 227 NINE = 9 228 TEN = 10
Class to represent the vial bar, has 10 locations.
Examples:
>>> vial_bar_2 = VialBar(2)
>>> vial_bar_10 = VialBar.TEN
ONE =
<VialBar.ONE: 1>
TWO =
<VialBar.TWO: 2>
THREE =
<VialBar.THREE: 3>
FOUR =
<VialBar.FOUR: 4>
FIVE =
<VialBar.FIVE: 5>
SIX =
<VialBar.SIX: 6>
SEVEN =
<VialBar.SEVEN: 7>
EIGHT =
<VialBar.EIGHT: 8>
NINE =
<VialBar.NINE: 9>
TEN =
<VialBar.TEN: 10>
@dataclass
class
LocationPlus:
231@dataclass 232class LocationPlus: 233 """Not sure what location refers to, but part the `Draw` function for specifying an `InjectorTable`""" 234 235 unit: int 236 tray: int 237 row: int 238 col: int
Not sure what location refers to, but part the Draw function for specifying an InjectorTable
Tray =
typing.Union[FiftyFourVialPlate, VialBar, LocationPlus]