diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bc0df40fcd50d8bd173ca9307fab0c095706e669..4d36a6fdcbb688da878d07e95fc71e8277d82aa4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,14 +14,14 @@ build-job: # This job runs in the build stage, which runs first. - pip install -r requirements_dev.txt - pip install . -# unit-test-job: # This job runs in the test stage. -# stage: test # It only starts when the job in the build stage completes successfully. -# script: -# - pip install -r requirements.txt -# - pip install -r requirements_dev.txt -# - pip install . -# - coverage run --source primingsitepredictor -m pytest -# - coverage report -m +unit-test-job: # This job runs in the test stage. + stage: test # It only starts when the job in the build stage completes successfully. + script: + - pip install -r requirements.txt + - pip install -r requirements_dev.txt + - pip install . + - coverage run --source primingsitepredictor -m pytest + - coverage report -m lint-test-job: # This job also runs in the test stage. stage: test # It can run at the same time as unit-test-job (in parallel). @@ -29,6 +29,6 @@ lint-test-job: # This job also runs in the test stage. - pip install -r requirements.txt - pip install -r requirements_dev.txt - pip install . - - flake8 --docstring-convention google primingsitepredictor/ # tests/ - - pylint primingsitepredictor/ # tests/ + - flake8 --docstring-convention google primingsitepredictor/ + - pylint primingsitepredictor/ - mypy primingsitepredictor/ diff --git a/README.md b/README.md index c9f0307bdaa5dde8f5140f8f5f6a2487e9b6d7a3..484ae490a8bb65bd910e683ead36d9c56f6e0821 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,7 @@ Priming Site Predictor which uses a seed-and-extension algorithm (*RIblast*: htt ## Version Version 0.1.0 (2022/11/15) -## Acknowledgements -We used the RIblast algorithm created by Tsukasa Fukunaga (https://github.com/fukunagatsu). - -## Installation from github +## Installation from GitLab Priming Site Predictor requires Python 3.9 or later. Install Priming Site Predictor from GitLab using: @@ -17,7 +14,11 @@ Install Priming Site Predictor from GitLab using: ``` git clone https://git.scicore.unibas.ch/zavolan_group/tools/priming-site-predictor.git cd priming-site-predictor -pip install . +``` +Create scRNA-seq-simulation conda environment: +``` +conda env create --file environment.yml +conda activate scrna-seq-sim ``` ## Usage @@ -54,5 +55,8 @@ This software is released under the MIT License, see LICENSE.txt. ## Contributors Max Bär, Sophie Schnider, Robin Christen (University of Basel) +## Acknowledgements +We used the RIblast algorithm created by Tsukasa Fukunaga (https://github.com/fukunagatsu). + ## Reference Tsukasa Fukunaga and Michiaki Hamada. "RIblast: An ultrafast RNA-RNA interaction prediction system based on a seed-and-extension approach." btx287, Bioinformatics (2017) diff --git a/primingsitepredictor/__init__.py b/primingsitepredictor/__init__.py index 66eb5ba958a8629efed8cbd53b8801505a0c219f..bb7d5f3186cb2e516714c06c9a5e8d2b57696586 100644 --- a/primingsitepredictor/__init__.py +++ b/primingsitepredictor/__init__.py @@ -1 +1 @@ -"""Initialize package.""" +"""Initialise package.""" diff --git a/primingsitepredictor/cli.py b/primingsitepredictor/cli.py index 3f5c9f791454ba8e0b82d5363460bfe24f92b67a..530cab497ca0b519780cc7aa536cb00dcafa623d 100644 --- a/primingsitepredictor/cli.py +++ b/primingsitepredictor/cli.py @@ -1,8 +1,8 @@ """Receive command line arguments.""" import argparse import logging -from primingsitepredictor.prime_site_predictor import CreatePrimer -from primingsitepredictor.prime_site_predictor import PrimingSitePredictor +from primingsitepredictor.psp import CreatePrimer +from primingsitepredictor.psp import PrimingSitePredictor LOG = logging.getLogger(__name__) @@ -34,8 +34,8 @@ def setup_logging() -> None: verbosity: Level of logging verbosity. """ logging.basicConfig( - format='[%(asctime)s: %(levelname)s] %(message)s \ - (module "%(module)s")', + format='[%(asctime)s: %(levelname)s] %(message)s ' + '(module "%(module)s")', level=logging.INFO, ) @@ -72,44 +72,3 @@ def parse_args(): if __name__ == "__main__": main() - - -# def main(): -# """Execute generate_RIBlast.""" -# generate_riblast_input() - - -# def generate_riblast_input(): -# """Create a list of the filenames for the RIBlast.""" -# my_primer = CreatePrimer() -# my_primer.create_fasta() -# primer_filename = my_primer.name + ".fasta" -# transcripts_filename = "transcripts.fasta" - -# return [primer_filename, transcripts_filename] - - -# def create_gtf(): -# """Create gtf.""" -# gtf_file = PostProcessRIBlast().output -# print(gtf_file) - - -# def create_parser(): -# """Create a parser.""" -# parser = argparse.ArgumentParser( -# prog='PrimingSitePredictor', -# description='Takes a cutoff energy and the predicts \ -# location of priming sites of transcripts', -# epilog='To predict or not to predict') -# parser.add_argument('--float', type=float, required=True, -# help='An energy-cutoff float number') -# parsed_args = parser.parse_args() -# energy_cutoff = parsed_args.float -# return energy_cutoff - - -# def letsgo(): -# """Create a parser and print the energycutoff.""" -# energy_cutoff = create_parser() -# print(f"Your energy cutoff is {energy_cutoff}") diff --git a/primingsitepredictor/prime_site_predictor.py b/primingsitepredictor/psp.py similarity index 85% rename from primingsitepredictor/prime_site_predictor.py rename to primingsitepredictor/psp.py index bbc3cd31e1ca2ab7eeb7e05e23beb94658b7acc0..9c92cd18ef928d79ffdf2314183cae536c66dffa 100644 --- a/primingsitepredictor/prime_site_predictor.py +++ b/primingsitepredictor/psp.py @@ -1,6 +1,7 @@ """Main module for Priming Site Predictor.""" import math import logging +import re import pandas as pd # type: ignore LOG = logging.getLogger(__name__) @@ -8,10 +9,10 @@ LOG = logging.getLogger(__name__) # pylint: disable=R0903 class CreatePrimer: - """Create an instance of a primer of a desired length \ - and primer name which can be saved as a fasta file. + """Create an instance of a primer of a desired length and name. - By default the length is 15 and name is primer. + The primer is saved as a fasta file. + By default, the length is 15 and the name is "primer". """ def __init__(self, name='primer', primerlength=15): @@ -60,10 +61,12 @@ class PrimingSitePredictor: raw_interactions = file.readlines()[firstline:] number_entries = len(raw_interactions) - for i in range(0, number_entries-1): - current_interaction = raw_interactions[i].strip( - ' \n').replace('(', '').replace(')', '').replace( - '-', ',').replace(':', ',').split(',') + for i in range(0, number_entries): + pattern = r'(?<=[0-9])-(?=[0-9])' + current_interaction = re.sub(pattern, ',', raw_interactions[i]) + current_interaction = current_interaction.strip( + ' \n').replace('(', '').replace( + ')', '').replace(':', ',').split(',') interaction_list.append(current_interaction) return interaction_list diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0cf13d23e76271f02a81a28e53018db6ed382f49 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Initialise testing.""" diff --git a/tests/test_cli.py b/tests/test_cli.py index 94676a8ba6d32fe5a501c921fc0b80f45730377c..624f47493939f9396c84dc405ced4beca25c7f52 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,6 +2,7 @@ import unittest from unittest.mock import patch from primingsitepredictor import cli +from primingsitepredictor.cli import setup_logging class TestCli(unittest.TestCase): @@ -75,3 +76,11 @@ class TestCli(unittest.TestCase): ]): with self.assertRaises(SystemExit): cli.parse_args() + + +class TestSetupLogging: + """Test ``setup_logging()`` function.""" + + def test_log_level_default(self): + """Call without args.""" + setup_logging() diff --git a/tests/test_createprimer.py b/tests/test_createprimer.py deleted file mode 100644 index 94e15043299e883f9858ee7e3f30c78883eddc91..0000000000000000000000000000000000000000 --- a/tests/test_createprimer.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Tests for createprimer.py.""" -import unittest -from unittest.mock import patch, mock_open -import pytest -from primingsitepredictor.prime_site_predictor import CreatePrimer - - -class TestCreatePrimer(unittest.TestCase): - """Test CreatePrimer function.""" - - def test_init(self): - """Test the __init__() default values.""" - primer = CreatePrimer() - self.assertEqual(primer.name, 'primer') - self.assertEqual(primer.primer_length, 15) - self.assertEqual(primer.primer_sequence, 'T'*15) - self.assertEqual(primer.lines, ['<primer', 'TTTTTTTTTTTTTTT']) - with pytest.raises(TypeError): - CreatePrimer(type(int), type(str)) - - def test_init_custom(self): - """Test the __init__() custom values.""" - primer = CreatePrimer('my_primer', 20) - self.assertEqual(primer.name, 'my_primer') - self.assertEqual(primer.primer_length, 20) - self.assertEqual(primer.primer_sequence, 'T'*20) - self.assertEqual(primer.lines, ['<my_primer', 'TTTTTTTTTTTTTTTTTTTT']) - - def test_create_fasta(self): - """Test the test_create_fasta default values.""" - with patch('builtins.open', new_callable=mock_open) as mock_file: - primer = CreatePrimer() - primer.create_fasta() - - # assert if opened file on write mode 'w' - mock_file.assert_called_once_with( - 'primer.fasta', 'w', encoding='utf-8' - ) - # assert if the specific content was written in file - expected_content = '<primer\nTTTTTTTTTTTTTTT' - mock_file().write.assert_called_once_with(expected_content) - - def test_create_fasta_custom(self): - """Test the test_create_fasta custom values.""" - with patch('builtins.open', new_callable=mock_open) as mock_file: - primer = CreatePrimer('my_primer', 20) - primer.create_fasta() - - # assert if opened file on write mode 'w' - mock_file.assert_called_once_with( - 'my_primer.fasta', 'w', encoding='utf-8' - ) - # assert if the specific content was written in file - expected_content = '<my_primer\nTTTTTTTTTTTTTTTTTTTT' - mock_file().write.assert_called_once_with(expected_content) diff --git a/tests/test_main.py b/tests/test_main.py deleted file mode 100644 index d57812ddb70b516bd672b74e486ad1cb176d3f92..0000000000000000000000000000000000000000 --- a/tests/test_main.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -Created on Tue Dec 20 14:06:20 2022 - -@author: RobinC -""" - -# Imports -import os -import sys -currentdir = os.path.dirname(os.path.realpath(__file__)) -parentdir = os.path.dirname(currentdir) -sys.path.append(parentdir) -import unittest -import main as mn -import createprimer as cp -import postprocessing as pp -from io import StringIO - -class TestMain(unittest.TestCase): - - def test_import(self): - """Test function for module imports""" - try: - import CreatePrimer - except ImportError: - self.fail("Failed to import CreatePrimer") - - try: - import PostProcessRIBlast - except ImportError: - self.fail("Failed to import PostProcessRIBlast") - - def test_generate_riblast_input(self): - """Test funciton for the method generate_riblast_input()""" - - # Call the CreatePrimer() method and the create_fasta() method - primer = cp.CreatePrimer() - primer.create_fasta() - - # get the name and the transcript filename - primer_filename = primer.name+".fasta" - transcript_filename = "transcripts.fasta" - - # Call the generate_riblast_input() method - result = mn.generate_riblast_input() - - assert result == [primer_filename, transcript_filename] - - - def test_create_gtf(self): - """Test funciton for the method create_gtf()""" - - # Call the output of the PostprocessRIBlast() method - result = pp.PostProcessRIBlast().output - - # Open the expected output - with open('output_transcripts_df.txt', 'r') as f: - expected_output = f.read() - - # Use an assertion to check the output result - assert result == expected_output - - # Capture the output of the print statement - captured_output = StringIO() - sys.stdout = captured_output - - # Call the function that contains the print statement - mn.create_gtf() - - # Check the captured output against the expected value - assert captured_output.getvalue().strip() == expected_output - - - def test_main(self): - """Test function for the method main()""" - - # Call the CreatePrimer() method and the create_fasta() method - primer = cp.CreatePrimer() - primer.create_fasta() - - # get the name and the transcript filename - primer_filename = primer.name+".fasta" - transcripts_filename = "transcripts.fasta" - - # Get the results for the called methods - result1 = mn.generate_riblast_input() - result2 = mn.create_gtf() - - # Open the expected output - with open('output_transcripts_df.txt', 'r') as f: - expected_output = f.read() - - assert result1 == [primer_filename, transcripts_filename] - assert result2 == expected_output diff --git a/tests/test_postprocessing.py b/tests/test_postprocessing.py deleted file mode 100644 index 0132906fe1f4cf2e7efd59376be904e3f68129b7..0000000000000000000000000000000000000000 --- a/tests/test_postprocessing.py +++ /dev/null @@ -1,213 +0,0 @@ - -import unittest -from unittest import mock -import math -from primingsitepredictor.prime_site_predictor import PrimingSitePredictor - - -class TestPrimingSitePredictor(unittest.TestCase): - """Test PrimingSitePredictor.""" - - def setUp(self): - """Create an instance of PrimingSitePredictor.""" - # You need to provide the required parameters to the constructor - self.post_processor = PrimingSitePredictor( - fasta_file="test.fasta", - primer_sequence="T" * 15, - energy_cutoff=0.5, - riblast_output="test_files/RIBlast_output_example.txt", - output_filename="test_output.gtf" - ) - - def test_generate_gtf(self): - """Test the generate_gtf() method.""" - # Create a mock DataFrame to be returned by create_pandas_df() - mock_df = mock.MagicMock() - mock_df.index = [0] - mock_df[3][0] = 'Transcript_1' - mock_df[13][0] = '(0-14:2988-2974)' - mock_df[12][0] = '+' - mock_df["Normalised_interaction_energy"][0] = 0.75 - - with mock.patch.object( - self.post_processor, "create_pandas_df", return_value=mock_df - ): - expected_output = ( - 'Transcript_1\tRIBlast\tPriming_site\t(0-14:2988-2974)\t+\t.\t+\t.\tInteraction_Energy\t0.75\n' - ) - print(self.post_processor.generate_gtf()) - print(expected_output) - # self.assertEqual(self.post_processor.generate_gtf(), expected_output) - - def test_calculate_energy(self): - """Test the calculate_energy() method.""" - test_instance = self.post_processor - decimal_place = 20 - - # Test for a positive value - self.assertAlmostEqual( - test_instance.calculate_energy(9.76), - math.exp(-9.76 * 6.9477 * 10 ** -21 / (1.380649e-23 * 298)), - decimal_place - ) - - # Test for a negative value - self.assertAlmostEqual( - test_instance.calculate_energy(-5.0), - math.exp(5.0 * 6.9477 * 10 ** -21 / (1.380649e-23 * 298)), - decimal_place - ) - - # Test for zero - self.assertAlmostEqual( - test_instance.calculate_energy(0), - math.exp(0 * 6.9477 * 10 ** -21 / (1.380649e-23 * 298)), - decimal_place - ) - - def test_create_list_from_output(self): - """Test the create_list_from_output() method.""" - # Mocking the file readlines method - mock_readlines = mock.MagicMock() - mock_readlines.return_value = [ - "header1\n", - "header2\n", - "header3\n", - "0,Test_Primer,15,Transcript_1,3233,1.49191,-9.76,-8.26809,(0-14:2988-2974)\n", - "1,Test_Primer,15,Transcript_1,3233,1.02308,-9.76,-8.73692,(0-14:18-4)\n", - ] - - with mock.patch("builtins.open", mock.mock_open(read_data="")) as m, \ - mock.patch.object( - m.return_value, "readlines", mock_readlines - ): - expected_output = [ - ['0', 'Test_Primer', '15', 'Transcript_1', '3233', '1.49191', '-9.76', '-8.26809', '(0-14:2988-2974)'], - ['1', 'Test_Primer', '15', 'Transcript_1', '3233', '1.02308', '-9.76', '-8.73692', '(0-14:18-4)'] - ] - self.assertEqual(self.post_processor.create_list_from_output(), expected_output) - - -if __name__ == "__main__": - unittest.main() - -# class TestPrimingSitePredictor(unittest.TestCase): -# """Test PrimingSitePredictor.""" - -# def setUp(self): -# """Setup function to create an instance of PrimingSitePredictor.""" -# self.post_processor = PrimingSitePredictor() - -# def test_init(self): -# """Test the __init__() method.""" -# # Test if generate_gtf method is being called -# with unittest.mock.patch.object( -# PrimingSitePredictor, "generate_gtf" -# ) as mock_generate_gtf: -# PrimingSitePredictor() -# mock_generate_gtf.assert_called_once() - -# # Test if generate_gtf returns the expected output -# expected_output = ( -# 'Transcript_1\tRIBlast\tPriming_site\t2974[3257 chars]3"\n' -# ) -# self.assertEqual(self.post_processor.generate_gtf(), expected_output) - -# def test_calculate_energy(self): -# """Test the calculate_energy() method.""" - -# def calculate_energy(value): -# energy_constant = 1.380649*10**(-23)*298 -# kcalmol_joul = 6.9477*10**-21 -# return (math.exp(-float(value)*kcalmol_joul/energy_constant)) - -# # set a decimal place -# decimalPlace = 20 - -# # Test for a positive value -# self.testinstance = pp.PostProcessRIBlast.calculate_energy(self, 5.0) -# expected_output = calculate_energy(5.0) -# self.assertAlmostEqual(self.testinstance, expected_output, decimalPlace) - - -# # Test for a negative value -# self.testinstance = pp.PostProcessRIBlast.calculate_energy(self, -5.0) -# expected_output = calculate_energy(-5.0) -# self.assertAlmostEqual(self.testinstance, expected_output, decimalPlace) - -# # Test for zero -# self.testinstance = pp.PostProcessRIBlast.calculate_energy(self, 0) -# expected_output = calculate_energy(0) -# self.assertAlmostEqual(self.testinstance, expected_output, decimalPlace) - -# def test_create_list_from_output(self): -# """Test the create_list_from_output() method.""" -# # create an instance test of the class PostProcessRIBlast() -# test = pp.PostProcessRIBlast() -# test.file = "RIBlast output example.txt" - -# with open('RIBlast output example.txt', 'r', encoding="utf-8") as file: -# data_list = file.readlines() - -# # Remove the header -# data_list = data_list[3:] - -# # Convert each row of the list into a list of values -# expected_output = [] -# for row in data_list: -# # Split the row by the comma character -# values = row.strip().split(',') -# # Append the list of values to the final list -# expected_output.append(values) -# return expected_output -# self.assertEqual(test.create_list_from_output(), expected_output) - -# def test_create_pandas_df(self): -# """Test the create_pandas_df() method with mock data.""" -# # create a test instance -# test_instance = pp.PostProcessRIBlast() - -# # create some mock data for the input list -# test_instance.interaction_list = [ -# ['Id', 'Query name', 'Query Length', 'Target name', 'Target Length', 'Accessibility Energy', 'Hybridization Energy', 'Interaction Energy', 'BasePair'], -# [0, 'Test_Primer', 15, 'Transcript_1', 3233, 1.49191, -9.76, -8.26809, '(0-14:2988-2974)'], -# [1, 'Test_Primer', 15, 'Transcript_1', 3233, 1.02308, -9.76, -8.73692, '(0-14:18-4)'], -# [2, 'Test_Primer', 15, 'Transcript_1', 3233, 0.947439, -9.73, -8.78256, '(0-14:17-3)'], -# [3, 'Test_Primer', 15, 'Transcript_1', 3233, 0.793049, -9.73, -8.93695, '(0-14:16-2)'], -# [4, 'Test_Primer', 15, 'Transcript_1', 3233, 0.483869, -9.73, -9.24613, '(0-14:15-1)'], -# [5, 'Test_Primer', 15, 'Transcript_1', 3233, 0.441093, -9.17, -8.72891, '(0-14:14-0)']] - -# # create the DataFrame using the mock data -# df = test_instance.create_pandas_df() - -# #self.assertEqual(len(df),7) - -# # check that the DataFrame has the expected column names -# #self.assertEqual(list(df.columns), ['Id','Query name', 'Query Length', 'Target name', 'Target Length', 'Accessibility Energy', 'Hybridization Energy', 'Interaction Energy', 'BasePair']) - -# # check that the values in the 'Id' column are correct -# self.assertEqual(list(df['Id']), ['Id',0,1,2,3,4,5]) - -# # check that the values in the 'Query name' column are correct -# self.assertEqual(list(df['Query name']), ['Query name','Test_Primer','Test_Primer','Test_Primer','Test_Primer','Test_Primer','Test_Primer']) - -# # check that the values in the 'Query Length' column are correct -# self.assertEqual(list(df['Query Length']), ['Query Length',15, 15, 15, 15, 15, 15]) - -# # check that the values in the 'Target name' column are correct -# self.assertEqual(list(df['Target name']), ['Target name','Transcript_1','Transcript_1','Transcript_1','Transcript_1','Transcript_1','Transcript_1']) - -# # check that the values in the 'Target Length' column are correct -# self.assertEqual(list(df['Target Length']), ['Target Length', 3233, 3233, 3233, 3233, 3233, 3233]) - -# # check that the values in the 'Accessibility Energy' column are correct -# self.assertEqual(list(df['Accessibility Energy']), ['Accessibility Energy',1.49191, 1.02308, 0.947439, 0.793049, 0.483869, 0.441093]) - -# # check that the values in the 'Hybridization Energy' column are correct -# self.assertEqual(list(df['Hybridization Energy']), ['Hybridization Energy',-9.76, -9.76, -9.73, -9.73, -9.73, -9.17]) - -# # check that the values in the 'Interaction Energy' column are correct -# self.assertEqual(list(df['Interaction Energy']), ['Interaction Energy',-8.26809, -8.73692, -8.78256, -8.93695, -9.24613, -8.72891]) - -# # check that the values in the 'BasePair' column are correct -# self.assertEqual(list(df['BasePair']), ['BasePair','(0-14:2988-2974)', '(0-14:18-4)', '(0-14:17-3)', '(0-14:16-2)', '(0-14:15-1)', '(0-14:14-0)']) diff --git a/tests/test_psp.py b/tests/test_psp.py new file mode 100644 index 0000000000000000000000000000000000000000..988a2e2f5bf4b99c0012aad7365aed3d4657865a --- /dev/null +++ b/tests/test_psp.py @@ -0,0 +1,166 @@ +"""Tests for createprimer.py.""" +import unittest +from unittest.mock import patch, mock_open +from unittest import mock +import math +import pandas as pd # type: ignore +import pytest +from primingsitepredictor.psp import CreatePrimer +from primingsitepredictor.psp import PrimingSitePredictor + + +class TestCreatePrimer(unittest.TestCase): + """Test CreatePrimer function.""" + + def test_init(self): + """Test the __init__() default values.""" + primer = CreatePrimer() + self.assertEqual(primer.name, 'primer') + self.assertEqual(primer.primer_length, 15) + self.assertEqual(primer.primer_sequence, 'T'*15) + self.assertEqual(primer.lines, ['<primer', 'TTTTTTTTTTTTTTT']) + with pytest.raises(TypeError): + CreatePrimer(type(int), type(str)) + + def test_init_custom(self): + """Test the __init__() custom values.""" + primer = CreatePrimer('my_primer', 20) + self.assertEqual(primer.name, 'my_primer') + self.assertEqual(primer.primer_length, 20) + self.assertEqual(primer.primer_sequence, 'T'*20) + self.assertEqual(primer.lines, ['<my_primer', 'TTTTTTTTTTTTTTTTTTTT']) + + def test_create_fasta(self): + """Test the test_create_fasta default values.""" + with patch('builtins.open', new_callable=mock_open) as mock_file: + primer = CreatePrimer() + primer.create_fasta() + + # assert if opened file on write mode 'w' + mock_file.assert_called_once_with( + 'primer.fasta', 'w', encoding='utf-8' + ) + # assert if the specific content was written in file + expected_content = '<primer\nTTTTTTTTTTTTTTT' + mock_file().write.assert_called_once_with(expected_content) + + def test_create_fasta_custom(self): + """Test the test_create_fasta custom values.""" + with patch('builtins.open', new_callable=mock_open) as mock_file: + primer = CreatePrimer('my_primer', 20) + primer.create_fasta() + + # assert if opened file on write mode 'w' + mock_file.assert_called_once_with( + 'my_primer.fasta', 'w', encoding='utf-8' + ) + # assert if the specific content was written in file + expected_content = '<my_primer\nTTTTTTTTTTTTTTTTTTTT' + mock_file().write.assert_called_once_with(expected_content) + + +class TestPrimingSitePredictor(unittest.TestCase): + """Test PrimingSitePredictor.""" + + def setUp(self): + """Create an instance of PrimingSitePredictor.""" + # You need to provide the required parameters to the constructor + self.post_processor = PrimingSitePredictor( + fasta_file="test.fasta", + primer_sequence="T" * 15, + energy_cutoff=0.5, + riblast_output="test_files/RIBlast_output_example.txt", + output_filename="test_output.gtf" + ) + + def test_generate_gtf(self): + """Test the generate_gtf() method.""" + # Create a mock DataFrame to be returned by create_pandas_df() + mock_df = mock.MagicMock() + mock_df.index = [0] + mock_df[3][0] = 'Transcript_1' + mock_df[13][0] = '(0-14:2988-2974)' + mock_df[12][0] = '+' + mock_df["Normalised_interaction_energy"][0] = 0.75 + + with mock.patch.object( + self.post_processor, "create_pandas_df", return_value=mock_df + ): + expected_output = ( + 'Transcript_1\tRIBlast\tPriming_site\t(0-14:2988-2974)\t' + '+\t.\t+\t.\tInteraction_Energy\t0.75\n' + ) + print(self.post_processor.generate_gtf()) + print(expected_output) + # self.assertEqual(self.post_processor.generate_gtf(), + # expected_output) + + def test_calculate_energy(self): + """Test the calculate_energy() method.""" + test_instance = self.post_processor + decimal_place = 20 + + # Test for a positive value + self.assertAlmostEqual( + test_instance.calculate_energy(9.76), + math.exp(-9.76 * 6.9477 * 10 ** -21 / (1.380649e-23 * 298)), + decimal_place + ) + + # Test for a negative value + self.assertAlmostEqual( + test_instance.calculate_energy(-5.0), + math.exp(5.0 * 6.9477 * 10 ** -21 / (1.380649e-23 * 298)), + decimal_place + ) + + # Test for zero + self.assertAlmostEqual( + test_instance.calculate_energy(0), + math.exp(0 * 6.9477 * 10 ** -21 / (1.380649e-23 * 298)), + decimal_place + ) + + def test_create_list_from_output(self): + """Test the create_list_from_output() method.""" + # Mocking the file readlines method + mock_readlines = mock.MagicMock() + mock_readlines.return_value = [ + "header1\n", + "header2\n", + "header3\n", + "0,Test_Primer,15,Transcript_1,3233," + "1.49191,-9.76,-8.26809,(0-14:2988-2974)\n", + "1,Test_Primer,15,Transcript_1,3233," + "1.02308,-9.76,-8.73692,(0-14:18-4)\n", + ] + + with mock.patch("builtins.open", mock.mock_open(read_data="")) as m, \ + mock.patch.object( + m.return_value, "readlines", mock_readlines + ): + expected_output = [ + ['0', 'Test_Primer', '15', 'Transcript_1', '3233', + '1.49191', '-9.76', '-8.26809', '0', '14', '2988', '2974'], + ['1', 'Test_Primer', '15', 'Transcript_1', '3233', + '1.02308', '-9.76', '-8.73692', '0', '14', '18', '4'] + ] + self.assertEqual( + self.post_processor.create_list_from_output(), expected_output + ) + + def test_create_pandas_df(self): + """Test the create_pandas_df() method.""" + # Mock the create_list_from_output method + with mock.patch.object( + self.post_processor, 'create_list_from_output', return_value=[ + ['0', 'Test_Primer', '15', 'Transcript_1', '3233', + '1.49191', '-9.76', '-8.26809', '0', '14', '2988', '2974'], + ['1', 'Test_Primer', '15', 'Transcript_1', '3233', + '1.02308', '-9.76', '-8.73692', '0', '14', '18', '4'] + ]): + result = self.post_processor.create_pandas_df() + + # Perform your assertions on the result DataFrame + self.assertIsInstance(result, pd.DataFrame) + self.assertEqual(len(result), 2)