3620fc5bd5fd12e3f4679c6e1fed85e332b37e94
[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-atv/)
14
15 require 'lib/evo/util/constants'
16 require 'lib/evo/util/util'
17 require 'lib/evo/util/command_line_arguments'
18
19 require 'date'
20
21 module Evoruby
22
23   class PhylogeniesDecorator
24
25     #DECORATOR_OPTIONS_SEQ_NAMES = '-r=1 -mdn'
26     DECORATOR_OPTIONS_SEQ_NAMES = '-p -t -sn'
27     # -mdn is a hidden expert option to rename e.g. "6_ORYLA3" to "6_[3]_ORYLA"
28     #DECORATOR_OPTIONS_SEQ_NAMES = '-sn -r=1'
29     #DECORATOR_OPTIONS_DOMAINS = '-r=1'
30     DECORATOR_OPTIONS_DOMAINS = '-p -t'
31     IDS_MAPFILE_SUFFIX        = '.nim'
32     DOMAINS_MAPFILE_SUFFIX    = '.dff'
33     SLEEP_TIME                = 0.1
34     REMOVE_NI                 = true
35     TMP_FILE                  = '___PD___'
36     LOG_FILE                  = '00_phylogenies_decorator.log'
37     FORESTER_HOME             = ENV[Constants::FORESTER_HOME_ENV_VARIABLE]
38     JAVA_HOME                 = ENV[Constants::JAVA_HOME_ENV_VARIABLE]
39
40     PRG_NAME       = "phylogenies_decorator"
41     PRG_DATE       = "2012.10.11"
42     PRG_DESC       = "decoration of phylogenies with sequence/species names and domain architectures"
43     PRG_VERSION    = "1.02"
44     COPYRIGHT      = "2012 Christian M Zmasek"
45     CONTACT        = "phylosoft@gmail.com"
46     WWW            = "www.phylosoft.org"
47
48     IDS_ONLY_OPTION     = "n"
49     DOMAINS_ONLY_OPTION = "d"
50     HELP_OPTION_1       = "help"
51     HELP_OPTION_2       = "h"
52
53     NL = Constants::LINE_DELIMITER
54
55     def run
56
57       Util.print_program_information( PRG_NAME,
58         PRG_VERSION,
59         PRG_DESC,
60         PRG_DATE,
61         COPYRIGHT,
62         CONTACT,
63         WWW,
64         STDOUT )
65
66       if ( ARGV == nil || ARGV.length > 3 || ARGV.length < 2  )
67         print_help
68         exit( -1 )
69       end
70
71       if FORESTER_HOME == nil || FORESTER_HOME.length < 1
72         Util.fatal_error( PRG_NAME, "apparently environment variable #{Constants::FORESTER_HOME_ENV_VARIABLE} has not been set" )
73       end
74       if JAVA_HOME == nil ||  JAVA_HOME.length < 1
75         Util.fatal_error( PRG_NAME, "apparently environment variable #{Constants::JAVA_HOME_ENV_VARIABLE} has not been set" )
76       end
77
78       if !File.exist?( FORESTER_HOME )
79         Util.fatal_error( PRG_NAME, '[' + FORESTER_HOME + '] does not exist' )
80       end
81       if !File.exist?( JAVA_HOME )
82         Util.fatal_error( PRG_NAME, '[' + JAVA_HOME + '] does not exist' )
83       end
84
85       decorator = JAVA_HOME + '/bin/java -cp ' + FORESTER_HOME + '/java/forester.jar org.forester.application.decorator'
86
87       begin
88         cla = CommandLineArguments.new( ARGV )
89       rescue ArgumentError => e
90         Util.fatal_error( PRG_NAME, "error: " + e.to_s )
91       end
92
93       if ( cla.is_option_set?( HELP_OPTION_1 ) ||
94            cla.is_option_set?( HELP_OPTION_2 ) )
95         print_help
96         exit( 0 )
97       end
98
99       if File.exist?( LOG_FILE )
100         Util.fatal_error( PRG_NAME, 'logfile [' + LOG_FILE + '] already exists' )
101       end
102
103       allowed_opts = Array.new
104       allowed_opts.push( IDS_ONLY_OPTION )
105       allowed_opts.push( DOMAINS_ONLY_OPTION )
106
107       disallowed = cla.validate_allowed_options_as_str( allowed_opts )
108       if ( disallowed.length > 0 )
109         Util.fatal_error( PRG_NAME, "unknown option(s): " + disallowed )
110       end
111
112       ids_only = false
113       domains_only = false
114
115       in_suffix = cla.get_file_name( 0 )
116       out_suffix = cla.get_file_name( 1 )
117
118       if cla.is_option_set?( IDS_ONLY_OPTION )
119         ids_only = true
120       end
121       if cla.is_option_set?( DOMAINS_ONLY_OPTION )
122         domains_only = true
123       end
124
125       if ( ids_only && domains_only )
126         Util.fatal_error( PRG_NAME, 'attempt to use ids only and domains only at the same time' )
127       end
128
129       log = String.new
130
131       now = DateTime.now
132       log << "Program              : " + PRG_NAME + NL
133       log << "Version              : " + PRG_VERSION + NL
134       log << "Program date         : " + PRG_DATE + NL
135       log << "Options for seq names: " + DECORATOR_OPTIONS_SEQ_NAMES + NL
136       log << "Options for domains  : " + DECORATOR_OPTIONS_DOMAINS + NL
137       log << "FORESTER_HOME        : " + FORESTER_HOME + NL
138       log << "JAVA_HOME            : " + JAVA_HOME + NL + NL
139       log << "Date/time: " + now.to_s + NL
140       log << "Directory: " + Dir.getwd  + NL + NL
141
142       Util.print_message( PRG_NAME, 'input suffix     : ' + in_suffix )
143       Util.print_message( PRG_NAME, 'output suffix    : ' + out_suffix )
144
145       log << 'input suffix     : ' + in_suffix + NL
146       log << 'output suffix    : ' + out_suffix + NL
147
148       if ( File.exists?( TMP_FILE ) )
149         File.delete( TMP_FILE )
150       end
151
152       files = Dir.entries( "." )
153
154       counter = 0
155
156       files.each { | phylogeny_file |
157         if ( !File.directory?( phylogeny_file ) &&
158              phylogeny_file !~ /^\./ &&
159              phylogeny_file !~ /^00/ &&
160              phylogeny_file !~ /#{out_suffix}$/ &&
161              phylogeny_file =~ /#{in_suffix}$/ )
162           begin
163             Util.check_file_for_readability( phylogeny_file )
164           rescue ArgumentError
165             Util.fatal_error( PRG_NAME, 'can not read from: ' + phylogeny_file + ': '+ $! )
166           end
167
168           counter += 1
169
170           outfile = phylogeny_file.sub( /#{in_suffix}$/, out_suffix )
171
172           if REMOVE_NI
173             outfile = outfile.sub( /_ni_/, '_' )
174           end
175
176           if File.exists?( outfile )
177             msg = counter.to_s + ': ' + phylogeny_file + ' -> ' +  outfile +
178              ' : already exists, skipping'
179             Util.print_message( PRG_NAME, msg  )
180             log << msg + NL
181             next
182           end
183
184           Util.print_message( PRG_NAME, counter.to_s + ': ' + phylogeny_file + ' -> ' +  outfile )
185           log << counter.to_s + ': ' + phylogeny_file + ' -> ' +  outfile + NL
186
187           phylogeny_id = get_id( phylogeny_file )
188
189           ids_mapfile_name = nil
190           domains_mapfile_name = nil
191
192           if ids_only
193             ids_mapfile_name = get_file( files, phylogeny_id, IDS_MAPFILE_SUFFIX )
194           elsif domains_only
195             domains_mapfile_name = get_file( files, phylogeny_id, DOMAINS_MAPFILE_SUFFIX )
196           else
197             ids_mapfile_name = get_file( files, phylogeny_id, IDS_MAPFILE_SUFFIX )
198             domains_mapfile_name = get_file( files, phylogeny_id, DOMAINS_MAPFILE_SUFFIX )
199           end
200
201           if domains_mapfile_name != nil
202             begin
203               Util.check_file_for_readability( domains_mapfile_name )
204             rescue ArgumentError
205               Util.fatal_error( PRG_NAME, 'failed to read from [#{domains_mapfile_name}]: ' + $! )
206             end
207           end
208
209           if ids_mapfile_name != nil
210             begin
211               Util.check_file_for_readability( ids_mapfile_name )
212             rescue ArgumentError
213               Util.fatal_error( PRG_NAME, 'failed to read from [#{ids_mapfile_name}]: ' + $! )
214             end
215           end
216
217           if domains_mapfile_name != nil
218             if ids_mapfile_name != nil
219               my_outfile = TMP_FILE
220             else
221               my_outfile = outfile
222             end
223             cmd = decorator + ' ' + DECORATOR_OPTIONS_DOMAINS + ' ' +
224              '-f=d ' + phylogeny_file + ' ' +
225              domains_mapfile_name + ' ' + my_outfile
226             execute_cmd( cmd, log )
227           end
228
229           if ids_mapfile_name != nil
230             if domains_mapfile_name != nil
231               my_infile = TMP_FILE
232             else
233               my_infile = phylogeny_file
234             end
235             cmd = decorator + ' ' +  DECORATOR_OPTIONS_SEQ_NAMES + ' ' +
236              '-f=s ' + my_infile + ' ' +
237              ids_mapfile_name + ' ' + outfile
238             execute_cmd( cmd, log )
239           end
240
241           if ( File.exists?( TMP_FILE ) )
242             File.delete( TMP_FILE )
243           end
244         end
245       }
246       open( LOG_FILE, 'w' ) do | f |
247         f.write( log )
248       end
249       puts
250       Util.print_message( PRG_NAME, 'OK' )
251       puts
252     end # def run
253
254     def execute_cmd( cmd, log )
255       log << 'excuting ' + cmd + NL
256       IO.popen( cmd , 'r+' ) do | pipe |
257         pipe.close_write
258         log << pipe.read + NL + NL
259       end
260       sleep( SLEEP_TIME )
261     end
262
263
264     def get_id( phylogeny_file_name )
265       phylogeny_file_name =~ /^([^_]+)/
266       $1
267     end
268
269     def get_file( files_in_dir, phylogeny_id, suffix_pattern )
270       matching_files = Array.new
271       files_in_dir.each { | file |
272
273         if ( !File.directory?( file ) &&
274              file !~ /^\./ &&
275              file !~ /^00/ &&
276              file =~ /^#{phylogeny_id}.*#{suffix_pattern}$/ )
277           matching_files << file
278         end
279       }
280       if matching_files.length < 1
281         Util.fatal_error( PRG_NAME, 'no file matching [' + phylogeny_id +
282            '_] [' + suffix_pattern + '] present in current directory' )
283       elsif matching_files.length > 1
284         Util.fatal_error( PRG_NAME, 'more than one file matching [' + phylogeny_id +
285            '_] [' + suffix_pattern + '] present in current directory' )
286       end
287       matching_files[ 0 ]
288     end
289
290     def print_help()
291       puts( "Usage:" )
292       puts()
293       puts( "  " + PRG_NAME + ".rb [options] <suffix of intrees to be decorated> <suffix for decorated outtrees> " )
294       puts()
295       puts( "  options: -" + IDS_ONLY_OPTION + ": decorate with sequence/species names only" )
296       puts( "           -" + DOMAINS_ONLY_OPTION + ": decorate with domain structures" )
297       puts()
298     end
299   end # class PhylogenyiesDecorator
300
301 end # module Evoruby