diff options
author | Martin Gütlein <martin.guetlein@gmail.com> | 2010-03-23 14:07:14 +0100 |
---|---|---|
committer | Martin Gütlein <martin.guetlein@gmail.com> | 2010-03-23 14:07:14 +0100 |
commit | 14d2a68564061d63166cd409bf4fd30dc841d2b8 (patch) | |
tree | e07cbc10883ee8c116caa443048df0471efe02b2 | |
parent | 6a5ebb67493ab2c30121ae26fb75d6a24c36eafc (diff) |
added predictedValues feature, some report changes (true feature prediction class), some hacks to validate IDEA/AMBIT
-rw-r--r-- | RankPlotter/RankPlotter.jar | bin | 12307194 -> 12309175 bytes | |||
-rw-r--r-- | lib/merge.rb | 6 | ||||
-rw-r--r-- | lib/ot_predictions.rb | 33 | ||||
-rw-r--r-- | lib/predictions.rb | 41 | ||||
-rw-r--r-- | lib/validation_db.rb | 7 | ||||
-rw-r--r-- | lib/wrapper.rb | 192 | ||||
-rw-r--r-- | report/environment.rb | 4 | ||||
-rw-r--r-- | report/plot_factory.rb | 23 | ||||
-rw-r--r-- | report/prediction_util.rb | 13 | ||||
-rw-r--r-- | report/report_factory.rb | 57 | ||||
-rw-r--r-- | report/report_service.rb | 2 | ||||
-rw-r--r-- | report/report_test.rb | 10 | ||||
-rw-r--r-- | report/validation_access.rb | 35 | ||||
-rw-r--r-- | report/validation_data.rb | 84 | ||||
-rw-r--r-- | report/xml_report.rb | 2 | ||||
-rw-r--r-- | validation/validation_application.rb | 4 | ||||
-rw-r--r-- | validation/validation_test.rb | 153 |
17 files changed, 508 insertions, 158 deletions
diff --git a/RankPlotter/RankPlotter.jar b/RankPlotter/RankPlotter.jar Binary files differindex 7ccd252..08509f5 100644 --- a/RankPlotter/RankPlotter.jar +++ b/RankPlotter/RankPlotter.jar diff --git a/lib/merge.rb b/lib/merge.rb index b42df1e..7e05cd8 100644 --- a/lib/merge.rb +++ b/lib/merge.rb @@ -28,6 +28,10 @@ module Lib return m end + def self.merged?(object) + return merge_count(object)>1 + end + def self.merge_objects( object1, object2 ) raise "classes not equal" if object1.class != object2.class @@ -51,7 +55,7 @@ module Lib end def self.register_merge_attributes( object_class, avg_attributes, sum_attributes, non_numeric_attributes) - @@avg_attributes[object_class] = avg_attributes + @@avg_attributes[object_class] = avg_attributes + avg_attributes.collect{ |a| (a.to_s+"_ranking").to_sym } @@sum_attributes[object_class] = sum_attributes @@non_numeric_attributes[object_class] = non_numeric_attributes end diff --git a/lib/ot_predictions.rb b/lib/ot_predictions.rb index ab6ca3a..2098ab1 100644 --- a/lib/ot_predictions.rb +++ b/lib/ot_predictions.rb @@ -5,15 +5,22 @@ module Lib class OTPredictions < Predictions + def identifier(instance_index) + return compound(instance_index) + end + def compound(instance_index) return @compounds[instance_index] end - def initialize(is_classification, prediction_feature, test_dataset_uri, prediction_dataset_uri) + def initialize(is_classification, test_dataset_uri, prediction_feature, prediction_dataset_uri, predicted_variable) LOGGER.debug("loading prediciton via test-dateset:'"+test_dataset_uri.to_s+ "' and prediction-dataset:'"+prediction_dataset_uri.to_s+ - "', prediction_feature: '"+prediction_feature.to_s+"'") + "', prediction_feature: '"+prediction_feature.to_s+"' "+ + "', predicted_variable: '"+predicted_variable.to_s+"'") + + predicted_variable=prediction_feature if predicted_variable==nil test_dataset = OpenTox::Dataset.find test_dataset_uri prediction_dataset = OpenTox::Dataset.find prediction_dataset_uri @@ -30,6 +37,8 @@ module Lib #@compounds << (OpenTox::Compound.new :uri=>compound.to_s).smiles value = nil + LOGGER.warn "value is hash, but no feature value '"+prediction_feature.to_s+ + "', hash: '"+featuresValues.inspect+"'" if featuresValues.is_a?(Hash) and !featuresValues.has_key?(prediction_feature) value = featuresValues[prediction_feature] if featuresValues.is_a?(Hash) value = value[0] if value.is_a?(Array) and value.length == 1 value = nil if value.to_s.size==0 # PENDING: hack to avoid illegal boolean values @@ -39,11 +48,18 @@ module Lib raise "illegal class_value of actual value "+value.to_s+" class: "+value.class.to_s unless value==nil or class_values.index(value)!=nil actual_values.push class_values.index(value) else - value = value.to_f unless value==nil or value.is_a?(Numeric) + begin + value = value.to_f unless value==nil or value.is_a?(Numeric) + rescue + LOGGER.warn "no numeric value for regression: '"+value.to_s+"'" + value = nil + end actual_values.push value end end + raise "test dataset is empty" unless @compounds.size>0 + predicted_values = Array.new(actual_values.size) confidence_values = Array.new(actual_values.size) @@ -53,7 +69,9 @@ module Lib raise "compound "+compound.to_s+" not found in\n"+@compounds.inspect if index==nil value = nil - value = featuresValues[prediction_feature] if featuresValues.is_a?(Hash) + LOGGER.warn "value is hash, but no feature value '"+predicted_variable.to_s+ + "', hash: '"+featuresValues.inspect+"'" if featuresValues.is_a?(Hash) and !featuresValues.has_key?(predicted_variable) + value = featuresValues[predicted_variable] if featuresValues.is_a?(Hash) value = value[0] if value.is_a?(Array) and value.length == 1 value = nil if value.to_s.size==0 @@ -73,7 +91,12 @@ module Lib predicted_values[index] = class_values.index(value) confidence_values[index] = confidence if confidence!=nil else - value = value.to_f unless value==nil or value.is_a?(Numeric) + begin + value = value.to_f unless value==nil or value.is_a?(Numeric) + rescue + LOGGER.warn "no numeric value for regression: '"+value.to_s+"'" + value = nil + end predicted_values[index] = value end index += 1 diff --git a/lib/predictions.rb b/lib/predictions.rb index 259a990..6d7d9b2 100644 --- a/lib/predictions.rb +++ b/lib/predictions.rb @@ -15,6 +15,10 @@ module Lib class Predictions + def identifier(instance_index) + return instance_index.to_s + end + def initialize( predicted_values, actual_values, confidence_values, @@ -28,6 +32,10 @@ module Lib @prediction_feature_values = prediction_feature_values @num_classes = 1 + #puts "predicted: "+predicted_values.inspect + #puts "actual: "+actual_values.inspect + #puts "confidence: "+confidence_values.inspect + raise "no predictions" if @predicted_values.size == 0 num_info = "predicted:"+@predicted_values.size.to_s+ " confidence:"+@confidence_values.size.to_s+" actual:"+@actual_values.size.to_s @@ -369,9 +377,37 @@ module Lib return incorrect end + def weighted_area_under_roc + return weighted_measure( :area_under_roc ) + end + + def weighted_f_measure + return weighted_measure( :f_measure ) + end + + private + def weighted_measure( measure ) + + sum_instances = 0 + num_instances_per_class = Array.new(@num_classes, 0) + (0..@num_classes-1).each do |i| + (0..@num_classes-1).each do |j| + num_instances_per_class[i] += @confusion_matrix[i][j] + end + sum_instances += num_instances_per_class[i] + end + raise "sum instances ("+sum_instances.to_s+") != num predicted ("+@num_predicted.to_s+")" unless @num_predicted == sum_instances + + weighted = 0; + (0..@num_classes-1).each do |i| + weighted += self.send(measure,i) * num_instances_per_class[i] + end + return weighted / @num_predicted.to_f + end # regression ####################################################################################### + public def root_mean_squared_error return 0 if (@num_with_actual_value - @num_unpredicted)==0 Math.sqrt(@sum_squared_error / (@num_with_actual_value - @num_unpredicted).to_f) @@ -403,6 +439,7 @@ module Lib a << @actual_values[i] end end + return {:predicted_values => p, :actual_values => a, :confidence_values => c} end @@ -448,6 +485,10 @@ module Lib ################################################################################################################### + #def compound(instance_index) + #return "instance_index.to_s" + #end + private def prediction_feature_value_map(proc) res = {} diff --git a/lib/validation_db.rb b/lib/validation_db.rb index 8f4a540..a38b901 100644 --- a/lib/validation_db.rb +++ b/lib/validation_db.rb @@ -18,7 +18,7 @@ module Lib # :classification_statistics VAL_CLASS_PROPS_SINGLE_SUM = [ :num_correct, :num_incorrect, :confusion_matrix ] - VAL_CLASS_PROPS_SINGLE_AVG = [ :percent_correct, :percent_incorrect ] + VAL_CLASS_PROPS_SINGLE_AVG = [ :percent_correct, :percent_incorrect, :weighted_area_under_roc ] VAL_CLASS_PROPS_SINGLE = VAL_CLASS_PROPS_SINGLE_SUM + VAL_CLASS_PROPS_SINGLE_AVG # :class_value_statistics @@ -26,8 +26,11 @@ module Lib :num_true_positives, :num_true_negatives ] VAL_CLASS_PROPS_PER_CLASS_AVG = [ :area_under_roc, :false_negative_rate, :false_positive_rate, :f_measure, :precision, - :recall, :true_negative_rate, :true_positive_rate ] + :true_negative_rate, :true_positive_rate ] #:recall, VAL_CLASS_PROPS_PER_CLASS = VAL_CLASS_PROPS_PER_CLASS_SUM + VAL_CLASS_PROPS_PER_CLASS_AVG + VAL_CLASS_PROPS_PER_CLASS_COMPLEMENT_EXISTS = [ :num_false_positives, :num_false_negatives, + :num_true_positives, :num_true_negatives, :false_negative_rate, :false_positive_rate, + :true_negative_rate, :true_positive_rate ] #:precision, :recall, VAL_CLASS_PROPS = VAL_CLASS_PROPS_SINGLE + VAL_CLASS_PROPS_PER_CLASS VAL_CLASS_PROPS_EXTENDED = VAL_CLASS_PROPS + [:accuracy] diff --git a/lib/wrapper.rb b/lib/wrapper.rb index 7c811fb..d620651 100644 --- a/lib/wrapper.rb +++ b/lib/wrapper.rb @@ -1,8 +1,79 @@ +class String + + def is_uri? + begin + URI::parse(self) + rescue URI::InvalidURIError + false + end + end + +end + module OpenTox + class AmbitTask + include Owl + + +# def wait_for_completion +# until self.completed? or self.failed? +# sleep 1 +# end +# end + + def running? + me = @model.subject(RDF['type'],OT["Task"]) + status = @model.object(me, OT['hasStatus']).literal.value.to_s + puts status + status=="Running" + end + + def reload + data = "" + IO.popen("curl -i -X GET "+uri.to_s+" 2> /dev/null") do |f| + while line = f.gets + data += line + end + end + #data = OpenTox::RestClientWrapper.get uri + + puts "reload "+data.to_s + self.rdf = data + end + + def wait_while_running + while running? + sleep 1 + reload + end + end + + def self.from_uri(uri) + + begin + t = AmbitTask.new #(uri) + t.uri = uri + data = OpenTox::RestClientWrapper.get uri + puts "loaded ambit task "+data.to_s + t.rdf = data + #t.running? + t + rescue => ex + raise ex + #=> ex + #puts "error "+ex.message.to_s + nil + end + + end + + end + class Task + def self.as_task task = OpenTox::Task.create @@ -12,6 +83,7 @@ module OpenTox begin result = yield rescue => ex + raise ex LOGGER.error ex.message task.failed break @@ -38,21 +110,25 @@ module OpenTox module Feature def self.range( feature_uri ) - #TODO - ["true", "false"] + if feature_uri =~ /ambit/ + return nil + else + return ["true", "false"] + end end end module Model class PredictionModel + include Owl - attr_reader :uri + #attr_reader :uri def self.build( algorithm_uri, algorithm_parms ) model_task_uri = OpenTox::RestClientWrapper.post algorithm_uri,algorithm_parms - model_uri = OpenTox::Task.find(model_task_uri).wait_for_resource + model_uri = OpenTox::Task.find(model_task_uri).wait_for_resource.to_s if model_uri - return PredictionModel.new(model_uri) + return PredictionModel.find(model_uri) else return nil end @@ -60,9 +136,21 @@ module OpenTox def self.find( uri ) begin - RestClient.get uri,:accept => "application/rdf+xml" - PredictionModel.new(uri) - rescue #=> ex + #RestClient.get(uri,:accept => "application/rdf+xml") + #PredictionModel.new(uri) + + model = PredictionModel.new #(uri) + model.uri= uri + data = RestClient.get(uri,:accept => "application/rdf+xml") + #LOGGER.debug "creating model from data: "+data.to_s + model.rdf = data + raise "uri not set: '"+model.uri+"'" unless model.uri.to_s.size>5 + model + + rescue => ex + LOGGER.error "could not get model with uri: '"+uri+"', error-msg: "+ex.message.to_s + raise ex + #=> ex #puts "error "+ex.message.to_s nil end @@ -73,22 +161,98 @@ module OpenTox #prediction_task_uri = RestClientWrapper.post @uri,{:dataset_uri => dataset_uri} #puts prediction_task_uri #return OpenTox::Task.find(prediction_task_uri).wait_for_resource - return RestClientWrapper.post @uri,{:dataset_uri => dataset_uri} + + #res = RestClientWrapper.post @uri,{:dataset_uri => dataset_uri} + + res = "" + IO.popen("curl -X POST -d dataset_uri='"+dataset_uri+"' "+@uri.to_s+" 2> /dev/null") do |f| + while line = f.gets + res += line + end + end + puts "done "+res.to_s + + raise "neither prediction-dataset or task-uri: "+res.to_s unless res.to_s.is_uri? + + #HACK + if res.to_s =~ /dataset.*\/[0-9]+$/ # lazar + return res + elsif res.to_s =~ /\/task\// #pantelis + ambitTask = OpenTox::AmbitTask.from_uri(res.to_s) + ambitTask.wait_while_running + raise "done" + else + raise "not sure about prediction result: "+res.to_s + end end def classification? #TODO return true end - + + def predictedVariables + +# puts OT[self.owl_class] +# puts @model.subject(RDF['type'],OT[self.owl_class]) + + +# me = @model.subject(RDF['type'],OT[self.owl_class]) +# puts "title "+@model.object(me, DC['title']).to_s +# puts "pred "+@model.object(me, DC['predictedVariables']).to_s +# puts "rights "+@model.object(me, DC['rights']).to_s +# +# puts "title "+@model.object(me, OT['title']).to_s +# puts "pred "+@model.object(me, OT['predictedVariables']).to_s +# puts "rights "+@model.object(me, OT['rights']).to_s +# +# +# puts "1 "+@model.subjects(RDF['type'], OT['Feature']).each{|s| s.inspect.to_s}.join("\n") +# puts "f "+@model.subjects(RDF['type'], OT['predictedVariables']).each{|s| s.inspect.to_s}.join("\n") +# puts @model.object(me, OT['Feature']).to_s +# +# puts @model.subjects(RDF['type'],OT[self.owl_class]) +# puts identifier +# puts title +# puts @model.to_s +# puts @uri +# puts @model.get_resource(@uri) +# puts "XXX" +# +# @model.subjects(RDF['type'], OT['Feature']).each do |s| +# puts "s "+s.to_s +# puts "o "+@model.object(s, RDF['type']).to_s +# @model.subjects(OT['independentVariables'],s).each do |s2| +# puts "s2a "+s2.to_s +# end +# @model.subjects(OT['dependentVariables'],s).each do |s2| +# puts "s2b "+s2.to_s +# end +# @model.subjects(OT['predictedVariables'],s).each do |s2| +# puts "s2c "+s2.to_s +# end +# end + + me = @model.subject(RDF['type'],OT[self.owl_class]) + return @model.object(me, OT['predictedVariables']).uri.to_s + + #LOGGER.debug "getting lazar model" + #m = OpenTox::Model::Lazar.find(@uri) + #LOGGER.debug "getting lazar model DONE" + #LOGGER.debug "getting predict values" + #p = m.predictedVariables + #LOGGER.debug "getting predict values DONE" + #return p + end + def destroy RestClientWrapper.delete @uri end - protected - def initialize(uri) - @uri = uri - end + #protected + #def initialize(uri) + # @uri = uri + #end end end diff --git a/report/environment.rb b/report/environment.rb index 6d31615..71c1b1c 100644 --- a/report/environment.rb +++ b/report/environment.rb @@ -15,8 +15,8 @@ module Reports end #unless(defined? LOGGER) - #LOGGER = Logger.new(STDOUT) - #LOGGER.datetime_format = "%Y-%m-%d %H:%M:%S " + LOGGER = Logger.new(STDOUT) + LOGGER.datetime_format = "%Y-%m-%d %H:%M:%S " #end require "report/plot_factory.rb" diff --git a/report/plot_factory.rb b/report/plot_factory.rb index afe98de..4226b02 100644 --- a/report/plot_factory.rb +++ b/report/plot_factory.rb @@ -84,12 +84,17 @@ module Reports def self.create_ranking_plot( svg_out_file, validation_set, compare_attribute, equal_attribute, rank_attribute, class_value=nil ) #compute ranks + #puts "rank attibute is "+rank_attribute.to_s + rank_set = validation_set.compute_ranking([equal_attribute],rank_attribute,class_value) - #puts rank_set.to_array([:algorithm_uri, :dataset_uri, :acc, :acc_ranking]).collect{|a| a.inspect}.join("\n") - + #puts compare_attribute + #puts rank_set.to_array([:algorithm_uri, :dataset_uri, :percent_correct, :percent_correct_ranking]).collect{|a| a.inspect}.join("\n") + #puts "\n" + #compute avg ranks merge_set = rank_set.merge([compare_attribute]) - #puts merge_set.to_array([:algorithm_uri, :dataset_uri, :acc, :acc_ranking]).collect{|a| a.inspect}.join("\n") + #puts merge_set.to_array([:algorithm_uri, :dataset_uri, :percent_correct, :percent_correct_ranking]).collect{|a| a.inspect}.join("\n") + comparables = merge_set.get_values(compare_attribute) ranks = merge_set.get_values((rank_attribute.to_s+"_ranking").to_sym,false) @@ -97,7 +102,7 @@ module Reports plot_ranking( rank_attribute.to_s+" ranking", comparables, ranks, - 0.1, + nil, #0.1, validation_set.num_different_values(equal_attribute), svg_out_file) end @@ -121,6 +126,8 @@ module Reports res += line end end + raise "rank plot failed" unless $?==0 + if svg_out_file f = File.new(svg_out_file, "w") f.puts res @@ -161,7 +168,7 @@ module Reports faint << false return { :names => names, :fp_rate => fp_rate, :tp_rate => tp_rate, :faint => faint } else - roc_values = validation_set.first.get_predictions.get_roc_values(class_value) + roc_values = validation_set.validations[0].get_predictions.get_roc_values(class_value) tp_fp_rates = get_tp_fp_rates(roc_values) return { :names => ["default"], :fp_rate => [tp_fp_rates[:fp_rate]], :tp_rate => [tp_fp_rates[:tp_rate]] } end @@ -183,7 +190,7 @@ module Reports end end end - #puts c.inspect+"\n"+a.inspect+"\n"+p.inspect + #puts c.inspect+"\n"+a.inspect+"\n"+p.inspect+"\n\n" tp_rate = [0] fp_rate = [0] @@ -196,13 +203,13 @@ module Reports tp_rate << tp_rate[-1] end end - #puts tp_rate.inspect+"\n"+fp_rate.inspect + #puts tp_rate.inspect+"\n"+fp_rate.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 fp_rate[i] = fp_rate[-1]>0 ? fp_rate[i]/fp_rate[-1].to_f*100 : 100 end - #puts tp_rate.inspect+"\n"+fp_rate.inspect + #puts tp_rate.inspect+"\n"+fp_rate.inspect+"\n\n" return {:tp_rate => tp_rate,:fp_rate => fp_rate} end diff --git a/report/prediction_util.rb b/report/prediction_util.rb index 7a873eb..051022f 100644 --- a/report/prediction_util.rb +++ b/report/prediction_util.rb @@ -14,16 +14,17 @@ module Reports::PredictionUtil 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) } - #a.push(v.get_predictions.compound(i)[0,65]) #.gsub(/[-(),=]/, '')[0,10]) - a.push(OpenTox::Compound.new(:uri=>v.get_predictions.compound(i)).smiles[0,65]) #.gsub(/[-(),=]/, '')[0,10]) + + a.push(v.get_predictions.identifier(i)[0,65]) #.gsub(/[-(),=]/, '')[0,10]) + #a.push(OpenTox::Compound.new(:uri=>v.get_predictions.compound(i)).smiles[0,65]) #.gsub(/[-(),=]/, '')[0,10]) + 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 v.get_predictions.classification? + 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? res.push(a) end @@ -31,8 +32,8 @@ module Reports::PredictionUtil #res = res.sort{|x,y| y[3] <=> x[3] } header = [ "compound", "actual value", "predicted value"] - header.push "missclassified" if validation_set.first.get_predictions.classification? - header.push "confidence value" if validation_set.first.get_predictions.confidence_values_available? + header.push "missclassified" if validation_set.all_classification? + header.push "confidence value" if validation_set.validations[0].get_predictions.confidence_values_available? res.insert(0, validation_attributes + header) #puts res.collect{|c| c.inspect}.join("\n") diff --git a/report/report_factory.rb b/report/report_factory.rb index fbcd2eb..52c0642 100644 --- a/report/report_factory.rb +++ b/report/report_factory.rb @@ -4,8 +4,8 @@ VAL_ATTR_TRAIN_TEST = [ :model_uri, :training_dataset_uri, :test_dataset_uri, :p # 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 = [ :area_under_roc, :percent_correct, :true_positive_rate, :true_negative_rate ] -VAL_ATTR_BAR_PLOT_CLASS = [ :area_under_roc, :accuracy, :true_positive_rate, :true_negative_rate ] +VAL_ATTR_CLASS = [ :percent_correct, :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_REGR = [ :root_mean_squared_error, :mean_absolute_error, :r_square ] @@ -56,7 +56,7 @@ module Reports::ReportFactory #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(validation_set.first) + report.add_section_confusion_matrix(val) else #regression report.add_section_result(validation_set, VAL_ATTR_TRAIN_TEST + VAL_ATTR_REGR, "Results", "Results") end @@ -69,28 +69,29 @@ module Reports::ReportFactory def self.create_report_crossvalidation(validation_set) raise Reports::BadRequest.new("num validations is not >1") unless validation_set.size>1 - raise Reports::BadRequest.new("crossvalidation-id not set in all validations") if validation_set.has_nil_values?(:crossvalidation_id) - raise Reports::BadRequest.new("num different cross-validation-id's must be equal to 1") unless validation_set.num_different_values(:crossvalidation_id)==1 + raise Reports::BadRequest.new("crossvalidation-id not unique and != nil") 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 ("+validation_set.first.num_folds.to_s+")") unless validation_set.first.num_folds==validation_set.size + raise Reports::BadRequest.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? merged = validation_set.merge([:crossvalidation_id]) + raise unless merged.size==1 #puts merged.get_values(:percent_correct_variance, false).inspect report = Reports::ReportContent.new("Crossvalidation report") - if (validation_set.first.classification?) + 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", nil, nil, "Roc plot") report.add_section_roc_plot(validation_set, nil, :crossvalidation_fold, "roc-plot-folds.svg", nil, nil, "Roc plots for folds") - #validation_set.validations[0].get_prediction_feature_values.each do |class_value| + #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.first) + 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") @@ -121,10 +122,10 @@ module Reports::ReportFactory #merged = validation_set.merge([:algorithm_uri, :dataset_uri]) report = Reports::ReportContent.new("Algorithm comparison report - Many datasets") - if (validation_set.first.classification?) + 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, - [:accuracy, :true_positive_rate, :true_negative_rate], "true") + [:percent_correct, :true_positive_rate, :true_negative_rate], "true") else # regression raise Reports::BadRequest.new("not implemented yet for regression") end @@ -137,10 +138,10 @@ module Reports::ReportFactory report = Reports::ReportContent.new("Algorithm comparison report") - if (validation_set.first.classification?) + 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.validations[0].get_prediction_feature_values.each do |class_value| + #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 @@ -169,7 +170,7 @@ module Reports::ReportFactory merged = validation_set.merge([:algorithm_uri, :dataset_uri]) report = Reports::ReportContent.new("Algorithm comparison report - Many datasets") - if (validation_set.first.classification?) + 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 @@ -186,12 +187,21 @@ module Reports::ReportFactory report = Reports::ReportContent.new("Algorithm comparison report") - if (validation_set.first.classification?) - validation_set.validations[0].get_prediction_feature_values.each do |class_value| - report.add_section_bar_plot(merged,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 + 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") @@ -224,7 +234,7 @@ class Reports::ReportContent table_title="Predictions") section_table = @xml_report.add_section(@xml_report.get_root_element, section_title) - if validation_set.first.get_predictions + 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 @@ -240,7 +250,7 @@ class Reports::ReportContent 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) + vals = validation_set.to_array(validation_attributes,false,validation_set.get_true_prediction_feature_value) #PENDING rexml strings in tables not working when >66 vals = vals.collect{|a| a.collect{|v| v.to_s[0,66] }} #PENDING transpose values if there more than 4 columns, and there are more than columns than rows @@ -318,9 +328,10 @@ class Reports::ReportContent rank_attribute, class_value=nil, plot_file_name="ranking.svg", - image_title="Ranking Plot", + image_title=nil, image_caption=nil) - + + 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) diff --git a/report/report_service.rb b/report/report_service.rb index bcb2e1d..6517a94 100644 --- a/report/report_service.rb +++ b/report/report_service.rb @@ -106,7 +106,7 @@ module Reports # no api-access for this method def delete_all_reports( type ) - LOGGER.info "deleteing all reports of ype '"+type.to_s+"'" + 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) } end diff --git a/report/report_test.rb b/report/report_test.rb index 0782d56..126a978 100644 --- a/report/report_test.rb +++ b/report/report_test.rb @@ -24,11 +24,15 @@ class Reports::ApplicationTest < Test::Unit::TestCase #get '/report/validation/1',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/crossvalidation',:validation_uris=>"http://ot.validation.de/crossvalidation/1" + #post 'http://ot.validation.de/report/crossvalidation',:validation_uris=>"http://ot.validation.de/crossvalidation/1" + #uri = last_response.body.to_s + + post 'http://ot.validation.de/report/validation',:validation_uris=>"http://ot.validation.de/validation/1" uri = last_response.body.to_s + puts uri - post uri.to_s+'/format_html',:css_style_sheet=>"http://apps.ideaconsult.net:8180/ToxPredict/style/global.css" - puts last_response.body.to_s.gsub(/\n.*/,"") + #post uri.to_s+'/format_html',:css_style_sheet=>"http://apps.ideaconsult.net:8180/ToxPredict/style/global.css" + #puts last_response.body.to_s.gsub(/\n.*/,"") end # diff --git a/report/validation_access.rb b/report/validation_access.rb index e06c1f0..855bdf1 100644 --- a/report/validation_access.rb +++ b/report/validation_access.rb @@ -44,10 +44,18 @@ class Reports::ValidationAccess 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) res = [] validation_uris.each do |u| @@ -73,7 +81,7 @@ class Reports::ValidationDB < Reports::ValidationAccess validation.send("#{p.to_s}=".to_sym, v[p]) end - {:classification_statistics => Lib::VAL_CLASS_PROPS_EXTENDED, + {:classification_statistics => Lib::VAL_CLASS_PROPS, :regression_statistics => Lib::VAL_REGR_PROPS}.each do |subset_name,subset_props| subset = v[subset_name] subset_props.each{ |prop| validation.send("#{prop.to_s}=".to_sym, subset[prop]) } if subset @@ -91,8 +99,8 @@ class Reports::ValidationDB < Reports::ValidationAccess end def get_predictions(validation) - Lib::OTPredictions.new( validation.classification?, validation.prediction_feature, - validation.test_dataset_uri, validation.prediction_dataset_uri) + Lib::OTPredictions.new( validation.classification?, validation.test_dataset_uri, + validation.prediction_feature, validation.prediction_dataset_uri, validation.predicted_variable) end def get_prediction_feature_values( validation ) @@ -100,11 +108,26 @@ class Reports::ValidationDB < Reports::ValidationAccess end def classification?( validation ) - model = OpenTox::Model::PredictionModel.find(validation.model_uri) - raise "model not found '"+validation.model_uri+"'" unless validation.model_uri && model - model.classification? + get_model(validation).classification? end + def predicted_variable(validation) + 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 # diff --git a/report/validation_data.rb b/report/validation_data.rb index 7943cd9..3f96cc8 100644 --- a/report/validation_data.rb +++ b/report/validation_data.rb @@ -3,6 +3,8 @@ 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 ] + + class Object def to_nice_s @@ -25,19 +27,6 @@ class Object end end -class Hash - - def mean_value - sum = 0 - self.values.collect do |v| - raise "cannot compute mean of non-numeric values '"+self.inspect+"'" unless v!=nil and v.is_a?(Numeric) - sum+=v - end - sum/=self.values.size.to_f - end - -end - module Reports @@ -53,7 +42,6 @@ module Reports def self.reset_validation_access(validation_access) @@validation_access = validation_access end - def self.resolve_cv_uris(validation_uris) @@validation_access.resolve_cv_uris(validation_uris) @@ -99,6 +87,11 @@ module Reports @is_classification = @@validation_access.classification?(self) end + def predicted_variable + return @predicted_variable if @predicted_variable!=nil + @predicted_variable = @@validation_access.predicted_variable(self) + end + # loads all crossvalidation attributes, of the corresponding cv into this object def load_cv_attributes raise "crossvalidation-id not set" unless @crossvalidation_id @@ -119,6 +112,7 @@ module Reports class ValidationSet def initialize(validation_uris = nil) + @unique_values = {} validation_uris = Reports::Validation.resolve_cv_uris(validation_uris) if validation_uris @validations = Array.new validation_uris.each{|u| @validations.push(Reports::Validation.new(u))} if validation_uris @@ -128,9 +122,9 @@ module Reports return @validations[index] end - def first() - return @validations.first - end + #def first() + #return @validations.first + #end # returns the values of the validations for __attribute__ # * if unique is true a set is returned, i.e. not redundant info @@ -170,18 +164,46 @@ module Reports @validations.each{ |v| v.load_cv_attributes } end + def unique_value(validation_prop) + return @unique_values[validation_prop] if @unique_values.has_key?(validation_prop) + val = @validations[0].send(validation_prop) + (1..@validations.size-1).each do |i| + if @validations[i].send(validation_prop)!=val + val = nil + break + end + end + @unique_values[validation_prop] = val + 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 + end + + def get_prediction_feature_values + return unique_value("get_prediction_feature_values") + end + # checks weather all validations are classification validations # def all_classification? - @validations.each{ |v| return false unless v.classification? } - true + return unique_value("classification?") end # checks weather all validations are regression validations # def all_regression? - @validations.each{ |v| return false if v.classification? } - true + # 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 @@ -214,18 +236,22 @@ module Reports # call-seq: # to_array(attributes, remove_nil_attributes) => array # - def to_array(attributes, remove_nil_attributes=true) + def to_array(attributes, remove_nil_attributes=true, true_class_value=nil) array = Array.new array.push(attributes) attribute_not_nil = Array.new(attributes.size) @validations.each do |v| index = 0 array.push(attributes.collect do |a| - variance = v.send( (a.to_s+"_variance").to_sym ) if VAL_ATTR_VARIANCE.index(a) + 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 - v.send(a).to_nice_s + variance.to_s + 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 end) end if remove_nil_attributes #delete in reverse order to avoid shifting of indices @@ -245,6 +271,10 @@ module Reports def merge(equal_attributes) new_set = Reports::ValidationSet.new + # unique values stay unique when merging + # derive unique values before, because model dependent props cannot be accessed later (when mergin validations from different models) + new_set.unique_values = @unique_values + #compute grouping grouping = Reports::Util.group(@validations, equal_attributes) @@ -290,7 +320,7 @@ 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 - val = val.mean_value + raise "is a hash "+ranking_attribute+", specify class value plz" end end rank_hash[i] = val @@ -342,6 +372,10 @@ module Reports @validations end + protected + def unique_values=(unique_values) + @unique_values = unique_values + end end end diff --git a/report/xml_report.rb b/report/xml_report.rb index 4b62457..6f8a817 100644 --- a/report/xml_report.rb +++ b/report/xml_report.rb @@ -83,7 +83,7 @@ class Reports::XMLReport figure << Reports::XMLReportUtil.text_element("title", title) media = Element.new("mediaobject") image = Element.new("imageobject") - imagedata = Reports::XMLReportUtil.attribute_element("imagedata",{"contentwidth" => "75%", "fileref" => path, "format"=>filetype}) + imagedata = Reports::XMLReportUtil.attribute_element("imagedata",{"contentwidth" => "100%", "fileref" => path, "format"=>filetype}) #imagedata = Reports::XMLReportUtil.attribute_element("imagedata",{"width" => "6in", "fileref" => path, "format"=>filetype}) @resource_path_elements[imagedata] = "fileref" image << imagedata diff --git a/validation/validation_application.rb b/validation/validation_application.rb index eb9574d..3a9aa61 100644 --- a/validation/validation_application.rb +++ b/validation/validation_application.rb @@ -12,8 +12,8 @@ require 'lib/merge.rb' before {$sinatra = self unless $sinatra} #unless(defined? LOGGER) - #LOGGER = Logger.new(STDOUT) - #LOGGER.datetime_format = "%Y-%m-%d %H:%M:%S " + LOGGER = Logger.new(STDOUT) + LOGGER.datetime_format = "%Y-%m-%d %H:%M:%S " #end diff --git a/validation/validation_test.rb b/validation/validation_test.rb index 90bc0d3..619556c 100644 --- a/validation/validation_test.rb +++ b/validation/validation_test.rb @@ -117,42 +117,66 @@ class ValidationTest < Test::Unit::TestCase # end # end # -# def test_validate_model -# begin -## data_uri_train = upload_data(WS_DATA, DATA_TRAIN, FILE_TRAIN) -## data_uri_test = upload_data(WS_DATA, DATA_TEST, FILE_TEST) -## #data_uri_train = WS_DATA+"/"+DATA_TRAIN -## #data_uri_test = WS_DATA+"/"+DATA_TEST -## -## if WS_FEATURE_ALG -## feature_uri = RestClient.post WS_FEATURE_ALG, :dataset_uri => data_uri_train -## model_uri = RestClient.post(WS_CLASS_ALG,{ :activity_dataset_uri => data_uri_train, :feature_dataset_uri => feature_uri }) -## else -## model_uri = RestClient.post(WS_CLASS_ALG,{ :dataset_uri => data_uri_train }) -## end -# -# #model_uri = "http://ot.model.de/12" -# #data_uri_test = "http://ot.dataset.de/67" -# -# model_uri = "http://ot.model.de/1" + def test_validate_model + begin +# data_uri_train = upload_data(WS_DATA, DATA_TRAIN, FILE_TRAIN) +# data_uri_test = upload_data(WS_DATA, DATA_TEST, FILE_TEST) +# #data_uri_train = WS_DATA+"/"+DATA_TRAIN +# #data_uri_test = WS_DATA+"/"+DATA_TEST +# +# if WS_FEATURE_ALG +# feature_uri = RestClient.post WS_FEATURE_ALG, :dataset_uri => data_uri_train +# model_uri = RestClient.post(WS_CLASS_ALG,{ :activity_dataset_uri => data_uri_train, :feature_dataset_uri => feature_uri }) +# else +# model_uri = RestClient.post(WS_CLASS_ALG,{ :dataset_uri => data_uri_train }) +# end + +# model_uri = "http://ot.model.de/1" # data_uri_test = "http://ot.dataset.de/3" -# -# post '', {:test_dataset_uri => data_uri_test, :model_uri => model_uri, :prediction_feature => FEATURE_URI} -# -# puts last_response.body -# #verify_validation -# -# task = OpenTox::Task.find(last_response.body) -# task.wait_for_completion -# val_uri = task.resource -# puts val_uri -# -# get val_uri -# verify_validation(last_response.body) + + #model_uri = "http://ot.model.de/7" + #data_uri_test = "http://ot.dataset.de/41" + + model_uri = "http://opentox.ntua.gr:3000/model/9" + data_uri_test = "http://ambit.uni-plovdiv.bg:8080/ambit2/dataset/342" + + post '', {:test_dataset_uri => data_uri_test, :model_uri => model_uri, :prediction_feature => FEATURE_URI} + + puts last_response.body + #verify_validation + + task = OpenTox::Task.find(last_response.body) + task.wait_for_completion + val_uri = task.resource + puts val_uri + + get val_uri + verify_validation(last_response.body) + + ensure + #delete_resources + end + end + +# def test_prediction_dataset +# +# classification = false +# test_dataset_uri = "http://ambit.uni-plovdiv.bg:8080/ambit2/dataset/342" +# prediction_dataset_uri = "http://ambit.uni-plovdiv.bg:8080/ambit2/dataset/407" +# actual_feature="http://ambit.uni-plovdiv.bg:8080/ambit2/feature/103141" +# predicted_feature = OpenTox::Model::PredictionModel.find("http://opentox.ntua.gr:3000/model/9").predictedVariables +# assert predicted_feature=="http://ambit.uni-plovdiv.bg:8080/ambit2/feature/227289","nope: "+predicted_feature.to_s +# #predicted_feature="http://ambit.uni-plovdiv.bg:8080/ambit2/feature/227289" # -# ensure -# #delete_resources -# end +## classification = true +## test_dataset_uri = "http://ot.dataset.de/79" +## prediction_dataset_uri = "http://ot.dataset.de/101" +## actual_feature="http://www.epa.gov/NCCT/dsstox/CentralFieldDef.html#ActivityOutcome_CPDBAS_Hamster" +## predicted_feature = OpenTox::Model::PredictionModel.find("http://ot.model.de/18").predictedVariables +## assert predicted_feature=="http://www.epa.gov/NCCT/dsstox/CentralFieldDef.html#ActivityOutcome_CPDBAS_Hamster_lazar_prediction" +## #predicted_feature="http://www.epa.gov/NCCT/dsstox/CentralFieldDef.html#ActivityOutcome_CPDBAS_Hamster_lazar_prediction" +# +# puts Lib::OTPredictions.new( classification, test_dataset_uri, actual_feature, prediction_dataset_uri, predicted_feature ).compute_stats.each{|key,value| puts key.to_s+" => "+value.to_s } # end # # def test_validate_algorithm @@ -178,6 +202,11 @@ class ValidationTest < Test::Unit::TestCase # def test_split # begin +# +# #model = OpenTox::Model::PredictionModel.find("http://ot.model.de/18") +# #puts model.predictedVariables +# #exit +# # data_uri = upload_data(WS_DATA, FILE) # #data_uri = "http://ot.dataset.de/199" #bbrc # #data_uri = "http://ot.dataset.de/67" #hamster @@ -204,6 +233,7 @@ class ValidationTest < Test::Unit::TestCase def verify_validation(val_yaml) + puts val_yaml val = YAML.load(val_yaml) puts val.inspect @@ -258,31 +288,28 @@ class ValidationTest < Test::Unit::TestCase assert string_val.to_f<=max if max!=nil end - def test_nothing - - #puts "testing nothing" - - #get '/' - - #get '/crossvalidation/loo' - #get '/training_test_split' - - get '/prepare_examples' - #get '/test_examples' - - #get '/1',nil,'HTTP_ACCEPT' => "application/rdf+xml" - #get '/1',nil,'HTTP_ACCEPT' => "text/x-yaml" - - - #get '/crossvalidation/1',nil,'HTTP_ACCEPT' => "application/rdf+xml" - #get '/crossvalidation/1/statistics',nil,'HTTP_ACCEPT' => "text/x-yaml" - - #puts last_response.body - - #get '/2' - #verify_validation(last_response.body) - - end +# def test_nothing +# +# #puts "testing nothing" +# +# #get '/' +# +# #get '/crossvalidation/loo' +# #get '/training_test_split' +# +# #get '/1',nil,'HTTP_ACCEPT' => "application/rdf+xml" +# #get '/1',nil,'HTTP_ACCEPT' => "text/x-yaml" +# +# +# #get '/crossvalidation/1',nil,'HTTP_ACCEPT' => "application/rdf+xml" +# #get '/crossvalidation/1/statistics',nil,'HTTP_ACCEPT' => "text/x-yaml" +# +# #puts last_response.body +# +# #get '/2' +# #verify_validation(last_response.body) +# +# end # private # def verify_validation (delete=true) @@ -310,5 +337,13 @@ class ValidationTest < Test::Unit::TestCase ## puts content ## end # end + +## def test_prepare_examples +## get '/prepare_examples' +## end +# +# def test_examples # USES CURL, DO NOT FORGET TO RESTART +# get '/test_examples' +# end end |