예제 #1
0
파일: base.py 프로젝트: papagr/TheLMA
 def __create_stock_rack_worklist_series(self):
     # The transfer for each worklist series are derived from the stock
     # rack layouts.
     ticket_number = self._iso_request.experiment_metadata.ticket_number
     robot_specs = self._get_stock_transfer_pipetting_specs()
     for sr_marker in sorted(self._stock_rack_layouts.keys()):
         sr_layout = self._stock_rack_layouts[sr_marker]
         worklist_series = WorklistSeries()
         for rack_marker in self.__get_sorted_plate_markers():
             transfers = []
             for sr_pos in sr_layout.working_positions():
                 psts = sr_pos.get_planned_sample_transfers(rack_marker)
                 transfers.extend(psts)
             if len(transfers) < 1: continue
             wl_index = len(worklist_series)
             wl_label = LABELS.create_worklist_label(
                 ticket_number,
                 worklist_number=(wl_index + 1),
                 target_rack_marker=rack_marker,
                 source_rack_marker=sr_marker)
             worklist = PlannedWorklist(
                 label=wl_label,
                 transfer_type=TRANSFER_TYPES.SAMPLE_TRANSFER,
                 planned_liquid_transfers=transfers,
                 pipetting_specs=robot_specs)
             worklist_series.add_worklist(wl_index, worklist)
         self.__stock_transfer_series[sr_marker] = worklist_series
예제 #2
0
파일: base.py 프로젝트: helixyte/TheLMA
 def __create_stock_rack_worklist_series(self):
     # The transfer for each worklist series are derived from the stock
     # rack layouts.
     ticket_number = self._iso_request.experiment_metadata.ticket_number
     robot_specs = self._get_stock_transfer_pipetting_specs()
     for sr_marker in sorted(self._stock_rack_layouts.keys()):
         sr_layout = self._stock_rack_layouts[sr_marker]
         worklist_series = WorklistSeries()
         for rack_marker in self.__get_sorted_plate_markers():
             transfers = []
             for sr_pos in sr_layout.working_positions():
                 psts = sr_pos.get_planned_sample_transfers(rack_marker)
                 transfers.extend(psts)
             if len(transfers) < 1: continue
             wl_index = len(worklist_series)
             wl_label = LABELS.create_worklist_label(ticket_number,
                              worklist_number=(wl_index + 1),
                              target_rack_marker=rack_marker,
                              source_rack_marker=sr_marker)
             worklist = PlannedWorklist(label=wl_label,
                            transfer_type=TRANSFER_TYPES.SAMPLE_TRANSFER,
                            planned_liquid_transfers=transfers,
                            pipetting_specs=robot_specs)
             worklist_series.add_worklist(wl_index, worklist)
         self.__stock_transfer_series[sr_marker] = worklist_series
예제 #3
0
 def run(self):
     self.reset()
     self.add_info('Generate worklist series ...')
     self.__check_input()
     if not self.has_errors():
         self.__series_map[self.ISO_KEY] = WorklistSeries()
         self.__series_map[self.JOB_KEY] = WorklistSeries()
         self._sort_layouts()
         self.__create_buffer_worklists()
         self.__create_transfer_worklists()
     if not self.has_errors():
         self.return_value = self.__series_map
         self.add_info('Worklist series generation completed.')
예제 #4
0
파일: generation.py 프로젝트: papagr/TheLMA
 def run(self):
     self.reset()
     self.add_info('Start worklist generation ...')
     self.__check_input()
     if not self.has_errors(): self.__sort_into_sectors()
     if not self.has_errors():
         self.__worklist_series = WorklistSeries()
         self.__create_stock_rack_buffer_worklists()
         self.__create_source_plate_buffer_worklists()
         self.__create_stock_to_prep_worklists()
         self.__create_prep_to_aliquot_worklist()
     if not self.has_errors():
         self.return_value = self.__worklist_series
         self.add_info('Worklist generation completed.')
예제 #5
0
파일: writer.py 프로젝트: helixyte/TheLMA
 def __make_stock_rack_worklist_series(self, label, volume, layout):
     # Builds a sector 0 -> sector 0 rack transfer worklist series.
     pip_specs_cy = get_pipetting_specs_cybio()
     wl_series = WorklistSeries()
     psts = []
     if layout is None:
         # Rack transfer (only for pool stock rack -> prep plate transfer).
         pst = PlannedRackSampleTransfer.get_entity(volume, 1, 0, 0)
         psts.append(pst)
         pwl_type = TRANSFER_TYPES.RACK_SAMPLE_TRANSFER
     else:
         # Sample transfers (for single stock transfer).
         for rack_pos in layout.get_positions():
             pst = PlannedSampleTransfer.get_entity(volume, rack_pos,
                                                    rack_pos)
             psts.append(pst)
         pwl_type = TRANSFER_TYPES.SAMPLE_TRANSFER
     wl = PlannedWorklist(label, pwl_type,
                          pip_specs_cy, planned_liquid_transfers=psts)
     wl_series.add_worklist(0, wl)
     return wl_series
예제 #6
0
 def __make_stock_rack_worklist_series(self, label, volume, layout):
     # Builds a sector 0 -> sector 0 rack transfer worklist series.
     pip_specs_cy = get_pipetting_specs_cybio()
     wl_series = WorklistSeries()
     psts = []
     if layout is None:
         # Rack transfer (only for pool stock rack -> prep plate transfer).
         pst = PlannedRackSampleTransfer.get_entity(volume, 1, 0, 0)
         psts.append(pst)
         pwl_type = TRANSFER_TYPES.RACK_SAMPLE_TRANSFER
     else:
         # Sample transfers (for single stock transfer).
         for rack_pos in layout.get_positions():
             pst = PlannedSampleTransfer.get_entity(volume, rack_pos,
                                                    rack_pos)
             psts.append(pst)
         pwl_type = TRANSFER_TYPES.SAMPLE_TRANSFER
     wl = PlannedWorklist(label,
                          pwl_type,
                          pip_specs_cy,
                          planned_liquid_transfers=psts)
     wl_series.add_worklist(0, wl)
     return wl_series
예제 #7
0
 def run(self):
     self.reset()
     self.add_info("Start worklist generation ...")
     self.__check_input()
     if not self.has_errors():
         self.__sort_into_sectors()
     if not self.has_errors():
         self.__worklist_series = WorklistSeries()
         if not self.pool_buffer_volume is None:
             self.__create_pool_rack_buffer_worklists()
     if not self.has_errors():
         self.__create_preparation_plate_buffer_worklists()
     if not self.has_errors():
         self.__create_prep_to_aliquot_worklist()
     if not self.has_errors():
         self.return_value = self.__worklist_series
         self.add_info("Worklist generation completed.")
