opentox-ruby 0.0.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -39,3 +39,48 @@
39
39
 
40
40
  # Uncomment for verbose logging
41
41
  # :logger: debug
42
+ # :backtrace: 1
43
+
44
+
45
+ # OpenSSO Authorization
46
+ # set ":server: " to disable A&A
47
+ :authorization:
48
+ :server: "https://opensso.in-silico.ch"
49
+ :free_request: #request-method not controlled by A&A
50
+ - "GET"
51
+ :authenticate_request: #only for authenticated user
52
+ - "POST"
53
+ :authorize_request: #only for authenticated and authorizeduser
54
+ - "DELETE"
55
+ - "PUT"
56
+ # Exceptions:
57
+ :free_uris: #request-method for uri not controlled by A&A
58
+ ? - :GET
59
+ : - !ruby/regexp /localhost\/algorithm/
60
+ - "http://localhost/dataset"
61
+ - "http://localhost/model"
62
+ - "http://localhost/validation"
63
+ - "http://localhost/validation/crossvalidation"
64
+ - "http://localhost/validation/reach_report"
65
+ - "http://localhost/validation/reach_report/crossvalidation"
66
+ - "http://localhost/validation/report"
67
+ - "http://localhost/validation/report/crossvalidation"
68
+ - "http://localhost/validation/reach_report/qmrf"
69
+ ? - :GET
70
+ - :POST
71
+ : - !ruby/regexp /localhost\/toxcreate/
72
+ - !ruby/regexp /localhost\/task/
73
+ - !ruby/regexp /localhost\/compound/
74
+ ? - :PUT
75
+ : - !ruby/regexp /localhost\/task/
76
+
77
+ :authorize_exceptions: #request-method for uri only authenticated, no authorization
78
+ ? - :POST
79
+ : - !ruby/regexp /localhost\/algorithm/
80
+ - "http://localhost/dataset"
81
+ - "http://localhost/model"
82
+ - "http://localhost/validation"
83
+ - !ruby/regexp /localhost\/validation\/[a-z,A-Z,\/,_\-]*$/
84
+
85
+
86
+
@@ -0,0 +1,53 @@
1
+ <!DOCTYPE Policies PUBLIC "-//Sun Java System Access Manager7.1 2006Q3
2
+ Admin CLI DTD//EN" "jar://com/sun/identity/policy/policyAdmin.dtd">
3
+
4
+ <Policies>
5
+ <Policy name="policy_user" referralPolicy="false" active="true">
6
+ <Rule name="rule_user">
7
+ <ServiceName name="iPlanetAMWebAgentService" />
8
+ <ResourceName name="uri"/>
9
+ <AttributeValuePair>
10
+ <Attribute name="GET" />
11
+ <Value>allow</Value>
12
+ </AttributeValuePair>
13
+ <AttributeValuePair>
14
+ <Attribute name="POST" />
15
+ <Value>allow</Value>
16
+ </AttributeValuePair>
17
+ <AttributeValuePair>
18
+ <Attribute name="PUT" />
19
+ <Value>allow</Value>
20
+ </AttributeValuePair>
21
+ <AttributeValuePair>
22
+ <Attribute name="DELETE" />
23
+ <Value>allow</Value>
24
+ </AttributeValuePair>
25
+ </Rule>
26
+ <Subjects name="subjects_user" description="">
27
+ <Subject name="subject_user" type="LDAPUsers" includeType="inclusive">
28
+ <AttributeValuePair>
29
+ <Attribute name="Values"/>
30
+ <Value>uid=guest,ou=people,dc=opentox,dc=org</Value>
31
+ </AttributeValuePair>
32
+ </Subject>
33
+ </Subjects>
34
+ </Policy>
35
+ <Policy name="policy_group" referralPolicy="false" active="true">
36
+ <Rule name="rule_group">
37
+ <ServiceName name="iPlanetAMWebAgentService" />
38
+ <ResourceName name="uri"/>
39
+ <AttributeValuePair>
40
+ <Attribute name="GET" />
41
+ <Value>allow</Value>
42
+ </AttributeValuePair>
43
+ </Rule>
44
+ <Subjects name="subjects_group" description="">
45
+ <Subject name="subject_group" type="LDAPGroups" includeType="inclusive">
46
+ <AttributeValuePair>
47
+ <Attribute name="Values"/>
48
+ <Value>cn=member,ou=groups,dc=opentox,dc=org</Value>
49
+ </AttributeValuePair>
50
+ </Subject>
51
+ </Subjects>
52
+ </Policy>
53
+ </Policies>
@@ -0,0 +1,53 @@
1
+ <!DOCTYPE Policies PUBLIC "-//Sun Java System Access Manager7.1 2006Q3
2
+ Admin CLI DTD//EN" "jar://com/sun/identity/policy/policyAdmin.dtd">
3
+
4
+ <Policies>
5
+ <Policy name="policy_user" referralPolicy="false" active="true">
6
+ <Rule name="rule_user">
7
+ <ServiceName name="iPlanetAMWebAgentService" />
8
+ <ResourceName name="uri"/>
9
+ <AttributeValuePair>
10
+ <Attribute name="GET" />
11
+ <Value>allow</Value>
12
+ </AttributeValuePair>
13
+ <AttributeValuePair>
14
+ <Attribute name="POST" />
15
+ <Value>allow</Value>
16
+ </AttributeValuePair>
17
+ <AttributeValuePair>
18
+ <Attribute name="PUT" />
19
+ <Value>allow</Value>
20
+ </AttributeValuePair>
21
+ <AttributeValuePair>
22
+ <Attribute name="DELETE" />
23
+ <Value>allow</Value>
24
+ </AttributeValuePair>
25
+ </Rule>
26
+ <Subjects name="subjects_user" description="">
27
+ <Subject name="subject_user" type="LDAPUsers" includeType="inclusive">
28
+ <AttributeValuePair>
29
+ <Attribute name="Values"/>
30
+ <Value>uid=guest,ou=people,dc=opentox,dc=org</Value>
31
+ </AttributeValuePair>
32
+ </Subject>
33
+ </Subjects>
34
+ </Policy>
35
+ <Policy name="policy_group" referralPolicy="false" active="true">
36
+ <Rule name="rule_group">
37
+ <ServiceName name="iPlanetAMWebAgentService" />
38
+ <ResourceName name="uri"/>
39
+ <AttributeValuePair>
40
+ <Attribute name="GET" />
41
+ <Value>allow</Value>
42
+ </AttributeValuePair>
43
+ </Rule>
44
+ <Subjects name="subjects_group" description="">
45
+ <Subject name="subject_group" type="LDAPGroups" includeType="inclusive">
46
+ <AttributeValuePair>
47
+ <Attribute name="Values"/>
48
+ <Value>cn=member,ou=groups,dc=opentox,dc=org</Value>
49
+ </AttributeValuePair>
50
+ </Subject>
51
+ </Subjects>
52
+ </Policy>
53
+ </Policies>
data/lib/to-html.rb ADDED
@@ -0,0 +1,112 @@
1
+
2
+ OT_LOGO = "http://opentox.informatik.uni-freiburg.de/ot-logo.png"
3
+
4
+ class String
5
+
6
+ # encloses URI in text with with link tag
7
+ # @return [String] new text with marked links
8
+ def link_urls
9
+ self.gsub(/(?i)http(s?):\/\/[^\r\n\s']*/, '<a href=\0>\0</a>')
10
+ end
11
+ end
12
+
13
+ module OpenTox
14
+
15
+ # produces a html page for making web services browser friendly
16
+ # format of text (=string params) is preserved (e.g. line breaks)
17
+ # urls are marked as links
18
+ # @example post params:
19
+ # [ [ [:mandatory_param_1], [:mandatory_param_2], [:optional_param,"default_value"] ],
20
+ # [ [:alteranative_mandatory_param_1], [:alteranative_mandatory_param_2] ]
21
+ # ]
22
+ # @param [String] text this is the actual content,
23
+ # @param [optional,String] related_links info on related resources
24
+ # @param [optional,String] description general info
25
+ # @param [optional,Array] post_params, array of arrays containing info on POST operation, see example
26
+ # @return [String] html page
27
+ def self.text_to_html( text, subjectid=nil, related_links=nil, description=nil, post_params=nil )
28
+
29
+ # TODO add title as parameter
30
+ title = nil #$sinatra.url_for($sinatra.request.env['PATH_INFO'], :full) if $sinatra
31
+ html = "<html>"
32
+ html += "<title>"+title+"</title>" if title
33
+ html += "<img src="+OT_LOGO+"><body>"
34
+
35
+ if AA_SERVER
36
+ user = OpenTox::Authorization.get_user(subjectid) if subjectid
37
+ html += "<pre><p align=\"right\">"
38
+ unless user
39
+ html += "You are currently not logged in to "+$url_provider.url_for("",:full)+
40
+ ", <a href="+$url_provider.url_for("/login",:full)+">login</a>"
41
+ else
42
+ html += "You are logged in as '#{user}' to "+$url_provider.url_for("",:full)+
43
+ ", <a href="+$url_provider.url_for("/logout",:full)+">logout</a>"
44
+ end
45
+ html += " </p></pre>"
46
+ end
47
+
48
+ html += "<h3>Description</h3><pre><p>"+description.link_urls+"</p></pre>" if description
49
+ html += "<h3>Related links</h3><pre><p>"+related_links.link_urls+"</p></pre>" if related_links
50
+ if post_params
51
+ html += "<h3>POST parameters</h3>"
52
+ count = 0
53
+ post_params.each do |p|
54
+ html += "<pre><p>alternatively:</p></pre>" if count > 0
55
+ html += "<pre><p><table><thead><tr><th>param</th><th>default_value</th></tr></thead>"
56
+ p.each do |k,v|
57
+ html += "<tr><th>"+k.to_s+"</th><th>"+(v!=nil ? v.to_s : "<i>mandatory</i>")+"</th></tr>"
58
+ end
59
+ html += "</table></p></pre>"
60
+ count += 1
61
+ end
62
+ end
63
+ html += "<h3>Content</h3>" if description || related_links
64
+ html += "<pre><p style=\"padding:15px; border:10px solid \#5D308A\">"
65
+ html += text.link_urls
66
+ html += "</p></pre></body><html>"
67
+ html
68
+ end
69
+
70
+ def self.login( msg=nil )
71
+ html = "<html><title>Login</title><img src="+OT_LOGO+"><body>"
72
+ html += "<form method='POST' action='"+$url_provider.url_for("/login",:full)+"'>"
73
+ html += "<pre><p style=\"padding:15px; border:10px solid \#5D308A\">"
74
+ html += msg+"\n\n" if msg
75
+ html += "Please login to "+$url_provider.url_for("",:full)+"\n\n"
76
+ html += "<table border=0>"
77
+ html += "<tr><td>user:</td><td><input type='text' name='user' size='15' /></td></tr>"+
78
+ "<tr><td>password:</td><td><input type='password' name='password' size='15' /></td></tr>"+
79
+ #"<input type=hidden name=back_to value="+back_to.to_s+">"+
80
+ "<tr><td><input type='submit' value='Login' /></td></tr>"
81
+ html += "</table></p></pre></form></body><html>"
82
+ html
83
+ end
84
+ end
85
+
86
+ =begin
87
+ get '/logout/?' do
88
+ response.set_cookie("subjectid",{:value=>nil})
89
+ content_type "text/html"
90
+ content = "Sucessfully logged out from "+$url_provider.url_for("",:full)
91
+ OpenTox.text_to_html(content)
92
+ end
93
+
94
+ get '/login/?' do
95
+ content_type "text/html"
96
+ OpenTox.login
97
+ end
98
+
99
+ post '/login/?' do
100
+ subjectid = OpenTox::Authorization.authenticate(params[:user], params[:password])
101
+ if (subjectid)
102
+ response.set_cookie("subjectid",{:value=>subjectid})
103
+ content_type "text/html"
104
+ content = "Sucessfully logged in as '"+params[:user]+"' to "+$url_provider.url_for("",:full)
105
+ OpenTox.text_to_html(content,subjectid)
106
+ else
107
+ content_type "text/html"
108
+ OpenTox.login("Login failed, please try again")
109
+ end
110
+ end
111
+ =end
112
+
data/lib/validation.rb CHANGED
@@ -1,70 +1,196 @@
1
1
  module OpenTox
2
- class Validation
2
+ class Validation
3
3
  include OpenTox
4
-
5
- attr_accessor :report_uri, :qmrf_report_uri
6
-
7
- def self.create_crossvalidation(params)
8
- params[:uri] = File.join(CONFIG[:services]['opentox-validation'], "crossvalidation")
9
- params[:num_folds] = 10 unless params[:num_folds]
10
- params[:random_seed] = 2 unless params[:random_seed]
11
- params[:stratified] = false unless params[:stratified]
12
- uri = OpenTox::RestClientWrapper.post(File.join(CONFIG[:services]["opentox-validation"],"/crossvalidation"),params,nil,false)
13
- OpenTox::Validation.new(uri)
14
- end
15
-
16
- def create_report
17
- @report_uri = RestClientWrapper.post(File.join(CONFIG[:services]["opentox-validation"],"/report/crossvalidation"), :validation_uris => @uri).to_s
18
- @report_uri
4
+
5
+ # find validation, raises error if not found
6
+ # @param [String] uri
7
+ # @param [String,optional] subjectid
8
+ # @return [OpenTox::Validation]
9
+ def self.find( uri, subjectid=nil )
10
+ val = Validation.new(uri)
11
+ val.load_metadata( subjectid )
12
+ val
19
13
  end
20
-
21
- def create_qmrf_report
22
- @qmrf_report_uri = RestClientWrapper.post(File.join(CONFIG[:services]["opentox-validation"],"/reach_report/qmrf"), :model_uri => @uri).to_s
23
- @qmrf_report_uri
14
+
15
+ # creates a validation object from crossvaldiation statistics, raise error if not found
16
+ # (as crossvaldiation statistics are returned as an average valdidation over all folds)
17
+ # @param [String] crossvalidation uri
18
+ # @param [String,optional] subjectid
19
+ # @return [OpenTox::Validation]
20
+ def self.from_cv_statistics( crossvalidation_uri, subjectid=nil )
21
+ find( File.join(crossvalidation_uri, 'statistics'),subjectid )
24
22
  end
25
-
26
- def summary(type)
27
- v = YAML.load RestClientWrappper.get(File.join(@uri, 'statistics'),:accept => "application/x-yaml").to_s
28
-
29
- case type
30
- when "classification"
31
- tp=0; tn=0; fp=0; fn=0; n=0
32
- v[:classification_statistics][:confusion_matrix][:confusion_matrix_cell].each do |cell|
33
- if cell[:confusion_matrix_predicted] == "true" and cell[:confusion_matrix_actual] == "true"
34
- tp = cell[:confusion_matrix_value]
35
- n += tp
36
- elsif cell[:confusion_matrix_predicted] == "false" and cell[:confusion_matrix_actual] == "false"
37
- tn = cell[:confusion_matrix_value]
38
- n += tn
39
- elsif cell[:confusion_matrix_predicted] == "false" and cell[:confusion_matrix_actual] == "true"
40
- fn = cell[:confusion_matrix_value]
41
- n += fn
42
- elsif cell[:confusion_matrix_predicted] == "true" and cell[:confusion_matrix_actual] == "false"
43
- fp = cell[:confusion_matrix_value]
44
- n += fp
23
+
24
+ # loads metadata via yaml from validation object
25
+ # fields (like for example the validated model) can be acces via validation.metadata[OT.model]
26
+ def load_metadata( subjectid=nil )
27
+ @metadata = YAML.load(OpenTox::RestClientWrapper.get(uri,{:subjectid => subjectid, :accept => "application/x-yaml"}))
28
+ end
29
+
30
+ # PENDING: creates summary as used for ToxCreate
31
+ def summary
32
+ if @metadata[OT.classificationStatistics]
33
+ res = {
34
+ :nr_predictions => @metadata[OT.numInstances] - @metadata[OT.numUnpredicted],
35
+ :correct_predictions => @metadata[OT.classificationStatistics][OT.percentCorrect],
36
+ :weighted_area_under_roc => @metadata[OT.classificationStatistics][OT.weightedAreaUnderRoc],
37
+ }
38
+ @metadata[OT.classificationStatistics][OT.classValueStatistics].each do |s|
39
+ if s[OT.classValue].to_s=="true"
40
+ res[:true_positives] = s[OT.numTruePositives]
41
+ res[:false_positives] = s[OT.numFalsePositives]
42
+ res[:true_negatives] = s[OT.numTrueNegatives]
43
+ res[:false_negatives] = s[OT.numFalseNegatives]
44
+ res[:sensitivity] = s[OT.truePositiveRate]
45
+ res[:specificity] = s[OT.falsePositiveRate]
46
+ break
45
47
  end
46
48
  end
49
+ res
50
+ elsif @metadata[OT.regressionStatistics]
47
51
  {
48
- :nr_predictions => n,
49
- :true_positives => tp,
50
- :false_positives => fp,
51
- :true_negatives => tn,
52
- :false_negatives => fn,
53
- :correct_predictions => 100*(tp+tn).to_f/n,
54
- :weighted_area_under_roc => v[:classification_statistics][:weighted_area_under_roc].to_f,
55
- :sensitivity => tp.to_f/(tp+fn),
56
- :specificity => tn.to_f/(tn+fp),
57
- }
58
- when "regression"
59
- {
60
- :nr_predictions => v[:num_instances] - v[:num_unpredicted],
61
- :r_square => v[:regression_statistics][:r_square],
62
- :root_mean_squared_error => v[:regression_statistics][:root_mean_squared_error],
63
- :mean_absolute_error => v[:regression_statistics][:mean_absolute_error],
52
+ :nr_predictions => @metadata[OT.numInstances] - @metadata[OT.numUnpredicted],
53
+ :r_square => @metadata[OT.regressionStatistics][OT.rSquare],
54
+ :root_mean_squared_error => @metadata[OT.regressionStatistics][OT.rootMeanSquaredError],
55
+ :mean_absolute_error => @metadata[OT.regressionStatistics][OT.meanAbsoluteError],
64
56
  }
65
57
  end
66
58
  end
59
+ end
60
+
61
+ class Crossvalidation
62
+ include OpenTox
63
+
64
+ attr_reader :report
65
+
66
+ # find crossvalidation, raises error if not found
67
+ # @param [String] uri
68
+ # @param [String,optional] subjectid
69
+ # @return [OpenTox::Crossvalidation]
70
+ def self.find( uri, subjectid=nil )
71
+ cv = Crossvalidation.new(uri)
72
+ cv.load_metadata( subjectid )
73
+ cv
74
+ end
75
+
76
+ # creates a crossvalidations, waits until it finishes, may take some time
77
+ # @param [Hash] params (required:algorithm_uri,dataset_uri,prediction_feature, optional:algorithm_params,num_folds(10),random_seed(1),stratified(false))
78
+ # @param [String,optional] subjectid
79
+ # @param [OpenTox::Task,optional] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
80
+ # @return [OpenTox::Crossvalidation]
81
+ def self.create( params, subjectid=nil, waiting_task=nil )
82
+ params[:subjectid] = subjectid if subjectid
83
+ uri = OpenTox::RestClientWrapper.post( File.join(CONFIG[:services]["opentox-validation"],"crossvalidation"),
84
+ params,{:content_type => "text/uri-list"},waiting_task )
85
+ Crossvalidation.new(uri)
86
+ end
67
87
 
68
- end
88
+ # looks for report for this crossvalidation, creates a report if no report is found
89
+ # @param [String,optional] subjectid
90
+ # @param [OpenTox::Task,optional] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
91
+ # @return [String] report uri
92
+ def find_or_create_report( subjectid=nil, waiting_task=nil )
93
+ @report = CrossvalidationReport.find_for_crossvalidation(@uri, subjectid) unless @report
94
+ @report = CrossvalidationReport.create(@uri, subjectid, waiting_task) unless @report
95
+ @report.uri
96
+ end
97
+
98
+ # loads metadata via yaml from crossvalidation object
99
+ # fields (like for example the validations) can be acces via validation.metadata[OT.validation]
100
+ def load_metadata( subjectid=nil )
101
+ @metadata = YAML.load(OpenTox::RestClientWrapper.get(uri,{:subjectid => subjectid, :accept => "application/x-yaml"}))
102
+ end
103
+
104
+ # PENDING: creates summary as used for ToxCreate
105
+ def summary( subjectid=nil )
106
+ Validation.from_cv_statistics( @uri, subjectid ).summary
107
+ end
108
+ end
109
+
110
+ class ValidationReport
111
+ include OpenTox
112
+
113
+ # finds ValidationReport for a particular validation
114
+ # @param [String] crossvalidation uri
115
+ # @param [String,optional] subjectid
116
+ # @return [OpenTox::ValidationReport] nil if no report found
117
+ def self.find_for_validation( validation_uri, subjectid=nil )
118
+ uris = RestClientWrapper.get(File.join(CONFIG[:services]["opentox-validation"],
119
+ "/report/validation?validation="+validation_uri), {:subjectid => subjectid}).chomp.split("\n")
120
+ uris.size==0 ? nil : ValidationReport.new(uris[-1])
121
+ end
122
+
123
+ end
124
+
125
+ class CrossvalidationReport
126
+ include OpenTox
127
+
128
+ # finds CrossvalidationReport via uri, raises error if not found
129
+ # @param [String] uri
130
+ # @param [String,optional] subjectid
131
+ # @return [OpenTox::CrossvalidationReport]
132
+ def self.find( uri, subjectid=nil )
133
+ # PENDING load report data?
134
+ OpenTox::RestClientWrapper.get(uri,{:subjectid => subjectid})
135
+ CrossvalidationReport.new(uri)
136
+ end
137
+
138
+ # finds CrossvalidationReport for a particular crossvalidation
139
+ # @param [String] crossvalidation uri
140
+ # @param [String,optional] subjectid
141
+ # @return [OpenTox::CrossvalidationReport] nil if no report found
142
+ def self.find_for_crossvalidation( crossvalidation_uri, subjectid=nil )
143
+ uris = RestClientWrapper.get(File.join(CONFIG[:services]["opentox-validation"],
144
+ "/report/crossvalidation?crossvalidation="+crossvalidation_uri), {:subjectid => subjectid}).chomp.split("\n")
145
+ uris.size==0 ? nil : CrossvalidationReport.new(uris[-1])
146
+ end
147
+
148
+ # creates a crossvalidation report via crossvalidation
149
+ # @param [String] crossvalidation uri
150
+ # @param [String,optional] subjectid
151
+ # @param [OpenTox::Task,optional] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
152
+ # @return [OpenTox::CrossvalidationReport]
153
+ def self.create( crossvalidation_uri, subjectid=nil, waiting_task=nil )
154
+ uri = RestClientWrapper.post(File.join(CONFIG[:services]["opentox-validation"],"/report/crossvalidation"),
155
+ { :validation_uris => crossvalidation_uri, :subjectid => subjectid }, {}, waiting_task )
156
+ CrossvalidationReport.new(uri)
157
+ end
158
+ end
159
+
160
+ class QMRFReport
161
+ include OpenTox
162
+
163
+ # finds QMRFReport, raises Error if not found
164
+ # @param [String] uri
165
+ # @param [String,optional] subjectid
166
+ # @return [OpenTox::QMRFReport]
167
+ def self.find( uri, subjectid=nil )
168
+ # PENDING load crossvalidation data?
169
+ OpenTox::RestClientWrapper.get(uri,{:subjectid => subjectid})
170
+ QMRFReport.new(uri)
171
+ end
172
+
173
+ # finds QMRF report for a particular model
174
+ # @param [String] model_uri
175
+ # @param [String,optional] subjectid
176
+ # @return [OpenTox::QMRFReport] nil if no report found
177
+ def self.find_for_model( model_uri, subjectid=nil )
178
+ uris = RestClientWrapper.get(File.join(CONFIG[:services]["opentox-validation"],
179
+ "/reach_report/qmrf?model="+model_uri), {:subjectid => subjectid}).chomp.split("\n")
180
+ uris.size==0 ? nil : QMRFReport.new(uris[-1])
181
+ end
182
+
183
+ # creates a qmrf report via model
184
+ # @param [String] model_uri
185
+ # @param [String,optional] subjectid
186
+ # @param [OpenTox::Task,optional] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
187
+ # @return [OpenTox::QMRFReport]
188
+ def self.create( model_uri, subjectid=nil, waiting_task=nil )
189
+ uri = RestClientWrapper.post(File.join(CONFIG[:services]["opentox-validation"],"/reach_report/qmrf"),
190
+ { :model_uri => model_uri, :subjectid => subjectid }, {}, waiting_task )
191
+ QMRFReport.new(uri)
192
+ end
193
+ end
194
+
69
195
  end
70
196