File Coverage

blib/lib/Microarray/GEO/SOFT.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Microarray::GEO::SOFT;
2              
3 6     6   59520 use List::Vectorize qw(!table);
  6         25828  
  6         3012  
4              
5 6     6   3964 use Microarray::ExprSet;
  0            
  0            
6             use File::Basename;
7             use LWP::UserAgent;
8             use Time::HiRes qw(usleep gettimeofday);
9             use Carp;
10             use Cwd;
11             use strict;
12              
13             require Microarray::GEO::SOFT::GPL;
14             require Microarray::GEO::SOFT::GSM;
15             require Microarray::GEO::SOFT::GDS;
16             require Microarray::GEO::SOFT::GSE;
17              
18             our $VERSION = "0.20";
19             our $wd = getcwd();
20              
21             $| = 1;
22              
23             # download geo files
24             our $ua;
25             our $response;
26             our $download_start_time;
27             our $fh_out;
28              
29             1;
30              
31             sub new {
32              
33             my $invocant = shift;
34             my $class = ref($invocant) || $invocant;
35             my $self = { "file" => "",
36             "tmp_dir" => ".tmp_soft",
37             "verbose" => 1,
38             "sample_value_column" => "VALUE",
39             @_ };
40             bless($self, $class);
41            
42             opendir DIR, $self->{tmp_dir} and closedir DIR
43             or mkdir $self->{tmp_dir};
44              
45             return $self;
46            
47             }
48              
49             sub _set_to_null_fh {
50              
51             my $null = $^O eq "MSWin32" ? "NUL" : "/dev/null";
52             open my $fh_out, ">", $null;
53             $| = 1;
54             select($fh_out);
55             }
56              
57             sub _set_to_std_fh {
58             $| = 1;
59             select(STDOUT);
60             }
61              
62             sub _set_fh {
63             my $verbose = shift;
64            
65             $verbose ? _set_to_std_fh() : _set_to_null_fh();
66             }
67              
68             BEGIN {
69            
70             no strict 'refs';
71            
72             for my $accessor (qw(meta table)) {
73             *{$accessor} = sub {
74             my $self = shift;
75             return defined($self->{$accessor}) ? $self->{$accessor}
76             : undef;
77             }
78             }
79            
80             for my $accessor (qw(accession title platform)) {
81             *{$accessor} = sub {
82             my $self = shift;
83             return defined($self->{meta}->{$accessor}) ? $self->{meta}->{$accessor}
84             : undef;
85             }
86             }
87            
88             for my $accessor (qw(rownames colnames colnames_explain matrix)) {
89             *{$accessor} = sub {
90             my $self = shift;
91             return defined($self->{table}->{$accessor}) ? $self->{table}->{$accessor}
92             : undef;
93             }
94             }
95            
96             }
97              
98             sub soft_dir {
99              
100             my $self = shift;
101             return $self->{tmp_dir};
102             }
103              
104             sub parse {
105              
106             my $self = shift;
107            
108             _set_fh($self->{verbose});
109            
110             # decompress file
111             if( -e $self->{file} and (! -T $self->{file}) ) {
112             $self->{file} = _decompress($self->{file});
113             }
114              
115             my $type = _check_type($self->{file});
116            
117             my $obj;
118             if($type eq "SERIES") {
119            
120             $obj = Microarray::GEO::SOFT::GSE->new(file => $self->{file},
121             verbose => $self->{verbose},
122             sample_value_column => $self->{sample_value_column},
123             @_);
124             $obj->parse;
125            
126             }
127             elsif($type eq "DATASET") {
128            
129             $obj = Microarray::GEO::SOFT::GDS->new(file => $self->{file},
130             verbose => $self->{verbose},
131             @_);
132             $obj->parse;
133            
134             }
135             elsif($type eq "PLATFORM") {
136            
137             $obj = Microarray::GEO::SOFT::GPL->new(file => $self->{file},
138             verbose => $self->{verbose},
139             @_);
140             $obj->parse;
141             }
142             else {
143            
144             croak "ERROR: Format not supported. Only GSExxx, GDSxxx and GPLxxx are valid\n";
145            
146             }
147            
148             _set_to_std_fh();
149            
150             return $obj;
151             }
152              
153             # determine what type is the input file by reading first few lines
154             sub _check_type {
155              
156             my $file = shift;
157            
158             open F, $file or croak "Cannot open $file.\n";
159            
160             while(my $line = ) {
161            
162             if($line =~/^\^SERIES /) {
163             return "SERIES";
164             }
165            
166             elsif($line =~/^\^DATASET /) {
167             return "DATASET";
168             }
169            
170             elsif($line =~/^\^PLATFORM /) {
171             return "PLATFORM";
172             }
173            
174             elsif($line =~/^\^Annotation/) {
175             return "PLATFORM";
176             }
177             }
178            
179             return undef;
180             }
181              
182              
183             sub set_meta {
184              
185             my $self = shift;
186             my $arg = {'accession' => $self->accession,
187             'title' => $self->title,
188             'platform' => $self->platform,
189             @_};
190            
191             $self->{meta}->{"accession"} = $arg->{'accession'};
192             $self->{meta}->{"title"} = $arg->{'title'};
193             $self->{meta}->{"platform"} = $arg->{'platform'};
194            
195             return $self;
196             }
197              
198              
199             sub set_table {
200              
201             my $self = shift;
202             my $arg = {'rownames' => $self->rownames,
203             'colnames' => $self->colnames,
204             'colnames_explain' => $self->colnames_explain,
205             'matrix' => $self->matrix,
206             @_};
207            
208             $self->{table}->{"rownames"} = $arg->{'rownames'};
209             $self->{table}->{"colnames"} = $arg->{'colnames'};
210             $self->{table}->{"colnames_explain"} = $arg->{'colnames_explain'};
211             $self->{table}->{"matrix"} = $arg->{'matrix'};
212            
213             return $self;
214             }
215              
216             # download data from GEO ftp
217             # returns a list of filenames (array reference)
218             # in most circumstance, there is only one file at each series/platform/gds directory
219             # but still there is probability that multiple files locate in directory (especially for series matrix format)
220             # we only deal with one file situation
221             # for multiple files situation, users can downloaded manually
222             # and initial this object with file argument
223             sub download {
224              
225             my $self = shift;
226            
227             my $id = shift;
228            
229             _set_fh($self->{verbose});
230            
231             my %option = ( "proxy" => "", # proxy setting, only http, should be like "http://username:password@127.0.0.1:808/"
232             "timeout" => 30,
233             @_ );
234            
235             my $remote_file_list;
236             my $remote_file_name;
237             my $remote_file_size;
238             my $local_file;
239            
240             #$fh_out = _open_out_handle($self->{verbose});
241            
242             $ua = LWP::UserAgent->new;
243             $ua->timeout($option{timeout});
244            
245             if($option{proxy}) {
246             $ua->proxy(["http"], $option{proxy});
247             }
248            
249             my $url;
250            
251             # different geo data type
252             my $url_format = { "gse" => "ftp://ftp.ncbi.nih.gov/pub/geo/DATA/SOFT/by_series",
253             "gpl" => "ftp://ftp.ncbi.nih.gov/pub/geo/DATA/annotation/platforms",
254             "gds" => "ftp://ftp.ncbi.nih.gov/pub/geo/DATA/SOFT/GDS" };
255              
256             # format url based on different GEO id type
257             if($id =~/^gse\d+$/i) {
258             $url = "$url_format->{gse}/$id";
259             }
260             elsif($id =~/^gpl\d+$/i) {
261             $url = "$url_format->{gpl}";
262             }
263             elsif($id =~/^gds\d+$/i) {
264             $url = "$url_format->{gds}";
265             }
266             else {
267             croak "ERROR: GEO ID should look like 'GSE123', 'GPL123' and 'GDS123'";
268             }
269            
270             # if GSE or GPL
271             if($id =~/^gse\d+$/i) {
272            
273             # first get the file list in the directory
274             # because some GSE or GPL term would have more than one file
275             print "Reading dir from GEO FTP site:\n";
276             print " $url\n\n";
277             $response = $ua->get($url);
278            
279             unless($response->is_success) {
280             croak $response->status_line;
281             }
282            
283             my $content = $response->content;
284             @$remote_file_list = split "\n", $content;
285            
286             print "found ", scalar(@$remote_file_list), " file.\n";
287            
288             if(scalar(@$remote_file_list) > 1) {
289             croak "ERROR: There are more than one files in the remote directory and this ".
290             "situation has not been supported by ". __PACKAGE__." by this version. ".
291             "But still you can download them by hand.";
292             }
293             if(! scalar(@$remote_file_list)) {
294             croak "Can not find any file.";
295             }
296            
297             my @tmp = split " ", $remote_file_list->[0];
298             $remote_file_name = $tmp[$#tmp];
299             $remote_file_size = $tmp[4];
300             $local_file = $self->soft_dir."/$tmp[$#tmp]";
301            
302             print "remote file is: $remote_file_name\n";
303             print "\n";
304            
305             }
306             elsif($id =~/gpl\d+$/i) {
307            
308             print "Validating link from GEO FTP site:\n";
309             print " $url/$id.annot.gz\n";
310             $response = $ua->head("$url/$id.annot.gz");
311            
312             unless($response->is_success) {
313             croak $response->status_line;
314             }
315            
316             print "found $id.annot.gz on the server.\n\n";
317             $remote_file_name = "$id.annot.gz";
318             $remote_file_size = $response->header("content-length");
319             $local_file = $self->soft_dir."/$id.annot.gz";
320            
321             }
322             # if GDS
323             elsif($id =~/gds\d+$/i) {
324            
325             print "Validating link from GEO FTP site:\n";
326             print " $url/$id.soft.gz\n\n";
327             $response = $ua->head("$url/$id.soft.gz");
328            
329             unless($response->is_success) {
330             croak $response->status_line;
331             }
332            
333             print "found $id.soft.gz on the server.\n\n";
334             $remote_file_name = "$id.soft.gz";
335             $remote_file_size = $response->header("content-length");
336             $local_file = $self->soft_dir."/$id.soft.gz";
337            
338             }
339            
340             # whether there already has a file with the same name
341             while(-e $local_file) {
342             my $r = int(rand(100000));
343             if($remote_file_name =~/^(.*?)\.(\S+)$/) {
344             my $base = $1;
345             my $ext = $2;
346             $local_file = $self->soft_dir."/$base.$r.$ext";
347             }
348             else {
349             $local_file = $self->soft_dir."/$remote_file_name.$r";
350             }
351             }
352            
353             $url = "$url/$remote_file_name";
354             print "downloading $url\n";
355             print "file size: $remote_file_size byte.\n";
356             print "local file: $wd/$local_file\n\n";
357            
358             # begin to download
359             # if thread supported, progress would be shown
360             $response = undef;
361            
362             eval 'require Thread; require threads::shared';
363            
364             if($@) {
365             _download($url, $local_file);
366            
367             unless($response->is_success) {
368             croak $response->status_line;
369             }
370            
371             } else {
372             eval q`
373             my $response : shared;
374             $download_start_time = gettimeofday();
375             my $download_start_time : shared;
376             my $f1 = Thread->new(\&_download, $url, $local_file);
377             my $f2 = Thread->new(\&_progress, $local_file, $remote_file_size);
378            
379             $f1->join;
380             $f2->join;
381             `;
382             }
383            
384             $self->{file} = $local_file;
385            
386             _set_to_std_fh();
387            
388             return $self;
389             }
390              
391             sub _download {
392             my $url = shift;
393             my $local_file = shift;
394            
395             $response = $ua->get($url, ":content_file" => $local_file);
396             }
397              
398             sub _progress {
399             my $local_file = shift;
400             my $remote_file_size = shift;
401            
402             # still connecting
403             while(! -e $local_file) {
404             #print "$local_file does not exist, sleep $s_sleep_ms ms.\n";
405             usleep(500000);
406            
407             if($response) {
408             print "\n\n";
409             last;
410             }
411             }
412            
413             my $recieved_file_size = -s "$local_file";
414             my $i = 0;
415             my $bar = ["|", "\\", "-", "/"];
416             while($recieved_file_size != $remote_file_size) {
417             $recieved_file_size = -s "$local_file";
418             my $percentage = $recieved_file_size / $remote_file_size;
419             $percentage = sprintf("%.1f", $percentage * 100);
420             my $speed = $recieved_file_size / (gettimeofday - $download_start_time);
421             $speed = sprintf("%.2f", $speed / 1024); #KB/s
422             my $passed_time = (gettimeofday - $download_start_time);
423             $passed_time = int($passed_time);
424             print "\b" x 100;
425              
426             print "[", $bar->[$i % scalar(@$bar)], "]";
427             print " Recieving $recieved_file_size byte.\t$percentage\%\t$speed KB/s\t$passed_time"."s";
428             $i ++;
429            
430             usleep(500000);
431            
432             # if download is done
433             if($response) {
434             last;
435             }
436             }
437            
438             print "\n\n";
439              
440             }
441              
442              
443             sub _decompress {
444            
445             # 压缩文件
446             my $compressed_file = shift;
447            
448             my $null = $^O eq "MSWin32" ? "NUL" : "/dev/null";
449             eval("system('gzip --version > $null')") == 0
450             or croak "ERROR: Cannot find 'gzip'\n";
451              
452             # 压缩文件的文件名
453             my $basename = basename($compressed_file);
454            
455             print "decompress $compressed_file...\n";
456             my $command;
457            
458             # 获得解压缩文件的文件名
459             $command = "gzip -l \"$compressed_file\"";
460             my $status = `$command`;
461            
462             my @foo = split "\n", $status;
463             @foo = split " ", $foo[1];
464             my $uncompressed_file = $foo[$#foo];
465            
466             # 解压缩
467             $command = "gzip -cd \"$compressed_file\" > \"$uncompressed_file\"";
468            
469             system($command) == 0
470             or croak "ERROR: $!\n";
471            
472             # 返回解压后的文件名
473             return "$uncompressed_file";
474             }
475              
476              
477             __END__