예제 #8
0
파일: worklist.py 프로젝트: papagr/TheLMA
 def __generate_cell_plate_worklist_for_experiment_design(
         self, transfer_index, cell_index):
     # Generates the cell plate worklists for the experiment design as
     # storage location.
     self.add_debug('Create cell plate worklists for experiment design ...')
     if self.__design_series is None:
         self.__design_series = WorklistSeries()
     transfer_generator = _CybioTransferWorklistGenerator(self.label,
                                                          parent=self)
     self.__generate_transfer_worklist(transfer_generator, transfer_index,
                                       self.__design_series)
     for rack_pos, tf_pos in self.source_layout.iterpositions():
         tf_pos.cell_plate_positions = [rack_pos]
     cell_generator = _CellSuspensionWorklistGenerator(self.label,
                                                       self.source_layout,
                                                       parent=self)
     self.__generate_cell_worklist(cell_generator, cell_index,
                                   self.__design_series)
예제 #9
0
파일: worklist.py 프로젝트: papagr/TheLMA
 def __generate_cell_plate_worklist_for_racks(self, transfer_index,
                                              cell_index):
     # Generates the cell plate worklists for the experiment design racks
     # as storage locations.
     self.add_debug('Create cell plate worklists for design racks ...')
     for design_rack in self.experiment_design.experiment_design_racks:
         worklist_series = WorklistSeries()
         completed_layout = self.design_rack_associations[design_rack.label]
         label = '%s-%s' % (self.label, design_rack.label)
         transfer_generator = \
                 _BiomekTransferWorklistGenerator(label, completed_layout,
                                                  parent=self)
         self.__generate_transfer_worklist(transfer_generator,
                                           transfer_index, worklist_series)
         cell_generator = _CellSuspensionWorklistGenerator(label,
                                                           completed_layout,
                                                           parent=self)
         self.__generate_cell_worklist(cell_generator, cell_index,
                                       worklist_series)
         design_rack.worklist_series = worklist_series
예제 #10
0
class StockSampleCreationWorklistGenerator(BaseTool):
    """
    Creates the worklist series containing the worklists involved in pool
    stock sample creation. This comprises only one worklist which deals with
    the addition of buffer to the future pool stock tubes. The tool will
    create planned sample dilutions for all positions of a 8x12 rack shape.

    **Return Value:**  worklist series
        (:class:`thelma.entities.liquidtransfer.WorklistSeries`).
    """
    NAME = 'Pool Creation Worklist Generator'
    #: The index for the buffer worklist within the series.
    BUFFER_WORKLIST_INDEX = 0

    def __init__(self, volume_calculator, iso_request_label, parent=None):
        """
        Constructor.

        :param volume_calculator: Determines transfer and dilution volumes
            for pool stock sample ISO requests.
        :type volume_calculator: :class:`VolumeCalculator`
        :param int number_designs: The number of single molecule designs for
            each pool (positive number).
        :param int target_volume: The final volume for the new pool stock
            tubes in ul (positive number).
        :param int target_concentration: The final pool concentration for the
            new pool stock tubes in nM (positive number).
        :param str iso_request_label: The plate set label of the ISO request
            to be created - will be used as part of the worklist name.
        """
        BaseTool.__init__(self, parent=parent)
        #: The plate set label of the ISO request to be created - will be used
        #: as part of the worklist name.
        self.iso_request_label = iso_request_label
        #: The :class:`VolumeCalculator` determines transfer and buffer volumes
        #: and might also adjust the target volume for the ISO request.
        self.volume_calculator = volume_calculator
        #: The worklist series for the ISO request.
        self.__worklist_series = None

    def reset(self):
        BaseTool.reset(self)
        self.__worklist_series = WorklistSeries()

    def run(self):
        self.reset()
        self.add_info('Start worklist series generation ...')
        self.__check_input()
        if not self.has_errors():
            self.__create_transfers()
        if not self.has_errors():
            self.return_value = self.__worklist_series
            self.add_info('Worklist series generation completed.')

    def __check_input(self):
        """
        Checks the initialisation values.
        """
        self.add_debug('Check input ...')

        self._check_input_class('volume calculator', self.volume_calculator,
                                VolumeCalculator)
        self._check_input_class('ISO request label', self.iso_request_label,
                                basestring)

    def __create_transfers(self):
        # Creates a :class:`PlannedSampleDilution` for each rack position
        # in a 8x12 rack shape.
        self.add_debug('Create transfers ...')
        self._run_and_record_error(
            self.volume_calculator.calculate,
            'Error when trying to determine buffer volume: ', ValueError)
        buffer_volume = self.volume_calculator.get_buffer_volume()
        if buffer_volume is not None:
            volume = buffer_volume / VOLUME_CONVERSION_FACTOR
            wl_label = LABELS.create_buffer_worklist_label(
                self.iso_request_label)
            wl = PlannedWorklist(wl_label, TRANSFER_TYPES.SAMPLE_DILUTION,
                                 get_pipetting_specs_cybio())
            for rack_pos in get_positions_for_shape(RACK_SHAPE_NAMES.SHAPE_96):
                psd = PlannedSampleDilution.get_entity(
                    volume=volume,
                    target_position=rack_pos,
                    diluent_info=DILUENT_INFO)
                wl.planned_liquid_transfers.append(psd)
            self.__worklist_series.add_worklist(self.BUFFER_WORKLIST_INDEX, wl)
