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>
@classmethod
def from_num(cls, plate: int) -> Plate:
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")
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>
@classmethod
def from_str(cls, let: str) -> Letter:
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")
@classmethod
def from_int(cls, num: int) -> Letter:
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)
FiftyFourVialPlate( plate: Plate, letter: Letter, num: Num)
plate: Plate
letter: Letter
num: Num
def value(self) -> int:
110    def value(self) -> int:
111        return self.plate.value + self.letter.value + self.num.value
@classmethod
def from_tray_row_col(cls, tray: int, row: int, col: int):
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.")
@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

FiftyFourVialPlate object 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")
@classmethod
def from_int( cls, num: int) -> Union[FiftyFourVialPlate, VialBar, LocationPlus]:
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

LocationPlus(unit: int, tray: int, row: int, col: int)
unit: int
tray: int
row: int
col: int
Tray = typing.Union[FiftyFourVialPlate, VialBar, LocationPlus]