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