root/galaxy-taverna/workflow-to-galaxy/lib/workflow-to-galaxy/generator.rb @ 78

Revision 78, 9.8 KB (checked in by kostas.karasavvas@…, 3 years ago)

added chomp_last_newline and removes indendation from description in xml file

Line 
1
2# Contains code to generate the Galaxy's tool xml and script files
3module Generator
4
5
6  # private methods
7  private
8
9
10  def indent(n)
11    ind = ""
12    n.times { ind += "  "  }
13    ind
14  end
15
16
17  def tool_b(out, name)
18    out.write("<tool id=\"#{name}_id\" name=\"#{name}\">\n")
19  end
20
21  def command_be(out, me_rest, script)
22    out.write indent(1) + "<command interpreter=\"ruby\">\n"
23    out.write indent(2) + script + "\n"
24    me_rest.workflow.inputs.each do |i|
25      out.write indent(2) + "#if $#{i.name}_source.history_or_textfield == \"textfield\":\n"
26      out.write indent(3) + "false \"$#{i.name}_source.textfield_#{i.name}\"\n"
27      out.write indent(2) + "#else:\n"
28      out.write indent(3) + "true  \"$#{i.name}_source.history_#{i.name}\"\n"
29      out.write indent(2) + "#end if\n"
30    end
31    out.write indent(2)
32    me_rest.workflow.outputs.each do |o|
33      out.write "$#{o.name} "
34    end
35    out.write "\n"
36    out.write indent(1) + "</command>\n"
37  end
38
39  def inputs_be(out, inputs)
40    out.write indent(1) + "<inputs>\n"
41    if inputs.size >= 1
42      inputs.each do |i|
43        out.write indent(2) + "<conditional name=\"#{i.name}_source\">\n"
44        out.write indent(3) + "<param name=\"history_or_textfield\" type=\"select\" label=\"Select source for #{i.name}\">\n"
45        out.write indent(4) + "<option value=\"history\">From history</option>\n"
46        out.write indent(4) + "<option value=\"textfield\" selected=\"true\">Type manually</option>\n"
47        out.write indent(3) + "</param>\n"
48        out.write indent(3) + "<when value=\"history\">\n"
49        out.write indent(4) + "<param name=\"history_#{i.name}\" type=\"data\" label=\"Select #{i.name}\"/>\n"
50        out.write indent(3) + "</when>\n"
51        out.write indent(3) + "<when value=\"textfield\">\n"
52        out.write indent(4) + "<param name=\"textfield_#{i.name}\" type=\"text\" size=\"30\" "
53        if i.examples.size >= 1 
54          # escape double quotes characters for galaxy's xml file
55          ex = i.examples[0].to_s.gsub('"', '&quot;')
56          out.write "value=\"#{ex}\" "         
57        end
58        out.write "label=\"Enter #{i.name}\"/>\n"
59        out.write indent(3) + "</when>\n"
60        out.write indent(2) + "</conditional>\n"
61      end
62    else
63      out.write indent(2) + "<param name=\"input\" type=\"select\" display=\"radio\" size=\"250\" label=\"This workflow has no inputs\" />\n"
64    end 
65    out.write indent(1) + "</inputs>\n"
66  end
67
68  def outputs_be(out, outputs)
69    out.write indent(1) + "<outputs>\n"
70    outputs.each do |o|
71      out.write indent(2) + "<data format=\"tabular\" name=\"#{o.name}\" label=\"#{o.name}\"/>\n"
72    end
73    out.write indent(1) + "</outputs>\n"
74  end
75
76  def help_be(out, me_rest)
77    out.write indent(1) + "<help>\n"
78    out.write "**What it does**\n\n"
79
80    description = me_rest.workflow.description + "\n\n"
81
82    # Sometimes the workflow description contains HTML tags that are not allowed
83    # in Galaxy's xml interface specification and thus are removed! Same for
84    # HTML entities!
85    # TODO go through tags and find Galaxy's equivalent to include
86    description.gsub!(/<.*?>|&.*?;/, '')
87
88    # To remove ^M (cntl-v + cntl-m) characters that DOS files might have
89    description.gsub!(/\r/, '')
90   
91    # TODO that works as a literal too but font changes to courier!
92    #out.write "::\n\n"   # Start Galaxy's literal block to ignore indendation
93
94    # remove indendation from all description lines since Galaxy is confused by it
95    description.split(/[\n]/).each { |l| out.write "#{l.gsub(/^\s+/, '')}\n" }
96
97    # endline makes the following be parsed as a Galaxy GUI construct
98    out.write "\n"       
99
100    if me_rest.workflow.inputs.size >= 1
101      out.write "-----\n\n"
102      out.write "**Inputs**\n\n"
103      me_rest.workflow.inputs.each do |i|
104        out.write "- **#{i.name}** "
105        if i.descriptions.size >= 1
106          i.descriptions.each do |desc|
107            out.write desc.to_s + " "
108          end
109        end
110        if i.examples.size >= 1
111          out.write "Examples include:\n\n"
112          i.examples.each do |ex|
113            # some examples have a newline between them that breaks Galaxy's GUI
114            # so we remove it
115            out.write "  - " + ex.to_s.gsub(/[\n]/, ' ') + "\n"
116          end
117        end
118        out.write "\n"
119      end
120      out.write "\n"
121    end
122
123    # TODO this code is identical to the inputs code above -- method?
124    if me_rest.workflow.outputs.size >= 1
125      out.write "-----\n\n"
126      out.write "**Outputs**\n\n"
127      me_rest.workflow.outputs.each do |o|
128        out.write "- **#{o.name}** "
129        if o.descriptions.size >= 1
130          o.descriptions.each do |desc|
131            out.write desc.to_s + " "
132          end
133        end
134        if o.examples.size >= 1
135          out.write "Examples include:\n\n"
136          o.examples.each do |ex|
137            out.write "  - " + ex.to_s + "\n"
138          end
139        end
140        out.write "\n"
141      end
142      out.write "\n"
143    end
144
145    out.write "-----\n\n"
146    out.write ".. class:: warningmark\n\n"
147    out.write "**Please note that some workflows are not up-to-date or have dependencies** " <<
148              "that cannot be met by the specific Taverna server that you specified during " <<
149              "generation of this tool. You can make sure that the workflow is valid " <<
150              "by running it in the Taverna Workbench first to confirm that it works " <<
151              "before running it via Galaxy.\n\n"
152
153
154    out.write "-----\n\n"
155    out.write ".. class:: infomark\n\n"
156    out.write "**For more information on that workflow please visit** #{me_rest.uri.gsub(/(.*workflows\/\d+)[\/.].*/, '\1')}.\n\n"
157
158    out.write indent(1) + "</help>\n"
159  end
160
161  def tool_e(out)
162    out.write("</tool>\n")
163  end
164
165
166
167
168  def script_preample(out)
169    out.write("#!/usr/bin/env ruby\n\n")
170    out.write("require 'rubygems'\n")
171    out.write("require 't2-server'\n")
172    out.write("require 'open-uri'\n\n")
173  end
174
175  def script_util_methods(out)
176
177    out.write <<UTIL_METHODS
178   
179# method that flattens the list of list of list ... result of get_output
180def print_flattened_result(out, data_lists)
181  data_lists.each do |l|
182    if l.instance_of? Array
183      print_flattened_result(out, l)
184    else
185      out.puts l
186    end
187  end
188end
189
190
191# method that acquires all the results of the specified output
192def get_outputs(run, refs, outfile, dir)
193  data_lists = run.get_output(dir, refs)
194  print_flattened_result(outfile, data_lists)
195end
196
197
198#
199# Sanitize single and double quotes in str. E.g. galaxy substitutes them to
200# __sq__ and __dq__ respectively. This methods turns them back to their
201# original values before using them
202#
203def sanitize(string)
204  string.gsub(/(__sq__|__dq__|__at__)/) do
205    if $1 == '__sq__'
206      "'"
207    elsif $1 == '__dq__'
208      '\\\"'
209    else
210      '@'
211    end
212  end
213end
214
215#
216# Deletes last new line of file if it exists! It is needed for t2 workflows that
217# do not sanitize properly, i.e. via a user-provided beanshell script
218#
219def chomp_last_newline(file)
220
221  if File.file?(file) and File.size(file) > 1
222    f = open(file, "rb+")
223    f.seek(-1, File::SEEK_END)
224    f.truncate(File.size(file) - 1) if f.read(1) == "\\n"
225    f.close
226  end
227
228end
229
230
231UTIL_METHODS
232
233  end
234
235
236  def script_create_t2_run(out, wkf_uri, t2_uri)
237    out.write <<CREATE_T2_RUN
238
239# use the uri reference to download the workflow locally
240wkf_file = URI.parse('#{wkf_uri}')
241in_wkf = open(wkf_file)
242wkf = in_wkf.read()
243
244# create run
245begin
246  run = T2Server::Run.create('#{t2_uri}', wkf)
247rescue T2Server::T2ServerError => e
248  exit 1
249end
250
251CREATE_T2_RUN
252
253  end
254
255
256  def script_init_inputs(out, me_rest)
257    out.write "# get input arguments -- for each input a boolean specifies if it's from history\n"
258    out.write "# thus, for each me_rest input we have two arguments in the script!\n"
259    me_rest.workflow.inputs.each_with_index do |input, i|
260      i_name = input.name.to_s
261      out.write "#{i_name}_from_history = ARGV[#{i*2}].chomp\n"
262      out.write "#{i_name}_tmp = ARGV[#{i*2+1}].chomp\n"
263      out.write "if #{i_name}_from_history == \"true\"\n"
264      out.write "  chomp_last_newline(#{i_name}_tmp)\n"
265      out.write "  run.upload_input_file('#{i_name}', #{i_name}_tmp)\n"
266      out.write "else\n"
267      out.write "  run.set_input('#{i_name}', sanitize(#{i_name}_tmp))\n"
268      out.write "end\n"
269    end
270
271  end
272
273
274  def script_start_run(out)
275    out.write <<START_RUN
276
277# start run and wait until it is finished
278run.start
279run.wait(:progress => true)
280
281START_RUN
282  end
283
284
285  def script_get_outputs(out, me_rest)
286    outputs_start_index = me_rest.workflow.inputs.size * 2
287    outputs_end_index = outputs_start_index + me_rest.workflow.outputs.size - 1
288    out.write "# get output arguments and associated them with a file\n"
289    outputs_start_index.upto(outputs_end_index) do |o|
290      out.write "output#{o} = File.open(ARGV[#{o}], \"w\")\n"
291      out.write "get_outputs(run, false, output#{o}, '" + me_rest.workflow.outputs[o - outputs_start_index].name.to_s + "')\n"
292    end
293
294  end
295
296
297  def script_finish_run(out)
298    out.write "\n# delete run\n"
299    out.write "run.delete\n"
300  end
301
302
303
304  # public methods from here onwards
305  public
306
307  # Generates the Galaxy tool's xml file responsible for the UI.
308  def generate_xml(me_rest, xml_file)
309    out = File.open(xml_file, "w")
310    tool_b(out, me_rest.workflow.title)
311    command_be(out, me_rest, xml_file.gsub('.xml', '.rb'))
312    inputs_be(out, me_rest.workflow.inputs)
313    outputs_be(out, me_rest.workflow.outputs)
314    help_be(out, me_rest)
315    tool_e(out)
316    out.close
317  end
318
319  #
320  # Generates the Galaxy tool's script file responsible for talking to the
321  # taverna server
322  #
323  def generate_script(me_rest, t2_server, script_file)
324    out = File.open(script_file, "w")
325    script_preample(out)
326    script_util_methods(out)
327    script_create_t2_run(out, me_rest.uri, t2_server)
328    script_init_inputs(out, me_rest)
329    script_start_run(out)
330    script_get_outputs(out, me_rest)
331    script_finish_run(out)
332    out.close
333  end
334
335end
Note: See TracBrowser for help on using the browser.