lazar-rest 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.yardopts +5 -0
- data/ChangeLog +9 -0
- data/Gemfile +6 -0
- data/LICENSE +674 -0
- data/README.md +54 -0
- data/VERSION +1 -0
- data/api/api.json +1209 -0
- data/config.ru +5 -0
- data/lazar-rest.gemspec +26 -0
- data/lib/aa.rb +82 -0
- data/lib/api.rb +9 -0
- data/lib/compound.rb +64 -0
- data/lib/dataset.rb +47 -0
- data/lib/feature.rb +25 -0
- data/lib/lazar-rest.rb +37 -0
- data/lib/model.rb +50 -0
- data/lib/nanoparticle.rb +25 -0
- data/lib/report.rb +209 -0
- data/lib/substance.rb +25 -0
- data/lib/validation.rb +71 -0
- data/test/aa.rb +23 -0
- data/test/all.rb +5 -0
- data/test/api.rb +13 -0
- data/test/compound.rb +54 -0
- data/test/data/test_03_post_descriptor_file.result +152 -0
- data/test/descriptor.rb +48 -0
- data/test/model.rb +17 -0
- data/test/setup.rb +8 -0
- data/test/validation.rb +17 -0
- data/unicorn.rb +2 -0
- data/views/model_details.haml +121 -0
- metadata +176 -0
data/config.ru
ADDED
data/lazar-rest.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "lazar-rest"
|
5
|
+
s.version = File.read("./VERSION")
|
6
|
+
s.authors = ["Christoph Helma","Micha Rautenberg","Denis Gebele"]
|
7
|
+
s.email = ["supprot@in-silico.ch"]
|
8
|
+
s.homepage = "http://github.com/opentox/lazar-rest"
|
9
|
+
s.summary = %q{lazar-rest}
|
10
|
+
s.description = %q{REST Interface for Lazar Toxicology Predictions}
|
11
|
+
s.license = 'GPL-3'
|
12
|
+
|
13
|
+
s.rubyforge_project = "lazar-rest"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.required_ruby_version = '>= 2.0.0'
|
18
|
+
|
19
|
+
s.add_runtime_dependency "lazar"
|
20
|
+
s.add_runtime_dependency "qsar-report"
|
21
|
+
s.add_runtime_dependency "sinatra"
|
22
|
+
s.add_runtime_dependency "haml"
|
23
|
+
s.add_runtime_dependency "sass"
|
24
|
+
s.add_runtime_dependency "unicorn"
|
25
|
+
s.add_runtime_dependency 'rack-cors'
|
26
|
+
end
|
data/lib/aa.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
post "/aa/authenticate/?" do
|
2
|
+
mime_types = ["text/plain"]
|
3
|
+
bad_request_error "Mime type #{@accept} not supported here. Please request data as #{mime_types.join(', ')}." unless mime_types.include? @accept
|
4
|
+
bad_request_error "Please send formdata username." unless params[:username]
|
5
|
+
bad_request_error "Please send formdata password." unless params[:password]
|
6
|
+
case @accept
|
7
|
+
when "text/plain"
|
8
|
+
if OpenTox::Authorization.authenticate(params[:username], params[:password])
|
9
|
+
return OpenTox::RestClientWrapper.subjectid
|
10
|
+
else
|
11
|
+
return nil
|
12
|
+
end
|
13
|
+
else
|
14
|
+
bad_request_error "'#{@accept}' is not a supported content type."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
post "/aa/logout/?" do
|
19
|
+
mime_types = ["text/plain"]
|
20
|
+
bad_request_error "Mime type #{@accept} not supported here. Please request data as #{mime_types.join(', ')}." unless mime_types.include? @accept
|
21
|
+
bad_request_error "Please send formdata subjectid." unless params[:subjectid]
|
22
|
+
case @accept
|
23
|
+
when "text/plain"
|
24
|
+
if OpenTox::Authorization.logout(params[:subjectid])
|
25
|
+
return "Successfully logged out. \n"
|
26
|
+
else
|
27
|
+
return "Logout failed.\n"
|
28
|
+
end
|
29
|
+
else
|
30
|
+
bad_request_error "'#{@accept}' is not a supported content type."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module OpenTox
|
35
|
+
|
36
|
+
AA = "https://opensso.in-silico.ch"
|
37
|
+
|
38
|
+
module Authorization
|
39
|
+
#Authentication against OpenSSO. Returns token. Requires Username and Password.
|
40
|
+
# @param user [String] Username
|
41
|
+
# @param pw [String] Password
|
42
|
+
# @return [Boolean] true if successful
|
43
|
+
def self.authenticate(user, pw)
|
44
|
+
begin
|
45
|
+
res = RestClientWrapper.post("#{AA}/auth/authenticate",{:username=>user, :password => pw},{:subjectid => ""}).sub("token.id=","").sub("\n","")
|
46
|
+
if is_token_valid(res)
|
47
|
+
RestClientWrapper.subjectid = res
|
48
|
+
return true
|
49
|
+
else
|
50
|
+
bad_request_error "Authentication failed #{res.inspect}"
|
51
|
+
end
|
52
|
+
rescue
|
53
|
+
bad_request_error "Authentication failed #{res.inspect}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#Logout on opensso. Make token invalid. Requires token
|
58
|
+
# @param [String] subjectid the subjectid
|
59
|
+
# @return [Boolean] true if logout is OK
|
60
|
+
def self.logout(subjectid=RestClientWrapper.subjectid)
|
61
|
+
begin
|
62
|
+
out = RestClientWrapper.post("#{AA}/auth/logout", :subjectid => subjectid)
|
63
|
+
return true unless is_token_valid(subjectid)
|
64
|
+
rescue
|
65
|
+
return false
|
66
|
+
end
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
|
70
|
+
#Checks if a token is a valid token
|
71
|
+
# @param [String]subjectid subjectid from openSSO session
|
72
|
+
# @return [Boolean] subjectid is valid or not.
|
73
|
+
def self.is_token_valid(subjectid=RestClientWrapper.subjectid)
|
74
|
+
begin
|
75
|
+
return true if RestClientWrapper.post("#{AA}/auth/isTokenValid",:tokenid => subjectid) == "boolean=true\n"
|
76
|
+
rescue #do rescue because openSSO throws 401
|
77
|
+
return false
|
78
|
+
end
|
79
|
+
return false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/api.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# route to swagger API file
|
2
|
+
get "/api/api.json" do
|
3
|
+
response['Content-Type'] = "application/json"
|
4
|
+
api_file = File.join("api", "api.json")
|
5
|
+
bad_request_error "API Documentation in Swagger JSON is not implemented.", uri("/#{SERVICE}/api") unless File.exists?(api_file)
|
6
|
+
api_hash = JSON.parse(File.read(api_file))
|
7
|
+
api_hash["host"] = request.env['HTTP_HOST']
|
8
|
+
return api_hash.to_json
|
9
|
+
end
|
data/lib/compound.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Get a list of a single or all descriptors
|
2
|
+
# @param [Header] Accept one of text/plain, application/json
|
3
|
+
# @param [Path] Descriptor name or descriptor ID (e.G.: Openbabel.HBA1, 5755f8eb3cf99a00d8fedf2f)
|
4
|
+
# @return [text/plain, application/json] list of all prediction models
|
5
|
+
get "/compound/descriptor/?:descriptor?" do
|
6
|
+
case @accept
|
7
|
+
when "application/json"
|
8
|
+
return "#{JSON.pretty_generate PhysChem::DESCRIPTORS} " unless params[:descriptor]
|
9
|
+
return PhysChem.find_by(:name => params[:descriptor]).to_json if PhysChem::DESCRIPTORS.include?(params[:descriptor])
|
10
|
+
return PhysChem.find(params[:descriptor]).to_json if PhysChem.find(params[:descriptor])
|
11
|
+
else
|
12
|
+
return PhysChem::DESCRIPTORS.collect{|k, v| "#{k}: #{v}\n"} unless params[:descriptor]
|
13
|
+
return PhysChem::DESCRIPTORS[params[:descriptor]] if PhysChem::DESCRIPTORS.include?(params[:descriptor])
|
14
|
+
return "#{PhysChem.find(params[:descriptor]).name}: #{PhysChem.find(params[:descriptor]).description}" if PhysChem.find(params[:descriptor])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
post "/compound/descriptor/?" do
|
19
|
+
bad_request_error "Missing Parameter " unless params[:identifier] && params[:descriptor]
|
20
|
+
descriptors = params['descriptor'].split(',')
|
21
|
+
compound = Compound.from_smiles params[:identifier]
|
22
|
+
physchem_descriptors = []
|
23
|
+
descriptors.each do |descriptor|
|
24
|
+
physchem_descriptors << PhysChem.find_by(:name => descriptor)
|
25
|
+
end
|
26
|
+
result = compound.physchem physchem_descriptors
|
27
|
+
csv = result.collect{|k,v| "\"#{PhysChem.find(k).name}\",#{v}" }.join("\n")
|
28
|
+
csv = "SMILES,#{params[:identifier]}\n#{csv}" if params[:identifier]
|
29
|
+
case @accept
|
30
|
+
when "text/csv","application/csv"
|
31
|
+
return csv
|
32
|
+
when "application/json"
|
33
|
+
result_hash = result.collect{|k,v| {"#{PhysChem.find(k).name}" => "#{v}"}} # result.collect{|k,v| "\"#{PhysChem.find(k).name}\"" => "#{v}"}.join(",")
|
34
|
+
data = {"compound" => {"SMILES" => "#{params[:identifier]}"}}
|
35
|
+
data["compound"]["InChI"] = "#{compound.inchi}" if compound.inchi
|
36
|
+
data["compound"]["results"] = result_hash
|
37
|
+
return JSON.pretty_generate(data)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
get %r{/compound/(.+)} do |inchi|
|
42
|
+
bad_request_error "Input parameter #{inchi} is not an InChI" unless inchi.match(/^InChI=/)
|
43
|
+
compound = Compound.from_inchi URI.unescape(inchi)
|
44
|
+
response['Content-Type'] = @accept
|
45
|
+
case @accept
|
46
|
+
when "application/json"
|
47
|
+
return JSON.pretty_generate JSON.parse(compound.to_json)
|
48
|
+
when "chemical/x-daylight-smiles"
|
49
|
+
return compound.smiles
|
50
|
+
when "chemical/x-inchi"
|
51
|
+
return compound.inchi
|
52
|
+
when "chemical/x-mdl-sdfile"
|
53
|
+
return compound.sdf
|
54
|
+
when "chemical/x-mdl-molfile"
|
55
|
+
when "image/png"
|
56
|
+
return compound.png
|
57
|
+
when "image/svg+xml"
|
58
|
+
return compound.svg
|
59
|
+
when "text/plain"
|
60
|
+
return "#{compound.names}\n"
|
61
|
+
else
|
62
|
+
return compound.inspect
|
63
|
+
end
|
64
|
+
end
|
data/lib/dataset.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Get all datasets
|
2
|
+
get "/dataset/?" do
|
3
|
+
datasets = Dataset.all
|
4
|
+
case @accept
|
5
|
+
when "text/uri-list"
|
6
|
+
uri_list = datasets.collect{|dataset| uri("/dataset/#{dataset.id}")}
|
7
|
+
return uri_list.join("\n") + "\n"
|
8
|
+
when "application/json"
|
9
|
+
datasets = JSON.parse datasets.to_json
|
10
|
+
datasets.each_index do |idx|
|
11
|
+
datasets[idx][:URI] = uri("/dataset/#{datasets[idx]["_id"]["$oid"]}")
|
12
|
+
end
|
13
|
+
return datasets.to_json
|
14
|
+
else
|
15
|
+
bad_request_error "Mime type #{@accept} is not supported."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Get a dataset
|
20
|
+
get "/dataset/:id/?" do
|
21
|
+
dataset = Dataset.find :id => params[:id]
|
22
|
+
resource_not_found_error "Dataset with id: #{params[:id]} not found." unless dataset
|
23
|
+
case @accept
|
24
|
+
when "application/json"
|
25
|
+
dataset.data_entries.each do |k, v|
|
26
|
+
dataset.data_entries[k][:URI] = uri("/substance/#{k}")
|
27
|
+
end
|
28
|
+
dataset[:URI] = uri("/dataset/#{dataset.id}")
|
29
|
+
dataset[:substances] = uri("/dataset/#{dataset.id}/substances")
|
30
|
+
dataset[:features] = uri("/dataset/#{dataset.id}/features")
|
31
|
+
return dataset.to_json
|
32
|
+
when "text/csv", "application/csv"
|
33
|
+
return dataset.to_csv
|
34
|
+
else
|
35
|
+
bad_request_error "Mime type #{@accept} is not supported."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get a dataset attribute. One of compounds, nanoparticles, substances, features
|
40
|
+
get "/dataset/:id/:attribute/?" do
|
41
|
+
dataset = Dataset.find :id => params[:id]
|
42
|
+
resource_not_found_error "Dataset with id: #{params[:id]} not found." unless dataset
|
43
|
+
attribs = ["compounds", "nanoparticles", "substances", "features"]
|
44
|
+
return "Attribute '#{params[:attribute]}' is not available. Choose one of #{attribs.join(', ')}." unless attribs.include? params[:attribute]
|
45
|
+
out = dataset.send(params[:attribute])
|
46
|
+
return out.to_json
|
47
|
+
end
|
data/lib/feature.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Get all Features
|
2
|
+
get "/feature/?" do
|
3
|
+
features = Feature.all
|
4
|
+
case @accept
|
5
|
+
when "text/uri-list"
|
6
|
+
uri_list = features.collect{|feature| uri("/feature/#{feature.id}")}
|
7
|
+
return uri_list.join("\n") + "\n"
|
8
|
+
when "application/json"
|
9
|
+
features = JSON.parse features.to_json
|
10
|
+
features.each_index do |idx|
|
11
|
+
features[idx][:URI] = uri("/feature/#{features[idx]["_id"]["$oid"]}")
|
12
|
+
end
|
13
|
+
return features.to_json
|
14
|
+
else
|
15
|
+
bad_request_error "Mime type #{@accept} is not supported."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Get a feature
|
20
|
+
get "/feature/:id/?" do
|
21
|
+
feature = Feature.find :id => params[:id]
|
22
|
+
resource_not_found_error "Feature with id: #{params[:id]} not found." unless feature
|
23
|
+
feature[:URI] = uri("/feature/#{feature.id}")
|
24
|
+
return feature.to_json
|
25
|
+
end
|
data/lib/lazar-rest.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "sinatra"
|
2
|
+
require "../lazar/lib/lazar.rb"
|
3
|
+
require "../qsar-report/lib/qsar-report.rb"
|
4
|
+
include OpenTox
|
5
|
+
|
6
|
+
require 'rack/cors'
|
7
|
+
|
8
|
+
|
9
|
+
set :show_exceptions => false
|
10
|
+
|
11
|
+
# add CORS support for swagger
|
12
|
+
use Rack::Cors do |config|
|
13
|
+
config.allow do |allow|
|
14
|
+
allow.origins '*'
|
15
|
+
allow.resource "/#{SERVICE}/*",
|
16
|
+
:methods => [:head, :get, :post, :put, :delete, :options],
|
17
|
+
:headers => :any,
|
18
|
+
:max_age => 0
|
19
|
+
end
|
20
|
+
end
|
21
|
+
before do
|
22
|
+
@accept = request.env['HTTP_ACCEPT']
|
23
|
+
response['Content-Type'] = @accept
|
24
|
+
end
|
25
|
+
|
26
|
+
[
|
27
|
+
"aa.rb",
|
28
|
+
"api.rb",
|
29
|
+
"compound.rb",
|
30
|
+
"dataset.rb",
|
31
|
+
"feature.rb",
|
32
|
+
"model.rb",
|
33
|
+
"nanoparticle.rb",
|
34
|
+
"report.rb",
|
35
|
+
"substance.rb",
|
36
|
+
"validation.rb"
|
37
|
+
].each{ |f| require_relative f }
|
data/lib/model.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
# Get a list of all prediction models
|
3
|
+
# @param [Header] Accept one of text/uri-list,
|
4
|
+
# @return [text/uri-list] list of all prediction models
|
5
|
+
get "/model/?" do
|
6
|
+
models = Model::Prediction.all
|
7
|
+
case @accept
|
8
|
+
when "text/uri-list"
|
9
|
+
uri_list = models.collect{|model| uri("/model/#{model.model_id}")}
|
10
|
+
return uri_list.join("\n") + "\n"
|
11
|
+
when "application/json"
|
12
|
+
models = JSON.parse models.to_json
|
13
|
+
models.each_index do |idx|
|
14
|
+
models[idx][:URI] = uri("/model/#{models[idx]["model_id"]["$oid"]}")
|
15
|
+
models[idx][:crossvalidation_uri] = uri("/crossvalidation/#{models[idx]["crossvalidation_id"]["$oid"]}") if models[idx]["crossvalidation_id"]
|
16
|
+
end
|
17
|
+
return models.to_json
|
18
|
+
else
|
19
|
+
bad_request_error "Mime type #{@accept} is not supported."
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
get "/model/:id/?" do
|
24
|
+
model = Model::Lazar.find params[:id]
|
25
|
+
resource_not_found_error "Model with id: #{params[:id]} not found." unless model
|
26
|
+
model[:URI] = uri("/model/#{model.id}")
|
27
|
+
model[:neighbor_algorithm_parameters][:feature_dataset_uri] = uri("/dataset/#{model[:neighbor_algorithm_parameters][:feature_dataset_id]}") if model[:neighbor_algorithm_parameters][:feature_dataset_id]
|
28
|
+
model[:training_dataset_uri] = uri("/dataset/#{model.training_dataset_id}") if model.training_dataset_id
|
29
|
+
model[:prediction_feature_uri] = uri("/dataset/#{model.prediction_feature_id}") if model.prediction_feature_id
|
30
|
+
return model.to_json
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
post "/model/:id/?" do
|
35
|
+
identifier = params[:identifier].split(",")
|
36
|
+
begin
|
37
|
+
# get compound from SMILES
|
38
|
+
compounds = identifier.collect{ |i| Compound.from_smiles i.strip }
|
39
|
+
rescue
|
40
|
+
@error_report = "Attention, '#{params[:identifier]}' is not a valid SMILES string."
|
41
|
+
return @error_report
|
42
|
+
end
|
43
|
+
model = Model::Lazar.find params[:id]
|
44
|
+
batch = {}
|
45
|
+
compounds.each do |compound|
|
46
|
+
prediction = model.predict(compound)
|
47
|
+
batch[compound] = {:id => compound.id, :inchi => compound.inchi, :smiles => compound.smiles, :model => model, :prediction => prediction}
|
48
|
+
end
|
49
|
+
return batch.to_json
|
50
|
+
end
|
data/lib/nanoparticle.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Get all Nanoparticles
|
2
|
+
get "/nanoparticle/?" do
|
3
|
+
nanoparticles = Nanoparticle.all
|
4
|
+
case @accept
|
5
|
+
when "text/uri-list"
|
6
|
+
uri_list = nanoparticles.collect{|nanoparticle| uri("/nanoparticle/#{nanoparticle.id}")}
|
7
|
+
return uri_list.join("\n") + "\n"
|
8
|
+
when "application/json"
|
9
|
+
nanoparticles = JSON.parse nanoparticles.to_json
|
10
|
+
nanoparticles.each_index do |idx|
|
11
|
+
nanoparticles[idx][:URI] = uri("/nanoparticle/#{nanoparticles[idx]["_id"]["$oid"]}")
|
12
|
+
end
|
13
|
+
return nanoparticles.to_json
|
14
|
+
else
|
15
|
+
bad_request_error "Mime type #{@accept} is not supported."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Get a nanoparticle
|
20
|
+
get "/nanoparticle/:id/?" do
|
21
|
+
nanoparticle = Nanoparticle.find :id => params[:id]
|
22
|
+
resource_not_found_error "Nanoparticle with id: #{params[:id]} not found." unless nanoparticle
|
23
|
+
nanoparticle[:URI] = uri("/nanoparticle/#{nanoparticle.id}")
|
24
|
+
return nanoparticle.to_json
|
25
|
+
end
|
data/lib/report.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
# Get a list of all possible reports to prediction models
|
2
|
+
# @param [Header] Accept one of text/uri-list,
|
3
|
+
# @return [text/uri-list] list of all prediction models
|
4
|
+
get "/report/?" do
|
5
|
+
models = Model::Prediction.all
|
6
|
+
case @accept
|
7
|
+
when "text/uri-list"
|
8
|
+
uri_list = models.collect{|model| uri("/report/#{model.model_id}")}
|
9
|
+
return uri_list.join("\n") + "\n"
|
10
|
+
when "application/json"
|
11
|
+
reports = [{}]
|
12
|
+
#models = JSON.parse models.to_json
|
13
|
+
models.each_index do |idx|
|
14
|
+
reports[idx] = {}
|
15
|
+
reports[idx][:URI] = uri("/report/#{models[idx]["model_id"]}")
|
16
|
+
reports[idx][:repeated_crossvalidation_uri] = uri("/validation/repeatedcrossvalidation/#{models[idx]["repeated_crossvalidation_id"]}") if models[idx]["repeated_crossvalidation_id"]
|
17
|
+
reports[idx][:leave_one_out_validation_uri] = uri("/validation/leaveoneoutvalidation/#{models[idx]["leave_one_out_validation_id"]}") if models[idx]["leave_one_out_validation_id"]
|
18
|
+
reports[idx][:training_dataset_URI] = uri("/dataset/#{models[idx].training_dataset.id}") if models[idx].training_dataset.id
|
19
|
+
end
|
20
|
+
return reports.to_json
|
21
|
+
else
|
22
|
+
bad_request_error "Mime type #{@accept} is not supported."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
get "/report/:id/?" do
|
27
|
+
model = Model::Lazar.find params[:id]
|
28
|
+
resource_not_found_error "Model with id: #{params[:id]} not found." unless model
|
29
|
+
prediction_model = Model::Prediction.find_by :model_id => params[:id]
|
30
|
+
validation_template = File.join(File.dirname(__FILE__),"../views/model_details.haml")
|
31
|
+
|
32
|
+
if File.directory?("#{File.dirname(__FILE__)}/../../lazar")
|
33
|
+
lazar_commit = `cd #{File.dirname(__FILE__)}/../../lazar; git rev-parse HEAD`.strip
|
34
|
+
lazar_commit = "https://github.com/opentox/lazar/tree/#{lazar_commit}"
|
35
|
+
else
|
36
|
+
lazar_commit = "https://github.com/opentox/lazar/releases/tag/v#{Gem.loaded_specs["lazar"].version}"
|
37
|
+
end
|
38
|
+
|
39
|
+
report = OpenTox::QMRFReport.new
|
40
|
+
|
41
|
+
# QSAR Identifier Title 1.1
|
42
|
+
report.value "QSAR_title", "Lazar model for #{prediction_model.species} #{prediction_model.endpoint}"
|
43
|
+
|
44
|
+
# Software coding the model 1.3
|
45
|
+
report.change_catalog :software_catalog, :firstsoftware, {:name => "lazar", :description => "lazar Lazy Structure- Activity Relationships", :number => "1", :url => "https://lazar.in-silico.ch", :contact => "info@in-silico.ch"}
|
46
|
+
report.ref_catalog :QSAR_software, :software_catalog, :firstsoftware
|
47
|
+
|
48
|
+
# Date of QMRF 2.1
|
49
|
+
report.value "qmrf_date", "#{Time.now.strftime('%d %B %Y')}"
|
50
|
+
|
51
|
+
# QMRF author(s) and contact details 2.1
|
52
|
+
report.change_catalog :authors_catalog, :firstauthor, {:name => "Christoph Helma", :affiliation => "in silico toxicology gmbh", :contact => "Rastatterstr. 41, CH-4057 Basel", :email => "info@in-silico.ch", :number => "1", :url => "www.in-silico.ch"}
|
53
|
+
report.ref_catalog :qmrf_authors, :authors_catalog, :firstauthor
|
54
|
+
|
55
|
+
# Model developer(s) and contact details 2.5
|
56
|
+
report.change_catalog :authors_catalog, :modelauthor, {:name => "Christoph Helma", :affiliation => "in silico toxicology gmbh", :contact => "Rastatterstr. 41, CH-4057 Basel", :email => "info@in-silico.ch", :number => "1", :url => "www.in-silico.ch"}
|
57
|
+
report.ref_catalog :model_authors, :authors_catalog, :modelauthor
|
58
|
+
|
59
|
+
# Date of model development and/or publication 2.6
|
60
|
+
report.value "model_date", "#{Time.parse(model.created_at.to_s).strftime('%Y')}"
|
61
|
+
|
62
|
+
# Reference(s) to main scientific papers and/or software package 2.7
|
63
|
+
report.change_catalog :publications_catalog, :publications_catalog_1, {:title => "Maunz, Guetlein, Rautenberg, Vorgrimmler, Gebele and Helma (2013), lazar: a modular predictive toxicology framework ", :url => "http://dx.doi.org/10.3389/fphar.2013.00038"}
|
64
|
+
report.ref_catalog :references, :publications_catalog, :publications_catalog_1
|
65
|
+
|
66
|
+
# Reference(s) to main scientific papers and/or software package 2.7
|
67
|
+
report.change_catalog :publications_catalog, :publications_catalog_2, {:title => "Maunz A and Helma C (2008) Prediction of chemical toxicity with local support vector regression and activity-specific kernels. SAR & QSAR in Environmental Research 19 (5-6), 413-431", :url => "http://dx.doi.org/10.1080/10629360802358430"}
|
68
|
+
report.ref_catalog :references, :publications_catalog, :publications_catalog_2
|
69
|
+
|
70
|
+
# Species 3.1
|
71
|
+
report.value "model_species", prediction_model.species
|
72
|
+
|
73
|
+
# Endpoint 3.2
|
74
|
+
report.change_catalog :endpoints_catalog, :endpoints_catalog_1, {:name => prediction_model.endpoint, :group => ""}
|
75
|
+
report.ref_catalog :model_endpoint, :endpoints_catalog, :endpoints_catalog_1
|
76
|
+
|
77
|
+
# Endpoint Units 3.4
|
78
|
+
report.value "endpoint_units", "#{prediction_model.unit}"
|
79
|
+
|
80
|
+
model_type = model.class.to_s.gsub('OpenTox::Model::Lazar','')
|
81
|
+
|
82
|
+
# Type of model 4.1
|
83
|
+
report.value "algorithm_type", "#{model_type}"
|
84
|
+
|
85
|
+
# Explicit algorithm 4.2
|
86
|
+
report.change_catalog :algorithms_catalog, :algorithms_catalog_1, {:definition => "see Helma 2016 and lazar.in-silico.ch, submitted version: #{lazar_commit}", :description => "Neighbor algorithm: #{model.neighbor_algorithm.gsub('_',' ').titleize}#{(model.neighbor_algorithm_parameters[:min_sim] ? ' with similarity > ' + model.neighbor_algorithm_parameters[:min_sim].to_s : '')}"}
|
87
|
+
report.ref_catalog :algorithm_explicit, :algorithms_catalog, :algorithms_catalog_1
|
88
|
+
report.change_catalog :algorithms_catalog, :algorithms_catalog_3, {:definition => "see Helma 2016 and lazar.in-silico.ch, submitted version: #{lazar_commit}", :description => "modified k-nearest neighbor #{model_type}"}
|
89
|
+
report.ref_catalog :algorithm_explicit, :algorithms_catalog, :algorithms_catalog_3
|
90
|
+
if model.prediction_algorithm_parameters
|
91
|
+
pred_algorithm_params = (model.prediction_algorithm_parameters[:method] == "rf" ? "random forest" : model.prediction_algorithm_parameters[:method])
|
92
|
+
end
|
93
|
+
report.change_catalog :algorithms_catalog, :algorithms_catalog_2, {:definition => "see Helma 2016 and lazar.in-silico.ch, submitted version: #{lazar_commit}", :description => "Prediction algorithm: #{model.prediction_algorithm.gsub('OpenTox::Algorithm::','').gsub('_',' ').gsub('.', ' with ')} #{(pred_algorithm_params ? pred_algorithm_params : '')}"}
|
94
|
+
report.ref_catalog :algorithm_explicit, :algorithms_catalog, :algorithms_catalog_2
|
95
|
+
|
96
|
+
# Descriptors in the model 4.3
|
97
|
+
if model.neighbor_algorithm_parameters[:type]
|
98
|
+
report.change_catalog :descriptors_catalog, :descriptors_catalog_1, {:description => "", :name => "#{model.neighbor_algorithm_parameters[:type]}", :publication_ref => "", :units => ""}
|
99
|
+
report.ref_catalog :algorithms_descriptors, :descriptors_catalog, :descriptors_catalog_1
|
100
|
+
end
|
101
|
+
|
102
|
+
# Descriptor selection 4.4
|
103
|
+
report.value "descriptors_selection", "#{model.feature_selection_algorithm.gsub('_',' ')} #{model.feature_selection_algorithm_parameters.collect{|k,v| k.to_s + ': ' + v.to_s}.join(', ')}" if model.feature_selection_algorithm
|
104
|
+
|
105
|
+
# Algorithm and descriptor generation 4.5
|
106
|
+
report.value "descriptors_generation", "exhaustive breadth first search for paths in chemical graphs (simplified MolFea algorithm)"
|
107
|
+
|
108
|
+
# Software name and version for descriptor generation 4.6
|
109
|
+
report.change_catalog :software_catalog, :software_catalog_2, {:name => "lazar, submitted version: #{lazar_commit}", :description => "simplified MolFea algorithm", :number => "2", :url => "https://lazar.in-silico.ch", :contact => "info@in-silico.ch"}
|
110
|
+
report.ref_catalog :descriptors_generation_software, :software_catalog, :software_catalog_2
|
111
|
+
|
112
|
+
# Chemicals/Descriptors ratio 4.7
|
113
|
+
report.value "descriptors_chemicals_ratio", "not applicable (classification based on activities of neighbors, descriptors are used for similarity calculation)"
|
114
|
+
|
115
|
+
# Description of the applicability domain of the model 5.1
|
116
|
+
report.value "app_domain_description", "<html><head></head><body>
|
117
|
+
<p>
|
118
|
+
The applicability domain (AD) of the training set is characterized by
|
119
|
+
the confidence index of a prediction (high confidence index: close to
|
120
|
+
the applicability domain of the training set/reliable prediction, low
|
121
|
+
confidence: far from the applicability domain of the
|
122
|
+
trainingset/unreliable prediction). The confidence index considers (i)
|
123
|
+
the similarity and number of neighbors and (ii) contradictory examples
|
124
|
+
within the neighbors. A formal definition can be found in Helma 2006.
|
125
|
+
</p>
|
126
|
+
<p>
|
127
|
+
The reliability of predictions decreases gradually with increasing
|
128
|
+
distance from the applicability domain (i.e. decreasing confidence index)
|
129
|
+
</p>
|
130
|
+
</body>
|
131
|
+
</html>"
|
132
|
+
|
133
|
+
# Method used to assess the applicability domain 5.2
|
134
|
+
report.value "app_domain_method", "see Helma 2006 and Maunz 2008"
|
135
|
+
|
136
|
+
# Software name and version for applicability domain assessment 5.3
|
137
|
+
report.change_catalog :software_catalog, :software_catalog_3, {:name => "lazar, submitted version: #{lazar_commit}", :description => "integrated into main lazar algorithm", :number => "3", :url => "https://lazar.in-silico.ch", :contact => "info@in-silico.ch"}
|
138
|
+
report.ref_catalog :app_domain_software, :software_catalog, :software_catalog_3
|
139
|
+
|
140
|
+
# Limits of applicability 5.4
|
141
|
+
report.value "applicability_limits", "Predictions with low confidence index, unknown substructures and neighbors that might act by different mechanisms"
|
142
|
+
|
143
|
+
# Availability of the training set 6.1
|
144
|
+
report.change_attributes "training_set_availability", {:answer => "Yes"}
|
145
|
+
|
146
|
+
# Available information for the training set 6.2
|
147
|
+
report.change_attributes "training_set_data", {:cas => "Yes", :chemname => "Yes", :formula => "Yes", :inchi => "Yes", :mol => "Yes", :smiles => "Yes"}
|
148
|
+
|
149
|
+
# Data for each descriptor variable for the training set 6.3
|
150
|
+
report.change_attributes "training_set_descriptors", {:answer => "No"}
|
151
|
+
|
152
|
+
# Data for the dependent variable for the training set 6.4
|
153
|
+
report.change_attributes "dependent_var_availability", {:answer => "All"}
|
154
|
+
|
155
|
+
# Other information about the training set 6.5
|
156
|
+
report.value "other_info", "#{prediction_model.source}"
|
157
|
+
|
158
|
+
# Pre-processing of data before modelling 6.6
|
159
|
+
report.value "preprocessing", (model.class == OpenTox::Model::LazarRegression ? "-log10 transformation" : "none")
|
160
|
+
|
161
|
+
# Robustness - Statistics obtained by leave-many-out cross-validation 6.9
|
162
|
+
if prediction_model.repeated_crossvalidation
|
163
|
+
crossvalidations = prediction_model.crossvalidations
|
164
|
+
out = haml File.read(validation_template), :layout=> false, :locals => {:model => prediction_model}
|
165
|
+
report.value "lmo", out
|
166
|
+
end
|
167
|
+
|
168
|
+
# Mechanistic basis of the model 8.1
|
169
|
+
report.value "mechanistic_basis","<html><head></head><body>
|
170
|
+
<p>
|
171
|
+
Compounds with similar structures (neighbors) are assumed to have
|
172
|
+
similar activities as the query compound. For the determination of
|
173
|
+
activity specific similarities only statistically relevant subtructures
|
174
|
+
(paths) are used. For this reason there is a priori no bias towards
|
175
|
+
specific mechanistic hypothesis.
|
176
|
+
</p>
|
177
|
+
</body>
|
178
|
+
</html>"
|
179
|
+
|
180
|
+
# A priori or a posteriori mechanistic interpretation 8.2
|
181
|
+
report.value "mechanistic_basis_comments","a posteriori for individual predictions"
|
182
|
+
|
183
|
+
# Other information about the mechanistic interpretation 8.3
|
184
|
+
report.value "mechanistic_basis_info","<html><head></head><body><p>Hypothesis about biochemical mechanisms can be derived from individual
|
185
|
+
predictions by inspecting neighbors and relevant fragments.</p>
|
186
|
+
<p>Neighbors are compounds that are similar in respect to a certain
|
187
|
+
endpoint and it is likely that compounds with high similarity act by
|
188
|
+
similar mechanisms as the query compound. Links at the webinterface
|
189
|
+
prove an easy access to additional experimental data and literature
|
190
|
+
citations for the neighbors and the query structure.</p>
|
191
|
+
<p>Activating and deactivating parts of the query compound are highlighted
|
192
|
+
in red and green on the webinterface. Fragments that are unknown (or too
|
193
|
+
infrequent for statistical evaluation are marked in yellow and
|
194
|
+
additional statistical information about the individual fragments can be
|
195
|
+
retrieved. Please note that lazar predictions are based on neighbors and
|
196
|
+
not on fragments. Fragments and their statistical significance are used
|
197
|
+
for the calculation of activity specific similarities.</p>"
|
198
|
+
|
199
|
+
# Bibliography 9.2
|
200
|
+
report.ref_catalog :bibliography, :publications_catalog, :publications_catalog_1
|
201
|
+
report.ref_catalog :bibliography, :publications_catalog, :publications_catalog_2
|
202
|
+
report.change_catalog :publications_catalog, :publications_catalog_3, {:title => "Helma (2006), Lazy structure-activity relationships (lazar) for the prediction of rodent carcinogenicity and Salmonella mutagenicity.", :url => "http://dx.doi.org/10.1007/s11030-005-9001-5"}
|
203
|
+
report.ref_catalog :bibliography, :publications_catalog, :publications_catalog_3
|
204
|
+
|
205
|
+
# output
|
206
|
+
response['Content-Type'] = "application/xml"
|
207
|
+
return report.to_xml
|
208
|
+
|
209
|
+
end
|