t2-web 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES ADDED
@@ -0,0 +1,18 @@
1
+ = Changes log for the Taverna via the Web Gem
2
+
3
+ == Version 0.0.5
4
+ * Updated to new myexperiment-rest API
5
+
6
+ == Version 0.0.4
7
+ * Improved the UI (added logos, tooltips, changed colours).
8
+ * Made some code abstractions
9
+
10
+ == Version 0.0.3
11
+ * Improved the UI (changed divs with frames).
12
+
13
+ == Version 0.0.2
14
+ * Added upload functionality option for each input.
15
+
16
+ == Version 0.0.1
17
+ * Works. Very basic functionality and UI.
18
+
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ == t2-web
2
+
3
+ The MIT License
4
+
5
+ Copyright (c) 2010 - Netherlands Bioinformatics Centre
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,41 @@
1
+ = t2-web interface generator
2
+
3
+
4
+ Authors:: Konstantinos Karasavvas
5
+ Gem Version:: 0.0.5
6
+ Contact:: mailto:kostas.karasavvas@nbic.nl
7
+ Licence:: MIT (See LICENCE or http://www.opensource.org/licenses/mit-license)
8
+ Copyright:: (c) 2010 Netherlands Bioinformatics Centre, The Netherlands
9
+
10
+
11
+ == Synopsis
12
+
13
+ This web application accepts a myExperiment workflow identifier and constructs
14
+ a web form to allow end-users to configure and execute that workflow via a web
15
+ browser with no other dependencies.. The web form interface is as good as the
16
+ description provided in the workflow itself in myExperiment.
17
+
18
+
19
+ == Installation
20
+
21
+ [sudo] gem install t2-web
22
+
23
+
24
+ == Usage
25
+
26
+ t2_webapp
27
+
28
+ Runs the web application at port 9494 and waits for incoming requests.
29
+
30
+
31
+
32
+ == References
33
+
34
+ Taverna to Web Form Generator:: https://trac.nbic.nl/elabfactory/wiki/t2web
35
+ Taverna2:: http://www.taverna.org.uk
36
+ myExperiment:: http://www.myexperiment.org
37
+
38
+
39
+ == Semantic Versioning
40
+
41
+ This module uses semantic versioning concepts from http://semver.org/.
@@ -0,0 +1,54 @@
1
+ #
2
+ # To change this template, choose Tools | Templates
3
+ # and open the template in the editor.
4
+
5
+
6
+ require 'rubygems'
7
+ require 'rake'
8
+ require 'rake/clean'
9
+ require 'rake/testtask'
10
+ require 'rubygems/package_task'
11
+ require 'rdoc/task'
12
+
13
+ spec = Gem::Specification.new do |s|
14
+ s.name = 't2-web'
15
+ s.version = '0.0.5'
16
+ s.extra_rdoc_files = ['README', 'LICENSE', 'CHANGES']
17
+ s.summary = 'WS (with libs) that generates a Web UI form for a Taverna2 workflow and then enacts it to a T2 server.'
18
+ s.description = s.summary
19
+ s.author = 'Kostas Karasavvas'
20
+ s.email = 'kostas.karasavvas@nbic.nl'
21
+ s.executables = ['t2_webapp.rb']
22
+ s.files = %w(LICENSE README CHANGES Rakefile) + Dir.glob("{bin,lib,spec,public,views}/**/*")
23
+ s.require_path = "lib"
24
+ s.bindir = "bin"
25
+ s.add_dependency 'sinatra', '~> 1.2.6'
26
+ s.add_dependency 'haml', '~> 3.1.2'
27
+ s.add_dependency 'rest-client', '~> 1.6.3'
28
+ s.add_dependency 't2-server', '~> 0.6.1'
29
+ s.add_dependency 'myexperiment-rest', '~> 0.3.0'
30
+ end
31
+
32
+ Gem::PackageTask.new(spec) do |p|
33
+ p.gem_spec = spec
34
+ p.need_tar = true
35
+ p.need_zip = true
36
+ end
37
+
38
+ RDoc::Task.new do |rdoc|
39
+ files =['README', 'LICENSE', 'CHANGES', 'lib/**/*.rb']
40
+ rdoc.rdoc_files.add(files)
41
+ rdoc.main = "README" # page to start on
42
+ rdoc.title = "web-t2 Docs"
43
+ rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
44
+ rdoc.options << '--line-numbers'
45
+ end
46
+
47
+ Rake::TestTask.new do |t|
48
+ t.test_files = FileList['test/**/*.rb']
49
+ end
50
+
51
+ # make gem and copy to test deployment server
52
+ task :copy_to_server => :gem do
53
+ `scp pkg/#{spec.name}-#{spec.version}.gem kostas@mybiobank.org:/home/kostas`
54
+ end
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems' if RUBY_VERSION < '1.9'
4
+ require 'sinatra'
5
+ require 'haml'
6
+ require 'open-uri'
7
+ require 'rest-client'
8
+ require 't2-server'
9
+ require 'myexperiment-rest'
10
+ require 'cgi'
11
+ #require "web-t2" TODO: move whole of WebT2App in lib
12
+
13
+
14
+ class WebT2App < Sinatra::Base
15
+
16
+ WEB_APP_NAME = "t2web"
17
+ # TODO: const for TMP_UPLOAD_PATH ? - need to read from config file?
18
+
19
+ set :port, 9494
20
+ set :views, File.dirname(__FILE__) + '/../views'
21
+ set :public, File.dirname(__FILE__) + '/../public'
22
+
23
+ # can be used from routes and views (haml)
24
+ helpers do
25
+
26
+ # Used before we sent the workflow inputs to the taverna server. Just converts
27
+ # double quotations to single ones. That is because the t2-server library
28
+ # is confused with double quotes and apparently it is not easy to fix.
29
+ def t2_library_sanitize(string)
30
+ ex = string.gsub('"', "'")
31
+ end
32
+
33
+ #
34
+ # Deletes last new line of file if it exists! It is needed for t2 workflows that
35
+ # do not sanitize properly, i.e. via a user-provided beanshell script
36
+ #
37
+ def chomp_last_newline(file)
38
+
39
+ if File.file?(file) and File.size(file) > 1
40
+ f = open(file, "rb+")
41
+ f.seek(-1, File::SEEK_END)
42
+ f.truncate(File.size(file) - 1) if f.read(1) == "\n"
43
+ f.close
44
+ end
45
+
46
+ end
47
+
48
+ # Construct the complete or partial URL as MyExperimentRest lib expects it
49
+ # TODO: Maybe update myExperimentREST lib to accept only wid and wkf_version
50
+ # Minor for now
51
+ def get_url_from_wid(wid, wkf_version)
52
+ if wkf_version == "default"
53
+ "/workflows/#{wid}"
54
+ else
55
+ "/workflows/#{wid}?version=#{wkf_version}"
56
+ end
57
+ end
58
+
59
+
60
+ # Generates the contents of the header frame
61
+ def generate_header_frame(my_exp_wkf, my_exp_usr)
62
+ <<END
63
+ <html>
64
+ <head><link href='/css/form.css' rel='stylesheet'/></head>
65
+ <body>
66
+ #{generate_header_table(my_exp_wkf, my_exp_usr)}
67
+ </body>
68
+ </html>
69
+ END
70
+ end
71
+
72
+
73
+ # Generates the contents of the header frame
74
+ def generate_header_table(my_exp_wkf, my_exp_usr)
75
+ <<END
76
+ <div id='header'>
77
+ <table class='header'>
78
+ <tr>
79
+ <td>
80
+ <img alt='NBIC logo' src='/images/nbic_logo.gif' />
81
+ </td>
82
+ <td>
83
+ <table class='header-title'>
84
+ <tr>
85
+ <td class='header-title'>Workflow: #{@my_exp_wkf.title}</td>
86
+ </tr>
87
+ <tr>
88
+ <td class='right'>workflow by #{@my_exp_usr.name}</td>
89
+
90
+ </tr>
91
+ </table>
92
+ </td>
93
+ <td>
94
+ <img alt='LUMC logo' class='right' src='/images/lumc_logo2.png' />
95
+ </td>
96
+ </tr>
97
+ </table>
98
+ </div>
99
+ END
100
+ end
101
+
102
+
103
+ # Generates tooltip html text for descriptions
104
+ def generate_label_tooltip(input)
105
+ "Description: " << CGI::unescapeHTML(input.descriptions[0])
106
+ end
107
+
108
+ # Generates tooltip html text for textarea example
109
+ def generate_textarea_tooltip(input)
110
+ "Example: " << CGI::unescapeHTML(input.examples[0])
111
+ end
112
+
113
+
114
+ # Generates the contents of the data-navigation frame
115
+ def generate_data_navigation_frame(my_exp_wkf, uuid, wid, wkf_version, t2_server, finished_execution)
116
+
117
+ data_navigation_frame = "<html><head><link href='/css/results_navigation.css' rel='stylesheet'/></head><body><table>"
118
+ if my_exp_wkf.outputs.size >=1
119
+ my_exp_wkf.outputs.each do |output|
120
+ data_navigation_frame << "<tr>"
121
+ if finished_execution
122
+ data_navigation_frame << "<td><a href='javascript:void(0);' onclick='parent.getSampleOutput(\\\"#{t2_server}\\\", \\\"#{uuid}\\\", " <<
123
+ "\\\"#{output.name}\\\", \\\"#{wid}\\\", \\\"#{wkf_version}\\\");'>#{output.name}</a></td><td></td>"
124
+ else
125
+ data_navigation_frame << "<td>#{output.name}</td><td><img src='/images/snake_transparent.gif' alt='Loading Content...'></td>"
126
+ end
127
+ data_navigation_frame << "</tr>"
128
+ end
129
+ end
130
+ data_navigation_frame << "</table></body></html>"
131
+ end
132
+
133
+
134
+ # Check type of data and generate appropriate html for that result!
135
+ def generate_html_result(data)
136
+ html = ""
137
+ #p data
138
+ if data.instance_of? Array
139
+ html = data.flatten.join('<br>')
140
+ elsif data.instance_of? String
141
+ html = data.gsub(/[\n]/, '<br>')
142
+ else
143
+ html = data
144
+ end
145
+ html
146
+ end
147
+
148
+ end
149
+
150
+
151
+
152
+ # Create the Web UI for that workflow
153
+ get "/#{WEB_APP_NAME}/workflow/:wid" do
154
+
155
+ # Get http request's parameters
156
+ # TODO: consider adding the following to a session
157
+ @wid = params[:wid]
158
+ @wkf_version = params[:wkf_version] || nil
159
+ @t2_server = params[:server] || "http://test.mybiobank.org/taverna-server"
160
+
161
+ # Get myExperiment workflow object
162
+ # TODO: catch exception -- make custom exceptions?
163
+ @my_exp_wkf = MyExperimentREST::Workflow.from_id_and_version(@wid, @wkf_version)
164
+
165
+ # Get myExperiment user object
166
+ # TODO: catch exception -- make custom exceptions?
167
+ @my_exp_usr = MyExperimentREST::User.from_uri(@my_exp_wkf.uploader_uri)
168
+
169
+ haml :form
170
+
171
+ end
172
+
173
+
174
+ # Enact the workflow with the submitted parameters and returns
175
+ # result display (involves checking run's status and getting results)
176
+ post "/#{WEB_APP_NAME}/enact" do
177
+ #p params
178
+
179
+ # Get http POST request's parameters
180
+ # TODO: consider adding the following to a session
181
+ @wid = params[:wid]
182
+ @wkf_version = params[:wkf_version]
183
+ @t2_server = params[:server]
184
+
185
+ @my_exp_wkf = MyExperimentREST::Workflow.from_id_and_version(@wid, @wkf_version)
186
+
187
+ # Get myExperiment user object (TODO: session!, exceptions)
188
+ @my_exp_usr = MyExperimentREST::User.from_uri(@my_exp_wkf.uploader_uri)
189
+
190
+
191
+ # use the uri reference to download the workflow locally
192
+ #wkf_file = URI.parse(@my_exp_wkf.content_uri)
193
+ wkf = open(@my_exp_wkf.content_uri).read
194
+
195
+ # create run
196
+ begin
197
+ run = T2Server::Run.create(@t2_server, wkf)
198
+ rescue T2Server::T2ServerError => e
199
+ return "404 Not Found: run could not be instantiated!"
200
+ end
201
+
202
+ # Get the run as instance member to make visible in views
203
+ @run_uuid = run.uuid
204
+
205
+ # Set workflow inputs
206
+ @my_exp_wkf.inputs.each do |input|
207
+ if params[:"upload-checkbox-#{input.name}"] == "yes"
208
+ filename = params[:"#{input.name}-file"]
209
+ tmp_filename = "/tmp/#{WEB_APP_NAME}/#{filename}"
210
+
211
+ # make sure that the file is uploaded completely, i.e. all it's
212
+ # connections are closed
213
+ #while `lsof -c cp | grep /tmp/#{WEB_APP_NAME}/#{filename}` != ""
214
+ # sleep 1
215
+ #end
216
+
217
+ chomp_last_newline(tmp_filename)
218
+ run.upload_input_file(input.name, tmp_filename)
219
+ else
220
+ run.set_input(input.name, t2_library_sanitize(params["#{input.name}-input".to_sym]) )
221
+ end
222
+ end
223
+
224
+ # start run and wait until it is finished
225
+ run.start
226
+
227
+ haml :results
228
+ end
229
+
230
+
231
+
232
+ # Proxy operation (to bypass cross-domain AJAX) to get T2 run's status
233
+ get '/runs/:uuid/status' do
234
+ uuid = params[:uuid]
235
+ t2_server = params[:server]
236
+
237
+ response = RestClient.get t2_server + "/rest/runs/" + uuid + "/status", :content_type => "text/plain"
238
+
239
+ response.to_str
240
+ end
241
+
242
+
243
+
244
+ # Get results for specified run and output - display it in data-display div
245
+ # Cross-domain AJAX to get result from T2
246
+ get "/#{WEB_APP_NAME}/run/:uuid/output/:out" do
247
+
248
+ # Get http request's parameters
249
+ # TODO: consider adding the following to a session
250
+ @wid = params[:wid]
251
+ @wkf_version = params[:wkf_version]
252
+
253
+ # Get myExperiment workflow object
254
+ # TODO: with sessions that would be remembered
255
+ @my_exp_wkf = MyExperimentREST::Workflow.from_id_and_version(@wid, @wkf_version)
256
+
257
+ @run_uuid = params[:uuid]
258
+ @t2_server = params[:server]
259
+ t2_output = params[:out]
260
+
261
+ # Get t2 server and then the run using uuid
262
+ server = T2Server::Server.connect(@t2_server)
263
+ run = server.run(@run_uuid)
264
+
265
+ # Get output from t2 server
266
+ # TODO: that gets the whole output.. request client lib to support partial
267
+ # download! -- refs could then be used to download the full results!
268
+ begin
269
+ data_lists = run.get_output(t2_output, false)
270
+ rescue Exception => e
271
+ data_lists = run.get_output("#{t2_output}.error", false)
272
+ end
273
+
274
+ # that should be a sample of the results -- not the complete results!
275
+ data = generate_html_result(data_lists)
276
+
277
+ data
278
+ end
279
+
280
+
281
+ # Used from input's upload-form to upload files without refresh (actually
282
+ # a hidden iframe is refreshed).
283
+ post "/#{WEB_APP_NAME}/upload" do
284
+ filename = params[:file][:filename]
285
+ tempfile = params[:file][:tempfile]
286
+ FileUtils.mkdir("/tmp/#{WEB_APP_NAME}") unless File.exist?("/tmp/#{WEB_APP_NAME}")
287
+ FileUtils.copy(tempfile.path, "/tmp/#{WEB_APP_NAME}/#{filename}")
288
+ "Successfully copied #{filename}!!"
289
+ end
290
+
291
+ end
292
+
293
+
294
+ # Start Web App
295
+ WebT2App.run!
@@ -0,0 +1,70 @@
1
+ body{
2
+ background-color: #fdfdfd;
3
+ }
4
+
5
+ table.header {
6
+ width: 100%;
7
+ }
8
+
9
+ table.header-title {
10
+ border-collapse: collapse;
11
+ /* With equal left-right margins table will be aligned in the centre */
12
+ margin-left: auto;
13
+ margin-right: auto;
14
+ }
15
+
16
+ td.header-title {
17
+ font-size: 200%;
18
+ background-color: #f0f0f0;
19
+ padding: 12px;
20
+ border-bottom: 4px solid #909090;
21
+ border-top: 4px solid #909090;
22
+ }
23
+
24
+ #inputs {
25
+ }
26
+
27
+ table.inputs {
28
+ width: 800px;
29
+ border-collapse: collapse;
30
+ /* With equal left-right margins table will be aligned in the centre */
31
+ margin-left: auto;
32
+ margin-right: auto;
33
+ }
34
+
35
+ th.inputs {
36
+ font-size: 120%;
37
+ background-color: #f0f0f0;
38
+ padding: 10px;
39
+ border-bottom: 1px solid #909090;
40
+ border-top: 4px solid #909090;
41
+ }
42
+
43
+ td.inputs {
44
+ padding: 5px;
45
+ vertical-align: top;
46
+ }
47
+
48
+ td.right {
49
+ text-align: right;
50
+ }
51
+
52
+ img.right {
53
+ float: right;
54
+ }
55
+
56
+ #description {
57
+ }
58
+
59
+ #inputs {
60
+ }
61
+
62
+ #outputs {
63
+ }
64
+
65
+ #note {
66
+ }
67
+
68
+ label {
69
+ text-decoration: underline;
70
+ }