def pcr_from_genome(genome, primer_a_sequence, primer_b_sequence): """ PCR on genome, using two primer sequences. Returns tuple of PCR product sequence, primer a blast results, and primer b blast results. Produuct sequence may be None if primers do not bind unique or in a way that can result in a PCR product. A PCR can create a product if each primer uniquely binds to the genome, primers bind to the same fragment, on sense and antisense strands respectively, and non-overlapping in their sense strand binding positions """ primer_a_results = blast_genome(genome, 'blastn', primer_a_sequence) primer_b_results = blast_genome(genome, 'blastn', primer_b_sequence) pcr_products = [] for a_res in primer_a_results: for b_res in primer_b_results: product = compute_pcr_product(primer_a_sequence, a_res, primer_b_sequence, b_res) if product is not None: pcr_products.append(product) if len(pcr_products) == 1: product = pcr_products[0][0] region = pcr_products[0][1] return (product, primer_a_results, primer_b_results, dict(region=region['region'], fragment_name=region['fragment'].name, fragment_id=region['fragment'].id)) else: return (None, primer_a_results, primer_b_results, None)
def test_finds_sequence_on_specified_genome(self): s1 = 'atcggtatcttctatgcgtatgcgtcatgattatatatattagcggcatg' s2 = 'agcgtcgatgcatgagtcgatcggcagtcgtgtagtcgtcgtatgcgtta' g1 = Genome(name='Foo') g1.save() f1 = Fragment.create_with_sequence('Bar', s1) f2 = Fragment.create_with_sequence('Baz', s2) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() Genome_Fragment(genome=g1, fragment=f2, inherited=False).save() g2 = Genome(name='Far') g2.save() f3 = Fragment.create_with_sequence('Bar', s1) Genome_Fragment(genome=g2, fragment=f3, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) os.unlink(fragment_fasta_fn(f2)) os.unlink(fragment_fasta_fn(f3)) except: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = s1[6:20] + 'aaaaaaaaa' r = blast_genome(g1, 'blastn', query) # only returns hit from genome self.assertEquals(len(r), 1) self.assertEquals(r[0].fragment_id, f1.id) self.assertEquals(r[0].query_start, 1) self.assertEquals(r[0].query_end, 14) self.assertEquals(r[0].subject_start, 7) self.assertEquals(r[0].subject_end, 20) self.assertEquals(r[0].strand(), 1)
def test_finds_sequence_on_specified_genome(self): s1 = 'atcggtatcttctatgcgtatgcgtcatgattatatatattagcggcatg' s2 = 'agcgtcgatgcatgagtcgatcggcagtcgtgtagtcgtcgtatgcgtta' g1 = Genome(name='Foo') g1.save() f1 = Fragment.create_with_sequence('Bar', s1) f2 = Fragment.create_with_sequence('Baz', s2) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() Genome_Fragment(genome=g1, fragment=f2, inherited=False).save() g2 = Genome(name='Far') g2.save() f3 = Fragment.create_with_sequence('Bar', s1) Genome_Fragment(genome=g2, fragment=f3, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) os.unlink(fragment_fasta_fn(f2)) os.unlink(fragment_fasta_fn(f3)) except: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = s1[6:20] + 'aaaaaaaaa' r = blast_genome(g1, 'blastn', query) # only returns hit from genome self.assertEquals(len(r), 1) self.assertEquals(r[0].fragment_id, f1.id) self.assertEquals(r[0].query_start, 1) self.assertEquals(r[0].query_end, 14) self.assertEquals(r[0].subject_start, 7) self.assertEquals(r[0].subject_end, 20) self.assertEquals(r[0].strand(), 1)
def test_finds_sequence_on_specified_genome(self): s1 = get_random_sequence(200) s2 = get_random_sequence(200) g1 = Genome(name="Foo") g1.save() f1 = Fragment.create_with_sequence("Bar", s1) f2 = Fragment.create_with_sequence("Baz", s2) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() Genome_Fragment(genome=g1, fragment=f2, inherited=False).save() g2 = Genome(name="Far") g2.save() f3 = Fragment.create_with_sequence("Bar", s1) Genome_Fragment(genome=g2, fragment=f3, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) os.unlink(fragment_fasta_fn(f2)) os.unlink(fragment_fasta_fn(f3)) except BaseException: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = s1[6:20] + "aaaaaaaaa" r = blast_genome(g1, "blastn", query) # only returns hit from genome self.assertEquals(len(r), 1) self.assertEquals(r[0].fragment_id, f1.id) self.assertEquals(r[0].query_start, 1) self.assertEquals(r[0].query_end, 14) self.assertEquals(r[0].subject_start, 7) self.assertEquals(r[0].subject_end, 20) self.assertEquals(r[0].strand(), 1)
def get_cassette_inherited_annotations(genome, cassette, fragment_id, region_start, region_end): # blast cassette against unmodified genome cassette_blast_res = blast_genome(genome, 'blastn', cassette) # find matches inside region to be replaced matches = [ res for res in cassette_blast_res if res.fragment_id == fragment_id and res.subject_start >= region_start and res.subject_end <= region_end ] inherited_annotations = [] for blast_res in matches: # get annotations for each match annotations = blast_result_annotations(blast_res) # print blast_res.to_dict() # print annotations # update annotations with blast result for annotation in annotations: inherited_annotations.append( get_annotation_on_cassette(annotation, blast_res)) return inherited_annotations
def find_possible_swap_regions(genome, front_arm_sequence, back_arm_sequence): """ Computes all possible recombined region from arm sequences. """ front_arm_results = blast_genome(genome, 'blastn', front_arm_sequence) back_arm_results = blast_genome(genome, 'blastn', back_arm_sequence) regions = [] for a_res in front_arm_results: for b_res in back_arm_results: region = compute_swap_region_from_results(front_arm_sequence, a_res, back_arm_sequence, b_res) if region is not None: regions.append(region) return regions
def find_possible_swap_regions(genome, front_arm_sequence, back_arm_sequence, cassette): """ Computes all possible recombined region from arm sequences. """ front_arm_results = blast_genome(genome, 'blastn', front_arm_sequence) back_arm_results = blast_genome(genome, 'blastn', back_arm_sequence) regions = [] for a_res in front_arm_results: for b_res in back_arm_results: region = compute_swap_region_from_results(front_arm_sequence, a_res, back_arm_sequence, b_res, cassette) if region is not None: regions.append(region) return regions
def on_post(self, request, genome_id): from edge.blast import blast_genome genome = get_genome_or_404(genome_id) parser = RequestParser() parser.add_argument('query', field_type=str, required=True, location='json') parser.add_argument('program', field_type=str, required=True, location='json') args = parser.parse_args(request) results = blast_genome(genome, args['program'], args['query']) results = [r.to_dict() for r in results] return results, 200
def pcr_from_genome(genome, primer_a_sequence, primer_b_sequence): """ PCR on genome, using two primer sequences. Returns tuple of PCR product sequence, primer a blast results, and primer b blast results. Produuct sequence may be None if primers do not bind unique or in a way that can result in a PCR product. A PCR can create a product if each primer uniquely binds to the genome, primers bind to the same fragment, on sense and antisense strands respectively, and non-overlapping in their sense strand binding positions """ primer_a_results = blast_genome(genome, 'blastn', primer_a_sequence) primer_b_results = blast_genome(genome, 'blastn', primer_b_sequence) pcr_products = [] uniq_products = {} for a_res in primer_a_results: for b_res in primer_b_results: product = compute_pcr_product(primer_a_sequence, a_res, primer_b_sequence, b_res) if product is not None: k = (product[0], a_res.fragment_id) if k not in uniq_products: pcr_products.append(product) uniq_products[k] = product if len(pcr_products) == 1: product = pcr_products[0][0] region = pcr_products[0][1] return (product, primer_a_results, primer_b_results, dict(region=region['region'], fragment_name=region['fragment'].name, fragment_id=region['fragment'].id)) else: return (None, primer_a_results, primer_b_results, None)
def find_crispr_target(genome, guide, pam): """ Find sequences on genome that have exact match to guide, followed by pam sequence. """ guide_matches = blast_genome(genome, 'blastn', guide) targets = [] for res in guide_matches: if res.query_start == 1 and res.query_end == len(guide): target = target_followed_by_pam(res, pam) if target is not None: targets.append(target) return targets
def find_crispr_target(genome, guide, pam): """ Find sequences on genome that have exact match to guide, followed by pam sequence. """ guide_matches = blast_genome(genome, 'blastn', guide) targets = [] for res in guide_matches: if res.query_start == 1 and res.query_end == len(guide): target = target_followed_by_pam(res, pam) if target is not None: targets.append(target) return targets
def test_does_not_return_duplicate_hits_for_circular_fragments(self): s1 = 'atcggtatcttctatgcgtatgcgtcatgattatatatattagcggcatg' g1 = Genome(name='Foo') g1.save() f1 = Fragment.create_with_sequence('Bar', s1, circular=True) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) except: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = s1[5:20] + 'tttttttttt' r = blast_genome(g1, 'blastn', query) self.assertEquals(len(r), 1)
def test_does_not_return_duplicate_hits_for_circular_fragments(self): s1 = 'atcggtatcttctatgcgtatgcgtcatgattatatatattagcggcatg' g1 = Genome(name='Foo') g1.save() f1 = Fragment.create_with_sequence('Bar', s1, circular=True) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) except: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = s1[5:20] + 'tttttttttt' r = blast_genome(g1, 'blastn', query) self.assertEquals(len(r), 1)
def test_does_not_return_duplicate_hits_for_circular_fragments(self): s1 = get_random_sequence(200) g1 = Genome(name="Foo") g1.save() f1 = Fragment.create_with_sequence("Bar", s1, circular=True) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) except BaseException: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = s1[5:20] + "tttttttttt" r = blast_genome(g1, "blastn", query) self.assertEquals(len(r), 1)
def test_does_not_align_sequence_across_boundry_for_non_circular_fragment(self): s1 = get_random_sequence(200) g1 = Genome(name="Foo") g1.save() f1 = Fragment.create_with_sequence("Bar", s1, circular=False) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) except BaseException: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = (s1[-10:] + s1[0:10]) + "tttttttttt" res = blast_genome(g1, "blastn", query) for r in res: self.assertEquals(r.subject_start > 0 and r.subject_start <= len(s1), True) self.assertEquals(r.subject_end > 0 and r.subject_end <= len(s1), True)
def test_does_not_align_sequence_across_boundry_for_non_circular_fragment(self): s1 = 'atcggtatcttctatgcgtatgcgtcatgattatatatattagcggcatg' g1 = Genome(name='Foo') g1.save() f1 = Fragment.create_with_sequence('Bar', s1, circular=False) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) except: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = (s1[-10:] + s1[0:10]) + 'tttttttttt' res = blast_genome(g1, 'blastn', query) for r in res: self.assertEquals(r.subject_start > 0 and r.subject_start <= len(s1), True) self.assertEquals(r.subject_end > 0 and r.subject_end <= len(s1), True)
def on_post(self, request, genome_id): from edge.blast import blast_genome from edge.blastdb import check_and_build_genome_db genome = get_genome_or_404(genome_id) check_and_build_genome_db(genome) parser = RequestParser() parser.add_argument('query', field_type=str, required=True, location='json') parser.add_argument('program', field_type=str, required=True, location='json') args = parser.parse_args(request) results = blast_genome(genome, args['program'], args['query']) results = [r.to_dict() for r in results] return results, 200
def test_does_not_align_sequence_across_boundry_for_non_circular_fragment( self): s1 = 'atcggtatcttctatgcgtatgcgtcatgattatatatattagcggcatg' g1 = Genome(name='Foo') g1.save() f1 = Fragment.create_with_sequence('Bar', s1, circular=False) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) except: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = (s1[-10:] + s1[0:10]) + 'tttttttttt' res = blast_genome(g1, 'blastn', query) for r in res: self.assertEquals( r.subject_start > 0 and r.subject_start <= len(s1), True) self.assertEquals(r.subject_end > 0 and r.subject_end <= len(s1), True)
def get_cassette_inherited_annotations(genome, cassette, fragment_id, region_start, region_end): # blast cassette against unmodified genome cassette_blast_res = blast_genome(genome, 'blastn', cassette) # find matches inside region to be replaced matches = [res for res in cassette_blast_res if res.fragment_id == fragment_id and res.subject_start >= region_start and res.subject_end <= region_end] inherited_annotations = [] for blast_res in matches: # get annotations for each match annotations = blast_result_annotations(blast_res) # print blast_res.to_dict() # print annotations # update annotations with blast result for annotation in annotations: inherited_annotations.append(get_annotation_on_cassette(annotation, blast_res)) return inherited_annotations
def test_aligns_sequence_to_antisense_strand(self): s1 = 'atcggtatcttctatgcgtatgcgtcatgattatatatattagcggcatg' g1 = Genome(name='Foo') g1.save() f1 = Fragment.create_with_sequence('Bar', s1) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) except: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = str(Seq(s1[6:20]).reverse_complement()) + 'tttttttttt' r = blast_genome(g1, 'blastn', query) self.assertEquals(len(r), 1) self.assertEquals(r[0].fragment_id, f1.id) self.assertEquals(r[0].query_start, 1) self.assertEquals(r[0].query_end, 14) self.assertEquals(r[0].subject_start, 20) self.assertEquals(r[0].subject_end, 7) self.assertEquals(r[0].strand(), -1)
def test_aligns_sequence_to_antisense_strand(self): s1 = get_random_sequence(200) g1 = Genome(name="Foo") g1.save() f1 = Fragment.create_with_sequence("Bar", s1) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) except BaseException: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = str(Seq(s1[6:20]).reverse_complement()) + "tttttttttt" r = blast_genome(g1, "blastn", query) self.assertEquals(len(r), 1) self.assertEquals(r[0].fragment_id, f1.id) self.assertEquals(r[0].query_start, 1) self.assertEquals(r[0].query_end, 14) self.assertEquals(r[0].subject_start, 20) self.assertEquals(r[0].subject_end, 7) self.assertEquals(r[0].strand(), -1)
def test_aligns_sequence_to_antisense_strand(self): s1 = 'atcggtatcttctatgcgtatgcgtcatgattatatatattagcggcatg' g1 = Genome(name='Foo') g1.save() f1 = Fragment.create_with_sequence('Bar', s1) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) except: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = str(Seq(s1[6:20]).reverse_complement()) + 'tttttttttt' r = blast_genome(g1, 'blastn', query) self.assertEquals(len(r), 1) self.assertEquals(r[0].fragment_id, f1.id) self.assertEquals(r[0].query_start, 1) self.assertEquals(r[0].query_end, 14) self.assertEquals(r[0].subject_start, 20) self.assertEquals(r[0].subject_end, 7) self.assertEquals(r[0].strand(), -1)
def test_aligns_sequence_across_boundry_for_circular_fragment(self): s1 = get_random_sequence(200) g1 = Genome(name="Foo") g1.save() f1 = Fragment.create_with_sequence("Bar", s1, circular=True) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) except BaseException: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = (s1[-10:] + s1[0:10]) + "ttttttttttt" res = blast_genome(g1, "blastn", query) # we are not removing redundant matches when matching across circular # boundaries, since blasting across circular boundary of a genome is a # rare case. so in this particular case, you will find two results, one # for the end of the query at the start of the genome, one for across # the circular boundary. found = False for r in res: if r.query_start == 1 and r.query_end == 20: self.assertEquals(r.fragment_id, f1.id) self.assertEquals(r.query_start, 1) self.assertEquals(r.query_end, 20) self.assertEquals(r.subject_start, len(s1) - 10 + 1) self.assertEquals(r.subject_end, len(s1) + 10) self.assertEquals(r.fragment_length, len(s1)) self.assertEquals(r.strand(), 1) found = True break self.assertEquals(found, True)
def test_aligns_sequence_across_boundry_for_circular_fragment(self): s1 = 'atcggtatctactatgcgtatgcgtcatgattatatatattagcggcatg' g1 = Genome(name='Foo') g1.save() f1 = Fragment.create_with_sequence('Bar', s1, circular=True) Genome_Fragment(genome=g1, fragment=f1, inherited=False).save() try: os.unlink(fragment_fasta_fn(f1)) except: pass build_all_genome_dbs(refresh=True) g1 = Genome.objects.get(pk=g1.id) query = (s1[-10:] + s1[0:10]) + 'ttttttttttt' res = blast_genome(g1, 'blastn', query) # we are not removing redundant matches when matching across circular # boundaries, since blasting across circular boundary of a genome is a # rare case. so in this particular case, you will find two results, one # for the end of the query at the start of the genome, one for across # the circular boundary. found = False for r in res: if r.query_start == 1 and r.query_end == 20: self.assertEquals(r.fragment_id, f1.id) self.assertEquals(r.query_start, 1) self.assertEquals(r.query_end, 20) self.assertEquals(r.subject_start, len(s1) - 10 + 1) self.assertEquals(r.subject_end, len(s1) + 10) self.assertEquals(r.fragment_length, len(s1)) self.assertEquals(r.strand(), 1) found = True break self.assertEquals(found, True)
# flake8: noqa import django django.setup() import sys from edge.models import Genome from edge.blast import blast_genome from edge.pcr import pcr_from_genome genome = Genome.objects.get(pk=36161) print(genome.fragments.count()) res = blast_genome(genome, "blastn", "ctcacgttactgtggggtggaggggaca") for r in res: print("p: %s" % r.evalue) primer_fwd = "aggaagtgccattccgcctgacctcgtctcactgaccgtctctctcctgagtccgga" primer_rev = "aaagtgtcaaggtctcacgttactgtggggtggaggggaca" res = blast_genome(genome, "blastn", primer_fwd) for r in res: print("fwd: %s" % r.evalue) res = blast_genome(genome, "blastn", primer_rev) for r in res: print("rev: %s" % r.evalue) print(pcr_from_genome(genome, primer_fwd, primer_rev))
def pcr_from_genome(genome, primer_a_sequence, primer_b_sequence): """ PCR on genome, using two primer sequences. Returns tuple of PCR product sequence, primer a blast results, and primer b blast results. Produuct sequence may be None if primers do not bind unique or in a way that can result in a PCR product. A PCR can create a product if each primer uniquely binds to the genome, primers bind to the same fragment, on sense and antisense strands respectively, and non-overlapping in their sense strand binding positions """ primer_a_results = [] primer_a_binding_check_len = [] primer_b_results = [] primer_b_binding_check_len = [] res = blast_genome(genome, 'blastn', primer_a_sequence) for r in res: primer_a_results.append(r) primer_a_binding_check_len.append(len(primer_a_sequence)) res = blast_genome(genome, 'blastn', primer_b_sequence) for r in res: primer_b_results.append(r) primer_b_binding_check_len.append(len(primer_b_sequence)) # checking various binding length for i in range(MIN_BINDING_LENGTH, BINDING_LENGTH_HI): p = primer_a_sequence[-i:] res = blast_genome(genome, 'blastn', p) for r in res: primer_a_results.append(r) primer_a_binding_check_len.append(len(p)) # checking various binding length for i in range(MIN_BINDING_LENGTH, BINDING_LENGTH_HI): p = primer_b_sequence[-i:] res = blast_genome(genome, 'blastn', p) for r in res: primer_b_results.append(r) primer_b_binding_check_len.append(len(p)) pcr_products = [] uniq_products = {} for a_len, a_res in zip(primer_a_binding_check_len, primer_a_results): for b_len, b_res in zip(primer_b_binding_check_len, primer_b_results): product = compute_pcr_product(primer_a_sequence, a_len, a_res, primer_b_sequence, b_len, b_res) if product is not None: k = (product[0], a_res.fragment_id) if k not in uniq_products: pcr_products.append(product) uniq_products[k] = product if len(pcr_products) == 1: product = pcr_products[0][0] region = pcr_products[0][1] return (product, primer_a_results, primer_b_results, dict(region=region['region'], fragment_name=region['fragment'].name, fragment_id=region['fragment'].id)) else: return (None, primer_a_results, primer_b_results, None)
def pcr_from_genome(genome, primer_a_sequence, primer_b_sequence): """ PCR on genome, using two primer sequences. Returns tuple of PCR product sequence, primer a blast results, and primer b blast results. Produuct sequence may be None if primers do not bind unique or in a way that can result in a PCR product. A PCR can create a product if each primer uniquely binds to the genome, primers bind to the same fragment, on sense and antisense strands respectively, and non-overlapping in their sense strand binding positions """ primer_a_results = [] primer_a_binding_check_len = [] primer_b_results = [] primer_b_binding_check_len = [] res = blast_genome(genome, "blastn", primer_a_sequence) for r in res: primer_a_results.append(r) primer_a_binding_check_len.append(len(primer_a_sequence)) res = blast_genome(genome, "blastn", primer_b_sequence) for r in res: primer_b_results.append(r) primer_b_binding_check_len.append(len(primer_b_sequence)) # checking various binding length for i in range(MIN_BINDING_LENGTH, BINDING_LENGTH_HI): p = primer_a_sequence[-i:] res = blast_genome(genome, "blastn", p) for r in res: primer_a_results.append(r) primer_a_binding_check_len.append(len(p)) # checking various binding length for i in range(MIN_BINDING_LENGTH, BINDING_LENGTH_HI): p = primer_b_sequence[-i:] res = blast_genome(genome, "blastn", p) for r in res: primer_b_results.append(r) primer_b_binding_check_len.append(len(p)) pcr_products = [] uniq_products = {} for a_len, a_res in zip(primer_a_binding_check_len, primer_a_results): for b_len, b_res in zip(primer_b_binding_check_len, primer_b_results): product = compute_pcr_product(primer_a_sequence, a_len, a_res, primer_b_sequence, b_len, b_res) if product is not None: k = (product[0], a_res.fragment_id) if k not in uniq_products: pcr_products.append(product) uniq_products[k] = product if len(pcr_products) == 1: product = pcr_products[0][0] region = pcr_products[0][1] return ( product, primer_a_results, primer_b_results, dict( region=region["region"], fragment_name=region["fragment"].name, fragment_id=region["fragment"].id, ), ) else: return (None, primer_a_results, primer_b_results, None)