From 35186cf8ed5c7f3569ab546bf9975949951cdd5a Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Fri, 13 Sep 2024 15:12:44 +0430 Subject: [PATCH 1/8] feat : gc_content property added --- opr/opr_obj.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/opr/opr_obj.py b/opr/opr_obj.py index 94f8666..0db184d 100644 --- a/opr/opr_obj.py +++ b/opr/opr_obj.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- """OPR modules.""" +from warnings import warn from .opr_error import OPRBaseError from .opr_param import VALID_BASES from .opr_param import PRIMER_SEQUENCE_TYPE_ERROR, PRIMER_SEQUENCE_LENGTH_ERROR, PRIMER_SEQUENCE_VALID_BASES_ERROR, PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_ERROR @@ -27,6 +28,7 @@ def __init__(self, primer_sequence): """ self._sequence = Primer.validate_primer(primer_sequence) self._molecular_weight = None + self._gc_content = None def reverse(self, inplace=False): """ @@ -76,12 +78,6 @@ def validate_primer(primer_sequence): if not all(base in VALID_BASES for base in primer_sequence): raise OPRBaseError(PRIMER_SEQUENCE_VALID_BASES_ERROR) - - gc_count = primer_sequence.count('G') + primer_sequence.count('C') - gc_content = gc_count / len(primer_sequence) - - if gc_content < PRIMER_LOWEST_GC_RANGE or gc_content > PRIMER_HIGHEST_GC_RANGE: - raise OPRBaseError(PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_ERROR) return primer_sequence @property @@ -126,3 +122,18 @@ def molecular_weight(self, _): @molecular_weight.deleter def molecular_weight(self, _): self._molecular_weight = None + + @property + def gc_content(self): + """ + Calculate gc content. + + :return: gc content + """ + if self._gc_content is None: + gc_count = self._sequence.count('G') + self._sequence.count('C') + self._gc_content = gc_count / len(self._sequence) + if self._gc_content < PRIMER_LOWEST_GC_RANGE or self._gc_content > PRIMER_HIGHEST_GC_RANGE: + warn(PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_ERROR, RuntimeWarning) + return self._gc_content + From 5c24fd786d1e3214b002332ecce895f03308c32b Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Fri, 13 Sep 2024 15:16:19 +0430 Subject: [PATCH 2/8] fix : gc_content setter and deleter added --- opr/opr_obj.py | 11 +++++++++-- opr/opr_param.py | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/opr/opr_obj.py b/opr/opr_obj.py index 0db184d..68227c9 100644 --- a/opr/opr_obj.py +++ b/opr/opr_obj.py @@ -3,7 +3,7 @@ from warnings import warn from .opr_error import OPRBaseError from .opr_param import VALID_BASES -from .opr_param import PRIMER_SEQUENCE_TYPE_ERROR, PRIMER_SEQUENCE_LENGTH_ERROR, PRIMER_SEQUENCE_VALID_BASES_ERROR, PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_ERROR +from .opr_param import PRIMER_SEQUENCE_TYPE_ERROR, PRIMER_SEQUENCE_LENGTH_ERROR, PRIMER_SEQUENCE_VALID_BASES_ERROR, PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_WARNING from .opr_param import PRIMER_LOWER_LENGTH, PRIMER_HIGHEST_LENGTH, PRIMER_LOWEST_GC_RANGE, PRIMER_HIGHEST_GC_RANGE from .opr_param import PRIMER_READ_ONLY_ATTRIBUTE_ERROR, PRIMER_NOT_REMOVABLE_ATTRIBUTE_ERROR from .opr_param import A_WEIGHT, T_WEIGHT, C_WEIGHT, G_WEIGHT, ANHYDROUS_MOLECULAR_WEIGHT_CONSTANT @@ -134,6 +134,13 @@ def gc_content(self): gc_count = self._sequence.count('G') + self._sequence.count('C') self._gc_content = gc_count / len(self._sequence) if self._gc_content < PRIMER_LOWEST_GC_RANGE or self._gc_content > PRIMER_HIGHEST_GC_RANGE: - warn(PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_ERROR, RuntimeWarning) + warn(PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_WARNING, RuntimeWarning) return self._gc_content + @gc_content.setter + def gc_content(self, _): + raise OPRBaseError(PRIMER_READ_ONLY_ATTRIBUTE_ERROR) + + @gc_content.deleter + def gc_content(self, _): + raise OPRBaseError(PRIMER_NOT_REMOVABLE_ATTRIBUTE_ERROR) \ No newline at end of file diff --git a/opr/opr_param.py b/opr/opr_param.py index b1ecde8..6d1fede 100644 --- a/opr/opr_param.py +++ b/opr/opr_param.py @@ -18,6 +18,6 @@ PRIMER_SEQUENCE_TYPE_ERROR = "Primer sequence should be a string variable." PRIMER_SEQUENCE_LENGTH_ERROR = "Primer length should be between 18 and 30 nucleotides." PRIMER_SEQUENCE_VALID_BASES_ERROR = "Primer sequence should only contain the nucleotide bases A, T, C, and G." -PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_ERROR = "Primer GC content should be between 30% and 80%." +PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_WARNING = "Primer GC content should be between 30% and 80%." PRIMER_READ_ONLY_ATTRIBUTE_ERROR = "This attribute is read-only." PRIMER_NOT_REMOVABLE_ATTRIBUTE_ERROR = "This attribute is not removable." From a9e8bd111e5eaa770a501fea9ccaff3aaaee399f Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Fri, 13 Sep 2024 15:18:07 +0430 Subject: [PATCH 3/8] fix : PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_WARNING updated --- opr/opr_param.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opr/opr_param.py b/opr/opr_param.py index 6d1fede..25d2ec6 100644 --- a/opr/opr_param.py +++ b/opr/opr_param.py @@ -18,6 +18,6 @@ PRIMER_SEQUENCE_TYPE_ERROR = "Primer sequence should be a string variable." PRIMER_SEQUENCE_LENGTH_ERROR = "Primer length should be between 18 and 30 nucleotides." PRIMER_SEQUENCE_VALID_BASES_ERROR = "Primer sequence should only contain the nucleotide bases A, T, C, and G." -PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_WARNING = "Primer GC content should be between 30% and 80%." +PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_WARNING = "The recommended range for GC content is between 30% and 80%." PRIMER_READ_ONLY_ATTRIBUTE_ERROR = "This attribute is read-only." PRIMER_NOT_REMOVABLE_ATTRIBUTE_ERROR = "This attribute is not removable." From 740e5c63ea81475246c703a533c98a2115ade17a Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Fri, 13 Sep 2024 15:21:42 +0430 Subject: [PATCH 4/8] fix : PRIMER_SEQUENCE_LENGTH_WARNING updated --- opr/opr_obj.py | 4 ++-- opr/opr_param.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/opr/opr_obj.py b/opr/opr_obj.py index 68227c9..f9c1c99 100644 --- a/opr/opr_obj.py +++ b/opr/opr_obj.py @@ -3,7 +3,7 @@ from warnings import warn from .opr_error import OPRBaseError from .opr_param import VALID_BASES -from .opr_param import PRIMER_SEQUENCE_TYPE_ERROR, PRIMER_SEQUENCE_LENGTH_ERROR, PRIMER_SEQUENCE_VALID_BASES_ERROR, PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_WARNING +from .opr_param import PRIMER_SEQUENCE_TYPE_ERROR, PRIMER_SEQUENCE_LENGTH_WARNING, PRIMER_SEQUENCE_VALID_BASES_ERROR, PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_WARNING from .opr_param import PRIMER_LOWER_LENGTH, PRIMER_HIGHEST_LENGTH, PRIMER_LOWEST_GC_RANGE, PRIMER_HIGHEST_GC_RANGE from .opr_param import PRIMER_READ_ONLY_ATTRIBUTE_ERROR, PRIMER_NOT_REMOVABLE_ATTRIBUTE_ERROR from .opr_param import A_WEIGHT, T_WEIGHT, C_WEIGHT, G_WEIGHT, ANHYDROUS_MOLECULAR_WEIGHT_CONSTANT @@ -74,7 +74,7 @@ def validate_primer(primer_sequence): primer_sequence = primer_sequence.upper() if len(primer_sequence) < PRIMER_LOWER_LENGTH or len(primer_sequence) > PRIMER_HIGHEST_LENGTH: - raise OPRBaseError(PRIMER_SEQUENCE_LENGTH_ERROR) + warn(PRIMER_SEQUENCE_LENGTH_WARNING, RuntimeWarning) if not all(base in VALID_BASES for base in primer_sequence): raise OPRBaseError(PRIMER_SEQUENCE_VALID_BASES_ERROR) diff --git a/opr/opr_param.py b/opr/opr_param.py index 25d2ec6..8769f3a 100644 --- a/opr/opr_param.py +++ b/opr/opr_param.py @@ -16,7 +16,7 @@ ANHYDROUS_MOLECULAR_WEIGHT_CONSTANT = 61.96 PRIMER_SEQUENCE_TYPE_ERROR = "Primer sequence should be a string variable." -PRIMER_SEQUENCE_LENGTH_ERROR = "Primer length should be between 18 and 30 nucleotides." +PRIMER_SEQUENCE_LENGTH_WARNING = "The recommended range for primer length is between 18 and 30." PRIMER_SEQUENCE_VALID_BASES_ERROR = "Primer sequence should only contain the nucleotide bases A, T, C, and G." PRIMER_SEQUENCE_VALID_GC_CONTENT_RANGE_WARNING = "The recommended range for GC content is between 30% and 80%." PRIMER_READ_ONLY_ATTRIBUTE_ERROR = "This attribute is read-only." From 1e79a0db568a0d261c50717cd4fec239a38b107e Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Fri, 13 Sep 2024 15:22:43 +0430 Subject: [PATCH 5/8] doc : CHANGELOG.md updated --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b4e8e3..57c18a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - `Primer` class - Molecular weight calculation +- GC content calculation - Sequence validation - Unit tests - `complement` method From e1c584577164c773e645df35c9b9f893c76f05ff Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Fri, 13 Sep 2024 15:25:12 +0430 Subject: [PATCH 6/8] fix : tests name updated --- tests/{test_molecular_weight.py => test_calculations.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/{test_molecular_weight.py => test_calculations.py} (71%) diff --git a/tests/test_molecular_weight.py b/tests/test_calculations.py similarity index 71% rename from tests/test_molecular_weight.py rename to tests/test_calculations.py index f893110..5f0d62f 100644 --- a/tests/test_molecular_weight.py +++ b/tests/test_calculations.py @@ -1,6 +1,6 @@ from opr import Primer -TEST_CASE_NAME = "molecular weight calculation testcase" +TEST_CASE_NAME = "Calculations tests" def test_mwc(): oprimer = Primer("ATCGATCGATCGATCGAT") From c9e387f323ca12abdb5d93274c7ae6ab02693bc8 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Fri, 13 Sep 2024 15:34:29 +0430 Subject: [PATCH 7/8] fix : tests updated --- tests/test_calculations.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_calculations.py b/tests/test_calculations.py index 5f0d62f..e21e729 100644 --- a/tests/test_calculations.py +++ b/tests/test_calculations.py @@ -5,3 +5,17 @@ def test_mwc(): oprimer = Primer("ATCGATCGATCGATCGAT") assert round(oprimer.molecular_weight, 1) == 5498.7 + + +def test_gc_content_1(): #Reference: https://jamiemcgowan.ie/bioinf/gc_content.html + oprimer = Primer("ATCG") + assert oprimer.gc_content == 0.5 + + +def test_gc_content_2(): #Reference: https://jamiemcgowan.ie/bioinf/gc_content.html + oprimer = Primer("ATTCG") + assert oprimer.gc_content == 0.4 + +def test_gc_content_3(): #Reference: https://jamiemcgowan.ie/bioinf/gc_content.html + oprimer = Primer("ATTTTTT") + assert oprimer.gc_content == 0 From 4186880e740ee81c90540ad2170fdeaf300947bf Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Fri, 13 Sep 2024 15:34:40 +0430 Subject: [PATCH 8/8] fix : autopep8 --- opr/opr_obj.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opr/opr_obj.py b/opr/opr_obj.py index f9c1c99..22c1691 100644 --- a/opr/opr_obj.py +++ b/opr/opr_obj.py @@ -143,4 +143,4 @@ def gc_content(self, _): @gc_content.deleter def gc_content(self, _): - raise OPRBaseError(PRIMER_NOT_REMOVABLE_ATTRIBUTE_ERROR) \ No newline at end of file + raise OPRBaseError(PRIMER_NOT_REMOVABLE_ATTRIBUTE_ERROR)