예제 #11
0
파일: generation.py 프로젝트: papagr/TheLMA
class LibraryCreationWorklistGenerator(BaseTool):
    """
    Creates the worklist series for containing the worklists involved in
    library creation.

     1. buffer addition into the pool sample stock racks (1 for each quadrant)
     2. buffer addition into preparation plates (1 for each quadrant)
     3. rack transfers from normal stock racks into pool stock racks (as usually
        the take out worklists are not stored as part of this worklist series
        but at the sample stock racks as container transfer worklist to allow
        for container tracking)
     4. rack transfer from stock rack to preparation plate
     5. rack transfer from preparation plates to aliquot plate

    **Return Value:**  worklist series
        (:class:`thelma.entities.liquidtransfer.WorklistSeries`).
    """
    NAME = 'Library Creation Worklist Generator'

    #: Name pattern for the worklists that add annealing buffer to the pool
    #: stock racks. The placeholders will contain the library name and the
    #: quadrant sector.
    LIBRARY_STOCK_BUFFER_WORKLIST_LABEL = '%s_stock_buffer_Q%i'
    #: Name pattern for the worklists that add annealing buffer to the pool
    #: preparation plates. The placeholders will contain the library name and
    #: the quadrant sector.
    LIBRARY_PREP_BUFFER_WORKLIST_LABEL = '%s_prep_buffer_Q%i'
    #: Name pattern for the worklists that transfers the pool from the pool
    #: stock rack to the preparation plate. The placeholder will contain the
    #: library name.
    STOCK_TO_PREP_TRANSFER_WORKLIST_LABEL = '%s_stock_to_prep'
    #: Name pattern for the worklists that transfers the pool from the
    #: preparation plate to the final library aliqut plate. The placeholder will
    #: contain the library name.
    PREP_TO_ALIQUOT_TRANSFER_WORKLIST_LABEL = '%s_prep_to_aliquot'

    #: The dilution info for the dilution worklists.
    DILUTION_INFO = 'annealing buffer'

    def __init__(self,
                 base_layout,
                 stock_concentration,
                 library_name,
                 parent=None):
        """
        Constructor.

        :param base_layout: The layout defining which positions of the layout
            are allowed to take up library samples.
        :type base_layout: :class:`LibraryBaseLayout`
        :param int stock_concentration: The concentration of the single
            source molecule designs in the stock in nM (positive number).
        :param str library_name: The name of the library to be created.
        """
        BaseTool.__init__(self, parent=parent)
        #: Defines which positions of the layout are allowed to take up
        #: library samples.
        self.base_layout = base_layout
        #: The concentration of the single source molecule designs in the
        #: stock in nM.
        self.stock_concentration = stock_concentration
        #: The name of the library to be created.
        self.library_name = library_name
        #: The worklist series for the ISO request.
        self.__worklist_series = None
        #: The last used worklist index (within the series).
        self.__last_worklist_index = None
        #: The base layout for each quadrant.
        self.__quadrant_layouts = None
        #: The volume transferred from the pool stock rack to the preparation
        #: plate.
        self.__stock_to_prep_vol = None

    def reset(self):
        BaseTool.reset(self)
        self.__worklist_series = None
        self.__last_worklist_index = -1
        self.__quadrant_layouts = dict()
        self.__stock_to_prep_vol = None

    def run(self):
        self.reset()
        self.add_info('Start worklist generation ...')
        self.__check_input()
        if not self.has_errors(): self.__sort_into_sectors()
        if not self.has_errors():
            self.__worklist_series = WorklistSeries()
            self.__create_stock_rack_buffer_worklists()
            self.__create_source_plate_buffer_worklists()
            self.__create_stock_to_prep_worklists()
            self.__create_prep_to_aliquot_worklist()
        if not self.has_errors():
            self.return_value = self.__worklist_series
            self.add_info('Worklist generation completed.')

    def __check_input(self):
        # Checks the initialisation values.
        self.add_debug('Check input ...')
        self._check_input_class('base library layout', self.base_layout,
                                LibraryBaseLayout)
        self._check_input_class('library name', self.library_name, basestring)
        if not is_valid_number(self.stock_concentration):
            msg = 'The stock concentration for the single source molecules ' \
                  'must be a positive number (obtained: %s).' \
                  % (self.stock_concentration)
            self.add_error(msg)

    def __sort_into_sectors(self):
        # Create a rack layout for each quadrant.
        self.add_debug('Sort positions into sectors ...')
        quadrant_positions = QuadrantIterator.sort_into_sectors(
            self.base_layout, NUMBER_SECTORS)
        rack_shape_96 = get_96_rack_shape()
        for sector_index, positions in quadrant_positions.iteritems():
            if len(positions) < 1: continue
            base_layout = LibraryBaseLayout(shape=rack_shape_96)
            for pos in positions:
                base_layout.add_position(pos)
            if len(base_layout) > 0:
                self.__quadrant_layouts[sector_index] = base_layout
        if len(self.__quadrant_layouts) < NUMBER_SECTORS:
            missing_sectors = []
            for sector_index in range(NUMBER_SECTORS):
                if not self.__quadrant_layouts.has_key(sector_index):
                    missing_sectors.append(str(sector_index + 1))
            msg = 'Some rack sectors are empty. You do not require stock ' \
                  'racks for them: %s!' % (', '.join(missing_sectors))
            self.add_warning(msg)

    def __create_stock_rack_buffer_worklists(self):
        # These worklists are responsible for the addition of annealing buffer
        # to the pool stock rack. There is 1 worklist for each quadrant.
        self.add_debug('Create stock rack buffer worklists ...')
        buffer_volume = get_stock_pool_buffer_volume()
        for sector_index, base_layout in self.__quadrant_layouts.iteritems():
            label = self.LIBRARY_STOCK_BUFFER_WORKLIST_LABEL % (
                self.library_name, (sector_index + 1))
            self.__create_buffer_worklist(base_layout, buffer_volume, label,
                                          sector_index)

    def __create_source_plate_buffer_worklists(self):
        # These worklists are responsible for the addition of annealing
        # buffer to the pool preparation plate. There is 1 worklist for each
        # quadrant.
        self.add_debug('Create preparation plate buffer worklist ...')
        self.__stock_to_prep_vol = get_source_plate_transfer_volume()
        buffer_volume = PREPARATION_PLATE_VOLUME - self.__stock_to_prep_vol
        for sector_index, base_layout in self.__quadrant_layouts.iteritems():
            label = self.LIBRARY_PREP_BUFFER_WORKLIST_LABEL % (
                self.library_name, (sector_index + 1))
            self.__create_buffer_worklist(base_layout, buffer_volume, label,
                                          sector_index)

    def __create_buffer_worklist(self, quadrant_layout, buffer_volume, label,
                                 sector_index):
        # Creates buffer dilutions worklist for a particular quadrant
        # and adds it to the worklist series.
        volume = buffer_volume / VOLUME_CONVERSION_FACTOR
        planned_transfers = []
        translator = RackSectorTranslator(
            number_sectors=NUMBER_SECTORS,
            source_sector_index=sector_index,
            target_sector_index=0,
            enforce_type=RackSectorTranslator.ONE_TO_MANY)

        for rack_pos_384 in quadrant_layout.get_positions():
            rack_pos_96 = translator.translate(rack_pos_384)
            planned_transfer = PlannedSampleDilution.get_entity(
                volume, self.DILUTION_INFO, rack_pos_96)
            planned_transfers.append(planned_transfer)
        worklist = PlannedWorklist(label=label,
                                   planned_transfers=planned_transfers)
        self.__last_worklist_index += 1
        self.__worklist_series.add_worklist(self.__last_worklist_index,
                                            worklist)

    def __create_stock_to_prep_worklists(self):
        # This rack transfer worklist (transfer from pool stock rack to
        # preparation plate) is executed once for each quadrant.
        self.add_debug('Add worklist for transfer to preparation plate ...')
        label = self.STOCK_TO_PREP_TRANSFER_WORKLIST_LABEL \
                % (self.library_name)
        volume = self.__stock_to_prep_vol / VOLUME_CONVERSION_FACTOR
        rack_transfer = PlannedRackSampleTransfer.get_entity(volume, 1, 0, 0)
        worklist = PlannedWorklist(label=label,
                                   planned_transfers=[rack_transfer])
        self.__last_worklist_index += 1
        self.__worklist_series.add_worklist(self.__last_worklist_index,
                                            worklist)

    def __create_prep_to_aliquot_worklist(self):
        # There is one rack transfer for each sector (many-to-one transfer).
        # Each transfer is executed once per aliquot plate.
        self.add_debug('Add worklist for transfer into aliquot plates ...')
        volume = ALIQUOT_PLATE_VOLUME / VOLUME_CONVERSION_FACTOR
        rack_transfers = []
        for sector_index in self.__quadrant_layouts.keys():
            rack_transfer = PlannedRackSampleTransfer.get_entity(
                volume, NUMBER_SECTORS, 0, sector_index)
            rack_transfers.append(rack_transfer)
        label = self.PREP_TO_ALIQUOT_TRANSFER_WORKLIST_LABEL % (
            self.library_name)
        worklist = PlannedWorklist(label=label,
                                   planned_transfers=rack_transfers)
        self.__last_worklist_index += 1
        self.__worklist_series.add_worklist(self.__last_worklist_index,
                                            worklist)
