]> git.donarmstrong.com Git - function2gene.git/blob - bin/do_it_all
3d3cfa3effb42640cbe917f9ebb41da3fbcad279
[function2gene.git] / bin / do_it_all
1 #! /usr/bin/perl
2 # do_it_all, is part of the gene search suite and is released
3 # under the terms of the GPL version 2, or any later version, at your
4 # option. See the file README and COPYING for more information.
5 # Copyright 2007 by Don Armstrong <don@donarmstrong.com>.
6 # $Id: perl_script 495 2006-08-10 08:02:01Z don $
7
8
9 use warnings;
10 use strict;
11
12 use Getopt::Long;
13 use Pod::Usage;
14
15 use Storable;
16
17 =head1 NAME
18
19 do_it_all - Call out to each of the search modules to search for each
20 of the terms
21
22 =head1 SYNOPSIS
23
24  do_it_all --keywords keywords.txt --results gene_search_results
25
26  Options:
27   --keywords newline delineated list of keywords to search for
28   --results directory to store results in
29   --database databases to search
30   --restart-at mode to start searching at
31   --debug, -d debugging level (Default 0)
32   --help, -h display this help
33   --man, -m display manual
34
35 =head1 OPTIONS
36
37 =over
38
39 =item B<--keywords>
40
41 A file which contains a newline delinated list of keywords to search
42 for. Can be specified multiple times. Lines starting with # or ; are
43 ignored.
44
45 =item B<--results>
46
47 Directory in which to store results; also stores the current state of
48 the system
49
50 =item B<--database>
51
52 Databases to search, can be specified multiple times. [Defaults to
53 NCBI, GeneCards and Harvester, the only currently supported
54 databases.]
55
56 =item B<--restart-at>
57
58 If you need to restart the process at a particular state (which has
59 already been completed) specify this option.
60
61 =item B<--debug, -d>
62
63 Debug verbosity. (Default 0)
64
65 =item B<--help, -h>
66
67 Display brief useage information.
68
69 =item B<--man, -m>
70
71 Display this manual.
72
73 =back
74
75 =head1 EXAMPLES
76
77
78 =cut
79
80
81 use vars qw($DEBUG);
82 use Cwd qw(abs_path);
83 use IO::File;
84 use Storable qw(thaw freeze);
85
86 my %options = (databases       => [],
87                keywords        => [],
88                debug           => 0,
89                help            => 0,
90                man             => 0,
91                results         => '',
92                );
93
94 GetOptions(\%options,'keywords=s@','databases=s@',
95            'restart_at|restart-at=s','results=s',
96            'debug|d+','help|h|?','man|m');
97
98 pod2usage() if $options{help};
99 pod2usage({verbose=>2}) if $options{man};
100
101 my $ERRORS='';
102
103 $ERRORS.="restart-at must be one of get, parse or combine\n" if
104      exists $options{restart_at} and $options{restart_at} !~ /^(?:get|parse|combine)$/;
105
106 $ERRORS.="unknown database(s)" if
107      @{$options{databases}} and
108      grep {$_ !~ /^(?:ncbi|genecards|harvester)$/i} @{$options{databases}};
109
110 if (not length $options{results}) {
111      $ERRORS.="results directory not specified";
112 }
113 elsif (not -d $options{results} or not -w $options{results}) {
114      $ERRORS.="results directory $options{results} does not exist or is not writeable";
115 }
116
117 pod2usage($ERRORS) if length $ERRORS;
118
119 if (not @{$options{databases}}) {
120      $options{databases} = [qw(ncbi genecards harvester)]
121 }
122
123 $DEBUG = $options{debug};
124
125 # There are three states for our engine
126 # Getting results
127 # Parsing them
128 # Combining results
129
130 # first, check to see if the state in the result directory exists
131
132 my %state;
133
134 $options{keywords} = [map {abs_path($_)} @{$options{keywords}}];
135
136 chdir $options{results} or die "Unable to chdir to $options{results}";
137
138 if (-e "do_it_all_state") {
139      ADVISE("Using existing state information");
140      my $state_fh = IO::File->new("do_it_all_state",'r') or die
141           "Unable to open state file for reading: $!";
142      local $/;
143      my $state_file = <$state_fh>;
144      %state = %{thaw($state_file)} or die "Unable to thaw state file";
145 }
146 else {
147      ADVISE("Starting new run");
148      %state = (keywords => [],
149                databases => [map {lc($_)} @{$options{databases}}],
150                done_keywords => {
151                                  get => {},
152                                  parse => {},
153                                  combine => {},
154                                 },
155               );
156 }
157
158 my @new_keywords;
159 if (@{$options{keywords}}) {
160      # uniqify keywords
161      my %old_keywords;
162      @old_keywords{@{$state{keywords}}} = (1) x @{$state{keywords}};
163      for my $keyword_file (@{$options{keywords}}) {
164           my $keyword_fh = IO::File->new($keyword_file,'r') or die
165                "Unable to open $keyword_file for reading: $!";
166           local $/;
167           while (<$keyword_fh>) {
168                next if /^\s*[#;]/;
169                chomp;
170                if (not $old_keywords{$_}) {
171                     DEBUG("Adding new keyword '$_'");
172                     push @new_keywords, $_;
173                }
174                else {
175                     DEBUG("Not adding duplicate keyword '$_'");
176                }
177           }
178      }
179 }
180
181 if (exists $options{restart_at} and length $options{restart_at}) {
182      if (lc($options{restart_at}) eq 'get') {
183           delete $state{gotten_keywords};
184           delete $state{parsed_keywords};
185           delete $state{combined_keywords};
186      }
187      elsif (lc($options{restart_at}) eq 'parse') {
188           delete $state{parsed_keywords};
189           delete $state{combined_keywords};
190      }
191      elsif (lc($options{restart_at}) eq 'combine') {
192           delete $state{combined_keywords};
193      }
194 }
195
196 # now we need to figure out what has to happen
197 # for each keyword, we check to see if we've got results, parsed
198 # results, and combined it. If not, we queue up those actions.
199
200 my %actions = (combine => 0,
201                get     => {},
202                parse   => {},
203               );
204
205 if (not @{$state{keywords}}) {
206      ADVISE("There are no keywords specified");
207 }
208
209 for my $keyword (@{$state{keywords}}) {
210      for my $database (@{$state{databases}}) {
211           if (not exists $state{done_keywords}{get}{$database}{$keyword}) {
212                push @{$actions{get}{$database}}, $keyword;
213                delete $state{done_keywords}{parse}{$database}{$keyword} if
214                     exists $state{done_keywords}{parse}{$database}{$keyword};
215                delete $state{done_keywords}{combine}{$database}{$keyword} if
216                     exists $state{done_keywords}{combine}{$database}{$keyword};
217           }
218           if (not exists $state{done_keywords}{parse}{$database}{$keyword}) {
219                push @{$actions{parse}{$database}},$keyword;
220        delete $state{done_keywords}{combine}{$database}{$keyword} if
221                     exists $state{done_keywords}{combine}{$database}{$keyword};
222           }
223           if (not exists $state{done_keywords}{combine}{$database}{$keyword}) {
224               $actions{combine} = 1;
225           }
226      }
227 }
228
229 use threads;
230 use Thread::Queue;
231
232 for my $state (qw(get parse)) {
233      my %databases;
234      for my $database (keys %{$actions{$state}}) {
235           next unless @{$actions{$state}{$database}};
236           $databases{$database}{queue} = Thread::Queue->new;
237           $databases{$database}{thread} = threads->new(\&handle_action($state,$database,$databases{database}{queue}));
238           $databases{$database}{queue}->enqueue(@{$actions{$state}{$database}});
239           $databases{$database}{queue}->enqueue(undef);
240      }
241      my $ERRORS=0;
242      for my $database (keys %databases) {
243           my ($actioned_keywords,$failed_keywords) = $databases{$database}{thread}->join;
244           if (@{$failed_keywords}) {
245                ADVISE("These keywords failed during '$state' of '$database':",@{$failed_keywords});
246                $ERRORS=1;
247           }
248           @{$state{done_keywords}{$state}{$database}}{@{$actioned_keywords}} = (1) x @{$actioned_keywords};
249           delete @{$state{done_keywords}{$state}{$database}}{@{$failed_keywords}};
250      }
251      save_state(\%state);
252      if ($ERRORS) {
253           WARN("Stoping, as there are errors");
254           exit 1;
255      }
256 }
257
258 sub handle_action{
259      my ($state,$database,$queue) = @_;
260      my $keyword;
261      my $actioned_keywords = ();
262      my $failed_keywords = ();
263      while ($keyword = $queue->dequeue) {
264           # handle the action, baybee
265           ADVISE("$state results from '$database' for '$keyword'");
266           push @{$actioned_keywords},$keyword;
267      }
268      return ($actioned_keywords,$failed_keywords);
269 }
270
271 sub save_state{
272      my ($state) = @_;
273      my $state_fh = IO::File->new("do_it_all_state",'w') or die
274           "Unable to open state file for writing: $!";
275      print {$state_fh} freeze($state) or die "Unable to freeze state file";
276      close $state_fh or die "Unable to close state file: $!";
277 }
278
279
280 sub ADVISE{
281      print STDOUT map {($_,qq(\n))} @_;
282 }
283
284 sub DEBUG{
285      print STDERR map {($_,qq(\n))} @_;
286 }
287
288
289 sub WARN {
290      print STDERR map {($_,qq(\n))} @_;
291 }
292
293 __END__