diff options
author | Christoph Helma <helma@in-silico.ch> | 2011-03-11 14:29:13 +0100 |
---|---|---|
committer | Christoph Helma <helma@in-silico.ch> | 2011-03-11 14:29:13 +0100 |
commit | 96d500e9691eedbfdd57ec35a2572b59641377f3 (patch) | |
tree | 211391f36e269bf36ca09d54aab0d93ababfa412 /reach_reports | |
parent | 5f243f1e9e0a8e12cd8a2267bcec3140d21bf445 (diff) | |
parent | 7cd9c9656010b88b7e41477e1571cb7e722650c4 (diff) |
Merge branch 'release/v1.0.1'v1.0.1
Diffstat (limited to 'reach_reports')
-rwxr-xr-x | reach_reports/reach_application.rb | 242 | ||||
-rwxr-xr-x | reach_reports/reach_persistance.rb | 1209 | ||||
-rwxr-xr-x | reach_reports/reach_service.rb | 319 | ||||
-rwxr-xr-x | reach_reports/reach_test.rb | 284 |
4 files changed, 2054 insertions, 0 deletions
diff --git a/reach_reports/reach_application.rb b/reach_reports/reach_application.rb new file mode 100755 index 0000000..cd0695c --- /dev/null +++ b/reach_reports/reach_application.rb @@ -0,0 +1,242 @@ + +[ 'rubygems', 'sinatra', 'sinatra/url_for', 'opentox-ruby' ].each do |lib| + require lib +end + +QMRF_EDITOR_URI = "http://ortona.informatik.uni-freiburg.de/qmrfedit2/OT_QMRFEditor.jnlp" + +require 'reach_reports/reach_persistance.rb' +require 'reach_reports/reach_service.rb' + +require "lib/format_util.rb" + +def extract_type(params) + halt 400, "illegal type, neither QMRF nor QPRF: "+params[:type] unless params[:type] && params[:type] =~ /(?i)Q(M|P)RF/ + params.delete("type") +end + +get '/reach_report' do + uri_list = url_for('/reach_report/QMRF', :full)+"\n"+url_for('/reach_report/QPRF', :full)+"\n" + if request.env['HTTP_ACCEPT'] =~ /text\/html/ + content_type "text/html" + related_links = + "All validations: "+url_for("/",:full)+"\n"+ + "Validation reporting: "+url_for("/report",:full) + description = + "A list of all suported REACH reporting types." + OpenTox.text_to_html uri_list,related_links,description, @subjectid + else + content_type "text/uri-list" + uri_list + end +end + +get '/reach_report/:type' do + type = extract_type(params) + LOGGER.info "list all "+type+" reports" + if request.env['HTTP_ACCEPT'] =~ /text\/html/ + content_type "text/html" + related_links = + "All REACH reporting types: "+url_for("/reach_report",:full) + description = + "A list of "+type+" reports." + post_params = "" + case type + when /(?i)QMRF/ + related_links += "\n"+ + "OpenTox version of QMRF editor: "+QMRF_EDITOR_URI + description += "\n"+ + "To create a QMRF report use the POST method." + post_params = [[[:model_uri]],[["Existing QMRF report, content-type application/qmrf-xml"]]] + when /(?i)QPRF/ + #TODO + end + OpenTox.text_to_html ReachReports.list_reports(type),@subjectid,related_links,description,post_params + else + content_type "text/uri-list" + ReachReports.list_reports(type) + end +end + +post '/reach_report/:type' do + + type = extract_type(params) + content_type "text/uri-list" + + LOGGER.info "creating "+type+" report "+params.inspect + #puts "creating "+type+" report "+params.inspect + result_uri = ReachReports.create_report(type,params,@subjectid,request.env["rack.input"]) + + if result_uri and result_uri.task_uri? + halt 202,result_uri+"\n" + else + result_uri+"\n" + end +end + +get '/reach_report/:type/:id' do + + type = extract_type(params) + LOGGER.info "get "+type+" report with id '"+params[:id].to_s+"' "+request.env['HTTP_ACCEPT'].to_s+"'" + rep = ReachReports.get_report(type, params[:id]) + + case request.env['HTTP_ACCEPT'].to_s + when "application/rdf+xml" + halt 400, "application/rdf+xml not yet supported" + owl = OpenTox::Owl.create(type+"Report",rep.report_uri) + owl.set_data( rep.get_content.keys_to_rdf_format ) + owl.rdf + when "application/qmrf-xml" + content_type "application/qmrf-xml" + rep.to_xml + #f = File.new("/home/martin/info_home/.public_html/qmrf.out.xml","w") + #f.puts result + when /text\/html/ + content_type "text/html" + related_links = + "Open report in QMRF editor: "+rep.report_uri+"/editor"+"\n"+ + "All "+type+" reports: "+url_for("/reach_report/"+type,:full) + description = + "A QMRF report." + OpenTox.text_to_html rep.to_yaml,@subjectid,related_links,description + when /application\/x-yaml|\*\/\*|^$/ # matches 'application/x-yaml', '*/*', '' + content_type "application/x-yaml" + rep.to_yaml + else + halt 400, "MIME type '"+request.env['HTTP_ACCEPT'].to_s+"' not supported, valid Accept-Headers are \"application/rdf+xml\", \"application/x-yaml\", \"application/qmrf-xml\"." + end +end + +post '/reach_report/:type/:id' do + + type = extract_type(params) + LOGGER.info "Post to "+type+" report with id "+params[:id].to_s+"' "+request.env['HTTP_ACCEPT'].to_s+"'" + rep = ReachReports.get_report(type, params[:id]) + + input = request.env["rack.input"].read + halt 400, "no xml data specified" unless input && input.to_s.size>0 + LOGGER.debug "size of posted data: "+input.to_s.size.to_s + + ReachReports::QmrfReport.from_xml(rep,input) + + #f = File.new("/home/martin/info_home/.public_html/qmrf.out.xml","w") + #f.puts rep.to_xml +end + +delete '/reach_report/:type/:id' do + type = extract_type(params) + LOGGER.info "delete "+type+" report with id '"+params[:id].to_s+"'" + ReachReports.delete_report(type, params[:id], @subjectid) +end + + +#get '/reach_report/:type/:id/:section' do +# +# type = extract_type(params) +# LOGGER.info "get "+type+" report section '"+params[:section].to_s+"', with id "+params[:id].to_s+"' "+request.env['HTTP_ACCEPT'].to_s+"'" +# ReachReports.get_report(type, params[:id], params[:section]).to_yaml +#end +# +#get '/reach_report/:type/:id/:section/:subsection' do +# +# type = extract_type(params) +# LOGGER.info "get "+type+" report subsection '"+params[:subsection].to_s+"', section '"+params[:section].to_s+"', with id "+params[:id].to_s+"' "+request.env['HTTP_ACCEPT'].to_s+"'" +# ReachReports.get_report(type, params[:id], params[:section], params[:subsection]).to_yaml +#end + +get '/reach_report/:type/:id/editor' do + + type = extract_type(params) + LOGGER.info "editor for "+type+" report with id '"+params[:id].to_s+"' "+params.inspect + + jnlp = <<EOF +<?xml version ="1.0" encoding="utf-8"?> +<jnlp spec="1.0+" codebase="http://opentox.informatik.uni-freiburg.de/" href="qmrfedit2/OT_QMRFEditor.jnlp" > +<information> +<title>QMRF Editor</title> +<vendor>www.opentox.org</vendor> +<description>(Q)SAR Model Reporting Format Editor</description> +<description kind="short">(Q)SAR Model Reporting Format Editor</description> +<icon href="qmrfedit2/OTLogo.png" /> +</information> +<resources> +<j2se version="1.6+" java-vm-args="-Xincgc"/> + +<jar href="qmrfedit2/OT_QMRFEditor.jar" download="eager" main="true"/> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-applications.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-builder3d.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-charges.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-core.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-datadebug.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-data.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-experimental.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-extra.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-forcefield.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-interfaces.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-io.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-jchempaint.applet.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-jchempaint.application.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-jchempaint.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-libio-cml.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-libio-weka.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-nonotify.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-pdb-cml.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-pdb.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-qsar-cml.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-qsar.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/cdk-qsar-pdb.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/commons-cli-1.0.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/commons-io-1.1.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/commons-logging-1.0.4.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/commons-codec-1.3.jar" download="eager" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/fop.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/jai_codec.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/jai_core.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/jgrapht-0.6.0.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/jh.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/l2fprod-common-all.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/libfonts-0.1.4.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/log4j-1.2.8.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/log4j.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/mysql-connector-java-5.0.5-bin.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/naming-factory-dbcp.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/naming-factory.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/naming-resources.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/opsin-big-0.1.0.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/org.restlet.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/swing-layout-1.0.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/xmlgraphics-commons-1.1.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/xom-1.1b2.jar" download="lazy" /> +<jar href="qmrfedit2/OT_QMRFEditor_lib/xom-1.1.jar" download="lazy" /> + + +</resources> +<application-desc main-class="ambit.applications.qmrf.QMRFEditor"> +<argument>-x +EOF + jnlp.chomp! + jnlp += File.join(url_for("/reach_report/QMRF",:full),params[:id]) + + jnlp += <<EOF +</argument> +<argument>--subjectid= +EOF + jnlp.chomp! + jnlp += @subjectid.to_s + + jnlp += <<EOF +</argument> +<argument>-d http://opentox.informatik.uni-freiburg.de/qmrfedit2/qmrf.dtd</argument> +<argument>-t http://opentox.informatik.uni-freiburg.de/qmrfedit2/verdana.ttf</argument> + +</application-desc> +<security> + <all-permissions/> +</security> +</jnlp> +EOF + + content_type "application/x-java-jnlp-file" + jnlp +end + diff --git a/reach_reports/reach_persistance.rb b/reach_reports/reach_persistance.rb new file mode 100755 index 0000000..3118809 --- /dev/null +++ b/reach_reports/reach_persistance.rb @@ -0,0 +1,1209 @@ + +require "dm-validations" + +DataMapper::Model.raise_on_save_failure = true + +module REXML + + class Element + def self.find_or_create(parent_node, name) + if parent_node.elements[name] + parent_node.elements[name] + else + node = Element.new(name) + parent_node << node + node + end + end + end + + class TextElement < Element + def initialize(name, text) + super(name) + self.text = text + end + + def self.find_or_create(parent_node, name, text) + elem = Element.find_or_create(parent_node, name) + elem.text = text + elem + end + end +end + + +class Symbol + + XML_ALIAS = { + :qsar_identifier => "QSAR_identifier", :qsar_title => "QSAR_title", :qsar_software => "QSAR_software", + :qsar_models => "QSAR_models", :qsar_general_information => "QSAR_General_information", + :qsar_endpoint => "QSAR_Endpoint", :qmrf_author => "qmrf_authors", :qsar_algorithm => "QSAR_Algorithm", + :qsar_applicability_domain => "QSAR_Applicability_domain", :qsar_robustness => "QSAR_Robustness", + :qsar_predictivity => "QSAR_Predictivity", :qsar_interpretation => "QSAR_Interpretation", + :qsar_miscellaneous => "QSAR_Miscelaneous", :qmrf_summary => "QMRF_Summary", :qmrf_number => "QMRF_number" } + + def xml_alias + XML_ALIAS[self] ? XML_ALIAS[self] : self.to_s + end +end + +class DataMapper::Associations::OneToMany::Relationship + def get_child_model + @child_model + end +end + +module DataMapper::Resource + + def association_class( property ) + relationship = relationships[property] + raise "no relationship found for "+property.to_s+" "+relationships.inspect unless relationship + raise "not a many-to-one or one-to-one association for "+property.to_s+" "+relationship.inspect unless + relationship.is_a?(DataMapper::Associations::OneToMany::Relationship) or + relationship.is_a?(DataMapper::Associations::OneToOne::Relationship) + relationship.get_child_model + end + + def from_xml(node) + + raise "node is nil ("+self.class.to_s+".from_xml)" unless node + #puts "FROM xml: "+self.class.to_s #+", NODE: "+node.to_s + + dbg_check = { :attributes => Set.new(), :nodes => Set.new(), :subnodes => Set.new() } + + xml_infos.each do |xml_info| + + if xml_info.is_a?(ReachReports::TextNodeProperty) + text_node = node.elements[ xml_info.xml_prop ] + raise "node not found: "+ xml_info.xml_prop+" ("+self.class.to_s+".from_xml)" unless text_node + self.send( xml_info.prop.to_s+"=", text_node.text ) + dbg_check[:nodes] << text_node.name + + elsif xml_info.is_a?(ReachReports::CatalogReference) + root_node = node.elements[ xml_info.xml_prop ] + raise "node not found: "+ xml_info.xml_prop+" ("+self.class.to_s+".from_xml)" unless root_node + dbg_check[:nodes] << root_node.name + + self.send(xml_info.prop).each{ |s| s.destroy } if self.send(xml_info.prop) + self.send(xml_info.prop).clear() #otherwise the content is tmp still here + + catalog_node = $catalogs_node.elements[xml_info.catalog_name] + + root_node.each_element(xml_info.catalog_element+"_ref") do |n| + ref = nil + catalog_node.each_element_with_attribute("id", n.attribute("idref").to_s) do |e| + ref = e + break + end + raise "referenced node not found for "+xml_info.xml_prop+" in catalog "+xml_info.catalog_name+" ref-node was "+n.to_s unless ref + dbg_check[:subnodes] << n + + entry = self.association_class(xml_info.prop).new + entry.from_xml( ref ) + self.send(xml_info.prop) << entry + end + + elsif xml_info.is_a?(ReachReports::AttributeProperty) + #puts "attr "+xml_info.prop.to_s + self.send(xml_info.prop.to_s+"=", node.attribute(xml_info.xml_prop)) + dbg_check[:attributes] << xml_info.prop + + elsif xml_info.is_a?(ReachReports::TextSubnodeProperty) + parent_node = node.elements[ xml_info.parent_prop.xml_alias ] + raise "parent node not found: '"+ xml_info.parent_prop.xml_alias+"' ("+self.class.to_s+".from_xml)" unless parent_node + text_node = parent_node.elements[ xml_info.xml_prop ] + raise "node not found: "+ xml_info.xml_prop+" ("+self.class.to_s+".from_xml)" unless text_node + self.send( xml_info.prop.to_s+"=", text_node.text ) + dbg_check[:nodes] << parent_node.name + dbg_check[:subnodes] << text_node + + elsif xml_info.is_a?(ReachReports::SingleAttributeNodeProperty) + attr_node = node.elements[ xml_info.xml_prop ] + self.send(xml_info.prop.to_s+"=", attr_node.attribute(xml_info.attribute)) + dbg_check[:nodes] << attr_node.name + + elsif xml_info.is_a?(ReachReports::AttributeNodeProperty) + attr_node = node.elements[ xml_info.xml_prop ] + entry = self.association_class( xml_info.prop ).new + entry.from_xml(attr_node) + self.send(xml_info.prop.to_s+"=",entry) + dbg_check[:nodes] << attr_node.name + + elsif xml_info.is_a?(ReachReports::AttributeSubNodeListProperty) + + parent_node = node.elements[ xml_info.parent_prop.xml_alias ] + raise "parent node not found: '"+ xml_info.parent_prop.xml_alias+"' ("+self.class.to_s+".from_xml)" unless parent_node + #puts "parent node "+xml_info.parent_prop.xml_alias + prop_node = parent_node.elements[ xml_info.xml_prop ] + if prop_node + #puts "prop node "+xml_info.xml_prop.to_s + prop_node.each_element do |n| + #puts "elem node "+n.name.to_s + raise "illegal node '"+n.name.to_s+"' should be '"+xml_info.list_element.to_s+"'" unless n.name==xml_info.list_element.to_s + entry = self.association_class( xml_info.prop ).new + entry.from_xml( n ) + self.send(xml_info.prop) << entry + end + dbg_check[:subnodes] << prop_node + end + dbg_check[:nodes] << parent_node.name + + else + raise "type not supported yet: "+xml_info.inspect + end + end + + ##raise "not a qsar_identifier" unless qsar_identifier.is_a?(QsarIdentifier) + + #puts node.elements.inspect + #puts "there we go: "+qsar_identifier.qsar_software.to_s + #qsar_identifier.qsar_software = QsarSoftware.new + #puts "there we go: "+qsar_identifier.qsar_software.to_s + #exit + + # if defined?(self.class.text_properties) +# self.class.text_properties.each do |p| +# puts "set "+p.to_s +# raise "node not found: "+p.xml_alias.to_s+" ("+self.class.to_s+".from_xml)" unless node.elements[p.xml_alias] +# #puts "set "+p.to_s+" to: "+node.elements[p.xml_alias].text.to_s +# self.send(p.to_s+"=", node.elements[p.xml_alias].text) +# #qsar_identifier.qsar_models = node.elements["qsar_models".xml_alias].text +# dbg_check[:nodes] << node.elements[p.xml_alias].name +# end +# + + +# if defined?(self.class.subsection_properties) +# self.class.subsection_properties.each do |section_p, subsection_p| +# #puts "set "+p.to_s +# #raise "node not found: "+p.xml_alias.to_s+" ("+self.class.to_s+".from_xml)" unless node.elements[p.xml_alias] +# #puts "set "+p.to_s+" to: "+node.elements[p.xml_alias].text.to_s +# section = node.elements[section_p.xml_alias] +# subsection = section.elements[subsection_p.xml_alias] +# self.send(subsection_p.to_s+"=", subsection.text) +# +# dbg_check[:nodes] << section.name +# dbg_check[:subnodes] << subsection +# +# #qsar_identifier.qsar_models = node.elements["qsar_models".xml_alias].text +# #dbg_check[:text_nodes] << node.elements[p.xml_alias].name +# end +# end + +# if defined?(self.class.attribute_properties) +# self.class.attribute_properties.each do |p| +# puts "read attribute "+p.to_s +# #self.update(p => node.attribute(p.xml_alias)) +# self.send(p.to_s+"=", node.attribute(p.xml_alias)) +# dbg_check[:attributes] << p +# #qsar_identifier.qsar_models = node.elements["qsar_models".xml_alias].text +# end +# end + + #qsar_identifier.qsar_title = node.elements["qsar_title".xml_alias].text + #qsar_identifier.qsar_models = node.elements["qsar_models".xml_alias].text + + + + ignore_attribs =[ "id", "name", "help", "chapter" ] + node.attributes.each do |a,v| + unless (ignore_attribs.include?(a.to_s)) || dbg_check[:attributes].include?(a.to_sym) + raise "not handled : attribute '"+a.to_s+"' -> '"+v.to_s+"'" + + "\n("+self.class.to_s+".from_xml)" + + "\nchecked:\n"+dbg_check[:attributes].to_yaml+ + "\nnode-attribtues:\n"+node.attributes.to_yaml + end + end + + node.each_element do |n| +# if n.text!=nil and n.text.to_s.size>0 and +# !dbg_check[:text_nodes].include?(n.name) and +# (!dbg_check[:catalog_nodes].has_key?(n.name)) + + valid = dbg_check[:nodes].include?(n.name) + if (valid) + refs = dbg_check[:subnodes] + #puts "sub "+refs.inspect + n.each_element do |nn| + #puts "lookin for ref "+nn.to_s + unless refs.include?(nn) + valid = false + break + end + end + end + + unless valid + raise puts "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nnot handled node : "+n.to_s+ + "\n("+self.class.to_s+".from_xml)" +# raise "not handled node : "+n.to_s+ +# "\n("+self.class.to_s+".from_xml)" + +# "\nchecked text nodes:\n"+dbg_check[:text_nodes].to_yaml+ +# "\nchecked catalog nodes:\n"+dbg_check[:catalog_nodes].to_yaml+ +# "\nnode-attribtues:\n"+n.attributes.to_yaml + end + end + + + #self.save! + #self.reload +# unless self.save +# self.errors.each do |e| +# puts "Error: "+e.to_s +# end +# end + #puts "self id: "+self.id.to_s + +# qsar_identifier.qsar_software.each{ |s| s.destroy } if qsar_identifier.qsar_software +# qsar_identifier.qsar_software.update({}) #otherwise the content is tmp still here +# +# catalog_node = $catalogs_node.elements["software_catalog"] +# +# node.elements[:qsar_software.xml_alias].each_element do |n| +# +# puts "reading software ref "+n.to_s +# ref = nil +# catalog_node.each_element_with_attribute("id", n.attribute("idref").to_s) do |e| +# ref = e +# break +# end +# software = QsarSoftware.new +# QsarSoftware.from_xml( software, ref ) +# qsar_identifier.qsar_software << software +# end +# #qsar_identifier.qsar_software = QsarSoftware.new unless qsar_identifier.qsar_software +# #QsarSoftware.from_xml( qsar_identifier.qsar_software, node.elements["qsar_software".xml_alias] ) + + end + + def to_XML(node, chapter=nil ) + + xml_infos.each do |xml_info| + if xml_info.is_a?(ReachReports::TextNodeProperty) + new_node = REXML::TextElement.find_or_create(node, xml_info.xml_prop,self.send(xml_info.prop)) + + elsif xml_info.is_a?(ReachReports::CatalogReference) + + new_node = REXML::Element.find_or_create( node, xml_info.xml_prop ) + catalog_node = $catalogs_node.elements[ xml_info.catalog_name ] + + self.send(xml_info.prop.to_s+"=",[self.association_class(xml_info.prop).new]) unless self.send(xml_info.prop) and self.send(xml_info.prop).size>0 + + self.send( xml_info.prop ).each do |elem| + elem_node = REXML::Element.new(xml_info.catalog_element ) + elem.to_XML( elem_node ) + # if not saved, i.e. the element was only created for a complete xml, count elements in catalog for id + element_id = xml_info.catalog_element+"_"+(elem.id ? elem.id.to_s : (catalog_node.elements.size+1).to_s) + elem_node.add_attribute("id",element_id) + catalog_node << elem_node + + ref_node = REXML::Element.new(xml_info.catalog_element+"_ref" ) + ref_node.add_attributes("idref"=>element_id,"catalog"=>xml_info.catalog_name) + new_node << ref_node + end + + elsif xml_info.is_a?(ReachReports::AttributeProperty) + node.add_attribute( xml_info.xml_prop, self.send(xml_info.prop).to_s ) + + elsif xml_info.is_a?(ReachReports::TextSubnodeProperty) + new_node = REXML::Element.find_or_create( node, xml_info.parent_prop.xml_alias) + REXML::TextElement.find_or_create( new_node, xml_info.xml_prop,self.send(xml_info.prop)) + + elsif xml_info.is_a?(ReachReports::SingleAttributeNodeProperty) + new_node = REXML::Element.find_or_create(node, xml_info.xml_prop) + new_node.add_attribute(xml_info.attribute, self.send(xml_info.prop).to_s) + + elsif xml_info.is_a?(ReachReports::AttributeNodeProperty) + new_node = REXML::Element.find_or_create(node, xml_info.xml_prop) + self.send(xml_info.prop.to_s+"=",self.association_class(xml_info.prop).new) unless self.send(xml_info.prop) + self.send(xml_info.prop).to_XML(new_node) + + elsif xml_info.is_a?(ReachReports::AttributeSubNodeListProperty) + new_node = REXML::Element.find_or_create( node, xml_info.parent_prop.xml_alias ) + #puts "new parent "+xml_info.parent_prop.xml_alias + prop_node = REXML::Element.find_or_create( new_node, xml_info.xml_prop ) + #puts "new prop "+xml_info.xml_prop + self.send( xml_info.prop ).each do |elem| + #puts "elem "+elem.to_yaml + elem_node = REXML::Element.new( xml_info.list_element.xml_alias ) + elem.to_XML( elem_node ) + prop_node << elem_node + end + + else + raise "type not supported yet: "+xml_info.inspect + end + + new_node.add_attribute("chapter", chapter.to_s+"."+node.elements.size.to_s) if chapter and new_node and new_node.attribute("chapter")==nil + end + +# if defined?(self.class.text_properties) +# self.class.text_properties.each do |p| +# node << REXML::TextElement.new(p.xml_alias,self.send(p)) +# end +# end + #node << REXML::TextElement.new(:qsar_title.xml_alias,qsar_title) + #node << REXML::TextElement.new(:qsar_models.xml_alias,qsar_models) + +# if defined?(self.class.catalog_entries) +# self.class.catalog_entries.each do |p| #,associatedClass| +# +# assoc_node = REXML::Element.new( p.xml_alias ) +# self.send(p).each{ |s| s.to_XML( assoc_node ) } +# node << assoc_node +# end +# end +# +# if defined?(self.class.catalog_name) +# catalog_node = $catalogs_node.elements[self.class.catalog_name] +# node_ref = REXML::Element.new(self.class.catalog_element+"_ref") +# +# raise "id is nil" if self.id==nil || self.id.to_s.size==0 +# element_id = self.class.catalog_element+"_"+self.id.to_s +# node_ref.add_attributes("idref"=>element_id,"catalog"=>self.class.catalog_name) +# +# content_node = REXML::Element.new(self.class.catalog_element) +# content_node.add_attribute("id",element_id) +# self.class.attribute_properties.each do |p| +# content_node.add_attribute p.xml_alias,send(p) +# end +# catalog_node << content_node +# node << node_ref +# +## def catalog_to_xml(node, catalog_name, element_name, attributes) +## catalog_node = $catalogs_node.elements[catalog_name] +## node_ref = REXML::Element.new(element_name+"_ref") +## attributes["id"] = element_name+"_"+attributes.delete("id").to_s +## node_ref.add_attributes("idref"=>attributes["id"],"catalog"=>catalog_name) +## content_node = REXML::Element.new(element_name) +## #puts "my attribts: "+attributes.inspect +## attributes.each do |k,v| +## content_node.add_attribute k,v.to_s +## end +## catalog_node << content_node +## node << node_ref +## end +# end + + +# node << REXML::TextElement.new(:qsar_title.xml_alias,qsar_title) +# node << REXML::TextElement.new(:qsar_models.xml_alias,qsar_models) +# qsar_software_node = REXML::Element.new(:qsar_software.xml_alias) +# qsar_software.each{ |s| s.to_xml( qsar_software_node ) } +# node << qsar_software_node + end + +end + + +module ReachReports + + def self.get_uri( report ) + raise "internal error, id not set "+to_yaml if report.id==nil + return $url_provider.url_for("/"+File.join(report.type,report.id.to_s), :full).to_s + end + + + + +# module CatalogEntry +# +# def catalog_to_xml(node, catalog_name, element_name, attributes) +# catalog_node = $catalogs_node.elements[catalog_name] +# node_ref = REXML::Element.new(element_name+"_ref") +# attributes["id"] = element_name+"_"+attributes.delete("id").to_s +# node_ref.add_attributes("idref"=>attributes["id"],"catalog"=>catalog_name) +# content_node = REXML::Element.new(element_name) +# #puts "my attribts: "+attributes.inspect +# attributes.each do |k,v| +# content_node.add_attribute k,v.to_s +# end +# catalog_node << content_node +# node << node_ref +# end +# end + +# class QsarSoftware +# include DataMapper::Resource #, CatalogEntry +# +# property :id, Serial +# property :contact, String, :length => 255 +# property :description, String, :length => 255 +# property :name, String, :length => 255 +# property :number, String, :length => 255 +# property :url, String, :length => 255 +# +# def self.attribute_properties +# [ :contact, :description, :name, :number, :url ] +# end +# +# def self.catalog_name +# "software_catalog" +# end +# +# def self.catalog_element +# "software" +# end +# +# belongs_to :qsar_identifier +# end + + class Software + include DataMapper::Resource #, CatalogEntry + + property :id, Serial + property :contact, String, :length => 255 + property :description, String, :length => 255 + property :name, String, :length => 255 + property :number, String, :length => 255 + property :url, String, :length => 255 + + def xml_infos + [ AttributeProperty.new(:contact), + AttributeProperty.new(:description), + AttributeProperty.new(:name), + AttributeProperty.new(:number), + AttributeProperty.new(:url) ] + end + end + + class QsarSoftware < Software + #belongs_to :qsar_identifier, :key => false + property :qsar_identifier_id, Integer + end + + class XmlInfo + + attr_accessor :prop + + protected + def initialize( prop ) + @prop = prop + end + + public + def xml_prop + @prop.xml_alias + end + end + + class TextNodeProperty < XmlInfo + + end + + class TextSubnodeProperty < XmlInfo + attr_accessor :parent_prop + + def initialize( prop, parent_prop ) + super(prop) + @parent_prop = parent_prop + end + end + + class AttributeProperty < XmlInfo + + end + + class AttributeNodeProperty < XmlInfo + + end + + class AttributeSubNodeListProperty < XmlInfo + + attr_accessor :list_element, :parent_prop + + def initialize( prop, list_element, parent_prop ) + super(prop) + @list_element = list_element + @parent_prop = parent_prop + end + end + + class SingleAttributeNodeProperty < XmlInfo + attr_accessor :attribute + + def initialize( prop, attribute ) + super(prop) + @attribute = attribute + end + end + + class CatalogReference < XmlInfo + attr_accessor :catalog_name, :catalog_element + + def initialize( prop, catalog_name, catalog_element ) + super(prop) + @catalog_name = catalog_name + @catalog_element = catalog_element + end + end + + + class QsarIdentifier + include DataMapper::Resource + + property :id, Serial + property :qsar_title, Text + property :qsar_models, Text + + has n, :qsar_software + + def xml_infos + [ TextNodeProperty.new(:qsar_title), + TextNodeProperty.new(:qsar_models), + CatalogReference.new(:qsar_software, "software_catalog", "software") ] + end + + belongs_to :qmrf_report + end + + class Author + include DataMapper::Resource + + property :id, Serial + property :affiliation, String, :length => 255 + property :contact, String, :length => 255 + property :email, String, :length => 255 + property :name, String, :length => 255 + property :number, String, :length => 255 + property :url, String, :length => 255 + + def xml_infos + [ AttributeProperty.new(:affiliation), + AttributeProperty.new(:contact), + AttributeProperty.new(:email), + AttributeProperty.new(:name), + AttributeProperty.new(:number), + AttributeProperty.new(:url) ] + end + + belongs_to :qsar_general_information + end + + class QmrfAuthor < Author + property :type, String, :default => "QmrfAuthor" + end + + class ModelAuthor < Author + property :type, String, :default => "ModelAuthor" + end + + class Publication + include DataMapper::Resource + + property :id, Serial + property :title, Text + property :url, String, :length => 255 + + def xml_infos + [ AttributeProperty.new(:title), + AttributeProperty.new(:url) ] + end + end + + class Reference < Publication + #belongs_to :qsar_general_information + property :qsar_general_information_id, Integer + end + + + class QsarGeneralInformation + include DataMapper::Resource + + property :id, Serial + property :qmrf_date, Text #String, :length => 255 #PENDING -> datetime + property :model_date, Text #String, :length => 255 #PENDING -> datetime + property :qmrf_date_revision, Text #String, :length => 255 #PENDING -> datetime + property :qmrf_revision, Text + property :model_date, Text + property :info_availability, Text + property :related_models, Text + + # type is needed to distinguish between authors + # (the datamapper creates a table "Authors", the relation is defined by QsarGeneral.id and Author.id) + has n, :qmrf_authors, :type => "QmrfAuthor" + has n, :model_authors, :type => "ModelAuthor" + has n, :references + + def xml_infos + [ TextNodeProperty.new(:qmrf_date), + CatalogReference.new(:qmrf_authors, "authors_catalog", "author"), + TextNodeProperty.new(:qmrf_date_revision), + TextNodeProperty.new(:qmrf_revision), + CatalogReference.new(:model_authors, "authors_catalog", "author"), + TextNodeProperty.new(:model_date), + CatalogReference.new(:references, "publications_catalog", "publication"), + TextNodeProperty.new(:info_availability), + TextNodeProperty.new(:related_models) ] + end + + belongs_to :qmrf_report + end + + class ModelEndpoint + include DataMapper::Resource + + property :id, Serial + property :group, String, :length => 255 + property :name, String, :length => 255 + property :subgroup, String, :length => 255 + + def xml_infos + [ AttributeProperty.new(:group), + AttributeProperty.new(:name), + AttributeProperty.new(:subgroup) ] + end + + belongs_to :qsar_endpoint + end + + class QsarEndpoint + include DataMapper::Resource + + property :id, Serial + property :endpoint_variable, Text + property :model_species, Text + property :endpoint_comments, Text + property :endpoint_units, Text + property :endpoint_protocol, Text + property :endpoint_data_quality, Text + + has n, :model_endpoint + + def xml_infos + [ TextNodeProperty.new(:model_species), + CatalogReference.new(:model_endpoint, "endpoints_catalog", "endpoint"), + TextNodeProperty.new(:endpoint_comments), + TextNodeProperty.new(:endpoint_units), + TextNodeProperty.new(:endpoint_variable), + TextNodeProperty.new(:endpoint_protocol), + TextNodeProperty.new(:endpoint_data_quality) ] + end + + belongs_to :qmrf_report + end + + class AlgorithmExplicit + include DataMapper::Resource + + property :id, Serial + property :definition, Text + property :description, Text + property :publication_ref, Text + + def xml_infos + [ AttributeProperty.new(:definition), + AttributeProperty.new(:description), + AttributeProperty.new(:publication_ref) ] + end + + belongs_to :qsar_algorithm + end + + class AlgorithmsDescriptor + include DataMapper::Resource + + property :id, Serial + property :description, Text + property :name, Text + property :publication_ref, Text + property :units, Text + + def xml_infos + [ AttributeProperty.new(:description), + AttributeProperty.new(:name), + AttributeProperty.new(:publication_ref), + AttributeProperty.new(:units) ] + end + + belongs_to :qsar_algorithm + end + + class DescriptorsGenerationSoftware < Software + + #belongs_to :qsar_algorithm, :key => false + property :qsar_algorithm_id, Integer + end + + class QsarAlgorithm + include DataMapper::Resource + + property :id, Serial + + property :algorithm_type, Text + property :descriptors_selection, Text + property :descriptors_generation, Text + property :descriptors_chemicals_ratio, Text + property :equation, Text + + has n, :algorithm_explicit + has n, :algorithms_descriptors + has n, :descriptors_generation_software + + def xml_infos + [ TextNodeProperty.new(:algorithm_type), + CatalogReference.new(:algorithm_explicit, "algorithms_catalog", "algorithm"), + TextSubnodeProperty.new(:equation, :algorithm_explicit), + CatalogReference.new(:algorithms_descriptors, "descriptors_catalog", "descriptor"), + TextNodeProperty.new(:descriptors_selection), + TextNodeProperty.new(:descriptors_generation), + CatalogReference.new(:descriptors_generation_software, "software_catalog", "software"), + TextNodeProperty.new(:descriptors_chemicals_ratio), + ] + end + + belongs_to :qmrf_report + end + + class AppDomainSoftware < Software + + #belongs_to :qsar_algorithm, :key => false + property :qsar_applicability_domain_id, Integer + end + + class QsarApplicabilityDomain + include DataMapper::Resource + + property :id, Serial + property :app_domain_description, Text + property :app_domain_method, Text + property :applicability_limits, Text + + has n,:app_domain_software + + def xml_infos + [ TextNodeProperty.new(:app_domain_description), + TextNodeProperty.new(:app_domain_method), + CatalogReference.new(:app_domain_software, "software_catalog", "software"), + TextNodeProperty.new(:applicability_limits), ] + end + + belongs_to :qmrf_report + + end + + class DatasetData + include DataMapper::Resource + + property :id, Serial + property :chemname, String, :default => "No" + property :cas, String, :default => "No" + property :smiles, String, :default => "No" + property :inchi, String, :default => "No" + property :mol, String, :default => "No" + property :formula, String, :default => "No" + + def xml_infos + [ AttributeProperty.new(:chemname), + AttributeProperty.new(:cas), + AttributeProperty.new(:smiles), + AttributeProperty.new(:inchi), + AttributeProperty.new(:mol), + AttributeProperty.new(:formula) ] + end + + end + + class TrainingSetData < DatasetData + + #belongs_to :qsar_robustness + property :qsar_robustness_id, Integer + end + + + class QsarRobustness + include DataMapper::Resource + + property :id, Serial + property :training_set_availability, String, :default => "No" + property :training_set_descriptors, String, :default => "No" + property :dependent_var_availability, String, :default => "No" + property :other_info, Text + property :preprocessing, Text + property :goodness_of_fit, Text + property :loo, Text + property :lmo, Text + property :yscrambling, Text + property :bootstrap, Text + property :other_statistics, Text + + has 1, :training_set_data, :model => "TrainingSetData" + + def xml_infos + [ SingleAttributeNodeProperty.new(:training_set_availability, "answer"), + AttributeNodeProperty.new(:training_set_data), + SingleAttributeNodeProperty.new(:training_set_descriptors, "answer"), + SingleAttributeNodeProperty.new(:dependent_var_availability, "answer"), + TextNodeProperty.new(:other_info), + TextNodeProperty.new(:preprocessing), + TextNodeProperty.new(:goodness_of_fit), + TextNodeProperty.new(:loo), + TextNodeProperty.new(:lmo), + TextNodeProperty.new(:yscrambling), + TextNodeProperty.new(:bootstrap), + TextNodeProperty.new(:other_statistics), + ] + end + + belongs_to :qmrf_report + + end + + + class ValidationSetData < DatasetData + + #belongs_to :qsar_predictivity + property :qsar_predictivity_id, Integer + end + + class QsarPredictivity + include DataMapper::Resource + + property :id, Serial + + property :validation_set_availability, String, :default => "No" + property :validation_set_descriptors, String, :default => "No" + property :validation_dependent_var_availability, String, :default => "No" + property :validation_other_info, Text + property :experimental_design, Text + property :validation_predictivity, Text + property :validation_assessment, Text + property :validation_comments, Text + + has 1, :validation_set_data, :model => "ValidationSetData" + + def xml_infos + [ SingleAttributeNodeProperty.new(:validation_set_availability, "answer"), + AttributeNodeProperty.new(:validation_set_data), + SingleAttributeNodeProperty.new(:validation_set_descriptors, "answer"), + SingleAttributeNodeProperty.new(:validation_dependent_var_availability, "answer"), + TextNodeProperty.new(:validation_other_info), + TextNodeProperty.new(:experimental_design), + TextNodeProperty.new(:validation_predictivity), + TextNodeProperty.new(:validation_assessment), + TextNodeProperty.new(:validation_comments), + ] + end + + belongs_to :qmrf_report + end + + class QsarInterpretation + include DataMapper::Resource + + property :id, Serial + property :mechanistic_basis, Text + property :mechanistic_basis_comments, Text + property :mechanistic_basis_info, Text + + def xml_infos + [ TextNodeProperty.new(:mechanistic_basis), + TextNodeProperty.new(:mechanistic_basis_comments), + TextNodeProperty.new(:mechanistic_basis_info), + ] + end + + belongs_to :qmrf_report + end + + class Bibliography < Publication + + #belongs_to :qsar_miscellaneous + property :qsar_miscellaneous_id, Integer + end + + class Attachment + include DataMapper::Resource + + property :id, Serial + property :description, Text + property :filetype, String + property :url, String, :length => 255 + + def xml_infos + [ AttributeProperty.new(:description), + AttributeProperty.new(:filetype), + AttributeProperty.new(:url), + ] + end + + belongs_to :qsar_miscellaneous + end + + class AttachmentTrainingData < Attachment + property :type, String, :default => "AttachmentTrainingData" + end + + class AttachmentValidationData < Attachment + property :type, String, :default => "AttachmentValidationData" + end + + class AttachmentDocument < Attachment + property :type, String, :default => "AttachmentDocument" + end + + + class QsarMiscellaneous + include DataMapper::Resource + + property :id, Serial + property :comments, Text + + has n, :bibliography, Text + + # type is needed to distinguish between attachments + # (the datamapper creates a table "Attachments", the relation is defined by QsarMisc.id and Attachment.id) + has n, :attachment_training_data, :model => "AttachmentValidationData", :type => "AttachmentTrainingData" + has n, :attachment_validation_data, :model => "AttachmentValidationData", :type => "AttachmentValidationData" + has n, :attachment_documents, :type => "AttachmentDocument" + + def xml_infos + [ TextNodeProperty.new(:comments), + CatalogReference.new(:bibliography,"publications_catalog", "publication"), + AttributeSubNodeListProperty.new(:attachment_training_data, :molecules, :attachments), + AttributeSubNodeListProperty.new(:attachment_validation_data, :molecules, :attachments), + AttributeSubNodeListProperty.new(:attachment_documents, :documents, :attachments), + ] + end + + belongs_to :qmrf_report + end + + class QmrfSummary + include DataMapper::Resource + + property :id, Serial + property :qmrf_number, Text + property :date_publication, Text + property :keywords, Text + property :summary_comments, Text + + def xml_infos + [ TextNodeProperty.new(:qmrf_number), + TextNodeProperty.new(:date_publication), + TextNodeProperty.new(:keywords), + TextNodeProperty.new(:summary_comments), + ] + end + + belongs_to :qmrf_report + end + + class QmrfReport + include DataMapper::Resource, REXML + + property :id, Serial + property :model_uri, String, :length => 255 + + CHAPTERS = [ :qsar_identifier, :qsar_general_information, :qsar_endpoint, :qsar_algorithm, + :qsar_applicability_domain, :qsar_robustness, :qsar_predictivity, :qsar_interpretation, + :qsar_miscellaneous, :qmrf_summary ] + + CHAPTERS.each{ |c,clazz| has 1, c } + + attr_accessor :subjectid + + after :save, :check_policy + private + def check_policy + raise "no id" unless @id + #raise "no subjectid" unless subjectid + OpenTox::Authorization.check_policy(report_uri, subjectid) + end + + public + def to_yaml + super(:methods => CHAPTERS) + end + + def report_uri + return $url_provider.url_for("/reach_report/QMRF/"+@id.to_s, :full).to_s + end + + def self.from_xml(report, xml_data) + + raise "xml data size < 255, probably no qmrf report : '"+xml_data.to_s+"'" if xml_data.to_s.size<255 + doc = Document.new xml_data + + root = doc.elements["QMRF"] + raise "no QMRF node found" unless root + chapters = root.elements["QMRF_chapters"] + raise "no chapter node found" unless chapters + $catalogs_node = root.elements["Catalogs"] + raise "catalogs not found" unless $catalogs_node + + CHAPTERS.each do |p| #, chapterClass| + #unless report.send(p) + report.send(p).destroy if report.send(p) + c = report.association_class(p).new #chapterClass.new + #c.save + report.send(p.to_s+"=",c) + #end + report.send(p).from_xml( chapters.elements[p.xml_alias] ) + end + + #raise "already exists" if report.qsar_identifier + #report.qsar_general_information.destroy if report.qsar_general_information + #report.qsar_identifier.clear + #report.qsar_general_information = QsarGeneralInformation.new + #report.qsar_general_information.qmrf_date = "DateTime.now" + #report.qsar_general_information.model_authors << ModelAuthor.new + #report.qsar_general_information.qmrf_authors << QmrfAuthor.new + + #report.qsar_identifier = QsarIdentifier.new unless report.qsar_identifier + #report.qsar_identifier.from_xml( chapters.elements[:qsar_identifier.xml_alias] ) + + #report.qsar_general_information = QsarGeneralInformation.new unless report.qsar_general_information + #report.qsar_general_information.from_xml( chapters.elements[:qsar_general_information.xml_alias] ) + + + #QsarGeneralInformation.from_xml( report.qsar_general_information, chapters.elements["qsar_general_information".xml_alias] ) + + #puts "set qsar_identifier to "+report.qsar_identifier.class.to_s + +# begin + report.save +# rescue DataObjects::SQLError => e +# puts e.message +# exit +# rescue DataObjects::DataError => e +# puts e.message +# exit +# rescue DataMapper::SaveFailureError => e +# puts e.resource.errors.inspect +# exit +# end + +# puts "XXXXXXXxxxxx" +# +# puts "1" +# puts report.qsar_miscellaneous.attachment_training_data.inspect +# puts "2" +# puts report.qsar_miscellaneous.attachment_validation_data.inspect +# puts "3" +# puts report.qsar_miscellaneous.attachment_documents.inspect +# +# +# r = QmrfReport.get(report.id) +# +# puts "1" +# puts r.qsar_miscellaneous.attachment_training_data.inspect +# puts "2" +# puts r.qsar_miscellaneous.attachment_validation_data.inspect +# puts "3" +# puts r.qsar_miscellaneous.attachment_documents.inspect +# +# exit + + + end + + def to_xml + #puts "now qsar_identifier is "+self.qsar_identifier.class.to_s + + doc = Document.new + decl = XMLDecl.new + decl.encoding = "UTF-8" + doc << decl + type = DocType.new('QMRF SYSTEM "http://ambit.sourceforge.net/qmrf/jws/qmrf.dtd"') + doc << type + + root = Element.new("QMRF") + root.add_attributes( "version" => 1.2, "schema_version" => 1.0, "name" => "(Q)SAR Model Reporting Format", + "author" => "Joint Research Centre, European Commission", "contact" => "Joint Research Centre, European Commission", + "date" => "July 2007", "email" => "qsardb@jrc.it", "url" => "http://ecb.jrc.ec.europa.eu/qsar/" ) + + catalogs = Element.new("Catalogs") + [ "software_catalog", "algorithms_catalog", "descriptors_catalog", + "endpoints_catalog", "publications_catalog", "authors_catalog"].each do |c| + catalogs << Element.new(c) + end + $catalogs_node = catalogs + + chapters = Element.new("QMRF_chapters") + chapter_count = 1 + + CHAPTERS.each do |p| + node = Element.new( p.xml_alias ) + node.add_attribute("chapter",chapter_count) + self.send(p.to_s+"=", self.association_class(p).new) unless self.send(p) # create empy chapter, as xml must be complete + self.send(p).to_XML( node, chapter_count ) + chapters << node + chapter_count += 1 + end + +# qsar_identifier_node = Element.new(:qsar_identifier.xml_alias) +# self.qsar_identifier.to_XML( qsar_identifier_node ) +# chapters << qsar_identifier_node +# +# qsar_general_information_node = Element.new(:qsar_general_information.xml_alias) +# self.qsar_general_information.to_XML( qsar_general_information_node ) +# chapters << qsar_general_information_node + + + +# [ @qsar_identifier, @qsar_general_information, @qsar_endpoint ].each do |c| +# n = c.to_xml +# raise "no node "+n.to_s+" "+n.class.to_s unless n.is_a?(Element) +# chapters << n +# end + + root << chapters + root << catalogs + doc << root + + s = "" + doc.write(s, 2, false, false) + return s + + end + end + +#Profile2.auto_upgrade! + +# class QmrfReport < ActiveRecord::Base +# +# alias_attribute :date, :created_at +# +# QmrfProperties.serialized_properties.each do |p| +# serialize p +# end +# +# def type +# "QMRF" +# end +# +# def report_uri +# ReachReports.get_uri(self) +# end +# +# def get_content +# hash = {} +# [ :model_uri, :date ].each{ |p| hash[p] = self.send(p) } +# QmrfProperties.properties.each{ |p| hash[p] = self.send(p) } +# return hash +# end +# end +# +# +# class QprfReport < ActiveRecord::Base +# +# alias_attribute :date, :created_at +# +# def report_uri +# ReachReports.get_uri(self) +# end +# +# def type +# "QPRF" +# end +# end + + [ QsarSoftware, QsarIdentifier, QmrfAuthor, ModelAuthor, Reference, QsarGeneralInformation, ModelEndpoint, QsarEndpoint, AlgorithmExplicit, + AlgorithmsDescriptor, DescriptorsGenerationSoftware, QsarAlgorithm, AppDomainSoftware, QsarApplicabilityDomain, TrainingSetData, + QsarRobustness, ValidationSetData, QsarPredictivity, QsarInterpretation, Bibliography, AttachmentTrainingData, AttachmentValidationData, + AttachmentDocument, QsarMiscellaneous, QmrfSummary, QmrfReport ].each do |model| + model.auto_upgrade! + model.raise_on_save_failure = true + end +end
\ No newline at end of file diff --git a/reach_reports/reach_service.rb b/reach_reports/reach_service.rb new file mode 100755 index 0000000..53acb62 --- /dev/null +++ b/reach_reports/reach_service.rb @@ -0,0 +1,319 @@ + +class Array + + def to_html + return "" unless size>0 + s = "<html>\n<head>\n</head>\n<body>\n" + s += join(" <br>\n") + s += "</body>\n</html>\n" + return s + end +end + +module ReachReports + + def self.list_reports(type) + case type + when /(?i)QMRF/ + ReachReports::QmrfReport.all.collect{ |r| r.report_uri }.join("\n")+"\n" + when /(?i)QPRF/ + ReachReports::QprfReport.all.collect{ |r| r.report_uri }.join("\n")+"\n" + end + end + + def self.create_report( type, params, subjectid, xml_data=nil ) + + case type + when /(?i)QMRF/ + if params[:model_uri] + task = OpenTox::Task.create( "Create "+type+" report", + $url_provider.url_for("/reach_report/"+type, :full) ) do |task| #, params + + report = ReachReports::QmrfReport.new :model_uri => params[:model_uri] + report.subjectid = subjectid + build_qmrf_report(report, task) + report.report_uri + end + result_uri = task.uri + elsif xml_data and (input = xml_data.read).to_s.size>0 + report = ReachReports::QmrfReport.new + report.subjectid = subjectid + ReachReports::QmrfReport.from_xml(report,input) + result_uri = report.report_uri + else + raise OpenTox::BadRequestError.new "illegal parameters for qmrf-report creation, either\n"+ + "* give 'model_uri' as param\n"+ + "* provide xml file\n"+ + "params given: "+params.inspect + end + when /(?i)QPRF/ + raise OpenTox::BadRequestError.new "qprf report creation not yet implemented" + if params[:compound_uri] + #report = ReachReports::QprfReport.new :compound_uri => params[:compound_uri] + else + raise OpenTox::BadRequestError.new "illegal parameters for qprf-report, use either\n"+ + "* compound-uri\n"+ + "params given: "+params.inspect + end + end + result_uri + end + + def self.build_qmrf_report(r, task=nil) + + #puts r.model_uri + model = OpenTox::Model::Generic.find(r.model_uri, r.subjectid) + feature_type = model.feature_type(r.subjectid) + + # chapter 1 + r.qsar_identifier = QsarIdentifier.new + r.qsar_identifier.qsar_title = model.metadata[DC.title] + # TODO QSAR_models -> sparql same endpoint + r.qsar_identifier.qsar_software << QsarSoftware.new( :url => model.uri, + :name => model.metadata[DC.title], :contact => model.metadata[DC.creator] ) + algorithm = OpenTox::Algorithm::Generic.find(model.metadata[OT.algorithm], r.subjectid) if model.metadata[OT.algorithm] + r.qsar_identifier.qsar_software << QsarSoftware.new( :url => algorithm.uri, :name => algorithm.metadata[DC.title] ) + task.progress(10) if task + + #chpater 2 + r.qsar_general_information = QsarGeneralInformation.new + r.qsar_general_information.qmrf_date = DateTime.now.to_s + # EMPTY: qmrf_authors, qmrf_date_revision, qmrf_revision + # TODO: model_authors ? + r.qsar_general_information.model_date = model.metadata[DC.date].to_s + # TODO: references? + # EMPTY: info_availablity + # TODO: related_models = find qmrf reports for QSAR_models + task.progress(20) if task + + # chapter 3 + # TODO "model_species" ? + r.qsar_endpoint = QsarEndpoint.new + model.metadata[OT.predictedVariables].each do |p| + r.qsar_endpoint.model_endpoint << ModelEndpoint.new( :name => p ) + end if model.metadata[OT.predictedVariables] + # TODO "endpoint_comments" => "3.3", "endpoint_units" => "3.4", + r.qsar_endpoint.endpoint_variable = model.metadata[OT.dependentVariables] if model.metadata[OT.dependentVariables] + # TODO "endpoint_protocol" => "3.6", "endpoint_data_quality" => "3.7", + task.progress(30) if task + + # chapter 4 + # TODO algorithm_type (='type of model') + # TODO algorithm_explicit.equation + # TODO algorithm_explicit.algorithms_catalog + # TODO algorithms_descriptors, descriptors_selection, descriptors_generation, descriptors_generation_software, descriptors_chemicals_ratio + task.progress(40) if task + + # chapter 5 + # TODO app_domain_description, app_domain_method, app_domain_software, applicability_limits + + #training_dataset = model.trainingDataset ? OpenTox::Dataset.find(model.trainingDataset+"/metadata") : nil + if ( OpenTox::Dataset.exist?(model.metadata[OT.trainingDataset]) ) + training_dataset = OpenTox::Dataset.new( model.metadata[OT.trainingDataset] ) + training_dataset.load_metadata( r.subjectid ) + else + training_dataset = nil + LOGGER.warn "build qmrf: training_dataset not found "+model.metadata[OT.trainingDataset].to_s + end + task.progress(50) if task + + # chapter 6 + r.qsar_robustness = QsarRobustness.new + if training_dataset + r.qsar_robustness.training_set_availability = "Yes" + r.qsar_robustness.training_set_data = TrainingSetData.new(:chemname => "Yes", :cas => "Yes", + :smiles => "Yes", :inchi => "Yes", :mol => "Yes", :formula => "Yes") + end + + #TODO "training_set_data" => "6.2", + # "training_set_descriptors" => "6.3", + # "dependent_var_availability" => "6.4", "other_info" => "6.5", "preprocessing" => "6.6", "goodness_of_fit" => "6.7", + # "loo" => "6.8", + + val_datasets = [] + + if algorithm + cvs = Lib::Crossvalidation.find_all_uniq({:algorithm_uri => algorithm.uri, :finished => true},r.subjectid) + # PENDING: cv classification/regression hack + cvs = cvs.delete_if do |cv| + #val = Validation::Validation.first( :all, :conditions => { :crossvalidation_id => cv.id } ) + val = Validation::Validation.first( :crossvalidation_id => cv.id ) + raise "should not happen: no validations found for crossvalidation "+cv.id.to_s unless val + (val.classification_statistics!=nil) != (feature_type=="classification") + end + + lmo = [ "found "+cvs.size.to_s+" crossvalidation/s for algorithm '"+algorithm.uri.to_s+"'" ] + if cvs.size>0 + cvs_same_data = [] + cvs_other_data = [] + cvs.each do |cv| + if cv.dataset_uri == model.metadata[OT.trainingDataset] + cvs_same_data << cv + else + cvs_other_data << cv + end + end + lmo << cvs_same_data.size.to_s+" crossvalidations/s where performed on the training dataset of the model ("+ + model.metadata[OT.trainingDataset].to_s+")" + lmo << cvs_other_data.size.to_s+" crossvalidations/s where performed on the other datasets" + lmo << "" + + {cvs_same_data => "training dataset", cvs_other_data => "other datasets"}.each do |cvs,desc| + next if cvs.size==0 + lmo << "crossvalidation/s on "+desc + cvs.each do |cv| + lmo << "crossvalidation: "+cv.crossvalidation_uri + lmo << "dataset (see 9.3 Validation data): "+cv.dataset_uri + val_datasets << cv.dataset_uri + lmo << "settings: num-folds="+cv.num_folds.to_s+", random-seed="+cv.random_seed.to_s+", stratified:"+cv.stratified.to_s + + val = YAML.load( OpenTox::RestClientWrapper.get(File.join(cv.crossvalidation_uri,"statistics"),{:subjectid => r.subjectid}) ) + case feature_type + when "classification" + lmo << "percent_correct: "+val[OT.classificationStatistics][OT.percentCorrect].to_s + lmo << "weighted AUC: "+val[OT.classificationStatistics][OT.weightedAreaUnderRoc].to_s + when "regression" + lmo << "root_mean_squared_error: "+val[OT.regressionStatistics][OT.rootMeanSquaredError].to_s + lmo << "r_square "+val[OT.regressionStatistics][OT.rSquare].to_s + end + reports = OpenTox::RestClientWrapper.get(File.join(CONFIG[:services]["opentox-validation"], + "report/crossvalidation?crossvalidation_uris="+cv.crossvalidation_uri),{:subjectid => r.subjectid}) + if reports and reports.chomp.size>0 + lmo << "for more info see report: "+reports.split("\n")[0] + else + lmo << "for more info see report: not yet created for '"+cv.crossvalidation_uri+"'" + end + end + lmo << "" + end + end + + else + lmo = [ "no prediction algortihm for model found, crossvalidation not possible" ] + end + r.qsar_robustness.lmo = lmo.to_html + # "lmo" => "6.9", "yscrambling" => "6.10", "bootstrap" => "6.11", "other_statistics" => "6.12", + + LOGGER.debug "looking for validations with "+{:model_uri => model.uri}.inspect + #vals = Lib::Validation.find(:all, :conditions => {:model_uri => model.uri}) + vals = Lib::Validation.all({:model_uri => model.uri}) + uniq_vals = [] + vals.each do |val| + match = false + uniq_vals.each do |val2| + if val.test_dataset_uri == val2.test_dataset_uri + match = true + break + end + end + uniq_vals << val unless match + end + + r.qsar_predictivity = QsarPredictivity.new + if uniq_vals.size > 0 + r.qsar_predictivity.validation_set_availability = "Yes" + r.qsar_predictivity.validation_set_data = ValidationSetData.new(:chemname => "Yes", :cas => "Yes", + :smiles => "Yes", :inchi => "Yes", :mol => "Yes", :formula => "Yes") + + v = [ "found '"+uniq_vals.size.to_s+"' test-set validations of model '"+model.uri+"'" ] + v << "" + uniq_vals.each do |validation| + v << "validation: "+validation.validation_uri + v << "dataset (see 9.3 Validation data): "+validation.test_dataset_uri + val_datasets << validation.test_dataset_uri + case feature_type + when "classification" + v << "percent_correct: "+validation.classification_statistics[:percent_correct].to_s + v << "weighted AUC: "+validation.classification_statistics[:weighted_area_under_roc].to_s + when "regression" + v << "root_mean_squared_error: "+validation.regression_statistics[:root_mean_squared_error].to_s + v << "r_square "+validation.regression_statistics[:r_square].to_s + end + report = OpenTox::ValidationReport.find_for_validation(validation.validation_uri,r.subjectid) + #reports = OpenTox::RestClientWrapper.get(File.join(CONFIG[:services]["opentox-validation"], + # "report/validation?validation_uris="+validation.validation_uri),{:subjectid => r.subjectid}) + if report + v << "for more info see report: "+report.uri + else + v << "for more info see report: not yet created for '"+validation.validation_uri+"'" + end + v << "" + end + else + v = [ "no validation for model '"+model.uri+"' found" ] + end + r.qsar_predictivity.validation_predictivity = v.to_html + task.progress(60) if task + + # chapter 7 + # "validation_set_availability" => "7.1", "validation_set_data" => "7.2", "validation_set_descriptors" => "7.3", + # "validation_dependent_var_availability" => "7.4", "validation_other_info" => "7.5", "experimental_design" => "7.6", + # "validation_predictivity" => "7.7", "validation_assessment" => "7.8", "validation_comments" => "7.9", + task.progress(70) if task + + # chapter 8 + # "mechanistic_basis" => "8.1", "mechanistic_basis_comments" => "8.2", "mechanistic_basis_info" => "8.3", + task.progress(80) if task + + # chapter 9 + # "comments" => "9.1", "bibliography" => "9.2", "attachments" => "9.3", + + r.qsar_miscellaneous = QsarMiscellaneous.new + + r.qsar_miscellaneous.attachment_training_data << AttachmentTrainingData.new( + { :description => training_dataset.title, + :filetype => "owl-dl", + :url => training_dataset.uri} ) if training_dataset + + val_datasets.each do |data_uri| + if OpenTox::Dataset.exist?(data_uri, r.subjectid) + d = OpenTox::Dataset.new(data_uri) + d.load_metadata( r.subjectid) + r.qsar_miscellaneous.attachment_validation_data << AttachmentValidationData.new( + { :description => d.title, + :filetype => "owl-dl", + :url => data_uri} ) + end + end + task.progress(90) if task + + r.save + task.progress(100) if task + end + +# def self.get_report_content(type, id, *keys) +# +# report_content = get_report(type, id).get_content +# keys.each do |k| +# $sinatra.halt 400, type+" unknown report property '#{key}'" unless report_content.is_a?(Hash) and report_content.has_key?(k) +# report_content = report_content[k] +# end +# report_content +# end + + def self.get_report(type, id) + + case type + when /(?i)QMRF/ + report = ReachReports::QmrfReport.get(id) + when /(?i)QPRF/ + report = ReachReports::QprfReport.get(id) + end + raise OpenTox::NotFoundError.new type+" report with id '#{id}' not found." unless report + return report + end + + def self.delete_report(type, id, subjectid=nil) + + case type + when /(?i)QMRF/ + report = ReachReports::QmrfReport.get(id) + when /(?i)QPRF/ + report = ReachReports::QprfReport.get(id) + end + raise OpenTox::NotFoundError.new type+" report with id '#{id}' not found." unless report + OpenTox::Authorization.delete_policies_from_uri(report.report_uri, subjectid) if subjectid + return report.destroy + end + +end diff --git a/reach_reports/reach_test.rb b/reach_reports/reach_test.rb new file mode 100755 index 0000000..87addfa --- /dev/null +++ b/reach_reports/reach_test.rb @@ -0,0 +1,284 @@ + +require "rubygems" +require "sinatra" +before { + request.env['HTTP_HOST']="local-ot/validation" + request.env["REQUEST_URI"]=request.env["PATH_INFO"] +} + +require "uri" +require "yaml" +ENV['RACK_ENV'] = 'test' +require 'application.rb' +require 'test/unit' +require 'rack/test' +require 'lib/test_util.rb' +require 'test/test_examples.rb' + +LOGGER = OTLogger.new(STDOUT) +LOGGER.datetime_format = "%Y-%m-%d %H:%M:%S " +LOGGER.formatter = Logger::Formatter.new + +if AA_SERVER + #TEST_USER = "mgtest" + #TEST_PW = "mgpasswd" + TEST_USER = "guest" + TEST_PW = "guest" + SUBJECTID = OpenTox::Authorization.authenticate(TEST_USER,TEST_PW) + raise "could not log in" unless SUBJECTID + puts "logged in: "+SUBJECTID.to_s +else + puts "AA disabled" + SUBJECTID = nil +end + +#Rack::Test::DEFAULT_HOST = "local-ot/validation" +module Sinatra + module UrlForHelper + BASE = "http://local-ot/validation" + def url_for url_fragment, mode=:path_only + case mode + when :path_only + raise "not impl" + when :full + end + "#{BASE}#{url_fragment}" + end + end + set :raise_errors, false + set :show_exceptions, false +end + + +#DataMapper::Model.raise_on_save_failure = true +# +#class TestResourceX +# include DataMapper::Resource +# +# property :id, Serial +# +# has 1, :test_resource +#end +# +#class DataMapper::Associations::ManyToOne::Relationship +# def get_parent_model +# @parent_model +# end +#end +# +#class TestResource +# include DataMapper::Resource +# +# property :id, Serial +# property :time, DateTime +# property :body, Text +# +# def self.info +# relationships.each do |k,v| +# puts k +# puts v.inspect +# puts v.get_parent_model +# +# end +# end +# #validates_format_of :time +# #validates_length_of :body, :minimum => 1000 +# +# belongs_to :test_resource_x +#end +# +#TestResourceX.auto_upgrade! +#TestResource.auto_upgrade! + +class ReachTest < Test::Unit::TestCase + include Rack::Test::Methods + include Lib::TestUtil + + def app + Sinatra::Application + end + + def test_it + + begin + + # delete '/reach_report/QMRF/3' + # puts last_response.body + + #exit +# testResource = TestResource.new +# +# TestResource.info +# exit + +# p = nil +# #puts TestResource.properties.inspect +# TestResource.properties.each do |pp| +# p = pp if pp.name==:time +# end +# #puts p +# val = "no time" #DateTime.new +# testResource.time = val +# #puts p.valid?(val) +# +# #puts "test restource: "+testResource.valid?.to_s +# +# #puts testResource.time.to_s + " " + testResource.time.class.to_s +# begin +# testResource.save +# rescue DataMapper::SaveFailureError => e +# puts e.message +# puts e.resource.errors.inspect +# end +# exit + + #$test_case = self + +# #file = File.new("qmrf-report.xml") +# file = File.new("/home/martin/win/home/test2.xml") +# raise "File not found: "+file.path.to_s unless File.exist?(file.path) +# data = File.read(file.path) +# #puts "data found "+data.to_s[0..1000] +# puts OpenTox::RestClientWrapper.post("http://local-ot/validation/reach_report/qmrf/20",{:content_type => "application/qmrf-xml"},data).to_s.chomp + +# post "/reach_report/qmrf/8" +# puts last_response.body + + #model_uri = "http://ambit.uni-plovdiv.bg:8080/ambit2/model/173393" + + #model_uri = "http://local-ot/majority/class/model/58" + + + #model_uri = "http://local-ot/model/104" + model_uri = "http://local-ot/majority/class/model/125" + + +# m = OpenTox::Model::Generic.find(model_uri) +# puts m.metadata[OT.algorithm] if m +# a = OpenTox::Algorithm::Generic.find(m.metadata[OT.algorithm]) +# puts a.metadata.inspect +# exit + + puts SUBJECTID + +# model_uri = "http://local-ot/model/1" + #http://local-ot/majority/class/model/15 + #model_uri = "http://local-ot/majority/class/model/15" + # model_uri = "http://local-ot/majority/class/model/91" + #model_uri = "http://apps.ideaconsult.net:8080/ambit2/model/2" + post '/reach_report/qmrf',{:model_uri=>model_uri, :subjectid => SUBJECTID} #http://local-ot/model/1" + ##post '/reach_report/qprf',:compound_uri=>"http://local-ot/compound/XYZ" + uri = last_response.body + puts "task: "+uri.to_s + uri = Lib::TestUtil.wait_for_task(uri) + if uri + id = uri.split("/")[-1] + puts uri + end + +# id = "8" + + #get '/reach_report/qmrf' + #puts last_response.body + +# get '/reach_report/qmrf/'+id.to_s,nil,'HTTP_ACCEPT'=>"application/x-yaml" +# puts "YAML" +# puts last_response.body + +# get '/reach_report/qmrf/'+id.to_s,nil,'HTTP_ACCEPT'=>"application/rdf+xml" +# puts "RDF" +# puts last_response.body + + #get '/reach_report/qmrf/'+id,nil,'HTTP_ACCEPT' => "application/qmrf-xml" + #puts "XML" + #puts last_response.body + + + #r = ReachReports::QmrfReport.find_like( :QSAR_title => "Hamster") + #puts r.collect{|rr| "report with id:"+rr.id.to_s}.inspect + + #File.new("/home/martin/tmp/qmr_rep_del_me.xml","w").puts last_response.body + #File.new("/home/martin/win/home/qmr_rep_del_me.xml","w").puts last_response.body + #File.new("/home/martin/info_home/.public_html/qmr_rep_del_me.xml","w").puts last_response.body + + rescue => ex + rep = OpenTox::ErrorReport.create(ex, "") + puts rep.to_yaml + end + + + end +end + + +# query = <<EOF +#PREFIX ot:<http://www.opentox.org/api/1.1#> +#PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#> +#select ?model +#where { +#?model rdf:type ot:Model +#} +#EOF +# puts OpenTox::RestClientWrapper.post("http://apps.ideaconsult.net:8080/ontology/",{:accept => "application/rdf+xml", :query => query}) +# exit + +#class Person +# include DataMapper::Resource +# +# property :id, Serial +# +# has 1, :profile +# has 1, :profile2 +# end +# +# class Profile +# include DataMapper::Resource +# +# property :id, Serial +# property :val, Text +# +# belongs_to :person +# end +# +# class Profile2 +# include DataMapper::Resource +# +# property :id, Serial +# property :val, Text +# +# belongs_to :person +# end +# +#Person.auto_upgrade! +#Profile.auto_upgrade! +#Profile2.auto_upgrade! +# +# A.auto_upgrade! +# ValTest.auto_upgrade! + #A.auto_migrate! + #ValTest.auto_migrate! + +# class ReachTest < Test::Unit::TestCase +# include Rack::Test::Methods +# include Lib::TestUtil +# +# +# def app +# Sinatra::Application +# end +# +# def test_datamapper +# +# # Assigning a resource to a one-to-one relationship +# puts Person.all.collect{|v| v.id}.inspect +# +# person = Person.create +# person.profile = Profile.new +# person.profile2 = Profile2.new +# person.profile2.val = "bla" +# person.save +# +# p = Person.get(11) +## puts p.profile2 +# puts p.profile2.val + |