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

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

designed multiple solutions (use 'prettiest' one) for displaying as nicely as possible multiline input examples (can be improved!)

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