File Coverage

blib/lib/App/Fasops/Command/stat.pm
Criterion Covered Total %
statement 80 84 95.2
branch 19 26 73.0
condition 6 9 66.6
subroutine 12 12 100.0
pod 6 6 100.0
total 123 137 89.7


line stmt bran cond sub pod time code
1             package App::Fasops::Command::stat;
2 20     20   14125 use strict;
  20         65  
  20         625  
3 20     20   124 use warnings;
  20         42  
  20         512  
4 20     20   107 use autodie;
  20         49  
  20         116  
5              
6 20     20   124468 use Text::CSV_XS;
  20         223655  
  20         1220  
7              
8 20     20   180 use App::Fasops -command;
  20         59  
  20         261  
9 20     20   8406 use App::Fasops::Common;
  20         53  
  20         17364  
10              
11             sub abstract {
12 2     2 1 48 return 'basic statistics on alignments';
13             }
14              
15             sub opt_spec {
16             return (
17 6     6 1 65 [ "outfile|o=s", "output filename. [stdout] for screen" ],
18             [ "length|l=i", "the threshold of alignment length", { default => 1 } ],
19             [ 'outgroup', 'alignments have an outgroup', ],
20             { show_defaults => 1, }
21             );
22             }
23              
24             sub usage_desc {
25 6     6 1 56110 return "fasops stat [options] ";
26             }
27              
28             sub description {
29 1     1 1 1069 my $desc;
30 1         6 $desc .= ucfirst(abstract) . ".\n";
31 1         4 $desc .= <<'MARKDOWN';
32              
33             * are paths to axt files, .axt.gz is supported
34             * infile == stdin means reading from STDIN
35              
36             MARKDOWN
37              
38 1         3 return $desc;
39             }
40              
41             sub validate_args {
42 5     5 1 5816 my ( $self, $opt, $args ) = @_;
43              
44 5 100       8 if ( @{$args} != 1 ) {
  5         23  
45 1         3 my $message = "This command need one input file.\n\tIt found";
46 1         2 $message .= sprintf " [%s]", $_ for @{$args};
  1         4  
47 1         3 $message .= ".\n";
48 1         10 $self->usage_error($message);
49             }
50 4         8 for ( @{$args} ) {
  4         13  
51 4 50       17 next if lc $_ eq "stdin";
52 4 100       21 if ( !Path::Tiny::path($_)->is_file ) {
53 1         119 $self->usage_error("The input file [$_] doesn't exist.");
54             }
55             }
56              
57 3 50       265 if ( !exists $opt->{outfile} ) {
58 0         0 $opt->{outfile} = Path::Tiny::path( $args->[0] )->absolute . ".csv";
59             }
60             }
61              
62             sub execute {
63 3     3 1 22 my ( $self, $opt, $args ) = @_;
64              
65             #@type IO::Handle
66 3         7 my $in_fh;
67 3 50       15 if ( lc $args->[0] eq "stdin" ) {
68 0         0 $in_fh = *STDIN{IO};
69             }
70             else {
71 3         25 $in_fh = IO::Zlib->new( $args->[0], "rb" );
72             }
73              
74 3         5304 my $out_fh;
75 3 50       14 if ( lc( $opt->{outfile} ) eq "stdout" ) {
76 3         10 $out_fh = *STDOUT{IO};
77             }
78             else {
79 0         0 open $out_fh, ">", $opt->{outfile};
80             }
81              
82             # csv object
83 3         32 my $csv = Text::CSV_XS->new( { eol => $/, } );
84              
85             # headers
86 3         465 my @headers = qw{
87             first legnth comparables identities differences gaps ns errors D indel
88             };
89 3         71 $csv->print( $out_fh, \@headers );
90              
91 3         102 my $content = ''; # content of one block
92 3         7 while (1) {
93 84 100 66     836 last if $in_fh->eof and $content eq '';
94 81         3801 my $line = '';
95 81 50       288 if ( !$in_fh->eof ) {
96 81         3332 $line = $in_fh->getline;
97             }
98 81 50       10164 next if substr( $line, 0, 1 ) eq "#";
99              
100 81 100 66     483 if ( ( $line eq '' or $line =~ /^\s+$/ ) and $content ne '' ) {
      66        
101 9         43 my $info_of = App::Fasops::Common::parse_block( $content, 1 );
102 9         20 $content = '';
103              
104 9         14 my @full_names;
105 9         18 my $seq_refs = [];
106              
107 9         14 for my $key ( keys %{$info_of} ) {
  9         34  
108 36         548 push @full_names, $key;
109 36         47 push @{$seq_refs}, $info_of->{$key}{seq};
  36         113  
110             }
111              
112 9 50       114 if ( $opt->{length} ) {
113 9 100       30 next if length $info_of->{ $full_names[0] }{seq} < $opt->{length};
114             }
115              
116             # outgroup
117 8         97 my $out_seq;
118 8 100       32 if ( $opt->{outgroup} ) {
119 3         5 $out_seq = pop @{$seq_refs};
  3         6  
120             }
121              
122 8         15 my $first_name = $full_names[0];
123 8         29 my $result = App::Fasops::Common::multi_seq_stat($seq_refs);
124 8         32 my $indel_sites = App::Fasops::Common::get_indels($seq_refs);
125              
126 8         18 $csv->print( $out_fh, [ $first_name, @{$result}, scalar( @{$indel_sites} ) ] );
  8         16  
  8         164  
127             }
128             else {
129 72         149 $content .= $line;
130             }
131             }
132              
133 3         575 close $out_fh;
134 0           $in_fh->close;
135             }
136              
137             1;