예제 #12
0
 def reset(self):
     LayoutParserHandler.reset(self)
     self.__worklist_series = WorklistSeries()
     self.__rack_agg = get_root_aggregate(IRack)
     self.__missing_rack_barcodes = []
예제 #13
0
class GenericSampleTransferPlanParserHandler(LayoutParserHandler):
    """
    Converts the data from the :class:`GenericSampleTransferPlanParser`
    into a :class:`WorklistSeries`.

    **Return Value:** :class:`WorklistSeries`

    """
    NAME = 'Generic Sample Transfer Plan Parser Handler'

    _PARSER_CLS = GenericSampleTransferPlanParser

    def __init__(self, stream, allow_rack_creation=False, parent=None):
        """
        Constructor.

        :param bool allow_rack_creation: Flag indicating if it is allowed to
            create racks. This option can only be set if the racks are not
            used for worklist printing or execution right away (because the
            barcode is generated by the DB upon persisting).
        :default allow_rack_creation: *False*
        """
        LayoutParserHandler.__init__(self, stream, parent=parent)
        #: Is it allowed to create racks? This option can only be set to *True*
        #: if the racks are not used for worklist printing or execution right
        #: away (because the barcode is generated by the DB upon persisting).
        self.allow_rack_creation = allow_rack_creation
        #: The :class:`WorklistSeries` to be generated.
        self.__worklist_series = None
        #: The :class:`RackOrReservoirItem` objects mapped onto rack identifiers.
        self.__ror_map = dict()
        #: The aggregate used to fetch racks.
        self.__rack_agg = None
        #: Intermediate error storage.
        self.__missing_rack_barcodes = None
        #: Intermediate error storage.
        self.__missing_rack_barcodes = None

    def reset(self):
        LayoutParserHandler.reset(self)
        self.__worklist_series = WorklistSeries()
        self.__rack_agg = get_root_aggregate(IRack)
        self.__missing_rack_barcodes = []

    def get_racks_and_reservoir_items(self):
        """
        Returns the :class:`RackOrReservoirItem` objects found in the sheet.
        """
        return self._get_additional_value(self.__ror_map.values())

    def _initialize_parser_keys(self):
        """
        We need to set the allowed rack shapes (that is all available rack
        shapes) and the transfer role markers.
        """
        self.parser.source_role_marker = TRANSFER_ROLES.SOURCE
        self.parser.target_role_marker = TRANSFER_ROLES.TARGET
        rack_shape_agg = get_root_aggregate(IRackShape)
        rack_shape_agg.filter = None
        self.parser.allowed_rack_dimensions = [(rs.number_rows,
                                                rs.number_columns)
                                               for rs in rack_shape_agg]

    def _convert_results_to_entity(self):
        # Assembles a worklist series from the parsed sheet.
        self.add_info('Start conversion into worklist series ...')
        self._check_input_class('"allow_rack_creation" flag',
                                self.allow_rack_creation, bool)
        if not self.has_errors():
            self.__get_or_generate_racks()
        if not self.has_errors():
            self.__create_worklists()
        if not self.has_errors():
            self.add_info('Conversion completed.')
            self.return_value = self.__worklist_series

    def __get_or_generate_racks(self):
        # Racks (recognized by specs) are fetched from the DB.
        # Plates (recognized by specs) are fetched from the DB.
        # Reservoirs are generated.
        self.add_debug('Fetch or generate racks ...')
        for rack_container in self.parser.rack_containers.values():
            data_item = self.__create_rack_or_reservoir_data(rack_container)
            if data_item is None:
                break
            self.__ror_map[data_item.identifier] = data_item

    def __create_rack_or_reservoir_data(self, rack_container):
        # Determines whether the specified items is a rack or a reservoirs.
        # Fetches racks for which there are barcodes or generates racks
        # without barcode.
        if rack_container.specs is None:
            rack = self.__get_rack_for_barcode(rack_container.barcode)
            if rack is None:
                return None
            else:
                rs = self.__get_reservoir_specs_for_rack(rack)
                if rs is None:
                    return None
                data_item = \
                    RackOrReservoirItem(is_rack=True,
                                        reservoir_specs=rs,
                                        identifier=rack_container.rack_label)
                data_item.set_rack(rack)
                return data_item
        else:
            rs = self.__get_reservoir_specs(rack_container)
            if rs is None: return None
            is_rack = RESERVOIR_SPECS_NAMES.is_rack_spec(rs)
            data_item = \
                RackOrReservoirItem(is_rack=is_rack,
                                    reservoir_specs=rs,
                                    identifier=rack_container.rack_label)
            barcode = rack_container.barcode
            if is_rack:
                if barcode is None and not self.allow_rack_creation:
                    msg = 'When printing or executing worklists directly ' \
                          'all used racks must already be stored in the DB ' \
                          'and be specified by barcode. Rack "%s" does not ' \
                          'have a barcode.' % (rack_container.identifier)
                    self.add_error(msg)
                    rack = None
                elif barcode is None:
                    rack = self.__create_plate(rack_container, rs)
                else:
                    rack = self.__get_rack_for_barcode(barcode)
                    rs = self.__get_reservoir_specs_for_rack(rack, rs)
                    if rs is None:
                        return None
                    data_item.reservoir_specs = rs
                if rack is None:
                    return None
                data_item.set_rack(rack)
            else:
                if barcode is None:
                    barcode = data_item.identifier
                data_item.barcode = barcode
            return data_item

    def __get_reservoir_specs(self, rack_container):
        # Also records an error message if the spec has not been found.
        try:
            rs = get_reservoir_spec(rack_container.specs.lower())
        except ValueError as ve:
            msg = 'Error when trying to fetch reservoir specs for rack "%s": ' \
                  '%s' % (rack_container.identifier, ve)
            self.add_error(msg)
            result = None
        else:
            result = rs
        return result

    def __get_rack_for_barcode(self, barcode):
        # Also records error message if the aggregate did not return a rack.
        rack = self.__rack_agg.get_by_slug(barcode)
        if rack is None:
            msg = 'Could not find rack "%s" in the DB!' % (barcode)
            self.add_error(msg)
        return rack

    def __get_reservoir_specs_for_rack(self, rack, reservoir_specs=None):
        # Fetches the reservoir specs for a rack. If there is already a specs
        # in the file, the specs are compared.
        try:
            rs = get_reservoir_specs_from_rack_specs(rack.specs)
        except ValueError as ve:
            msg = 'Error when trying to determine reservoir specs for ' \
                  'rack "%s" (rack specs "%s"): %s' \
                   % (rack.barcode, rack.specs.name, ve)
            self.add_error(msg)
            result = None
        else:
            if reservoir_specs is not None and not rs == reservoir_specs:
                msg = 'You specified a wrong reservoir spec for rack "%s" ' \
                      '("%s" instead of "%s"). Will use spec "%s".' \
                      % (rack.barcode, reservoir_specs.name, rs.name, rs.name)
                self.add_warning(msg)
            result = rs
        return result

    def __create_plate(self, rack_container, reservoir_specs):
        # Creates a new plate incl. label. The plate specs are derived
        # from the reservoir specs.
        # Creating of stock racks is not allowed.
        try:
            plate_specs = get_rack_specs_from_reservoir_specs(reservoir_specs)
        except ValueError as ve:
            msg = 'Error when trying to determine plate specs for rack "%s": ' \
                  '%s.' % (rack_container.identifier, ve)
            self.add_error(msg)
            result = None
        else:
            rack_id = rack_container.identifier
            plate_label = '%s_%s' % (self.parser.worklist_prefix, rack_id)
            if len(plate_label) > MAX_PLATE_LABEL_LENGTH:
                msg = 'The label that has been generated for the new plate ' \
                      '"%s" ("%s") is longer than %i characters (%i ' \
                      'characters). You will not be able to print this ' \
                      'label properly. To circumvent this problem choose ' \
                      'a shorter rack identifier or a shorter worklist ' \
                      'prefix.' \
                       % (rack_id, plate_label, MAX_PLATE_LABEL_LENGTH,
                          len(plate_label))
                self.add_warning(msg)
            result = plate_specs.create_rack(label=plate_label,
                                             status=get_item_status_future())
        return result

    def __create_worklists(self):
        # Creates :class:`PlannedWorklist` objects and adds them to the
        # :attr:`__worklist_series`
        self.add_debug('Create worklists ...')
        for step_number in sorted(self.parser.step_containers.keys()):
            step_container = self.parser.step_containers[step_number]
            if not self.__has_consistent_rack_shapes(step_container):
                break
            transfer_type = self.__get_transfer_type(step_container)
            if transfer_type is None:
                break
            if not self.__has_only_racks_as_target(step_container):
                break
            planned_liquid_transfers = self.__get_planned_liquid_transfers(
                                                step_container, transfer_type)
            if planned_liquid_transfers is None:
                break
            robot_specs = self.__get_robot_specs(step_container)
            wl_label = '%s_%i' % (self.parser.worklist_prefix, step_number)
            worklist = PlannedWorklist(label=wl_label,
                            transfer_type=transfer_type,
                            planned_liquid_transfers=planned_liquid_transfers,
                            pipetting_specs=robot_specs)
            self.__worklist_series.add_worklist(step_number, worklist)
            for role, rack_ids in step_container.rack_containers.iteritems():
                for rack_id in rack_ids:
                    data_item = self.__ror_map[rack_id]
                    data_item.add_worklist(role, worklist)

    def __has_consistent_rack_shapes(self, step_container):
        # Makes sure the rack items assigned to the source and target layouts
        # of a step have the requested rack shape.
        for role, layout_container in step_container.layouts.iteritems():
            rack_shape = self._convert_to_rack_shape(layout_container.shape)
            if rack_shape is None:
                return False
            for rack_id in step_container.rack_containers[role]:
                data_item = self.__ror_map[rack_id]
                if not data_item.rack_shape == rack_shape:
                    msg = 'The rack shape for layout at %s (%s) does not ' \
                          'match the rack shape for rack "%s" (%s).' \
                           % (layout_container.get_starting_cell_name(),
                              layout_container.shape.name,
                              data_item.identifier, data_item.rack_shape.name)
                    self.add_error(msg)
                    return False
        return True

    def __get_transfer_type(self, step_container):
        # Possible types are SAMPLE_DILUTION and SAMPLE_TRANSFER.
        is_dilution = False
        for rack_id in step_container.rack_containers[TRANSFER_ROLES.SOURCE]:
            data_item = self.__ror_map[rack_id]
            if not data_item.is_rack:
                is_dilution = True
            elif is_dilution:
                # this should not happen because reservoirs have other rack
                # shapes (consistency has already been tested at this point).
                # However, there might be other reservoirs in the future.
                msg = 'The source types for step %i are inconsistent. There ' \
                      'must be either all racks or all reservoirs!' \
                       % (step_container.number)
                self.add_error(msg)
                return None
        if is_dilution:
            transfer_type = TRANSFER_TYPES.SAMPLE_DILUTION
        else:
            transfer_type = TRANSFER_TYPES.SAMPLE_TRANSFER
        return transfer_type

    def __has_only_racks_as_target(self, step_container):
        # Reservoirs must not be targets.
        result = True
        for rack_id in step_container.rack_containers[TRANSFER_ROLES.TARGET]:
            data_item = self.__ror_map[rack_id]
            if not data_item.is_rack:
                msg = 'The target for step %i is a reservoirs. Reservoirs ' \
                      'may only serve as sources!' % (step_container.number)
                self.add_error(msg)
                result = False
                break
        return result

    def __get_robot_specs(self, step_container):
        # Always returns a Biomek spec. If the source racks are tube racks
        # the stock settings are returned.
        result = get_pipetting_specs_biomek()
        for rack_id in step_container.rack_containers[TRANSFER_ROLES.SOURCE]:
            data_item = self.__ror_map[rack_id]
            if isinstance(data_item.rack, TubeRack):
                result = get_pipetting_specs_biomek_stock()
                break
        return result

    def __get_planned_liquid_transfers(self, step_container, transfer_type):
        # Create :class:`PlannedSampleDilution`s for dilution (requires
        # valid diluent), otherwise it creates
        # :class:`PlannedSampleTransfer`s.
        planned_liquid_transfers = []
        for transfer_container in step_container.get_transfer_containers():
            src_positions = transfer_container.get_source_positions()
            trg_positions = transfer_container.get_target_positions()
            volume = float(transfer_container.volume) / VOLUME_CONVERSION_FACTOR
            if transfer_type == TRANSFER_TYPES.SAMPLE_DILUTION:
                diluent = transfer_container.diluent
                if diluent is None or not len(str(diluent)) > 1:
                    msg = 'A diluent must be at least 2 characters long! ' \
                          'Change the diluent for step %i code %s, ' \
                          'please.' \
                          % (step_container.number, transfer_container.code)
                    self.add_error(msg)
                    return None
            for trg_pos_container in trg_positions:
                trg_pos = self._convert_to_rack_position(trg_pos_container)
                kw = dict(target_position=trg_pos, volume=volume)
                if transfer_type == TRANSFER_TYPES.SAMPLE_DILUTION:
                    kw['diluent_info'] = str(transfer_container.diluent)
                    psd = PlannedSampleDilution.get_entity(**kw)
                    planned_liquid_transfers.append(psd)
                else:
                    for src_pos_container in src_positions:
                        src_pos = self._convert_to_rack_position(
                                                            src_pos_container)
                        kw['source_position'] = src_pos
                        pst = PlannedSampleTransfer.get_entity(**kw)
                        planned_liquid_transfers.append(pst)
        return planned_liquid_transfers
