summaryrefslogtreecommitdiff
path: root/report
diff options
context:
space:
mode:
Diffstat (limited to 'report')
-rwxr-xr-x[-rw-r--r--]report/environment.rb24
-rw-r--r--report/external/mimeparse.rb220
-rw-r--r--report/plot_factory.rb153
-rw-r--r--report/prediction_util.rb45
-rwxr-xr-x[-rw-r--r--]report/report_application.rb123
-rwxr-xr-xreport/report_content.rb294
-rwxr-xr-x[-rw-r--r--]report/report_factory.rb464
-rw-r--r--report/report_format.rb31
-rwxr-xr-x[-rw-r--r--]report/report_persistance.rb183
-rw-r--r--report/report_service.rb87
-rwxr-xr-x[-rw-r--r--]report/report_test.rb52
-rw-r--r--report/statistical_test.rb76
-rw-r--r--report/util.rb8
-rwxr-xr-x[-rw-r--r--]report/validation_access.rb309
-rwxr-xr-x[-rw-r--r--]report/validation_data.rb231
-rwxr-xr-x[-rw-r--r--]report/xml_report.rb54
-rwxr-xr-x[-rw-r--r--]report/xml_report_util.rb9
17 files changed, 1190 insertions, 1173 deletions
diff --git a/report/environment.rb b/report/environment.rb
index 714cebe..12e3272 100644..100755
--- a/report/environment.rb
+++ b/report/environment.rb
@@ -1,41 +1,33 @@
['rubygems', 'logger', 'fileutils', 'sinatra', 'sinatra/url_for', 'rest_client',
- 'yaml', 'fileutils', 'mime/types', 'abbrev',
- 'rexml/document', 'ruby-plot', 'active_record', 'ar-extensions', 'opentox-ruby-api-wrapper' ].each do |g|
+ 'yaml', 'fileutils', 'mime/types', 'abbrev', 'rinruby',
+ 'rexml/document', 'ruby-plot', 'opentox-ruby' ].each do |g|
require g
end
gem 'ruby-plot', '= 0.0.2'
-unless ActiveRecord::Base.connected?
- ActiveRecord::Base.establish_connection(
- :adapter => @@config[:database][:adapter],
- :host => @@config[:database][:host],
- :database => @@config[:database][:database],
- :username => @@config[:database][:username],
- :password => @@config[:database][:password]
- )
- ActiveRecord::Base.logger = Logger.new("/dev/null")
-end
+#R.quit
module Reports
end
-require "lib/rdf_provider.rb"
+require "lib/ot_predictions.rb"
+#require "lib/active_record_setup.rb"
+require "lib/data_mapper_util.rb"
require "report/plot_factory.rb"
require "report/xml_report.rb"
require "report/xml_report_util.rb"
require "report/report_persistance.rb"
+require "report/report_content.rb"
require "report/report_factory.rb"
require "report/report_service.rb"
require "report/report_format.rb"
require "report/validation_access.rb"
require "report/validation_data.rb"
-require "report/prediction_util.rb"
require "report/util.rb"
-require "report/external/mimeparse.rb"
+require "report/statistical_test.rb"
-require "lib/ot_predictions.rb"
diff --git a/report/external/mimeparse.rb b/report/external/mimeparse.rb
deleted file mode 100644
index 553c431..0000000
--- a/report/external/mimeparse.rb
+++ /dev/null
@@ -1,220 +0,0 @@
-# mimeparse.rb
-#
-# This module provides basic functions for handling mime-types. It can
-# handle matching mime-types against a list of media-ranges. See section
-# 14.1 of the HTTP specification [RFC 2616] for a complete explanation.
-#
-# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
-#
-# ---------
-#
-# This is a port of Joe Gregario's mimeparse.py, which can be found at
-# <http://code.google.com/p/mimeparse/>.
-#
-# ported from version 0.1.2
-#
-# Comments are mostly excerpted from the original.
-
-module MIMEParse
- module_function
-
-# Carves up a mime-type and returns an Array of the
-# [type, subtype, params] where "params" is a Hash of all
-# the parameters for the media range.
-#
-# For example, the media range "application/xhtml;q=0.5" would
-# get parsed into:
-#
-# ["application", "xhtml", { "q" => "0.5" }]
-def parse_mime_type(mime_type)
- parts = mime_type.split(";")
-
- params = {}
-
- parts[1..-1].map do |param|
- k,v = param.split("=").map { |s| s.strip }
- params[k] = v
- end
-
- full_type = parts[0].strip
- # Java URLConnection class sends an Accept header that includes a single "*"
- # Turn it into a legal wildcard.
- full_type = "*/*" if full_type == "*"
- type, subtype = full_type.split("/")
- raise "malformed mime type" unless subtype
-
- [type.strip, subtype.strip, params]
-end
-
-# Carves up a media range and returns an Array of the
-# [type, subtype, params] where "params" is a Hash of all
-# the parameters for the media range.
-#
-# For example, the media range "application/*;q=0.5" would
-# get parsed into:
-#
-# ["application", "*", { "q", "0.5" }]
-#
-# In addition this function also guarantees that there
-# is a value for "q" in the params dictionary, filling it
-# in with a proper default if necessary.
-def parse_media_range(range)
- type, subtype, params = parse_mime_type(range)
- unless params.has_key?("q") and params["q"] and params["q"].to_f and params["q"].to_f <= 1 and params["q"].to_f >= 0
- params["q"] = "1"
- end
-
- [type, subtype, params]
-end
-
-# Find the best match for a given mime-type against a list of
-# media_ranges that have already been parsed by #parse_media_range
-#
-# Returns the fitness and the "q" quality parameter of the best match,
-# or [-1, 0] if no match was found. Just as for #quality_parsed,
-# "parsed_ranges" must be an Enumerable of parsed media ranges.
-def fitness_and_quality_parsed(mime_type, parsed_ranges)
- best_fitness = -1
- best_fit_q = 0
- target_type, target_subtype, target_params = parse_media_range(mime_type)
-
- parsed_ranges.each do |type,subtype,params|
- if (type == target_type or type == "*" or target_type == "*") and
- (subtype == target_subtype or subtype == "*" or target_subtype == "*")
- param_matches = target_params.find_all { |k,v| k != "q" and params.has_key?(k) and v == params[k] }.length
-
- fitness = (type == target_type) ? 100 : 0
- fitness += (subtype == target_subtype) ? 10 : 0
- fitness += param_matches
-
- if fitness > best_fitness
- best_fitness = fitness
- best_fit_q = params["q"]
- end
- end
- end
-
- [best_fitness, best_fit_q.to_f]
-end
-
-# Find the best match for a given mime-type against a list of
-# media_ranges that have already been parsed by #parse_media_range
-#
-# Returns the "q" quality parameter of the best match, 0 if no match
-# was found. This function behaves the same as #quality except that
-# "parsed_ranges" must be an Enumerable of parsed media ranges.
-def quality_parsed(mime_type, parsed_ranges)
- fitness_and_quality_parsed(mime_type, parsed_ranges)[1]
-end
-
-# Returns the quality "q" of a mime_type when compared against
-# the media-ranges in ranges. For example:
-#
-# irb> quality("text/html", "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5")
-# => 0.7
-def quality(mime_type, ranges)
- parsed_ranges = ranges.split(",").map { |r| parse_media_range(r) }
- quality_parsed(mime_type, parsed_ranges)
-end
-
-# Takes a list of supported mime-types and finds the best match
-# for all the media-ranges listed in header. The value of header
-# must be a string that conforms to the format of the HTTP Accept:
-# header. The value of supported is an Enumerable of mime-types
-#
-# irb> best_match(["application/xbel+xml", "text/xml"], "text/*;q=0.5,*/*; q=0.1")
-# => "text/xml"
-def best_match(supported, header)
- parsed_header = header.split(",").map { |r| parse_media_range(r) }
-
- weighted_matches = supported.map do |mime_type|
- [fitness_and_quality_parsed(mime_type, parsed_header), mime_type]
- end
-
- weighted_matches.sort!
-
- weighted_matches.last[0][1].zero? ? nil : weighted_matches.last[1]
-end
-end
-
-if __FILE__ == $0
- require "test/unit"
-
- class TestMimeParsing < Test::Unit::TestCase
- include MIMEParse
-
- def test_parse_media_range
- assert_equal [ "application", "xml", { "q" => "1" } ],
- parse_media_range("application/xml;q=1")
-
- assert_equal [ "application", "xml", { "q" => "1" } ],
- parse_media_range("application/xml")
-
- assert_equal [ "application", "xml", { "q" => "1" } ],
- parse_media_range("application/xml;q=")
-
- assert_equal [ "application", "xml", { "q" => "1", "b" => "other" } ],
- parse_media_range("application/xml ; q=1;b=other")
-
- assert_equal [ "application", "xml", { "q" => "1", "b" => "other" } ],
- parse_media_range("application/xml ; q=2;b=other")
-
- # Java URLConnection class sends an Accept header that includes a single "*"
- assert_equal [ "*", "*", { "q" => ".2" } ],
- parse_media_range(" *; q=.2")
- end
-
- def test_rfc_2616_example
- accept = "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5"
-
- assert_equal 1, quality("text/html;level=1", accept)
- assert_equal 0.7, quality("text/html", accept)
- assert_equal 0.3, quality("text/plain", accept)
- assert_equal 0.5, quality("image/jpeg", accept)
- assert_equal 0.4, quality("text/html;level=2", accept)
- assert_equal 0.7, quality("text/html;level=3", accept)
- end
-
- def test_best_match
- @supported_mime_types = [ "application/xbel+xml", "application/xml" ]
-
- # direct match
- assert_best_match "application/xbel+xml", "application/xbel+xml"
- # direct match with a q parameter
- assert_best_match "application/xbel+xml", "application/xbel+xml; q=1"
- # direct match of our second choice with a q parameter
- assert_best_match "application/xml", "application/xml; q=1"
- # match using a subtype wildcard
- assert_best_match "application/xml", "application/*; q=1"
- # match using a type wildcard
- assert_best_match "application/xml", "*/*"
-
- @supported_mime_types = [ "application/xbel+xml", "text/xml" ]
- # match using a type versus a lower weighted subtype
- assert_best_match "text/xml", "text/*;q=0.5,*/*;q=0.1"
- # fail to match anything
- assert_best_match nil, "text/html,application/atom+xml; q=0.9"
- # common AJAX scenario
- @supported_mime_types = [ "application/json", "text/html" ]
- assert_best_match "application/json", "application/json, text/javascript, */*"
- # verify fitness sorting
- assert_best_match "application/json", "application/json, text/html;q=0.9"
- end
-
- def test_support_wildcards
- @supported_mime_types = ['image/*', 'application/xml']
- # match using a type wildcard
- assert_best_match 'image/*', 'image/png'
- # match using a wildcard for both requested and supported
- assert_best_match 'image/*', 'image/*'
- end
-
- def assert_best_match(expected, header)
- assert_equal(expected, best_match(@supported_mime_types, header))
- end
- end
-end
-
-
-#puts MIMEParse::best_match(["text/xml","text/html","application/pdf"],
-# 'application/x-ms-application,image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*')
diff --git a/report/plot_factory.rb b/report/plot_factory.rb
index daaba52..43c45fc 100644
--- a/report/plot_factory.rb
+++ b/report/plot_factory.rb
@@ -8,6 +8,43 @@ class Array
self[i] = self[j]
self[j] = tmp
end
+
+ # summing up values of fields where array __groups__ has equal values
+ # EXAMPLE
+ # self: [1, 0, 1, 2, 3, 0, 2]
+ # __groups__: [100, 90, 70, 70, 30, 10, 0]
+ # returns:
+ # [ 1, 0, 3, 3, 0, 2]
+ # (fields with equal value 70 are compressed)
+ # PRECONDITION
+ # __groups__ has to be sorted
+ def compress_sum(groups)
+ compress(groups) do |a,b|
+ a+b
+ end
+ end
+
+ # see compress_sum, replace sum with max
+ def compress_max(groups)
+ compress(groups) do |a,b|
+ a > b ? a : b
+ end
+ end
+
+ private
+ def compress(groups)
+ raise "length not equal" unless self.size==groups.size
+ raise "to small" unless self.size>=2
+ a = [ self[0] ]
+ (1..groups.size-1).each do |i|
+ if groups[i]!=groups[i-1]
+ a << self[i]
+ else
+ a[-1] = yield a[-1],self[i]
+ end
+ end
+ a
+ end
end
@@ -15,7 +52,7 @@ module Reports
module PlotFactory
- def self.create_regression_plot( out_file, validation_set )
+ def self.create_regression_plot( out_file, validation_set, name_attribute )
LOGGER.debug "Creating regression plot, out-file:"+out_file.to_s
@@ -23,14 +60,28 @@ module Reports
x = []
y = []
validation_set.validations.each do |v|
- names << v.algorithm_uri
- x << v.get_predictions.predicted_values
- y << v.get_predictions.actual_values
+ x_i = v.get_predictions.predicted_values
+ y_i = v.get_predictions.actual_values
+
+ # filter out nil-predictions
+ not_nil_indices = []
+ x_i.size.times do |i|
+ not_nil_indices << i if x_i[i]!=nil && y_i[i]!=nil
+ end
+ if not_nil_indices.size < x_i.size
+ x_i = not_nil_indices.collect{ |i| x_i[i] }
+ y_i = not_nil_indices.collect{ |i| y_i[i] }
+ end
+
+ names << ( name_attribute==:crossvalidation_fold ? "fold " : "" ) + v.send(name_attribute).to_s
+ x << x_i
+ y << y_i
end
RubyPlot::plot_points(out_file, "Regression plot", "Predicted values", "Actual values", names, x, y )
end
+
# creates a roc plot (result is plotted into out_file)
# * if (split_set_attributes == nil?)
# * the predictions of all validations in the validation set are plotted as one average roc-curve
@@ -41,19 +92,22 @@ module Reports
#
def self.create_roc_plot( out_file, validation_set, class_value, split_set_attribute=nil, show_single_curves=false )
- LOGGER.debug "creating roc plot, out-file:"+out_file.to_s
+ LOGGER.debug "creating roc plot for '"+validation_set.size.to_s+"' validations, out-file:"+out_file.to_s
if split_set_attribute
attribute_values = validation_set.get_values(split_set_attribute)
-
names = []
fp_rates = []
tp_rates = []
attribute_values.each do |value|
- data = transform_predictions(validation_set.filter({split_set_attribute => value}), class_value, false)
- names << value.to_s
- fp_rates << data[:fp_rate][0]
- tp_rates << data[:tp_rate][0]
+ begin
+ data = transform_predictions(validation_set.filter({split_set_attribute => value}), class_value, false)
+ names << value.to_s
+ fp_rates << data[:fp_rate][0]
+ tp_rates << data[:tp_rate][0]
+ rescue
+ LOGGER.warn "could not create ROC plot for "+value.to_s
+ end
end
RubyPlot::plot_lines(out_file, "ROC-Plot", "False positive rate", "True Positive Rate", names, fp_rates, tp_rates )
else
@@ -62,28 +116,33 @@ module Reports
end
end
- def self.create_bar_plot( out_file, validation_set, class_value, title_attribute, value_attributes )
+ def self.create_bar_plot( out_file, validation_set, title_attribute, value_attributes )
LOGGER.debug "creating bar plot, out-file:"+out_file.to_s
data = []
titles = []
+ labels = []
validation_set.validations.each do |v|
values = []
value_attributes.each do |a|
- value = v.send(a)
- if value.is_a?(Hash)
- if class_value==nil
- avg_value = 0
- value.values.each{ |val| avg_value+=val }
- value = avg_value/value.values.size.to_f
- else
- raise "bar plot value is hash, but no entry for class-value ("+class_value.to_s+"); value for "+a.to_s+" -> "+value.inspect unless value.key?(class_value)
- value = value[class_value]
+ validation_set.get_domain_for_attr(a).each do |class_value|
+ value = v.send(a)
+ if value.is_a?(Hash)
+ if class_value==nil
+ avg_value = 0
+ value.values.each{ |val| avg_value+=val }
+ value = avg_value/value.values.size.to_f
+ else
+ raise "bar plot value is hash, but no entry for class-value ("+class_value.to_s+"); value for "+a.to_s+" -> "+value.inspect unless value.key?(class_value)
+ value = value[class_value]
+ end
end
+ raise "value is nil\nattribute: "+a.to_s+"\nvalidation: "+v.inspect if value==nil
+ values.push(value)
+ labels.push(a.to_s.gsub("_","-") + ( class_value==nil ? "" : "("+class_value.to_s+")" ))
end
- values.push(value)
end
titles << v.send(title_attribute).to_s
@@ -95,8 +154,6 @@ module Reports
data[i] = [titles[i]] + data[i]
end
- labels = value_attributes.collect{|a| a.to_s.gsub("_","-")}
-
LOGGER.debug "bar plot labels: "+labels.inspect
LOGGER.debug "bar plot data: "+data.inspect
@@ -177,11 +234,15 @@ module Reports
sum_roc_values[:confidence_values] += roc_values[:confidence_values]
sum_roc_values[:actual_values] += roc_values[:actual_values]
if add_single_folds
- tp_fp_rates = get_tp_fp_rates(roc_values)
- names << "fold "+i.to_s
- fp_rate << tp_fp_rates[:fp_rate]
- tp_rate << tp_fp_rates[:tp_rate]
- faint << true
+ begin
+ tp_fp_rates = get_tp_fp_rates(roc_values)
+ names << "fold "+i.to_s
+ fp_rate << tp_fp_rates[:fp_rate]
+ tp_rate << tp_fp_rates[:tp_rate]
+ faint << true
+ rescue
+ LOGGER.warn "could not get ROC vals for fold "+i.to_s
+ end
end
end
tp_fp_rates = get_tp_fp_rates(sum_roc_values)
@@ -197,6 +258,18 @@ module Reports
end
end
+ def self.demo_rock_plot
+ roc_values = {:confidence_values => [0.1, 0.9, 0.5, 0.6, 0.6, 0.6],
+ :predicted_values => [1, 0, 0, 1, 0, 1],
+ :actual_values => [0, 1, 0, 0, 1, 1]}
+ tp_fp_rates = get_tp_fp_rates(roc_values)
+ data = { :names => ["default"], :fp_rate => [tp_fp_rates[:fp_rate]], :tp_rate => [tp_fp_rates[:tp_rate]] }
+ RubyPlot::plot_lines("/tmp/plot.svg",
+ "ROC-Plot",
+ "False positive rate",
+ "True Positive Rate", data[:names], data[:fp_rate], data[:tp_rate], data[:faint] )
+ end
+
def self.get_tp_fp_rates(roc_values)
c = roc_values[:confidence_values]
@@ -232,9 +305,11 @@ module Reports
end
end
#puts c.inspect+"\n"+a.inspect+"\n"+p.inspect+"\n\n"
-
+
tp_rate = [0]
fp_rate = [0]
+ w = [1]
+ c2 = [Float::MAX]
(0..p.size-1).each do |i|
if a[i]==p[i]
tp_rate << tp_rate[-1]+1
@@ -243,8 +318,15 @@ module Reports
fp_rate << fp_rate[-1]+1
tp_rate << tp_rate[-1]
end
+ w << 1
+ c2 << c[i]
end
- #puts tp_rate.inspect+"\n"+fp_rate.inspect+"\n\n"
+ #puts c2.inspect+"\n"+tp_rate.inspect+"\n"+fp_rate.inspect+"\n"+w.inspect+"\n\n"
+
+ tp_rate = tp_rate.compress_max(c2)
+ fp_rate = fp_rate.compress_max(c2)
+ w = w.compress_sum(c2)
+ #puts tp_rate.inspect+"\n"+fp_rate.inspect+"\n"+w.inspect+"\n\n"
(0..tp_rate.size-1).each do |i|
tp_rate[i] = tp_rate[-1]>0 ? tp_rate[i]/tp_rate[-1].to_f*100 : 100
@@ -256,5 +338,14 @@ module Reports
end
end
end
-
+
+#require "rubygems"
+#require "ruby-plot"
#Reports::PlotFactory::demo_ranking_plot
+#Reports::PlotFactory::demo_rock_plot
+
+#a = [1, 0, 1, 2, 3, 0, 2]
+#puts a.compress_sum([100, 90, 70, 70, 30, 10, 0]).inspect
+#puts a.compress_max([100, 90, 70, 70, 30, 10, 0]).inspect
+
+
diff --git a/report/prediction_util.rb b/report/prediction_util.rb
deleted file mode 100644
index 5ba3716..0000000
--- a/report/prediction_util.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-module Reports::PredictionUtil
-
- # creates an Array for a table
- # * first row: header values
- # * other rows: the prediction values
- # additional attribute values of the validation that should be added to the table can be defined via validation_attributes
- #
- # call-seq:
- # predictions_to_array(validation_set, validation_attributes=[]) => Array
- #
- def self.predictions_to_array(validation_set, validation_attributes=[])
-
- res = []
-
- validation_set.validations.each do |v|
- (0..v.get_predictions.num_instances-1).each do |i|
- a = []
- validation_attributes.each{ |att| a.push(v.send(att).to_s) }
-
- #PENDING!
- a.push( "http://ambit.uni-plovdiv.bg:8080/ambit2/depict/cdk?search="+URI.encode(OpenTox::Compound.new(:uri=>v.get_predictions.identifier(i)).smiles) )
-
- a.push(v.get_predictions.actual_value(i).to_nice_s)
- a.push(v.get_predictions.predicted_value(i).to_nice_s)
- a.push(v.get_predictions.classification_miss?(i)?"X":"") if validation_set.all_classification?
- a.push(v.get_predictions.confidence_value(i).to_nice_s) if v.get_predictions.confidence_values_available?
- a.push(v.get_predictions.identifier(i)) #.gsub(/[-(),=]/, '')[0,10])
- res.push(a)
- end
- end
-
- #res = res.sort{|x,y| y[3] <=> x[3] }
- header = [ "compound", "actual value", "predicted value"]
- header.push "missclassified" if validation_set.all_classification?
- header.push "confidence value" if validation_set.validations[0].get_predictions.confidence_values_available?
- header << "compound-uri"
- res.insert(0, validation_attributes + header)
- #puts res.collect{|c| c.inspect}.join("\n")
-
- return res
- end
-
-end
diff --git a/report/report_application.rb b/report/report_application.rb
index efa0298..5a47063 100644..100755
--- a/report/report_application.rb
+++ b/report/report_application.rb
@@ -1,22 +1,27 @@
require "report/environment.rb"
def perform
- begin
- $rep = Reports::ReportService.new(url_for("/report", :full)) unless $rep
- yield( $rep )
- rescue Reports::NotFound => ex
- halt 404, ex.message
- rescue Reports::BadRequest => ex
- halt 400, ex.message
- rescue Exception => ex
- #LOGGER.error(ex.message)
- LOGGER.error "report error: "+ex.message
- LOGGER.error ": "+ex.backtrace.join("\n")
- raise ex # sinatra returns 501
- #halt 500, ex.message
+ @@report_service = Reports::ReportService.instance( url_for("/report", :full) ) unless defined?@@report_service
+ yield( @@report_service )
+end
+
+def get_docbook_resource(filepath)
+ perform do |rs|
+ halt 404,"not found: "+filepath unless File.exist?(filepath)
+ types = MIME::Types.type_for(filepath)
+ content_type(types[0].content_type) if types and types.size>0 and types[0]
+ result = body(File.new(filepath))
end
end
+get '/'+ENV['DOCBOOK_DIRECTORY']+'/:subdir/:resource' do
+ path_array = request.env['REQUEST_URI'].split("/")
+ get_docbook_resource ENV['DOCBOOK_DIRECTORY']+"/"+path_array[-2]+"/"+path_array[-1]
+end
+
+get '/'+ENV['DOCBOOK_DIRECTORY']+'/:resource' do
+ get_docbook_resource ENV['DOCBOOK_DIRECTORY']+"/"+request.env['REQUEST_URI'].split("/")[-1]
+end
get '/report/:type/css_style_sheet/?' do
perform do |rs|
@@ -24,66 +29,73 @@ get '/report/:type/css_style_sheet/?' do
end
end
-
get '/report/?' do
perform do |rs|
- content_type "text/uri-list"
- rs.get_report_types
+ case request.env['HTTP_ACCEPT'].to_s
+ when /text\/html/
+ related_links =
+ "All validations: "+url_for("/",:full)
+ description =
+ "A list of all report types."
+ content_type "text/html"
+ OpenTox.text_to_html rs.get_report_types,@subjectid,related_links,description
+ else
+ content_type "text/uri-list"
+ rs.get_report_types
+ end
end
end
get '/report/:report_type' do
perform do |rs|
- content_type "text/uri-list"
- rs.get_all_reports(params[:report_type], params)
+ case request.env['HTTP_ACCEPT'].to_s
+ when /text\/html/
+ related_links =
+ "Available report types: "+url_for("/report",:full)+"\n"+
+ "Single validations: "+url_for("/",:full)+"\n"+
+ "Crossvalidations: "+url_for("/crossvalidation",:full)
+ description =
+ "A list of all "+params[:report_type]+" reports. To create a report, use the POST method."
+ post_params = [[:validation_uris]]
+ content_type "text/html"
+ OpenTox.text_to_html rs.get_all_reports(params[:report_type], params),@subjectid,related_links,description,post_params
+ else
+ content_type "text/uri-list"
+ rs.get_all_reports(params[:report_type], params)
+ end
end
end
post '/report/:type/:id/format_html' do
-
perform do |rs|
rs.get_report(params[:type],params[:id],"text/html",true,params)
content_type "text/uri-list"
- rs.get_uri(params[:type],params[:id])
+ rs.get_uri(params[:type],params[:id])+"\n"
end
end
get '/report/:type/:id' do
-
perform do |rs|
accept_header = request.env['HTTP_ACCEPT']
- if accept_header =~ /MSIE/
- LOGGER.info "Changing MSIE accept-header to text/html"
- accept_header = "text/html"
- end
- #request.env['HTTP_ACCEPT'] = "application/pdf"
-
- #QMRF-STUB
- if params[:type] == Reports::ReportFactory::RT_QMRF
- #raise Reports::BadRequest.new("only 'application/qmrf-xml' provided so far") if accept_header != "application/qmrf-xml"
- content_type "application/qmrf-xml"
- result = body(OpenTox::RestClientWrapper.get("http://ecb.jrc.ec.europa.eu/qsar/qsar-tools/qrf/QMRF_v1.2_FishTox.xml"))
+ report = rs.get_report(params[:type],params[:id],accept_header)
+ format = Reports::ReportFormat.get_format(accept_header)
+ content_type format
+ #PENDING: get_report should return file or string, check for result.is_file instead of format
+ if format=="application/x-yaml" or format=="application/rdf+xml"
+ report
else
- report = rs.get_report(params[:type],params[:id],accept_header)
- format = Reports::ReportFormat.get_format(accept_header)
- content_type format
- #PENDING: get_report should return file or string, check for result.is_file instead of format
- if format=="application/x-yaml" or format=="application/rdf+xml"
- report
- else
- result = body(File.new(report))
- end
+ result = body(File.new(report))
end
end
end
-get '/report/:type/:id/:resource' do
- #hack: using request.env['REQUEST_URI'].split("/")[-1] instead of params[:resource] because the file extension is lost
+#OpenTox::Authorization.whitelist( Regexp.new("/report/.*/[0-9]+/.*"),"GET")
+get '/report/:type/:id/:resource' do
perform do |rs|
- filepath = rs.get_report_resource(params[:type],params[:id],request.env['REQUEST_URI'].split("/")[-1])
+ filepath = rs.get_report_resource(params[:type],params[:id],params[:resource])
types = MIME::Types.type_for(filepath)
content_type(types[0].content_type) if types and types.size>0 and types[0]
result = body(File.new(filepath))
@@ -93,30 +105,15 @@ end
delete '/report/:type/:id' do
perform do |rs|
content_type "text/plain"
- rs.delete_report(params[:type],params[:id])
+ rs.delete_report(params[:type],params[:id],@subjectid)
end
end
post '/report/:type' do
- task_uri = OpenTox::Task.as_task do
+ task = OpenTox::Task.create("Create report",url_for("/report/"+params[:type], :full)) do |task| #,params
perform do |rs|
- content_type "text/uri-list"
- rs.create_report(params[:type],params[:validation_uris]?params[:validation_uris].split(/\n|,/):nil)
- end
- end
- halt 202,task_uri
-end
-
-
-post '/report/:type/:id' do
- perform do |rs|
- #QMRF-STUB
- if params[:type] == Reports::ReportFactory::RT_QMRF
- #raise Reports::BadRequest.new("only 'application/qmrf-xml' provided so far") if request.content_type != "application/qmrf-xml"
- input = request.env["rack.input"].read
- "save qmrf would have been successfull, received data with "+input.to_s.size.to_s+" characters, this is just a stub, changes discarded"
- else
- "operation not supported yet"
+ rs.create_report(params[:type],params[:validation_uris]?params[:validation_uris].split(/\n|,/):nil,@subjectid,task)
end
end
+ return_task(task)
end
diff --git a/report/report_content.rb b/report/report_content.rb
new file mode 100755
index 0000000..1345e6f
--- /dev/null
+++ b/report/report_content.rb
@@ -0,0 +1,294 @@
+
+# = Reports::ReportContent
+#
+# wraps an xml-report, adds functionality for adding sections, adds a hash for tmp files
+#
+class Reports::ReportContent
+
+ attr_accessor :xml_report, :tmp_files
+
+ def initialize(title)
+ @xml_report = Reports::XMLReport.new(title, Time.now.strftime("Created at %m.%d.%Y - %H:%M"))
+ @tmp_file_count = 0
+ @current_section = @xml_report.get_root_element
+ end
+
+ def add_section( section_title, section_text=nil )
+ @current_section = @xml_report.add_section(@xml_report.get_root_element, section_title)
+ @xml_report.add_paragraph(@current_section, section_text) if section_text
+ end
+
+ def end_section()
+ @current_section = @xml_report.get_root_element
+ end
+
+ def add_paired_ttest_table( validation_set,
+ group_attribute,
+ test_attribute,
+ section_title = "Paired t-test",
+ section_text = nil)
+
+ level = 0.90
+ test_matrix = Reports::ReportStatisticalTest.test_matrix( validation_set.validations,
+ group_attribute, test_attribute, "paired_ttest", level )
+ puts test_matrix.inspect
+ titles = test_matrix[:titles]
+ matrix = test_matrix[:matrix]
+ table = []
+ puts titles.inspect
+ table << [""] + titles
+ titles.size.times do |i|
+ table << [titles[i]] + matrix[i].collect{|v| (v==nil || v==0) ? "" : (v<0 ? "-" : "+") }
+ end
+
+ section_test = @xml_report.add_section(@current_section, section_title)
+ @xml_report.add_paragraph(section_test, section_text) if section_text
+ @xml_report.add_table(section_test, test_attribute.to_s+", significance-level: "+level.to_s, table, true, true)
+ end
+
+ def add_predictions( validation_set,
+ validation_attributes=[],
+ section_title="Predictions",
+ section_text=nil,
+ table_title="Predictions")
+
+ #PENING
+ raise "validation attributes not implemented in get prediction array" if validation_attributes.size>0
+
+ section_table = @xml_report.add_section(@current_section, section_title)
+ if validation_set.validations[0].get_predictions
+ @xml_report.add_paragraph(section_table, section_text) if section_text
+ @xml_report.add_table(section_table, table_title, Lib::OTPredictions.to_array(validation_set.validations.collect{|v| v.get_predictions},
+ true, true))
+ else
+ @xml_report.add_paragraph(section_table, "No prediction info available.")
+ end
+ end
+
+
+ def add_result_overview( validation_set,
+ attribute_col,
+ attribute_row,
+ attribute_values,
+ table_titles=nil,
+ section_title="Result overview",
+ section_text=nil )
+
+
+ section_table = @xml_report.add_section(@current_section, section_title)
+ @xml_report.add_paragraph(section_table, section_text) if section_text
+
+ attribute_values.size.times do |i|
+ attribute_val = attribute_values[i]
+ table_title = table_titles ? table_titles[i] : "Result overview for "+attribute_val.to_s
+ vals = validation_set.to_table( attribute_col, attribute_row, attribute_val)
+ @xml_report.add_table(section_table, table_title, vals, true, true)
+ end
+ end
+
+ # result (could be transposed)
+ #
+ # attr1 | attr2 | attr3
+ # ===========|===========|===========
+ # val1-attr1 |val1-attr2 |val1-attr3
+ # val2-attr1 |val2-attr2 |val2-attr3
+ # val3-attr1 |val3-attr2 |val3-attr3
+ #
+ def add_result( validation_set,
+ validation_attributes,
+ table_title,
+ section_title="Results",
+ section_text=nil,
+ #rem_equal_vals_attr=[],
+ search_for_existing_report_type=nil)
+
+ section_table = @xml_report.add_section(@current_section, section_title)
+ @xml_report.add_paragraph(section_table, section_text) if section_text
+ vals = validation_set.to_array(validation_attributes, true)
+ vals = vals.collect{|a| a.collect{|v| v.to_s }}
+
+ if (search_for_existing_report_type)
+ vals.size.times do |i|
+ puts i
+ if (i==0)
+ vals[i] = [ "Reports" ] + vals[i]
+ puts vals[i].inspect
+ else
+ if search_for_existing_report_type=="validation"
+ vals[i] = [ validation_set.validations[i-1].validation_report_uri() ] + vals[i]
+ elsif search_for_existing_report_type=="crossvalidation"
+ vals[i] = [ validation_set.validations[i-1].cv_report_uri() ] + vals[i]
+ else
+ raise "illegal report type: "+search_for_existing_report_type.to_s
+ end
+ end
+ end
+ end
+ #PENDING transpose values if there more than 4 columns, and there are more than columns than rows
+ transpose = vals[0].size>4 && vals[0].size>vals.size
+ @xml_report.add_table(section_table, table_title, vals, !transpose, transpose, transpose)
+ end
+
+ def add_confusion_matrix( validation,
+ section_title="Confusion Matrix",
+ section_text=nil,
+ table_title="Confusion Matrix")
+ section_confusion = @xml_report.add_section(@current_section, section_title)
+ @xml_report.add_paragraph(section_confusion, section_text) if section_text
+ @xml_report.add_table(section_confusion, table_title,
+ Reports::XMLReportUtil::create_confusion_matrix( validation.confusion_matrix ), true, true)
+ end
+
+ def add_regression_plot( validation_set,
+ name_attribute,
+ section_title="Regression Plot",
+ section_text=nil,
+ image_title=nil,
+ image_caption=nil)
+
+ image_title = "Regression plot" unless image_title
+
+ section_regr = @xml_report.add_section(@current_section, section_title)
+ prediction_set = validation_set.collect{ |v| v.get_predictions }
+
+ if prediction_set.size>0
+
+ section_text += "\nWARNING: regression plot information not available for all validation results" if prediction_set.size!=validation_set.size
+ @xml_report.add_paragraph(section_regr, section_text) if section_text
+ plot_file_name = "regr_plot"+@tmp_file_count.to_s+".svg"
+ @tmp_file_count += 1
+ begin
+ plot_file_path = add_tmp_file(plot_file_name)
+ Reports::PlotFactory.create_regression_plot( plot_file_path, prediction_set, name_attribute )
+ @xml_report.add_imagefigure(section_regr, image_title, plot_file_name, "SVG", 120, image_caption)
+ rescue RuntimeError => ex
+ LOGGER.error("Could not create regression plot: "+ex.message)
+ rm_tmp_file(plot_file_name)
+ @xml_report.add_paragraph(section_regr, "could not create regression plot: "+ex.message)
+ end
+ else
+ @xml_report.add_paragraph(section_regr, "No prediction info for regression available.")
+ end
+ end
+
+ def add_roc_plot( validation_set,
+ split_set_attribute = nil,
+ section_title="ROC Plots",
+ section_text=nil,
+ image_titles=nil,
+ image_captions=nil)
+
+ section_roc = @xml_report.add_section(@current_section, section_title)
+ prediction_set = validation_set.collect{ |v| v.get_predictions && v.get_predictions.confidence_values_available? }
+
+ if prediction_set.size>0
+ if prediction_set.size!=validation_set.size
+ section_text += "\nWARNING: roc plot information not available for all validation results"
+ LOGGER.error "WARNING: roc plot information not available for all validation results:\n"+
+ "validation set size: "+validation_set.size.to_s+", prediction set size: "+prediction_set.size.to_s
+ end
+ @xml_report.add_paragraph(section_roc, section_text) if section_text
+
+ class_domain = validation_set.get_class_domain
+ class_domain.size.times do |i|
+ class_value = class_domain[i]
+ image_title = image_titles ? image_titles[i] : "ROC Plot for class-value '"+class_value.to_s+"'"
+ image_caption = image_captions ? image_captions[i] : nil
+ plot_file_name = "roc_plot"+@tmp_file_count.to_s+".svg"
+ @tmp_file_count += 1
+ begin
+ plot_file_path = add_tmp_file(plot_file_name)
+ Reports::PlotFactory.create_roc_plot( plot_file_path, prediction_set, class_value, split_set_attribute, false )#prediction_set.size>1 )
+ @xml_report.add_imagefigure(section_roc, image_title, plot_file_name, "SVG", 120, image_caption)
+ rescue RuntimeError => ex
+ msg = "WARNING could not create roc plot for class value '"+class_value.to_s+"': "+ex.message
+ LOGGER.error(msg)
+ rm_tmp_file(plot_file_name)
+ @xml_report.add_paragraph(section_roc, msg)
+ end
+ end
+ else
+ @xml_report.add_paragraph(section_roc, "No prediction-confidence info for roc plot available.")
+ end
+
+ end
+
+ def add_ranking_plots( validation_set,
+ compare_attribute,
+ equal_attribute,
+ rank_attributes,
+ section_title="Ranking Plots",
+ section_text="This section contains the ranking plots.")
+
+ section_rank = @xml_report.add_section(@current_section, section_title)
+ @xml_report.add_paragraph(section_rank, section_text) if section_text
+
+ rank_attributes.each do |a|
+ add_ranking_plot(section_rank, validation_set, compare_attribute, equal_attribute, a)
+ end
+ end
+
+ def add_ranking_plot( report_section,
+ validation_set,
+ compare_attribute,
+ equal_attribute,
+ rank_attribute,
+ image_titles=nil,
+ image_captions=nil)
+
+ class_domain = validation_set.get_domain_for_attr(rank_attribute)
+ puts "ranking plot for "+rank_attribute.to_s+", class values: "+class_domain.to_s
+
+ class_domain.size.times do |i|
+ class_value = class_domain[i]
+ if image_titles
+ image_title = image_titles[i]
+ else
+ if class_value!=nil
+ image_title = rank_attribute.to_s+" Ranking Plot for class-value '"+class_value.to_s+"'"
+ else
+ image_title = rank_attribute.to_s+" Ranking Plot"
+ end
+ end
+ image_caption = image_captions ? image_captions[i] : nil
+ plot_file_name = "ranking_plot"+@tmp_file_count.to_s+".svg"
+ @tmp_file_count += 1
+ plot_file_path = add_tmp_file(plot_file_name)
+ Reports::PlotFactory::create_ranking_plot(plot_file_path, validation_set, compare_attribute, equal_attribute, rank_attribute, class_value)
+ @xml_report.add_imagefigure(report_section, image_title, plot_file_name, "SVG", 75, image_caption)
+ end
+ end
+
+ def add_bar_plot(validation_set,
+ title_attribute,
+ value_attributes,
+ section_title="Bar Plot",
+ section_text=nil,
+ image_title="Bar Plot",
+ image_caption=nil)
+
+ section_bar = @xml_report.add_section(@current_section, section_title)
+ @xml_report.add_paragraph(section_bar, section_text) if section_text
+
+ plot_file_name = "bar_plot"+@tmp_file_count.to_s+".svg"
+ @tmp_file_count += 1
+ plot_file_path = add_tmp_file(plot_file_name)
+ Reports::PlotFactory.create_bar_plot(plot_file_path, validation_set, title_attribute, value_attributes )
+ @xml_report.add_imagefigure(section_bar, image_title, plot_file_name, "SVG", 120, image_caption)
+ end
+
+ private
+ def add_tmp_file(tmp_file_name)
+
+ @tmp_files = {} unless @tmp_files
+ raise "file name already exits" if @tmp_files[tmp_file_name] || (@text_files && @text_files[tmp_file_name])
+ tmp_file_path = Reports::Util.create_tmp_file(tmp_file_name)
+ @tmp_files[tmp_file_name] = tmp_file_path
+ return tmp_file_path
+ end
+
+ def rm_tmp_file(tmp_file_name)
+ @tmp_files.delete(tmp_file_name) if @tmp_files.has_key?(tmp_file_name)
+ end
+
+end \ No newline at end of file
diff --git a/report/report_factory.rb b/report/report_factory.rb
index 646ecfd..e770d2f 100644..100755
--- a/report/report_factory.rb
+++ b/report/report_factory.rb
@@ -3,11 +3,14 @@
VAL_ATTR_TRAIN_TEST = [ :model_uri, :training_dataset_uri, :test_dataset_uri, :prediction_feature ]
# selected attributes of interest when generating the crossvalidation report
VAL_ATTR_CV = [ :algorithm_uri, :dataset_uri, :num_folds, :crossvalidation_fold ]
+
# selected attributes of interest when performing classification
-VAL_ATTR_CLASS = [ :percent_correct, :weighted_area_under_roc, :area_under_roc, :f_measure, :true_positive_rate, :true_negative_rate ]
-VAL_ATTR_REGR = [ :root_mean_squared_error, :mean_absolute_error, :r_square ]
+VAL_ATTR_CLASS = [ :num_instances, :num_unpredicted, :accuracy, :weighted_accuracy, :weighted_area_under_roc,
+ :area_under_roc, :f_measure, :true_positive_rate, :true_negative_rate ]
+VAL_ATTR_REGR = [ :num_instances, :num_unpredicted, :root_mean_squared_error, :mean_absolute_error, :r_square ]
-VAL_ATTR_BAR_PLOT_CLASS = [ :accuracy, :weighted_area_under_roc, :area_under_roc, :f_measure, :true_positive_rate, :true_negative_rate ]
+VAL_ATTR_BAR_PLOT_CLASS = [ :accuracy, :weighted_area_under_roc,
+ :area_under_roc, :f_measure, :true_positive_rate, :true_negative_rate ]
VAL_ATTR_BAR_PLOT_REGR = [ :root_mean_squared_error, :mean_absolute_error, :r_square ]
@@ -20,64 +23,77 @@ module Reports::ReportFactory
RT_VALIDATION = "validation"
RT_CV = "crossvalidation"
RT_ALG_COMP = "algorithm_comparison"
- RT_QMRF = "qmrf"
- REPORT_TYPES = [RT_VALIDATION, RT_CV, RT_ALG_COMP, RT_QMRF ]
+ REPORT_TYPES = [RT_VALIDATION, RT_CV, RT_ALG_COMP ]
# creates a report of a certain type according to the validation data in validation_set
#
# call-seq:
# self.create_report(type, validation_set) => Reports::ReportContent
#
- def self.create_report(type, validation_set)
+ def self.create_report(type, validation_set, task=nil)
case type
when RT_VALIDATION
- create_report_validation(validation_set)
+ create_report_validation(validation_set, task)
when RT_CV
- create_report_crossvalidation(validation_set)
+ create_report_crossvalidation(validation_set, task)
when RT_ALG_COMP
- create_report_compare_algorithms(validation_set)
+ create_report_compare_algorithms(validation_set, task)
else
raise "unknown report type "+type.to_s
end
end
private
- def self.create_report_validation(validation_set)
+ # this function is only to set task progress accordingly
+ # loading predicitons is time consuming, and is done dynamically ->
+ # pre-load and set task progress
+ def self.pre_load_predictions( validation_set, task=nil)
+ i = 0
+ task_step = 100 / validation_set.size.to_f
+ validation_set.validations.each do |v|
+ v.get_predictions( OpenTox::SubTask.create(task, i*task_step, (i+1)*task_step ) )
+ i += 1
+ end
+ end
+
+ def self.create_report_validation(validation_set, task=nil)
- raise Reports::BadRequest.new("num validations is not equal to 1") unless validation_set.size==1
+ raise OpenTox::BadRequestError.new("num validations is not equal to 1") unless validation_set.size==1
val = validation_set.validations[0]
-
+ pre_load_predictions( validation_set, OpenTox::SubTask.create(task,0,80) )
+
report = Reports::ReportContent.new("Validation report")
- if (val.classification?)
- report.add_section_result(validation_set, VAL_ATTR_TRAIN_TEST + VAL_ATTR_CLASS, "Results", "Results")
- report.add_section_roc_plot(validation_set, nil, nil, "roc-plot.svg")
- #val.get_prediction_feature_values.each do |class_value|
- #report.add_section_roc_plot(validation_set, class_value, nil, "roc-plot-"+class_value+".svg")
- #end
- report.add_section_confusion_matrix(val)
- else #regression
- report.add_section_result(validation_set, VAL_ATTR_TRAIN_TEST + VAL_ATTR_REGR, "Results", "Results")
- report.add_section_regression_plot(validation_set)
+ case val.feature_type
+ when "classification"
+ report.add_result(validation_set, [:validation_uri] + VAL_ATTR_TRAIN_TEST + VAL_ATTR_CLASS, "Results", "Results")
+ report.add_roc_plot(validation_set)
+ report.add_confusion_matrix(val)
+ when "regression"
+ report.add_result(validation_set, [:validation_uri] + VAL_ATTR_TRAIN_TEST + VAL_ATTR_REGR, "Results", "Results")
+ report.add_regression_plot(validation_set, :model_uri)
end
+ task.progress(90) if task
- report.add_section_result(validation_set, Lib::ALL_PROPS, "All Results", "All Results")
- report.add_section_predictions( validation_set )
- return report
+ report.add_result(validation_set, Lib::ALL_PROPS, "All Results", "All Results")
+ report.add_predictions( validation_set )
+ task.progress(100) if task
+ report
end
- def self.create_report_crossvalidation(validation_set)
+ def self.create_report_crossvalidation(validation_set, task=nil)
- raise Reports::BadRequest.new("num validations is not >1") unless validation_set.size>1
- raise Reports::BadRequest.new("crossvalidation-id not unique and != nil: "+
+ raise OpenTox::BadRequestError.new("num validations is not >1") unless validation_set.size>1
+ raise OpenTox::BadRequestError.new("crossvalidation-id not unique and != nil: "+
validation_set.get_values(:crossvalidation_id,false).inspect) if validation_set.unique_value(:crossvalidation_id)==nil
validation_set.load_cv_attributes
- raise Reports::BadRequest.new("num validations ("+validation_set.size.to_s+") is not equal to num folds ("+
+ raise OpenTox::BadRequestError.new("num validations ("+validation_set.size.to_s+") is not equal to num folds ("+
validation_set.unique_value(:num_folds).to_s+")") unless validation_set.unique_value(:num_folds)==validation_set.size
- raise Reports::BadRequest.new("num different folds is not equal to num validations") unless validation_set.num_different_values(:crossvalidation_fold)==validation_set.size
- raise Reports::BadRequest.new("validations must be either all regression, "+
- +"or all classification validations") unless validation_set.all_classification? or validation_set.all_regression?
+ raise OpenTox::BadRequestError.new("num different folds is not equal to num validations") unless validation_set.num_different_values(:crossvalidation_fold)==validation_set.size
+ raise OpenTox::BadRequestError.new("validations must have unique feature type, i.e. must be either all regression, "+
+ +"or all classification validations") unless validation_set.unique_feature_type
+ pre_load_predictions( validation_set, OpenTox::SubTask.create(task,0,80) )
merged = validation_set.merge([:crossvalidation_id])
raise unless merged.size==1
@@ -85,336 +101,104 @@ module Reports::ReportFactory
#puts merged.get_values(:percent_correct_variance, false).inspect
report = Reports::ReportContent.new("Crossvalidation report")
- if (validation_set.all_classification?)
- report.add_section_result(merged, VAL_ATTR_CV+VAL_ATTR_CLASS-[:crossvalidation_fold],"Mean Results","Mean Results")
-
- report.add_section_roc_plot(validation_set, nil, nil, "roc-plot.svg", "Roc Plot", nil, "Roc plot")
- report.add_section_roc_plot(validation_set, nil, :crossvalidation_fold, "roc-plot-folds.svg", "Roc Plot", nil, "Roc plots for folds")
- #validation_set.first.get_prediction_feature_values.each do |class_value|
- #report.add_section_roc_plot(validation_set, class_value, nil, "roc-plot-"+class_value+".svg")
- #end
- report.add_section_confusion_matrix(merged.validations[0])
- report.add_section_result(validation_set, VAL_ATTR_CV+VAL_ATTR_CLASS-[:num_folds], "Results","Results")
- else #regression
- report.add_section_result(merged, VAL_ATTR_CV+VAL_ATTR_REGR-[:crossvalidation_fold],"Mean Results","Mean Results")
- report.add_section_result(validation_set, VAL_ATTR_CV+VAL_ATTR_REGR-[:num_folds], "Results","Results")
+ case validation_set.unique_feature_type
+ when "classification"
+ report.add_result(merged, [:crossvalidation_uri]+VAL_ATTR_CV+VAL_ATTR_CLASS-[:crossvalidation_fold],"Mean Results","Mean Results")
+ report.add_roc_plot(validation_set, nil, "ROC Plots over all folds")
+ report.add_roc_plot(validation_set, :crossvalidation_fold)
+ report.add_confusion_matrix(merged.validations[0])
+ report.add_result(validation_set, VAL_ATTR_CV+VAL_ATTR_CLASS-[:num_folds],
+ "Results","Results",nil,"validation")
+ when "regression"
+ report.add_result(merged, [:crossvalidation_uri]+VAL_ATTR_CV+VAL_ATTR_REGR-[:crossvalidation_fold],"Mean Results","Mean Results")
+ report.add_regression_plot(validation_set, :crossvalidation_fold)
+ report.add_result(validation_set, VAL_ATTR_CV+VAL_ATTR_REGR-[:num_folds], "Results","Results")
end
+ task.progress(90) if task
- report.add_section_result(validation_set, Lib::ALL_PROPS, "All Results", "All Results")
- report.add_section_predictions( validation_set, [:crossvalidation_fold] )
- return report
+ report.add_result(validation_set, Lib::ALL_PROPS, "All Results", "All Results")
+ report.add_predictions( validation_set ) #, [:crossvalidation_fold] )
+ task.progress(100) if task
+ report
end
- def self.create_report_compare_algorithms(validation_set)
+ def self.create_report_compare_algorithms(validation_set, task=nil)
#validation_set.to_array([:test_dataset_uri, :model_uri, :algorithm_uri], false).each{|a| puts a.inspect}
- raise Reports::BadRequest.new("num validations is not >1") unless validation_set.size>1
- raise Reports::BadRequest.new("validations must be either all regression, "+
- "or all classification validations") unless validation_set.all_classification? or validation_set.all_regression?
- raise Reports::BadRequest.new("number of different algorithms <2: "+
+ raise OpenTox::BadRequestError.new("num validations is not >1") unless validation_set.size>1
+ raise OpenTox::BadRequestError.new("validations must have unique feature type, i.e. must be either all regression, "+
+ +"or all classification validations") unless validation_set.unique_feature_type
+ raise OpenTox::BadRequestError.new("number of different algorithms <2: "+
validation_set.get_values(:algorithm_uri).inspect) if validation_set.num_different_values(:algorithm_uri)<2
if validation_set.has_nil_values?(:crossvalidation_id)
- if validation_set.num_different_values(:test_dataset_uri)>1
-
- # groups results into sets with equal test and training dataset
- dataset_grouping = Reports::Util.group(validation_set.validations, [:test_dataset_uri, :training_dataset_uri])
- # check if the same algorithms exists for each test and training dataset
- Reports::Util.check_group_matching(dataset_grouping, [:algorithm_uri])
-
- #merged = validation_set.merge([:algorithm_uri, :dataset_uri])
- report = Reports::ReportContent.new("Algorithm comparison report - Many datasets")
-
- if (validation_set.all_classification?)
- report.add_section_result(validation_set,[:algorithm_uri, :test_dataset_uri]+VAL_ATTR_CLASS,"Mean Results","Mean Results")
- report.add_section_ranking_plots(validation_set, :algorithm_uri, :test_dataset_uri,
- [:percent_correct, :true_positive_rate, :true_negative_rate], "true")
- else # regression
- raise Reports::BadRequest.new("not implemented yet for regression")
- end
- return report
- else
- # this groups all validations in x different groups (arrays) according to there algorithm-uri
- algorithm_grouping = Reports::Util.group(validation_set.validations, [:algorithm_uri])
- # we check if there are corresponding validations in each group that have equal attributes (folds, num-folds,..)
- Reports::Util.check_group_matching(algorithm_grouping, [:training_dataset_uri, :test_dataset_uri, :prediction_feature])
-
- report = Reports::ReportContent.new("Algorithm comparison report")
-
- if (validation_set.all_classification?)
- report.add_section_bar_plot(validation_set,nil,:algorithm_uri,VAL_ATTR_BAR_PLOT_CLASS, "bar-plot.svg")
- report.add_section_roc_plot(validation_set,nil, :algorithm_uri, "roc-plot.svg")
- #validation_set.first.get_prediction_feature_values.each do |class_value|
- #report.add_section_bar_plot(validation_set,class_value,:algorithm_uri,VAL_ATTR_CLASS, "bar-plot-"+class_value+".svg")
- #report.add_section_roc_plot(validation_set, class_value, :algorithm_uri, "roc-plot-"+class_value+".svg")
- #end
- report.add_section_result(validation_set,[:algorithm_uri]+VAL_ATTR_CLASS,"Results","Results")
- else
- #regression
- report.add_section_result(validation_set,[:algorithm_uri]+VAL_ATTR_REGR,"Results","Results")
- report.add_section_bar_plot(validation_set,nil,:algorithm_uri,VAL_ATTR_BAR_PLOT_REGR, "bar-plot.svg")
- report.add_section_regression_plot(validation_set)
-
- #report.add_section_result(merged, VAL_ATTR_CV+VAL_ATTR_REGR-[:crossvalidation_fold],"Mean Results","Mean Results")
- #report.add_section_result(validation_set, VAL_ATTR_CV+VAL_ATTR_REGR-[:num_folds], "Results","Results")
- end
- report.add_section_result(validation_set, Lib::ALL_PROPS, "All Results", "All Results")
- return report
- end
+ raise OpenTox::BadRequestError.new("algorithm comparison for non crossvalidation not yet implemented")
else
- raise Reports::BadRequest.new("num different cross-validation-ids <2") if validation_set.num_different_values(:crossvalidation_id)<2
+ raise OpenTox::BadRequestError.new("num different cross-validation-ids <2") if validation_set.num_different_values(:crossvalidation_id)<2
validation_set.load_cv_attributes
-
- if validation_set.num_different_values(:dataset_uri)>1
- # groups results into sets with equal dataset
- dataset_grouping = Reports::Util.group(validation_set.validations, [:dataset_uri])
- # check if equal values in each group exist
- Reports::Util.check_group_matching(dataset_grouping, [:algorithm_uri, :crossvalidation_fold, :num_folds, :stratified, :random_seed])
- # we only checked that equal validations exist in each dataset group, now check for each algorithm
- dataset_grouping.each do |validations|
- algorithm_grouping = Reports::Util.group(validations, [:algorithm_uri])
- Reports::Util.check_group_matching(algorithm_grouping, [:crossvalidation_fold, :num_folds, :stratified, :random_seed])
- end
-
- merged = validation_set.merge([:algorithm_uri, :dataset_uri])
- report = Reports::ReportContent.new("Algorithm comparison report - Many datasets")
-
- if (validation_set.all_classification?)
- report.add_section_result(merged,VAL_ATTR_CV+VAL_ATTR_CLASS-[:crossvalidation_fold],"Mean Results","Mean Results")
- report.add_section_ranking_plots(merged, :algorithm_uri, :dataset_uri, [:acc, :auc, :sens, :spec], "true")
- else # regression
- report.add_section_result(merged,VAL_ATTR_CV+VAL_ATTR_REGR-[:crossvalidation_fold],"Mean Results","Mean Results")
- end
-
- return report
- else
- # this groups all validations in x different groups (arrays) according to there algorithm-uri
- algorithm_grouping = Reports::Util.group(validation_set.validations, [:algorithm_uri])
- # we check if there are corresponding validations in each group that have equal attributes (folds, num-folds,..)
- Reports::Util.check_group_matching(algorithm_grouping, [:crossvalidation_fold, :num_folds, :dataset_uri, :stratified, :random_seed])
- merged = validation_set.merge([:algorithm_uri])
-
- report = Reports::ReportContent.new("Algorithm comparison report")
-
- if (validation_set.all_classification?)
-
- report.add_section_result(merged,VAL_ATTR_CV+VAL_ATTR_CLASS-[:crossvalidation_fold],"Mean Results","Mean Results")
-
- true_class = validation_set.get_true_prediction_feature_value
- if true_class!=nil
- report.add_section_bar_plot(merged,true_class,:algorithm_uri,VAL_ATTR_BAR_PLOT_CLASS, "bar-plot.svg")
- report.add_section_roc_plot(validation_set, nil, :algorithm_uri, "roc-plot.svg")
- else
- validation_set.get_prediction_feature_values.each do |class_value|
- report.add_section_bar_plot(merged,class_value,:algorithm_uri,VAL_ATTR_BAR_PLOT_CLASS, "bar-plot-"+class_value+".svg")
- report.add_section_roc_plot(validation_set, class_value, :algorithm_uri, "roc-plot-"+class_value+".svg")
- end
- end
-
- report.add_section_result(validation_set,VAL_ATTR_CV+VAL_ATTR_CLASS-[:num_folds],"Results","Results")
- else #regression
- report.add_section_result(merged, VAL_ATTR_CV+VAL_ATTR_REGR-[:crossvalidation_fold],"Mean Results","Mean Results")
- report.add_section_result(validation_set, VAL_ATTR_CV+VAL_ATTR_REGR-[:num_folds], "Results","Results")
- end
-
- return report
- end
- end
- end
-
-end
-
-# = Reports::ReportContent
-#
-# wraps an xml-report, adds functionality for adding sections, adds a hash for tmp files
-#
-class Reports::ReportContent
-
- attr_accessor :xml_report, :tmp_files
-
- def initialize(title)
- @xml_report = Reports::XMLReport.new(title, Time.now.strftime("Created at %m.%d.%Y - %H:%M"))
- end
-
- def add_section_predictions( validation_set,
- validation_attributes=[],
- section_title="Predictions",
- section_text="This section contains predictions.",
- table_title="Predictions")
-
- section_table = @xml_report.add_section(@xml_report.get_root_element, section_title)
- if validation_set.validations[0].get_predictions
- @xml_report.add_paragraph(section_table, section_text) if section_text
- @xml_report.add_table(section_table, table_title, Reports::PredictionUtil.predictions_to_array(validation_set, validation_attributes))
- else
- @xml_report.add_paragraph(section_table, "No prediction info available.")
+ compare_algorithms_crossvalidation(validation_set, task)
end
- end
-
- def add_section_result( validation_set,
- validation_attributes,
- table_title,
- section_title="Results",
- section_text="This section contains results.")
-
- section_table = @xml_report.add_section(xml_report.get_root_element, section_title)
- @xml_report.add_paragraph(section_table, section_text) if section_text
- vals = validation_set.to_array(validation_attributes,true,validation_set.get_true_prediction_feature_value)
- vals = vals.collect{|a| a.collect{|v| v.to_s }}
- #PENDING transpose values if there more than 4 columns, and there are more than columns than rows
- transpose = vals[0].size>4 && vals[0].size>vals.size
- @xml_report.add_table(section_table, table_title, vals, !transpose, transpose)
- end
-
- def add_section_confusion_matrix( validation,
- section_title="Confusion Matrix",
- section_text="This section contains the confusion matrix.",
- table_title="Confusion Matrix")
- section_confusion = @xml_report.add_section(xml_report.get_root_element, section_title)
- @xml_report.add_paragraph(section_confusion, section_text) if section_text
- @xml_report.add_table(section_confusion, table_title,
- Reports::XMLReportUtil::create_confusion_matrix( validation.confusion_matrix ), false)
- end
+ end
- def add_section_regression_plot( validation_set,
- split_set_attribute = nil,
- plot_file_name="regression-plot.svg",
- section_title="Regression Plot",
- section_text=nil,
- image_title=nil,
- image_caption=nil)
-
- section_text = "This section contains the regression plot." unless section_text
- image_title = "Regression plot" unless image_title
+ # create Algorithm Comparison report
+ # crossvalidations, 1-n datasets, 2-n algorithms
+ def self.compare_algorithms_crossvalidation(validation_set, task=nil)
- section_regr = @xml_report.add_section(@xml_report.get_root_element, section_title)
- prediction_set = validation_set.collect{ |v| v.get_predictions }
-
- if prediction_set.size>0
-
- section_text += "\nWARNING: regression plot information not available for all validation results" if prediction_set.size!=validation_set.size
- @xml_report.add_paragraph(section_regr, section_text) if section_text
- begin
- plot_file_path = add_tmp_file(plot_file_name)
- Reports::PlotFactory.create_regression_plot( plot_file_path, prediction_set )
- @xml_report.add_imagefigure(section_regr, image_title, plot_file_name, "SVG", image_caption)
- rescue RuntimeError => ex
- LOGGER.error("Could not create regression plot: "+ex.message)
- rm_tmp_file(plot_file_name)
- @xml_report.add_paragraph(section_regr, "could not create regression plot: "+ex.message)
- end
+ # groups results into sets with equal dataset
+ if (validation_set.num_different_values(:dataset_uri)>1)
+ dataset_grouping = Reports::Util.group(validation_set.validations, [:dataset_uri])
+ # check if equal values in each group exist
+ Reports::Util.check_group_matching(dataset_grouping, [:algorithm_uri, :crossvalidation_fold, :num_folds, :stratified, :random_seed])
else
- @xml_report.add_paragraph(section_regr, "No prediction info for regression available.")
+ dataset_grouping = [ validation_set.validations ]
end
- end
-
- def add_section_roc_plot( validation_set,
- class_value = nil,
- split_set_attribute = nil,
- plot_file_name="roc-plot.svg",
- section_title="Roc Plot",
- section_text=nil,
- image_title=nil,
- image_caption=nil)
-
- if class_value
- section_text = "This section contains the roc plot for class '"+class_value+"'." unless section_text
- image_title = "Roc Plot for class-value '"+class_value+"'" unless image_title
- else
- section_text = "This section contains the roc plot." unless section_text
- image_title = "Roc Plot for all classes" unless image_title
+ # we only checked that equal validations exist in each dataset group, now check for each algorithm
+ dataset_grouping.each do |validations|
+ algorithm_grouping = Reports::Util.group(validations, [:algorithm_uri])
+ Reports::Util.check_group_matching(algorithm_grouping, [:crossvalidation_fold, :num_folds, :stratified, :random_seed])
end
- section_roc = @xml_report.add_section(@xml_report.get_root_element, section_title)
- prediction_set = validation_set.collect{ |v| v.get_predictions && v.get_predictions.confidence_values_available? }
+ pre_load_predictions( validation_set, OpenTox::SubTask.create(task,0,80) )
+ report = Reports::ReportContent.new("Algorithm comparison report - Many datasets")
+
+ if (validation_set.num_different_values(:dataset_uri)>1)
+ all_merged = validation_set.merge([:algorithm_uri, :dataset_uri, :crossvalidation_id, :crossvalidation_uri])
+ report.add_ranking_plots(all_merged, :algorithm_uri, :dataset_uri,
+ [:percent_correct, :weighted_area_under_roc, :true_positive_rate, :true_negative_rate] )
+ report.add_result_overview(all_merged, :algorithm_uri, :dataset_uri, [:percent_correct, :weighted_area_under_roc, :true_positive_rate, :true_negative_rate])
+
+ end
+
+ case validation_set.unique_feature_type
+ when "classification"
+ attributes = VAL_ATTR_CV+VAL_ATTR_CLASS-[:crossvalidation_fold]
+ attributes = ([ :dataset_uri ] + attributes).uniq
+
+ dataset_grouping.each do |validations|
+
+ set = Reports::ValidationSet.create(validations)
+
+ dataset = validations[0].dataset_uri
+ merged = set.merge([:algorithm_uri, :dataset_uri, :crossvalidation_id, :crossvalidation_uri])
+ merged.sort(:dataset_uri)
- if prediction_set.size>0
+ report.add_section("Dataset: "+dataset)
+ report.add_result(merged,attributes,
+ "Mean Results","Mean Results",nil,"crossvalidation")
+ report.add_paired_ttest_table(set, :algorithm_uri, :percent_correct)
+
+ report.add_bar_plot(merged, :algorithm_uri, VAL_ATTR_BAR_PLOT_CLASS)
+ report.add_roc_plot(set, :algorithm_uri)
+ report.end_section
+ end
- section_text += "\nWARNING: roc plot information not available for all validation results" if prediction_set.size!=validation_set.size
- @xml_report.add_paragraph(section_roc, section_text) if section_text
- begin
- plot_file_path = add_tmp_file(plot_file_name)
- Reports::PlotFactory.create_roc_plot( plot_file_path, prediction_set, class_value, split_set_attribute, false )#prediction_set.size>1 )
- @xml_report.add_imagefigure(section_roc, image_title, plot_file_name, "SVG", image_caption)
- rescue RuntimeError => ex
- LOGGER.error("could not create roc plot: "+ex.message)
- rm_tmp_file(plot_file_name)
- @xml_report.add_paragraph(section_roc, "could not create roc plot: "+ex.message)
- end
- else
- @xml_report.add_paragraph(section_roc, "No prediction-confidence info for roc plot available.")
+ when "regression"
+ raise OpenTox::BadRequestError.new("algorithm comparison for regression not yet implemented")
end
-
+ task.progress(100) if task
+ report
end
-
- def add_section_ranking_plots( validation_set,
- compare_attribute,
- equal_attribute,
- rank_attributes,
- class_value,
- section_title="Ranking Plots",
- section_text="This section contains the ranking plots.")
-
- section_rank = @xml_report.add_section(@xml_report.get_root_element, section_title)
- @xml_report.add_paragraph(section_rank, section_text) if section_text
- rank_attributes.each{|a| add_ranking_plot(section_rank, validation_set, compare_attribute, equal_attribute, a, class_value, a.to_s+"-ranking.svg")}
- end
-
- def add_ranking_plot( report_section,
- validation_set,
- compare_attribute,
- equal_attribute,
- rank_attribute,
- class_value=nil,
- plot_file_name="ranking.svg",
- image_title=nil,
- image_caption=nil)
+end
- image_title = "Ranking Plot for class value: '"+class_value.to_s+"'" if image_title==nil
- plot_file_path = add_tmp_file(plot_file_name)
- Reports::PlotFactory::create_ranking_plot(plot_file_path, validation_set, compare_attribute, equal_attribute, rank_attribute, class_value)
- @xml_report.add_imagefigure(report_section, image_title, plot_file_name, "SVG", image_caption)
-
- end
-
- def add_section_bar_plot(validation_set,
- class_value,
- title_attribute,
- value_attributes,
- plot_file_name="bar-plot.svg",
- section_title="Bar Plot",
- section_text=nil,
- image_title=nil,
- image_caption=nil)
- if class_value
- section_text = "This section contains the bar plot for class '"+class_value+"'." unless section_text
- image_title = "Bar Plot for class-value '"+class_value+"'" unless image_title
- else
- section_text = "This section contains the bar plot." unless section_text
- image_title = "Bar Plot for all classes" unless image_title
- end
- section_bar = @xml_report.add_section(@xml_report.get_root_element, section_title)
- @xml_report.add_paragraph(section_bar, section_text) if section_text
-
- plot_file_path = add_tmp_file(plot_file_name)
- Reports::PlotFactory.create_bar_plot(plot_file_path, validation_set, class_value, title_attribute, value_attributes )
- @xml_report.add_imagefigure(section_bar, image_title, plot_file_name, "SVG", image_caption)
- end
-
- private
- def add_tmp_file(tmp_file_name)
-
- @tmp_files = {} unless @tmp_files
- raise "file name already exits" if @tmp_files[tmp_file_name] || (@text_files && @text_files[tmp_file_name])
- tmp_file_path = Reports::Util.create_tmp_file(tmp_file_name)
- @tmp_files[tmp_file_name] = tmp_file_path
- return tmp_file_path
- end
-
- def rm_tmp_file(tmp_file_name)
- @tmp_files.delete(tmp_file_name) if @tmp_files.has_key?(tmp_file_name)
- end
-
-end \ No newline at end of file
diff --git a/report/report_format.rb b/report/report_format.rb
index e61d9be..67abc1e 100644
--- a/report/report_format.rb
+++ b/report/report_format.rb
@@ -1,5 +1,5 @@
-ENV['REPORT_XSL'] = "docbook-xsl-1.75.2/html/docbook.xsl" unless ENV['REPORT_XSL']
+ENV['REPORT_XSL'] = "docbook-xsl-1.76.1/html/docbook.xsl" unless ENV['REPORT_XSL']
ENV['JAVA_HOME'] = "/usr/bin" unless ENV['JAVA_HOME']
ENV['PATH'] = ENV['JAVA_HOME']+":"+ENV['PATH'] unless ENV['PATH'].split(":").index(ENV['JAVA_HOME'])
ENV['SAXON_JAR'] = "saxonhe9-2-0-3j/saxon9he.jar" unless ENV['SAXON_JAR']
@@ -10,17 +10,18 @@ ENV['SAXON_JAR'] = "saxonhe9-2-0-3j/saxon9he.jar" unless ENV['SAXON_JAR']
#
module Reports::ReportFormat
- CONTENT_TYPES = ["application/x-yaml","text/html","application/rdf+xml", "text/xml","application/pdf"]
-
# returns report-format, according to header value
def self.get_format(accept_header_value)
- begin
- content_type = MIMEParse::best_match(CONTENT_TYPES, accept_header_value)
- raise RuntimeException.new unless content_type
- rescue
- raise Reports::BadRequest.new("Accept header '"+accept_header_value.to_s+"' not supported, supported types are "+CONTENT_TYPES.join(", "))
+ case accept_header_value
+ when /application\/rdf\+xml/
+ "application/rdf+xml"
+ when /text\/xml/
+ "text/xml"
+ when /application\/x-yaml/
+ "application/x-yaml"
+ else
+ "text/html"
end
- return content_type
end
def self.get_filename_extension(format)
@@ -60,14 +61,18 @@ module Reports::ReportFormat
def self.format_report_to_html(directory, xml_filename, html_filename, css_style_sheet)
css_style_sheet = "http://opentox.informatik.uni-freiburg.de/simple_ot_stylesheet.css" unless css_style_sheet
- css = css_style_sheet ? " html.stylesheet=css_style_sheet?css_style_sheet="+URI.encode(css_style_sheet.to_s) : nil
+
+ css = css_style_sheet ? "--stringparam html.stylesheet "+URI.encode(css_style_sheet.to_s) : nil
+ cmd = "xsltproc "+css.to_s+" "+ENV['REPORT_XSL']+" "+File.join(directory,xml_filename.to_s)+" > "+File.join(directory,html_filename.to_s)
+ #css = css_style_sheet ? " html.stylesheet=css_style_sheet?css_style_sheet="+URI.encode(css_style_sheet.to_s) : nil
+ #cmd = "java -jar "+ENV['SAXON_JAR']+" -o:" + File.join(directory,html_filename.to_s)+
+ # " -s:"+File.join(directory,xml_filename.to_s)+" -xsl:"+ENV['REPORT_XSL']+" -versionmsg:off"+css.to_s
- cmd = "java -jar "+ENV['SAXON_JAR']+" -o:" + File.join(directory,html_filename.to_s)+
- " -s:"+File.join(directory,xml_filename.to_s)+" -xsl:"+ENV['REPORT_XSL']+" -versionmsg:off"+css.to_s
LOGGER.debug "Converting report to html: '"+cmd+"'"
IO.popen(cmd.to_s) do |f|
while line = f.gets do
- LOGGER.info "saxon-xslt> "+line
+ LOGGER.info "xsltproc> "+line
+ #LOGGER.info "saxon-xslt> "+line
end
end
raise "error during conversion" unless $?==0
diff --git a/report/report_persistance.rb b/report/report_persistance.rb
index 46a014e..df4930c 100644..100755
--- a/report/report_persistance.rb
+++ b/report/report_persistance.rb
@@ -1,5 +1,6 @@
REPORT_DIR = File.join(Dir.pwd,'/reports')
+require "lib/format_util.rb"
# = Reports::ReportPersistance
#
@@ -51,7 +52,7 @@ class Reports::ReportPersistance
# call-seq:
# delete_report(type, id) => boolean
#
- def delete_report(type, id)
+ def delete_report(type, id, subjectid=nil)
raise "not implemented"
end
@@ -69,7 +70,6 @@ end
class Reports::FileReportPersistance < Reports::ReportPersistance
def initialize()
- raise "pls specify report-directory (:reports -> :report_dir) in config file" unless @@config[:reports] and @@config[:reports][:report_dir]
FileUtils.mkdir REPORT_DIR unless File.directory?(REPORT_DIR)
raise "report cannot be found nor created" unless File.directory?(REPORT_DIR)
LOGGER.debug "reports are stored in "+REPORT_DIR
@@ -100,11 +100,11 @@ class Reports::FileReportPersistance < Reports::ReportPersistance
report_dir = report_directory(type, id)
raise_report_not_found(type, id) unless File.directory?(report_dir)
file_path = report_dir+"/"+resource.to_s
- raise Reports::NotFound.new("resource not found, resource: '"+resource.to_s+"', type:'"+type.to_s+"', id:'"+id.to_s+"'") unless File.exist?(file_path)
+ raise OpenTox::NotFoundError.new("resource not found, resource: '"+resource.to_s+"', type:'"+type.to_s+"', id:'"+id.to_s+"'") unless File.exist?(file_path)
return file_path
end
- def delete_report(type, id)
+ def delete_report(type, id, subjectid=nil)
report_dir = report_directory(type, id)
raise_report_not_found(type, id) unless File.directory?(report_dir)
@@ -162,7 +162,7 @@ class Reports::FileReportPersistance < Reports::ReportPersistance
private
def raise_report_not_found(type, id)
- raise Reports::NotFound.new("report not found, type:'"+type.to_s+"', id:'"+id.to_s+"'")
+ raise OpenTox::NotFoundError.new("report not found, type:'"+type.to_s+"', id:'"+id.to_s+"'")
end
def type_directory(type)
@@ -181,72 +181,101 @@ end
module Reports
- class ReportData < ActiveRecord::Base
- include Lib::RDFProvider
+ #class ReportData < ActiveRecord::Base
+# serialize :validation_uris
+# serialize :crossvalidation_uris
+# serialize :algorithm_uris
+# serialize :model_uris
+# alias_attribute :date, :created_at
+
+ class ReportData
+ include DataMapper::Resource
- def get_content_as_hash
- map = {}
- map[:created_at] = created_at
- map[:report_uri] = report_uri
- map[:report_type] = report_type
- map[:validation_uris] = validation_uris
- map[:crossvalidation_uris] = crossvalidation_uris
- map[:algorithm_uris] = algorithm_uris
- map[:model_uris] = model_uris
- map
+ property :id, Serial
+ property :report_type, String, :length => 255
+ property :created_at, DateTime
+ property :validation_uris, Object
+ property :crossvalidation_uris, Object
+ property :model_uris, Object
+ property :algorithm_uris, Object
+
+ attr_accessor :subjectid
+
+ after :save, :check_policy
+ private
+ def check_policy
+ OpenTox::Authorization.check_policy(report_uri, subjectid)
end
- def rdf_title
- "ValidationReport"
+ public
+ def date
+ created_at
end
- def uri
- report_uri
+ def report_uri
+ raise "no id" if self.id==nil
+ Reports::ReportService.instance.get_uri(self.report_type, self.id)
end
- LITERALS = [ :created_at, :report_type ]
- LITERAL_NAMES = {:created_at => OT["date"] }
- OBJECT_PROPERTIES = { :crossvalidation_uris => OT['reportCrossvalidation'], :algorithm_uris => OT['reportAlgorithm'],
- :validation_uris => OT['reportValidation'], :model_uris => OT['reportModel'] }
- OBJECTS = { :crossvalidation_uris => OT['Crossvalidation'], :algorithm_uris => OT['Algorithm'],
- :validation_uris => OT['Validation'], :model_uris => OT['Model'] }
- CLASSES = {}
- IGNORE = [ :id, :report_uri ]
+ def get_content_as_hash
+ map = {}
+ [ :date, :report_type, :validation_uris, :crossvalidation_uris,
+ :algorithm_uris, :model_uris ].each do |p|
+ map[p] = self.send(p)
+ end
+ map
+ end
+
+ def to_yaml
+ get_content_as_hash.keys_to_rdf_format.keys_to_owl_uris.to_yaml
+ end
- serialize :validation_uris
- serialize :crossvalidation_uris
- serialize :algorithm_uris
- serialize :model_uris
+ def to_rdf
+ s = OpenTox::Serializer::Owl.new
+ s.add_resource(report_uri,OT.Report,get_content_as_hash.keys_to_rdf_format.keys_to_owl_uris)
+ s.to_rdfxml
+ end
end
class ExtendedFileReportPersistance < FileReportPersistance
- def new_report(report_content, type, meta_data, uri_provider)
+ def new_report(report_content, type, meta_data, uri_provider, subjectid=nil)
raise "report meta data missing" unless meta_data
report = ReportData.new(meta_data)
- report.save #to set id
- report.attributes = { :report_type => type, :report_uri => uri_provider.get_uri(type, report.id) }
+ report.subjectid = subjectid
+ report.report_type = type
report.save
new_report_with_id(report_content, type, report.id)
end
- def list_reports(type, filter_params=nil)
- #QMRF-STUB
- return "1" if type == ReportFactory::RT_QMRF
+ def list_reports(type, filter_params={})
+ filter_params["report_type"]=type unless filter_params.has_key?("report_type")
+ #ReportData.find_like(filter_params).delete_if{|r| r.report_type!=type}.collect{ |r| r.id }
+
+ filter_params = Lib::DataMapperUtil.check_params(ReportData, filter_params)
+ # unfortunately, datamapper does not allow searching in Objects
+ # do filtering for list = Object params manually
+ list_params = {}
+ [:validation_uris, :crossvalidation_uris, :algorithm_uris, :model_uris].each do |l|
+ list_params[l] = filter_params.delete(l) if filter_params.has_key?(l)
+ end
- filter_params = {} unless filter_params
- filter_params.each{ |k,v| raise Reports::BadRequest.new("no report-attribute: "+k.to_s) unless ReportData.column_names.include?(k.gsub(/_like$/,"")) }
- filter_params[:report_type] = type
- ReportData.find(:all, :conditions => filter_params).collect{ |r| r.id }
+ reports = ReportData.all(filter_params).delete_if{|r| r.report_type!=type}
+ list_params.each do |k,v|
+ reports = reports.delete_if{ |r| !r.send(k).include?(v) }
+ end
+ reports.collect{ |r| r.id }
end
def get_report(type, id, format, force_formating, params)
- begin
- report = ReportData.find(:first, :conditions => {:id => id, :report_type => type})
- rescue ActiveRecord::RecordNotFound
- raise Reports::NotFound.new("Report with id='"+id.to_s+"' and type='"+type.to_s+"' not found.")
- end
+ report = ReportData.first({:id => id, :report_type => type})
+ raise OpenTox::NotFoundError.new("Report with id='"+id.to_s+"' and type='"+type.to_s+"' not found.") unless report
+# begin
+# report = ReportData.find(:first, :conditions => {:id => id, :report_type => type})
+# rescue ActiveRecord::RecordNotFound
+# raise OpenTox::NotFoundError.new("Report with id='"+id.to_s+"' and type='"+type.to_s+"' not found.")
+# end
case format
when "application/rdf+xml"
@@ -258,14 +287,60 @@ module Reports
end
end
- def delete_report(type, id)
- begin
- report = ReportData.find(:first, :conditions => {:id => id, :report_type => type})
- rescue ActiveRecord::RecordNotFound
- raise Reports::NotFound.new("Report with id='"+id.to_s+"' and type='"+type.to_s+"' not found.")
+ def delete_report(type, id, subjectid=nil)
+# begin
+# report = ReportData.find(:first, :conditions => {:id => id, :report_type => type})
+# rescue ActiveRecord::RecordNotFound
+# raise OpenTox::NotFoundError.new("Report with id='"+id.to_s+"' and type='"+type.to_s+"' not found.")
+# end
+# ReportData.delete(id)
+ report = ReportData.first({:id => id, :report_type => type})
+ raise OpenTox::NotFoundError.new("Report with id='"+id.to_s+"' and type='"+type.to_s+"' not found.") unless report
+ report.destroy
+ if (subjectid)
+ begin
+ res = OpenTox::Authorization.delete_policies_from_uri(report.report_uri, subjectid)
+ LOGGER.debug "Deleted validation policy: #{res}"
+ rescue
+ LOGGER.warn "Policy delete error for validation: #{report.report_uri}"
+ end
end
- ReportData.delete(id)
super
end
end
end
+
+Reports::ReportData.auto_upgrade!
+Reports::ReportData.raise_on_save_failure = true
+
+#module Reports
+# def self.check_filter_params(model, filter_params)
+# prop_names = model.properties.collect{|p| p.name.to_s}
+# filter_params.keys.each do |k|
+# key = k.to_s
+# unless prop_names.include?(key)
+# key = key.from_rdf_format
+# unless prop_names.include?(key)
+# key = key+"_uri"
+# unless prop_names.include?(key)
+# key = key+"s"
+# unless prop_names.include?(key)
+# err = "no attribute found: '"+k.to_s+"'"
+# if $sinatra
+# $sinatra.halt 400,err
+# else
+# raise err
+# end
+# end
+# end
+# end
+# end
+# filter_params[key] = filter_params.delete(k)
+# end
+# filter_params
+# end
+#
+# def ReportData.all( params )
+# super Reports.check_filter_params( ReportData, params )
+# end
+#end
diff --git a/report/report_service.rb b/report/report_service.rb
index d6d0e1a..91eefe8 100644
--- a/report/report_service.rb
+++ b/report/report_service.rb
@@ -6,12 +6,31 @@ module Reports
class ReportService
+ @@persistance = Reports::ExtendedFileReportPersistance.new
+
+ def self.persistance
+ @@persistance
+ end
+
+ def self.instance( home_uri=nil )
+ if !defined?@@instance
+ @@instance = ReportService.new(home_uri)
+ elsif home_uri && @@instance.home_uri != home_uri
+ raise "already initialized with different home_uri!!!"
+ end
+ @@instance
+ end
+
+ private
def initialize(home_uri)
+ raise "supposed to be a singleton" if defined?@@instance
+ raise "plz specify home_uri" unless home_uri
LOGGER.info "init report service"
@home_uri = home_uri
- @persistance = Reports::ExtendedFileReportPersistance.new
+ @@instance = self
end
+ public
# lists all available report types, returns list of uris
#
# call-seq:
@@ -20,7 +39,7 @@ module Reports
def get_report_types
LOGGER.info "list all report types"
- Reports::ReportFactory::REPORT_TYPES.collect{ |t| get_uri(t) }.join("\n")
+ Reports::ReportFactory::REPORT_TYPES.collect{ |t| get_uri(t) }.join("\n")+"\n"
end
# lists all stored reports of a certain type, returns a list of uris
@@ -30,9 +49,9 @@ module Reports
#
def get_all_reports(type, filter_params)
- LOGGER.info "get all reports of type '"+type.to_s+"'"
+ LOGGER.info "get all reports of type '"+type.to_s+"', filter_params: '"+filter_params.inspect+"'"
check_report_type(type)
- @persistance.list_reports(type, filter_params).collect{ |id| get_uri(type,id) }.join("\n")
+ @@persistance.list_reports(type, filter_params).collect{ |id| get_uri(type,id) }.join("\n")+"\n"
end
# creates a report of a certain type, __validation_uris__ must contain be a list of validation or cross-validation-uris
@@ -41,25 +60,28 @@ module Reports
# call-seq:
# create_report(type, validation_uris) => string
#
- def create_report(type, validation_uris)
+ def create_report(type, validation_uris, subjectid=nil, task=nil)
LOGGER.info "create report of type '"+type.to_s+"'"
check_report_type(type)
# step1: load validations
- raise Reports::BadRequest.new("validation_uris missing") unless validation_uris
+ raise OpenTox::BadRequestError.new("validation_uris missing") unless validation_uris
LOGGER.debug "validation_uri(s): '"+validation_uris.inspect+"'"
- validation_set = Reports::ValidationSet.new(validation_uris)
- raise Reports::BadRequest.new("cannot get validations from validation_uris '"+validation_uris.inspect+"'") unless validation_set and validation_set.size > 0
+ validation_set = Reports::ValidationSet.new(validation_uris, subjectid)
+ raise OpenTox::BadRequestError.new("cannot get validations from validation_uris '"+validation_uris.inspect+"'") unless validation_set and validation_set.size > 0
LOGGER.debug "loaded "+validation_set.size.to_s+" validation/s"
+ task.progress(10) if task
#step 2: create report of type
- report_content = Reports::ReportFactory.create_report(type, validation_set)
+ report_content = Reports::ReportFactory.create_report(type, validation_set,
+ OpenTox::SubTask.create(task,10,90))
LOGGER.debug "report created"
#step 3: persist report if creation not failed
- id = @persistance.new_report(report_content, type, create_meta_data(type, validation_set, validation_uris), self)
+ id = @@persistance.new_report(report_content, type, create_meta_data(type, validation_set, validation_uris), self, subjectid)
LOGGER.debug "report persisted with id: '"+id.to_s+"'"
+ task.progress(100) if task
return get_uri(type, id)
end
@@ -75,7 +97,7 @@ module Reports
accept_header_value.to_s+"', force-formating:"+force_formating.to_s+" params: '"+params.inspect+"')"
check_report_type(type)
format = Reports::ReportFormat.get_format(accept_header_value)
- return @persistance.get_report(type, id, format, force_formating, params)
+ return @@persistance.get_report(type, id, format, force_formating, params)
end
# returns a report resource (i.e. image)
@@ -87,7 +109,7 @@ module Reports
LOGGER.info "get resource '"+resource+"' for report '"+id.to_s+"' of type '"+type.to_s+"'"
check_report_type(type)
- return @persistance.get_report_resource(type, id, resource)
+ return @@persistance.get_report_resource(type, id, resource)
end
@@ -96,19 +118,19 @@ module Reports
# call-seq:
# delete_report( type, id )
#
- def delete_report( type, id )
+ def delete_report( type, id, subjectid=nil )
LOGGER.info "delete report '"+id.to_s+"' of type '"+type.to_s+"'"
check_report_type(type)
- @persistance.delete_report(type, id)
+ @@persistance.delete_report(type, id, subjectid)
end
# no api-access for this method
- def delete_all_reports( type )
+ def delete_all_reports( type, subjectid=nil )
LOGGER.info "deleting all reports of type '"+type.to_s+"'"
check_report_type(type)
- @persistance.list_reports(type).each{ |id| @persistance.delete_report(type, id) }
+ @@persistance.list_reports(type).each{ |id| @@persistance.delete_report(type, id, subjectid) }
end
def parse_type( report_uri )
@@ -123,17 +145,21 @@ module Reports
raise "invalid uri" unless report_uri.to_s =~/^#{@home_uri}.*/
id = report_uri.squeeze("/").split("/")[-1]
- @persistance.check_report_id_format(id)
+ @@persistance.check_report_id_format(id)
return id
end
+ def home_uri
+ @home_uri
+ end
+
def get_uri(type, id=nil)
@home_uri+"/"+type.to_s+(id!=nil ? "/"+id.to_s : "")
end
protected
def create_meta_data(type, validation_set, validation_uris)
- # the validtion_set contains the resolved single validations
+ # the validation_set contains the resolved single validations
# crossvalidation uris are only added if given as validation_uris - param
meta_data = {}
{ :validation_uri => "validation_uris",
@@ -151,34 +177,13 @@ module Reports
cvs << v if v =~ /crossvalidation/ and !cvs.include?(v)
end
meta_data[:crossvalidation_uris] = cvs
+
meta_data
end
def check_report_type(type)
- raise Reports::NotFound.new("report type not found '"+type.to_s+"'") unless Reports::ReportFactory::REPORT_TYPES.index(type)
+ raise OpenTox::NotFoundError.new("report type not found '"+type.to_s+"'") unless Reports::ReportFactory::REPORT_TYPES.index(type)
end
end
end
-
-class Reports::LoggedException < Exception
-
- def initialize(message)
- super(message)
- LOGGER.error(message)
- end
-
-end
-
-# corresponds to 400
-#
-class Reports::BadRequest < Reports::LoggedException
-
-end
-
-# corresponds to 404
-#
-class Reports::NotFound < Reports::LoggedException
-
-end
-
diff --git a/report/report_test.rb b/report/report_test.rb
index 1c838bf..571d6d8 100644..100755
--- a/report/report_test.rb
+++ b/report/report_test.rb
@@ -17,14 +17,14 @@ class Reports::ApplicationTest < Test::Unit::TestCase
def test_nothing
- file = File.new("qmrf-report.xml")
- raise "File not found: "+file.path.to_s unless File.exist?(file.path)
- data = File.read(file.path)
- puts OpenTox::RestClientWrapper.post("http://localhost/validation/report/qmrf/1",{:content_type => "application/qmrf-xml"},data).to_s.chomp
+# file = File.new("qmrf-report.xml")
+# raise "File not found: "+file.path.to_s unless File.exist?(file.path)
+# data = File.read(file.path)
+# puts OpenTox::RestClientWrapper.post("http://local-ot/validation/report/qmrf/1",{:content_type => "application/qmrf-xml"},data).to_s.chomp
#get "/report/qmrf/1",nil,'HTTP_ACCEPT' => "application/qmrf-xml"#"application/rdf+xml"#"application/x-yaml"
- #get "/report/validation/1",nil,'HTTP_ACCEPT' => "application/rdf+xml"#"application/x-yaml"
- #puts last_response.body.to_s
+# get "/report/validation" # ?model=http://local-ot/model/1" #,nil,'HTTP_ACCEPT' => "application/rdf+xml"#"application/x-yaml"
+# puts last_response.body.to_s
#Reports::XMLReport.generate_demo_xml_report.write_to
#raise "stop"
@@ -33,7 +33,17 @@ class Reports::ApplicationTest < Test::Unit::TestCase
#puts uri
#get uri
- #get '/report/validation/1',nil,'HTTP_ACCEPT' => "text/html"
+ #get '/report/crossvalidation',:model=>"http://local-ot/majority/class/model/101"
+ #puts last_response.body.to_s
+
+ get '/report/validation/2',nil,'HTTP_ACCEPT' => "application/x-yaml"
+ puts last_response.body.to_s
+
+ get '/report/validation/2',nil,'HTTP_ACCEPT' => "application/rdf+xml"
+ puts last_response.body.to_s
+
+
+ #get '/report/validation/117',nil,'HTTP_ACCEPT' => "text/html"
#post '/report/validation/1/format_html',:css_style_sheet=>"http://apps.ideaconsult.net:8180/ToxPredict/style/global.css"
#post 'http://ot.validation.de/report/validation',:validation_uris=>"http://ot.validation.de/1"
@@ -42,7 +52,7 @@ class Reports::ApplicationTest < Test::Unit::TestCase
#post 'http://ot.validation.de/report/crossvalidation',:validation_uris=>"http://ot.validation.de/crossvalidation/1"
#uri = last_response.body.to_s
-# val_uris = ["http://localhost/validation/64"]#,"http://localhost/validation/65" ]
+# val_uris = ["http://local-ot/validation/64"]#,"http://local-ot/validation/65" ]
#
# post '/report/validation',:validation_uris=>val_uris.join("\n")
# uri = wait_for_task(last_response.body.to_s)
@@ -119,14 +129,14 @@ end
#class Reports::ReportServiceTest < Test::Unit::TestCase
# include Lib::TestUtil
#
-# WS_VAL = @@config[:services]["opentox-validation"]
-# WS_DATA=@@config[:services]["opentox-dataset"]
+# WS_VAL = CONFIG[:services]["opentox-validation"]
+# WS_DATA=CONFIG[:services]["opentox-dataset"]
# FILE=File.new("data/hamster_carcinogenicity.owl","r")
#
-# WS_CLASS_ALG=File.join(@@config[:services]["opentox-algorithm"],"lazar")
-# WS_FEATURE_ALG=File.join(@@config[:services]["opentox-algorithm"],"fminer")
+# WS_CLASS_ALG=File.join(CONFIG[:services]["opentox-algorithm"],"lazar")
+# WS_FEATURE_ALG=File.join(CONFIG[:services]["opentox-algorithm"],"fminer")
#
-# #WS_CLASS_ALG_2="localhost:4008/algorithm"
+# #WS_CLASS_ALG_2="local-ot:4008/algorithm"
# #WS_FEATURE_ALG_2=nil
#
# def test_service_ot_webservice
@@ -138,7 +148,7 @@ end
# assert types.is_a?(String)
# assert types.split("\n").size == Reports::ReportFactory::REPORT_TYPES.size
# #Reports::ReportFactory::REPORT_TYPES.each{|t| rep.get_all_reports(t)}
-# #assert_raise(Reports::NotFound){rep.get_all_reports("osterhase")}
+# #assert_raise(OpenTox::NotFoundError){rep.get_all_reports("osterhase")}
#
# ### using ot_mock_layer (reporting component does not rely on ot validation webservice)
#
@@ -146,11 +156,11 @@ end
# #Reports::Validation.reset_validation_access
#
## create_report(rep, "validation_uri_1", "validation")
-## assert_raise(Reports::BadRequest){create_report(rep, ["validation_uri_1","validation_uri_2"], "validation")}
+## assert_raise(OpenTox::BadRequestError){create_report(rep, ["validation_uri_1","validation_uri_2"], "validation")}
##
## create_report(rep, "crossvalidation_uri_1", "crossvalidation")
## create_report(rep, ["validation_uri_1"]*Reports::OTMockLayer::NUM_FOLDS, "crossvalidation")
-## assert_raise(Reports::BadRequest){create_report(rep, ["validation_uri_1"]*(Reports::OTMockLayer::NUM_FOLDS-1), "crossvalidation")}
+## assert_raise(OpenTox::BadRequestError){create_report(rep, ["validation_uri_1"]*(Reports::OTMockLayer::NUM_FOLDS-1), "crossvalidation")}
##
## create_report(rep, ["crossvalidation_uri_1"]* (Reports::OTMockLayer::NUM_DATASETS * Reports::OTMockLayer::NUM_ALGS), "algorithm_comparison")
## create_report(rep, ["validation_uri_1"]* (Reports::OTMockLayer::NUM_DATASETS * Reports::OTMockLayer::NUM_ALGS * Reports::OTMockLayer::NUM_FOLDS), "algorithm_comparison")
@@ -172,13 +182,13 @@ end
# #val_uri = create_cross_validation(data_uri, WS_CLASS_ALG_2, WS_FEATURE_ALG_2)
# #val_uri = create_cross_validation(data_uri)
# val_uri = File.join(WS_VAL,"crossvalidation/1")
-# #val_uri2 = "http://localhost:4007/crossvalidation/14"
+# #val_uri2 = "http://local-ot:4007/crossvalidation/14"
## # add_resource val_uri
# create_report(rep, val_uri, "crossvalidation")
#
## #val_uri2 = create_cross_validation(data_uri, WS_CLASS_ALG_2, WS_FEATURE_ALG_2)
-## #val_uri = ["http://localhost:4007/crossvalidation/6", "http://localhost:4007/crossvalidation/8"]
-# #val_uri = ["http://localhost:4007/crossvalidation/7", "http://localhost:4007/crossvalidation/8"]
+## #val_uri = ["http://local-ot:4007/crossvalidation/6", "http://local-ot:4007/crossvalidation/8"]
+# #val_uri = ["http://local-ot:4007/crossvalidation/7", "http://local-ot:4007/crossvalidation/8"]
## #add_resource val_uri
# #create_report(rep, val_uri, "algorithm_comparison")
#
@@ -219,11 +229,11 @@ end
#
# #puts "created report with id "+id.to_s
#
-# #assert_raise(Reports::BadRequest){report_service.get_report(type, id, "weihnachtsmann")}
+# #assert_raise(OpenTox::BadRequestError){report_service.get_report(type, id, "weihnachtsmann")}
#
# report_service.get_report(type, id, "text/html")
# #report_service.get_report(type, id, "application/pdf")
-# #assert_raise(Reports::NotFound){report_service.delete_report(type, 877658)}
+# #assert_raise(OpenTox::NotFoundError){report_service.delete_report(type, 877658)}
#
## rep.delete_report(type, id)
# end
diff --git a/report/statistical_test.rb b/report/statistical_test.rb
new file mode 100644
index 0000000..c37827e
--- /dev/null
+++ b/report/statistical_test.rb
@@ -0,0 +1,76 @@
+#require "rubygems"
+#require "rinruby"
+#R.quit
+
+module LIB
+ class StatisticalTest
+
+ @@r = RinRuby.new(true,false)
+
+ # -1 -> array1 < array2
+ # 0 -> not difference
+ # 1 -> array2 > array1
+ #
+ def self.pairedTTest(array1, array2, significance_level=0.95)
+ @@r.assign "v1",array1
+ @@r.assign "v2",array2
+ @@r.eval "ttest = t.test(v1,v2,paired=T)"
+ t = @@r.pull "ttest$statistic"
+ p = @@r.pull "ttest$p.value"
+ #@@r.quit
+ if (1-significance_level > p)
+ t
+ else
+ 0
+ end
+ end
+ end
+end
+
+module Reports
+
+ class ReportStatisticalTest
+
+ # __grouped_validations__ : array of validation arrays
+ def self.test_matrix( validations, group_attribute, test_attribute, test_method="paired_ttest", significance_level=0.95 )
+
+ raise "statistical-test: '"+test_method+"' does not exist" unless ReportStatisticalTest.respond_to?(test_method)
+ grouped_validations = Reports::Util.group(validations, [group_attribute])
+ LOGGER.debug "perfom test '"+test_method.to_s+"' for '"+test_attribute.to_s+"' for #"+grouped_validations.size.to_s+" "+group_attribute.to_s
+
+ titles = []
+ matrix = []
+ grouped_validations.size.times do |i|
+
+ validations1 = grouped_validations[i]
+ title1 = validations1[0].send(group_attribute)
+ titles << title1
+ matrix[i] = [] unless matrix[i]
+
+ grouped_validations.size.times do |j|
+ if (i == j)
+ matrix[i][j] = nil
+ else
+ validations2 = grouped_validations[j]
+ title2 = validations2[0].send(group_attribute)
+ matrix[i][j] = ReportStatisticalTest.send(test_method,validations1,validations2,
+ test_attribute, significance_level)
+ end
+ end
+ end
+ {:titles => titles, :matrix => matrix}
+ end
+
+ def self.paired_ttest( validations1, validations2, attribute, significance_level=0.95 )
+
+ array1 = validations1.collect{ |v| v.send(attribute) }
+ array2 = validations2.collect{ |v| v.send(attribute) }
+ LOGGER.debug "paired-t-testing "+attribute.to_s+" "+array1.inspect+" vs "+array2.inspect
+ LIB::StatisticalTest.pairedTTest(array1, array2, significance_level)
+ end
+ end
+
+end
+
+#puts LIB::StatisticalTest.pairedTTest([1,2,3],[2,3,3])
+
diff --git a/report/util.rb b/report/util.rb
index db783b3..ca5f3cc 100644
--- a/report/util.rb
+++ b/report/util.rb
@@ -1,4 +1,3 @@
-
# graph-files are generated in the tmp-dir before they are stored
ENV['TMP_DIR'] = File.join(FileUtils.pwd,"reports","tmp") unless ENV['TMP_DIR']
@@ -15,6 +14,7 @@ class Array
return self.collect{|word| word[prefix.size..-1]}
end
end
+ self
end
end
@@ -75,10 +75,10 @@ module Reports::Util
#
def self.check_group_matching( grouped_objects, match_attributes )
- raise Reports::BadRequest.new("less then 2 groups, no matching possible") if grouped_objects.size<2
+ raise OpenTox::BadRequestError.new("less then 2 groups, no matching possible") if grouped_objects.size<2
first_group = grouped_objects[0]
other_groups = grouped_objects[1..-1].collect{ |g| g.collect{|o| o }}
- other_groups.each{ |g| raise Reports::BadRequest.new("groups are not equally sized, matching impossible") if g.size != first_group.size }
+ other_groups.each{ |g| raise OpenTox::BadRequestError.new("groups are not equally sized, matching impossible") if g.size != first_group.size }
first_group.each do |o|
@@ -94,7 +94,7 @@ module Reports::Util
break
end
end
- raise Reports::BadRequest.new("no match found for "+inspect_attributes(o, match_attributes)) unless match
+ raise OpenTox::BadRequestError.new("no match found for "+inspect_attributes(o, match_attributes)) unless match
end
end
end
diff --git a/report/validation_access.rb b/report/validation_access.rb
index 7d318af..53ecc46 100644..100755
--- a/report/validation_access.rb
+++ b/report/validation_access.rb
@@ -1,65 +1,31 @@
require "lib/validation_db.rb"
-# = Reports::ValidationAccess
+# = Reports::ValidationDB
#
-# service that connects (mainly) to the validation-service
+# connects directly to the validation db, overwirte with restclient calls
+# if reports/reach reports are seperated from validation someday
#
-class Reports::ValidationAccess
-
- # initialize Reports::Validation object with data from Lib:Validation object
- #
- def init_validation(validation, uri)
- raise "not implemented"
- end
-
- # sets cv-attributes in Reports::Validation object
- #
- def init_cv(validation)
- raise "not implemented"
- end
-
- # yields predictions (Lib::OTPredictions) if available
- #
- def get_predictions(validation)
- raise "not implemented"
- end
-
- # replaces crossvalidations uris with corresponding validation uris, in-/output: array
- #
- def resolve_cv_uris(validation_uris)
- raise "not implemented"
- end
+class Reports::ValidationDB
- # get domain/class values of prediction feature
- #
- def get_prediction_feature_values(validation)
- raise "not implemented"
- end
-
- # is validation classification?
- #
- def classification?(validation)
- raise "not implemented"
- end
-
- def predicted_variable(validation)
- raise "not implemented"
- end
-
-end
-
-class Reports::ValidationDB < Reports::ValidationAccess
-
- def initialize
- @model_store = {}
- end
-
- def resolve_cv_uris(validation_uris)
+ def resolve_cv_uris(validation_uris, subjectid=nil)
res = []
validation_uris.each do |u|
if u.to_s =~ /.*\/crossvalidation\/[0-9]+/
cv_id = u.split("/")[-1].to_i
- res += Lib::Validation.find( :all, :conditions => { :crossvalidation_id => cv_id } ).collect{|v| v.validation_uri.to_s}
+ cv = nil
+
+ raise OpenTox::NotAuthorizedError.new "Not authorized: GET "+u.to_s if
+ AA_SERVER and !OpenTox::Authorization.authorized?(u,"GET",subjectid)
+# begin
+# #cv = Lib::Crossvalidation.find( cv_id )
+# rescue => ex
+# raise "could not access crossvalidation with id "+validation_id.to_s+", error-msg: "+ex.message
+# end
+ cv = Lib::Crossvalidation.get( cv_id )
+ raise OpenTox::NotFoundError.new "crossvalidation with id "+cv_id.to_s+" not found" unless cv
+ raise OpenTox::BadRequestError.new("crossvalidation with id '"+cv_id.to_s+"' not finished") unless cv.finished
+ #res += Lib::Validation.find( :all, :conditions => { :crossvalidation_id => cv_id } ).collect{|v| v.validation_uri.to_s}
+ res += Lib::Validation.all( :crossvalidation_id => cv_id ).collect{|v| v.validation_uri.to_s }
else
res += [u.to_s]
end
@@ -67,20 +33,18 @@ class Reports::ValidationDB < Reports::ValidationAccess
res
end
+ def init_validation(validation, uri, subjectid=nil)
- def init_validation(validation, uri)
-
- raise Reports::BadRequest.new "not a validation uri: "+uri.to_s unless uri =~ /.*\/[0-9]+/
+ raise OpenTox::BadRequestError.new "not a validation uri: "+uri.to_s unless uri =~ /.*\/[0-9]+/
validation_id = uri.split("/")[-1]
- raise Reports::BadRequest.new "invalid validation id "+validation_id.to_s unless validation_id!=nil and
+ raise OpenTox::BadRequestError.new "invalid validation id "+validation_id.to_s unless validation_id!=nil and
(validation_id.to_i > 0 || validation_id.to_s=="0" )
v = nil
- begin
- v = Lib::Validation.find(validation_id)
- rescue => ex
- raise "could not access validation with id "+validation_id.to_s+", error-msg: "+ex.message
- end
- raise Reports::BadRequest.new "no validation found with id "+validation_id.to_s unless v #+" and uri "+uri.to_s unless v
+ raise OpenTox::NotAuthorizedError.new "Not authorized: GET "+uri.to_s if
+ AA_SERVER and !OpenTox::Authorization.authorized?(uri,"GET",subjectid)
+ v = Lib::Validation.get(validation_id)
+ raise OpenTox::NotFoundError.new "validation with id "+validation_id.to_s+" not found" unless v
+ raise OpenTox::BadRequestError.new "validation with id "+validation_id.to_s+" is not finished yet" unless v.finished
(Lib::VAL_PROPS + Lib::VAL_CV_PROPS).each do |p|
validation.send("#{p.to_s}=".to_sym, v.send(p))
@@ -95,207 +59,48 @@ class Reports::ValidationDB < Reports::ValidationAccess
def init_cv(validation)
- cv = Lib::Crossvalidation.find(validation.crossvalidation_id)
- raise Reports::BadRequest.new "no crossvalidation found with id "+validation.crossvalidation_id.to_s unless cv
+ #cv = Lib::Crossvalidation.find(validation.crossvalidation_id)
+ cv = Lib::Crossvalidation.get(validation.crossvalidation_id)
+ raise OpenTox::BadRequestError.new "no crossvalidation found with id "+validation.crossvalidation_id.to_s unless cv
Lib::CROSS_VAL_PROPS.each do |p|
validation.send("#{p.to_s}=".to_sym, cv[p])
end
end
- def get_predictions(validation)
- Lib::OTPredictions.new( validation.classification?, validation.test_dataset_uri, validation.test_target_dataset_uri,
- validation.prediction_feature, validation.prediction_dataset_uri, validation.predicted_variable)
- end
-
- def get_prediction_feature_values( validation )
- OpenTox::Feature.domain( validation.prediction_feature )
+ def get_predictions(validation, subjectid=nil, task=nil)
+ Lib::OTPredictions.new( validation.feature_type, validation.test_dataset_uri,
+ validation.test_target_dataset_uri, validation.prediction_feature, validation.prediction_dataset_uri,
+ validation.predicted_variable, subjectid, task)
end
- def classification?( validation )
- get_model(validation).classification?
+ def get_class_domain( validation )
+ OpenTox::Feature.new( validation.prediction_feature ).domain
end
- def predicted_variable(validation)
- get_model(validation).predictedVariables
+ def feature_type( validation, subjectid=nil )
+ OpenTox::Model::Generic.new(validation.model_uri).feature_type(subjectid)
+ #get_model(validation).classification?
end
- private
- def get_model(validation)
+ def predicted_variable(validation, subjectid=nil)
raise "cannot derive model depended props for merged validations" if Lib::MergeObjects.merged?(validation)
- model = @model_store[validation.model_uri]
- unless model
- model = OpenTox::Model::PredictionModel.find(validation.model_uri)
- raise "model not found '"+validation.model_uri+"'" unless validation.model_uri && model
- @model_store[validation.model_uri] = model
- end
- return model
- end
-
+ model = OpenTox::Model::Generic.find(validation.model_uri, subjectid)
+ raise OpenTox::NotFoundError.new "model not found '"+validation.model_uri+"'" unless model
+ model.metadata[OT.predictedVariables]
+ #get_model(validation).predictedVariables
+ end
+
+# private
+# def get_model(validation)
+# raise "cannot derive model depended props for merged validations" if Lib::MergeObjects.merged?(validation)
+# model = @model_store[validation.model_uri]
+# unless model
+# model = OpenTox::Model::PredictionModel.find(validation.model_uri)
+# raise "model not found '"+validation.model_uri+"'" unless validation.model_uri && model
+# @model_store[validation.model_uri] = model
+# end
+# return model
+# end
end
-
-#
-# OUTDATED, please update before use
-#
-class Reports::ValidationWebservice < Reports::ValidationAccess
-
- def resolve_cv_uris(validation_uris)
- res = []
- validation_uris.each do |u|
- if u.to_s =~ /.*\/crossvalidation\/.*/
- uri = u.to_s+"/validations"
- begin
- vali_uri_list = RestClientWrapper.get uri
- rescue => ex
- raise Reports::BadRequest.new "cannot get validations for cv at '"+uri.to_s+"', error msg: "+ex.message
- end
- res += vali_uri_list.split("\n")
- else
- res += [u.to_s]
- end
- end
- res
- end
-
-
- def init_validation(validation, uri)
-
- begin
- data = YAML.load(RestClient.get uri)
- rescue => ex
- raise Reports::BadRequest.new "cannot get validation at '"+uri.to_s+"', error msg: "+ex.message
- end
-
- Lib::VAL_PROPS.each do |p|
- validation.send("#{p}=".to_sym, data[p])
- end
-
- #model = OpenTox::Model::LazarClassificationModel.new(v[:model_uri])
- #raise "cannot access model '"+v[:model_uri].to_s+"'" unless model
- #validation.prediction_feature = model.get_prediction_feature
-
- {Lib::VAL_CV_PROP => Lib::VAL_CV_PROPS,
- Lib::VAL_CLASS_PROP => Lib::VAL_CLASS_PROPS_EXTENDED}.each do |subset_name,subset_props|
- subset = data[subset_name]
- subset_props.each{ |prop| validation.send("#{prop}=".to_sym, subset[prop]) } if subset
- end
- end
-
- def init_cv(validation)
-
- raise "cv-uri not set" unless validation.crossvalidation_uri
-
- begin
- data = YAML.load(RestClient.get validation.crossvalidation_uri)
- rescue => ex
- raise Reports::BadRequest.new "cannot get crossvalidation at '"+validation.crossvalidation_uri.to_s+"', error msg: "+ex.message
- end
-
- Lib::CROSS_VAL_PROPS.each do |p|
- validation.send("#{p.to_s}=".to_sym, data[p])
- end
- end
-
- def get_predictions(validation)
- Lib::Predictions.new( validation.prediction_feature, validation.test_dataset_uri, validation.prediction_dataset_uri)
- end
-end
-
-# = Reports::OTMockLayer
-#
-# OUTDATED, please update before use
-#
-# does not connect to other services, provides randomly generated data
-#
-class Reports::ValidationMockLayer < Reports::ValidationAccess
-
- NUM_DATASETS = 1
- NUM_ALGS = 4
- NUM_FOLDS = 5
- NUM_PREDICTIONS = 30
- ALGS = ["naive-bayes", "c4.5", "svm", "knn", "lazar", "id3"]
- DATASETS = ["hamster", "mouse" , "rat", "dog", "cat", "horse", "bug", "ant", "butterfly", "rabbit", "donkey", "monkey", "dragonfly", "frog", "dragon", "dinosaur"]
- FOLDS = [1,2,3,4,5,6,7,8,9,10]
-
- def initialize
-
- super
- @algs = []
- @datasets = []
- @folds = []
- sum = NUM_DATASETS*NUM_ALGS*NUM_FOLDS
- (0..sum-1).each do |i|
- @folds[i] = FOLDS[i%NUM_FOLDS]
- @algs[i] = ALGS[(i/NUM_FOLDS)%NUM_ALGS]
- @datasets[i] = DATASETS[((i/NUM_FOLDS)/NUM_ALGS)%NUM_DATASETS]
- end
- @count = 0
- end
-
- def resolve_cv_uris(validation_uris)
- res = []
- validation_uris.each do |u|
- if u.to_s =~ /.*crossvalidation.*/
- res += ["validation_x"]*NUM_FOLDS
- else
- res += [u.to_s]
- end
- end
- res
- end
-
- def init_validation(validation, uri)
-
- validation.model_uri = @algs[@count]
- validation.test_dataset_uri = @datasets[@count]
- validation.prediction_dataset_uri = "bla"
-
- cv_id = @count/NUM_FOLDS
- validation.crossvalidation_id = cv_id
- validation.crossvalidation_fold = @folds[@count]
-
- validation.auc = 0.5 + cv_id*0.02 + rand/3.0
- validation.acc = 0.5 + cv_id*0.02 + rand/3.0
- validation.tp = 1
- validation.fp = 1
- validation.tn = 1
- validation.fn = 1
-
- validation.algorithm_uri = @algs[@count]
- validation.training_dataset_uri = @datasets[@count]
- validation.test_dataset_uri = @datasets[@count]
-
- validation.prediction_feature = "classification"
-
- @count += 1
- end
-
- def init_cv(validation)
-
- raise "cv-id not set" unless validation.crossvalidation_id
-
- validation.num_folds = NUM_FOLDS
- validation.algorithm_uri = @algs[validation.crossvalidation_id.to_i * NUM_FOLDS]
- validation.dataset_uri = @datasets[validation.crossvalidation_id.to_i * NUM_FOLDS]
- validation.stratified = true
- validation.random_seed = 1
- #validation.CV_dataset_name = @datasets[validation.crossvalidation_id.to_i * NUM_FOLDS]
- end
-
- def get_predictions(validation)
-
- p = Array.new
- c = Array.new
- conf = Array.new
- u = Array.new
- (0..NUM_PREDICTIONS).each do |i|
- p.push rand(2)
- c.push rand(2)
- conf.push rand
- u.push("compound no"+(i+1).to_s)
- end
- Lib::MockPredictions.new( p, c, conf, u )
- end
-
- end
diff --git a/report/validation_data.rb b/report/validation_data.rb
index 0a25e87..15d51ec 100644..100755
--- a/report/validation_data.rb
+++ b/report/validation_data.rb
@@ -1,7 +1,7 @@
# the variance is computed when merging results for these attributes
VAL_ATTR_VARIANCE = [ :area_under_roc, :percent_correct, :root_mean_squared_error, :mean_absolute_error, :r_square, :accuracy ]
-VAL_ATTR_RANKING = [ :area_under_roc, :percent_correct, :true_positive_rate, :true_negative_rate, :accuracy ]
+VAL_ATTR_RANKING = [ :area_under_roc, :percent_correct, :true_positive_rate, :true_negative_rate, :weighted_area_under_roc ] #:accuracy ]
ATTR_NICE_NAME = {}
@@ -20,10 +20,14 @@ class Object
def to_nice_s
if is_a?(Float)
- if self>0.01
+ if self==0
+ return "0"
+ elsif abs>0.1
return "%.2f" % self
+ elsif abs>0.01
+ return "%.3f" % self
else
- return self.to_s
+ return "%.2e" % self
end
end
return collect{ |i| i.to_nice_s }.join(", ") if is_a?(Array)
@@ -60,8 +64,8 @@ module Reports
@@validation_access = validation_access
end
- def self.resolve_cv_uris(validation_uris)
- @@validation_access.resolve_cv_uris(validation_uris)
+ def self.resolve_cv_uris(validation_uris, subjectid)
+ @@validation_access.resolve_cv_uris(validation_uris, subjectid)
end
# create member variables for all validation properties
@@ -72,8 +76,10 @@ module Reports
attr_reader :predictions
- def initialize(uri = nil)
- @@validation_access.init_validation(self, uri) if uri
+ def initialize(uri = nil, subjectid = nil)
+ @@validation_access.init_validation(self, uri, subjectid) if uri
+ @subjectid = subjectid
+ #raise "subjectid is nil" unless subjectid
end
# returns/creates predictions, cache to save rest-calls/computation time
@@ -81,32 +87,38 @@ module Reports
# call-seq:
# get_predictions => Reports::Predictions
#
- def get_predictions
- return @predictions if @predictions
- unless @prediction_dataset_uri
- LOGGER.info("no predictions available, prediction_dataset_uri not set")
- return nil
+ def get_predictions( task=nil )
+ if @predictions
+ task.progress(100) if task
+ @predictions
+ else
+ unless @prediction_dataset_uri
+ LOGGER.info("no predictions available, prediction_dataset_uri not set")
+ task.progress(100) if task
+ nil
+ else
+ @predictions = @@validation_access.get_predictions( self, @subjectid, task )
+ end
end
- @predictions = @@validation_access.get_predictions( self )
end
# returns the predictions feature values (i.e. the domain of the class attribute)
#
- def get_prediction_feature_values
- return @prediction_feature_values if @prediction_feature_values
- @prediction_feature_values = @@validation_access.get_prediction_feature_values(self)
+ def get_class_domain()
+ @class_domain = @@validation_access.get_class_domain(self) unless @class_domain
+ @class_domain
end
- # is classification validation? cache to save resr-calls
+ # is classification/regression validation? cache to save rest-calls
#
- def classification?
- return @is_classification if @is_classification!=nil
- @is_classification = @@validation_access.classification?(self)
+ def feature_type
+ return @feature_type if @feature_type!=nil
+ @feature_type = @@validation_access.feature_type(self, @subjectid)
end
def predicted_variable
return @predicted_variable if @predicted_variable!=nil
- @predicted_variable = @@validation_access.predicted_variable(self)
+ @predicted_variable = @@validation_access.predicted_variable(self, @subjectid)
end
# loads all crossvalidation attributes, of the corresponding cv into this object
@@ -115,6 +127,24 @@ module Reports
@@validation_access.init_cv(self)
end
+ @@persistance = Reports::ReportService.persistance
+
+ def validation_report_uri
+ #puts "searching for validation report: "+self.validation_uri.to_s
+ return @validation_report_uri if @validation_report_uri!=nil
+ ids = @@persistance.list_reports("validation",{:validation_uris=>validation_uri })
+ @validation_report_uri = Reports::ReportService.instance.get_uri("validation",ids[-1]) if ids and ids.size>0
+ end
+
+ def cv_report_uri
+ #puts "searching for cv report: "+self.crossvalidation_uri.to_s
+ return @cv_report_uri if @cv_report_uri!=nil
+ raise "no cv uri "+to_yaml unless self.crossvalidation_uri
+ ids = @@persistance.list_reports("crossvalidation",{:crossvalidation=>self.crossvalidation_uri.to_s })
+ #puts "-> "+ids.inspect
+ @cv_report_uri = Reports::ReportService.instance.get_uri("crossvalidation",ids[-1]) if ids and ids.size>0
+ end
+
def clone_validation
new_val = clone
VAL_ATTR_VARIANCE.each { |a| new_val.send((a.to_s+"_variance=").to_sym,nil) }
@@ -128,11 +158,18 @@ module Reports
#
class ValidationSet
- def initialize(validation_uris = nil)
+ def initialize(validation_uris=nil, subjectid=nil)
@unique_values = {}
- validation_uris = Reports::Validation.resolve_cv_uris(validation_uris) if validation_uris
+ validation_uris = Reports::Validation.resolve_cv_uris(validation_uris, subjectid) if validation_uris
@validations = Array.new
- validation_uris.each{|u| @validations.push(Reports::Validation.new(u))} if validation_uris
+ validation_uris.each{|u| @validations.push(Reports::Validation.new(u, subjectid))} if validation_uris
+ end
+
+
+ def self.create(validations)
+ set = ValidationSet.new
+ validations.each{ |v| set.validations.push(v) }
+ set
end
def get(index)
@@ -194,35 +231,42 @@ module Reports
return val
end
- def get_true_prediction_feature_value
- if all_classification?
- class_values = get_prediction_feature_values
- if class_values.size == 2
- (0..1).each do |i|
- return class_values[i] if (class_values[i].to_s.downcase == "true" || class_values[i].to_s.downcase == "active")
- end
- end
- end
- return nil
+# def get_true_prediction_feature_value
+# if all_classification?
+# class_values = get_class_domain
+# if class_values.size == 2
+# (0..1).each do |i|
+# return class_values[i] if (class_values[i].to_s.downcase == "true" || class_values[i].to_s.downcase == "active")
+# end
+# end
+# end
+# return nil
+# end
+
+ def get_class_domain( )
+ return unique_value("get_class_domain")
end
- def get_prediction_feature_values
- return unique_value("get_prediction_feature_values")
+ def get_domain_for_attr( attribute )
+ class_domain = get_class_domain()
+ if Lib::Validation.classification_property?(attribute) and
+ !Lib::Validation.depends_on_class_value?(attribute)
+ [ nil ]
+ elsif Lib::Validation.classification_property?(attribute) and
+ class_domain.size==2 and
+ Lib::Validation.complement_exists?(attribute)
+ [ class_domain[0] ]
+ else
+ class_domain
+ end
end
- # checks weather all validations are classification validations
+ # checks weather all validations are classification/regression validations
#
- def all_classification?
- return unique_value("classification?")
+ def unique_feature_type
+ return unique_value("feature_type")
end
- # checks weather all validations are regression validations
- #
- def all_regression?
- # WARNING, NOT TRUE: !all_classification == all_regression?
- return unique_value("classification?")==false
- end
-
# returns a new set with all validation that have values as specified in the map
#
# call-seq:
@@ -246,6 +290,47 @@ module Reports
return new_set
end
+ def to_table( attribute_col, attribute_row, attribute_val)
+
+ row_values = get_values(attribute_row)
+ #puts row_values.inspect
+ col_values = get_values(attribute_col)
+ #puts col_values.inspect
+
+ # get domain for classification attribute, i.e. ["true","false"]
+ class_domain = get_domain_for_attr(attribute_val)
+ # or the attribute has a complementary value, i.e. true_positive_rate
+ # -> domain is reduced to one class value
+ first_value_elem = (class_domain.size==1 && class_domain[0]!=nil)
+
+ cell_values = {}
+ row_values.each do |row|
+ col_values.each do |col|
+ val = nil
+ @validations.each do |v|
+ if v.send(attribute_row)==row and v.send(attribute_col)==col
+ raise "two validation have equal row and column values"if val!=nil
+ val = v.send(attribute_val)
+ val = val[class_domain[0]] if first_value_elem
+ val = val.to_nice_s
+ end
+ end
+ cell_values[row] = [] if cell_values[row]==nil
+ cell_values[row] << val
+ end
+ end
+ #puts cell_values.inspect
+
+ table = []
+ table << [ "" ] + col_values
+ row_values.each do |row|
+ table << [ row ] + cell_values[row]
+ end
+ #puts table.inspect
+
+ table
+ end
+
# returns an array, with values for __attributes__, that can be use for a table
# * first row is header row
# * other rows are values
@@ -253,29 +338,56 @@ module Reports
# call-seq:
# to_array(attributes, remove_nil_attributes) => array
#
- def to_array(attributes, remove_nil_attributes=true, true_class_value=nil)
+ def to_array(attributes, remove_nil_attributes=true)
array = Array.new
array.push(attributes.collect{|a| a.to_s.nice_attr})
attribute_not_nil = Array.new(attributes.size)
@validations.each do |v|
- index = 0
+ index = -1
array.push(attributes.collect do |a|
+ index += 1
if VAL_ATTR_VARIANCE.index(a)
variance = v.send( (a.to_s+"_variance").to_sym )
end
- variance = " +- "+variance.to_nice_s if variance
- attribute_not_nil[index] = true if remove_nil_attributes and v.send(a)!=nil
- index += 1
+
+ #variance = " +- "+variance.to_nice_s if variance
val = v.send(a)
- val = val[true_class_value] if true_class_value!=nil && val.is_a?(Hash) && Lib::VAL_CLASS_PROPS_PER_CLASS_COMPLEMENT_EXISTS.index(a)!=nil
- val.to_nice_s + variance.to_s
+ if val==nil || val.to_s.chomp.size==0
+ ''
+ else
+ attribute_not_nil[index] = true if remove_nil_attributes
+
+ class_domain = get_domain_for_attr(a)
+ # get domain for classification attribute, i.e. ["true","false"]
+ if class_domain.size==1 && class_domain[0]!=nil
+ # or the attribute has a complementary value, i.e. true_positive_rate
+ # -> domain is reduced to one class value
+ raise "illegal state, value for "+a.to_s+" is no hash: '"+val.to_s+"'" unless (val.is_a?(Hash))
+ val = val[class_domain[0]]
+ end
+
+ if variance
+ if (val.is_a?(Array))
+ raise "not implemented"
+ elsif (val.is_a?(Hash))
+ val.collect{ |i,j| i.to_nice_s+": "+j.to_nice_s + " +- " +
+ variance[i].to_nice_s }.join(", ")
+ else
+ val.to_nice_s + " +- " + variance.to_nice_s
+ end
+ else
+ val.to_nice_s
+ end
+ end
end)
end
+
if remove_nil_attributes #delete in reverse order to avoid shifting of indices
(0..attribute_not_nil.size-1).to_a.reverse.each do |i|
array.each{|row| row.delete_at(i)} unless attribute_not_nil[i]
end
end
+
return array
end
@@ -294,6 +406,7 @@ module Reports
#compute grouping
grouping = Reports::Util.group(@validations, equal_attributes)
+ #puts "groups "+grouping.size.to_s
Lib::MergeObjects.register_merge_attributes( Reports::Validation,
Lib::VAL_MERGE_AVG,Lib::VAL_MERGE_SUM,Lib::VAL_MERGE_GENERAL) unless
@@ -310,6 +423,10 @@ module Reports
return new_set
end
+ def sort(attribute, ascending=true)
+ @validations.sort!{ |a,b| a.send(attribute).to_s <=> b.send(attribute).to_s }
+ end
+
# creates a new validaiton set, that contains a ranking for __ranking_attribute__
# (i.e. for ranking attribute :acc, :acc_ranking is calculated)
# all validation with equal values for __equal_attributes__ are compared
@@ -319,7 +436,8 @@ module Reports
# compute_ranking(equal_attributes, ranking_attribute) => array
#
def compute_ranking(equal_attributes, ranking_attribute, class_value=nil )
-
+
+ #puts "compute_ranking("+equal_attributes.inspect+", "+ranking_attribute.inspect+", "+class_value.to_s+" )"
new_set = Reports::ValidationSet.new
(0..@validations.size-1).each do |i|
new_set.validations.push(@validations[i].clone_validation)
@@ -337,14 +455,16 @@ module Reports
raise "no value for class value "+class_value.class.to_s+" "+class_value.to_s+" in hash "+val.inspect.to_s unless val.has_key?(class_value)
val = val[class_value]
else
- raise "is a hash "+ranking_attribute+", specify class value plz"
+ raise "value for '"+ranking_attribute.to_s+"' is a hash, specify class value plz"
end
end
rank_hash[i] = val
end
+ #puts rank_hash.inspect
# sort group accrording to second value (= ranking value)
rank_array = rank_hash.sort { |a, b| b[1] <=> a[1] }
+ #puts rank_array.inspect
# create ranks array
ranks = Array.new
@@ -370,6 +490,7 @@ module Reports
end
end
end
+ #puts ranks.inspect
# set rank as validation value
(0..rank_array.size-1).each do |j|
diff --git a/report/xml_report.rb b/report/xml_report.rb
index a621400..4b9a11a 100644..100755
--- a/report/xml_report.rb
+++ b/report/xml_report.rb
@@ -1,9 +1,15 @@
+#['rubygems', 'rexml/document' ].each do |g|
+# require g
+#end
require "report/xml_report_util.rb"
-ENV['REPORT_DTD'] = "docbook-xml-4.5/docbookx.dtd" unless ENV['REPORT_DTD']
+ENV['DOCBOOK_DIRECTORY'] = "docbook-xml-4.5" unless ENV['DOCBOOK_DIRECTORY']
+ENV['REPORT_DTD'] = "docbookx.dtd" unless ENV['REPORT_DTD']
+
#transfer to absolute path
-ENV['REPORT_DTD'] = File.expand_path(ENV['REPORT_DTD']) if File.exist?(ENV['REPORT_DTD'])
+#ENV['REPORT_DTD'] = File.expand_path(ENV['REPORT_DTD']) if File.exist?(ENV['REPORT_DTD'])
+
# = XMLReport
#
@@ -15,13 +21,23 @@ module Reports
class XMLReport
include REXML
+ def self.dtd_directory
+ if $url_provider
+ $url_provider.url_for('/'+ENV['DOCBOOK_DIRECTORY']+'/'+ENV['REPORT_DTD'], :full)
+ else
+ f = File.expand_path(File.join(ENV['DOCBOOK_DIRECTORY'],ENV['REPORT_DTD']))
+ raise "cannot find dtd" unless File.exist?(f)
+ f
+ end
+ end
+
# create new xmlreport
def initialize(title, pubdate=nil, author_firstname = nil, author_surname = nil)
@doc = Document.new
decl = XMLDecl.new
@doc << decl
- type = DocType.new('article PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "'+ENV['REPORT_DTD']+'"')
+ type = DocType.new('article PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "'+XMLReport.dtd_directory+'"')
@doc << type
@root = Element.new("article")
@@ -84,19 +100,27 @@ module Reports
# call-seq:
# add_imagefigure( element, title, path, filetype, caption = nil ) => REXML::Element
#
- def add_imagefigure( element, title, path, filetype, caption = nil )
+ def add_imagefigure( element, title, path, filetype, size_pct=100, caption = nil )
figure = Reports::XMLReportUtil.attribute_element("figure", {"float" => 0})
figure << Reports::XMLReportUtil.text_element("title", title)
media = Element.new("mediaobject")
image = Element.new("imageobject")
imagedata = Reports::XMLReportUtil.attribute_element("imagedata",
- {"fileref" => path, "format"=>filetype, "contentwidth" => "6in", "contentdepth"=> "4in"
+ {"fileref" => path, "format"=>filetype, "contentwidth" => size_pct.to_s+"%",
+ #"contentdepth"=> "4in"
})#"width" => "6in", "height" => "5in"}) #"contentwidth" => "100%"})
#imagedata = Reports::XMLReportUtil.attribute_element("imagedata",{"width" => "6in", "fileref" => path, "format"=>filetype})
@resource_path_elements[imagedata] = "fileref"
image << imagedata
+
media << image
+
+# ulink = Element.new("ulink")
+# ulink.add_attributes({"url" => "http://google.de"})
+# ulink << image
+# media << ulink
+
media << Reports::XMLReportUtil.text_element("caption", caption) if caption
figure << media
element << figure
@@ -116,15 +140,17 @@ module Reports
# adds a table to a REXML:Element, _table_values_ should be a multi-dimensional-array, returns the table as element
#
# call-seq:
- # add_table( element, title, table_values, first_row_is_table_header=true ) => REXML::Element
+ # add_table( element, title, table_values, first_row_header=true ) => REXML::Element
#
- def add_table( element, title, table_values, first_row_is_table_header=true, transpose=false, auto_link_urls=true )
+ def add_table( element, title, table_values, first_row_header=true, first_col_header=false, transpose=false, auto_link_urls=true )
raise "table_values is not mulit-dimensional-array" unless table_values && table_values.is_a?(Array) && table_values[0].is_a?(Array)
values = transpose ? table_values.transpose : table_values
- table = Reports::XMLReportUtil.attribute_element("table",{"frame" => "none", "colsep" => 1, "rowsep" => 1 })
+ params = {"frame" => "none", "colsep" => 1, "rowsep" => 1 }
+ params["rowheader"] = "firstcol" if first_col_header
+ table = Reports::XMLReportUtil.attribute_element("table",params)
table << Reports::XMLReportUtil.text_element("title", title)
@@ -134,7 +160,7 @@ module Reports
table_body_values = values
- if first_row_is_table_header
+ if first_row_header
table_head_values = values[0];
table_body_values = values[1..-1];
@@ -142,7 +168,7 @@ module Reports
row = Element.new("row")
table_head_values.each do |v|
entry = Element.new("entry")
- if auto_link_urls && v.to_s =~ /^http:\/\//
+ if auto_link_urls && v.to_s =~ /^http(s?):\/\//
add_url(entry, v.to_s)
else
entry.text = v.to_s
@@ -158,9 +184,9 @@ module Reports
row = Element.new("row")
r.each do |v|
entry = Element.new("entry")
- if auto_link_urls && v.to_s =~ /depict/ #PENDING
+ if auto_link_urls && v.to_s =~ /depict/ || v.to_s =~ /image$/ #PENDING
add_image(entry, v.to_s)
- elsif auto_link_urls && v.to_s =~ /^http:\/\//
+ elsif auto_link_urls && v.to_s =~ /^http(s?):\/\//
add_url(entry, v.to_s, v.to_s)
else
entry.text = v.to_s
@@ -215,7 +241,7 @@ module Reports
end
end
- @doc.write(out,2, true, true)
+ @doc.write(out) #,2, true, true)
out.flush
end
@@ -228,7 +254,7 @@ module Reports
section1 = rep.add_section(rep.get_root_element, "First Section")
rep.add_paragraph(section1, "some text")
rep.add_paragraph(section1, "even more text")
- rep.add_imagefigure(section1, "Figure", "http://upload.wikimedia.org/wikipedia/commons/thumb/e/eb/Siegel_der_Albert-Ludwigs-Universit%C3%A4t_Freiburg.svg/354px-Siegel_der_Albert-Ludwigs-Universit%C3%A4t_Freiburg.svg", "SVG", "this is the logo of freiburg university")
+ rep.add_imagefigure(section1, "Figure", "http://upload.wikimedia.org/wikipedia/commons/thumb/e/eb/Siegel_der_Albert-Ludwigs-Universit%C3%A4t_Freiburg.svg/354px-Siegel_der_Albert-Ludwigs-Universit%C3%A4t_Freiburg.svg", "SVG", 100, "this is the logo of freiburg university")
section2 = rep.add_section(rep.get_root_element,"Second Section")
rep.add_section(section2,"A Subsection")
rep.add_section(section2,"Another Subsection")
diff --git a/report/xml_report_util.rb b/report/xml_report_util.rb
index fcb7d96..fd1a179 100644..100755
--- a/report/xml_report_util.rb
+++ b/report/xml_report_util.rb
@@ -35,21 +35,22 @@ module Reports
end
confusion = []
- confusion.push( [ "", "", "actual" ] + [""] * num_classes )
- confusion.push( [ "", "" ] + class_values + [ "total"])
+ confusion.push( [ " ", " ", "actual" ] + [" "] * num_classes )
+ confusion.push( [ " ", " " ] + class_values + [ "total"])
class_values.each do |predicted|
- row = [ (confusion.size==2 ? "predicted" : ""), predicted ]
+ row = [ (confusion.size==2 ? "predicted" : " "), predicted ]
class_values.each do |actual|
row.push( confusion_matrix[{:confusion_matrix_actual => actual, :confusion_matrix_predicted => predicted}].to_nice_s )
end
row.push( sum_predicted[predicted].to_nice_s )
confusion.push( row )
end
- last_row = [ "", "total" ]
+ last_row = [ " ", "total" ]
class_values.each do |actual|
last_row.push( sum_actual[actual].to_nice_s )
end
+ last_row.push(" ")
confusion.push( last_row )
return confusion