def pick_colonies():

    parser = argparse.ArgumentParser(
        description="Resuspend a plate of DNA on an Opentrons OT-1 robot.")
    parser.add_argument(
        '-r',
        '--run',
        required=False,
        action="store_true",
        help="Send commands to the robot and print command output.")
    parser.add_argument(
        '-i',
        '--inoculate',
        required=False,
        action="store_true",
        help="Picks colonies and inoculates a deep well plate.")
    parser.add_argument('-c',
                        '--check',
                        required=False,
                        action="store_true",
                        help="Allows the user to choose the colonies.")
    args = parser.parse_args()

    if args.run:
        port = os.environ["ROBOT_DEV"]
        print("Connecting robot to port {}".format(port))
        robot.connect(port)
    else:
        print("Simulating protcol run")
        robot.connect()

    robot.home()

    p200_tipracks = []
    p10_tip_racks = []

    p10s_tipracks = [
        containers.load('tiprack-10ul', 'E1'),
        containers.load('tiprack-10ul', 'E2')
    ]
    trash = containers.load('point', 'D1', 'holywastedplasticbatman')
    deep_well = containers.load('96-deep-well', 'C2')
    trans_plate = containers.load('point', 'B2')

    p10, p10s, p200 = ot.initialize_pipettes(p10_tipracks, p10s_tipracks,
                                             p200_tipracks, trash)

    assemblies = []
    print("Choose which build you would like to pick from:")
    for index, assembly in enumerate(
            session.query(Plate).join(Build, Plate.builds).filter(
                Plate.plated == 'not_plated').order_by(Build.build_name)):
        print("{}. {}".format(index, assembly.builds.build_name))
        assemblies.append(assembly)
    plate_num = int(input("Enter plate here: "))
    target_plate = assemblies[plate_num]
    build_name = target_plate.builds.build_name
    path = '{}/builds/{}/{}_trans_pics'.format(BASE_PATH, build_name,
                                               build_name)

    for file in sorted(glob.glob('{}/*.jpg'.format(path)), reverse=True):
        print("Current image: ", file)
        skip = input('Skip? (y/n): ')
        if skip == 'y':
            continue
        print("Inoculate: ", args.inoculate)
        input()
        centers, agar, x_dim, y_dim, missing = find_colony_coordinates(
            file, args.check)
        coords = ot_coords(centers, agar, x_dim, y_dim)
        run_ot(p10s, trans_plate, deep_well, agar, coords, centers,
               args.inoculate)
        print('The following wells were skipped:\n', missing)
        print("Complete")
        robot.home()
    cv2.destroyAllWindows()
Esempio n. 2
0
def plate(session,engine):

    ot.print_center("============ Beginning to plate ============")

    # Take in command line arguments
    parser = argparse.ArgumentParser(description="Resuspend a plate of DNA on an Opentrons OT-1 robot.")
    parser.add_argument('-r', '--run', required=False, action="store_true", help="Send commands to the robot and print command output.")
    parser.add_argument('-m', '--manual', required=False, action="store_true", help="Maunal entry of parameters.")
    args = parser.parse_args()

    # Verify that the correct robot is being used
    if args.run:
        ot.check_robot()

    assemblies = []
    print("Choose which plate you would like to plate:")
    for index,assembly in enumerate(session.query(Plate).join(Build,Plate.builds)\
                .filter(Build.status == 'building').filter(Plate.transformed == 'transformed')\
                .filter(Plate.plated != 'plated').order_by(Build.build_name)):
        print("{}. {}".format(index,assembly.builds.build_name))
        assemblies.append(assembly)

    plate_num = ot.request_info("Enter plate here: ",type='int')
    target_plate = assemblies[plate_num]
    build_map = target_plate.wells
    if len(target_plate.wells) > 48:
        print("Too many samples to plate at once")
        portion = int(ot.request_info("Choose which half to plate, 1 or 2: ",type='int'))
        if portion == 1:
            build_map = target_plate.wells[:48]
        else:
            build_map = target_plate.wells[48:]
        num_reactions = len(build_map)
    else:
        portion = 1
        num_reactions = len(build_map)

    num_rows = math.ceil(num_reactions / 8)
    print("num rows: ",num_rows)
    trans_per_plate = 3
    num_plates = num_rows // trans_per_plate

    agar_plate_names = []
    for plate_num in range(num_plates):
        if portion == 2:
            plate_num += 2
        current_plate = target_plate.builds.build_name + "_p" + str(plate_num + 1)
        agar_plate_names.append(current_plate)
    print(agar_plate_names)
    print("You will need {} agar plates".format(len(agar_plate_names)))

    ## =============================================
    ## SETUP THE OT-1 DECK
    ## =============================================
    # Allocate slots for the required agar plates
    AGAR_SLOTS = ['D2','D3']
    layout = list(zip(agar_plate_names,AGAR_SLOTS[:len(agar_plate_names)]))
    print(layout)
    # Specify the locations of each object on the deck
    locations = {
                "tiprack-200" : "A3",
                "tiprack-10_2" : "E3",
                "tiprack-10_3" : "E2",
                "tiprack-10_1" : "E1",
                "trash" : "D1",
                "PCR-strip-tall" : "C3",
                "Transformation" : "C2",
                "Tube_rack" : "B1"
            }
    locations.update(dict(layout))
    ot.print_layout(locations)

    ## Initialize the OT-1
    ## ============================================

    # Determine whether to simulate or run the protocol
    if args.run:
        port = os.environ["ROBOT_DEV"]
        print("Connecting robot to port {}".format(port))
        robot.connect(port)
    else:
        print("Simulating protcol run")
        robot.connect()

    # Start timer
    start = datetime.now()
    print("Starting run at: ",start)

    # Start up and declare components on the deck
    robot.home()

    p200_tipracks = [
        containers.load('tiprack-200ul', locations['tiprack-200']),
    ]

    p10_tipracks = [
        containers.load('tiprack-10ul', locations['tiprack-10_2']),
        containers.load('tiprack-10ul', locations['tiprack-10_3']),
        containers.load('tiprack-10ul', locations['tiprack-10_1'])
    ]
    p10s_tipracks = [
    ]

    transformation_plate = containers.load('96-PCR-tall', locations['Transformation'])
    trash = containers.load('point', locations['trash'], 'holywastedplasticbatman')
    centrifuge_tube = containers.load('tube-rack-2ml',locations['Tube_rack'])
    master = containers.load('PCR-strip-tall', locations['PCR-strip-tall'])

    agar_plates = {}
    for plate, slot in layout:
        agar_plates[plate] = containers.load('96-deep-well', slot)
        print("agar_plates", agar_plates[plate])
    print("agar_plates",agar_plates,"\n")

    p10,p10s,p200 = ot.initialize_pipettes(p10_tipracks,p10s_tipracks,p200_tipracks,trash)


    def agar_plating(pipette,row,volume,calibrate,z):
        '''
        Dispenses the cells to be plated prior to reaching the agar and then
        stabs the tips slightly into the agar such that the droplets that pull
        up on the side of the tips make contact with the agar
        '''
        pipette.dispense(volume-1,row.top())
        pipette.dispense(1,row.bottom())
        if calibrate:
            calibrate,z = ot.change_height(p10,agar_plates[plate],agar_plates[plate].rows(plating_row)[0],recalibrate=True)
        return calibrate,z

    num_dilutions = 4
    plate_vol = 7.5
    dilution_vol = 9
    waste_vol = 2.5
    plating_row = 0

    calibrate = True
    z = 0
    media_per_tube = 150
    plate = agar_plate_names[0]
    plate_counter = 0

    # Aliquot the LB into the PCR tube strip for the dilutions
    p200.pick_up_tip()
    for well in range(8):
       print("Transferring {}ul to tube {}".format(media_per_tube,well))
       p200.transfer(media_per_tube, centrifuge_tube['A1'].bottom(),master.wells(well).bottom(),new_tip='never')
    p200.drop_tip()

    #input("Start the other run")
    # Iterate through each row of the transformation plate
    for trans_row in range(num_rows):
        # Iterates through each dilution
        for dilution in range(num_dilutions):
            # Resets to switch to the next plate
            if plating_row == 12:
                p200.pick_up_tip()
                for well in range(8):
                    print("Transferring {}ul to tube {}".format(media_per_tube,well))
                    p200.transfer(media_per_tube, centrifuge_tube['B1'].bottom(),master.wells(well).bottom(),new_tip='never')
                p200.drop_tip()
                plate = agar_plate_names[1]
                print("changing to :", plate)
                plating_row = 0
                calibrate = True
            print("trans_row",trans_row, "dilution",dilution,"plate",plate,"plate row",plating_row)
            p10.pick_up_tip()
            print("Diluting cells in row {} with {}ul".format(trans_row, dilution_vol))
            p10.transfer(dilution_vol, master['A1'].bottom(), transformation_plate.rows(trans_row).bottom(),new_tip='never',mix_before=(1,9))
            print("Plating {}ul from transformation row {} onto {} in row {}".format(plate_vol,trans_row,plate,plating_row))
            p10.aspirate(plate_vol,transformation_plate.rows(trans_row).bottom())

            calibrate,z = agar_plating(p10,agar_plates[plate].rows(plating_row),plate_vol,calibrate,z)

            print("Discard {}ul from transformation row {} into waste tube".format(waste_vol,trans_row))
            p10.aspirate(waste_vol,transformation_plate.rows(trans_row).bottom())
            p10.drop_tip()
            plating_row += 1
    stop = datetime.now()
    print(stop)
    runtime = stop - start
    print("Total runtime is: ", runtime)
    print("Rehoming")
    robot.home()

    commit = ot.request_info("Commit changes? (y/n): ",type='string',select_from=['y','n'])
    if commit == 'y':
        if len(target_plate.wells) <= 48:
            target_plate.plated = 'plated'
        else:
            ans = ot.request_info('Plated both halves of the build? (y/n): ',type='string',select_from=['y','n'])
            if ans == 'y':
                target_plate.plated = 'plated'
        session.commit()
    return
Esempio n. 3
0
    return sub_location


layout = pd.read_csv('./all_deck_layout.csv')
layout['type'] = layout.container_type.apply(lambda x: 0
                                             if 'tiprack' in x else 1)
sections = layout.groupby(['pipette', 'type'])
for (pip, type), data in sections:
    data = data.reset_index()
    for i, row in data.iterrows():

        input('Make sure {} is in slot {}'.format(row['container_type'],
                                                  row['slot']))
        current_container = containers.load(row['container_type'], row['slot'])

        p10, p10s, p200 = ot.initialize_pipettes(p10_tipracks, p10s_tipracks,
                                                 p200_tipracks, trash)
        pipette_dict = {'p10': p10, 'p10s': p10s, 'p200': p200}
        pipette = pipette_dict[row['pipette']]
        sub_location = check_calibration(robot, pipette, current_container)
        if type == 0:
            print('Tipracks', len(data))

            if i == len(data) - 1:
                print('Last rack')
                last_rack = sub_location
            else:
                pipette.drop_tip(sub_location)
                print('On to next rack')

        elif type == 1:
            if i == len(data) - 1:
Esempio n. 4
0
def transform(session, engine):
    # Take in command line arguments
    parser = argparse.ArgumentParser(
        description="Resuspend a plate of DNA on an Opentrons OT-1 robot.")
    parser.add_argument(
        '-r',
        '--run',
        required=False,
        action="store_true",
        help="Send commands to the robot and print command output.")
    # parser.add_argument('-m', '--manual', required=False, action="store_true", help="Maunal entry of parameters.")
    args = parser.parse_args()

    assemblies = []
    print("Choose which plate you would like to transform:")
    for index, assembly in enumerate(
            session.query(Plate).join(
                Build, Plate.builds).filter(Build.status == 'building').filter(
                    Plate.transformed == 'not_transformed').order_by(
                        Build.build_name)):
        print("{}. {}".format(index, assembly.builds.build_name))
        assemblies.append(assembly)

    plate_num = int(ot.request_info("Enter plate here: ", type='int'))
    target_plate = assemblies[plate_num]

    num_reactions = len(target_plate.wells)
    num_rows = num_reactions // 8

    # Verify that the correct robot is being used
    if args.run:
        ot.check_robot()

    # Specify the locations of each object on the deck
    locations = {
        "tiprack-200": "A3",
        "tiprack-10_2": "E2",
        "tiprack-10_3": "E3",
        "tiprack-10_1": "E1",
        "trash": "D1",
        "Transformation": "C2",
        "Build_plate": "C3",
        "Tube_rack": "B1"
    }
    ot.print_layout(locations)

    ## Initialize the OT-1
    ## ============================================

    # Determine whether to simulate or run the protocol
    if args.run:
        port = os.environ["ROBOT_DEV"]
        print("Connecting robot to port {}".format(port))
        robot.connect(port)
    else:
        print("Simulating protcol run")
        robot.connect()

    # Start timer
    start = datetime.now()
    print("Starting run at: ", start)

    # Start up and declare components on the deck
    robot.home()

    p200_tipracks = [
        containers.load('tiprack-200ul', locations['tiprack-200']),
    ]

    p10_tipracks = [
        containers.load('tiprack-10ul', locations['tiprack-10_2']),
        containers.load('tiprack-10ul', locations['tiprack-10_1']),
    ]

    p10s_tipracks = [
        containers.load('tiprack-10ul', locations["tiprack-10_3"])
    ]

    transformation_plate = containers.load('96-PCR-tall',
                                           locations['Transformation'])
    trash = containers.load('point', locations['trash'],
                            'holywastedplasticbatman')
    centrifuge_tube = containers.load('tube-rack-2ml', locations['Tube_rack'])
    build_plate = containers.load('96-PCR-tall', locations['Build_plate'])

    p10, p10s, p200 = ot.initialize_pipettes(p10_tipracks, p10s_tipracks,
                                             p200_tipracks, trash)

    dna_vol = 2

    for row in range(num_rows):
        p10.pick_up_tip()
        p10.transfer(dna_vol,
                     build_plate.rows(row).bottom(),
                     transformation_plate.rows(row).bottom(),
                     new_tip='never',
                     mix_before=(2, 9),
                     blow_out=True)
        print('Transferring DNA from row {}'.format(row))
        p10.drop_tip()
    if num_reactions % 8 > 0:
        p10s.pick_up_tip()
        print("need single channel for {}".format(num_reactions % 8))
        for missing in range(num_reactions % 8):
            current_well = (8 * num_rows) + (missing)
            print("Transferring {}ul of DNA to {}".format(
                dna_vol, current_well))
            p10s.transfer(dna_vol,
                          build_plate.wells(current_well).bottom(),
                          transformation_plate.wells(current_well).bottom(),
                          blow_out=True,
                          mix_before=(1, 8),
                          new_tip='never')
            p10s.drop_tip()
    commit = ot.request_info("Commit changes? (y/n): ",
                             type='string',
                             select_from=['y', 'n'])
    if commit == 'y':
        target_plate.transformed = 'transformed'
        session.commit()
    else:
        print('Not committed')
Esempio n. 5
0
def resuspension(session, engine, target):
    ot.print_center("============ Beginning resuspension ============")

    # Initial Setup
    fmoles = 40

    # Load files
    parser = argparse.ArgumentParser(
        description="Resuspend a plate of DNA on an Opentrons OT-1 robot.")
    parser.add_argument(
        '-r',
        '--run',
        required=False,
        action="store_true",
        help="Send commands to the robot and print command output.")
    args = parser.parse_args()

    # Verify that the correct robot is being used
    if args.run:
        ot.check_robot()

    ## =============================================
    ## SETUP THE OT-1 DECK
    ## =============================================
    # Specify the locations of each object on the deck
    locations = {
        "tiprack-200": "A3",
        "tiprack-10": "E2",
        "tiprack-10s1": "E3",
        "tiprack-10s2": "E1",
        "trash": "D1",
        "PLATE HERE": "B2",
        "Trough": "B1"
    }
    ot.print_layout(locations)

    ot.print_center('...Calculating the volumes to resuspend with...')

    def calc_vol(amount, length, fmoles=40):
        return math.ceil(
            ((((amount * 1000) / (660 * length)) * 1000) / fmoles) * 2), fmoles

    total = 0
    for well in target.wells:
        length = len(well.fragments.seq)
        amount = well.syn_yield
        volume, conc = calc_vol(amount, length)
        well.volume = volume
        well.concentration = conc
        total += volume
        session.add(well)

    # print("total volume of water needed: {}uL".format(total))
    # num_tubes = math.ceil(total / 1000)
    # print("Prep {} tubes with 1.2mL".format(num_tubes))
    # input("Press Enter when you have added them to the tube rack")

    ## Initialize the OT-1

    # Determine whether to simulate or run the protocol
    if args.run:
        #port = robot.get_serial_ports_list()[0]
        port = os.environ["ROBOT_DEV"]
        print("Connecting robot to port {}".format(port))
        robot.connect(port)
    else:
        print("Simulating protcol run")
        robot.connect()

    start = datetime.now()
    print("Starting run at: ", start)

    # Start up and declare components on the deck
    robot.home()

    p200_tipracks = [
        containers.load('tiprack-200ul', locations["tiprack-200"]),
    ]

    p10_tipracks = [
        containers.load('tiprack-10ul', locations["tiprack-10"]),
    ]

    p10s_tipracks = [
        containers.load('tiprack-10ul', locations["tiprack-10s1"]),
        containers.load('tiprack-10ul', locations["tiprack-10s2"])
    ]

    trash = containers.load('point', locations["trash"],
                            'holywastedplasticbatman')
    centrifuge_tube = containers.load('trough-12row', locations["Trough"])
    # centrifuge_tube = containers.load('tube-rack-2ml',locations["Tube_rack"])

    resuspend_plate = containers.load('96-flat', locations["PLATE HERE"])

    p10, p10s, p200 = ot.initialize_pipettes(p10_tipracks, p10s_tipracks,
                                             p200_tipracks, trash)

    ## =============================================
    ## OT-1 PROTOCOL
    ## =============================================

    # Start timer
    start = datetime.now()
    print("Starting run at: ", start)

    tubes = dict({
        1: "A1",
        2: "B1",
        3: "C1",
        4: "D1",
        5: "A2",
        6: "B2",
        7: "C2",
        8: "D2",
        9: "A3",
        10: "B3",
        11: "C3",
        12: "D3",
        13: "A4",
        14: "B4",
        15: "C4"
    })
    tube_count = 1
    current_vol = 1200
    last_pipette = "neither"
    exclude_wells = []

    for target_well in target.wells:
        vol = target_well.volume
        well = target_well.address
        if well in exclude_wells:
            continue
        # Determine which pipette to use
        if vol < 20:
            # Makes sure that the robot doesn't pick up multiple sets of tips
            if last_pipette == "p200":
                print("Changing to p10s")
                p200.drop_tip()
                p10s.pick_up_tip()
            elif last_pipette == "neither":
                p10s.pick_up_tip()

            # Changes tubes of water when one gets low
            # if current_vol - vol < 200:
            #     tube_count += 1
            #     current_vol = 1200
            # current_vol -= vol
            print("Adding {}ul to well {} with the p10".format(vol, well))
            #p10s.transfer(vol, centrifuge_tube[tubes[tube_count]].bottom(), resuspend_plate.wells(well),touch_tip=True, blow_out=True, new_tip='never')
            p10s.transfer(vol,
                          centrifuge_tube['A1'],
                          resuspend_plate.wells(well),
                          touch_tip=True,
                          blow_out=True,
                          new_tip='never')
            # print("Currently {}ul in tube {}".format(current_vol,tubes[tube_count]))
            last_pipette = "p10s"
        else:
            if last_pipette == "p10s":
                print("Changing to p200")
                p10s.drop_tip()
                p200.pick_up_tip()
            elif last_pipette == "neither":
                p200.pick_up_tip()

            # Changes tubes of water when one gets low
            # if current_vol - vol < 100:
            #     tube_count += 1
            #     current_vol = 1200
            # current_vol -= vol
            print("Adding {}ul to well {} with the p200".format(vol, well))
            #p200.transfer(vol, centrifuge_tube[tubes[tube_count]].bottom(), resuspend_plate.wells(well),touch_tip=True, blow_out=True, new_tip='never')
            p200.transfer(vol,
                          centrifuge_tube['A1'],
                          resuspend_plate.wells(well),
                          touch_tip=True,
                          blow_out=True,
                          new_tip='never')
            # print("currently {}ul in tube {}".format(current_vol,tubes[tube_count]))
            last_pipette = "p200"

    # Last drop tip
    if last_pipette == "p10s":
        p10s.drop_tip()
    elif last_pipette == "p200":
        p200.drop_tip()

    stop = datetime.now()
    print(stop)
    runtime = stop - start
    print("Total runtime is: ", runtime)
    robot.home()

    target.resuspend()
    session.add(target)

    commit = int(ot.request_info("Commit changes (1-yes, 2-no): ", type='int'))
    if commit == 1:
        session.commit()

    ot.print_center('...Completed resuspension...')

    return
Esempio n. 6
0
def run_build(session, engine):

    ot.print_center("============ Beginning build ============")

    # Take in the 'run' argument from the command line
    parser = argparse.ArgumentParser(
        description="Resuspend a plate of DNA on an Opentrons OT-1 robot.")
    parser.add_argument(
        '-r',
        '--run',
        required=False,
        action="store_true",
        help="Send commands to the robot and print command output.")
    args = parser.parse_args()

    # Verify that the correct robot is being used
    if args.run:
        ot.check_robot()

    # Choose which build plan to build
    build_options = []
    builds = [
        build
        for build in session.query(Build).filter(Build.status == 'planning')
    ]
    if len(builds) == 0:
        sys.exit(
            'No plans available, run `create_build_plan.py` to generate them')

    for num, build in enumerate([
            build for build in session.query(Build).filter(
                Build.status == 'planning')
    ]):
        print('{} - {}'.format(num, build.build_name))
        build_options.append(build)
    ans = ot.request_info("Enter desired plan number: ", type='int')
    target_build = build_options[ans]

    # Use that build name to create a dataframe with the information from the plan
    query = "SELECT parts.part_id,builds.build_name,part_wells.address as destination,fragments.fragment_name,frag_plates.plate_name,frag_plates.plate_id,frag_wells.address as source,frag_wells.volume FROM parts \
            INNER JOIN wells AS part_wells ON parts.id = part_wells.part_id\
            INNER JOIN plates AS part_plates ON part_wells.plate_id = part_plates.id\
            INNER JOIN builds ON part_plates.build_id = builds.id\
            INNER JOIN part_frag ON parts.id = part_frag.part_id\
            INNER JOIN fragments ON part_frag.fragment_id = fragments.id\
            INNER JOIN wells AS frag_wells ON fragments.id = frag_wells.fragment_id\
            INNER JOIN plates AS frag_plates ON frag_wells.plate_id = frag_plates.id\
            WHERE builds.build_name = '{}'".format(target_build.build_name)

    build_plan = pd.read_sql_query(query, con=engine)
    print(build_plan)
    frags = build_plan.groupby('part_id').agg(len)
    if len(frags) == len(
        [frag for frag in frags.fragment_name.tolist() if frag == 2]):
        print('Build only contains 2 fragment assemblies')
        num_frags = 2
        rxn_vol = 0.6
    else:
        print('Using MM for single fragment')
        rxn_vol = 0.8
        num_frags = 1

    unique_plates = build_plan.plate_id.unique().tolist()

    # Give each row a rank based on the order of the plates to sort on later
    plate_dict = dict([[y, x] for x, y in enumerate(unique_plates)])
    build_plan['plate_rank'] = build_plan.plate_id.apply(
        lambda x: plate_dict[x])
    build_plan = build_plan.sort_values('plate_rank')

    # Currently available spots on the OT-one deck
    SOURCE_SLOTS = ['D2', 'D3', 'B2']

    ## Generate a list of unique plates that are needed
    plate_index = [(y, x) for x, y in enumerate(unique_plates)]
    plate_index = dict(plate_index)

    ## Group the plates so that they can be swapped in batches
    ot.print_center("...Grouping plates...")
    group_plates = [
        unique_plates[n:n + len(SOURCE_SLOTS)]
        for n in range(0, len(unique_plates), len(SOURCE_SLOTS))
    ]
    for num, group in enumerate(group_plates):
        print("Group{}: {}".format(num + 1, group))

    ot.print_center("...Checking if plates need to be resuspended...")

    query_resuspend = "SELECT plates.plate_id,plates.resuspended FROM plates\
                        WHERE plates.resuspended = 'not_resuspended'\
                            AND plates.plate_id IN ({})".format(
        ot.list_to_string(unique_plates))

    resuspended = pd.read_sql_query(query_resuspend, con=engine)
    if len(resuspended) == 0:
        print('All plates are resuspended')
    else:
        for i, plate in resuspended.iterrows():
            ans = ot.request_info(
                'Plate {} is not resuspended, would you like to resuspend it? y/n: '
                .format(plate['plate_id']),
                type='string')
            if ans == 'y':
                resuspend.resuspension(
                    session, engine,
                    session.query(Plate).filter(
                        Plate.plate_id == plate['plate_id']).one())

    query = "SELECT parts.part_id,builds.build_name,part_wells.address as destination,fragments.fragment_name,frag_plates.plate_name,frag_plates.plate_id,frag_wells.address as source,frag_wells.volume FROM parts \
            INNER JOIN wells AS part_wells ON parts.id = part_wells.part_id\
            INNER JOIN plates AS part_plates ON part_wells.plate_id = part_plates.id\
            INNER JOIN builds ON part_plates.build_id = builds.id\
            INNER JOIN part_frag ON parts.id = part_frag.part_id\
            INNER JOIN fragments ON part_frag.fragment_id = fragments.id\
            INNER JOIN wells AS frag_wells ON fragments.id = frag_wells.fragment_id\
            INNER JOIN plates AS frag_plates ON frag_wells.plate_id = frag_plates.id\
            WHERE builds.build_name = '{}'".format(target_build.build_name)

    build_plan = pd.read_sql_query(query, con=engine)
    build_plan['plate_rank'] = build_plan.plate_id.apply(
        lambda x: plate_dict[x])
    build_plan = build_plan.sort_values('plate_rank')

    input("Press enter to continue")

    ## =============================================
    ## SETUP THE OT-1 DECK
    ## =============================================

    # Specify the locations of each object on the deck
    locations = {
        "tiprack-200": "A3",
        "tiprack-10": "E1",
        "tiprack-10s1": "E3",
        "tiprack-10s2": "E2",
        "trash": "D1",
        "PCR-strip-tall": "C3",
        "DEST_PLATE": "C2",
        "Tube_rack": "B1"
    }
    # Sets the first group of plates
    used_plates = []
    plate_counter = 0
    current_group = group_plates[plate_counter]
    source_plates = ot.change_plates(locations, current_group, SOURCE_SLOTS)

    ## =============================================
    ## SETUP THE MASTER MIX
    ## =============================================

    vol = int(
        ot.request_info('Enter desired reaction volume (i.e. 5,10,20): ',
                        type='int'))

    # Set the proportion of master mix to fragment to 4:1
    master_volume = rxn_vol * vol
    frag_vol = 0.2 * vol

    ot.print_center('...Calculating master mix volumes...')

    # Set a multiplier to account for pipetting error and evaporation
    extra_master = 1.3

    unique_frag = build_plan[['part_id', 'fragment_name',
                              'destination']].drop_duplicates()

    frag_df = unique_frag.groupby('destination').agg(len).part_id
    frag_df = frag_df.reset_index()
    frag_df = frag_df.rename(columns={'part_id': 'frag_num'})
    frag_dict = dict(
        zip(frag_df.destination.tolist(), frag_df.frag_num.tolist()))

    build_plan['frag_num'] = build_plan.destination.apply(
        lambda x: frag_dict[x])

    unique_df = build_plan[['part_id', 'destination',
                            'frag_num']].drop_duplicates()

    total_rxns = unique_df.frag_num.sum()

    need_extra = unique_df[unique_df.frag_num > 1]

    num_wells = len(build_plan.part_id.unique().tolist())
    num_rows = num_wells // 8
    master_reactions = math.ceil((total_rxns) * extra_master)
    print("Total rxns: {}".format(total_rxns, master_reactions))

    # Generate the dataframe to present the master mix composition
    master_mix = ot.make_gg_rxns(master_reactions, master_volume)
    print("Use the table below to create the master mix")
    print()
    print(master_mix)
    print()
    print("Place the master mix in the 'A1' position of the tube rack")
    print("Also place a tube of with 1.2 mL of water in the 'B1' position ")
    input("Press enter to continue")

    ## =============================================
    ## INITIALIZE THE OT-1
    ## =============================================
    # Determine whether to simulate or run the protocol

    if args.run:
        port = os.environ["ROBOT_DEV"]
        print("Connecting robot to port {}".format(port))
        robot.connect(port)
    else:
        print("Simulating protcol run")
        robot.connect()

    # Declare components on the deck
    p200_tipracks = [
        containers.load('tiprack-200ul', locations["tiprack-200"]),
    ]
    p10_tipracks = [
        containers.load('tiprack-10ul', locations["tiprack-10"]),
    ]
    p10s_tipracks = [
        containers.load('tiprack-10ul', locations["tiprack-10s1"]),
        containers.load('tiprack-10ul', locations["tiprack-10s2"])
    ]
    trash = containers.load('point', locations["trash"],
                            'holywastedplasticbatman')
    centrifuge_tube = containers.load('tube-rack-2ml', locations["Tube_rack"])
    master = containers.load('PCR-strip-tall', locations["PCR-strip-tall"])
    dest_plate = containers.load('96-PCR-tall', locations["DEST_PLATE"])

    p10, p10s, p200 = ot.initialize_pipettes(p10_tipracks, p10s_tipracks,
                                             p200_tipracks, trash)

    # Update database status
    def exit_handler():
        print('Choose one of the following options:')
        print('1-Save successful assembly\n2-Restore plan\n3-Abandon plan')
        ans = ot.request_info('Select what to do: ',
                              type='int',
                              select_from=[1, 2, 3])
        if ans == 1:
            ot.print_center('...Assembly is complete...')
        elif ans == 2:
            target_build.status = 'planning'
            ot.print_center('...Restoring the build plan...')
            for part in session.query(Part).filter(
                    Part.part_id.in_(build_plan.part_id.unique().tolist())):
                part.change_status('planning')
        elif ans == 3:
            target_build.status = 'abandoned'
            ot.print_center('...Unstaging all parts in build plan...')
            for part in session.query(Part).filter(
                    Part.part_id.in_(build_plan.part_id.unique().tolist())):
                part.change_status('received')
        session.commit()

    target_build.status = 'building'
    session.commit()

    atexit.register(exit_handler)

    ## =============================================
    ## OT-1 PROTOCOL
    ## =============================================

    # Start timer
    start = datetime.now()
    print("Starting run at: ", start)

    # Home the robot to start
    robot.home()

    # Aliquot the master mix into the PCR tube strip
    vol_per_tube = round((num_rows * master_volume * extra_master), 2)
    print("Aliquoting MM into PCR tubes")
    print("{}ul into each tube".format(vol_per_tube))
    p200.pick_up_tip()
    for well in range(8):
        print("Transferring {}ul to well {}".format(vol_per_tube, well))
        p200.transfer(vol_per_tube,
                      centrifuge_tube['A1'].bottom(),
                      master.wells(well).bottom(),
                      mix_before=(3, 50),
                      new_tip='never')
    p200.drop_tip()

    # Aliquot the master mix into all of the desired wells
    p10.pick_up_tip()
    for row in range(num_rows):
        print("Transferring {}ul of master mix to row {}".format(
            master_volume,
            int(row) + 1))
        p10.transfer(master_volume,
                     master['A1'].bottom(),
                     dest_plate.rows(row).bottom(),
                     mix_before=(1, 8),
                     blow_out=True,
                     new_tip='never')
    p10.drop_tip()

    # Aliquot master mix into the last row if not a complete row
    if num_wells % 8 > 0:
        p10s.pick_up_tip()
        print("need single channel for {}".format(num_wells % 8))
        for missing in range(num_wells % 8):
            current_well = (8 * num_rows) + (missing)
            print("Transferring {}ul of extra MM to {}".format(
                master_volume, current_well))
            p10s.transfer(master_volume,
                          centrifuge_tube['A1'].bottom(),
                          dest_plate.wells(current_well).bottom(),
                          blow_out=True,
                          mix_before=(1, 8),
                          new_tip='never')
        p10s.drop_tip()

    # Aliquot extra master mix into wells with multiple fragments
    if len(need_extra) != 0:
        p10s.pick_up_tip()
        for i, transfer in need_extra.iterrows():
            extra_volume = (int(transfer['frag_num']) - 1) * master_volume
            current_well = transfer['destination']
            print("Transferring {}ul of extra MM to {}".format(
                extra_volume, current_well))
            p10s.transfer(extra_volume,
                          centrifuge_tube['A1'].bottom(),
                          dest_plate.wells(current_well).bottom(),
                          blow_out=True,
                          mix_before=(1, 8),
                          new_tip='never')
        p10s.drop_tip()
    else:
        print('No extra MM must be aliquoted')

## Add the fragments from the source plates to the destination plate
## ============================================

# Sets the volume of water to dilute with, if needed
    dil_vol = 5

    build_plan = build_plan.sort_values('plate_rank')
    for i, row in build_plan.iterrows():
        start_well = row['source']
        dest_well = row['destination']
        gene = row['part_id']
        plate = row['plate_id']
        volume = row['volume']

        if plate not in current_group:
            plate_counter += 1
            current_group = group_plates[plate_counter]
            source_plates = ot.change_plates(locations, current_group,
                                             SOURCE_SLOTS)

        p10s.pick_up_tip()

        # Only dilutes wells that have low starting volume
        if volume < 30:
            print("Diluting sample in plate {} well {} with {}uL of water".
                  format(plate, start_well, dil_vol))
            p10s.transfer(dil_vol,
                          centrifuge_tube['B1'].bottom(),
                          source_plates[plate].wells(start_well).bottom(),
                          new_tip='never')

        print("Transferring {} of {} from plate {} well {} to well {}".format(
            frag_vol, gene, plate, start_well, dest_well))
        p10s.mix(3, 8, source_plates[plate].wells(start_well).bottom())

        # Checks the calibration to make sure that it can aspirate correctly
        p10s.aspirate(frag_vol,
                      source_plates[plate].wells(start_well).bottom())
        # if plate not in used_plates:
        #     ot.change_height(p10s,source_plates[plate],source_plates[plate].wells(start_well))
        p10s.dispense(frag_vol, dest_plate.wells(dest_well).bottom())
        used_plates.append(plate)

        p10s.drop_tip()

    robot.home()

    ot.print_center('...Updating part status...')

    to_build = [well.parts for well in target_build.plates[0].wells]
    for part in to_build:
        part.change_status('building')
    session.commit()

    return