<commit_before>package Transposome::SeqIO;

use 5.012;
use Moose;
use Try::Tiny;
use Carp;
use namespace::autoclean;

with 'Transposome::Role::File';

=head1 NAME

Transposome::SeqIO - Class for reading Fasta/q data.

=head1 VERSION

Version 0.01

=cut

our $VERSION = '0.01';

=head1 SYNOPSIS

    use Transposome::SeqIO;

    my $trans_obj = Transposome::SeqIO->new( file => $infile );
    my $fh = $trans_obj->get_fh;

    while (my $seq = $trans_obj->next_seq($fh)) {
         # do something interesting with $seq
    }

=cut

has 'id' => (
    is        => 'rw',
    isa       => 'Str',
    reader    => 'get_id',
    writer    => 'set_id',
    predicate => 'has_id',
    );

has 'seq' => (
    is        => 'rw',
    isa       => 'Str',
    reader    => 'get_seq',
    writer    => 'set_seq',
    predicate => 'has_seq',
    );

has 'qual' => (
    is        => 'rw',
    lazy      => 1,
    default   => undef,
    reader    => 'get_qual',
    writer    => 'set_qual',
    predicate => 'has_qual',
    );

=head1 METHODS

=head2 next_seq

 Title   : next_seq
 Usage   : while (my $seq = $trans_obj->next_seq($fh)) { ... };
           
 Function: Reads fasta/fastq files seamlessly without needing to 
           specify the format.
                                                                            
 Returns : A Transposome::SeqIO object on which you can call methods                  
           representing the sequence, id, or quality scores (in the
           case of fastq). E.g.,
           
           while (my $seq = $trans_obj->next_seq($fh)) { 
               $seq->get_id;   # gets the sequence id
               $seq->get_seq;  # gets the sequence
               $seq->get_qual; # gets the quality scores
           }

           Each of the above methods an easy way of checking to see
           if that slot is set. E.g.,
           
           if ($seq->has_id)   { ... # is the id set? }
           if ($seq->has_seq)  { ... # is the seq set? }
           if ($seq->has_qual) { ... # is the qual set? This will be no for Fasta. }

 Args    : Takes a file handle. You can get the file handle                 
           by calling the method 'get_fh' on a Transposome::SeqIO object. E.g.,
 
           my $trans_obj = Transposome::SeqIO->new( file => $infile );
           my $fh = $trans_obj->get_fh;

=cut

sub next_seq {
    my ($self, $fh) = @_;
    my (@seqs, @quals);

    my $line = <$fh>;
    return unless defined $line;
    chomp $line;
    try {
        die unless (substr($line, 0, 1) eq '>' || substr($line, 0, 1) eq '@');
    }
    catch {
        warn "\n[ERROR]: '$line' does not look like Fasta or Fastq.\nHere is the exception: $_\n" and exit(1);
    };

    if (substr($line, 0, 1) eq '>') {
        my $name = $self->_set_id_per_encoding($line);
        $self->set_id($name);
        
        my $sline = <$fh>;
        chomp $sline;

        my $seqline;
        if ($sline =~ /[ATCGNatcgn]/) {
            push @seqs, $sline;
            while ($seqline = <$fh>) {
                last if $seqline =~ />/;
                push @seqs, $seqline;
            }
        }
        seek $fh, -length($seqline), 1 if length $seqline;
        my $seq = join '', @seqs;
        $seq =~ s/>.*// if $seq =~ />/;
        $seq =~ s/\s//g;
        try {
            die if !length($seq);
        }
        catch {
            warn "\n[ERROR]: No sequence for Fasta record '$name'.\nHere is the exception: $_\n" and exit(1);
        };
        $self->set_seq($seq);

        return $self;
    }
    if (substr($line, 0, 1) eq '@') {
        my $name = $self->_set_id_per_encoding($line);
        my $id = $1 if $line =~ /^@(\w+|\d+)(?::|-)/;
        my $pat = qr/$id/;
        $self->set_id($name);

        my $sline = <$fh>;
        chomp $sline;

        my $seqline;
        if ($sline =~ /[ATCGNatcgn]/) {
            push @seqs, $sline;
            while ($seqline = <$fh>) {
                last if $seqline =~ /\+/;
                push @seqs, $seqline;
            }
        }
        seek $fh, -length($seqline), 1 if length $seqline;
        my $seq = join '', @seqs;
        $seq =~ s/\+.*// if $seq =~ /\+/;
        $seq =~ s/\s//g;
        try {
            die if !length($seq);
        }
        catch {
            warn "\n[ERROR]: No sequence for Fastq record '$name'.\nHere is the exception: $_\n" and exit(1);
        };
        $self->set_seq($seq);
        
        my $cline = <$fh>;
        chomp $cline;
        try {
            die unless length($cline) && substr($cline, 0, 1) eq '+';
        }
        catch {
            warn "\n[ERROR]: No comment line for Fastq record '$name'.\nHere is the exception: $_\n" and exit(1);
        };

	my $qline;
        while ($qline = <$fh>) {
            chomp $qline;
            last if $qline =~ /$pat/;
            push @quals, $qline;
        }
        seek $fh, -length($qline)-1, 1 if length $qline;
        my $qual = join '', @quals;
	try {
	    die if $qual !~ /${pat}/;
	}
	catch {
	    warn "\n[ERROR]: Different IDs found for record at line $. not matching '$name'.\nHere is the exception: $_\n" and exit(1);
	};
        $qual =~ s/${pat}.*//;# if $qual =~ /${pat}/;
	$qual =~ s/\s//g;
	try {
	    die if !length($qual);<commit_msg>rollback on exception handling method to clean qual string<commit_after>package Transposome::SeqIO;

use 5.012;
use Moose;
use Try::Tiny;
use Carp;
use namespace::autoclean;

with 'Transposome::Role::File';

=head1 NAME

Transposome::SeqIO - Class for reading Fasta/q data.

=head1 VERSION

Version 0.01

=cut

our $VERSION = '0.01';

=head1 SYNOPSIS

    use Transposome::SeqIO;

    my $trans_obj = Transposome::SeqIO->new( file => $infile );
    my $fh = $trans_obj->get_fh;

    while (my $seq = $trans_obj->next_seq($fh)) {
         # do something interesting with $seq
    }

=cut

has 'id' => (
    is        => 'rw',
    isa       => 'Str',
    reader    => 'get_id',
    writer    => 'set_id',
    predicate => 'has_id',
    );

has 'seq' => (
    is        => 'rw',
    isa       => 'Str',
    reader    => 'get_seq',
    writer    => 'set_seq',
    predicate => 'has_seq',
    );

has 'qual' => (
    is        => 'rw',
    lazy      => 1,
    default   => undef,
    reader    => 'get_qual',
    writer    => 'set_qual',
    predicate => 'has_qual',
    );

=head1 METHODS

=head2 next_seq

 Title   : next_seq
 Usage   : while (my $seq = $trans_obj->next_seq($fh)) { ... };
           
 Function: Reads fasta/fastq files seamlessly without needing to 
           specify the format.
                                                                            
 Returns : A Transposome::SeqIO object on which you can call methods                  
           representing the sequence, id, or quality scores (in the
           case of fastq). E.g.,
           
           while (my $seq = $trans_obj->next_seq($fh)) { 
               $seq->get_id;   # gets the sequence id
               $seq->get_seq;  # gets the sequence
               $seq->get_qual; # gets the quality scores
           }

           Each of the above methods an easy way of checking to see
           if that slot is set. E.g.,
           
           if ($seq->has_id)   { ... # is the id set? }
           if ($seq->has_seq)  { ... # is the seq set? }
           if ($seq->has_qual) { ... # is the qual set? This will be no for Fasta. }

 Args    : Takes a file handle. You can get the file handle                 
           by calling the method 'get_fh' on a Transposome::SeqIO object. E.g.,
 
           my $trans_obj = Transposome::SeqIO->new( file => $infile );
           my $fh = $trans_obj->get_fh;

=cut

sub next_seq {
    my ($self, $fh) = @_;
    my (@seqs, @quals);

    my $line = <$fh>;
    return unless defined $line;
    chomp $line;
    try {
        die unless (substr($line, 0, 1) eq '>' || substr($line, 0, 1) eq '@');
    }
    catch {
        warn "\n[ERROR]: '$line' does not look like Fasta or Fastq.\nHere is the exception: $_\n" and exit(1);
    };

    if (substr($line, 0, 1) eq '>') {
        my $name = $self->_set_id_per_encoding($line);
        $self->set_id($name);
        
        my $sline = <$fh>;
        chomp $sline;

        my $seqline;
        if ($sline =~ /[ATCGNatcgn]/) {
            push @seqs, $sline;
            while ($seqline = <$fh>) {
                last if $seqline =~ />/;
                push @seqs, $seqline;
            }
        }
        seek $fh, -length($seqline), 1 if length $seqline;
        my $seq = join '', @seqs;
        $seq =~ s/>.*// if $seq =~ />/;
        $seq =~ s/\s//g;
        try {
            die if !length($seq);
        }
        catch {
            warn "\n[ERROR]: No sequence for Fasta record '$name'.\nHere is the exception: $_\n" and exit(1);
        };
        $self->set_seq($seq);

        return $self;
    }
    if (substr($line, 0, 1) eq '@') {
        my $name = $self->_set_id_per_encoding($line);
        my $id = $1 if $line =~ /^@(\w+|\d+)(?::|-)/;
        my $pat = qr/$id/;
        $self->set_id($name);

        my $sline = <$fh>;
        chomp $sline;

        my $seqline;
        if ($sline =~ /[ATCGNatcgn]/) {
            push @seqs, $sline;
            while ($seqline = <$fh>) {
                last if $seqline =~ /\+/;
                push @seqs, $seqline;
            }
        }
        seek $fh, -length($seqline), 1 if length $seqline;
        my $seq = join '', @seqs;
        $seq =~ s/\+.*// if $seq =~ /\+/;
        $seq =~ s/\s//g;
        try {
            die if !length($seq);
        }
        catch {
            warn "\n[ERROR]: No sequence for Fastq record '$name'.\nHere is the exception: $_\n" and exit(1);
        };
        $self->set_seq($seq);
        
        my $cline = <$fh>;
        chomp $cline;
        try {
            die unless length($cline) && substr($cline, 0, 1) eq '+';
        }
        catch {
            warn "\n[ERROR]: No comment line for Fastq record '$name'.\nHere is the exception: $_\n" and exit(1);
        };

	my $qline;
        while ($qline = <$fh>) {
            chomp $qline;
            last if $qline =~ /$pat/;
            push @quals, $qline;
        }
        seek $fh, -length($qline)-1, 1 if length $qline;
        my $qual = join '', @quals;
        $qual =~ s/${pat}.*// if $qual =~ /${pat}/;
	$qual =~ s/\s//g;
	try {
	    die if !length($qual);
{'fraction_non_alphanumeric': 0.16136188271604937, 'fraction_numerical': 0.006847993827160494, 'mean_word_length': 1.5602469135802468, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 107, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1647720', 'n_tokens_mistral': 3623, 'n_tokens_neox': 3465, 'n_words': 995}