diff --git a/CHANGELOG.md b/CHANGELOG.md index 2348bf1..6cc40de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - `equality` operator overload - `gc_clamp` property +- `single_runs` property ## [0.1] - 2024-11-27 ### Added - `MeltingTemperature` enum diff --git a/README.md b/README.md index f34aa80..414d59a 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,11 @@ >>> primer1.gc_clamp 1 ``` +#### Single run length +```pycon +>>> primer1.single_runs +{'A': 2, 'T': 0, 'C': 0, 'G': 2} +``` #### Melting temperature ```pycon >>> primer1.melting_temperature() @@ -120,7 +125,7 @@ You can also join our discord server ## References - +
1- Oligo Calc: Oligonucleotide Properties Calculator
2- Marmur, Julius, and Paul Doty. "Determination of the base composition of deoxyribonucleic acid from its thermal denaturation temperature." Journal of molecular biology 5.1 (1962): 109-118.diff --git a/opr/functions.py b/opr/functions.py index 90c8c32..e742e65 100644 --- a/opr/functions.py +++ b/opr/functions.py @@ -49,3 +49,25 @@ def gc_clamp_calc(sequence): if len(sequence) < 5: return 0 return sequence[-5:].count('G') + sequence[-5:].count('C') + +def single_run_length(sequence, base): + """ + Calculate the maximum consecutive occurrence of a Nucleic acid (base) in a sequence. + + :param sequence: primer sequence + :type sequence: str + :param base: target Nucleic acid + :type base: str + :return: the length of maximum consecutive occurrence + """ + max_length = 0 + current_length = 0 + for c in sequence: + if c == base: + current_length += 1 + max_length = max(max_length, current_length) + else: + current_length = 0 + if max_length == 1: + return 0 + return max_length diff --git a/opr/primer.py b/opr/primer.py index 69eae54..fa83e05 100644 --- a/opr/primer.py +++ b/opr/primer.py @@ -10,7 +10,7 @@ from .params import DNA_COMPLEMENT_MAP from .params import PRIMER_ADDITION_ERROR, PRIMER_MULTIPLICATION_ERROR from .params import PRIMER_MELTING_TEMPERATURE_NOT_IMPLEMENTED_ERROR -from .functions import molecular_weight_calc, basic_melting_temperature_calc, gc_clamp_calc +from .functions import molecular_weight_calc, basic_melting_temperature_calc, gc_clamp_calc, single_run_length class MeltingTemperature(Enum): @@ -41,6 +41,7 @@ def __init__(self, sequence): self._molecular_weight = None self._gc_content = None self._gc_clamp = None + self._single_runs = None self._melting_temperature = { MeltingTemperature.BASIC: None, MeltingTemperature.SALT_ADJUSTED: None, @@ -217,6 +218,34 @@ def gc_clamp(self, _): def gc_clamp(self, _): raise OPRBaseError(PRIMER_NOT_REMOVABLE_ATTRIBUTE_ERROR) + @property + def single_runs(self): + """ + Calculate Single Runs of the primer. + + Run length refers to how many times a single base is repeated consecutively in the primer. + + :return: single runs of the primer + """ + if self._single_runs is None: + self._single_runs = { + 'A': 0, + 'T': 0, + 'C': 0, + 'G': 0, + } + for base in self._single_runs: + self._single_runs[base] = single_run_length(self._sequence, base) + return self._single_runs + + @single_runs.setter + def single_runs(self, _): + raise OPRBaseError(PRIMER_READ_ONLY_ATTRIBUTE_ERROR) + + @single_runs.deleter + def single_runs(self, _): + raise OPRBaseError(PRIMER_NOT_REMOVABLE_ATTRIBUTE_ERROR) + def melting_temperature(self, method=MeltingTemperature.BASIC): """ Calculate(if needed) the melting temperature. diff --git a/tests/test_calculations.py b/tests/test_calculations.py index a72c61e..bc268c7 100644 --- a/tests/test_calculations.py +++ b/tests/test_calculations.py @@ -40,3 +40,18 @@ def test_melt_temp_2(): #Reference: http://biotools.nubic.northwestern.edu/Oligo oprimer = Primer("ATCG") basic_melt_temp = oprimer.melting_temperature(method=MeltingTemperature.BASIC) assert round(basic_melt_temp,1) == 12 + +def test_single_runs_1(): #Reference: https://www.oligoevaluator.com/OligoCalcServlet + oprimer = Primer("ATCGATCG") + runs = oprimer.single_runs + assert runs['A'] == 0 and runs['T'] == 0 and runs['C'] == 0 and runs['G'] == 0 + +def test_single_runs_2(): #Reference: https://www.oligoevaluator.com/OligoCalcServlet + oprimer = Primer("ATTCGATCCCCG") + runs = oprimer.single_runs + assert runs['A'] == 0 and runs['T'] == 2 and runs['C'] == 4 and runs['G'] == 0 + +def test_single_runs_3(): #Reference: https://www.oligoevaluator.com/OligoCalcServlet + oprimer = Primer("AAAAATTCGGGGATCCCCG") + runs = oprimer.single_runs + assert runs['A'] == 5 and runs['T'] == 2 and runs['C'] == 4 and runs['G'] == 4