Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 73 additions & 1 deletion src/pydna/assembly2.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from pydna.gateway import gateway_overlap, find_gateway_sites
from pydna.cre_lox import cre_loxP_overlap
from pydna.alphabet import anneal_strands
from pydna.recombinase import Recombinase

from typing import TYPE_CHECKING, Callable, Literal
from pydna.opencloning_models import (
Expand All @@ -52,6 +53,7 @@
GatewaySource,
HomologousRecombinationSource,
CreLoxRecombinationSource,
RecombinaseSource,
PCRSource,
SourceInput,
CRISPRSource,
Expand Down Expand Up @@ -1968,11 +1970,15 @@ def __init__(self, frags: [Dseqrecord], limit=25, algorithm=common_sub_strings):
self.G.add_node(1, seq=frag)

matches = algorithm(frag, frag, limit)
# Remove matches where the whole sequence matches
matches = [match for match in matches if match[2] != len(frag)]

for match in matches:
self.add_edges_from_match(match, 1, 1, frag, frag)

# To avoid duplicated outputs
self.G.remove_edges_from([(-1, -1)])
while (-1, -1) in self.G.edges():
self.G.remove_edges_from([(-1, -1)])

# These two are constrained
self.use_fragment_order = True
Expand Down Expand Up @@ -2801,6 +2807,72 @@ def cre_lox_excision(genome: Dseqrecord) -> list[Dseqrecord]:
return _recast_sources(products, CreLoxRecombinationSource)


def recombinase_excision(
genome: Dseqrecord,
recombinase: Recombinase,
) -> list[Dseqrecord]:
"""Returns the products for recombinase-mediated excision.

Parameters
----------
genome : Dseqrecord
Target genome sequence containing two recombinase sites.
recombinase : Recombinase
Recombinase object.

Returns
-------
list[Dseqrecord]
List containing excised plasmid and remaining genome sequence.
"""
products = common_function_excision_products(genome, None, recombinase.overlap)
products = [recombinase.annotate(p) for p in products]
return _recast_sources(products, RecombinaseSource)


def recombinase_integration(
genome: Dseqrecord,
inserts: list[Dseqrecord],
recombinase: Recombinase,
) -> list[Dseqrecord]:
"""Returns the products resulting from recombinase-mediated integration.

Parameters
----------
genome : Dseqrecord
Target genome sequence.
inserts : list[Dseqrecord]
DNA fragment(s) to insert.
recombinase : Recombinase
Recombinase object.

Returns
-------
list[Dseqrecord]
List of integrated DNA molecules.

Examples
--------
>>> from pydna.dseqrecord import Dseqrecord
>>> from pydna.assembly2 import recombinase_integration, recombinase_excision
>>> from pydna.recombinase import Recombinase
>>> site1 = "ATGCCCTAAaaTT"
>>> site2 = "AAaaTTTTTTTCCCT"
>>> genome = Dseqrecord(f"cccccc{site1.upper()}aaaaa")
>>> insert = Dseqrecord(f"{site2.upper()}bbbbb", circular=True)
>>> recombinase = Recombinase(site1, site2)
>>> products = recombinase_integration(genome, [insert], recombinase)
>>> len(products) >= 1
True
"""
fragments = common_handle_insertion_fragments(genome, inserts)
products = common_function_integration_products(
fragments, None, recombinase.overlap
)
products = [recombinase.annotate(p) for p in products]
return _recast_sources(products, RecombinaseSource)


def crispr_integration(
genome: Dseqrecord,
inserts: list[Dseqrecord],
Expand Down
246 changes: 103 additions & 143 deletions src/pydna/gateway.py
Original file line number Diff line number Diff line change
@@ -1,164 +1,124 @@
# -*- coding: utf-8 -*-
from Bio.Seq import reverse_complement
from pydna.dseqrecord import Dseqrecord
import re
import itertools
from Bio.SeqFeature import SimpleLocation, SeqFeature
from pydna.utils import shift_location
from pydna.sequence_regex import compute_regex_site, dseqrecord_finditer


raw_gateway_common = {
"attB1": "CHWVTWTGTACAAAAAANNNG",
"attB2": "CHWVTWTGTACAAGAAANNNG",
"attB3": "CHWVTWTGTATAATAAANNNG",
"attB4": "CHWVTWTGTATAGAAAANNNG",
"attB5": "CHWVTWTGTATACAAAANNNG",
"attL1": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTGTACAAAAAANNNG",
"attL2": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTGTACAAGAAANNNG",
"attL3": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTGTATAATAAANNNG",
"attL4": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTGTATAGAAAANNNG",
"attL5": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTGTATACAAAANNNG",
"attR1": "CHWVTWTGTACAAAAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attR2": "CHWVTWTGTACAAGAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attR3": "CHWVTWTGTATAATAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attR4": "CHWVTWTGTATAGAAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attR5": "CHWVTWTGTATACAAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"overlap_1": "twtGTACAAAaaa",
"overlap_2": "twtGTACAAGaaa",
"overlap_3": "twtGTATAATaaa",
"overlap_4": "twtGTATAGAaaa",
"overlap_5": "twtGTATACAaaa",
}


raw_gateway_sites_greedy = {
**raw_gateway_common,
"attP1": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTGTACAAAAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attP2": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTGTACAAGAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attP3": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTGTATAATAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attP4": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTGTATAGAAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attP5": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTGTATACAAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
}

raw_gateway_sites_conservative = {
**raw_gateway_common,
"attP1": "AAAWWAWKRWTTTWWTTTGACTGATAGTGACCTGTTCGTTGCAACAMATTGATRAGCAATGCTTTYTTATAATGCCMASTTTGTACAAAAAAGYWGAACGAGAARCGTAAARTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATACTGTAAAACACAACATATSCAGTCACTATGAAYCAACTACTTAGATGGTATTAGTGACCTGTA",
"attP2": "AAAWWAWKRWTTTWWTTTGACTGATAGTGACCTGTTCGTTGCAACAMATTGATRAGCAATGCTTTYTTATAATGCCMASTTTGTACAAGAAAGYWGAACGAGAARCGTAAARTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATACTGTAAAACACAACATATSCAGTCACTATGAAYCAACTACTTAGATGGTATTAGTGACCTGTA",
"attP3": "AAAWWAWKRWTTTWWTTTGACTGATAGTGACCTGTTCGTTGCAACAMATTGATRAGCAATGCTTTYTTATAATGCCMASTTTGTATAATAAAGYWGAACGAGAARCGTAAARTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATACTGTAAAACACAACATATSCAGTCACTATGAAYCAACTACTTAGATGGTATTAGTGACCTGTA",
"attP4": "AAAWWAWKRWTTTWWTTTGACTGATAGTGACCTGTTCGTTGCAACAMATTGATRAGCAATGCTTTYTTATAATGCCMASTTTGTATAGAAAAGYWGAACGAGAARCGTAAARTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATACTGTAAAACACAACATATSCAGTCACTATGAAYCAACTACTTAGATGGTATTAGTGACCTGTA",
"attP5": "AAAWWAWKRWTTTWWTTTGACTGATAGTGACCTGTTCGTTGCAACAMATTGATRAGCAATGCTTTYTTATAATGCCMASTTTGTATACAAAAGYWGAACGAGAARCGTAAARTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATACTGTAAAACACAACATATSCAGTCACTATGAAYCAACTACTTAGATGGTATTAGTGACCTGTA",
}

gateway_sites_greedy = {
k: {
"forward_regex": compute_regex_site(v),
"reverse_regex": compute_regex_site(reverse_complement(v)),
"consensus_sequence": v,
from Bio.SeqFeature import SimpleLocation
from pydna.recombinase import Recombinase, RecombinaseCollection


def create_recombinase_dict() -> dict[str, dict[str, RecombinaseCollection]]:
"""Create a dictionary of recombinases for the Gateway reaction."""
raw_gateway_common = {
"attB1": "CHWVTWTgtacaaaAAANNNG",
"attB2": "CHWVTWTgtacaagAAANNNG",
"attB3": "CHWVTWTgtataatAAANNNG",
"attB4": "CHWVTWTgtatagaAAANNNG",
"attB5": "CHWVTWTgtatacaAAANNNG",
"attL1": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTgtacaaaAAANNNG",
"attL2": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTgtacaagAAANNNG",
"attL3": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTgtataatAAANNNG",
"attL4": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTgtatagaAAANNNG",
"attL5": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTgtatacaAAANNNG",
"attR1": "CHWVTWTgtacaaaAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attR2": "CHWVTWTgtacaagAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attR3": "CHWVTWTgtataatAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attR4": "CHWVTWTgtatagaAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attR5": "CHWVTWTgtatacaAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"overlap_1": "TWTgtacaaaAAA",
"overlap_2": "TWTgtacaagAAA",
"overlap_3": "TWTgtataatAAA",
"overlap_4": "TWTgtatagaAAA",
"overlap_5": "TWTgtatacaAAA",
}
for k, v in raw_gateway_sites_greedy.items()
}

gateway_sites_conservative = {
k: {
"forward_regex": compute_regex_site(v),
"reverse_regex": compute_regex_site(reverse_complement(v)),
"consensus_sequence": v,

raw_gateway_sites_greedy = {
**raw_gateway_common,
"attP1": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTgtacaaaAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attP2": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTgtacaagAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attP3": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTgtataatAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attP4": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTgtatagaAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
"attP5": "VAAWWAWKRWTTTWWTTYGACTGATAGTGACCTGTWCGTYGMAACAVATTGATRAGCAATKMTTTYYTATAWTGHCMASTWTgtatacaAAAGYWGARCGAGAARCGTAARRTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATRCTGTAARACACAACATATBCAGTCV",
}

raw_gateway_sites_conservative = {
**raw_gateway_common,
"attP1": "AAAWWAWKRWTTTWWTTTGACTGATAGTGACCTGTTCGTTGCAACAMATTGATRAGCAATGCTTTYTTATAATGCCMASTTTgtacaaaAAAGYWGAACGAGAARCGTAAARTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATACTGTAAAACACAACATATSCAGTCACTATGAAYCAACTACTTAGATGGTATTAGTGACCTGTA",
"attP2": "AAAWWAWKRWTTTWWTTTGACTGATAGTGACCTGTTCGTTGCAACAMATTGATRAGCAATGCTTTYTTATAATGCCMASTTTgtacaagAAAGYWGAACGAGAARCGTAAARTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATACTGTAAAACACAACATATSCAGTCACTATGAAYCAACTACTTAGATGGTATTAGTGACCTGTA",
"attP3": "AAAWWAWKRWTTTWWTTTGACTGATAGTGACCTGTTCGTTGCAACAMATTGATRAGCAATGCTTTYTTATAATGCCMASTTTgtataatAAAGYWGAACGAGAARCGTAAARTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATACTGTAAAACACAACATATSCAGTCACTATGAAYCAACTACTTAGATGGTATTAGTGACCTGTA",
"attP4": "AAAWWAWKRWTTTWWTTTGACTGATAGTGACCTGTTCGTTGCAACAMATTGATRAGCAATGCTTTYTTATAATGCCMASTTTgtatagaAAAGYWGAACGAGAARCGTAAARTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATACTGTAAAACACAACATATSCAGTCACTATGAAYCAACTACTTAGATGGTATTAGTGACCTGTA",
"attP5": "AAAWWAWKRWTTTWWTTTGACTGATAGTGACCTGTTCGTTGCAACAMATTGATRAGCAATGCTTTYTTATAATGCCMASTTTgtatacaAAAGYWGAACGAGAARCGTAAARTGATATAAATATCAATATATTAAATTAGAYTTTGCATAAAAAACAGACTACATAATACTGTAAAACACAACATATSCAGTCACTATGAAYCAACTACTTAGATGGTATTAGTGACCTGTA",
}
for k, v in raw_gateway_sites_conservative.items()
}

# From snapgene - ask Valerie
primer_design_attB = {
"attB1": "ACAAGTTTGTACAAAAAAGCAGGCT",
"attB2": "ACCACTTTGTACAAGAAAGCTGGGT",
"attB3": "ACAACTTTGTATAATAAAGTTGTA",
"attB4": "ACAACTTTGTATAGAAAAGTTGTA",
"attB5": "ACAACTTTGTATACAAAAGTTGTA",
}
out_dict = {}

for reaction in ["BP", "LR"]:
left, right = reaction
conservative: list[Recombinase] = []
greedy: list[Recombinase] = []
for i in range(1, 6):
site1 = f"att{left}{i}"
site2 = f"att{right}{i}"
seq1_conservative = raw_gateway_sites_conservative[site1]
seq2_conservative = raw_gateway_sites_conservative[site2]
seq1_greedy = raw_gateway_sites_greedy[site1]
seq2_greedy = raw_gateway_sites_greedy[site2]
conservative.append(
Recombinase(seq1_conservative, seq2_conservative, site1, site2)
)
greedy.append(Recombinase(seq1_greedy, seq2_greedy, site1, site2))

out_dict[reaction] = {
"conservative": RecombinaseCollection(conservative),
"greedy": RecombinaseCollection(greedy),
}
return out_dict


recombinase_dict = create_recombinase_dict()


def gateway_overlap(
seqx: Dseqrecord, seqy: Dseqrecord, reaction: str, greedy: bool
) -> list[tuple[int, int, int]]:
"""
Find gateway overlaps. If greedy is True, it uses a more greedy consensus site to find attP sites,
which might give false positives
Assembly Algorithm: Find gateway overlaps. If greedy is True, it uses a more greedy consensus site to find attP sites,
which might give false positives.

Parameters
----------
seqx : Dseqrecord
First sequence to find overlaps.
seqy : Dseqrecord
Second sequence to find overlaps.
reaction : str
Type of Gateway reaction (BP or LR).
greedy : bool
If True, use greedy gateway consensus sites.

Returns
-------
list[tuple[int, int, int]] A list of overlaps between the two sequences.
"""
if reaction not in ["BP", "LR"]:
raise ValueError(f"Invalid overlap type: {reaction}")

gateway_sites = gateway_sites_greedy if greedy else gateway_sites_conservative
out = list()
# Iterate over the four possible att sites
for num in range(1, 5):
# Iterate over the two possible orientations
# The sites have to be in the same orientation (fwd + fwd or rev + rev)
for pattern in ["forward_regex", "reverse_regex"]:
# The overlap regex is the same for all types
overlap_regex = gateway_sites[f"overlap_{num}"][pattern]

# Iterate over pairs B, P and P, B for BP and L, R and R, L for LR
for site_x, site_y in zip(reaction, reaction[::-1]):
site_x_regex = gateway_sites[f"att{site_x}{num}"][pattern]
matches_x = list(dseqrecord_finditer(site_x_regex, seqx))
if len(matches_x) == 0:
continue

site_y_regex = gateway_sites[f"att{site_y}{num}"][pattern]
matches_y = list(dseqrecord_finditer(site_y_regex, seqy))
if len(matches_y) == 0:
continue

for match_x, match_y in itertools.product(matches_x, matches_y):
# Find the overlap sequence within each match, and use the
# core 7 pbs that are constant
overlap_x = re.search(overlap_regex, match_x.group())
overlap_y = re.search(overlap_regex, match_y.group())

# Sanity check
assert (
overlap_x is not None and overlap_y is not None
), "Something went wrong, no overlap found within the matches"

out.append(
(
match_x.start() + overlap_x.start() + 3,
match_y.start() + overlap_y.start() + 3,
7,
)
)

return out
mode = "greedy" if greedy else "conservative"
return recombinase_dict[reaction][mode].overlap(seqx, seqy)


def find_gateway_sites(
seq: Dseqrecord, greedy: bool
) -> dict[str, list[SimpleLocation]]:
"""Find all gateway sites in a sequence and return a dictionary with the name and positions of the sites."""
gateway_sites = gateway_sites_greedy if greedy else gateway_sites_conservative
out = dict()
for site in gateway_sites:
if not site.startswith("att"):
continue

for pattern in ["forward_regex", "reverse_regex"]:
matches = list(dseqrecord_finditer(gateway_sites[site][pattern], seq))
for match in matches:
if site not in out:
out[site] = []
strand = 1 if pattern == "forward_regex" else -1
loc = SimpleLocation(match.start(), match.end(), strand)
loc = shift_location(loc, 0, len(seq))
out[site].append(loc)
return out

mode = "greedy" if greedy else "conservative"
collection = RecombinaseCollection(
recombinase_dict["BP"][mode].recombinases
+ recombinase_dict["LR"][mode].recombinases
)
return collection.find(seq)


def annotate_gateway_sites(seq: Dseqrecord, greedy: bool) -> Dseqrecord:
sites = find_gateway_sites(seq, greedy)
for site in sites:
for loc in sites[site]:
seq.features.append(
SeqFeature(loc, type="protein_bind", qualifiers={"label": [site]})
)
return seq
"""Annotate gateway sites in a sequence."""
mode = "greedy" if greedy else "conservative"
collection = RecombinaseCollection(
recombinase_dict["BP"][mode].recombinases
+ recombinase_dict["LR"][mode].recombinases
)
return collection.annotate(seq)
4 changes: 4 additions & 0 deletions src/pydna/opencloning_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,10 @@ class CreLoxRecombinationSource(AssemblySource):
)


class RecombinaseSource(AssemblySource):
pass


class PCRSource(AssemblySource):
TARGET_MODEL: ClassVar[Type[_PCRSource]] = _PCRSource
add_primer_features: bool = Field(default=False)
Expand Down
Loading
Loading