예제 #14
0
class LibraryCreationWorklistGenerator(BaseTool):
    """
    Creates the worklist series for containing the worklists involved in
    library creation.

    Preparation route with pool stock racks (default):
     1. Buffer addition into the pool stock racks (1 for each quadrant)
     2. Buffer addition into preparation plates (1 for each quadrant)
     3. Rack transfers from normal stock racks into pool stock racks (the
        take out worklists are not stored as part of this worklist series
        but at the sample stock racks as container transfer worklist to allow
        for container tracking)
     4. Rack transfer from stock rack to preparation plate
     5. Rack transfer from preparation plates to aliquot plate.

    For the preparation route without pool stock racks, steps 1 and 3 are
    left out and the stock transfer is made directly into the preparation
    plate (with the buffer volume adjusted accordingly).

    **Return Value:**  worklist series
        (:class:`thelma.entities.liquidtransfer.WorklistSeries`).
    """
    NAME = 'Library Creation Worklist Generator'
    #: Name pattern for the worklists that add annealing buffer to the pool
    #: stock racks. The placeholders will contain the library name and the
    #: quadrant sector.
    LIBRARY_STOCK_BUFFER_WORKLIST_LABEL = '%s_stock_buffer_Q%i'
    #: Name pattern for the worklists that add annealing buffer to the pool
    #: preparation plates. The placeholders will contain the library name and
    #: the quadrant sector.
    LIBRARY_PREP_BUFFER_WORKLIST_LABEL = '%s_prep_buffer_Q%i'
    #: Name pattern for the worklists that transfers the pool from the pool
    #: stock rack to the preparation plate. The placeholder will contain the
    #: library name.
    STOCK_TO_PREP_TRANSFER_WORKLIST_LABEL = '%s_stock_to_prep'
    #: Name pattern for the worklists that transfers the pool from the
    #: preparation plate to the final library aliqut plate. The placeholder will
    #: contain the library name.
    PREP_TO_ALIQUOT_TRANSFER_WORKLIST_LABEL = '%s_prep_to_aliquot'
    #: The dilution info for the dilution worklists.
    DILUTION_INFO = 'annealing buffer'


    def __init__(self, base_layout, stock_concentration,
                 library_name, preparation_buffer_volume,
                 pool_buffer_volume=None, parent=None):
        """
        Constructor.

        :param base_layout: Layout defining which positions of the layout
            are allowed to take up library samples.
        :type base_layout: :class:`LibraryBaseLayout`
        :param int stock_concentration: Concentration of the single
            source molecule designs in the stock in nM (positive number).
        :param str library_name: Name of the library to be created.
        :param float preparation_buffer_volume: Buffer volume for preparation
            plates in ul.
        :param float pool_buffer_volume: Buffer volume for pool
            plates in ul. May be `None` if no pool racks are created.
        """
        BaseTool.__init__(self, parent=parent)
        self.base_layout = base_layout
        self.stock_concentration = stock_concentration
        self.library_name = library_name
        self.preparation_buffer_volume = preparation_buffer_volume
        self.pool_buffer_volume = pool_buffer_volume
        #: The worklist series for the ISO request.
        self.__worklist_series = None
        #: The last used worklist index (within the series).
        self.__last_worklist_index = None
        #: The base layout for each sector.
        self.__sector_layouts = None

    def reset(self):
        BaseTool.reset(self)
        self.__worklist_series = None
        self.__last_worklist_index = 0
        self.__sector_layouts = dict()

    def run(self):
        self.reset()
        self.add_info('Start worklist generation ...')
        self.__check_input()
        if not self.has_errors():
            self.__sort_into_sectors()
        if not self.has_errors():
            self.__worklist_series = WorklistSeries()
            if not self.pool_buffer_volume is None:
                self.__create_pool_rack_buffer_worklists()
        if not self.has_errors():
            self.__create_preparation_plate_buffer_worklists()
        if not self.has_errors():
            self.__create_prep_to_aliquot_worklist()
        if not self.has_errors():
            self.return_value = self.__worklist_series
            self.add_info('Worklist generation completed.')

    def __check_input(self):
        # Checks the initialisation values.
        self.add_debug('Checking input.')
        self._check_input_class('base library layout', self.base_layout,
                                LibraryBaseLayout)
        self._check_input_class('library name', self.library_name, basestring)
        if not is_valid_number(self.stock_concentration):
            msg = 'The stock concentration for the single source molecules ' \
                  'must be a positive number (obtained: %s).' \
                  % (self.stock_concentration)
            self.add_error(msg)

    def __sort_into_sectors(self):
        # Create a rack layout for each quadrant.
        self.add_debug('Sorting positions into sectors.')
        self.__sector_layouts = \
                        get_sector_layouts_for_384_layout(self.base_layout,
                                                          LibraryBaseLayout)
        if len(self.__sector_layouts) < NUMBER_SECTORS:
            missing_sector_labels = [str(si + 1)
                                     for si in range(NUMBER_SECTORS)
                                     if not si in self.__sector_layouts]
            msg = 'Some rack sectors are empty. You do not require stock ' \
                  'racks for them: %s!' % ', '.join(missing_sector_labels)
            self.add_warning(msg)

    def __create_pool_rack_buffer_worklists(self):
        # These worklists are responsible for the addition of annealing buffer
        # to the pool rack. There is 1 worklist for each quadrant.
        self.add_debug('Creating pool buffer worklists.')
        for sector_index, sector_layout in self.__sector_layouts.iteritems():
            label = self.LIBRARY_STOCK_BUFFER_WORKLIST_LABEL \
                        % (self.library_name, sector_index + 1)
            self.__create_buffer_worklist(sector_layout,
                                          self.pool_buffer_volume, label)

    def __create_preparation_plate_buffer_worklists(self):
        # These worklists are responsible for the addition of annealing
        # buffer to the pool preparation plate. There is 1 worklist for each
        # quadrant.
        self.add_debug('Creating preparation plate buffer worklists.')
        for sector_index, sector_layout in self.__sector_layouts.iteritems():
            label = self.LIBRARY_PREP_BUFFER_WORKLIST_LABEL % (
                                        self.library_name, (sector_index + 1))
            self.__create_buffer_worklist(sector_layout,
                                          self.preparation_buffer_volume,
                                          label)

    def __create_buffer_worklist(self, sector_layout, buffer_volume, label):
        # Creates the buffer dilution worklist for a particular quadrant
        # and adds it to the worklist series.
        volume = buffer_volume / VOLUME_CONVERSION_FACTOR
        ptfs = []
        for rack_pos_96 in sector_layout.get_positions():
            planned_transfer = PlannedSampleDilution.get_entity(
                                                        volume,
                                                        self.DILUTION_INFO,
                                                        rack_pos_96)
            ptfs.append(planned_transfer)
        worklist = PlannedWorklist(label,
                                   TRANSFER_TYPES.SAMPLE_DILUTION,
                                   get_pipetting_specs_biomek(),
                                   planned_liquid_transfers=ptfs)
        self.__worklist_series.add_worklist(self.__last_worklist_index,
                                            worklist)
        self.__last_worklist_index += 1

    def __create_prep_to_aliquot_worklist(self):
        # There is one rack transfer for each sector (many-to-one transfer).
        # Each transfer is executed once per aliquot plate.
        self.add_debug('Add worklist for transfer into aliquot plates ...')
        volume = DEFAULT_ALIQUOT_PLATE_VOLUME / VOLUME_CONVERSION_FACTOR
        rack_transfers = []
        for sector_index in self.__sector_layouts.keys():
            rack_transfer = PlannedRackSampleTransfer.get_entity(
                                                            volume,
                                                            NUMBER_SECTORS,
                                                            0,
                                                            sector_index)
            rack_transfers.append(rack_transfer)
        label = self.PREP_TO_ALIQUOT_TRANSFER_WORKLIST_LABEL % (
                                                            self.library_name)
        worklist = PlannedWorklist(label,
                                   TRANSFER_TYPES.RACK_SAMPLE_TRANSFER,
                                   get_pipetting_specs_cybio(),
                                   planned_liquid_transfers=rack_transfers)
        self.__worklist_series.add_worklist(self.__last_worklist_index,
                                            worklist)
        self.__last_worklist_index += 1
