lazar-gui 1.3.1 → 1.4.0.pre.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/FAQ.md +9 -6
- data/Gemfile +2 -2
- data/README.md +19 -8
- data/VERSION +1 -1
- data/api/api.json +1000 -0
- data/application.rb +269 -206
- data/config.ru +7 -2
- data/docker/Dockerfile +73 -0
- data/docker/start.sh +12 -0
- data/docker/swagger.html +107 -0
- data/helper.rb +46 -1
- data/lazar-gui.gemspec +6 -6
- data/lib/api.rb +29 -0
- data/lib/compound.rb +68 -0
- data/lib/dataset.rb +33 -0
- data/lib/endpoint.rb +23 -0
- data/lib/feature.rb +29 -0
- data/lib/model.rb +152 -0
- data/lib/report.rb +29 -0
- data/lib/substance.rb +34 -0
- data/lib/swagger.rb +3 -0
- data/lib/validation.rb +67 -0
- data/public/fonts/FontAwesome.otf +0 -0
- data/public/fonts/fontawesome-webfont.eot +0 -0
- data/public/fonts/fontawesome-webfont.svg +2671 -0
- data/public/fonts/fontawesome-webfont.ttf +0 -0
- data/public/fonts/fontawesome-webfont.woff +0 -0
- data/public/fonts/fontawesome-webfont.woff2 +0 -0
- data/public/images/orn-logo.jpg +0 -0
- data/public/javascripts/bootstrap.js +3944 -0
- data/public/javascripts/google_analytics_lazar.js +2 -2
- data/public/javascripts/jquery.min.js +2 -0
- data/public/javascripts/lazar-gui.js +313 -0
- data/public/javascripts/pagination.min.js +11 -0
- data/public/javascripts/popper.min.js +5 -0
- data/public/stylesheets/bootstrap.min.css +7 -0
- data/public/stylesheets/bootstrap.min.css.map +1 -0
- data/public/stylesheets/font-awesome.min.css +4 -0
- data/public/stylesheets/pagination.css +1 -0
- data/qmrf_report.rb +64 -14
- data/task.rb +68 -0
- data/unicorn.rb +0 -1
- data/views/batch.haml +48 -107
- data/views/details.haml +10 -2
- data/views/error.haml +4 -5
- data/views/faq.haml +6 -2
- data/views/help.haml +68 -0
- data/views/info.haml +3 -2
- data/views/layout.haml +68 -78
- data/views/model_details.haml +179 -144
- data/views/neighbors.haml +98 -80
- data/views/predict.haml +43 -178
- data/views/prediction.haml +109 -102
- data/views/style.scss +44 -37
- data/views/upload.haml +28 -0
- metadata +51 -105
- data/public/css/bootstrap-theme.min.css +0 -5
- data/public/css/bootstrap.min.css +0 -5
- data/public/css/bootstrap.vertical-tabs.min.css +0 -1
- data/public/css/images/black-asc.gif +0 -0
- data/public/css/images/black-desc.gif +0 -0
- data/public/css/images/black-unsorted.gif +0 -0
- data/public/css/images/bootstrap-black-unsorted.png +0 -0
- data/public/css/images/bootstrap-white-unsorted.png +0 -0
- data/public/css/images/dragtable-handle.png +0 -0
- data/public/css/images/dragtable-handle.svg +0 -7
- data/public/css/images/dropbox-asc-hovered.png +0 -0
- data/public/css/images/dropbox-asc.png +0 -0
- data/public/css/images/dropbox-desc-hovered.png +0 -0
- data/public/css/images/dropbox-desc.png +0 -0
- data/public/css/images/first.png +0 -0
- data/public/css/images/green-asc.gif +0 -0
- data/public/css/images/green-desc.gif +0 -0
- data/public/css/images/green-header.gif +0 -0
- data/public/css/images/green-unsorted.gif +0 -0
- data/public/css/images/ice-asc.gif +0 -0
- data/public/css/images/ice-desc.gif +0 -0
- data/public/css/images/ice-unsorted.gif +0 -0
- data/public/css/images/last.png +0 -0
- data/public/css/images/loading.gif +0 -0
- data/public/css/images/metro-black-asc.png +0 -0
- data/public/css/images/metro-black-desc.png +0 -0
- data/public/css/images/metro-loading.gif +0 -0
- data/public/css/images/metro-unsorted.png +0 -0
- data/public/css/images/metro-white-asc.png +0 -0
- data/public/css/images/metro-white-desc.png +0 -0
- data/public/css/images/next.png +0 -0
- data/public/css/images/prev.png +0 -0
- data/public/css/images/white-asc.gif +0 -0
- data/public/css/images/white-desc.gif +0 -0
- data/public/css/images/white-unsorted.gif +0 -0
- data/public/css/jquery-ui.css +0 -1225
- data/public/css/jquery-ui.theme.min.css +0 -5
- data/public/css/theme.bootstrap.css +0 -158
- data/public/css/theme.bootstrap.min.css +0 -1
- data/public/css/theme.default.min.css +0 -1
- data/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/public/fonts/glyphicons-halflings-regular.svg +0 -288
- data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/public/images/Email.png +0 -0
- data/public/images/Facebook.png +0 -0
- data/public/images/Google+.png +0 -0
- data/public/images/LinkedIn.png +0 -0
- data/public/images/OpenToxEuro2013_small.png +0 -0
- data/public/images/Twitter.png +0 -0
- data/public/images/arrow_down_float.png +0 -0
- data/public/images/arrow_left_float.png +0 -0
- data/public/images/arrow_right_float.png +0 -0
- data/public/images/arrow_up_float.png +0 -0
- data/public/images/asc.gif +0 -0
- data/public/images/bg.gif +0 -0
- data/public/images/desc.gif +0 -0
- data/public/images/gray_jean.png +0 -0
- data/public/images/info_white.png +0 -0
- data/public/javascripts/bootstrap.min.js +0 -7
- data/public/javascripts/jquery-1.11.2.min.js +0 -4
- data/public/javascripts/jquery-1.8.3.min.js +0 -2
- data/public/javascripts/jquery-ui-1.10.3.custom.min.js +0 -6
- data/public/javascripts/jquery.bpopup.min.js +0 -7
- data/public/javascripts/jquery.tablesorter.min.js +0 -2
- data/public/javascripts/jquery.tablesorter.widgets.js +0 -2678
- data/public/javascripts/jquery.tools.min.js +0 -5
- data/public/rect.png +0 -0
- data/test/lazarweb.rb +0 -193
- data/test/setup.rb +0 -7
- data/views/significant_fragments.haml +0 -66
data/application.rb
CHANGED
@@ -1,20 +1,53 @@
|
|
1
1
|
require 'rdiscount'
|
2
2
|
require_relative 'qmrf_report.rb'
|
3
|
+
require_relative 'task.rb'
|
4
|
+
require_relative 'helper.rb'
|
3
5
|
include OpenTox
|
6
|
+
PUBCHEM_CID_URI = PUBCHEM_URI.split("/")[0..-3].join("/")+"/compound/"
|
4
7
|
|
8
|
+
[
|
9
|
+
"api.rb",
|
10
|
+
"compound.rb",
|
11
|
+
"dataset.rb",
|
12
|
+
"endpoint.rb",
|
13
|
+
"feature.rb",
|
14
|
+
"model.rb",
|
15
|
+
"report.rb",
|
16
|
+
"substance.rb",
|
17
|
+
"swagger.rb",
|
18
|
+
"validation.rb"
|
19
|
+
].each{ |f| require_relative "./lib/#{f}" }
|
5
20
|
|
6
|
-
configure :production do
|
21
|
+
configure :production, :development do
|
22
|
+
STDOUT.sync = true
|
7
23
|
$logger = Logger.new(STDOUT)
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
configure :development do
|
12
|
-
$logger = Logger.new(STDOUT)
|
13
|
-
enable :reloader
|
24
|
+
#$logger.level = Logger::DEBUG
|
14
25
|
end
|
15
26
|
|
16
27
|
before do
|
17
|
-
|
28
|
+
# use this hostname method instead to('/')
|
29
|
+
# allowes to set https for xhr requests
|
30
|
+
$host_with_port = request.host =~ /localhost/ ? request.host_with_port : request.host
|
31
|
+
$paths = [
|
32
|
+
"api",
|
33
|
+
"compound",
|
34
|
+
"dataset",
|
35
|
+
"endpoint",
|
36
|
+
"feature",
|
37
|
+
"model",
|
38
|
+
"report",
|
39
|
+
"substance",
|
40
|
+
"swagger",
|
41
|
+
"validation"]
|
42
|
+
if request.path =~ /predict/
|
43
|
+
@accept = request.env['HTTP_ACCEPT'].split(",").first
|
44
|
+
response['Content-Type'] = @accept
|
45
|
+
halt 400, "Mime type #{@accept} is not supported." unless @accept == "text/html" or @accept == "*/*"
|
46
|
+
@version = File.read("VERSION").chomp
|
47
|
+
else
|
48
|
+
@accept = request.env['HTTP_ACCEPT'].split(",").first
|
49
|
+
response['Content-Type'] = @accept
|
50
|
+
end
|
18
51
|
end
|
19
52
|
|
20
53
|
not_found do
|
@@ -22,258 +55,279 @@ not_found do
|
|
22
55
|
end
|
23
56
|
|
24
57
|
error do
|
25
|
-
|
26
|
-
|
58
|
+
# API errors
|
59
|
+
if request.path.split("/")[1] == "api" || $paths.include?(request.path.split("/")[2])
|
60
|
+
@accept = request.env['HTTP_ACCEPT']
|
61
|
+
response['Content-Type'] = @accept
|
62
|
+
@accept == "text/plain" ? request.env['sinatra.error'] : request.env['sinatra.error'].to_json
|
63
|
+
# batch dataset error
|
64
|
+
elsif request.env['sinatra.error.params']['batchfile'] && request.env['REQUEST_METHOD'] == "POST"
|
65
|
+
@error = request.env['sinatra.error']
|
66
|
+
response['Content-Type'] = "text/html"
|
67
|
+
status 200
|
68
|
+
return haml :error
|
69
|
+
# basic error
|
70
|
+
else
|
71
|
+
@error = request.env['sinatra.error']
|
72
|
+
return haml :error
|
73
|
+
end
|
27
74
|
end
|
28
75
|
|
29
|
-
|
30
|
-
|
76
|
+
# https://github.com/britg/sinatra-cross_origin#responding-to-options
|
77
|
+
options "*" do
|
78
|
+
response.headers["Allow"] = "HEAD,GET,PUT,POST,DELETE,OPTIONS"
|
79
|
+
response.headers["Access-Control-Allow-Headers"] = "X-Requested-With, X-HTTP-Method-Override, Content-Type, Cache-Control, Accept"
|
80
|
+
200
|
31
81
|
end
|
32
82
|
|
33
83
|
get '/predict/?' do
|
84
|
+
# handle user click on back button while batch prediction
|
85
|
+
if params[:tpid]
|
86
|
+
begin
|
87
|
+
Process.kill(9,params[:tpid].to_i) if !params[:tpid].blank?
|
88
|
+
rescue
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
# remove data helper method
|
92
|
+
remove_task_data(params[:tpid])
|
93
|
+
end
|
94
|
+
# regular request on '/predict' page
|
34
95
|
@models = OpenTox::Model::Validation.all
|
35
|
-
@models = @models.delete_if{|m| m.model.name =~ /\b(Net cell association)\b/}
|
36
96
|
@endpoints = @models.collect{|m| m.endpoint}.sort.uniq
|
37
|
-
if @models.count > 0
|
38
|
-
rodent_index = 0
|
39
|
-
@models.each_with_index{|model,idx| rodent_index = idx if model.species =~ /Rodent/}
|
40
|
-
@models.insert(rodent_index-1,@models.delete_at(rodent_index))
|
41
|
-
end
|
42
97
|
@models.count > 0 ? (haml :predict) : (haml :info)
|
43
98
|
end
|
44
99
|
|
45
100
|
get '/predict/modeldetails/:model' do
|
46
101
|
model = OpenTox::Model::Validation.find params[:model]
|
47
|
-
|
102
|
+
training_dataset = model.model.training_dataset
|
103
|
+
data_entries = training_dataset.data_entries
|
104
|
+
crossvalidations = model.crossvalidations
|
105
|
+
if model.classification?
|
106
|
+
crossvalidations.each do |cv|
|
107
|
+
File.open(File.join('public', "#{cv.id}.png"), 'w') do |file|
|
108
|
+
file.write(cv.probability_plot(format: "png"))
|
109
|
+
end unless File.exists? File.join('public', "#{cv.id}.png")
|
110
|
+
end
|
111
|
+
else
|
112
|
+
crossvalidations.each do |cv|
|
113
|
+
File.open(File.join('public', "#{cv.id}.png"), 'w') do |file|
|
114
|
+
file.write(cv.correlation_plot(format: "png"))
|
115
|
+
end unless File.exists? File.join('public', "#{cv.id}.png")
|
116
|
+
end
|
117
|
+
end
|
48
118
|
|
49
|
-
|
119
|
+
response['Content-Type'] = "text/html"
|
120
|
+
return haml :model_details, :layout=> false, :locals => {:model => model,
|
121
|
+
:crossvalidations => crossvalidations,
|
122
|
+
:training_dataset => training_dataset,
|
123
|
+
:data_entries => data_entries
|
124
|
+
}
|
50
125
|
end
|
51
126
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
@inchi = @compound.inchi.gsub("InChI=", "")
|
62
|
-
|
63
|
-
haml :details, :layout => false
|
127
|
+
get "/predict/report/:id/?" do
|
128
|
+
prediction_model = Model::Validation.find params[:id]
|
129
|
+
bad_request_error "model with id: '#{params[:id]}' not found." unless prediction_model
|
130
|
+
report = qmrf_report params[:id]
|
131
|
+
# output
|
132
|
+
t = Tempfile.new
|
133
|
+
t << report.to_xml
|
134
|
+
name = prediction_model.species.sub(/\s/,"-")+"-"+prediction_model.endpoint.downcase.sub(/\s/,"-")
|
135
|
+
send_file t.path, :filename => "QMRF_report_#{name.gsub!(/[^0-9A-Za-z]/, '_')}.xml", :type => "application/xml", :disposition => "attachment"
|
64
136
|
end
|
65
137
|
|
66
|
-
get '/jme_help/?' do
|
138
|
+
get '/predict/jme_help/?' do
|
67
139
|
File.read(File.join('views','jme_help.html'))
|
68
140
|
end
|
69
141
|
|
142
|
+
# download training dataset
|
70
143
|
get '/predict/dataset/:name' do
|
71
|
-
response['Content-Type'] = "text/csv"
|
72
144
|
dataset = Dataset.find_by(:name=>params[:name])
|
73
|
-
csv = dataset.
|
74
|
-
csv
|
145
|
+
csv = File.read dataset.source
|
146
|
+
name = params[:name] + ".csv"
|
147
|
+
t = Tempfile.new
|
148
|
+
t << csv
|
149
|
+
t.rewind
|
150
|
+
response['Content-Type'] = "text/csv"
|
151
|
+
send_file t.path, :filename => name, :type => "text/csv", :disposition => "attachment"
|
75
152
|
end
|
76
153
|
|
77
|
-
|
154
|
+
# download batch predicton file
|
155
|
+
get '/predict/batch/download/?' do
|
156
|
+
task = Task.find params[:tid]
|
157
|
+
dataset = Dataset.find task.dataset_id
|
158
|
+
name = dataset.name + ".csv"
|
159
|
+
t = Tempfile.new
|
160
|
+
# to_prediction_csv takes too much time; use task.csv instead which is the same
|
161
|
+
#t << dataset.to_prediction_csv
|
162
|
+
t << task.csv
|
163
|
+
t.rewind
|
78
164
|
response['Content-Type'] = "text/csv"
|
79
|
-
path
|
80
|
-
send_file path, :filename => "lazar_batch_prediction_#{params[:filename]}", :type => "text/csv", :disposition => "attachment"
|
165
|
+
send_file t.path, :filename => "#{Time.now.strftime("%Y-%m-%d")}_lazar_batch_prediction_#{name}", :type => "text/csv", :disposition => "attachment"
|
81
166
|
end
|
82
167
|
|
83
168
|
post '/predict/?' do
|
84
|
-
|
85
169
|
# process batch prediction
|
86
|
-
|
170
|
+
unless params[:fileselect].blank?
|
87
171
|
if params[:fileselect][:filename] !~ /\.csv$/
|
88
|
-
|
89
|
-
end
|
90
|
-
File.open('tmp/' + params[:fileselect][:filename], "w") do |f|
|
91
|
-
f.write(params[:fileselect][:tempfile].read)
|
172
|
+
raise "Wrong file extension for '#{params[:fileselect][:filename]}'. Please upload a CSV file."
|
92
173
|
end
|
93
174
|
@filename = params[:fileselect][:filename]
|
94
|
-
|
95
|
-
|
96
|
-
if input.class == OpenTox::Dataset
|
97
|
-
dataset = OpenTox::Dataset.find input
|
98
|
-
else
|
99
|
-
bad_request_error "Could not serialize file '#{@filename}'."
|
100
|
-
end
|
101
|
-
rescue
|
102
|
-
bad_request_error "Could not serialize file '#{@filename}'."
|
103
|
-
end
|
104
|
-
@compounds = dataset.compounds
|
105
|
-
if @compounds.size == 0
|
106
|
-
message = dataset[:warnings]
|
107
|
-
dataset.delete
|
108
|
-
bad_request_error message
|
175
|
+
File.open('tmp/' + @filename, "w") do |f|
|
176
|
+
f.write(params[:fileselect][:tempfile].read)
|
109
177
|
end
|
178
|
+
# check CSV structure by parsing and header check
|
179
|
+
csv = CSV.read File.join("tmp", @filename)
|
180
|
+
header = csv.shift
|
181
|
+
accepted = ["SMILES","InChI"]
|
182
|
+
raise "CSV header does not include 'SMILES' or 'InChI'. Please read the <a href='https://dg.in-silico.ch/predict/help' rel='external'> HELP </a> page." unless header.any?(/smiles|inchi/i)
|
183
|
+
@models = params[:selection].keys.join(",")
|
184
|
+
return haml :upload
|
185
|
+
end
|
110
186
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
187
|
+
unless params[:batchfile].blank?
|
188
|
+
dataset = Dataset.from_csv_file File.join("tmp", params[:batchfile])
|
189
|
+
raise "No compounds in Dataset. Please read the <a href='https://dg.in-silico.ch/predict/help' rel='external'> HELP </a> page." if dataset.compounds.size == 0
|
190
|
+
response['Content-Type'] = "application/json"
|
191
|
+
return {:dataset_id => dataset.id.to_s, :models => params[:models]}.to_json
|
192
|
+
end
|
115
193
|
|
116
|
-
|
117
|
-
params[:
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
@view[compound] << [model,prediction]
|
124
|
-
end
|
125
|
-
end
|
194
|
+
unless params[:models].blank?
|
195
|
+
dataset = Dataset.find params[:dataset_id]
|
196
|
+
@compounds_size = dataset.compounds.size
|
197
|
+
@models = params[:models].split(",")
|
198
|
+
@tasks = []
|
199
|
+
@models.each{|m| t = Task.new; t.save; @tasks << t}
|
200
|
+
@predictions = {}
|
126
201
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
202
|
+
maintask = Task.run do
|
203
|
+
@models.each_with_index do |model_id,idx|
|
204
|
+
t = @tasks[idx]
|
205
|
+
t.update_percent(1)
|
206
|
+
prediction = {}
|
207
|
+
model = Model::Validation.find model_id
|
208
|
+
t.update_percent(10)
|
209
|
+
prediction_dataset = model.predict dataset
|
210
|
+
t.update_percent(70)
|
211
|
+
t[:dataset_id] = prediction_dataset.id
|
212
|
+
t.update_percent(75)
|
213
|
+
prediction[model_id] = prediction_dataset.id.to_s
|
214
|
+
t.update_percent(80)
|
215
|
+
t[:predictions] = prediction
|
216
|
+
t.update_percent(90)
|
217
|
+
t[:csv] = prediction_dataset.to_prediction_csv
|
218
|
+
t.update_percent(100)
|
219
|
+
t.save
|
144
220
|
end
|
145
221
|
end
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
@
|
222
|
+
maintask[:subTasks] = @tasks.collect{|t| t.id}
|
223
|
+
maintask.save
|
224
|
+
@pid = maintask.pid
|
225
|
+
response['Content-Type'] = "text/html"
|
226
|
+
return haml :batch
|
227
|
+
else
|
228
|
+
# single compound prediction
|
229
|
+
# validate identifier input
|
230
|
+
if !params[:identifier].blank?
|
231
|
+
@identifier = params[:identifier].strip
|
232
|
+
$logger.debug "input:#{@identifier}"
|
233
|
+
# get compound from SMILES
|
234
|
+
begin
|
235
|
+
@compound = Compound.from_smiles @identifier
|
236
|
+
rescue
|
237
|
+
@error = "'#{@identifier}' is not a valid SMILES string." unless @compound
|
238
|
+
return haml :error
|
160
239
|
end
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
if id == 0
|
169
|
-
@csvhash[idx] += delEntries unless delEntries.blank?
|
170
|
-
end
|
171
|
-
unless array.kind_of? String
|
172
|
-
compound = array[0]
|
173
|
-
prediction = array[1]
|
174
|
-
smiles = compound.smiles
|
175
|
-
|
176
|
-
if prediction[:neighbors]
|
177
|
-
if prediction[:value]
|
178
|
-
pred = prediction[:value].numeric? ? "#{prediction[:value].delog10.signif(3)}" : prediction[:value]
|
179
|
-
predA = prediction[:value].numeric? ? "#{prediction[:value].delog10.signif(3)}" : prediction[:value]
|
180
|
-
predAunit = prediction[:value].numeric? ? "(#{model.unit})" : ""
|
181
|
-
predB = prediction[:value].numeric? ? "#{compound.mmol_to_mg(prediction[:value].delog10).signif(3)}" : prediction[:value]
|
182
|
-
predBunit = prediction[:value].numeric? ? "#{model.unit =~ /\b(mmol\/L)\b/ ? "(mg/L)" : "(mg/kg_bw/day)"}" : ""
|
183
|
-
int = (prediction[:prediction_interval].nil? ? nil : prediction[:prediction_interval])
|
184
|
-
intervalLow = (int.nil? ? "" : "#{int[1].delog10.signif(3)}")
|
185
|
-
intervalHigh = (int.nil? ? "" : "#{int[0].delog10.signif(3)}")
|
186
|
-
intervalLowMg = (int.nil? ? "" : "#{compound.mmol_to_mg(int[1].delog10).signif(3)}")
|
187
|
-
intervalHighMg = (int.nil? ? "" : "#{compound.mmol_to_mg(int[0].delog10).signif(3)}")
|
188
|
-
inApp = "yes"
|
189
|
-
inT = prediction[:info] =~ /\b(identical)\b/i ? "yes" : "no"
|
190
|
-
note = prediction[:warnings].join("\n") + ( prediction[:info] ? prediction[:info].sub(/\'.*\'/,"") : "\n" )
|
191
|
-
|
192
|
-
unless prediction[:probabilities].nil?
|
193
|
-
av = model.prediction_feature.accept_values
|
194
|
-
propA = "#{prediction[:probabilities][av[0]].to_f.signif(3)}"
|
195
|
-
propB = "#{prediction[:probabilities][av[1]].to_f.signif(3)}"
|
196
|
-
end
|
197
|
-
else
|
198
|
-
# no prediction value only one neighbor
|
199
|
-
inApp = "no"
|
200
|
-
inT = prediction[:info] =~ /\b(identical)\b/i ? "yes" : "no"
|
201
|
-
note = prediction[:warnings].join("\n") + ( prediction[:info] ? prediction[:info].sub(/\'.*\'/,"") : "\n" )
|
202
|
-
end
|
203
|
-
else
|
204
|
-
# no prediction value
|
205
|
-
inApp = "no"
|
206
|
-
inT = prediction[:info] =~ /\b(identical)\b/i ? "yes" : "no"
|
207
|
-
note = prediction[:warnings].join("\n") + ( prediction[:info] ? prediction[:info].sub(/\'.*\'/,"") : "\n" )
|
208
|
-
end
|
209
|
-
if @warnings
|
210
|
-
@warnings.each do |w|
|
211
|
-
note += (w.split(".").first + ".") if /\b(#{Regexp.escape(smiles)})\b/ === w
|
212
|
-
end
|
213
|
-
end
|
214
|
-
else
|
215
|
-
# string note for duplicates
|
216
|
-
endpoint = type = smiles = pred = predA = predB = propA = propB = intervalLow = intervalHigh = intervalLowMg = intervalHighMg = inApp = inT = ""
|
217
|
-
note = array
|
218
|
-
end
|
219
|
-
if model.regression?
|
220
|
-
@csvhash[idx] += "\"#{id+1}\",\"#{endpoint}\",\"#{type}\",\"#{smiles}\",\"#{predA}\",\"#{predB}\",\"#{intervalLow}\",\"#{intervalHigh}\",\"#{intervalLowMg}\",\"#{intervalHighMg}\",\"#{inApp}\",\"#{inT}\",\"#{note.chomp}\"\n"
|
221
|
-
else
|
222
|
-
@csvhash[idx] += "\"#{id+1}\",\"#{endpoint}\",\"#{type}\",\"#{smiles}\",\"#{pred}\",\"#{propA}\",\"#{propB}\",\"#{inApp}\",\"#{inT}\",\"#{note.chomp}\"\n"
|
223
|
-
end
|
240
|
+
@models = []
|
241
|
+
@predictions = []
|
242
|
+
params[:selection].keys.each do |model_id|
|
243
|
+
model = Model::Validation.find model_id
|
244
|
+
@models << model
|
245
|
+
prediction = model.predict(@compound)
|
246
|
+
@predictions << prediction
|
224
247
|
end
|
248
|
+
haml :prediction
|
225
249
|
end
|
226
|
-
t = Tempfile.new
|
227
|
-
@csvhash.each do |model, csv|
|
228
|
-
t.write(csv)
|
229
|
-
t.write("\n")
|
230
|
-
end
|
231
|
-
t.rewind
|
232
|
-
@tmppath = t.path.split("/").last
|
233
|
-
|
234
|
-
dataset.delete
|
235
|
-
File.delete File.join("tmp", params[:fileselect][:filename])
|
236
|
-
return haml :batch
|
237
250
|
end
|
251
|
+
end
|
238
252
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
@models << model
|
252
|
-
@predictions << model.predict(@compound)
|
253
|
+
get '/prediction/task/?' do
|
254
|
+
# returns task progress in percent
|
255
|
+
if params[:turi]
|
256
|
+
task = Task.find(params[:turi].to_s)
|
257
|
+
response['Content-Type'] = "application/json"
|
258
|
+
return JSON.pretty_generate(:percent => task.percent)
|
259
|
+
# kills task process id
|
260
|
+
elsif params[:ktpid]
|
261
|
+
begin
|
262
|
+
Process.kill(9,params[:ktpid].to_i) if !params[:ktpid].blank?
|
263
|
+
rescue
|
264
|
+
nil
|
253
265
|
end
|
254
|
-
|
266
|
+
#remove_task_data(params[:ktpid]) deletes also the source file
|
267
|
+
response['Content-Type'] = "application/json"
|
268
|
+
return JSON.pretty_generate(:ktpid => params[:ktpid])
|
269
|
+
# returns task details
|
270
|
+
elsif params[:predictions]
|
271
|
+
task = Task.find(params[:predictions])
|
272
|
+
pageSize = params[:pageSize].to_i - 1
|
273
|
+
pageNumber= params[:pageNumber].to_i - 1
|
274
|
+
csv = CSV.parse(task.csv)
|
275
|
+
header = csv.shift
|
276
|
+
string = "<td><table class=\"table table-bordered\">"
|
277
|
+
# find canonical smiles column
|
278
|
+
cansmi = 0
|
279
|
+
header.each_with_index do |h,idx|
|
280
|
+
cansmi = idx if h =~ /Canonical SMILES/
|
281
|
+
string += "<th class=\"fit\">#{h}</th>"
|
282
|
+
end
|
283
|
+
string += "</tr>"
|
284
|
+
string += "<tr>"
|
285
|
+
csv[pageNumber].each_with_index do |line,idx|
|
286
|
+
if idx == cansmi
|
287
|
+
c = Compound.from_smiles line
|
288
|
+
string += "<td class=\"fit\">#{line}</br>" \
|
289
|
+
"<a class=\"btn btn-link\" data-id=\"link\" " \
|
290
|
+
"data-remote=\"#{to("/prediction/#{c.id}/details")}\" data-toggle=\"modal\" " \
|
291
|
+
"href=#details>" \
|
292
|
+
"#{embedded_svg(c.svg, title: "click for details")}" \
|
293
|
+
"</td>"
|
294
|
+
else
|
295
|
+
string += "<td nowrap>#{line.numeric? && line.include?(".") ? line.to_f.signif(3) : (line.nil? ? line : line.gsub(" ","<br />"))}</td>"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
string += "</tr>"
|
299
|
+
string += "</table></td>"
|
300
|
+
response['Content-Type'] = "application/json"
|
301
|
+
return JSON.pretty_generate(:prediction => [string])
|
255
302
|
end
|
256
303
|
end
|
257
304
|
|
258
|
-
get
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
305
|
+
# get individual compound details
|
306
|
+
get '/prediction/:neighbor/details/?' do
|
307
|
+
@compound = OpenTox::Compound.find params[:neighbor]
|
308
|
+
@smiles = @compound.smiles
|
309
|
+
begin
|
310
|
+
@names = @compound.names.nil? ? "No names for this compound available." : @compound.names
|
311
|
+
rescue
|
312
|
+
@names = "No names for this compound available."
|
313
|
+
end
|
314
|
+
@inchi = @compound.inchi
|
315
|
+
|
316
|
+
haml :details, :layout => false
|
267
317
|
end
|
268
318
|
|
269
|
-
get '/license' do
|
319
|
+
get '/predict/license' do
|
270
320
|
@license = RDiscount.new(File.read("LICENSE.md")).to_html
|
271
321
|
haml :license, :layout => false
|
272
322
|
end
|
273
323
|
|
274
|
-
get '/faq' do
|
324
|
+
get '/predict/faq' do
|
275
325
|
@faq = RDiscount.new(File.read("FAQ.md")).to_html
|
276
|
-
haml :faq
|
326
|
+
haml :faq#, :layout => false
|
327
|
+
end
|
328
|
+
|
329
|
+
get '/predict/help' do
|
330
|
+
haml :help
|
277
331
|
end
|
278
332
|
|
279
333
|
get '/style.css' do
|
@@ -281,3 +335,12 @@ get '/style.css' do
|
|
281
335
|
scss :style
|
282
336
|
end
|
283
337
|
|
338
|
+
# for swagger representation
|
339
|
+
get '/api/swagger-ui.css' do
|
340
|
+
headers 'Content-Type' => 'text/css; charset=utf-8'
|
341
|
+
scss :style
|
342
|
+
end
|
343
|
+
|
344
|
+
get '/IST_logo_s.png' do
|
345
|
+
redirect to('/images/IST_logo_s.png')
|
346
|
+
end
|