in progress
[jalview.git] / forester / ruby / evoruby / lib / evo / tool / phylogenies_decorator.rb
1 #!/usr/local/bin/ruby -w
2 #
3 # = lib/evo/apps/phylogenies_decorator
4 #
5 # Copyright::  Copyright (C) 2006-2008 Christian M. Zmasek
6 # License::    GNU Lesser General Public License (LGPL)
7 #
8 # decoration of phylogenies with sequence/species names and domain architectures
9 #
10 # $Id: phylogenies_decorator.rb,v 1.34 2010/12/13 19:00:11 cmzmasek Exp $
11 #
12 # Environment variable FORESTER_HOME needs to point to the appropriate
13 # directory (e.g. setenv FORESTER_HOME $HOME/SOFTWARE_DEV/ECLIPSE_WORKSPACE/forester/)
14
15 require 'lib/evo/util/constants'
16 require 'lib/evo/util/util'
17 require 'lib/evo/util/command_line_arguments'
18 require 'date'
19
20 module Evoruby
21   class PhylogeniesDecorator
22
23     #DECORATOR_OPTIONS_SEQ_NAMES = '-r=1 -mdn'
24     #DECORATOR_OPTIONS_SEQ_NAMES = '-p -t -sn'
25     DECORATOR_OPTIONS_SEQ_NAMES = '-p -t -tc -mp -or'
26     # -mdn is a hidden expert option to rename e.g. "6_ORYLA3" to "6_[3]_ORYLA"
27     #DECORATOR_OPTIONS_SEQ_NAMES = '-sn -r=1'
28     #DECORATOR_OPTIONS_DOMAINS = '-r=1'
29     DECORATOR_OPTIONS_DOMAINS = '-p -t'
30     IDS_MAPFILE_SUFFIX        = '.nim'
31     DOMAINS_MAPFILE_SUFFIX    = '_hmmscan_10.dff'
32     SLEEP_TIME                = 0.05
33     REMOVE_NI                 = true
34     IDS_ONLY                  = true
35     TMP_FILE_1                  = '___PD1___'
36     TMP_FILE_2                  = '___PD2___'
37     LOG_FILE                  = '00_phylogenies_decorator.log'
38     FORESTER_HOME             = ENV[Constants::FORESTER_HOME_ENV_VARIABLE]
39     JAVA_HOME                 = ENV[Constants::JAVA_HOME_ENV_VARIABLE]
40
41     PRG_NAME       = "phylogenies_decorator"
42     PRG_DATE       = "2013.11.15"
43     PRG_DESC       = "decoration of phylogenies with sequence/species names and domain architectures"
44     PRG_VERSION    = "1.02"
45     COPYRIGHT      = "2013 Christian M Zmasek"
46     CONTACT        = "phylosoft@gmail.com"
47     WWW            = "https://sites.google.com/site/cmzmasek/home/software/forester"
48
49     HELP_OPTION_1       = "help"
50     HELP_OPTION_2       = "h"
51
52     NL = Constants::LINE_DELIMITER
53     def run
54
55       Util.print_program_information( PRG_NAME,
56       PRG_VERSION,
57       PRG_DESC,
58       PRG_DATE,
59       COPYRIGHT,
60       CONTACT,
61       WWW,
62       STDOUT )
63
64       if ( ARGV == nil || ARGV.length > 3 || ARGV.length < 2  )
65         print_help
66         exit( -1 )
67       end
68
69       if FORESTER_HOME == nil || FORESTER_HOME.length < 1
70         Util.fatal_error( PRG_NAME, "apparently environment variable #{Constants::FORESTER_HOME_ENV_VARIABLE} has not been set" )
71       end
72       if JAVA_HOME == nil ||  JAVA_HOME.length < 1
73         Util.fatal_error( PRG_NAME, "apparently environment variable #{Constants::JAVA_HOME_ENV_VARIABLE} has not been set" )
74       end
75
76       if !File.exist?( FORESTER_HOME )
77         Util.fatal_error( PRG_NAME, '[' + FORESTER_HOME + '] does not exist' )
78       end
79       if !File.exist?( JAVA_HOME )
80         Util.fatal_error( PRG_NAME, '[' + JAVA_HOME + '] does not exist' )
81       end
82
83       decorator = JAVA_HOME + '/bin/java -cp ' + FORESTER_HOME + '/java/forester.jar org.forester.application.decorator'
84
85       begin
86         cla = CommandLineArguments.new( ARGV )
87       rescue ArgumentError => e
88         Util.fatal_error( PRG_NAME, "error: " + e.to_s )
89       end
90
91       if ( cla.is_option_set?( HELP_OPTION_1 ) ||
92       cla.is_option_set?( HELP_OPTION_2 ) )
93         print_help
94         exit( 0 )
95       end
96
97       if File.exist?( LOG_FILE )
98         Util.fatal_error( PRG_NAME, 'logfile [' + LOG_FILE + '] already exists' )
99       end
100
101       in_suffix = cla.get_file_name( 0 )
102       out_suffix = cla.get_file_name( 1 )
103
104       log = String.new
105
106       now = DateTime.now
107       log << "Program              : " + PRG_NAME + NL
108       log << "Version              : " + PRG_VERSION + NL
109       log << "Program date         : " + PRG_DATE + NL
110       log << "Options for seq names: " + DECORATOR_OPTIONS_SEQ_NAMES + NL
111       log << "Options for domains  : " + DECORATOR_OPTIONS_DOMAINS + NL
112       log << "FORESTER_HOME        : " + FORESTER_HOME + NL
113       log << "JAVA_HOME            : " + JAVA_HOME + NL + NL
114       log << "Date/time: " + now.to_s + NL
115       log << "Directory: " + Dir.getwd  + NL + NL
116
117       Util.print_message( PRG_NAME, 'input suffix     : ' + in_suffix )
118       Util.print_message( PRG_NAME, 'output suffix    : ' + out_suffix )
119
120       log << 'input suffix     : ' + in_suffix + NL
121       log << 'output suffix    : ' + out_suffix + NL
122
123       if ( File.exists?( TMP_FILE_1 ) )
124         File.delete( TMP_FILE_1 )
125       end
126       if ( File.exists?( TMP_FILE_2 ) )
127         File.delete( TMP_FILE_2 )
128       end
129
130       files = Dir.entries( "." )
131
132       counter = 0
133
134       files.each { | phylogeny_file |
135         if ( !File.directory?( phylogeny_file ) &&
136         phylogeny_file !~ /^\./ &&
137         phylogeny_file !~ /^00/ &&
138         phylogeny_file !~ /#{out_suffix}$/ &&
139         phylogeny_file =~ /#{in_suffix}$/ )
140           begin
141             Util.check_file_for_readability( phylogeny_file )
142           rescue ArgumentError
143             Util.fatal_error( PRG_NAME, 'can not read from: ' + phylogeny_file + ': '+ $! )
144           end
145
146           counter += 1
147
148           outfile = phylogeny_file.sub( /#{in_suffix}$/, out_suffix )
149
150           if REMOVE_NI
151             outfile = outfile.sub( /_ni_/, '_' )
152           end
153
154           if File.exists?( outfile )
155             msg = counter.to_s + ': ' + phylogeny_file + ' -> ' +  outfile +
156             ' : already exists, skipping'
157             Util.print_message( PRG_NAME, msg  )
158             log << msg + NL
159             next
160           end
161
162           Util.print_message( PRG_NAME, counter.to_s + ': ' + phylogeny_file + ' -> ' +  outfile )
163           log << counter.to_s + ': ' + phylogeny_file + ' -> ' +  outfile + NL
164
165           phylogeny_id = get_id( phylogeny_file )
166           if  phylogeny_id == nil || phylogeny_id.size < 1
167             Util.fatal_error( PRG_NAME, 'could not get id from ' + phylogeny_file.to_s )
168           end
169           puts
170           Util.print_message( PRG_NAME, "id: " + phylogeny_id )
171           log << "id: " + phylogeny_id + NL
172
173           ids_mapfile_name = nil
174           domains_mapfile_name = nil
175           seqs_file_name = nil
176
177           ids_mapfile_name = get_file( files, phylogeny_id, IDS_MAPFILE_SUFFIX )
178
179           unless IDS_ONLY
180             domains_mapfile_name = get_file( files, phylogeny_id, DOMAINS_MAPFILE_SUFFIX )
181             seqs_file_name = get_seq_file( files, phylogeny_id )
182           end
183
184           unless IDS_ONLY
185             begin
186               Util.check_file_for_readability( domains_mapfile_name )
187             rescue ArgumentError
188               Util.fatal_error( PRG_NAME, 'failed to read from [#{domains_mapfile_name}]: ' + $! )
189             end
190             begin
191               Util.check_file_for_readability( seqs_file_name  )
192             rescue ArgumentError
193               Util.fatal_error( PRG_NAME, 'failed to read from [#{seqs_file_name }]: ' + $! )
194             end
195           end
196
197           begin
198             Util.check_file_for_readability( ids_mapfile_name )
199           rescue ArgumentError
200             Util.fatal_error( PRG_NAME, 'failed to read from [#{ids_mapfile_name}]: ' + $! )
201           end
202
203           unless IDS_ONLY
204             cmd = decorator +
205             ' -t -p -f=m ' + phylogeny_file + ' ' +
206             seqs_file_name  + ' ' + TMP_FILE_1
207             puts cmd
208             begin
209               execute_cmd( cmd, log )
210             rescue Error
211               Util.fatal_error( PRG_NAME, 'error: ' + $! )
212             end
213
214             cmd = decorator + ' ' + DECORATOR_OPTIONS_DOMAINS + ' ' +
215             '-f=d ' + TMP_FILE_1 + ' ' +
216             domains_mapfile_name + ' ' +TMP_FILE_2
217             puts cmd
218             begin
219               execute_cmd( cmd, log )
220             rescue Error
221               Util.fatal_error( PRG_NAME, 'error: ' + $! )
222             end
223           end
224
225           if IDS_ONLY
226             cmd = decorator + ' ' +  DECORATOR_OPTIONS_SEQ_NAMES + ' ' +
227             '-f=n ' + phylogeny_file + ' ' +
228             ids_mapfile_name + ' ' + outfile
229             puts cmd
230             begin
231               execute_cmd( cmd, log )
232             rescue Error
233               Util.fatal_error( PRG_NAME, 'error: ' + $! )
234             end
235           else
236             cmd = decorator + ' ' +  DECORATOR_OPTIONS_SEQ_NAMES + ' ' +
237             '-f=n ' + TMP_FILE_2 + ' ' +
238             ids_mapfile_name + ' ' + outfile
239             puts cmd
240             begin
241               execute_cmd( cmd, log )
242             rescue Error
243               Util.fatal_error( PRG_NAME, 'error: ' + $! )
244             end
245             File.delete( TMP_FILE_1 )
246             File.delete( TMP_FILE_2 )
247           end
248         end
249       }
250       open( LOG_FILE, 'w' ) do | f |
251         f.write( log )
252       end
253       puts
254       Util.print_message( PRG_NAME, 'OK' )
255       puts
256     end # def run
257
258     def execute_cmd( cmd, log )
259       log << 'executing ' + cmd + NL
260       IO.popen( cmd , 'r+' ) do | pipe |
261         pipe.close_write
262         log << pipe.read + NL + NL
263       end
264       sleep( SLEEP_TIME )
265     end
266
267     def get_id( phylogeny_file_name )
268       if phylogeny_file_name =~ /^(.+?_.+?)_/
269         return $1
270       elsif phylogeny_file_name =~ /^(.+?)__/
271         return $1
272       elsif phylogeny_file_name =~ /^(.+?)_/
273         return $1
274       end
275       nil
276     end
277
278     def get_file( files_in_dir, phylogeny_id, suffix_pattern )
279       matching_files = Array.new
280
281       files_in_dir.each { | file |
282
283         if ( !File.directory?( file ) &&
284         file !~ /^\./ &&
285         file !~ /^00/ &&
286         file =~ /^#{phylogeny_id}.*#{suffix_pattern}$/ )
287           matching_files << file
288         end
289       }
290       if matching_files.length < 1
291         Util.fatal_error( PRG_NAME, 'no file matching [' + phylogeny_id +
292         '...' + suffix_pattern + '] present in current directory' )
293       end
294       if matching_files.length > 1
295         Util.fatal_error( PRG_NAME, 'more than one file matching [' +
296         phylogeny_id  + '...' + suffix_pattern + '] present in current directory' )
297       end
298       matching_files[ 0 ]
299     end
300
301     def get_seq_file( files_in_dir, phylogeny_id )
302       matching_files = Array.new
303
304       files_in_dir.each { | file |
305
306         if ( !File.directory?( file ) &&
307         file !~ /^\./ &&
308         file !~ /^00/ &&
309         ( file =~ /^#{phylogeny_id}__.+\d$/ || file =~ /^#{phylogeny_id}_.*\.fasta$/ ) )
310           matching_files << file
311         end
312       }
313
314       if matching_files.length < 1
315         Util.fatal_error( PRG_NAME, 'no seq file matching [' +
316         phylogeny_id + '_] present in current directory' )
317       end
318       if matching_files.length > 1
319         Util.fatal_error( PRG_NAME, 'more than one seq file matching [' +
320         phylogeny_id + '_] present in current directory' )
321       end
322       matching_files[ 0 ]
323     end
324
325     def print_help()
326       puts( "Usage:" )
327       puts()
328       puts( "  " + PRG_NAME + ".rb <suffix of intrees to be decorated> <suffix for decorated outtrees> " )
329       puts()
330       puts()
331     end
332   end # class PhylogenyiesDecorator
333
334 end # module Evoruby