예제 #15
0
파일: worklist.py 프로젝트: papagr/TheLMA
 def __create_mastermix_series(self):
     # Creates the worklist series for the mastermix preparation.
     self.add_debug('Create mastermix preparation worklist series ...')
     self.__design_series = WorklistSeries()
     self.__generate_optimem_worklist()
     self.__generate_reagent_worklist()
예제 #16
0
class StockSampleCreationWorklistGenerator(BaseTool):
    """
    Creates the worklist series containing the worklists involved in pool
    stock sample creation. This comprises only one worklist which deals with
    the addition of buffer to the future pool stock tubes. The tool will
    create planned sample dilutions for all positions of a 8x12 rack shape.

    **Return Value:**  worklist series
        (:class:`thelma.entities.liquidtransfer.WorklistSeries`).
    """
    NAME = 'Pool Creation Worklist Generator'
    #: The index for the buffer worklist within the series.
    BUFFER_WORKLIST_INDEX = 0

    def __init__(self, volume_calculator, iso_request_label, parent=None):
        """
        Constructor.

        :param volume_calculator: Determines transfer and dilution volumes
            for pool stock sample ISO requests.
        :type volume_calculator: :class:`VolumeCalculator`
        :param int number_designs: The number of single molecule designs for
            each pool (positive number).
        :param int target_volume: The final volume for the new pool stock
            tubes in ul (positive number).
        :param int target_concentration: The final pool concentration for the
            new pool stock tubes in nM (positive number).
        :param str iso_request_label: The plate set label of the ISO request
            to be created - will be used as part of the worklist name.
        """
        BaseTool.__init__(self, parent=parent)
        #: The plate set label of the ISO request to be created - will be used
        #: as part of the worklist name.
        self.iso_request_label = iso_request_label
        #: The :class:`VolumeCalculator` determines transfer and buffer volumes
        #: and might also adjust the target volume for the ISO request.
        self.volume_calculator = volume_calculator
        #: The worklist series for the ISO request.
        self.__worklist_series = None

    def reset(self):
        BaseTool.reset(self)
        self.__worklist_series = WorklistSeries()

    def run(self):
        self.reset()
        self.add_info('Start worklist series generation ...')
        self.__check_input()
        if not self.has_errors():
            self.__create_transfers()
        if not self.has_errors():
            self.return_value = self.__worklist_series
            self.add_info('Worklist series generation completed.')

    def __check_input(self):
        """
        Checks the initialisation values.
        """
        self.add_debug('Check input ...')

        self._check_input_class('volume calculator', self.volume_calculator,
                                VolumeCalculator)
        self._check_input_class('ISO request label', self.iso_request_label,
                                basestring)

    def __create_transfers(self):
        # Creates a :class:`PlannedSampleDilution` for each rack position
        # in a 8x12 rack shape.
        self.add_debug('Create transfers ...')
        self._run_and_record_error(self.volume_calculator.calculate,
                            'Error when trying to determine buffer volume: ',
                            ValueError)
        buffer_volume = self.volume_calculator.get_buffer_volume()
        if buffer_volume is not None:
            volume = buffer_volume / VOLUME_CONVERSION_FACTOR
            wl_label = LABELS.create_buffer_worklist_label(
                                                    self.iso_request_label)
            wl = PlannedWorklist(wl_label,
                                 TRANSFER_TYPES.SAMPLE_DILUTION,
                                 get_pipetting_specs_cybio())
            for rack_pos in get_positions_for_shape(RACK_SHAPE_NAMES.SHAPE_96):
                psd = PlannedSampleDilution.get_entity(volume=volume,
                      target_position=rack_pos, diluent_info=DILUENT_INFO)
                wl.planned_liquid_transfers.append(psd)
            self.__worklist_series.add_worklist(self.BUFFER_WORKLIST_INDEX, wl)
