diff --git a/README.md b/README.md
index f993596..460b857 100644
--- a/README.md
+++ b/README.md
@@ -1,163 +1,186 @@
+# BioProg_SV
-# BIOPROG_SV
+**BioProg_SV** (Biological Program by Stepnova Valeriya) is a toolkit designed for performing basic DNA/RNA sequence analysis and filtering FASTQ data based on quality metrics. The toolset includes functions for sequence transformation, filtering, and analysis.
+---
-**BioProg_SV** (Biological Programm by Stepnova Valeriya) is a toolkit designed to calculate basic DNA/RNA analysis and validate DNA quality from FastQC data.
-
-Authors:
-**Stepanova Valeria - Bioinformatics institute 2024/2025**
-
+## Authors
-**BioProg_SV** documentation is available on the GitHub [repo](https://github.com/Stepanovalera/BioProgSV).
+- **Stepanova Valeriya**
+ Bioinformatics Institute, 2024–2025
+Documentation is available in the GitHub repository: [Repository Link](https://github.com/your-repo-link).
-## Content
+---
+## Table of Contents
-* [Instructions](#instructions)
-* [Example input](#examples)
-* [Contact](#contact)
+1. [Instructions](#instructions)
+2. [Function Descriptions](#function-descriptions)
+3. [Usage Examples](#usage-examples)
+4. [Contact](#contact)
+---
## Instructions
-### Function Descriptions
-This program contains main functions:
-#### `filter_fastqc()`
-This function processes FastQC output data to filter and validate DNA sequences based on quality metrics. It applies multiple quality criteria to determine which sequences from a given dictionary (seqs) pass the filtering steps. The filtering criteria include:
+### Installing Dependencies
-**Description:**
-Filters sequences in a FASTQ file based on GC content, length, and quality threshold.
+If the project uses third-party libraries, install them using the `requirements.txt` file:
-**Parameters:**
+```bash
+pip install -r requirements.txt
+```
-- `input_fastq` (str): Path to the input FASTQ file.
-
-- `output_fastq` (str): Name of output FASTQ file.
-
-- `gc_bounds` (tuple or int): GC content bounds for filtering.
+If the file is empty, it means the project only uses Python's standard libraries.
-- `length_bounds` (tuple or int): Length bounds for filtering.
+### Running the Program
-- `quality_threshold`(float): Minimum average quality score for filtering.
+The program supports two main modes of operation:
- *GC Content Bounds*: Only sequences within the specified percentage range are retained. The GC content influences sequence stability and should fall within acceptable bounds for specific analyses. Default paramenter: `gc_bounds = (0, 100)` Input can contain a single number, which will be interpreted as the upper bound.
+1. **DNA/RNA Sequence Analysis**:
+ ```bash
+ python main.py dna_rna_tools --seqs "ATG" --action transcribe
+ ```
- *Length Bounds*: The function checks if sequence lengths are within the specified minimum and maximum values, ensuring that only sequences of appropriate length are included. Default paramenter: `length_bounds = (0, 2**32)` Input can contain a single number, which will be interpreted as the upper bound.
+2. **FASTQ File Filtering**:
+ ```bash
+ python main.py filter_fastq input.fastq output.fastq --gc_bounds 50 60 --length_bounds 50 100 --quality_threshold 30
+ ```
- *Quality Threshold*: Sequences must exceed a minimum quality score, ensuring that only high-quality data are used for downstream analyses. Default paramenter: `quality_threshold = 0`
+---
-**Returns:**
+## Function Descriptions
-output FASTA file
+### 1. `filter_fastq`
+This function filters sequences from a FASTQ file based on specified quality criteria.
+#### Parameters:
+- `input_fastq` (str): Path to the input FASTQ file.
+- `output_fastq` (str): Path to the output FASTQ file.
+- `gc_bounds` (tuple or int): GC content bounds (default: `(0, 100)`). If a single number is provided, it is treated as the upper bound.
+- `length_bounds` (tuple or int): Sequence length bounds (default: `(0, 2**32)`). If a single number is provided, it is treated as the upper bound.
+- `quality_threshold` (float): Minimum average quality score required for sequences to be included (default: `0.0`).
- in-build function `fast_qc` - The function calculates:
+#### Returns:
+- A filtered FASTQ file containing sequences that meet the specified criteria.
+---
- - GC Content: Percentage of guanine (G) and cytosine (C) in the DNA sequence.
+### 2. `fast_qc`
+This function calculates quality metrics for DNA/RNA sequences.
- - Sequence Length: The number of bases in the DNA sequence.
+#### Metrics Calculated:
+- **GC Content**: Percentage of guanine (G) and cytosine (C) in the sequence.
+- **Sequence Length**: Number of bases in the sequence.
+- **Average Quality Score**: Mean quality score derived from Phred quality scores.
+#### Parameters:
+- `seqs` (dict): A dictionary where keys are sequence names, and values are tuples containing the sequence (str) and its quality string (str).
- - Average Quality Score: An average quality derived from Phred quality scores
+#### Returns:
+- A dictionary with sequence names as keys and tuples as values. Each tuple contains:
+ - GC content (float)
+ - Sequence length (int)
+ - Average quality score (float)
+---
-#### `convert_multiline_fasta_to_oneline(input_fasta, output_fasta)`
+### 3. `convert_multiline_fasta_to_oneline`
-**Description:**
-This function processes a FASTA file, converting sequences that are split over multiple lines into a single line format. Each sequence starts with a header line beginning with `>`. The function ensures that every sequence is represented as a single line in the output file.
+Converts a multi-line FASTA file into a single-line format.
-**Parameters:**
-- `input_fasta` (str): The path to the input FASTA file, where sequences may be spread across multiple lines.
-- `output_fasta` (str): The reformatted FASTA file name.
+#### Parameters:
+- `input_fasta` (str): Path to the input FASTA file.
+- `output_fasta` (str): Path to the output FASTA file.
-**Returns:**
-- (str): output FASTA file where sequences are converted to a one-line format.
+#### Returns:
+- A FASTA file where each sequence is represented as a single line.
---
-#### `parse_blast_output(input_blast, output_blast)`
+### 4. `parse_blast_output`
-**Description:**
-This function parses the output of a BLAST search, extracting relevant sequence descriptions. It starts recording after encountering the 'Description' line and continues until it reaches the first empty line. The function then saves a truncated version of each description line, containing only the portion before the ellipsis (`...`).
+Parses BLAST output files to extract relevant sequence descriptions.
-**Parameters:**
-- `input_blast` (str): The path to the input BLAST output file, which contains the results to be parsed.
-- `output_blast` (str): The file name where the parsed BLAST output will be saved.
+#### Parameters:
+- `input_blast` (str): Path to the input BLAST output file.
+- `output_blast` (str): Path to the output file for parsed results.
-**Returns:**
-- (str): The path to the output file containing the parsed BLAST results.
+#### Returns:
+- A file containing truncated descriptions of sequences.
---
-#### `select_genes_from_gbk_to_fasta(input_gbk, output_fasta, genes, n_before=1, n_after=1)`
+### 5. `select_genes_from_gbk_to_fasta`
-**Description:**
-This function extracts protein-coding sequences from a GenBank (GBK) file and writes neighboring genes' sequences to a FASTA file, excluding the target genes themselves. It identifies genes of interest and writes their adjacent genes' sequences, defined by `n_before` and `n_after`, to the output file.
+Extracts neighboring gene sequences from a GenBank (GBK) file.
-**Parameters:**
-- `input_gbk` (str): The path to the input GenBank file, containing the sequence data to be processed.
-- `output_fasta` (str): FASTA file name where selected genes' sequences will be saved.
-- `genes` (any): A list of target gene names adjacent to which the sequences will be extracted.
-- `n_before` (int, optional): The number of genes before each target gene to include in the output. Defaults to 1.
-- `n_after` (int, optional): The number of genes after each target gene to include in the output. Defaults to 1.
+#### Parameters:
+- `input_gbk` (str): Path to the input GenBank file.
+- `output_fasta` (str): Path to the output FASTA file.
+- `genes` (list): List of target gene names.
+- `n_before` (int, optional): Number of genes before the target gene to include (default: `1`).
+- `n_after` (int, optional): Number of genes after the target gene to include (default: `1`).
-**Returns:**
-- (str): the output FASTA file containing sequences of neighboring genes.
+#### Returns:
+- A FASTA file containing sequences of neighboring genes.
+---
+### 6. `run_dna_rna_tools`
-#### `run_dna_rna_tools() `
+Performs various DNA/RNA operations, including transcription, reverse complementation, and more.
-This function perform multiple DNA/RNA operations, providing a streamlined interface to process sequences efficiently.The function is case-insensitive.
+#### Supported Actions:
+- `transcribe`: Converts DNA to RNA.
+- `reverse`: Reverses a sequence.
+- `complement`: Generates the complement of a DNA sequence.
+- `reverse_complement`: Generates the reverse complement of a DNA sequence.
- `is_none()` - Checks if the given sequence is empty or None. This utility function ensures that subsequent operations are performed only on valid sequences, preventing errors in data processing.
-
- `is_DNA()` - Validates whether the provided sequence is a DNA sequence, checking for the presence of valid DNA nucleotides (A, T, C, G).
-
- `is_RNA()` - Determines if the sequence is an RNA sequence by verifying the presence of valid RNA nucleotides (A, U, C, G).
+#### Parameters:
+- `seqs` (list): List of DNA/RNA sequences.
+- `action` (str): Action to perform (e.g., `"transcribe"`, `"reverse"`).
- `reverse()` - Reverses a given nucleotide sequence.
-
+#### Returns:
+- A single sequence (if one input) or a list of processed sequences.
- `reverse_complement()` - Generates the reverse complement of a DNA sequence.
-
+---
- `transcribe()` - Converts a DNA sequence into an RNA sequence by replacing thymine with uracil.
-
-
- `complement()` - Produces the complement of a DNA strand by replacing each nucleotide with its complementary base.
-
+## Usage Examples
-Each sequence is processed, and results are collected into a list. If there is only one sequence to process, the function directly returns the single result; otherwise, it returns a `list()`.
+### Example 1: DNA/RNA Tools
-## Examples
+```python
+# Transcribe DNA to RNA
+run_dna_rna_tools(["ATG"], "transcribe") # Output: ["AUG"]
-For `run_dna_rna_tools()`:
+# Reverse a sequence
+run_dna_rna_tools(["AGT"], "reverse") # Output: ["TGA"]
+# Generate reverse complement
+run_dna_rna_tools(["GTGT"], "reverse_complement") # Output: ["ACAC"]
+```
-~~~
- run_dna_rna_tools("ATG", "transcribe") == "AUG"
- run_dna_rna_tools("AGt", "ATTCC" "reverse") == "AGu"
- run_dna_rna_tools("GTGT", "TGU" "complement") == "AGu"
-~~~
+### Example 2: FASTQ Filtering
-For `bioprogsv()`:
+```python
+# Filter sequences based on length
+filter_fastq("input.fastq", "output.fastq", length_bounds=(50, 100))
+# Filter sequences based on GC content and quality
+filter_fastq("input.fastq", "output.fastq", gc_bounds=(50, 60), quality_threshold=30)
+```
-~~~
-EXAMPLE_FASTQ = {
- # 'name' : ('sequence', 'quality')
- '@SRX079801': ('ACAGCAACATAAACATGATGGGATGGCGTAAGCCCCCGAGATATCAGTTTACCCAGGATAAGAGATTAAATTATGAGCAACATTATTAA', 'FGGGFGGGFGGGFGDFGCEBB@CCDFDDFFFFBFFGFGEFDFFFF;D@DD>C@DDGGGDFGDGG?GFGFEGFGGEF@FDGGGFGFBGGD'),
- '@SRX079802': ('ATTAGCGAGGAGGAGTGCTGAGAAGATGTCGCCTACGCCGTTGAAATTCCCTTCAATCAGGGGGTACTGGAGGATACGAGTTTGTGTG', 'BFFFFFFFB@B@A<@D>BDDACDDDEBEDEFFFBFFFEFFDFFF=CC@DDFD8FFFFFFF8/+.2,@7<<:?B/:<><-><@.A*C>D'),
- '@SRX079803': ('GAACGACAGCAGCTCCTGCATAACCGCGTCCTTCTTCTTTAGCGTTGTGCAAAGCATGTTTTGTATTACGGGCATCTCGAGCGAATC', 'DFFFEGDGGGGFGGEDCCDCEFFFFCCCCCB>CEBFGFBGGG?DE=:6@=>AD?D8DCEE:>EEABE5D@5:DDCA;EEE-DCD')}
+---
- filter_fastq(EXAMPLE_FASTQ, length_bounds=1)
- filter_fastq(EXAMPLE_FASTQ, gc_bounds = (55, 90), quality_threshold=10)
-~~~
## Contact
-Also, you can send your feedback to [ukrainskaya49@gmail.com](mailto:ukrainskaya49@gmail.com).
+For questions, feedback, or suggestions, please contact:
+
+- Email: ukrainskaya49@gmail.com
+
+---
+
diff --git a/addscript/fastq_read_write_file.py b/addscript/fastq_read_write_file.py
deleted file mode 100644
index 4124eb8..0000000
--- a/addscript/fastq_read_write_file.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import os
-
-
-def read_fastq(input_fastq):
- base_directory = os.path.dirname(input_fastq)
- filtered_directory = os.path.join(base_directory, 'filtered')
- if not os.path.exists(filtered_directory):
- os.makedirs(filtered_directory)
- with open(input_fastq) as file:
- keys = []
- values = []
- for line in file.readlines():
- line_new = line.strip()
- if line_new.startswith('@SRX'):
- keys.append(line_new)
- elif not line_new.startswith('+'):
- values.append(line_new)
- input_fastq_data = {keys[i]: (values[2 * i], values[2 * i + 1])
- for i in range(len(keys))}
- return input_fastq_data, filtered_directory
-
-
-def write_fastq(output_fastq_data, output_fastq):
- with open(output_fastq, 'w') as file:
- for sequence_id, (sequence_fastq, quality_fastq) in output_fastq_data.items():
- file.write(sequence_id + '\n')
- file.write(sequence_fastq + '\n')
- file.write('+' + sequence_id[1:] + '\n')
- file.write(quality_fastq + '\n')
diff --git a/addscript/fastq_qc_function.py b/addscript/qc_tool/fastq_qc_function.py
similarity index 64%
rename from addscript/fastq_qc_function.py
rename to addscript/qc_tool/fastq_qc_function.py
index 1573ead..3d235df 100644
--- a/addscript/fastq_qc_function.py
+++ b/addscript/qc_tool/fastq_qc_function.py
@@ -25,10 +25,16 @@ def fast_qc(seqs):
indicating the reliability of the sequence data.
"""
gc_len_q = {}
- quality_scores = []
for sequence_name, (sequence, quality) in seqs.items():
- gc_count = (sequence.count('G') + sequence.count('C'))/len(sequence) * 100
- quality_scores = [ord(char) - 33 for char in quality]
- average_quality = sum(quality_scores) / len(quality_scores) if quality_scores else 0
- gc_len_q[sequence_name] = (gc_count, len(sequence), average_quality)
+ if not sequence: # Проверка на пустую последовательность
+ gc_count = 0.0
+ length = 0
+ average_quality = 0.0
+ else:
+ gc_count = (sequence.count('G') + sequence.count('C')) / len(sequence) * 100
+ quality_scores = [ord(char) - 33 for char in quality]
+ average_quality = sum(quality_scores) / len(quality_scores) if quality_scores else 0
+ length = len(sequence)
+
+ gc_len_q[sequence_name] = (gc_count, length, average_quality)
return gc_len_q
diff --git a/addscript/qc_tool/fastq_read_write_file.py b/addscript/qc_tool/fastq_read_write_file.py
new file mode 100644
index 0000000..aa1aad0
--- /dev/null
+++ b/addscript/qc_tool/fastq_read_write_file.py
@@ -0,0 +1,28 @@
+import os
+
+def read_fastq(input_fastq):
+ base_directory = os.path.dirname(input_fastq)
+ filtered_directory = os.path.join(base_directory, 'filtered')
+ os.makedirs(filtered_directory, exist_ok=True)
+
+ input_fastq_data = {}
+ with open(input_fastq) as file:
+ while True:
+ header = file.readline().strip()
+ if not header:
+ break
+ sequence = file.readline().strip()
+ _ = file.readline().strip() # Пропускаем строку '+'
+ quality = file.readline().strip()
+ input_fastq_data[header] = (sequence, quality)
+
+ return input_fastq_data, filtered_directory
+
+
+def write_fastq(output_fastq_data, output_fastq):
+ with open(output_fastq, 'w') as file:
+ for sequence_id, (sequence, quality) in output_fastq_data.items():
+ file.write(f"{sequence_id}\n")
+ file.write(f"{sequence}\n")
+ file.write(f"+{sequence_id[1:]}\n")
+ file.write(f"{quality}\n")
\ No newline at end of file
diff --git a/addscript/qc_tool/test_qc_tool.py b/addscript/qc_tool/test_qc_tool.py
new file mode 100644
index 0000000..1522030
--- /dev/null
+++ b/addscript/qc_tool/test_qc_tool.py
@@ -0,0 +1,55 @@
+import unittest
+import os
+from qc_tool.fastq_read_write_file import read_fastq, write_fastq
+from qc_tool.fastq_qc_function import fast_qc
+
+class TestFastQC(unittest.TestCase):
+ def test_gc_content(self):
+ seqs = {"seq1": ("AGCTGCTA", "IIIIIIII")}
+ result = fast_qc(seqs)
+ self.assertAlmostEqual(result["seq1"][0], 50.0) # GC content is 50%
+
+ def test_sequence_length(self):
+ seqs = {"seq1": ("AGCTGCTA", "IIIIIIII")}
+ result = fast_qc(seqs)
+ self.assertEqual(result["seq1"][1], 8) # Length is 8
+
+ def test_average_quality(self):
+ seqs = {"seq1": ("AGCTGCTA", "IIIIIIII")}
+ result = fast_qc(seqs)
+ self.assertAlmostEqual(result["seq1"][2], 40.0) # Average quality is 40
+
+ def test_empty_sequence(self):
+ seqs = {"seq1": ("", "")}
+ result = fast_qc(seqs)
+ self.assertEqual(result["seq1"], (0.0, 0, 0.0)) # Empty sequence
+
+ def test_read_fastq(self):
+ with open("test.fastq", "w") as f:
+ f.write("@seq1\nAGCTGCTA\n+\nIIIIIIII\n")
+ seqs, _ = read_fastq("test.fastq")
+ self.assertEqual(seqs, {"@seq1": ("AGCTGCTA", "IIIIIIII")})
+ os.remove("test.fastq")
+
+ def test_write_fastq(self):
+ data = {"@seq1": ("AGCTGCTA", "IIIIIIII")}
+ write_fastq(data, "output.fastq")
+ with open("output.fastq") as f:
+ content = f.read()
+ expected = "@seq1\nAGCTGCTA\n+seq1\nIIIIIIII\n"
+ self.assertEqual(content, expected)
+ os.remove("output.fastq")
+
+ def test_invalid_file(self):
+ with self.assertRaises(FileNotFoundError):
+ read_fastq("nonexistent.fastq")
+
+ def test_logging(self):
+ import logging
+ logger = logging.getLogger(__name__)
+ with self.assertLogs(logger, level='INFO') as log:
+ logger.info("Test log message")
+ self.assertIn("INFO:Test log message", log.output[0])
+
+if __name__ == "__main__":
+ unittest.main()
\ No newline at end of file
diff --git a/bioprogsv.py b/bioprogsv.py
index c704552..7133a88 100644
--- a/bioprogsv.py
+++ b/bioprogsv.py
@@ -4,9 +4,10 @@
complement,
reverse_complement,
is_none)
-from addscript.fastq_qc_function import fast_qc
-from addscript.fastq_read_write_file import read_fastq, write_fastq
-
+from addscript.qc_tool.fastq_qc_function import fast_qc
+from addscript.qc_tool.fastq_read_write_file import read_fastq, write_fastq
+import argparse
+import logging
def run_dna_rna_tools(*args):
@@ -45,34 +46,106 @@ def run_dna_rna_tools(*args):
def filter_fastq(
input_fastq: str,
output_fastq: str,
- gc_bounds: tuple[int, int] = (0, 100),
- length_bounds: tuple[int, int] = (0, 2**32),
+ gc_bounds: tuple[int, int] | int = (0, 100),
+ length_bounds: tuple[int, int] | int = (0, 2**32),
quality_threshold: float = 0.0
) -> str:
"""
+ Filters FASTQ sequences based on GC content, length, and quality thresholds.
+
Parameters:
- input_fastq (str): Path to the input FASTQ file.
- output_fastq (str): filtered FASTQ file name.
- gc_bounds (tuple of int or int): Bounds for GC content filtering;
- if an int, used as an upper bound.
- length_bounds (tuple of int or int): Bounds for sequence length filtering;
- if an int, used as an upper bound.
- quality_threshold (float): Minimum quality score required for sequences to be included.
+ - input_fastq (str): Path to the input FASTQ file.
+ - output_fastq (str): Path to the filtered FASTQ file.
+ - gc_bounds (tuple[int, int] | int): Bounds for GC content filtering.
+ If an integer is provided, it is treated as the upper bound.
+ - length_bounds (tuple[int, int] | int): Bounds for sequence length filtering.
+ If an integer is provided, it is treated as the upper bound.
+ - quality_threshold (float): Minimum average quality score required.
Returns:
- str: Path to the generated FASTQ file containing the filtered sequences.
+ - str: Path to the generated FASTQ file containing the filtered sequences.
"""
- if isinstance(gc_bounds, (int)):
- gc_bounds = (0, gc_bounds)
- if isinstance(length_bounds, (int)):
- length_bounds = (0, length_bounds)
- input_fastq_data, filtered_directory = read_fastq(input_fastq)
- output_fastq = filtered_directory + '/' + output_fastq
- data_param = fast_qc(input_fastq_data)
- output_fastq_data = {}
- for sequence_name, (gc, length, quality) in data_param.items():
- if (gc_bounds[0] <= gc <= gc_bounds[1]) and \
- (length_bounds[0] <= length <= length_bounds[1]) and \
- (quality >= float(quality_threshold)):
- output_fastq_data[sequence_name] = input_fastq_data[sequence_name]
- return write_fastq(output_fastq_data, output_fastq)
+
+ gc_bounds = (0, gc_bounds) if isinstance(gc_bounds, int) else gc_bounds
+ length_bounds = (0, length_bounds) if isinstance(length_bounds, int) else length_bounds
+ input_fastq_data, _ = read_fastq(input_fastq)
+ qc_results = fast_qc(input_fastq_data)
+ filtered_data = {
+ seq_name: seq_data
+ for seq_name, seq_data in input_fastq_data.items()
+ if (
+ gc_bounds[0] <= qc_results[seq_name][0] <= gc_bounds[1] and
+ length_bounds[0] <= qc_results[seq_name][1] <= length_bounds[1] and
+ qc_results[seq_name][2] >= quality_threshold
+ )
+ }
+
+ write_fastq(filtered_data, output_fastq)
+ return output_fastq
+
+
+# Настройка парсера аргументов
+def parse_args():
+ parser = argparse.ArgumentParser(description="DNA/RNA tools and FASTQ filtering.")
+ subparsers = parser.add_subparsers(dest="command")
+
+ # Парсер для DNA/RNA инструментов
+ dna_rna_parser = subparsers.add_parser("dna_rna_tools", help="Perform DNA/RNA transformations.")
+ dna_rna_parser.add_argument("--seqs", nargs="+", required=True, help="List of DNA/RNA sequences.")
+ dna_rna_parser.add_argument("--action", choices=["transcribe", "reverse", "complement", "reverse_complement"],
+ required=True, help="Action to perform.")
+
+ # Парсер для фильтрации FASTQ
+ fastq_parser = subparsers.add_parser("filter_fastq", help="Filter FASTQ sequences.")
+ fastq_parser.add_argument("input_fastq", help="Path to the input FASTQ file.")
+ fastq_parser.add_argument("output_fastq", help="Path to the filtered FASTQ file.")
+ fastq_parser.add_argument("--gc_bounds", nargs=2, type=int, default=(0, 100),
+ help="GC content bounds (min max).")
+ fastq_parser.add_argument("--length_bounds", nargs=2, type=int, default=(0, 2**32),
+ help="Sequence length bounds (min max).")
+ fastq_parser.add_argument("--quality_threshold", type=float, default=0.0,
+ help="Minimum average quality score.")
+
+ return parser.parse_args()
+
+# Настройка логгирования
+def setup_logger(log_file):
+ logging.basicConfig(
+ filename=log_file,
+ level=logging.INFO,
+ format="%(asctime)s - %(levelname)s - %(message)s"
+ )
+ return logging.getLogger(__name__)
+
+# Основная программа
+def main():
+ args = parse_args()
+ logger = setup_logger("tool.log")
+
+ try:
+ if args.command == "dna_rna_tools":
+ logger.info("Running DNA/RNA tools...")
+ result = run_dna_rna_tools(args.seqs, args.action)
+ print(result)
+
+ elif args.command == "filter_fastq":
+ logger.info("Filtering FASTQ file...")
+ output_path = filter_fastq(
+ args.input_fastq,
+ args.output_fastq,
+ gc_bounds=args.gc_bounds,
+ length_bounds=args.length_bounds,
+ quality_threshold=args.quality_threshold
+ )
+ logger.info(f"Filtered FASTQ saved to {output_path}")
+
+ else:
+ logger.error("Unknown command.")
+ raise ValueError("Unknown command.")
+
+ except Exception as e:
+ logger.error(f"An error occurred: {e}")
+ raise
+
+if __name__ == "__main__":
+ main()
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..e69de29