From 5e8461b65787ac2f4dbb321dd258cee90b4a91ab Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Thu, 14 Jun 2012 11:28:01 +0000 Subject: Initial version of 4store based dataset service --- Gemfile | 4 ++++ Rakefile | 1 + VERSION | 1 + application.rb | 29 +++++++++++++++++++++++++++++ config.ru | 10 ++++------ data/.gitignore | 3 --- dataset.gemspec | 25 +++++++++++++++++++++++++ public/.gitignore | 2 -- public/robots.txt | 2 -- 9 files changed, 64 insertions(+), 13 deletions(-) create mode 100644 Gemfile create mode 100644 Rakefile create mode 100644 VERSION delete mode 100644 data/.gitignore create mode 100644 dataset.gemspec delete mode 100644 public/.gitignore delete mode 100644 public/robots.txt diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..36f1a90 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source :gemcutter +gemspec +gem "opentox-server", :path => "~/opentox-server" +gem "opentox-client", :path => "~/opentox-client" diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..2995527 --- /dev/null +++ b/Rakefile @@ -0,0 +1 @@ +require "bundler/gem_tasks" diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0 diff --git a/application.rb b/application.rb index bda0ff9..b3e3843 100644 --- a/application.rb +++ b/application.rb @@ -1,3 +1,31 @@ +module OpenTox + class Application < Service + + # Get metadata of the dataset + # @return [application/rdf+xml] Metadata OWL-DL + get '/:id/metadata' do + end + + # Get a dataset feature + # @param [Header] Accept one of `application/rdf+xml or application-x-yaml` (default application/rdf+xml) + # @return [application/rdf+xml,application/x-yaml] Feature metadata + get %r{/(\d+)/feature/(.*)$} do |id,feature| + end + + # Get a list of all features + # @param [Header] Accept one of `application/rdf+xml, application-x-yaml, text/uri-list` (default application/rdf+xml) + # @return [application/rdf+xml, application-x-yaml, text/uri-list] Feature list + get '/:id/features' do + end + + # Get a list of all compounds + # @return [text/uri-list] Feature list + get '/:id/compounds' do + end + end +end + +=begin require 'rubygems' gem "opentox-ruby", "~> 3" require 'opentox-ruby' @@ -410,3 +438,4 @@ delete '/?' do response['Content-Type'] = 'text/plain' "All datasets deleted." end +=end diff --git a/config.ru b/config.ru index a1aab0d..39d9385 100644 --- a/config.ru +++ b/config.ru @@ -1,6 +1,4 @@ -require 'rubygems' -require 'opentox-ruby' -require 'config/config_ru' -run Sinatra::Application -set :raise_errors, false -set :show_exceptions, false \ No newline at end of file +SERVICE = "dataset" +require 'bundler' +Bundler.require +run OpenTox::Service diff --git a/data/.gitignore b/data/.gitignore deleted file mode 100644 index 59e3edd..0000000 --- a/data/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.rdfxml -*.xls -*.yaml diff --git a/dataset.gemspec b/dataset.gemspec new file mode 100644 index 0000000..37d2d62 --- /dev/null +++ b/dataset.gemspec @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +$:.push File.expand_path("../lib", __FILE__) + +Gem::Specification.new do |s| + s.name = "feature" + s.version = File.read("./VERSION") + s.authors = ["Christoph Helma"] + s.email = ["helma@in-silico.ch"] + s.homepage = "" + s.summary = %q{Simple OpenTox Dataset Service} + s.description = %q{TODO: Write a gem description} + s.license = 'GPL-3' + + s.rubyforge_project = "feature" + + s.files = `git ls-files`.split("\n") + #s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") + #s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } + #s.require_paths = ["lib"] + s.required_ruby_version = '>= 1.9.2' + + # specify any dependencies here; for example: + s.add_runtime_dependency "opentox-server" + s.post_install_message = "Please configure your service in ~/.opentox/config/dataset.rb" +end diff --git a/public/.gitignore b/public/.gitignore deleted file mode 100644 index debc7d4..0000000 --- a/public/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.rdfxml -*.xls diff --git a/public/robots.txt b/public/robots.txt deleted file mode 100644 index 1f53798..0000000 --- a/public/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: / -- cgit v1.2.3 From ea4f6e8984ee6043913d2ae0cdd16449cc2b582f Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Thu, 14 Jun 2012 11:31:09 +0000 Subject: .gitignore added --- .gitignore | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index d21a58b..4040c6c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ -api_key.rb -*.sqlite3 -tmp/* -log/* -public/* -data/* +*.gem +.bundle +Gemfile.lock +pkg/* -- cgit v1.2.3 From 00d70503797a4a9aa64a2c92643c07f261e3a5b0 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Thu, 28 Jun 2012 12:19:54 +0000 Subject: csv parser added --- application.rb | 268 +++++++++++++++++++++++++++++++++++++++++++++++++-------- config.ru | 3 +- 2 files changed, 232 insertions(+), 39 deletions(-) diff --git a/application.rb b/application.rb index b3e3843..e6d89b3 100644 --- a/application.rb +++ b/application.rb @@ -1,26 +1,245 @@ +#require "./parser.rb" module OpenTox class Application < Service - # Get metadata of the dataset - # @return [application/rdf+xml] Metadata OWL-DL - get '/:id/metadata' do + @warnings = [] + + helpers do + def parse_csv(csv) + parse_table CSV.parse(csv) + end + + def parse_sdf(sdf) + + obconversion = OpenBabel::OBConversion.new + obmol = OpenBabel::OBMol.new + obconversion.set_in_and_out_formats "sdf", "inchi" + + table = [] + + properties = [] + sdf.each_line { |l| properties << l.to_s if l.match(//,'').strip.chomp } + properties.insert 0, "InChI" + table[0] = properties + + rec = 0 + sdf.split(/\$\$\$\$\r*\n/).each do |s| + rec += 1 + table << [] + begin + # TODO: use compound service + obconversion.read_string obmol, s + table.last << obconversion.write_string(obmol).gsub(/\s/,'').chomp + rescue + # TODO: Fix, will lead to follow up errors + table.last << "Could not convert structure at record #{rec}) have been ignored! \n#{s}" + end + obmol.get_data.each { |d| table.last[table.first.index(d.get_attribute)] = d.get_value } + end + parse_table table + end + + def parse_table table + + @warnings = [] + dataset_uri = File.join(uri("/dataset"), SecureRandom.uuid) + #ntriples = [] + ntriples = ["<#{dataset_uri}> <#{RDF.type}> <#{RDF::OT.Dataset}>."] + + # features + feature_names = table.shift.collect{|f| f.strip} + @warnings << "Duplicated features in table header." unless feature_names.size == feature_names.uniq.size + compound_format = feature_names.shift.strip + bad_request_error "#{compound_format} is not a supported compound format. Accepted formats: URI, SMILES, InChI." unless compound_format =~ /URI|URL|SMILES|InChI/i + features = [] + ignored_feature_indices = [] + feature_names.each_with_index do |f,i| + # TODO search for existing features + feature = OpenTox::Feature.new File.join($feature[:uri], SecureRandom.uuid) + feature[RDF.type] = RDF::OT.Feature + feature[RDF::DC.title] = f + features << feature + values = table.collect{|row| row[i+1].strip unless row[i+1].nil?}.uniq # skip compound column + if values.size <= 3 # max classes + feature[RDF.type] = RDF::OT.NominalFeature + feature[RDF.type] = RDF::OT.StringFeature + feature[RDF::OT.acceptValue] = values + else + types = values.collect{|v| feature_type(v)} + if types.include?(RDF::OT.NominalFeature) + @warnings << "Feature '#{f}' contains nominal and numeric values." + #ignored_feature_indices << i + #next + else + feature[RDF.type] = RDF::OT.NumericFeature + end + end + feature.save + case feature[RDF.type].class.to_s + when "Array" + feature[RDF.type].each{ |t| ntriples << "<#{feature.uri}> <#{RDF.type}> <#{t}>." } + when "String" + ntriples << "<#{feature.uri}> <#{RDF.type}> <#{feature[RDF.type]}>." + end + end + + # remove invalid features from table +# puts ignored_feature_indices.inspect +# ignored_feature_indices.each do |i| +# features.delete_at(i) +# table.each{|row| row.delete_at(i)} +# end + + # compounds and values + compound_uris = [] + data_entry_idx = 0 + table.each_with_index do |values,j| + values.collect!{|v| v.strip unless v.nil?} + compound = values.shift + begin + case compound_format + when /URI|URL/i + compound_uri = compound + when /SMILES/i + compound_uri = OpenTox::Compound.from_smiles($compound[:uri], compound).uri + when /InChI/i + compound_uri = OpenTox::Compound.from_inchi($compound[:uri], URI.decode_www_form_component(compound)).uri + end + @warnings << "Duplicated compound #{compound} at position #{j+2}, entries are accepted, assuming that measurements come from independent experiments." if compound_uris.include? compound_uri + rescue + @warnings << "Cannot parse compound #{compound} at position #{j+2}, all entries are ignored." + next + end + unless values.size == features.size + @warnings << "Number of values at position #{j+2} (#{values.size}) is different than header size (#{features.size}), all entries are ignored." + next + end + ntriples << "<#{compound_uri}> <#{RDF.type}> <#{RDF::OT.Compound}>." + + values.each_with_index do |v,i| + @warnings << "Empty value for compound '#{compound}' (row #{j+2}) and feature '#{feature_names[i]}' (column #{i+2})." if v.blank? + + # TODO multiple values, use data_entry/value uris for sorted datasets + # data_entry_uri = File.join dataset_uri, "dataentry", data_entry_idx + ntriples << "<#{dataset_uri}> <#{RDF::OT.dataEntry}> _:dataentry#{data_entry_idx} ." + ntriples << "_:dataentry#{data_entry_idx} <#{RDF.type}> <#{RDF::OT.DataEntry}> ." + ntriples << "_:dataentry#{data_entry_idx} <#{RDF::OT.compound}> <#{compound_uri}> ." + ntriples << "_:dataentry#{data_entry_idx} <#{RDF::OT.values}> _:values#{data_entry_idx} ." + ntriples << "_:values#{data_entry_idx} <#{RDF::OT.feature}> <#{features[i].uri}> ." + ntriples << "_:values#{data_entry_idx} <#{RDF::OT.value}> \"#{v}\" ." + + data_entry_idx += 1 + + end + + end + + ntriples << "<#{dataset_uri}> <#{RDF::OT.Warnings}> \"#{@warnings.join('\n')}\" ." + ntriples.join("\n") + end + + def feature_type(value) + if value.blank? + nil + elsif value.numeric? + RDF::OT.NumericFeature + else + RDF::OT.NominalFeature + end + end + + end + + # Create a new resource + post "/dataset/?" do + #begin + case @content_type + when "text/plain", "text/turtle", "application/rdf+xml" # no conversion needed + when "text/csv" + @body = parse_csv @body + @content_type = "text/plain" + when "application/vnd.ms-excel" + xls = params[:file][:tempfile].path + ".xls" + File.rename params[:file][:tempfile].path, xls # roo needs these endings + @body = parse_csv Excel.new(xls).to_csv + @content_type = "text/plain" + when "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + xlsx = params[:file][:tempfile].path + ".xlsx" + File.rename params[:file][:tempfile].path, xlsx # roo needs these endings + @body = parse_csv Excelx.new(xlsx).to_csv + @content_type = "text/plain" + when "application/vnd.oasis.opendocument.spreadsheet" + ods = params[:file][:tempfile].path + ".ods" + File.rename params[:file][:tempfile].path, ods # roo needs these endings + @body = parse_csv Excelx.new(ods).to_csv + @content_type = "text/plain" + when "chemical/x-mdl-sdfile" + @body = parse_sdf @body + @content_type = "text/plain" + else + bad_request_error "#{@content_type} is not a supported content type." + end + uri = uri("/#{SERVICE}/#{SecureRandom.uuid}") + FourStore.put(uri, @body, @content_type) + if params[:file] + nt = "<#{uri}> <#{RDF::DC.title}> \"#{params[:file][:filename]}\".\n<#{uri}> <#{RDF::OT.hasSource}> \"#{params[:file][:filename]}\"." + FourStore.post(uri, nt, "text/plain") + end + #rescue + #bad_request_error $!.message + #end + + #dataset.add_metadata({ + #DC.title => File.basename(params[:file][:filename],".csv"), + #OT.hasSource => File.basename(params[:file][:filename]) + #}) + response['Content-Type'] = "text/uri-list" + uri end - # Get a dataset feature - # @param [Header] Accept one of `application/rdf+xml or application-x-yaml` (default application/rdf+xml) - # @return [application/rdf+xml,application/x-yaml] Feature metadata - get %r{/(\d+)/feature/(.*)$} do |id,feature| + # Create or updata a resource + put "/dataset/:id/?" do + FourStore.put uri("/#{SERVICE}/#{params[:id]}"), @body, @content_type + end + # Get metadata of the dataset + # @return [application/rdf+xml] Metadata OWL-DL + get '/dataset/:id/metadata' do end # Get a list of all features - # @param [Header] Accept one of `application/rdf+xml, application-x-yaml, text/uri-list` (default application/rdf+xml) - # @return [application/rdf+xml, application-x-yaml, text/uri-list] Feature list - get '/:id/features' do + # @param [Header] Accept one of `application/rdf+xml, text/turtle, text/plain, text/uri-list` (default application/rdf+xml) + # @return [application/rdf+xml, text/turtle, text/plain, text/uri-list] Feature list + get '/dataset/:id/features' do + accept = request.env['HTTP_ACCEPT'] + uri = uri "/dataset/#{params[:id]}" + case accept + when "application/rdf+xml", "text/turtle", "text/plain" + sparql = "CONSTRUCT {?s ?p ?o.} FROM <#{uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}>; ?p ?o. }" + when "text/uri-list" + sparql = "SELECT DISTINCT ?s FROM <#{uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}>. }" + else + bad_request_error "'#{accept}' is not a supported content type." + end + FourStore.query sparql, accept end # Get a list of all compounds # @return [text/uri-list] Feature list - get '/:id/compounds' do + get '/dataset/:id/compounds' do + accept = request.env['HTTP_ACCEPT'] + uri = uri "/dataset/#{params[:id]}" + case accept + when "application/rdf+xml", "text/turtle", "text/plain" + sparql = "CONSTRUCT {?s ?p ?o.} FROM <#{uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Compound}>; ?p ?o. }" + when "text/uri-list" + sparql = "SELECT DISTINCT ?s FROM <#{uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Compound}>. }" + else + bad_request_error "'#{accept}' is not a supported content type." + end + FourStore.query sparql, accept end end end @@ -102,33 +321,6 @@ helpers do when "application/rdf+xml" dataset.load_rdfxml_file(params[:file][:tempfile], @subjectid) - when "text/csv" - dataset.load_csv(params[:file][:tempfile].read, @subjectid) - dataset.add_metadata({ - DC.title => File.basename(params[:file][:filename],".csv"), - OT.hasSource => File.basename(params[:file][:filename]) - }) - - when /ms-excel/ - extension = File.extname(params[:file][:filename]) - case extension - when ".xls" - xls = params[:file][:tempfile].path + ".xls" - File.rename params[:file][:tempfile].path, xls # roo needs these endings - book = Excel.new xls - when ".xlsx" - xlsx = params[:file][:tempfile].path + ".xlsx" - File.rename params[:file][:tempfile].path, xlsx # roo needs these endings - book = Excel.new xlsx - else - raise "#{params[:file][:filename]} is not a valid Excel input file." - end - dataset.load_spreadsheet(book, @subjectid) - dataset.add_metadata({ - DC.title => File.basename(params[:file][:filename],extension), - OT.hasSource => File.basename(params[:file][:filename]) - }) - else raise "MIME type \"#{params[:file][:type]}\" not supported." end diff --git a/config.ru b/config.ru index 39d9385..a8d4293 100644 --- a/config.ru +++ b/config.ru @@ -1,4 +1,5 @@ SERVICE = "dataset" require 'bundler' Bundler.require -run OpenTox::Service +require './application.rb' +run OpenTox::Application -- cgit v1.2.3 From 66b581a79bd9fdfb923a6b56d84ed95170a77e61 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Thu, 12 Jul 2012 16:35:46 +0200 Subject: all opentox-client tests pass --- application.rb | 608 ++++++++++++++------------------------------------------- 1 file changed, 143 insertions(+), 465 deletions(-) diff --git a/application.rb b/application.rb index e6d89b3..8b2fadf 100644 --- a/application.rb +++ b/application.rb @@ -1,19 +1,19 @@ -#require "./parser.rb" module OpenTox class Application < Service @warnings = [] helpers do - def parse_csv(csv) - parse_table CSV.parse(csv) + def from_csv(csv) + from_table CSV.parse(csv) end +=begin def parse_sdf(sdf) - obconversion = OpenBabel::OBConversion.new - obmol = OpenBabel::OBMol.new - obconversion.set_in_and_out_formats "sdf", "inchi" + #obconversion = OpenBabel::OBConversion.new + #obmol = OpenBabel::OBMol.new + #obconversion.set_in_and_out_formats "sdf", "inchi" table = [] @@ -31,7 +31,8 @@ module OpenTox table << [] begin # TODO: use compound service - obconversion.read_string obmol, s + compound = OpenTox::Compound.from_sdf sdf + #obconversion.read_string obmol, s table.last << obconversion.write_string(obmol).gsub(/\s/,'').chomp rescue # TODO: Fix, will lead to follow up errors @@ -39,15 +40,15 @@ module OpenTox end obmol.get_data.each { |d| table.last[table.first.index(d.get_attribute)] = d.get_value } end - parse_table table + from_table table end +=end - def parse_table table + def from_table table @warnings = [] - dataset_uri = File.join(uri("/dataset"), SecureRandom.uuid) - #ntriples = [] - ntriples = ["<#{dataset_uri}> <#{RDF.type}> <#{RDF::OT.Dataset}>."] + ntriples = ["<#{@uri}> <#{RDF.type}> <#{RDF::OT.Dataset}>."] + ntriples << ["<#{@uri}> <#{RDF.type}> <#{RDF::OT.OrderedDataset}>."] # features feature_names = table.shift.collect{|f| f.strip} @@ -57,42 +58,27 @@ module OpenTox features = [] ignored_feature_indices = [] feature_names.each_with_index do |f,i| - # TODO search for existing features feature = OpenTox::Feature.new File.join($feature[:uri], SecureRandom.uuid) - feature[RDF.type] = RDF::OT.Feature feature[RDF::DC.title] = f features << feature values = table.collect{|row| row[i+1].strip unless row[i+1].nil?}.uniq # skip compound column if values.size <= 3 # max classes - feature[RDF.type] = RDF::OT.NominalFeature - feature[RDF.type] = RDF::OT.StringFeature + feature.append RDF.type, RDF::OT.NominalFeature + feature.append RDF.type, RDF::OT.StringFeature feature[RDF::OT.acceptValue] = values else types = values.collect{|v| feature_type(v)} if types.include?(RDF::OT.NominalFeature) @warnings << "Feature '#{f}' contains nominal and numeric values." - #ignored_feature_indices << i - #next else - feature[RDF.type] = RDF::OT.NumericFeature + feature.append RDF.type, RDF::OT.NumericFeature end end - feature.save - case feature[RDF.type].class.to_s - when "Array" - feature[RDF.type].each{ |t| ntriples << "<#{feature.uri}> <#{RDF.type}> <#{t}>." } - when "String" - ntriples << "<#{feature.uri}> <#{RDF.type}> <#{feature[RDF.type]}>." - end + feature.put + ntriples << "<#{feature.uri}> <#{RDF.type}> <#{RDF::OT.Feature}>." + ntriples << "<#{feature.uri}> <#{RDF::OLO.index}> #{i} ." end - # remove invalid features from table -# puts ignored_feature_indices.inspect -# ignored_feature_indices.each do |i| -# features.delete_at(i) -# table.each{|row| row.delete_at(i)} -# end - # compounds and values compound_uris = [] data_entry_idx = 0 @@ -118,29 +104,84 @@ module OpenTox next end ntriples << "<#{compound_uri}> <#{RDF.type}> <#{RDF::OT.Compound}>." + ntriples << "<#{compound_uri}> <#{RDF::OLO.index}> #{j} ." values.each_with_index do |v,i| @warnings << "Empty value for compound '#{compound}' (row #{j+2}) and feature '#{feature_names[i]}' (column #{i+2})." if v.blank? - # TODO multiple values, use data_entry/value uris for sorted datasets - # data_entry_uri = File.join dataset_uri, "dataentry", data_entry_idx - ntriples << "<#{dataset_uri}> <#{RDF::OT.dataEntry}> _:dataentry#{data_entry_idx} ." - ntriples << "_:dataentry#{data_entry_idx} <#{RDF.type}> <#{RDF::OT.DataEntry}> ." - ntriples << "_:dataentry#{data_entry_idx} <#{RDF::OT.compound}> <#{compound_uri}> ." - ntriples << "_:dataentry#{data_entry_idx} <#{RDF::OT.values}> _:values#{data_entry_idx} ." - ntriples << "_:values#{data_entry_idx} <#{RDF::OT.feature}> <#{features[i].uri}> ." - ntriples << "_:values#{data_entry_idx} <#{RDF::OT.value}> \"#{v}\" ." - - data_entry_idx += 1 + data_entry_node = "_:dataentry"+ j.to_s + value_node = data_entry_node+ "_value"+ i.to_s + ntriples << "<#{@uri}> <#{RDF::OT.dataEntry}> #{data_entry_node} ." + ntriples << "#{data_entry_node} <#{RDF.type}> <#{RDF::OT.DataEntry}> ." + ntriples << "#{data_entry_node} <#{RDF::OLO.index}> #{j} ." + ntriples << "#{data_entry_node} <#{RDF::OT.compound}> <#{compound_uri}> ." + ntriples << "#{data_entry_node} <#{RDF::OT.values}> #{value_node} ." + ntriples << "#{value_node} <#{RDF::OT.feature}> <#{features[i].uri}> ." + ntriples << "#{value_node} <#{RDF::OT.value}> \"#{v}\" ." end end - ntriples << "<#{dataset_uri}> <#{RDF::OT.Warnings}> \"#{@warnings.join('\n')}\" ." + ntriples << "<#{@uri}> <#{RDF::OT.Warnings}> \"#{@warnings.join('\n')}\" ." ntriples.join("\n") end + def ordered? + sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.OrderedDataset}>}" + FourStore.query(sparql, "text/uri-list").split("\n").empty? ? false : true + end + + def to_csv + accept = "text/uri-list" + csv_string = CSV.generate do |csv| + if ordered? + sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}> . ?s <#{RDF::OLO.index}> ?i} ORDER BY ?i" + features = FourStore.query(sparql, accept).split("\n").collect{|uri| OpenTox::Feature.new uri} + csv << ["SMILES"] + features.collect{ |f| f.get; f[RDF::DC.title] } + sparql = "SELECT DISTINCT ?i FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.DataEntry}> . ?s <#{RDF::OLO.index}> ?i} ORDER BY ?i" + FourStore.query(sparql, accept).split("\n").each do |data_entry_idx| + sparql = "SELECT DISTINCT ?compound FROM <#{@uri}> WHERE { + ?data_entry <#{RDF::OLO.index}> #{data_entry_idx} ; + <#{RDF::OT.compound}> ?compound. }" + compound = OpenTox::Compound.new FourStore.query(sparql, accept).strip + sparql = "SELECT ?value FROM <#{@uri}> WHERE { + ?data_entry <#{RDF::OLO.index}> #{data_entry_idx} ; + <#{RDF::OT.values}> ?v . + ?v <#{RDF::OT.feature}> ?f; + <#{RDF::OT.value}> ?value . + ?f <#{RDF::OLO.index}> ?i. + + } ORDER BY ?i" + csv << [compound.to_smiles] + FourStore.query(sparql,accept).split("\n") + end + else + sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}>}" + features = FourStore.query(sparql, accept).split("\n").collect{|uri| OpenTox::Feature.new uri} + csv << ["SMILES"] + features.collect{ |f| f.get; f[RDF::DC.title] } + sparql = "SELECT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Compound}>. }" + compounds = FourStore.query(sparql, accept).split("\n").collect{|uri| OpenTox::Compound.new uri} + compounds.each do |compound| + data_entries = [] + features.each do |feature| + sparql = "SELECT ?value FROM <#{@uri}> WHERE { + ?data_entry <#{RDF::OT.compound}> <#{compound.uri}>; + <#{RDF::OT.values}> ?v . + ?v <#{RDF::OT.feature}> <#{feature.uri}>; + <#{RDF::OT.value}> ?value. + } ORDER BY ?data_entry" + FourStore.query(sparql, accept).split("\n").each_with_index do |value,i| + data_entries[i] = [] unless data_entries[i] + data_entries[i] << value + end + end + data_entries.each{|data_entry| csv << [compound.to_smiles] + data_entry} + end + end + end + csv_string + end + def feature_type(value) if value.blank? nil @@ -151,91 +192,115 @@ module OpenTox end end - end - - # Create a new resource - post "/dataset/?" do - #begin + def parse_put case @content_type when "text/plain", "text/turtle", "application/rdf+xml" # no conversion needed when "text/csv" - @body = parse_csv @body + @body = from_csv @body @content_type = "text/plain" when "application/vnd.ms-excel" xls = params[:file][:tempfile].path + ".xls" File.rename params[:file][:tempfile].path, xls # roo needs these endings - @body = parse_csv Excel.new(xls).to_csv + @body = from_csv Excel.new(xls).to_csv @content_type = "text/plain" when "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" xlsx = params[:file][:tempfile].path + ".xlsx" File.rename params[:file][:tempfile].path, xlsx # roo needs these endings - @body = parse_csv Excelx.new(xlsx).to_csv + @body = from_csv Excelx.new(xlsx).to_csv @content_type = "text/plain" when "application/vnd.oasis.opendocument.spreadsheet" ods = params[:file][:tempfile].path + ".ods" File.rename params[:file][:tempfile].path, ods # roo needs these endings - @body = parse_csv Excelx.new(ods).to_csv - @content_type = "text/plain" - when "chemical/x-mdl-sdfile" - @body = parse_sdf @body + @body = from_csv Excelx.new(ods).to_csv @content_type = "text/plain" + # when "chemical/x-mdl-sdfile" + # @body = parse_sdf @body + # @content_type = "text/plain" else bad_request_error "#{@content_type} is not a supported content type." end - uri = uri("/#{SERVICE}/#{SecureRandom.uuid}") - FourStore.put(uri, @body, @content_type) + FourStore.put @uri, @body, @content_type if params[:file] - nt = "<#{uri}> <#{RDF::DC.title}> \"#{params[:file][:filename]}\".\n<#{uri}> <#{RDF::OT.hasSource}> \"#{params[:file][:filename]}\"." + nt = "<#{@uri}> <#{RDF::DC.title}> \"#{params[:file][:filename]}\".\n<#{uri}> <#{RDF::OT.hasSource}> \"#{params[:file][:filename]}\"." FourStore.post(uri, nt, "text/plain") end - #rescue - #bad_request_error $!.message - #end + end - #dataset.add_metadata({ - #DC.title => File.basename(params[:file][:filename],".csv"), - #OT.hasSource => File.basename(params[:file][:filename]) - #}) + end + + before "/#{SERVICE}/:id/:property" do + @uri = uri("/#{SERVICE}/#{params[:id]}") + end + + # Create a new resource + post "/dataset/?" do + # TOD: task + @uri = uri("/#{SERVICE}/#{SecureRandom.uuid}") + parse_put response['Content-Type'] = "text/uri-list" - uri + @uri + end + + get "/dataset/:id/?" do + case @accept + when "application/rdf+xml", "text/turtle", "text/plain", /html/ + FourStore.get(@uri, @accept) + else + case @accept + when "text/csv" + to_csv + #when "application/vnd.ms-excel" + #when "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + #when "application/vnd.oasis.opendocument.spreadsheet" + #when "chemical/x-mdl-sdfile" + else + bad_request_error "'#{@accept}' is not a supported content type." + end + end end # Create or updata a resource put "/dataset/:id/?" do - FourStore.put uri("/#{SERVICE}/#{params[:id]}"), @body, @content_type + # TOD: task + parse_put end + # Get metadata of the dataset # @return [application/rdf+xml] Metadata OWL-DL get '/dataset/:id/metadata' do + case @accept + when "application/rdf+xml", "text/turtle", "text/plain" + sparql = "CONSTRUCT {?s ?p ?o.} FROM <#{@uri}> WHERE {<#{@uri}> ?p ?o. }" + FourStore.query sparql, @accept + else + bad_request_error "'#{@accept}' is not a supported content type." + end end # Get a list of all features # @param [Header] Accept one of `application/rdf+xml, text/turtle, text/plain, text/uri-list` (default application/rdf+xml) # @return [application/rdf+xml, text/turtle, text/plain, text/uri-list] Feature list get '/dataset/:id/features' do - accept = request.env['HTTP_ACCEPT'] - uri = uri "/dataset/#{params[:id]}" - case accept + case @accept when "application/rdf+xml", "text/turtle", "text/plain" - sparql = "CONSTRUCT {?s ?p ?o.} FROM <#{uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}>; ?p ?o. }" + sparql = "CONSTRUCT {?s ?p ?o.} FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}>; ?p ?o. }" when "text/uri-list" - sparql = "SELECT DISTINCT ?s FROM <#{uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}>. }" + sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}>. }" else - bad_request_error "'#{accept}' is not a supported content type." + bad_request_error "'#{@accept}' is not a supported content type." end - FourStore.query sparql, accept + FourStore.query sparql, @accept end # Get a list of all compounds # @return [text/uri-list] Feature list get '/dataset/:id/compounds' do accept = request.env['HTTP_ACCEPT'] - uri = uri "/dataset/#{params[:id]}" case accept when "application/rdf+xml", "text/turtle", "text/plain" - sparql = "CONSTRUCT {?s ?p ?o.} FROM <#{uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Compound}>; ?p ?o. }" + sparql = "CONSTRUCT {?s ?p ?o.} FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Compound}>; ?p ?o. }" when "text/uri-list" - sparql = "SELECT DISTINCT ?s FROM <#{uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Compound}>. }" + sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Compound}>. }" else bad_request_error "'#{accept}' is not a supported content type." end @@ -244,390 +309,3 @@ module OpenTox end end -=begin -require 'rubygems' -gem "opentox-ruby", "~> 3" -require 'opentox-ruby' -require 'profiler' -require 'rjb' - -set :lock, true - -@@datadir = "data" - -@@idfile_path = @@datadir+"/id" -unless File.exist?(@@idfile_path) - id = Dir["./#{@@datadir}/*json"].collect{|f| File.basename(f.sub(/.json/,'')).to_i}.sort.last - id = 0 if id.nil? - open(@@idfile_path,"w") do |f| - f.puts(id) - end -end - -helpers do - def next_id - open(@@idfile_path, "r+") do |f| - f.flock(File::LOCK_EX) - @id = f.gets.to_i + 1 - f.rewind - f.print @id - end - return @id - end - - def uri(id) - url_for "/#{id}", :full - end - - # subjectid ist stored as memeber variable, not in params - def load_dataset(id, params,content_type,input_data) - - @uri = uri id - raise "store subject-id in dataset-object, not in params" if params.has_key?(:subjectid) and @subjectid==nil - - content_type = "application/rdf+xml" if content_type.nil? - dataset = OpenTox::Dataset.new(@uri, @subjectid) - - case content_type - - when /yaml/ - dataset.load_yaml(input_data) - - when /json/ - dataset.load_json(input_data) - - when "text/csv" - dataset.load_csv(input_data, @subjectid) - - when /application\/rdf\+xml/ - dataset.load_rdfxml(input_data, @subjectid) - - when "chemical/x-mdl-sdfile" - dataset.load_sdf(input_data, @subjectid) - - when /multipart\/form-data/ , "application/x-www-form-urlencoded" # file uploads - - case params[:file][:type] - - when "chemical/x-mdl-sdfile" - dataset.load_sdf(input_data, @subjectid) - - when /json/ - dataset.load_json(params[:file][:tempfile].read) - - when /yaml/ - dataset.load_yaml(params[:file][:tempfile].read) - - when "application/rdf+xml" - dataset.load_rdfxml_file(params[:file][:tempfile], @subjectid) - - else - raise "MIME type \"#{params[:file][:type]}\" not supported." - end - - else - raise "MIME type \"#{content_type}\" not supported." - end - - dataset.uri = @uri # update uri (also in metdata) - dataset.features.keys.each { |f| dataset.features[f][OT.hasSource] = dataset.metadata[OT.hasSource] unless dataset.features[f][OT.hasSource]} - File.open("#{@@datadir}/#{@id}.json","w+"){|f| f.puts dataset.to_json} - end -end - -before do - - @accept = request.env['HTTP_ACCEPT'] - @accept = 'application/rdf+xml' if @accept == '*/*' or @accept == '' or @accept.nil? - @id = request.path_info.match(/^\/\d+/) - unless @id.nil? - @id = @id.to_s.sub(/\//,'').to_i - - @uri = uri @id - @json_file = "#{@@datadir}/#{@id}.json" - raise OpenTox::NotFoundError.new "Dataset #{@id} not found." unless File.exists? @json_file - - extension = File.extname(request.path_info) - unless extension.empty? - case extension - when ".html" - @accept = 'text/html' - when ".json" - @accept = 'application/json' - when ".yaml" - @accept = 'application/x-yaml' - when ".csv" - @accept = 'text/csv' - when ".rdfxml" - @accept = 'application/rdf+xml' - when ".xls" - @accept = 'application/ms-excel' - when ".sdf" - @accept = 'chemical/x-mdl-sdfile' - else - raise OpenTox::NotFoundError.new "File format #{extension} not supported." - end - end - end - - # make sure subjectid is not included in params, subjectid is set as member variable - params.delete(:subjectid) -end - -## REST API - -# Get a list of available datasets -# @return [text/uri-list] List of available datasets -get '/?' do - uri_list = Dir["./#{@@datadir}/*json"].collect{|f| File.basename(f.sub(/.json/,'')).to_i}.sort.collect{|n| uri n}.join("\n") + "\n" - case @accept - when /html/ - response['Content-Type'] = 'text/html' - OpenTox.text_to_html uri_list - else - response['Content-Type'] = 'text/uri-list' - uri_list - end -end - -# Get a dataset representation -# @param [Header] Accept one of `application/rdf+xml, application-x-yaml, text/csv, application/ms-excel` (default application/rdf+xml) -# @return [application/rdf+xml, application-x-yaml, text/csv, application/ms-excel] Dataset representation -get '/:id' do - case @accept - - when /rdf/ # redland sends text/rdf instead of application/rdf+xml - file = "#{@@datadir}/#{params[:id]}.rdfxml" - unless File.exists? file # lazy rdfxml generation - dataset = OpenTox::Dataset.from_json File.read(@json_file) - File.open(file,"w+") { |f| f.puts dataset.to_rdfxml } - end - send_file file, :type => 'application/rdf+xml' - - when /json/ - send_file @json_file, :type => 'application/json' - - when /yaml/ - file = "#{@@datadir}/#{params[:id]}.yaml" - unless File.exists? file # lazy yaml generation - dataset = OpenTox::Dataset.from_json File.read(@json_file) - File.open(file,"w+") { |f| f.puts dataset.to_yaml } - end - send_file file, :type => 'application/x-yaml' - - when /html/ - response['Content-Type'] = 'text/html' - OpenTox.text_to_html JSON.pretty_generate(JSON.parse(File.read(@json_file))) - - when "text/csv" - response['Content-Type'] = 'text/csv' - OpenTox::Dataset.from_json(File.read(@json_file)).to_csv - - when /ms-excel/ - file = "#{@@datadir}/#{params[:id]}.xls" - OpenTox::Dataset.from_json(File.read(@json_file)).to_xls.write(file) unless File.exists? file # lazy xls generation - send_file file, :type => 'application/ms-excel' - - when /sdfile/ - response['Content-Type'] = 'chemical/x-mdl-sdfile' - OpenTox::Dataset.from_json(File.read(@json_file)).to_sdf - -# when /uri-list/ -# response['Content-Type'] = 'text/uri-list' -# Yajl::Parser.parse(File.read(@json_file)).to_urilist - - else - raise OpenTox::NotFoundError.new "Content-type #{@accept} not supported." - end -end - -# Get metadata of the dataset -# @return [application/rdf+xml] Metadata OWL-DL -get '/:id/metadata' do - metadata = OpenTox::Dataset.from_json(File.read(@json_file)).metadata - - case @accept - when /rdf/ # redland sends text/rdf instead of application/rdf+xml - response['Content-Type'] = 'application/rdf+xml' - serializer = OpenTox::Serializer::Owl.new - serializer.add_metadata url_for("/#{params[:id]}",:full), metadata - serializer.to_rdfxml - when /yaml/ - response['Content-Type'] = 'application/x-yaml' - metadata.to_yaml - end - -end - -# Get a dataset feature -# @param [Header] Accept one of `application/rdf+xml or application-x-yaml` (default application/rdf+xml) -# @return [application/rdf+xml,application/x-yaml] Feature metadata -get %r{/(\d+)/feature/(.*)$} do |id,feature| - - @id = id - @uri = uri @id - @json_file = "#{@@datadir}/#{@id}.json" - feature_uri = url_for("/#{@id}/feature/#{URI.encode(feature)}",:full) # work around racks internal uri decoding - metadata = OpenTox::Dataset.from_json(File.read(@json_file)).features[feature_uri] - - case @accept - when /rdf/ # redland sends text/rdf instead of application/rdf+xml - response['Content-Type'] = 'application/rdf+xml' - serializer = OpenTox::Serializer::Owl.new - serializer.add_feature feature_uri, metadata - serializer.to_rdfxml - when /yaml/ - response['Content-Type'] = 'application/x-yaml' - metadata.to_yaml - when /json/ - response['Content-Type'] = 'application/json' - Yajl::Encoder.encode(metadata) - end - -end - -# Get a list of all features -# @param [Header] Accept one of `application/rdf+xml, application-x-yaml, text/uri-list` (default application/rdf+xml) -# @return [application/rdf+xml, application-x-yaml, text/uri-list] Feature list -get '/:id/features' do - - features = OpenTox::Dataset.from_json(File.read(@json_file)).features - - case @accept - when /rdf/ # redland sends text/rdf instead of application/rdf+xml - response['Content-Type'] = 'application/rdf+xml' - serializer = OpenTox::Serializer::Owl.new - features.each { |feature,metadata| serializer.add_feature feature, metadata } - serializer.to_rdfxml - when /yaml/ - response['Content-Type'] = 'application/x-yaml' - features.to_yaml - when /json/ - response['Content-Type'] = 'application/json' - Yajl::Encoder.encode(features) - when "text/uri-list" - response['Content-Type'] = 'text/uri-list' - features.keys.join("\n") + "\n" - end -end - -# Get a list of all compounds -# @return [text/uri-list] Feature list -get '/:id/compounds' do - response['Content-Type'] = 'text/uri-list' - OpenTox::Dataset.from_json(File.read(@json_file)).compounds.join("\n") + "\n" -end - -# Create a new dataset. -# -# Posting without parameters creates and saves an empty dataset (with assigned URI). -# Posting with parameters creates and saves a new dataset. -# Data can be submitted either -# - in the message body with the appropriate Content-type header or -# - as file uploads with Content-type:multipart/form-data and a specified file type -# @example -# curl -X POST -F "file=@training.csv;type=text/csv" http://webservices.in-silico.ch/dataset -# @param [Header] Content-type one of `application/x-yaml, application/rdf+xml, multipart/form-data/` -# @param [BODY] - string with data in selected Content-type -# @param [optional] file, for file uploads, Content-type should be multipart/form-data, please specify the file type `application/rdf+xml, application-x-yaml, text/csv, application/ms-excel` -# @return [text/uri-list] Task URI or Dataset URI (empty datasets) -post '/?' do - - response['Content-Type'] = 'text/uri-list' - - # it could be that the read function works only once!, store in varible - input_data = request.env["rack.input"].read - @id = next_id - @uri = uri @id - @json_file = "#{@@datadir}/#{@id}.json" - if params.size == 0 and input_data.size==0 - File.open(@json_file,"w+"){|f| f.puts OpenTox::Dataset.new(@uri).to_json} - OpenTox::Authorization.check_policy(@uri, @subjectid) if File.exists? @json_file - @uri - else - task = OpenTox::Task.create("Converting and saving dataset ", @uri) do - load_dataset @id, params, request.content_type, input_data - OpenTox::Authorization.check_policy(@uri, @subjectid) if File.exists? @json_file - @uri - end - raise OpenTox::ServiceUnavailableError.newtask.uri+"\n" if task.status == "Cancelled" - halt 202,task.uri+"\n" - end -end - -# Save a dataset, will overwrite all existing data -# -# Data can be submitted either -# - in the message body with the appropriate Content-type header or -# - as file uploads with Content-type:multipart/form-data and a specified file type -# @example -# curl -X POST -F "file=@training.csv;type=text/csv" http://webservices.in-silico.ch/dataset/1 -# @param [Header] Content-type one of `application/x-yaml, application/rdf+xml, multipart/form-data/` -# @param [BODY] - string with data in selected Content-type -# @param [optional] file, for file uploads, Content-type should be multipart/form-data, please specify the file type `application/rdf+xml, application-x-yaml, text/csv, application/ms-excel` -# @return [text/uri-list] Task ID -post '/:id' do - response['Content-Type'] = 'text/uri-list' - task = OpenTox::Task.create("Converting and saving dataset ", @uri) do - FileUtils.rm Dir["#{@@datadir}/#{@id}.*"] - load_dataset @id, params, request.content_type, request.env["rack.input"].read - @uri - end - raise OpenTox::ServiceUnavailableError.newtask.uri+"\n" if task.status == "Cancelled" - halt 202,task.uri.to_s+"\n" -end - - -# Deletes datasets that have been created by a crossvalidatoin that does not exist anymore -# (This can happen if a crossvalidation fails unexpectedly) -delete '/cleanup' do - Dir["./#{@@datadir}/*json"].each do |file| - dataset = OpenTox::Dataset.from_json File.read(file) - if dataset.metadata[DC.creator] && dataset.metadata[DC.creator] =~ /crossvalidation\/[0-9]/ - begin - cv = OpenTox::Crossvalidation.find(dataset.metadata[DC.creator],@subjectid) - raise unless cv - rescue - LOGGER.debug "deleting #{dataset.uri}, crossvalidation missing: #{dataset.metadata[DC.creator]}" - begin - dataset.delete @subjectid - rescue - end - end - end - end - "cleanup done" -end - -# Delete a dataset -# @return [text/plain] Status message -delete '/:id' do - LOGGER.debug "deleting dataset with id #{@id}" - begin - FileUtils.rm Dir["#{@@datadir}/#{@id}.*"] - if @subjectid and !File.exists? @json_file and @uri - begin - res = OpenTox::Authorization.delete_policies_from_uri(@uri, @subjectid) - LOGGER.debug "Policy deleted for Dataset URI: #{@uri} with result: #{res}" - rescue - LOGGER.warn "Policy delete error for Dataset URI: #{@uri}" - end - end - response['Content-Type'] = 'text/plain' - "Dataset #{@id} deleted." - rescue - raise OpenTox::NotFoundError.new "Dataset #{@id} does not exist." - end -end - -# Delete all datasets -# @return [text/plain] Status message -delete '/?' do - FileUtils.rm Dir["#{@@datadir}/*.rdfxml"] - FileUtils.rm Dir["#{@@datadir}/*.xls"] - FileUtils.rm Dir["#{@@datadir}/*.yaml"] - FileUtils.rm Dir["#{@@datadir}/*.json"] - response['Content-Type'] = 'text/plain' - "All datasets deleted." -end -=end -- cgit v1.2.3 From c118806d88e8009f27bda9b49876d3d14dd136aa Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Fri, 13 Jul 2012 12:23:44 +0200 Subject: tasks for datset services --- application.rb | 70 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/application.rb b/application.rb index 8b2fadf..a92affd 100644 --- a/application.rb +++ b/application.rb @@ -193,39 +193,43 @@ module OpenTox end def parse_put - case @content_type - when "text/plain", "text/turtle", "application/rdf+xml" # no conversion needed - when "text/csv" - @body = from_csv @body - @content_type = "text/plain" - when "application/vnd.ms-excel" - xls = params[:file][:tempfile].path + ".xls" - File.rename params[:file][:tempfile].path, xls # roo needs these endings - @body = from_csv Excel.new(xls).to_csv - @content_type = "text/plain" - when "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - xlsx = params[:file][:tempfile].path + ".xlsx" - File.rename params[:file][:tempfile].path, xlsx # roo needs these endings - @body = from_csv Excelx.new(xlsx).to_csv - @content_type = "text/plain" - when "application/vnd.oasis.opendocument.spreadsheet" - ods = params[:file][:tempfile].path + ".ods" - File.rename params[:file][:tempfile].path, ods # roo needs these endings - @body = from_csv Excelx.new(ods).to_csv - @content_type = "text/plain" - # when "chemical/x-mdl-sdfile" - # @body = parse_sdf @body - # @content_type = "text/plain" - else - bad_request_error "#{@content_type} is not a supported content type." - end - FourStore.put @uri, @body, @content_type - if params[:file] - nt = "<#{@uri}> <#{RDF::DC.title}> \"#{params[:file][:filename]}\".\n<#{uri}> <#{RDF::OT.hasSource}> \"#{params[:file][:filename]}\"." - FourStore.post(uri, nt, "text/plain") + task = OpenTox::Task.create $task[:uri], nil, RDF::DC.description => "Dataset upload: #{@uri}" do + case @content_type + when "text/plain", "text/turtle", "application/rdf+xml" # no conversion needed + when "text/csv" + @body = from_csv @body + @content_type = "text/plain" + when "application/vnd.ms-excel" + xls = params[:file][:tempfile].path + ".xls" + File.rename params[:file][:tempfile].path, xls # roo needs these endings + @body = from_csv Excel.new(xls).to_csv + @content_type = "text/plain" + when "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + xlsx = params[:file][:tempfile].path + ".xlsx" + File.rename params[:file][:tempfile].path, xlsx # roo needs these endings + @body = from_csv Excelx.new(xlsx).to_csv + @content_type = "text/plain" + when "application/vnd.oasis.opendocument.spreadsheet" + ods = params[:file][:tempfile].path + ".ods" + File.rename params[:file][:tempfile].path, ods # roo needs these endings + @body = from_csv Excelx.new(ods).to_csv + @content_type = "text/plain" + # when "chemical/x-mdl-sdfile" + # @body = parse_sdf @body + # @content_type = "text/plain" + else + bad_request_error "#{@content_type} is not a supported content type." + end + FourStore.put @uri, @body, @content_type + if params[:file] + nt = "<#{@uri}> <#{RDF::DC.title}> \"#{params[:file][:filename]}\".\n<#{uri}> <#{RDF::OT.hasSource}> \"#{params[:file][:filename]}\"." + FourStore.post(uri, nt, "text/plain") + end + @uri end + response['Content-Type'] = "text/uri-list" + task.uri end - end before "/#{SERVICE}/:id/:property" do @@ -234,11 +238,8 @@ module OpenTox # Create a new resource post "/dataset/?" do - # TOD: task @uri = uri("/#{SERVICE}/#{SecureRandom.uuid}") parse_put - response['Content-Type'] = "text/uri-list" - @uri end get "/dataset/:id/?" do @@ -261,7 +262,6 @@ module OpenTox # Create or updata a resource put "/dataset/:id/?" do - # TOD: task parse_put end -- cgit v1.2.3 From 86247691a4414780130b094ccf5578ff2eb7d532 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Fri, 13 Jul 2012 13:22:38 +0200 Subject: warnings for duplicated compounds --- application.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/application.rb b/application.rb index a92affd..fa04f92 100644 --- a/application.rb +++ b/application.rb @@ -81,7 +81,6 @@ module OpenTox # compounds and values compound_uris = [] - data_entry_idx = 0 table.each_with_index do |values,j| values.collect!{|v| v.strip unless v.nil?} compound = values.shift @@ -94,11 +93,11 @@ module OpenTox when /InChI/i compound_uri = OpenTox::Compound.from_inchi($compound[:uri], URI.decode_www_form_component(compound)).uri end - @warnings << "Duplicated compound #{compound} at position #{j+2}, entries are accepted, assuming that measurements come from independent experiments." if compound_uris.include? compound_uri rescue @warnings << "Cannot parse compound #{compound} at position #{j+2}, all entries are ignored." next end + compound_uris << compound_uri unless values.size == features.size @warnings << "Number of values at position #{j+2} (#{values.size}) is different than header size (#{features.size}), all entries are ignored." next @@ -122,6 +121,11 @@ module OpenTox end end + compound_uris.duplicates.each do |uri| + positions = [] + compound_uris.each_with_index{|c,i| positions << i+1 if c == uri} + @warnings << "Duplicated compound #{uri} at rows #{positions.join(', ')}. Entries are accepted, assuming that measurements come from independent experiments." + end ntriples << "<#{@uri}> <#{RDF::OT.Warnings}> \"#{@warnings.join('\n')}\" ." ntriples.join("\n") @@ -295,16 +299,15 @@ module OpenTox # Get a list of all compounds # @return [text/uri-list] Feature list get '/dataset/:id/compounds' do - accept = request.env['HTTP_ACCEPT'] - case accept + case @accept when "application/rdf+xml", "text/turtle", "text/plain" sparql = "CONSTRUCT {?s ?p ?o.} FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Compound}>; ?p ?o. }" when "text/uri-list" sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Compound}>. }" else - bad_request_error "'#{accept}' is not a supported content type." + bad_request_error "'#{@accept}' is not a supported content type." end - FourStore.query sparql, accept + FourStore.query sparql, @accept end end end -- cgit v1.2.3 From d9a599f46009ad19e5ccfeb3c511f8d578635802 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Fri, 13 Jul 2012 17:12:19 +0200 Subject: csv handling of empty cells fixed --- application.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/application.rb b/application.rb index fa04f92..d0f512f 100644 --- a/application.rb +++ b/application.rb @@ -61,7 +61,7 @@ module OpenTox feature = OpenTox::Feature.new File.join($feature[:uri], SecureRandom.uuid) feature[RDF::DC.title] = f features << feature - values = table.collect{|row| row[i+1].strip unless row[i+1].nil?}.uniq # skip compound column + values = table.collect{|row| row[i+1].strip unless row[i+1].nil?}.uniq.compact # skip compound column if values.size <= 3 # max classes feature.append RDF.type, RDF::OT.NominalFeature feature.append RDF.type, RDF::OT.StringFeature @@ -82,7 +82,7 @@ module OpenTox # compounds and values compound_uris = [] table.each_with_index do |values,j| - values.collect!{|v| v.strip unless v.nil?} + values.collect!{|v| v.nil? ? nil : v.strip } compound = values.shift begin case compound_format @@ -157,7 +157,9 @@ module OpenTox ?f <#{RDF::OLO.index}> ?i. } ORDER BY ?i" - csv << [compound.to_smiles] + FourStore.query(sparql,accept).split("\n") + values = FourStore.query(sparql,accept).split("\n") + # Fill up trailing empty cells + csv << [compound.to_smiles] + values.fill("",values.size,features.size-values.size) end else sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}>}" @@ -175,7 +177,7 @@ module OpenTox <#{RDF::OT.value}> ?value. } ORDER BY ?data_entry" FourStore.query(sparql, accept).split("\n").each_with_index do |value,i| - data_entries[i] = [] unless data_entries[i] + data_entries[i] = Array.new(features.size) unless data_entries[i] data_entries[i] << value end end -- cgit v1.2.3 From 68729542619a4d7effc3109d55229e93d09e8cf6 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Fri, 13 Jul 2012 20:58:39 +0200 Subject: excel upload added --- application.rb | 155 +++++++++++++++++++++++++++++++++++--------------------- dataset.gemspec | 10 ++-- 2 files changed, 102 insertions(+), 63 deletions(-) diff --git a/application.rb b/application.rb index d0f512f..4a0cfd0 100644 --- a/application.rb +++ b/application.rb @@ -1,15 +1,30 @@ +require 'roo' +#require 'simple_xlsx' +#require 'axlsx' + module OpenTox class Application < Service @warnings = [] helpers do + def from_csv(csv) from_table CSV.parse(csv) end + def from_spreadsheet spreadsheet + extensions = { Excel => ".xls", Excelx => ".xlsx", Openoffice => ".ods" } + input = params[:file][:tempfile].path + ".xls" + csv_file = params[:file][:tempfile].path + ".csv" + File.rename params[:file][:tempfile].path, input # roo needs "correct" extensions + spreadsheet.new(input).to_csv csv_file # roo cannot write to strings + @body = from_csv File.read(csv_file) + @content_type = "text/plain" + end + =begin - def parse_sdf(sdf) + def from_sdf(sdf) #obconversion = OpenBabel::OBConversion.new #obmol = OpenBabel::OBMol.new @@ -131,61 +146,86 @@ module OpenTox ntriples.join("\n") end - def ordered? - sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.OrderedDataset}>}" - FourStore.query(sparql, "text/uri-list").split("\n").empty? ? false : true +=begin + def to_xlsx + + # both simple_xlsx and axlsx create empty documents with OLE2 errors + xlsx = @uri.split("/").last+".xlsx" + p = Axlsx::Package.new + wb = p.workbook + wb.add_worksheet(:name => "test") do |sheet| + to_table.each { |row| sheet.add_row row; puts row } + end + p.serialize("test.xlsx") + + p.to_stream +#``` + #Tempfile.open(@uri.split("/").last+".xlsx") do |xlsx| + SimpleXlsx::Serializer.new(xlsx) do |doc| + doc.add_sheet("People") do |sheet| + to_table.each { |row| sheet.add_row row } + end + end + send_file xlsx + #end end +=end def to_csv - accept = "text/uri-list" csv_string = CSV.generate do |csv| - if ordered? - sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}> . ?s <#{RDF::OLO.index}> ?i} ORDER BY ?i" - features = FourStore.query(sparql, accept).split("\n").collect{|uri| OpenTox::Feature.new uri} - csv << ["SMILES"] + features.collect{ |f| f.get; f[RDF::DC.title] } - sparql = "SELECT DISTINCT ?i FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.DataEntry}> . ?s <#{RDF::OLO.index}> ?i} ORDER BY ?i" - FourStore.query(sparql, accept).split("\n").each do |data_entry_idx| - sparql = "SELECT DISTINCT ?compound FROM <#{@uri}> WHERE { - ?data_entry <#{RDF::OLO.index}> #{data_entry_idx} ; - <#{RDF::OT.compound}> ?compound. }" - compound = OpenTox::Compound.new FourStore.query(sparql, accept).strip + to_table.each { |row| csv << row } + end + end + + def to_table + accept = "text/uri-list" + table = [] + if ordered? + sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}> . ?s <#{RDF::OLO.index}> ?i} ORDER BY ?i" + features = FourStore.query(sparql, accept).split("\n").collect{|uri| OpenTox::Feature.new uri} + table << ["SMILES"] + features.collect{ |f| f.get; f[RDF::DC.title] } + sparql = "SELECT DISTINCT ?i FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.DataEntry}> . ?s <#{RDF::OLO.index}> ?i} ORDER BY ?i" + FourStore.query(sparql, accept).split("\n").each do |data_entry_idx| + sparql = "SELECT DISTINCT ?compound FROM <#{@uri}> WHERE { + ?data_entry <#{RDF::OLO.index}> #{data_entry_idx} ; + <#{RDF::OT.compound}> ?compound. }" + compound = OpenTox::Compound.new FourStore.query(sparql, accept).strip + sparql = "SELECT ?value FROM <#{@uri}> WHERE { + ?data_entry <#{RDF::OLO.index}> #{data_entry_idx} ; + <#{RDF::OT.values}> ?v . + ?v <#{RDF::OT.feature}> ?f; + <#{RDF::OT.value}> ?value . + ?f <#{RDF::OLO.index}> ?i. + + } ORDER BY ?i" + values = FourStore.query(sparql,accept).split("\n") + # Fill up trailing empty cells + table << [compound.to_smiles] + values.fill("",values.size,features.size-values.size) + end + else + sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}>}" + features = FourStore.query(sparql, accept).split("\n").collect{|uri| OpenTox::Feature.new uri} + table << ["SMILES"] + features.collect{ |f| f.get; f[RDF::DC.title] } + sparql = "SELECT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Compound}>. }" + compounds = FourStore.query(sparql, accept).split("\n").collect{|uri| OpenTox::Compound.new uri} + compounds.each do |compound| + data_entries = [] + features.each do |feature| sparql = "SELECT ?value FROM <#{@uri}> WHERE { - ?data_entry <#{RDF::OLO.index}> #{data_entry_idx} ; + ?data_entry <#{RDF::OT.compound}> <#{compound.uri}>; <#{RDF::OT.values}> ?v . - ?v <#{RDF::OT.feature}> ?f; - <#{RDF::OT.value}> ?value . - ?f <#{RDF::OLO.index}> ?i. - - } ORDER BY ?i" - values = FourStore.query(sparql,accept).split("\n") - # Fill up trailing empty cells - csv << [compound.to_smiles] + values.fill("",values.size,features.size-values.size) - end - else - sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Feature}>}" - features = FourStore.query(sparql, accept).split("\n").collect{|uri| OpenTox::Feature.new uri} - csv << ["SMILES"] + features.collect{ |f| f.get; f[RDF::DC.title] } - sparql = "SELECT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.Compound}>. }" - compounds = FourStore.query(sparql, accept).split("\n").collect{|uri| OpenTox::Compound.new uri} - compounds.each do |compound| - data_entries = [] - features.each do |feature| - sparql = "SELECT ?value FROM <#{@uri}> WHERE { - ?data_entry <#{RDF::OT.compound}> <#{compound.uri}>; - <#{RDF::OT.values}> ?v . - ?v <#{RDF::OT.feature}> <#{feature.uri}>; - <#{RDF::OT.value}> ?value. - } ORDER BY ?data_entry" - FourStore.query(sparql, accept).split("\n").each_with_index do |value,i| - data_entries[i] = Array.new(features.size) unless data_entries[i] - data_entries[i] << value - end + ?v <#{RDF::OT.feature}> <#{feature.uri}>; + <#{RDF::OT.value}> ?value. + } ORDER BY ?data_entry" + FourStore.query(sparql, accept).split("\n").each_with_index do |value,i| + data_entries[i] = Array.new(features.size) unless data_entries[i] + data_entries[i] << value end - data_entries.each{|data_entry| csv << [compound.to_smiles] + data_entry} end + data_entries.each{|data_entry| table << [compound.to_smiles] + data_entry} end end - csv_string + table end def feature_type(value) @@ -198,6 +238,11 @@ module OpenTox end end + def ordered? + sparql = "SELECT DISTINCT ?s FROM <#{@uri}> WHERE {?s <#{RDF.type}> <#{RDF::OT.OrderedDataset}>}" + FourStore.query(sparql, "text/uri-list").split("\n").empty? ? false : true + end + def parse_put task = OpenTox::Task.create $task[:uri], nil, RDF::DC.description => "Dataset upload: #{@uri}" do case @content_type @@ -206,20 +251,11 @@ module OpenTox @body = from_csv @body @content_type = "text/plain" when "application/vnd.ms-excel" - xls = params[:file][:tempfile].path + ".xls" - File.rename params[:file][:tempfile].path, xls # roo needs these endings - @body = from_csv Excel.new(xls).to_csv - @content_type = "text/plain" + from_spreadsheet Excel when "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - xlsx = params[:file][:tempfile].path + ".xlsx" - File.rename params[:file][:tempfile].path, xlsx # roo needs these endings - @body = from_csv Excelx.new(xlsx).to_csv - @content_type = "text/plain" + from_spreadsheet Excelx when "application/vnd.oasis.opendocument.spreadsheet" - ods = params[:file][:tempfile].path + ".ods" - File.rename params[:file][:tempfile].path, ods # roo needs these endings - @body = from_csv Excelx.new(ods).to_csv - @content_type = "text/plain" + from_spreadsheet Openoffice # when "chemical/x-mdl-sdfile" # @body = parse_sdf @body # @content_type = "text/plain" @@ -257,8 +293,11 @@ module OpenTox when "text/csv" to_csv #when "application/vnd.ms-excel" + #to_spreadsheet Excel #when "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + #to_xlsx #when "application/vnd.oasis.opendocument.spreadsheet" + #to_spreadsheet Openoffice #when "chemical/x-mdl-sdfile" else bad_request_error "'#{@accept}' is not a supported content type." diff --git a/dataset.gemspec b/dataset.gemspec index 37d2d62..31d0d62 100644 --- a/dataset.gemspec +++ b/dataset.gemspec @@ -7,19 +7,19 @@ Gem::Specification.new do |s| s.authors = ["Christoph Helma"] s.email = ["helma@in-silico.ch"] s.homepage = "" - s.summary = %q{Simple OpenTox Dataset Service} - s.description = %q{TODO: Write a gem description} + s.summary = %q{OpenTox Dataset Service} + s.description = %q{OpenTox Dataset Service} s.license = 'GPL-3' s.rubyforge_project = "feature" s.files = `git ls-files`.split("\n") - #s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") - #s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } - #s.require_paths = ["lib"] s.required_ruby_version = '>= 1.9.2' # specify any dependencies here; for example: s.add_runtime_dependency "opentox-server" + s.add_runtime_dependency 'roo' + s.add_runtime_dependency 'axlsx' + #s.add_runtime_dependency 'simple_xlsx_writer' s.post_install_message = "Please configure your service in ~/.opentox/config/dataset.rb" end -- cgit v1.2.3 From 01aeccce13a0f46ac23eeb680039d03189329922 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Wed, 18 Jul 2012 16:26:34 +0200 Subject: dataset-large tests pass --- application.rb | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- dataset.gemspec | 4 +- 2 files changed, 107 insertions(+), 10 deletions(-) diff --git a/application.rb b/application.rb index 4a0cfd0..494f050 100644 --- a/application.rb +++ b/application.rb @@ -1,6 +1,5 @@ require 'roo' -#require 'simple_xlsx' -#require 'axlsx' +#require 'profiler' module OpenTox class Application < Service @@ -61,6 +60,77 @@ module OpenTox def from_table table +=begin + dataset = OpenTox::Dataset.new @uri + puts dataset.uri + feature_names = table.shift.collect{|f| f.strip} + puts feature_names.inspect + dataset.append RDF::OT.Warnings, "Duplicated features in table header." unless feature_names.size == feature_names.uniq.size + compound_format = feature_names.shift.strip + bad_request_error "#{compound_format} is not a supported compound format. Accepted formats: URI, SMILES, InChI." unless compound_format =~ /URI|URL|SMILES|InChI/i + features = [] + feature_names.each_with_index do |f,i| + feature = OpenTox::Feature.new File.join($feature[:uri], SecureRandom.uuid) + feature[RDF::DC.title] = f + features << feature + values = table.collect{|row| row[i+1].strip unless row[i+1].nil?}.uniq.compact # skip compound column + if values.size <= 3 # max classes + feature.append RDF.type, RDF::OT.NominalFeature + feature.append RDF.type, RDF::OT.StringFeature + feature[RDF::OT.acceptValue] = values + else + types = values.collect{|v| feature_type(v)} + if types.include?(RDF::OT.NominalFeature) + dataset.append RDF::OT.Warnings, "Feature #{f} contains nominal and numeric values." + else + feature.append RDF.type, RDF::OT.NumericFeature + end + end + feature.put + end + dataset.features = features + compounds = [] + table.each_with_index do |values,j| + c = values.shift + puts c + puts compound_format + values.collect!{|v| v.nil? ? nil : v.strip } + #begin + case compound_format + when /URI|URL/i + compound = OpenTox::Compound.new c + when /SMILES/i + compound = OpenTox::Compound.from_smiles($compound[:uri], c) + when /InChI/i + compound = OpenTox::Compound.from_inchi($compound[:uri], URI.decode_www_form_component(c)) + end + #rescue + #dataset.append RDF::OT.Warnings, "Cannot parse compound \"#{c}\" at position #{j+2}, all entries are ignored." + #next + #end + unless compound_uri.match(/InChI=/) + dataset.append RDF::OT.Warnings, "Cannot parse compound \"#{c}\" at position #{j+2}, all entries are ignored." + next + end + compounds << compound + unless values.size == features.size + dataset.append RDF::OT.Warnings, "Number of values at position #{j+2} (#{values.size}) is different than header size (#{features.size}), all entries are ignored." + next + end + + dataset << values + + end + dataset.compounds = compounds + compounds.duplicates.each do |compound| + positions = [] + compounds.each_with_index{|c,i| positions << i+1 if c.uri == compound.uri} + dataset.append RDF::OT.Warnings, "Duplicated compound #{compound.uri} at rows #{positions.join(', ')}. Entries are accepted, assuming that measurements come from independent experiments." + end + puts dataset.to_ntriples + dataset.to_ntriples +=end + @warnings = [] ntriples = ["<#{@uri}> <#{RDF.type}> <#{RDF::OT.Dataset}>."] ntriples << ["<#{@uri}> <#{RDF.type}> <#{RDF::OT.OrderedDataset}>."] @@ -84,7 +154,7 @@ module OpenTox else types = values.collect{|v| feature_type(v)} if types.include?(RDF::OT.NominalFeature) - @warnings << "Feature '#{f}' contains nominal and numeric values." + @warnings << "Feature #{f} contains nominal and numeric values." else feature.append RDF.type, RDF::OT.NumericFeature end @@ -109,7 +179,11 @@ module OpenTox compound_uri = OpenTox::Compound.from_inchi($compound[:uri], URI.decode_www_form_component(compound)).uri end rescue - @warnings << "Cannot parse compound #{compound} at position #{j+2}, all entries are ignored." + @warnings << "Cannot parse compound \"#{compound}\" at position #{j+2}, all entries are ignored." + next + end + unless compound_uri.match(/InChI=/) + @warnings << "Cannot parse compound \"#{compound}\" at position #{j+2}, all entries are ignored." next end compound_uris << compound_uri @@ -121,7 +195,8 @@ module OpenTox ntriples << "<#{compound_uri}> <#{RDF::OLO.index}> #{j} ." values.each_with_index do |v,i| - @warnings << "Empty value for compound '#{compound}' (row #{j+2}) and feature '#{feature_names[i]}' (column #{i+2})." if v.blank? + #@warnings << "Empty value for compound #{compound} (row #{j+2}) and feature \"#{feature_names[i]}\" (column #{i+2})." if v.blank? + #@warnings << "Empty value in row #{j+2}, column #{i+2} (feature \"#{feature_names[i]}\")." if v.blank? data_entry_node = "_:dataentry"+ j.to_s value_node = data_entry_node+ "_value"+ i.to_s @@ -144,6 +219,8 @@ module OpenTox ntriples << "<#{@uri}> <#{RDF::OT.Warnings}> \"#{@warnings.join('\n')}\" ." ntriples.join("\n") +=begin +=end end =begin @@ -178,6 +255,16 @@ module OpenTox end def to_table +=begin + table = [] + dataset = OpenTox::Dataset.new @uri + dataset.get + table << ["SMILES"] + dataset.features.collect{|f| f.get; f.title} + dataset.data_entries.each_with_index do |data_entry,i| + table << [dataset.compounds[i]] + data_entry + end + table +=end accept = "text/uri-list" table = [] if ordered? @@ -226,6 +313,8 @@ module OpenTox end end table +=begin +=end end def feature_type(value) @@ -245,6 +334,7 @@ module OpenTox def parse_put task = OpenTox::Task.create $task[:uri], nil, RDF::DC.description => "Dataset upload: #{@uri}" do + #Profiler__::start_profile case @content_type when "text/plain", "text/turtle", "application/rdf+xml" # no conversion needed when "text/csv" @@ -265,8 +355,10 @@ module OpenTox FourStore.put @uri, @body, @content_type if params[:file] nt = "<#{@uri}> <#{RDF::DC.title}> \"#{params[:file][:filename]}\".\n<#{uri}> <#{RDF::OT.hasSource}> \"#{params[:file][:filename]}\"." - FourStore.post(uri, nt, "text/plain") + FourStore.post(@uri, nt, "text/plain") end + #Profiler__::stop_profile + #Profiler__::print_profile($stdout) @uri end response['Content-Type'] = "text/uri-list" @@ -285,13 +377,15 @@ module OpenTox end get "/dataset/:id/?" do + #Profiler__::start_profile + @accept = "text/html" if @accept == '*/*' case @accept when "application/rdf+xml", "text/turtle", "text/plain", /html/ - FourStore.get(@uri, @accept) + r = FourStore.get(@uri, @accept) else case @accept when "text/csv" - to_csv + r = to_csv #when "application/vnd.ms-excel" #to_spreadsheet Excel #when "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" @@ -303,6 +397,9 @@ module OpenTox bad_request_error "'#{@accept}' is not a supported content type." end end + #Profiler__::stop_profile + #Profiler__::print_profile($stdout) + r end # Create or updata a resource diff --git a/dataset.gemspec b/dataset.gemspec index 31d0d62..58b427a 100644 --- a/dataset.gemspec +++ b/dataset.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.description = %q{OpenTox Dataset Service} s.license = 'GPL-3' - s.rubyforge_project = "feature" + s.rubyforge_project = "dataset" s.files = `git ls-files`.split("\n") s.required_ruby_version = '>= 1.9.2' @@ -19,7 +19,7 @@ Gem::Specification.new do |s| # specify any dependencies here; for example: s.add_runtime_dependency "opentox-server" s.add_runtime_dependency 'roo' - s.add_runtime_dependency 'axlsx' + #s.add_runtime_dependency 'axlsx' #s.add_runtime_dependency 'simple_xlsx_writer' s.post_install_message = "Please configure your service in ~/.opentox/config/dataset.rb" end -- cgit v1.2.3