예제 #17
0
class LibraryCreationWorklistGenerator(BaseTool):
    """
    Creates the worklist series for containing the worklists involved in
    library creation.

    Preparation route with pool stock racks (default):
     1. Buffer addition into the pool stock racks (1 for each quadrant)
     2. Buffer addition into preparation plates (1 for each quadrant)
     3. Rack transfers from normal stock racks into pool stock racks (the
        take out worklists are not stored as part of this worklist series
        but at the sample stock racks as container transfer worklist to allow
        for container tracking)
     4. Rack transfer from stock rack to preparation plate
     5. Rack transfer from preparation plates to aliquot plate.

    For the preparation route without pool stock racks, steps 1 and 3 are
    left out and the stock transfer is made directly into the preparation
    plate (with the buffer volume adjusted accordingly).

    **Return Value:**  worklist series
        (:class:`thelma.entities.liquidtransfer.WorklistSeries`).
    """

    NAME = "Library Creation Worklist Generator"
    #: Name pattern for the worklists that add annealing buffer to the pool
    #: stock racks. The placeholders will contain the library name and the
    #: quadrant sector.
    LIBRARY_STOCK_BUFFER_WORKLIST_LABEL = "%s_stock_buffer_Q%i"
    #: Name pattern for the worklists that add annealing buffer to the pool
    #: preparation plates. The placeholders will contain the library name and
    #: the quadrant sector.
    LIBRARY_PREP_BUFFER_WORKLIST_LABEL = "%s_prep_buffer_Q%i"
    #: Name pattern for the worklists that transfers the pool from the pool
    #: stock rack to the preparation plate. The placeholder will contain the
    #: library name.
    STOCK_TO_PREP_TRANSFER_WORKLIST_LABEL = "%s_stock_to_prep"
    #: Name pattern for the worklists that transfers the pool from the
    #: preparation plate to the final library aliqut plate. The placeholder will
    #: contain the library name.
    PREP_TO_ALIQUOT_TRANSFER_WORKLIST_LABEL = "%s_prep_to_aliquot"
    #: The dilution info for the dilution worklists.
    DILUTION_INFO = "annealing buffer"

    def __init__(
        self,
        base_layout,
        stock_concentration,
        library_name,
        preparation_buffer_volume,
        pool_buffer_volume=None,
        parent=None,
    ):
        """
        Constructor.

        :param base_layout: Layout defining which positions of the layout
            are allowed to take up library samples.
        :type base_layout: :class:`LibraryBaseLayout`
        :param int stock_concentration: Concentration of the single
            source molecule designs in the stock in nM (positive number).
        :param str library_name: Name of the library to be created.
        :param float preparation_buffer_volume: Buffer volume for preparation
            plates in ul.
        :param float pool_buffer_volume: Buffer volume for pool
            plates in ul. May be `None` if no pool racks are created.
        """
        BaseTool.__init__(self, parent=parent)
        self.base_layout = base_layout
        self.stock_concentration = stock_concentration
        self.library_name = library_name
        self.preparation_buffer_volume = preparation_buffer_volume
        self.pool_buffer_volume = pool_buffer_volume
        #: The worklist series for the ISO request.
        self.__worklist_series = None
        #: The last used worklist index (within the series).
        self.__last_worklist_index = None
        #: The base layout for each sector.
        self.__sector_layouts = None

    def reset(self):
        BaseTool.reset(self)
        self.__worklist_series = None
        self.__last_worklist_index = 0
        self.__sector_layouts = dict()

    def run(self):
        self.reset()
        self.add_info("Start worklist generation ...")
        self.__check_input()
        if not self.has_errors():
            self.__sort_into_sectors()
        if not self.has_errors():
            self.__worklist_series = WorklistSeries()
            if not self.pool_buffer_volume is None:
                self.__create_pool_rack_buffer_worklists()
        if not self.has_errors():
            self.__create_preparation_plate_buffer_worklists()
        if not self.has_errors():
            self.__create_prep_to_aliquot_worklist()
        if not self.has_errors():
            self.return_value = self.__worklist_series
            self.add_info("Worklist generation completed.")

    def __check_input(self):
        # Checks the initialisation values.
        self.add_debug("Checking input.")
        self._check_input_class("base library layout", self.base_layout, LibraryBaseLayout)
        self._check_input_class("library name", self.library_name, basestring)
        if not is_valid_number(self.stock_concentration):
            msg = (
                "The stock concentration for the single source molecules "
                "must be a positive number (obtained: %s)." % (self.stock_concentration)
            )
            self.add_error(msg)

    def __sort_into_sectors(self):
        # Create a rack layout for each quadrant.
        self.add_debug("Sorting positions into sectors.")
        self.__sector_layouts = get_sector_layouts_for_384_layout(self.base_layout, LibraryBaseLayout)
        if len(self.__sector_layouts) < NUMBER_SECTORS:
            missing_sector_labels = [str(si + 1) for si in range(NUMBER_SECTORS) if not si in self.__sector_layouts]
            msg = "Some rack sectors are empty. You do not require stock " "racks for them: %s!" % ", ".join(
                missing_sector_labels
            )
            self.add_warning(msg)

    def __create_pool_rack_buffer_worklists(self):
        # These worklists are responsible for the addition of annealing buffer
        # to the pool rack. There is 1 worklist for each quadrant.
        self.add_debug("Creating pool buffer worklists.")
        for sector_index, sector_layout in self.__sector_layouts.iteritems():
            label = self.LIBRARY_STOCK_BUFFER_WORKLIST_LABEL % (self.library_name, sector_index + 1)
            self.__create_buffer_worklist(sector_layout, self.pool_buffer_volume, label)

    def __create_preparation_plate_buffer_worklists(self):
        # These worklists are responsible for the addition of annealing
        # buffer to the pool preparation plate. There is 1 worklist for each
        # quadrant.
        self.add_debug("Creating preparation plate buffer worklists.")
        for sector_index, sector_layout in self.__sector_layouts.iteritems():
            label = self.LIBRARY_PREP_BUFFER_WORKLIST_LABEL % (self.library_name, (sector_index + 1))
            self.__create_buffer_worklist(sector_layout, self.preparation_buffer_volume, label)

    def __create_buffer_worklist(self, sector_layout, buffer_volume, label):
        # Creates the buffer dilution worklist for a particular quadrant
        # and adds it to the worklist series.
        volume = buffer_volume / VOLUME_CONVERSION_FACTOR
        ptfs = []
        for rack_pos_96 in sector_layout.get_positions():
            planned_transfer = PlannedSampleDilution.get_entity(volume, self.DILUTION_INFO, rack_pos_96)
            ptfs.append(planned_transfer)
        worklist = PlannedWorklist(
            label, TRANSFER_TYPES.SAMPLE_DILUTION, get_pipetting_specs_biomek(), planned_liquid_transfers=ptfs
        )
        self.__worklist_series.add_worklist(self.__last_worklist_index, worklist)
        self.__last_worklist_index += 1

    def __create_prep_to_aliquot_worklist(self):
        # There is one rack transfer for each sector (many-to-one transfer).
        # Each transfer is executed once per aliquot plate.
        self.add_debug("Add worklist for transfer into aliquot plates ...")
        volume = DEFAULT_ALIQUOT_PLATE_VOLUME / VOLUME_CONVERSION_FACTOR
        rack_transfers = []
        for sector_index in self.__sector_layouts.keys():
            rack_transfer = PlannedRackSampleTransfer.get_entity(volume, NUMBER_SECTORS, 0, sector_index)
            rack_transfers.append(rack_transfer)
        label = self.PREP_TO_ALIQUOT_TRANSFER_WORKLIST_LABEL % (self.library_name)
        worklist = PlannedWorklist(
            label,
            TRANSFER_TYPES.RACK_SAMPLE_TRANSFER,
            get_pipetting_specs_cybio(),
            planned_liquid_transfers=rack_transfers,
        )
        self.__worklist_series.add_worklist(self.__last_worklist_index, worklist)
        self.__last_worklist_index += 1
예제 #18
0
 def reset(self):
     BaseTool.reset(self)
     self.__worklist_series = WorklistSeries()
예제 #19
0
 def reset(self):
     BaseTool.reset(self)
     self.__worklist_series = WorklistSeries()