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
4 changes: 3 additions & 1 deletion gfftk/gff.py
Original file line number Diff line number Diff line change
Expand Up @@ -3414,15 +3414,17 @@ def _sortDict(d):
# GFF3 phase is 1 less than flat file
current_phase = v["codon_start"][i] - 1
for y in range(0, num_cds):
cds_num = y + 1
gffout.write(
"{:}\t{:}\tCDS\t{:}\t{:}\t.\t{:}\t{:}\tID={:}.cds;Parent={:};\n".format(
"{:}\t{:}\tCDS\t{:}\t{:}\t.\t{:}\t{:}\tID={:}.cds{:};Parent={:};\n".format(
v["contig"],
new_source,
sortedCDS[y][0],
sortedCDS[y][1],
v["strand"],
current_phase,
v["ids"][i],
cds_num,
v["ids"][i],
)
)
Expand Down
40 changes: 38 additions & 2 deletions tests/unit/test_gff.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from gfftk.gff import (
_detect_format,
dict2gff3,
gff2dict,
is_combined_gff_fasta,
simplifyGO,
Expand All @@ -19,6 +20,42 @@
class TestGFFParsing:
"""Tests for GFF parsing functions."""

def test_dict2gff3_assigns_unique_cds_ids(self):
"""Each CDS segment emitted for a transcript must receive its own ID."""
with tempfile.NamedTemporaryFile(mode="w", delete=False) as gff_temp:
gff_temp.write("##gff-version 3\n")
gff_temp.write("contig1\ttest\tgene\t1\t12\t.\t+\t.\tID=gene1\n")
gff_temp.write(
"contig1\ttest\tmRNA\t1\t12\t.\t+\t.\tID=mRNA1;Parent=gene1\n"
)
gff_temp.write("contig1\ttest\texon\t1\t3\t.\t+\t.\tID=exon1;Parent=mRNA1\n")
gff_temp.write("contig1\ttest\texon\t7\t12\t.\t+\t.\tID=exon2;Parent=mRNA1\n")
gff_temp.write("contig1\ttest\tCDS\t1\t3\t.\t+\t0\tID=cds1;Parent=mRNA1\n")
gff_temp.write("contig1\ttest\tCDS\t7\t12\t.\t+\t0\tID=cds2;Parent=mRNA1\n")
gff_name = gff_temp.name

with tempfile.NamedTemporaryFile(mode="w", delete=False) as fasta_temp:
fasta_temp.write(">contig1\nATGAAATTTTAA\n")
fasta_name = fasta_temp.name

with tempfile.NamedTemporaryFile(mode="w", delete=False) as output_temp:
output_name = output_temp.name

try:
annotations = gff2dict(gff_name, fasta_name, debug=False)
dict2gff3(annotations, output=output_name)
with open(output_name) as gff_output:
cds_ids = [
line.split("ID=", 1)[1].split(";", 1)[0]
for line in gff_output
if "\tCDS\t" in line
]
assert cds_ids == ["mRNA1.cds1", "mRNA1.cds2"]
finally:
os.unlink(gff_name)
os.unlink(fasta_name)
os.unlink(output_name)

def test_detect_format(self):
"""Test the _detect_format function."""
# Create a GFF3-like file for testing
Expand Down Expand Up @@ -402,6 +439,7 @@ def test_longest_orf_gapmm2_alignment(self):
if os.path.exists(gff_file):
os.remove(gff_file)


def test_longest_orf_exact_boundary(self):
"""Test _longest_orf correctly processes an ORF that exactly ends on an exon boundary."""
# Create a fasta file
Expand Down Expand Up @@ -452,5 +490,3 @@ def test_longest_orf_exact_boundary(self):
os.remove(fasta_file)
if os.path.exists(gff_file):
os.remove(gff_file)