ncbo_resource_index_client 1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ module NCBO
2
+ class ResourceIndex
3
+
4
+ class Annotations
5
+ attr_accessor :resource, :annotations, :total_annotation_count, :offset, :limit
6
+ end
7
+
8
+ class Annotation
9
+ attr_accessor :score, :concept, :context, :element
10
+ end
11
+
12
+ class RankedElements
13
+ attr_accessor :concepts, :resources
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,229 @@
1
+ module NCBO
2
+ module Parser
3
+
4
+ class BaseParser
5
+ def parse_xml(xml)
6
+ if xml.kind_of?(String)
7
+ parser = XML::Parser.string(xml, :options => LibXML::XML::Parser::Options::NOBLANKS)
8
+ else
9
+ parser = XML::Parser.io(xml, :options => LibXML::XML::Parser::Options::NOBLANKS)
10
+ end
11
+ parser.parse
12
+ end
13
+
14
+ def safe_to_i(str)
15
+ Integer(str) rescue str
16
+ end
17
+ end
18
+
19
+ class ResourceIndex < BaseParser
20
+ def initialize(results)
21
+ @root = "/success/data/annotatorResultBean"
22
+ @results = parse_xml(results)
23
+ end
24
+
25
+ def self.parse_included_ontologies(ontologies)
26
+ new(ontologies).parse_included_ontologies
27
+ end
28
+
29
+ def parse_included_ontologies
30
+ @root = "/success/data/set"
31
+ ontologies = parse_ontologies("ontology")
32
+ end
33
+
34
+ def self.parse_resources(resources)
35
+ new(resources).parse_resources
36
+ end
37
+
38
+ def parse_resources
39
+ resources = []
40
+ @results.find("/success/data/set/resource").each do |resource|
41
+ a = {}
42
+ resource.children.each {|child| a[child.name.to_sym] = safe_to_i(child.content) if !child.first.nil? && !child.first.children?}
43
+ a[:contexts] = parse_resource_structure(resource.find_first("resourceStructure"))
44
+ resources << a
45
+ end
46
+ resources
47
+ end
48
+
49
+ def self.parse_results(results, options = {})
50
+ new(results).parse_results(options)
51
+ end
52
+
53
+ def parse_results(options = {})
54
+ resource ||= options[:resource]
55
+ annotation_location ||= options[:annotation_location]
56
+
57
+ results = []
58
+ @results.find("/success/data/list/*").each do |result|
59
+ resource_annotations = NCBO::ResourceIndex::Annotations.new
60
+ resource_annotations.resource = resource ||= result.find_first("resourceId").content
61
+
62
+ # Check to see if parameters are enabled that will change how we process the output
63
+ with_context = result.find_first("withContext").content.eql?("true") rescue false
64
+ counts = result.find_first("counts").content.eql?("true") rescue false
65
+
66
+ # Update total count (if available)
67
+ resource_annotations.total_annotation_count = result.find_first("resultStatistics/statistics/annotationCount").content.to_i if counts
68
+
69
+ resource_annotations.annotations = parse_annotations(result, with_context, annotation_location)
70
+ results << resource_annotations
71
+ end
72
+ results = results[0] if results.kind_of?(Array) && results.length == 1
73
+ results
74
+ end
75
+
76
+ def self.parse_ranked_element_results(results)
77
+ new(results).parse_ranked_element_results
78
+ end
79
+
80
+ def parse_ranked_element_results
81
+ ranked_elements = NCBO::ResourceIndex::RankedElements.new
82
+
83
+ ranked_elements.concepts = []
84
+ @results.find("/success/data/map/entry[string='concepts']/list/*").each do |concept|
85
+ concept = parse_concept(concept, ".")
86
+ ranked_elements.concepts << concept
87
+ end
88
+
89
+ ranked_elements.resources = []
90
+ @results.find("/success/data/map/entry[string='elements']/list/resourceElements").each do |resource|
91
+ r = {}
92
+ r[:resourceId] = resource.find_first("resourceId").content
93
+ r[:offset] = resource.find_first("offset").content.to_i
94
+ r[:limit] = resource.find_first("limit").content.to_i
95
+ r[:totalResults] = resource.find_first("totalResults").content.to_i
96
+ r[:elements] = []
97
+ resource.find("./elementResults/elementResult").each do |element|
98
+ r[:elements] << parse_element(element)
99
+ end
100
+ ranked_elements.resources << r
101
+ end
102
+
103
+ ranked_elements
104
+ end
105
+
106
+ def self.parse_popular_concepts(results)
107
+ new(results).parse_popular_concepts
108
+ end
109
+
110
+ def parse_popular_concepts
111
+ concepts = []
112
+ @results.find("/success/data/list/*").each do |concept_frequency|
113
+ concept = {}
114
+ concept[:counts] = concept_frequency.find_first("counts").content.to_i
115
+ concept[:score] = concept_frequency.find_first("score").content.to_i
116
+ concept[:concept] = parse_concept(concept_frequency)
117
+ concepts << concept
118
+ end
119
+ concepts
120
+ end
121
+
122
+ def self.parse_element_annotations(results)
123
+ new(results).parse_element_annotations
124
+ end
125
+
126
+ def parse_element_annotations
127
+ annotation_location = "annotation"
128
+ with_context = false
129
+
130
+ annotations = NCBO::ResourceIndex::Annotations.new
131
+ annotations.annotations = parse_annotations(@results.find_first("/success/data/list"), with_context, annotation_location)
132
+ annotations
133
+ end
134
+
135
+ private
136
+
137
+ def parse_annotations(result, with_context = false, annotation_location = "annotations/*")
138
+ annotations = []
139
+ if with_context
140
+ result.find("mgrepAnnotations/*").each do |annotation|
141
+ annotations << parse_annotation(annotation)
142
+ end
143
+ result.find("reportedAnnotations/*").each do |annotation|
144
+ annotations << parse_annotation(annotation)
145
+ end
146
+ result.find("isaAnnotations/*").each do |annotation|
147
+ annotations << parse_annotation(annotation)
148
+ end
149
+ result.find("mappingAnnotations/*").each do |annotation|
150
+ annotations << parse_annotation(annotation)
151
+ end
152
+ else
153
+ result.find(annotation_location).each do |annotation|
154
+ annotations << parse_annotation(annotation)
155
+ end
156
+ end
157
+ annotations
158
+ end
159
+
160
+ def parse_annotation(annotation, context = false)
161
+ new_annotation = NCBO::ResourceIndex::Annotation.new
162
+ new_annotation.score = annotation.find_first("score").content.to_f
163
+ new_annotation.concept = parse_concept(annotation)
164
+ new_annotation.context = parse_context(annotation)
165
+ new_annotation.element = parse_element(annotation)
166
+ new_annotation
167
+ end
168
+
169
+ # The only thing we care about from here is the contexts, everything else is internal info
170
+ def parse_resource_structure(resource_structure)
171
+ contexts = []
172
+ resource_structure.find_first("contexts").each {|context| contexts << context.first.content}
173
+ contexts
174
+ end
175
+
176
+ def parse_ontologies(ontology_location = "ontologies/ontologyUsedBean")
177
+ ontologies = []
178
+ @results.find(@root + "/#{ontology_location}").each do |ontology|
179
+ ont = {}
180
+ ontology.children.each {|child| ont[child.name.to_sym] = safe_to_i(child.content)}
181
+ ontologies << ont
182
+ end
183
+ ontologies
184
+ end
185
+
186
+ def parse_concept(annotation, concept_location = "concept")
187
+ a = {}
188
+ annotation.find("#{concept_location}/*").each {|child| a[child.name.to_sym] = safe_to_i(child.content) if !child.first.nil? && !child.first.children?}
189
+ a[:synonyms] = annotation.find("#{concept_location}/synonyms/string").map {|syn| safe_to_i(syn.content)}
190
+ semantic_types = parse_semantic_types(annotation.find_first("#{concept_location}/localSemanticTypeIds"))
191
+ a[:semantic_types] = semantic_types
192
+ a
193
+ end
194
+
195
+ def parse_semantic_types(semantic_types_xml)
196
+ return Array.new if semantic_types_xml.nil?
197
+
198
+ semantic_types = []
199
+ semantic_types_xml.each do |semantic_type_bean|
200
+ semantic_type_bean.children.each { |child| semantic_types << safe_to_i(child.content) }
201
+ end
202
+ semantic_types
203
+ end
204
+
205
+ def parse_context(annotation)
206
+ a = {}
207
+ annotation.find("context/*").each {|child| a[child.name.to_sym] = safe_to_i(child.content) if !child.first.nil? && !child.first.children?}
208
+ a[:contextType] = annotation.find_first("context").attributes["class"] unless annotation.find_first("context").nil?
209
+ a
210
+ end
211
+
212
+ def parse_element(annotation)
213
+ a = {}
214
+ a[:localElementId] = annotation.find_first("element/localElementId").content unless annotation.find_first("element/localElementId").nil?
215
+ # element text
216
+ a[:text] = {}
217
+ annotation.find("element/elementStructure/contexts/*").each {|context| a[:text][context.children[0].content] = context.children[1].content}
218
+ # element weights
219
+ a[:weights] = []
220
+ annotation.find("element/elementStructure/weights/*").each {|weight| a[:weights] << {:name => weight.children[0].content, :weight => weight.children[1].content.to_f} }
221
+ # which element portions are associated with an ontology
222
+ a[:ontoIds] = {}
223
+ annotation.find("element/elementStructure/ontoIds/*").each {|ont_id| a[:ontoIds][ont_id.children[0].content] = ont_id.children[1].content.to_i}
224
+ return a
225
+ end
226
+ end
227
+
228
+ end
229
+ end
@@ -0,0 +1,258 @@
1
+ require 'net/http'
2
+ require 'xml'
3
+ require 'uri'
4
+ require 'open-uri'
5
+ require 'cgi'
6
+ require 'ncbo_resource_index/parser'
7
+ require 'ncbo_resource_index/data'
8
+
9
+
10
+ module NCBO
11
+ class ResourceIndex
12
+
13
+ def initialize(args = {})
14
+ @options = {}
15
+
16
+ # Shared with Annotator
17
+ @options[:resource_index_location] = "http://rest.bioontology.org/resource_index/"
18
+ @options[:filterNumber] = true
19
+ @options[:isStopWordsCaseSensitive] = false
20
+ @options[:isVirtualOntologyId] = true
21
+ @options[:levelMax] = 0
22
+ @options[:longestOnly] = false
23
+ @options[:ontologiesToExpand] = []
24
+ @options[:ontologiesToKeepInResult] = []
25
+ @options[:mappingTypes] = []
26
+ @options[:minTermSize] = 3
27
+ @options[:scored] = true
28
+ @options[:semanticTypes] = []
29
+ @options[:stopWords] = []
30
+ @options[:wholeWordOnly] = true
31
+ @options[:withDefaultStopWords] = true
32
+ @options[:withSynonyms] = true
33
+
34
+ # RI-specific
35
+ @options[:conceptids] = []
36
+ @options[:mode] = :union
37
+ @options[:elementid] = []
38
+ @options[:resourceids] = []
39
+ @options[:elementDetails] = false
40
+ @options[:withContext] = true
41
+ @options[:offset] = 0
42
+ @options[:limit] = 10
43
+ @options[:format] = :xml
44
+ @options[:counts] = false
45
+ @options[:request_timeout] = 300
46
+
47
+ @options.merge!(args)
48
+
49
+ @ontologies = nil
50
+ @options[:resourceids] ||= []
51
+
52
+ # Check to make sure mappingTypes are capitalized
53
+ fix_params
54
+
55
+ raise ArgumentError, ":apikey is required, you can obtain one at http://bioportal.bioontology.org/accounts/new" if @options[:apikey].nil?
56
+ end
57
+
58
+ def self.find_by_concept(concepts, options = {})
59
+ new(options).find_by_concept(concepts)
60
+ end
61
+
62
+ def find_by_concept(concepts = [], options = {})
63
+ @options[:conceptids] = concepts unless concepts.nil? || concepts.empty?
64
+ @options.merge!(options) unless options.empty?
65
+ fix_params
66
+
67
+ raise ArgumentError, ":conceptids must be included" if @options[:conceptids].nil? || @options[:conceptids].empty?
68
+
69
+ result_xml = resource_index_post
70
+ Parser::ResourceIndex.parse_results(result_xml)
71
+ end
72
+
73
+ def self.find_by_element(element, resource, options = {})
74
+ new(options).find_by_element(element, resource)
75
+ end
76
+
77
+ def find_by_element(element, resource, options = {})
78
+ @options[:elementid] = element unless element.nil? || element.empty?
79
+ @options[:resourceids] = [resource] unless resource.nil? || resource.empty?
80
+ @options.merge!(options) unless options.empty?
81
+ fix_params
82
+ raise ArgumentError, ":elementid must be included" if @options[:elementid].nil? || @options[:elementid].empty?
83
+ raise ArgumentError, ":resourceids must be included" if @options[:resourceids].nil? || @options[:resourceids].empty?
84
+ Parser::ResourceIndex.parse_results(resource_index_post)
85
+ end
86
+
87
+ def self.element_annotations(element, concepts, resource, options = {})
88
+ new(options).element_annotations(element, concepts)
89
+ end
90
+
91
+ def element_annotations(element, concepts, resource)
92
+ @options[:conceptids] = concepts unless concepts.nil? || concepts.empty?
93
+ raise ArgumentError, ":conceptids must be included" if @options[:conceptids].nil? || @options[:conceptids].empty?
94
+ raise ArgumentError, ":resourceids must be an array" unless @options[:resourceids].kind_of? Array
95
+ resource = resource.upcase
96
+
97
+ concept_annotations = []
98
+ concepts.each do |concept|
99
+ split_concept = concept.split("/")
100
+ ontology_id = split_concept[0]
101
+ concept_id = split_concept[1]
102
+ virtual = @options[:isVirtualOntologyId] ? "/virtual" : ""
103
+ result_xml = open(["#{@options[:resource_index_location]}",
104
+ "details/#{@options[:elementDetails]}",
105
+ virtual,
106
+ "/concept/#{ontology_id}",
107
+ "/resource/#{resource}",
108
+ "/#{@options[:offset]}",
109
+ "/#{@options[:limit]}",
110
+ "?conceptid=#{CGI.escape(concept_id)}",
111
+ "&elementid=#{CGI.escape(element)}",
112
+ "&apikey=#{@options[:apikey]}"].join("")).read
113
+
114
+ annotations = Parser::ResourceIndex.parse_element_annotations(result_xml)
115
+ concept_annotations << annotations
116
+ end
117
+
118
+ if concept_annotations.length > 1
119
+ # Merge the two result sets
120
+ primary_annotations = Annotations.new
121
+ primary_annotations.annotations = []
122
+ primary_annotations.resource = resource
123
+ concept_annotations.each do |result|
124
+ primary_annotations.annotations.concat result.annotations
125
+ end
126
+ elsif concept_annotations.length == 1
127
+ primary_annotations = concept_annotations[0]
128
+ else
129
+ primary_annotations = nil
130
+ end
131
+ primary_annotations
132
+ end
133
+
134
+ def self.ranked_elements(concepts, options = {})
135
+ new(options).ranked_elements(concepts)
136
+ end
137
+
138
+ def ranked_elements(concepts = [], options = {})
139
+ @options[:conceptids] = concepts unless concepts.nil? || concepts.empty?
140
+ @options[:resourceids] ||= []
141
+ @options.merge!(options) unless options.empty?
142
+ fix_params
143
+
144
+ raise ArgumentError, ":conceptids must be included" if @options[:conceptids].nil? || @options[:conceptids].empty?
145
+ raise ArgumentError, ":resourceids must be an array" unless @options[:resourceids].kind_of? Array
146
+
147
+ result_url = ["#{@options[:resource_index_location]}",
148
+ "elements-ranked-by-concepts/#{@options[:resourceids].join(",")}",
149
+ "?offset=#{@options[:offset]}",
150
+ "&limit=#{@options[:limit]}",
151
+ "&conceptids=#{@options[:conceptids].join(",")}",
152
+ "&ontologiesToKeepInResult=#{@options[:ontologiesToKeepInResult].join(",")}",
153
+ "&isVirtualOntologyId=#{@options[:isVirtualOntologyId]}",
154
+ "&apikey=#{@options[:apikey]}",
155
+ "&mode=#{@options[:mode]}"].join("")
156
+ result_xml = open(result_url).read
157
+ Parser::ResourceIndex.parse_ranked_element_results(result_xml)
158
+ end
159
+
160
+ def self.popular_concepts(resources = nil, options = {})
161
+ new(options).popular_concepts(resources)
162
+ end
163
+
164
+ def popular_concepts(resources = nil, options = {})
165
+ @options[:resourceids] = resources
166
+ @options[:resourceids] = [resources] unless resources.nil? || resources.empty? || resources.kind_of?(Array)
167
+ @options.merge!(options) unless options.empty?
168
+ fix_params
169
+
170
+ if @options[:resourceids].nil? || @options[:resourceids].empty?
171
+ @options[:resourceids] = self.resources.collect {|resource| resource[:resourceId]}
172
+ end
173
+
174
+ popular_concepts = {}
175
+ @options[:resourceids].each do |resource|
176
+ popular_concepts_xml = open("#{@options[:resource_index_location]}most-used-concepts/#{resource}?apikey=#{@options[:apikey]}&offset=#{@options[:offset]}&limit=#{@options[:limit]}").read
177
+ popular_concepts[resource] = Parser::ResourceIndex.parse_popular_concepts(popular_concepts_xml)
178
+ end
179
+ popular_concepts
180
+ end
181
+
182
+ def self.ontologies(options)
183
+ new(options).ontologies
184
+ end
185
+
186
+ def ontologies(options = {})
187
+ @options.merge!(options) unless options.empty?
188
+
189
+ if @ontologies.nil?
190
+ ontologies_xml = open("#{@options[:resource_index_location]}ontologies?apikey=#{@options[:apikey]}").read
191
+ @ontologies = Parser::ResourceIndex.parse_included_ontologies(ontologies_xml)
192
+ else
193
+ @ontologies
194
+ end
195
+ end
196
+
197
+ def self.resources(options)
198
+ new(options).resources
199
+ end
200
+
201
+ def resources(options = {})
202
+ @options.merge!(options) unless options.empty?
203
+
204
+ if @resources.nil?
205
+ resources_xml = open("#{@options[:resource_index_location]}resources?apikey=#{@options[:apikey]}").read
206
+ @resources = Parser::ResourceIndex.parse_resources(resources_xml)
207
+ else
208
+ @resources
209
+ end
210
+ end
211
+
212
+ def self.resources_hash(options)
213
+ new(options).resources_hash
214
+ end
215
+
216
+ def resources_hash(options = {})
217
+ @options.merge!(options) unless options.empty?
218
+ resources = resources()
219
+ resources_hash = {}
220
+ resources.each {|res| resources_hash[res[:resourceId].downcase.to_sym] = res}
221
+ resources_hash
222
+ end
223
+
224
+ def options
225
+ @options
226
+ end
227
+
228
+ private
229
+
230
+ def fix_params
231
+ @options[:mappingTypes] = @options[:mappingTypes].split(",") if @options[:mappingTypes].kind_of?(String)
232
+ @options[:ontologiesToExpand] = @options[:ontologiesToExpand].split(",") if @options[:ontologiesToExpand].kind_of?(String)
233
+ @options[:ontologiesToKeepInResult] = @options[:ontologiesToKeepInResult].split(",") if @options[:ontologiesToKeepInResult].kind_of?(String)
234
+ @options[:semanticTypes] = @options[:semanticTypes].split(",") if @options[:semanticTypes].kind_of?(String)
235
+ @options[:stopWords] = @options[:stopWords].split(",") if @options[:stopWords].kind_of?(String)
236
+ @options[:mappingTypes].collect! {|e| e.capitalize} unless @options[:mappingTypes].nil?
237
+ end
238
+
239
+ def resource_index_post
240
+ url = URI.parse(@options[:resource_index_location])
241
+ request_body = []
242
+ @options.each do |k,v|
243
+ next if v.kind_of?(Array) && v.empty?
244
+ if v.kind_of?(Array)
245
+ request_body << "#{k}=#{v.collect {|val| CGI.escape(val)}.join(",")}"
246
+ else
247
+ request_body << "#{k}=#{v}"
248
+ end
249
+ end
250
+ req = Net::HTTP::Post.new(url.path)
251
+ req.body = request_body.join("&")
252
+ http = Net::HTTP.new(url.host, url.port)
253
+ http.read_timeout = @options[:request_timeout]
254
+ res = http.start {|http| http.request(req)}
255
+ res.body
256
+ end
257
+ end
258
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ncbo_resource_index_client
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: '1.4'
6
+ platform: ruby
7
+ authors:
8
+ - Paul R Alexander
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: libxml-ruby
16
+ type: :runtime
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: 2.2.0
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.2.0
30
+ description: The NCBO Resource Index Gem is a Ruby client for NCBO's Resource Index
31
+ Web service. The NCBO Resource Index is a system for ontology based annotation and
32
+ indexing of biomedical data; the key functionality of this system is to enable users
33
+ to locate biomedical data resources related to particular concepts. A set of annotations
34
+ is generated automatically and presented through integration with BioPortal, enabling
35
+ researchers to search for biomedical resources associated (annotated) with specific
36
+ ontology terms. This service uses a concept recognizer (developed by the National
37
+ Center for Integrative Biomedical Informatics, University of Michigan) to produce
38
+ a set of annotations and expand them using ontology is_a relations.
39
+ email: support@bioontology.org
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - lib/ncbo_resource_index/data.rb
45
+ - lib/ncbo_resource_index/parser.rb
46
+ - lib/ncbo_resource_index.rb
47
+ homepage: http://github.com/ncbo/resource_index_ruby_client
48
+ licenses: []
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 1.8.23
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: The NCBO Resource Index Gem is a Ruby client for NCBO's Resource Index Web
71
+ service
72
+ test_files: []
73
+ has_rdoc: