def create_BLAST_database(file_path='{}/raw_files/BLAST_db/current_BLAST_db.fsa'.format(BASE_PATH)): '''Generates a multi-input fasta file to store all of the sequences in the in the BLAST database''' ot.print_center('...Generating BLAST database...') db_counter = 1 with open(file_path,"w+") as fsa: for part in session.query(Part).order_by(Part.part_id): fsa.write(">{}|BLAST_db|{}\n{}\n".format(part.part_id,db_counter,part.seq)) db_counter += 1 # Convert the FSA file into a BLAST database os.system("makeblastdb -in {} -parse_seqids -dbtype nucl".format(file_path))
def connect_db(): ot.print_center('...Connecting to the database...') ## Connect to the database specified in the config file engine = sqlalchemy.create_engine(CONNECTION_STRING, echo=False) ## Create and commit all of the tables Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) Session.configure(bind=engine) session = Session() return session, engine
def check_alignment(row): if row['outcome'] == 'perfect': return row['outcome'] ot.print_center('================== \u001b[1mForward\u001b[0m ==================\n') forward = row['for_align_str'].replace('.','\u001b[41m.\u001b[0m') forward = re.sub(r'(?<=[A-Z])-(?=[A-Z])', '\u001b[41m-\u001b[0m',forward) print(forward) ot.print_center('================== \u001b[1mReverse\u001b[0m ==================\n') reverse = row['rev_align_str'].replace('.','\u001b[41m.\u001b[0m') reverse = re.sub(r'(?<=[A-Z])-(?=[A-Z])', '\u001b[41m-\u001b[0m',reverse) print(reverse) ot.print_center('{}: \u001b[31;1m\u001b[1m{}\u001b[0m'.format(row['part_id'],row['outcome'])) print('Options: k:keep, c:check, p:perfect, m:mutation, b:bad_reads,f:cloning_failure') ans = ot.request_info('Please state the outcome: ',type='string',select_from=['c','k','p','m','b','f']) if ans == 'k': return row['outcome'] elif ans == 'c': return 'CHECK' elif ans == 'p': return 'perfect' elif ans == 'm': return 'mutation' elif ans == 'b': return 'bad_reads' elif ans == 'f': return 'cloning_failure'
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()
prefix = ot.request_info( 'Enter the desired prefix for the unique id numbers (i.e. "initials"_)' ) else: prefix = data.part_id.tolist()[0].split('_')[0] + '_' start_id = len(data) + 1 files = [] for i, file in enumerate(glob.glob('{}/src/*.csv'.format(BASE_PATH))): print('{}: {}'.format(i, file)) files.append(file) ans = ot.request_info('Which file would you like to upload: ', type='int', select_from=[num for num in range(len(files))]) target_file = files[ans] data = pd.read_csv(target_file) ot.print_center('...Adding samples to the database...') for i, part in data.iterrows(): current_id = prefix + str(start_id + i).zfill(3) new_part = Part(part_id=current_id, part_name=part['part_name'], part_type=part['part_type'], original_seq=part['original_sequence'].upper()) session.add(new_part) session.commit()
def create_build_plans(session,engine,num_parts,frag_nums,enzyme='BbsI',max_rxns=96): # 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() ot.print_center("============ Creating Plan ============") num_builds = math.ceil(num_parts / 96) print(num_builds) used_plates = [] if num_parts < max_rxns: print('Generating a partial build') max_rxns = num_parts previous_plans = [build.build_name for build in session.query(Build).order_by(Build.build_name).filter(Build.status == 'planning')] if len(previous_plans) > 0: print("Are you sure you don't want to use these plans instead:\n",previous_plans) ans = ot.request_info("Use one of these plans instead (y/n):\n{}".format(previous_plans)) if ans == 'y': print('Enter the plan into the build script') sys.exit() print('Will continue to exclude parts in previous plans') past_builds = pd.read_sql_query("SELECT builds.build_name FROM builds", con=engine).build_name.tolist() if len(past_builds) == 0: last_build = 0 else: last_build = [int(build[-3:]) for build in past_builds][-1] print('last_build',last_build) new_builds = ["build"+str(last_build+num).zfill(3) for num in range(1,num_builds+1)] print(new_builds) acceptable_status = ['received'] # List with all of the status that you want to pull from rework = ['cloning_error','cloning_failure','trans_failure'] def list_to_string(ls): string = '' for l in ls: string += "'{}',".format(l) return string[:-1] def query_for_parts(status,enzyme,engine): query_parts = "SELECT parts.part_id,parts.status,fragments.fragment_name,plates.plate_id,wells.address,wells.volume,plates.id FROM parts\ INNER JOIN part_frag ON parts.id = part_frag.part_id\ INNER JOIN fragments ON part_frag.fragment_id = fragments.id\ INNER JOIN wells ON fragments.id = wells.fragment_id\ INNER JOIN plates on wells.plate_id = plates.id\ WHERE parts.status IN ({})\ AND parts.cloning_enzyme = '{}'".format(list_to_string(status),enzyme) return pd.read_sql_query(query_parts, con=engine) for build,frag_num in zip(new_builds,frag_nums): ## Pulls in parts that have the desirable status and then sorts them by the plates their fragments are in, ## sorts them by their plate numbers and returns the earliest set of parts ot.print_center("...Finding genes for {}...".format(build)) to_build = query_for_parts(acceptable_status,enzyme,engine) to_build.id = to_build.plate_id.apply(lambda x: 0 if x in used_plates else 1) grouped = to_build.groupby('part_id').filter(lambda x: len(x) == x['id'].sum()) # print(grouped) if frag_num != 0: grouped = grouped.groupby('part_id').filter(lambda x: len(x) == frag_num) sort_group = grouped.sort_values('plate_id') # print(sort_group) sub = sort_group.part_id.unique().tolist() if len(sub) < max_rxns: print('Not enough remaining parts to fill a build') rework_ans = ot.request_info("Include previously failed genes? (y/n): ",type='string') if rework_ans == 'y': rework_df = query_for_parts(rework,enzyme,engine).sort_values('plate_id') sort_group = pd.concat([sort_group,rework_df]) sub = sort_group.part_id.unique().tolist()[:max_rxns] else: print('truncating parts') sub = sub[:max_rxns] print(len(sub)) to_build = sort_group[sort_group.part_id.isin(sub)] print(len(to_build)) # print('To Build:\n',to_build) num_reactions = len(to_build) print("{} includes {} parts".format(build,len(to_build.part_id.unique().tolist()))) print("Fragments sourced from each plate: ") print(to_build.groupby('part_id').agg(len).status.value_counts()) used_plates += to_build.plate_id.unique().tolist() print(to_build.plate_id.unique().tolist()) master_mix = ot.make_gg_rxns(num_reactions,10) print("Below is the required master mix for {}\n{}".format(build,master_mix)) build_parts = to_build.part_id.unique().tolist() if args.run: ot.print_center('...Updating the database...') build_objs = [part for part in session.query(Part).filter(Part.part_id.in_(build_parts))] target_build = Build(build_objs,build_name=build) target_build.status = 'planning' session.add(target_build) for part in build_objs: part.change_status('planning') session.commit() else: print('Did not write plan. Add -r to the end of the function to write them')
df_unknown['forward'],df_unknown['reverse'] = zip(*df_unknown.apply(trim_reads,axis=1)) df_unknown['for_raw'],df_unknown['for_align'],df_unknown['for_score'],df_unknown['rev_raw'],df_unknown['rev_align'],df_unknown['rev_score'],df_unknown['seq_length'],df_unknown['for_align_str'],df_unknown['rev_align_str'] = zip(*df_unknown.apply(align_reads,axis=1)) df_unknown['outcome'] = df_unknown.apply(analyze_align,axis=1) fasta_path = "{}/builds/{}/{}_fasta_files".format(BASE_PATH,target,target) ot.make_directory(fasta_path) df.apply(export_fasta,axis=1) print('Completed analysis: ',datetime.now()) print() print(df.outcome.value_counts()) print() ot.print_center('Press Enter to review questionable sequences') input() df['outcome'] = df.apply(check_alignment,axis=1) df_unknown['outcome'] = df_unknown.apply(check_alignment,axis=1) print('New outcome totals:') print(df.outcome.value_counts()) print(df_unknown.outcome.value_counts()) ot.print_center('...Updating the database...') def update_database(row): '''Adds all of the alignment information into the database''' tar_well = session.query(Well).join(Plate,Well.plates).join(Build,Plate.builds)\ .join(Part,Well.parts).filter(Build.build_name == row['build_name'])\
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
'-r', '--run', required=False, action="store_true", help="Send commands to the robot and print command output.") args = parser.parse_args() # Declare components on the deck p200_tipracks = [] p10_tipracks = [] p10s_tipracks = [] trash = containers.load('point', 'D1', 'holywastedplasticbatman') if args.run: port = os.environ["ROBOT_DEV"] ot.print_center("...Connecting robot to port {}...".format(port)) robot.connect(port) else: ot.print_center("...Simulating protcol run...") robot.connect() robot.home() def move_ot(pipette): '''Allows for easy manual driving of the OT-One''' step = 1 print("Initiating manual control of OT") print( "'w'-'s' = back - forward\n'a'-'d' = left - right\n'W'-'S' = up - down" )
def frag_assign(): ## Build a list of the previously entered synthesis plates plates_made = [ plate.plate_name for plate in session.query(Plate).filter( Plate.plate_type == 'syn_plate') ] print("Plates that have already been parsed:\n", plates_made) ## Stores the last used plate ID so that it can increment previous_plates = [plate for plate in session.query(Plate).filter(Plate.plate_type == 'syn_plate')\ .order_by(Plate.plate_id)] if len(previous_plates) == 0: print('No previous plate ID, starting at 001') current_id = 1 else: last_plate = previous_plates[-1] print('last ID ', last_plate.plate_id) current_id = int(last_plate.plate_id[-3:]) + 1 ## Take in plate maps from twist and generate fragment objects for file in glob.glob('{}/src/data/plate_maps/*.csv'.format(BASE_PATH)): plate_map = pd.read_csv(file) ## Have to account for two different csv formats try: plate_map['Plate'] version = 1 print('version', version) plate_key = 'Plate' well_key = 'Well' name_key = 'customer_line_item_id' sequence_key = 'Insert Sequence' yield_key = 'Yield (ng)' except: try: plate_map['Plate ID'] version = 2 print('version', version) plate_key = 'Plate ID' well_key = 'Well Location' name_key = 'Name' sequence_key = 'Insert sequence' yield_key = 'Yield (ng)' except: print( "Doesn't fit the standard formats, please check column names in {}" .format(file)) sys.exit(0) unique = plate_map[plate_key].unique() for plate in unique: # Skips all of the plates that have already been parsed and added to the database if plate in plates_made: print('Plate {} has already been added'.format(plate)) continue ot.print_center("...Parsing plate {}...".format(plate)) # Create a subset of rows pertaining to a specific plate current_plate_map = plate_map[plate_map[plate_key] == plate] current_plate = Plate('', 'syn_plate', plate, plate_id='syn_plate' + str(current_id).zfill(3)) current_id += 1 session.add(current_plate) for index, row in current_plate_map.iterrows(): current_frag = '' current_frag = session.query(Fragment).filter( Fragment.seq == row[sequence_key]).first() # Checks if a corresponding fragment is found and then adds it to the plate if current_frag: current_plate.add_item(current_frag, address=row[well_key].strip(), syn_yield=row[yield_key]) else: new_frag = Fragment(fragment_name=row[name_key], seq=row[sequence_key].upper(), has_part='False') current_plate.add_item(new_frag, address=row[well_key].strip(), syn_yield=row[yield_key]) ot.print_center('...Updating part status...') for part in session.query(Part).join(Fragment,Part.fragments).join(Well,Fragment.wells)\ .join(Plate,Well.plates).filter(Plate.plate_name.notin_(plates_made)): # print(part.part_id) no_build = False for frag in part.fragments: if not frag.wells: no_build = True if not no_build: part.change_status('received') session.add(part) commit = int(ot.request_info("Commit changes (1-yes, 2-no): ", type='int')) if commit == 1: session.commit() current_ids = pd.read_sql_query( "SELECT plates.plate_name,plates.plate_id FROM plates WHERE plates.plate_type = 'syn_plate'", con=engine) print(current_ids) print( "Write the ID on the bottom left corner of the front edge of each plate" )
def assess_plate(): # Determine which build/plate to assess assemblies = [] print("Choose which build you would like to assess:") for index,assembly in enumerate(session.query(Plate).join(Build,Plate.builds)\ .filter(Build.status == 'building').filter(Plate.plated == 'plated')\ .filter(Plate.assessed == 'not_assessed').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_num = int(input("Enter build here: ")) # target_build = assemblies[build_num] print("Will assess: ", target_plate.builds.build_name) ## ============================================= ## QUERY DATABASE FOR REPLACEMENTS ## ============================================= # Take in the wells that didn't work print("Enter well addresses that didn't work. e.g. A1 H6") failed = [x for x in input("List: ").split(" ")] print("Will exclude the following:", failed) ot.print_center('...Generating sequencing plate...') # Generate an empty sequencing plate and update the build status seq_plate = Plate([], 'seq_plate', '{}-seq'.format(target_plate.builds.build_name)) target_plate.builds.plates.append(seq_plate) target_plate.builds.status = 'sequencing' session.add(seq_plate) # Change the status of the wells and add successful ones to seq_plate for well in target_plate.builds.plates[0].wells: if well.address in failed: well.trans_outcome = 'trans_failure' else: well.trans_outcome = 'trans_success' seq_plate.add_item(well.parts, address=well.address) # Query the plate to find how many wells it has already used used_wells = [well for well in seq_plate.wells] remaining = 96 - len(used_wells) print("Remaining: ", remaining) if remaining != 0: ans = ot.request_info("Backfill empty wells? (y/n): ", type='string', select_from=['y', 'n']) if ans == 'n': remaining = 0 # Pull parts that need to the retried acceptable_status = [ 'sequence_failure', 'cloning_mutation' ] # List with all of the status that you want to pull from replacement_parts = [part for part in session.query(Part).join(Well,Part.wells)\ .join(Plate,Well.plates).join(Build,Plate.builds).filter(Part.status\ .in_(acceptable_status)).order_by(Build.id.desc(),Well.id)] # Find the wells that house the parts with the desired status rep_wells = [] for part in replacement_parts: skip = False for well in part.wells: if skip: continue if well.seq_outcome == part.status: rep_wells.append(well) skip = True continue # Limit the number of wells to the number required and add it to the seq plate rep_wells = rep_wells[:remaining] part_to_well = dict(zip(replacement_parts[:remaining], rep_wells)) for well in rep_wells: seq_plate.add_item(well.parts) print(well.parts.part_id, well.plates.builds.build_name, well.address) # Generate a dictionary to sort on well addresses A1-H1 instead of A1-A12 well_index = ot.well_addresses() well_index = enumerate(well_index) well_index = [(y, x) for x, y in well_index] well_index = dict(well_index) # Generate a dictionary to link the parts to the well with the desired status part_to_well = dict(zip(replacement_parts[:remaining], rep_wells)) # Iterate through the plate and add the necessary information for the map parts = [] build_names = [] source_wells = [] target_wells = [] target_index = [] # Add all of the needed info to build a map for well in seq_plate.wells: parts.append(well.parts.part_id) # Finds parts coming from a different build and specifies where its coming from if well.parts in part_to_well.keys(): build_names.append( '__' + part_to_well[well.parts].plates.builds.build_name + '__') source_wells.append(part_to_well[well.parts].address) else: build_names.append(well.plates.builds.build_name) source_wells.append(well.address) target_wells.append(well.address) target_index.append(well_index[well.address]) # Build the plate map and sort based on the well index seq_plate_map = pd.DataFrame({ 'Part': parts, 'Build': build_names, 'Source Well': source_wells, 'Target Well': target_wells, 'Target Index': target_index }) seq_plate_map = seq_plate_map.set_index('Target Index') seq_plate_map = seq_plate_map.sort_index() print(seq_plate_map) # Print the plates that are required unique_builds = pd.Series(seq_plate_map['Build']).unique() print("You will need the following builds:") print(unique_builds) # Generate a sequence plate map path = '{}/src/data/builds/{}/'.format(BASE_PATH, target_plate.builds.build_name) ot.make_directory(path) file_path = '{}/{}_seq_plate.csv'.format(path, target_plate.builds.build_name) seq_plate_map.to_csv(file_path, index=False) commit = ot.request_info("Commit changes? (y/n): ", type='string', select_from=['y', 'n']) if commit == 'y': target_plate.assessed = 'assessed' target_plate.builds.status = 'sequencing' session.commit() else: print('Not committed')
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
df = pd.read_sql_query(query_seqs, con=engine) def optimize_sequences(row): if row['part_type'] != 'cds': return row['original_seq'] if row['organism'] == None: table = codon.load_codon_table(species='ecoli') else: table = codon.load_codon_table(species=row['organism']) protein_seq = fixer.translate(row['original_seq']) optimized = codon.optimize_protein(table, protein_seq) fixed = fixer.fix_sequence(row['part_id'], optimized) return fixed ot.print_center("...Optimizing sequences...") print("Starting at: {}".format(datetime.now())) df = df.loc[0:99, :] df['seq'] = df.apply(optimize_sequences, axis=1) print("Finished at: {}".format(datetime.now())) seq_dict = dict(zip(df.part_id.tolist(), df.seq.tolist())) for part in session.query(Part).filter(Part.part_id.in_(df.part_id.tolist())): part.seq = seq_dict[part.part_id] session.commit()
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
print('{}: {}'.format(i,build.build_name)) builds.append(build) ans = ot.request_info('Which build would you like to check: ',type='int',select_from=[x for x in range(len(builds))]) tar_build = builds[ans] data = pd.read_csv(glob.glob('{}/builds/{}/{}_manual_check.csv'.format(BASE_PATH,tar_build.build_name,tar_build.build_name))[0]) outcome_dict = { 'k':'keep', 'p':'perfect', 'm':'mutation', 'f':'cloning_failure' } data['manual'] = data.manual.apply(lambda x: outcome_dict[x]) ot.print_center('...Updating the database...') for i,row in data.iterrows(): tar_well = session.query(Well).join(Plate,Well.plates).join(Build,Plate.builds)\ .join(Part,Well.parts).filter(Build.build_name == tar_build.build_name)\ .filter(Part.part_id == row['part_id']).filter(Plate.plate_type == 'seq_plate')\ .filter(Well.address == row['address']).one() tar_well.seq_outcome = row['manual'] session.commit() query = "SELECT wells.seq_outcome,wells.misplaced FROM wells\ INNER JOIN plates ON wells.plate_id = plates.id\ INNER JOIN builds ON plates.build_id = builds.id\ WHERE builds.build_name = '{}'\ AND plates.plate_type = 'seq_plate'\