File Coverage

blib/lib/NCBIx/BigFetch.pm
Criterion Covered Total %
statement 124 253 49.0
branch 17 58 29.3
condition 1 6 16.6
subroutine 20 36 55.5
pod 10 15 66.6
total 172 368 46.7


line stmt bran cond sub pod time code
1             package NCBIx::BigFetch;
2 2     2   48670 use warnings;
  2         7  
  2         63  
3 2     2   10 use strict;
  2         3  
  2         66  
4 2     2   2815 use Class::Std;
  2         28913  
  2         15  
5 2     2   2362 use Class::Std::Utils;
  2         8085  
  2         14  
6 2     2   92 use Carp;
  2         5  
  2         119  
7 2     2   1880 use LWP::Simple;
  2         175728  
  2         21  
8 2     2   5593 use YAML qw(DumpFile LoadFile);
  2         19187  
  2         141  
9 2     2   335312 use Time::HiRes qw(usleep);
  2         4274  
  2         12  
10              
11 2     2   443 use version; our $VERSION = qv('0.5.6');
  2         5  
  2         19  
12              
13             our $config_file = 'efetch_N.yml';
14             our $esearch_file = 'esearch_N.txt';
15             our $data_file = 'sequences_N_M.txt';
16             our $sleep_policy = 2_750_000;
17              
18             {
19             # These properties have defaults but can also be initialized by new()
20             my %project_id_of :ATTR( :get :set );
21             my %base_url_of :ATTR( :get :set );
22             my %base_dir_of :ATTR( :get :set );
23             my %db_of :ATTR( :get :set );
24             my %query_of :ATTR( :get :set );
25             my %index_of :ATTR( :get :set );
26             my %return_max_of :ATTR( :get :set );
27             my %return_type_of :ATTR( :get :set );
28             my %return_mode_of :ATTR( :get :set );
29             my %missing_of :ATTR( :get :set );
30              
31             # These properties are set by the code
32             my %start_date_of :ATTR( :get :set );
33             my %start_time_of :ATTR( :get :set );
34             my %querykey_of :ATTR( :get :set );
35             my %webenv_of :ATTR( :get :set );
36             my %count_of :ATTR( :get :set );
37              
38 0     0 1 0 sub next_index { my ($self) = @_; my $ident = ident $self; $index_of{$ident} += $return_max_of{$ident}; $self->_save(); }
  0         0  
  0         0  
  0         0  
39              
40 3     3 0 6 sub get_config_filename { my ($self) = @_; my $project_id = $self->get_project_id(); $config_file =~ s/N/$project_id/; return $self->get_base_dir() . '/' . $config_file; }
  3         15  
  3         52  
  3         17  
41 2     2 0 5 sub get_esearch_filename { my ($self) = @_; my $project_id = $self->get_project_id(); $esearch_file =~ s/N/$project_id/; return $self->get_base_dir() . '/' . $esearch_file; }
  2         11  
  2         16  
  2         13  
42 0 0   0 0 0 sub get_data_filename { my ($self, $index) = @_; my $project_id = $self->get_project_id(); $index = defined($index) ? $index : $self->get_index(); my $filename = $data_file; $filename =~ s/N/$project_id/g; $filename =~ s/M/$index/g; return $self->get_base_dir() . '/' . $filename; }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
43              
44             sub BUILD {
45 1     1 0 82 my ($self, $ident, $arg_ref) = @_;
46              
47             # Set environment
48 1         6 $self->_init( $arg_ref );
49            
50             # Check for existing project
51 1 50       12 if (-e $self->get_config_filename()) {
52 0         0 $self->_status("Loading existing project");
53              
54             # Get existing config
55 0         0 $self->_load();
56             } else {
57 1         66 $self->_status("Starting new project");
58              
59             # Set start date and time
60 1         8 $self->_set_date();
61            
62             # Submit search and parse results
63 1         4 $self->_search();
64            
65             # Save config
66 1         5 $self->_save( $arg_ref );
67             }
68              
69 1         9 return;
70             }
71              
72             sub file_test {
73 1     1 0 1066 my ( $self ) = @_;
74 1         3 my $file_written;
75              
76             # Get the file names for the test (/home/username/e* or /root/e*)
77 1         5 my $config_filename = $self->get_config_filename();
78 1         8 my $esearch_filename = $self->get_esearch_filename();
79              
80             # Remove exisitng files and count files written
81 1 50       38 if (-e $config_filename ) { $file_written++; `rm $config_filename`; }
  1         3  
  1         7523  
82 1 50       148 if (-e $esearch_filename ) { $file_written++; `rm $esearch_filename`; }
  1         12  
  1         10483  
83              
84 1         104 return $file_written;
85             }
86              
87             sub results_waiting {
88 0     0 1 0 my ( $self ) = @_;
89 0 0       0 if ( $self->get_index() < $self->get_count() ) {
90 0         0 return 1;
91             } else {
92 0         0 $self->_status("Found " . $self->_commify( scalar(@{ $self->get_missing() }) ) . " missing batches." );
  0         0  
93 0         0 return 0;
94             }
95             }
96              
97             sub missing_batches {
98 0     0 1 0 my ( $self ) = @_;
99 0 0       0 if ( @{ $self->get_missing() } ) { return 1; } else { return 0; }
  0         0  
  0         0  
  0         0  
100             }
101              
102             sub get_next_batch {
103 0     0 1 0 my ( $self ) = @_;
104 0         0 my $index = $self->get_index();
105              
106             # Get the batch
107 0         0 $self->get_batch( $index );
108              
109             # Update the index
110 0         0 $self->next_index();
111              
112 0         0 return;
113             }
114              
115             sub get_batch {
116 0     0 1 0 my ( $self, $index ) = @_;
117 0         0 my $return_max = $self->get_return_max();
118 0         0 my $return_type = $self->get_return_type();
119 0         0 my $return_mode = $self->get_return_mode();
120              
121 0         0 $self->_status("Starting with index " . $self->_commify( $index ) );
122            
123             # Ethics requires we wait sleep_policy microseconds before retrieving
124 0         0 $self->_sleep();
125            
126             # Define a batch through URL
127 0         0 my $efetch_url = $self->get_base_url() . 'efetch.fcgi?db=' . $self->get_db();
128 0         0 $efetch_url .= '&WebEnv=' . $self->get_webenv() . '&query_key=' . $self->get_querykey() . "&rettype=$return_type&retmode=$return_mode";
129 0         0 $efetch_url .= "&retstart=$index&retmax=$return_max";
130 0         0 $efetch_url .= '&tool=ncbix_bigfetch&email=roger@iosea.com';
131              
132             # Get the batch using LWP::Simple (get)
133 0         0 my $results = get($efetch_url);
134            
135             # Check results # TODO: capture expired WebEnv and restart query
136 0 0       0 if ( $results =~ m/resource is temporarily unavailable/i ) { $self->note_missing_batch( $index ); }
  0         0  
137 0 0       0 if ( $results =~ m/NCBI C\+\+ Exception/i ) { $self->note_missing_batch( $index ); }
  0         0  
138 0 0       0 if ( $results eq '' ) { $self->note_missing_batch( $index ); }
  0         0  
139              
140             # Save the sequences
141 0         0 $self->_set_file_text( $self->get_data_filename( $index ), $results );
142              
143 0         0 return;
144             }
145              
146             sub get_missing_batch {
147 0     0 1 0 my ( $self ) = @_;
148              
149             # Get the next missing batch index
150 0         0 my @missing = @{ $self->get_missing() };
  0         0  
151 0         0 my $index = shift @missing;
152              
153             # Update the missing batch list
154 0         0 $self->set_missing( \@missing );
155              
156             # Get the batch
157 0         0 $self->get_batch( $index );
158              
159 0         0 return;
160             }
161              
162             sub note_missing_batch {
163 0     0 1 0 my ( $self, $index ) = @_;
164 0         0 my @missing;
165 0         0 my $missing = $self->get_missing();
166 0 0       0 if ( defined $missing ) {
167 0         0 @missing = @{ $missing };
  0         0  
168             } else {
169 0         0 @missing = ();
170             }
171 0         0 push @missing, $index;
172 0         0 $self->set_missing( \@missing );
173 0         0 $self->_save();
174             }
175              
176             sub get_sequence {
177 0     0 1 0 my ( $self, $id ) = @_;
178 0         0 my $return_type = $self->get_return_type();
179 0         0 my $return_mode = $self->get_return_mode();
180              
181 0         0 $self->_status("Fetching sequence $id");
182            
183             # Ethics requires we wait sleep_policy microseconds before retrieving
184 0         0 $self->_sleep();
185            
186             # Define a batch through URL
187 0         0 my $efetch_url = $self->get_base_url() . 'efetch.fcgi?db=' . $self->get_db();
188 0         0 $efetch_url .= '&id=' . $id . "&rettype=$return_type&retmode=$return_mode";
189 0         0 $efetch_url .= '&tool=ncbix_bigfetch&email=roger@iosea.com';
190              
191             # Get the sequence
192 0         0 my $results = get($efetch_url);
193            
194             # Save the sequences in missing file
195 0         0 $self->_add_file_text( $self->get_data_filename( 0 ), $results );
196              
197 0         0 return;
198             }
199              
200             sub unavailable_ids {
201 0     0 1 0 my ( $self ) = @_;
202 0         0 my $count = $self->get_index();
203 0         0 my $return_max = $self->get_return_max();
204 0         0 my $index = 1;
205 0         0 my @unavailables = ();
206              
207 0         0 while ( $index < $count ) {
208 0         0 $self->_status("Checking " . $self->_commify( $index ) . " through " . $self->_commify( $index + $return_max - 1 ) );
209            
210             # Get the sequences
211 0         0 my $text = $self->_get_file_text( $self->get_data_filename( $index ) );
212              
213 0         0 while ( $text =~ m/Error:\s(\d+)\sis\snot\savailable\sat\sthis\stime/g ) { push @unavailables, $1; }
  0         0  
214              
215             # Update the index
216 0         0 $index += $return_max;
217             }
218              
219 0         0 $self->_status("Found " . $self->_commify( scalar(@unavailables) ) . " unavailable ids." );
220              
221 0         0 return \@unavailables;
222             }
223              
224             sub _init {
225 1     1   3 my ( $self, $arg_ref ) = @_;
226              
227 1 50       5 my $project_id = $arg_ref->{project_id} ? $arg_ref->{project_id} : "1";
228 1 50       4 my $base_url = $arg_ref->{base_url} ? $arg_ref->{base_url} : "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/";
229 1 50       7 my $base_dir = $arg_ref->{base_dir} ? $arg_ref->{base_dir} : $self->_get_base_dir();
230 1 50       19 my $db = $arg_ref->{db} ? $arg_ref->{db} : "protein";
231 1 50       11 my $query = $arg_ref->{query} ? $arg_ref->{query} : "apoptosis";
232 1 50       15 my $index = $arg_ref->{index} ? $arg_ref->{index} : "1";
233 1 50       8 my $return_max = $arg_ref->{return_max} ? $arg_ref->{return_max} : "500";
234 1 50       13 my $return_type = $arg_ref->{return_type} ? $arg_ref->{return_type} : "fasta";
235 1 50       8 my $return_mode = $arg_ref->{return_mode} ? $arg_ref->{return_mode} : "text";
236 1 50       10 my $missing = $arg_ref->{missing} ? $arg_ref->{missing} : [];
237              
238 1         24 $self->set_project_id( $project_id );
239 1         48 $self->set_base_url( $base_url );
240 1         22 $self->set_base_dir( $base_dir );
241 1         16 $self->set_db( $db );
242 1         12 $self->set_query( $query );
243 1         12 $self->set_index( $index );
244 1         16 $self->set_return_max( $return_max );
245 1         21 $self->set_return_type( $return_type );
246 1         156 $self->set_return_mode( $return_mode );
247 1         17 $self->set_missing( $missing );
248              
249 1         16 return;
250             }
251              
252             sub _set_date {
253 1     1   3 my ( $self ) = @_;
254              
255 1         43 my @time = localtime;
256 1         9 my $year = 1900 + $time[5];
257 1         2 my $month = $time[4] + 1; $month =~ s/^(\d)$/0$1/;
  1         34  
258 1         2 my $day = $time[3]; $day =~ s/^(\d)$/0$1/;
  1         8  
259 1         3 my $hour = $time[2]; $hour =~ s/^(\d)$/0$1/;
  1         10  
260 1         5 my $min = $time[1]; $min =~ s/^(\d)$/0$1/;
  1         9  
261 1         3 my $sec = $time[0]; $sec =~ s/^(\d)$/0$1/;
  1         10  
262            
263 1         12 $self->set_start_date( "$year-$month-$day" );
264 1         16 $self->set_start_time( "$hour:$min:$sec" );
265              
266 1         17 return;
267             }
268              
269             sub _search {
270             #my ( $self, $arg_ref ) = @_;
271 1     1   3 my ( $self, $arg_ref ) = @_;
272              
273             # Get search result ticket
274 1         8 my $esearch_url = $self->get_base_url() . 'esearch.fcgi?db=' . $self->get_db();
275 1         14 $esearch_url .= '&term=' . $self->get_query() . '&usehistory=y';
276 1         6 $esearch_url .= '&tool=ncbix_bigfetch&email=roger@iosea.com';
277 1         57 my $esearch_result = get($esearch_url);
278              
279             # Save search result
280 1         2798379 $self->_set_file_text( $self->get_esearch_filename(), $esearch_result );
281            
282             # Parse the relevant keys
283 1         11 $esearch_result =~ m/([0-9]*)<\/Count>/g; $self->set_count( $1 );
  1         8  
284 1         16 $esearch_result =~ m/([0-9]*)<\/QueryKey>/g; $self->set_querykey( $1 );
  1         5  
285 1         15 $esearch_result =~ m/([\.a-zA-Z0-9_@\-]*)<\/WebEnv>/g; $self->set_webenv( $1 );
  1         7  
286              
287 1         11 return;
288             }
289              
290             sub _load {
291 0     0   0 my ( $self ) = @_;
292 0         0 my %config = %{ LoadFile( $self->get_config_filename() ) };
  0         0  
293 0         0 $self->set_project_id( $config{project_id} );
294 0         0 $self->set_base_url( $config{base_url} );
295 0         0 $self->set_base_dir( $config{base_dir} );
296 0         0 $self->set_db( $config{db} );
297 0         0 $self->set_query( $config{query} );
298 0         0 $self->set_querykey( $config{querykey} );
299 0         0 $self->set_webenv( $config{webenv} );
300 0         0 $self->set_count( $config{count} );
301 0         0 $self->set_index( $config{index} );
302 0         0 $self->set_start_date( $config{start_date} );
303 0         0 $self->set_start_time( $config{start_time} );
304 0         0 $self->set_return_max( $config{return_max} );
305 0         0 $self->set_return_type( $config{return_type} );
306 0         0 $self->set_return_mode( $config{return_mode} );
307 0         0 $self->set_missing( $config{missing} );
308             }
309              
310             sub _save {
311 1     1   3 my ( $self, $arg_ref ) = @_;
312 1         4 my $ident = ident $self;
313 1         2 my $config;
314              
315 1 50       3 if (defined $arg_ref) {
316 1         2 $config = $arg_ref;
317             } else {
318 0         0 $config = { project_id => $project_id_of{$ident},
319             base_url => $base_url_of{$ident},
320             base_dir => $base_dir_of{$ident},
321             db => $db_of{$ident},
322             query => $query_of{$ident},
323             querykey => $querykey_of{$ident},
324             webenv => $webenv_of{$ident},
325             count => $count_of{$ident},
326             index => $index_of{$ident},
327             start_date => $start_date_of{$ident},
328             start_time => $start_time_of{$ident},
329             return_max => $return_max_of{$ident},
330             return_type => $return_type_of{$ident},
331             return_mode => $return_mode_of{$ident},
332             missing => $missing_of{$ident} };
333             }
334 1         5 DumpFile( $self->get_config_filename(), $config );
335 1         14174 return;
336             }
337              
338             sub _get_base_dir {
339 1     1   3 my ( $self, $base_dir ) = @_;
340 1         7881 chomp( my $id = `id -nu`);
341 1 50       45 if ($id eq 'root') { $base_dir = '/root'; } else { $base_dir = '/home/' . $id; }
  1         96  
  0         0  
342 1         25 return $base_dir;
343             }
344              
345             sub _status {
346 1     1   3 my ( $self, $msg ) = @_;
347 1         58 print STDOUT " STATUS: $msg \n";
348 1         63 return;
349             }
350              
351             sub _sleep {
352 0     0   0 my ( $self ) = @_;
353 0         0 usleep($sleep_policy);
354 0         0 return;
355             }
356              
357             sub _get_file_text {
358 0     0   0 my ( $self, $path_file_name ) = @_;
359 0         0 my ($text, $line);
360 0 0       0 if (-e $path_file_name) {
361 0 0       0 open (my $IN, '<', $path_file_name) || croak( "Cannot open $path_file_name: $!" );
362 0         0 while ($line = <$IN>) { $text .= $line; }
  0         0  
363 0 0       0 close ($IN) || croak( "Cannot close $path_file_name: $!" );
364             }
365 0         0 return $text;
366             }
367            
368             sub _set_file_text {
369 1     1   10 my ( $self, $path_file_name, $text ) = @_;
370 1 50       131 open (my $OUT, '>', $path_file_name) || croak( "Cannot open $path_file_name: $!" );
371 1   33     12 print $OUT $text || croak( "Cannot write $path_file_name: $!" );
372 1 50       58 close ($OUT) || croak( "Cannot close $path_file_name: $!" );
373             }
374            
375             sub _add_file_text {
376 0     0     my ( $self, $path_file_name, $text ) = @_;
377 0 0         open (my $OUT, '>>', $path_file_name) || croak( "Cannot open $path_file_name: $!" );
378 0   0       print $OUT $text || croak( "Cannot write $path_file_name: $!" );
379 0 0         close ($OUT) || croak( "Cannot close $path_file_name: $!" );
380             }
381              
382             sub _commify { # Perl Cookbook 2.17
383 0     0     my ( $self, $string ) = @_;
384 0           my $text = reverse $string;
385 0           $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
386 0           return scalar reverse $text;
387             }
388            
389 0     0 1   sub authors { return 'Roger Hall , Michael Bauer , Kamakshi Duvvuru . Copyleft (C) 2009'; }
390             }
391              
392             1; # Magic true value required at end of module
393             __END__