openbel-api 0.6.2-java → 1.0.0-java

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.gemspec +11 -14
  3. data/CHANGELOG.md +18 -12
  4. data/README.md +25 -36
  5. data/VERSION +1 -1
  6. data/app/openbel/api/app.rb +13 -12
  7. data/app/openbel/api/config.rb +53 -11
  8. data/app/openbel/api/helpers/{evidence.rb → nanopub.rb} +13 -14
  9. data/app/openbel/api/middleware/auth.rb +22 -29
  10. data/app/openbel/api/resources/annotation.rb +7 -7
  11. data/app/openbel/api/resources/function.rb +12 -35
  12. data/app/openbel/api/resources/namespace.rb +13 -13
  13. data/app/openbel/api/resources/{evidence.rb → nanopub.rb} +23 -23
  14. data/app/openbel/api/resources/{evidence_transform.rb → nanopub_transform.rb} +8 -8
  15. data/app/openbel/api/resources/relationship.rb +74 -0
  16. data/app/openbel/api/routes/annotations.rb +1 -1
  17. data/app/openbel/api/routes/base.rb +11 -7
  18. data/app/openbel/api/routes/datasets.rb +74 -84
  19. data/app/openbel/api/routes/expressions.rb +86 -396
  20. data/app/openbel/api/routes/language.rb +118 -0
  21. data/app/openbel/api/routes/namespaces.rb +2 -2
  22. data/app/openbel/api/routes/{evidence.rb → nanopubs.rb} +68 -69
  23. data/app/openbel/api/routes/root.rb +2 -2
  24. data/app/openbel/api/routes/version.rb +37 -23
  25. data/app/openbel/api/schemas/annotation_resource.schema.json +1 -1
  26. data/app/openbel/api/schemas/{evidence.schema.json → nanopub.schema.json} +10 -10
  27. data/app/openbel/api/schemas/{evidence_collection.schema.json → nanopub_collection.schema.json} +5 -5
  28. data/app/openbel/api/schemas/{evidence_resource.schema.json → nanopub_resource.schema.json} +4 -4
  29. data/config/config.yml +15 -5
  30. data/lib/openbel/api/helpers/uuid_generator.rb +22 -0
  31. data/lib/openbel/api/{evidence → nanopub}/api.rb +9 -9
  32. data/lib/openbel/api/{evidence → nanopub}/facet_api.rb +2 -2
  33. data/lib/openbel/api/{evidence → nanopub}/facet_filter.rb +6 -6
  34. data/lib/openbel/api/{evidence → nanopub}/mongo.rb +54 -52
  35. data/lib/openbel/api/{evidence → nanopub}/mongo_facet.rb +17 -28
  36. data/lib/openbel/api/plugin/{evidence/evidence.rb → nanopub/nanopub.rb} +7 -7
  37. metadata +44 -56
  38. data/app/openbel/api/routes/functions.rb +0 -41
@@ -58,7 +58,7 @@ module OpenBEL
58
58
 
59
59
  render_collection(
60
60
  annotations.sort { |x,y|
61
- x.prefLabel.to_s <=> y.prefLabel.to_s
61
+ x.pref_label.to_s <=> y.pref_label.to_s
62
62
  },
63
63
  :annotation
64
64
  )
@@ -3,8 +3,9 @@ require 'json_schema'
3
3
  require 'multi_json'
4
4
  require_relative '../resources/annotation'
5
5
  require_relative '../resources/completion'
6
- require_relative '../resources/evidence'
6
+ require_relative '../resources/nanopub'
7
7
  require_relative '../resources/function'
8
+ require_relative '../resources/relationship'
8
9
  require_relative '../resources/match_result'
9
10
  require_relative '../resources/namespace'
10
11
  require_relative '../schemas'
@@ -14,9 +15,10 @@ module OpenBEL
14
15
 
15
16
  class Base < Sinatra::Application
16
17
  include OpenBEL::Resource::Annotations
17
- include OpenBEL::Resource::Evidence
18
+ include OpenBEL::Resource::Nanopub
18
19
  include OpenBEL::Resource::Expressions
19
20
  include OpenBEL::Resource::Functions
21
+ include OpenBEL::Resource::Relationships
20
22
  include OpenBEL::Resource::MatchResults
21
23
  include OpenBEL::Resource::Namespaces
22
24
  include OpenBEL::Schemas
@@ -36,15 +38,17 @@ module OpenBEL
36
38
  :completion_collection => CompletionCollectionSerializer,
37
39
  :function => FunctionResourceSerializer,
38
40
  :function_collection => FunctionCollectionSerializer,
41
+ :relationship => RelationshipResourceSerializer,
42
+ :relationship_collection => RelationshipCollectionSerializer,
39
43
  :match_result => MatchResultResourceSerializer,
40
44
  :match_result_collection => MatchResultCollectionSerializer,
41
45
  :namespace => NamespaceResourceSerializer,
42
46
  :namespace_collection => NamespaceCollectionSerializer,
43
47
  :namespace_value => NamespaceValueResourceSerializer,
44
48
  :namespace_value_collection => NamespaceValueCollectionSerializer,
45
- :evidence => EvidenceSerializer,
46
- :evidence_resource => EvidenceResourceSerializer,
47
- :evidence_collection => EvidenceCollectionSerializer
49
+ :nanopub => NanopubSerializer,
50
+ :nanopub_resource => NanopubResourceSerializer,
51
+ :nanopub_collection => NanopubCollectionSerializer
48
52
  }
49
53
 
50
54
  disable :protection
@@ -111,9 +115,9 @@ module OpenBEL
111
115
  end
112
116
  end
113
117
 
114
- def read_evidence
118
+ def read_nanopub
115
119
  halt 415 unless ::BEL.translator(request.media_type)
116
- ::BEL.evidence(request.body, request.media_type, :symbolize_keys => true)
120
+ ::BEL.nanopub(request.body, request.media_type, :symbolize_keys => true)
117
121
  end
118
122
 
119
123
  def read_json
@@ -1,12 +1,14 @@
1
1
  require 'bel'
2
2
  require 'bel/util'
3
3
  require 'rdf'
4
+ require 'rdf/vocab'
4
5
  require 'cgi'
5
6
  require 'multi_json'
6
- require 'openbel/api/evidence/mongo'
7
- require 'openbel/api/evidence/facet_filter'
8
- require_relative '../resources/evidence_transform'
9
- require_relative '../helpers/evidence'
7
+ require 'openbel/api/nanopub/mongo'
8
+ require 'openbel/api/nanopub/facet_filter'
9
+ require 'openbel/api/helpers/uuid_generator'
10
+ require_relative '../resources/nanopub_transform'
11
+ require_relative '../helpers/nanopub'
10
12
  require_relative '../helpers/filters'
11
13
  require_relative '../helpers/pager'
12
14
 
@@ -14,9 +16,10 @@ module OpenBEL
14
16
  module Routes
15
17
 
16
18
  class Datasets < Base
17
- include OpenBEL::Evidence::FacetFilter
18
- include OpenBEL::Resource::Evidence
19
+ include OpenBEL::Nanopub::FacetFilter
20
+ include OpenBEL::Resource::Nanopub
19
21
  include OpenBEL::Helpers
22
+ include OpenBEL::Helpers::UUIDGenerator
20
23
 
21
24
  DEFAULT_TYPE = 'application/hal+json'
22
25
  ACCEPTED_TYPES = {
@@ -28,15 +31,20 @@ module OpenBEL
28
31
 
29
32
  MONGO_BATCH = 500
30
33
  FACET_THRESHOLD = 10000
34
+ DC = ::RDF::Vocab::DC
35
+ VOID = ::RDF::Vocab::VOID
36
+ FOAF = ::RDF::Vocab::FOAF
31
37
 
32
38
  def initialize(app)
33
39
  super
34
40
 
35
41
  BEL.translator(:rdf)
36
42
 
37
- # Evidence API using Mongo.
38
- mongo = OpenBEL::Settings[:evidence_store][:mongo]
39
- @api = OpenBEL::Evidence::Evidence.new(mongo)
43
+ @bel_version = OpenBEL::Settings[:bel][:version]
44
+
45
+ # nanopub API using Mongo.
46
+ mongo = OpenBEL::Settings[:nanopub_store][:mongo]
47
+ @api = OpenBEL::Nanopub::Nanopub.new(mongo)
40
48
 
41
49
  # RdfRepository using Jena.
42
50
  @rr = BEL::RdfRepository.plugins[:jena].create_repository(
@@ -63,19 +71,19 @@ module OpenBEL
63
71
 
64
72
  def check_dataset(io, type)
65
73
  begin
66
- evidence = BEL.evidence(io, type).each.first
74
+ nanopub = BEL.nanopub(io, type).each.first
67
75
 
68
- unless evidence
76
+ unless nanopub
69
77
  halt(
70
78
  400,
71
79
  { 'Content-Type' => 'application/json' },
72
- render_json({ :status => 400, :msg => 'No BEL evidence was provided. Evidence is required to infer dataset information.' })
80
+ render_json({ :status => 400, :msg => 'No BEL nanopub was provided. nanopub is required to infer dataset information.' })
73
81
  )
74
82
  end
75
83
 
76
- void_dataset_uri = RDF::URI("#{base_url}/api/datasets/#{self.generate_uuid}")
84
+ void_dataset_uri = RDF::URI("#{base_url}/api/datasets/#{generate_uuid}")
77
85
 
78
- void_dataset = evidence.to_void_dataset(void_dataset_uri)
86
+ void_dataset = nanopub.to_void_dataset(void_dataset_uri)
79
87
  unless void_dataset
80
88
  halt(
81
89
  400,
@@ -85,7 +93,7 @@ module OpenBEL
85
93
  end
86
94
 
87
95
  identifier_statement = void_dataset.query(
88
- RDF::Statement.new(void_dataset_uri, RDF::DC.identifier, nil)
96
+ RDF::Statement.new(void_dataset_uri, DC.identifier, nil)
89
97
  ).to_a.first
90
98
  unless identifier_statement
91
99
  halt(
@@ -100,10 +108,10 @@ module OpenBEL
100
108
  )
101
109
  end
102
110
 
103
- datasets = @rr.query_pattern(RDF::Statement.new(nil, RDF.type, RDF::VOID.Dataset))
111
+ datasets = @rr.query_pattern(RDF::Statement.new(nil, RDF.type, VOID.Dataset))
104
112
  existing_dataset = datasets.find { |dataset_statement|
105
113
  @rr.has_statement?(
106
- RDF::Statement.new(dataset_statement.subject, RDF::DC.identifier, identifier_statement.object)
114
+ RDF::Statement.new(dataset_statement.subject, DC.identifier, identifier_statement.object)
107
115
  )
108
116
  }
109
117
 
@@ -131,24 +139,24 @@ module OpenBEL
131
139
 
132
140
  def dataset_exists?(uri)
133
141
  @rr.has_statement?(
134
- RDF::Statement.new(uri, RDF.type, RDF::VOID.Dataset)
142
+ RDF::Statement.new(uri, RDF.type, VOID.Dataset)
135
143
  )
136
144
  end
137
145
 
138
146
  def retrieve_dataset(uri)
139
147
  dataset = {}
140
148
  identifier = @rr.query(
141
- RDF::Statement.new(uri, RDF::DC.identifier, nil)
149
+ RDF::Statement.new(uri, DC.identifier, nil)
142
150
  ).first
143
151
  dataset[:identifier] = identifier.object.to_s if identifier
144
152
 
145
153
  title = @rr.query(
146
- RDF::Statement.new(uri, RDF::DC.title, nil)
154
+ RDF::Statement.new(uri, DC.title, nil)
147
155
  ).first
148
156
  dataset[:title] = title.object.to_s if title
149
157
 
150
158
  description = @rr.query(
151
- RDF::Statement.new(uri, RDF::DC.description, nil)
159
+ RDF::Statement.new(uri, DC.description, nil)
152
160
  ).first
153
161
  dataset[:description] = description.object.to_s if description
154
162
 
@@ -158,22 +166,22 @@ module OpenBEL
158
166
  dataset[:waiver] = waiver.object.to_s if waiver
159
167
 
160
168
  creator = @rr.query(
161
- RDF::Statement.new(uri, RDF::DC.creator, nil)
169
+ RDF::Statement.new(uri, DC.creator, nil)
162
170
  ).first
163
171
  dataset[:creator] = creator.object.to_s if creator
164
172
 
165
173
  license = @rr.query(
166
- RDF::Statement.new(uri, RDF::DC.license, nil)
174
+ RDF::Statement.new(uri, DC.license, nil)
167
175
  ).first
168
176
  dataset[:license] = license.object.to_s if license
169
177
 
170
178
  publisher = @rr.query(
171
- RDF::Statement.new(uri, RDF::DC.publisher, nil)
179
+ RDF::Statement.new(uri, DC.publisher, nil)
172
180
  ).first
173
181
  if publisher
174
182
  publisher.object
175
183
  contact_info = @rr.query(
176
- RDF::Statement.new(publisher.object, RDF::FOAF.mbox, nil)
184
+ RDF::Statement.new(publisher.object, FOAF.mbox, nil)
177
185
  ).first
178
186
  dataset[:contact_info] = contact_info.object.to_s if contact_info
179
187
  end
@@ -240,58 +248,58 @@ the "multipart/form-data" content type. Allowed dataset content types are: #{ACC
240
248
  dataset = retrieve_dataset(void_dataset_uri)
241
249
  dataset_id = dataset[:identifier]
242
250
 
243
- # Add batches of read evidence objects; save to Mongo and RDF.
251
+ # Add batches of read nanopub objects; save to Mongo and RDF.
244
252
  # TODO Add JRuby note regarding Enumerator threading.
245
- evidence_count = 0
246
- evidence_batch = []
253
+ nanopub_count = 0
254
+ nanopub_batch = []
247
255
 
248
256
  # Clear out all facets before loading dataset.
249
257
  @api.delete_facets
250
258
 
251
- BEL.evidence(io, type).each do |ev|
259
+ BEL.nanopub(io, type, :language => @bel_version).each do |nanopub|
252
260
  # Standardize annotations from experiment_context.
253
- @annotation_transform.transform_evidence!(ev, base_url)
254
-
255
- ev.metadata[:dataset] = dataset_id
256
- facets = map_evidence_facets(ev)
257
- ev.bel_statement = ev.bel_statement.to_s
258
- hash = BEL.object_convert(String, ev.to_h) { |str|
259
- str.gsub(/\n/, "\\n").gsub(/\r/, "\\r")
260
- }
261
- hash[:facets] = facets
261
+ @annotation_transform.transform_nanopub!(nanopub, base_url)
262
+
263
+ nanopub.metadata[:dataset] = dataset_id
264
+ facets = map_nanopub_facets(nanopub)
265
+ hash = BEL.object_convert(String, nanopub.to_h) { |str|
266
+ str.gsub(/\n/, "\\n").gsub(/\r/, "\\r")
267
+ }
268
+ hash[:facets] = facets
262
269
  # Create dataset field for efficient removal.
263
- hash[:_dataset] = dataset_id
270
+ hash[:_dataset] = dataset_id
271
+ hash[:bel_statement] = hash[:bel_statement].to_s
264
272
 
265
- evidence_batch << hash
273
+ nanopub_batch << hash
266
274
 
267
- if evidence_batch.size == MONGO_BATCH
268
- _ids = @api.create_evidence(evidence_batch)
275
+ if nanopub_batch.size == MONGO_BATCH
276
+ _ids = @api.create_nanopub(nanopub_batch)
269
277
 
270
278
  dataset_parts = _ids.map { |object_id|
271
- RDF::Statement.new(void_dataset_uri, RDF::DC.hasPart, object_id.to_s)
279
+ RDF::Statement.new(void_dataset_uri, DC.hasPart, object_id.to_s)
272
280
  }
273
281
  @rr.insert_statements(dataset_parts)
274
282
 
275
- evidence_batch.clear
283
+ nanopub_batch.clear
276
284
 
277
285
  # Clear out all facets after FACET_THRESHOLD nanopubs have been seen.
278
- evidence_count += MONGO_BATCH
279
- if evidence_count >= FACET_THRESHOLD
286
+ nanopub_count += MONGO_BATCH
287
+ if nanopub_count >= FACET_THRESHOLD
280
288
  @api.delete_facets
281
- evidence_count = 0
289
+ nanopub_count = 0
282
290
  end
283
291
  end
284
292
  end
285
293
 
286
- unless evidence_batch.empty?
287
- _ids = @api.create_evidence(evidence_batch)
294
+ unless nanopub_batch.empty?
295
+ _ids = @api.create_nanopub(nanopub_batch)
288
296
 
289
297
  dataset_parts = _ids.map { |object_id|
290
- RDF::Statement.new(void_dataset_uri, RDF::DC.hasPart, object_id.to_s)
298
+ RDF::Statement.new(void_dataset_uri, DC.hasPart, object_id.to_s)
291
299
  }
292
300
  @rr.insert_statements(dataset_parts)
293
301
 
294
- evidence_batch.clear
302
+ nanopub_batch.clear
295
303
  end
296
304
 
297
305
  # Clear out all facets after the dataset is completely loaded.
@@ -314,15 +322,15 @@ the "multipart/form-data" content type. Allowed dataset content types are: #{ACC
314
322
  :type => 'dataset',
315
323
  :href => void_dataset_uri.to_s
316
324
  },
317
- :evidence_collection => {
318
- :type => 'evidence_collection',
319
- :href => "#{base_url}/api/datasets/#{id}/evidence"
325
+ :nanopub_collection => {
326
+ :type => 'nanopub_collection',
327
+ :href => "#{base_url}/api/datasets/#{id}/nanopub"
320
328
  }
321
329
  }
322
330
  })
323
331
  end
324
332
 
325
- get '/api/datasets/:id/evidence' do
333
+ get '/api/datasets/:id/nanopub' do
326
334
  id = params[:id]
327
335
  void_dataset_uri = RDF::URI("#{base_url}/api/datasets/#{id}")
328
336
  halt 404 unless dataset_exists?(void_dataset_uri)
@@ -336,12 +344,12 @@ the "multipart/form-data" content type. Allowed dataset content types are: #{ACC
336
344
 
337
345
  filters = validate_filters!
338
346
 
339
- collection_total = @api.count_evidence
340
- filtered_total = @api.count_evidence(filters)
341
- page_results = @api.find_dataset_evidence(dataset, filters, start, size, faceted, max_values_per_facet)
347
+ collection_total = @api.count_nanopub
348
+ filtered_total = @api.count_nanopub(filters)
349
+ page_results = @api.find_dataset_nanopub(dataset, filters, start, size, faceted, max_values_per_facet)
342
350
  name = dataset[:identifier].gsub(/[^\w]/, '_')
343
351
 
344
- render_evidence_collection(
352
+ render_nanopub_collection(
345
353
  name, page_results, start, size, filters,
346
354
  filtered_total, collection_total, @api
347
355
  )
@@ -349,7 +357,7 @@ the "multipart/form-data" content type. Allowed dataset content types are: #{ACC
349
357
 
350
358
  get '/api/datasets' do
351
359
  dataset_uris = @rr.query(
352
- RDF::Statement.new(nil, RDF.type, RDF::VOID.Dataset)
360
+ RDF::Statement.new(nil, RDF.type, VOID.Dataset)
353
361
  ).map { |statement|
354
362
  statement.subject
355
363
  }.to_a
@@ -363,9 +371,9 @@ the "multipart/form-data" content type. Allowed dataset content types are: #{ACC
363
371
  :type => 'dataset',
364
372
  :href => uri.to_s
365
373
  },
366
- :evidence_collection => {
367
- :type => 'evidence_collection',
368
- :href => "#{uri}/evidence"
374
+ :nanopub_collection => {
375
+ :type => 'nanopub_collection',
376
+ :href => "#{uri}/nanopub"
369
377
  }
370
378
  }
371
379
  }
@@ -381,7 +389,7 @@ the "multipart/form-data" content type. Allowed dataset content types are: #{ACC
381
389
  halt 404 unless dataset_exists?(void_dataset_uri)
382
390
 
383
391
  dataset = retrieve_dataset(void_dataset_uri)
384
- # XXX Removes all facets due to load of many evidence.
392
+ # XXX Removes all facets due to load of many nanopub.
385
393
  @api.delete_dataset(dataset[:identifier])
386
394
  @rr.delete_statement(RDF::Statement.new(void_dataset_uri, nil, nil))
387
395
 
@@ -390,7 +398,7 @@ the "multipart/form-data" content type. Allowed dataset content types are: #{ACC
390
398
 
391
399
  delete '/api/datasets' do
392
400
  datasets = @rr.query(
393
- RDF::Statement.new(nil, RDF.type, RDF::VOID.Dataset)
401
+ RDF::Statement.new(nil, RDF.type, VOID.Dataset)
394
402
  ).map { |stmt|
395
403
  stmt.subject
396
404
  }.to_a
@@ -398,31 +406,13 @@ the "multipart/form-data" content type. Allowed dataset content types are: #{ACC
398
406
 
399
407
  datasets.each do |void_dataset_uri|
400
408
  dataset = retrieve_dataset(void_dataset_uri)
401
- # XXX Removes all facets due to load of many evidence.
409
+ # XXX Removes all facets due to load of many nanopub.
402
410
  @api.delete_dataset(dataset[:identifier])
403
411
  @rr.delete_statement(RDF::Statement.new(void_dataset_uri, nil, nil))
404
412
  end
405
413
 
406
414
  status 202
407
415
  end
408
-
409
- private
410
-
411
- unless self.methods.include?(:generate_uuid)
412
-
413
- # Dynamically defines an efficient UUID method for the current ruby.
414
- if RUBY_ENGINE =~ /^jruby/i
415
- java_import 'java.util.UUID'
416
- define_method(:generate_uuid) do
417
- Java::JavaUtil::UUID.random_uuid.to_s
418
- end
419
- else
420
- require 'uuid'
421
- define_method(:generate_uuid) do
422
- UUID.generate
423
- end
424
- end
425
- end
426
416
  end
427
417
  end
428
418
  end
@@ -1,6 +1,11 @@
1
1
  require 'cgi'
2
2
  require 'bel'
3
3
  require 'uri'
4
+ require 'bel_parser/expression/model'
5
+ require 'bel_parser/expression/parser'
6
+ require 'bel_parser/expression/validator'
7
+ require 'bel_parser/resources'
8
+ require 'bel_parser/resource/jena_tdb_reader'
4
9
 
5
10
  module OpenBEL
6
11
  module Routes
@@ -10,6 +15,10 @@ module OpenBEL
10
15
  def initialize(app)
11
16
  super
12
17
 
18
+ # Obtain configured BEL version.
19
+ bel_version = OpenBEL::Settings[:bel][:version]
20
+ @spec = BELParser::Language.specification(bel_version)
21
+
13
22
  # RdfRepository using Jena.
14
23
  @rr = BEL::RdfRepository.plugins[:jena].create_repository(
15
24
  :tdb_directory => OpenBEL::Settings[:resource_rdf][:jena][:tdb_directory]
@@ -25,7 +34,11 @@ module OpenBEL
25
34
  :database_file => OpenBEL::Settings[:resource_search][:sqlite][:database_file]
26
35
  )
27
36
 
28
- @sequence_variation = SequenceVariationFunctionHasLocationPredicate.new
37
+ @expression_validator = BELParser::Expression::Validator.new(
38
+ @spec,
39
+ BELParser::Resources::DEFAULT_NAMESPACES,
40
+ BELParser::Resource.default_uri_reader,
41
+ BELParser::Resource.default_url_reader)
29
42
  end
30
43
 
31
44
  options '/api/expressions/*/completions' do
@@ -43,26 +56,11 @@ module OpenBEL
43
56
  status 200
44
57
  end
45
58
 
46
- options '/api/expressions/*/syntax-validations/?' do
59
+ options '/api/expressions/*/validation-result/?' do
47
60
  response.headers['Allow'] = 'OPTIONS,GET'
48
61
  status 200
49
62
  end
50
63
 
51
- options '/api/expressions/*/semantic-validations/?' do
52
- response.headers['Allow'] = 'OPTIONS,GET'
53
- status 200
54
- end
55
-
56
- # options '/api/expressions/*/ortholog' do
57
- # response.headers['Allow'] = 'OPTIONS,GET'
58
- # status 200
59
- # end
60
-
61
- # options '/api/expressions/*/ortholog/:species' do
62
- # response.headers['Allow'] = 'OPTIONS,GET'
63
- # status 200
64
- # end
65
-
66
64
  helpers do
67
65
 
68
66
  def normalize_relationship(relationship)
@@ -73,22 +71,28 @@ module OpenBEL
73
71
  def statement_components(bel_statement, flatten = false)
74
72
  obj = {}
75
73
  if flatten
76
- obj[:subject] = bel_statement.subject ? bel_statement.subject.to_bel : nil
77
- obj[:relationship] = normalize_relationship(bel_statement.relationship)
78
- obj[:object] = bel_statement.object ? bel_statement.object.to_bel : nil
74
+ obj.merge!({
75
+ :subject => bel_statement.subject ? bel_statement.subject.to_s : nil,
76
+ :relationship => bel_statement.relationship && bel_statement.relationship.long,
77
+ :object => bel_statement.object ? bel_statement.object.to_s : nil
78
+ })
79
79
  else
80
- obj[:subject] = term_components(bel_statement.subject)
81
- obj[:relationship] = normalize_relationship(bel_statement.relationship)
82
- obj[:object] = term_components(bel_statement.object)
80
+ obj.merge!({
81
+ :subject => term_components(bel_statement.subject),
82
+ :relationship => bel_statement.relationship && bel_statement.relationship.to_h,
83
+ :object => term_components(bel_statement.object)
84
+ })
83
85
  end
86
+
84
87
  obj
85
88
  end
86
89
 
87
90
  def arg_components(bel_argument)
88
- if bel_argument.respond_to? :fx
89
- term_components(bel_argument)
90
- elsif bel_argument.respond_to? :ns
91
+ case bel_argument
92
+ when BELParser::Expression::Model::Parameter
91
93
  parameter_components(bel_argument)
94
+ when BELParser::Expression::Model::Term
95
+ term_components(bel_argument)
92
96
  else
93
97
  nil
94
98
  end
@@ -99,7 +103,7 @@ module OpenBEL
99
103
 
100
104
  {
101
105
  :term => {
102
- :fx => bel_term.fx,
106
+ :function => bel_term.function.to_h,
103
107
  :arguments => bel_term.arguments.map { |a| arg_components(a) }
104
108
  }
105
109
  }
@@ -107,14 +111,39 @@ module OpenBEL
107
111
 
108
112
  def parameter_components(bel_parameter)
109
113
  return nil unless bel_parameter
114
+ namespace = bel_parameter.namespace && bel_parameter.namespace.to_s
110
115
 
111
116
  {
112
117
  :parameter => {
113
- :ns => bel_parameter.ns ? bel_parameter.ns.prefix : nil,
118
+ :namespace => namespace,
114
119
  :value => bel_parameter.value.to_s
115
120
  }
116
121
  }
117
122
  end
123
+
124
+ def syntax_results(results)
125
+ results.select do |res|
126
+ res.is_a? BELParser::Language::Syntax::SyntaxResult
127
+ end
128
+ end
129
+
130
+ def semantics_results(results)
131
+ results.select do |res|
132
+ res.is_a? BELParser::Language::Semantics::SemanticsResult
133
+ end
134
+ end
135
+
136
+ def successfully_matched_signatures(results)
137
+ results.select do |res|
138
+ res.is_a?(BELParser::Language::Semantics::SignatureMappingSuccess)
139
+ end
140
+ end
141
+
142
+ def signature_warnings(results)
143
+ results.select do |res|
144
+ res.is_a?(BELParser::Language::Semantics::SignatureMappingWarning)
145
+ end
146
+ end
118
147
  end
119
148
 
120
149
  get '/api/expressions/*/completions/?' do
@@ -123,7 +152,7 @@ module OpenBEL
123
152
  halt 400 unless bel and caret_position
124
153
 
125
154
  begin
126
- completions = BEL::Completion.complete(bel, @search, @namespaces, caret_position)
155
+ completions = BEL::Completion.complete(bel, @spec, @search, @namespaces, caret_position)
127
156
  rescue IndexError => ex
128
157
  halt(
129
158
  400,
@@ -145,9 +174,10 @@ module OpenBEL
145
174
  bel = params[:splat].first
146
175
  flatten = as_bool(params[:flatten])
147
176
 
148
- statement = BEL::Script.parse(bel).find { |obj|
149
- obj.is_a? BEL::Model::Statement
150
- }
177
+ statement =
178
+ BELParser::Expression.parse_statements(
179
+ bel,
180
+ @spec)
151
181
  halt 404 unless statement
152
182
 
153
183
  response.headers['Content-Type'] = 'application/json'
@@ -163,23 +193,27 @@ module OpenBEL
163
193
  flatten = as_bool(params[:flatten])
164
194
  inner_terms = as_bool(params[:inner_terms])
165
195
 
166
- terms = BEL::Script.parse(bel).select { |obj|
167
- obj.is_a? BEL::Model::Term
168
- }
196
+ terms =
197
+ BELParser::Expression.parse_terms(
198
+ bel,
199
+ @spec)
200
+ halt 404 if terms.empty?
169
201
 
170
202
  if !functions.empty?
171
203
  functions = functions.map(&:to_sym)
172
- terms = terms.select { |term|
173
- functions.any? { |match|
174
- term.fx.short_form == match || term.fx.long_form == match
175
- }
176
- }
204
+ terms =
205
+ terms.select do |term|
206
+ functions.any? { |match| term.function === match }
207
+ end
177
208
  end
178
209
 
179
210
  if inner_terms
180
- terms = terms.flat_map { |term|
181
- term.arguments.select { |arg| arg.is_a? BEL::Model::Term }
182
- }
211
+ terms =
212
+ terms.flat_map do |term|
213
+ term.arguments.select do |arg|
214
+ arg.is_a?(BELParser::Expression::Model::Term)
215
+ end
216
+ end
183
217
  end
184
218
 
185
219
  terms = terms.to_a
@@ -188,7 +222,7 @@ module OpenBEL
188
222
  response.headers['Content-Type'] = 'application/json'
189
223
  if flatten
190
224
  MultiJson.dump({
191
- :terms => terms.map { |term| term.to_bel }
225
+ :terms => terms.map { |term| term.to_s }
192
226
  })
193
227
  else
194
228
  MultiJson.dump({
@@ -197,360 +231,16 @@ module OpenBEL
197
231
  end
198
232
  end
199
233
 
200
- # TODO Relies on LibBEL.bel_parse_statement which is not currently supported.
201
- # get '/api/expressions/*/ortholog/:species' do
202
- # bel = params[:splat].first
203
- # species = params[:species]
204
- # taxon_annotation = @annotations.find('taxon').first
205
- #
206
- # unless taxon_annotation
207
- # halt(
208
- # 404,
209
- # { 'Content-Type' => 'application/json' },
210
- # render_json({
211
- # :status => 404,
212
- # :msg => 'Could not find NCBI Taxonomy annotation.'
213
- # })
214
- # )
215
- # end
216
- #
217
- # species = taxon_annotation.find(species).first
218
- #
219
- # if species
220
- # species = species.identifier.to_s
221
- # else
222
- # halt(
223
- # 400,
224
- # { 'Content-Type' => 'application/json' },
225
- # render_json({
226
- # :status => 400,
227
- # :msg => %Q{Could not find species "#{params[:species]}"}
228
- # })
229
- # )
230
- # end
231
- #
232
- # bel_ast = BEL::Parser.parse(bel)
233
- #
234
- # if bel_ast.any?([@sequence_variation])
235
- # msg = 'Could not orthologize sequence variation terms with location'
236
- # halt(
237
- # 404,
238
- # { 'Content-Type' => 'application/json' },
239
- # render_json({
240
- # :status => 404,
241
- # :msg => msg
242
- # })
243
- # )
244
- # end
245
- #
246
- # param_transform = ParameterOrthologTransform.new(
247
- # @namespaces, @annotations, species
248
- # )
249
- # transformed_ast = bel_ast.transform_tree([param_transform])
250
- #
251
- # if !param_transform.parameter_errors.empty?
252
- # parameters = param_transform.parameter_errors.map { |p|
253
- # p.join(':')
254
- # }.join(', ')
255
- # halt(
256
- # 404,
257
- # { 'Content-Type' => 'application/json' },
258
- # render_json({
259
- # :status => 404,
260
- # :msg => "Could not orthologize #{parameters}"
261
- # })
262
- # )
263
- # end
264
- #
265
- # # serialize AST to BEL
266
- # bel_serialization = BELSerializationTransform.new
267
- # transformed_ast.transform_tree([bel_serialization])
268
- #
269
- # # write response
270
- # response.headers['Content-Type'] = 'application/json'
271
- # MultiJson.dump({
272
- # :original => bel,
273
- # :species => params[:species],
274
- # :orthologized => bel_serialization.bel_string
275
- # })
276
- # end
277
-
278
- # BEL Syntax Validation
279
- # TODO Move out to a separate route.
280
- get '/api/expressions/*/syntax-validations/?' do
281
- halt 501
282
- end
283
-
284
- # BEL Semantic Validations
285
- # TODO Move out to a separate route.
286
- get '/api/expressions/*/semantic-validations/?' do
287
- halt 501
288
- end
289
-
290
- class SequenceVariationFunctionHasLocationPredicate
291
- include BEL::LibBEL
292
-
293
- SEQUENCE_VARIATION_FX = [
294
- 'fus', 'fusion',
295
- 'pmod', 'proteinModification',
296
- 'sub', 'substitution',
297
- 'trunc', 'truncation'
298
- ]
299
-
300
- def call(ast_node)
301
- # check if AST node is a TERM
302
- if !ast_node.is_a?(BelAstNodeToken) || ast_node.token_type != :BEL_TOKEN_TERM
303
- return false
304
- end
305
-
306
- # check if AST node is a pmod TERM
307
- if !SEQUENCE_VARIATION_FX.include?(ast_node.left.to_typed_node.value)
308
- return false
309
- end
310
-
311
- # walk arg AST nodes until terminal
312
- arg_node = ast_node.right.to_typed_node
313
- while !(arg_node.left.pointer.null? && arg_node.right.pointer.null?)
314
- # check if NV token child
315
- arg_token = arg_node.left.to_typed_node
316
-
317
- if arg_token.token_type == :BEL_TOKEN_NV
318
- # true if namespace value is an integer
319
- node_value = arg_token.right.to_typed_node.value
320
- if integer?(node_value)
321
- return true
322
- end
323
- end
324
-
325
- # advance to the next ARG
326
- arg_node = arg_node.right.to_typed_node
327
- end
328
-
329
- return false
330
- end
331
-
332
- private
333
-
334
- def integer?(value)
335
- begin
336
- Integer(value)
337
- return true
338
- rescue ArgumentError
339
- return false
340
- end
341
- end
342
- end
343
-
344
- class ParameterOrthologTransform
345
- include BEL::LibBEL
346
-
347
- NAMESPACE_PREFERENCE = [
348
- "hgnc",
349
- "mgi",
350
- "rgd",
351
- "gocc",
352
- "scomp",
353
- "meshcs",
354
- "sfam",
355
- "gobp",
356
- "meshpp",
357
- "chebi",
358
- "schem",
359
- "do",
360
- "meshd",
361
- "sdis",
362
- "sp",
363
- "affx",
364
- "egid",
365
- ]
366
-
367
- def initialize(namespaces, annotations, species_tax_id)
368
- @namespaces = namespaces
369
- @orthology = OrthologAdapter.new(
370
- namespaces, species_tax_id
371
- )
372
- @species_tax_id = species_tax_id
373
- @parameter_errors = []
374
- end
375
-
376
- def parameter_errors
377
- @parameter_errors.uniq
378
- end
379
-
380
- def call(ast_node)
381
- if ast_node.is_a?(BelAstNodeToken) &&
382
- ast_node.token_type == :BEL_TOKEN_NV
383
-
384
- ns_value = [
385
- ast_node.left.to_typed_node.value,
386
- ast_node.right.to_typed_node.value
387
- ]
388
- orthologs = @orthology[ns_value]
389
- if !orthologs.empty?
390
- orthologs.sort_by! { |ortholog| namespace_preference(ortholog) }
391
- ortholog = orthologs.first
392
- BEL::LibBEL::bel_free_ast_node(ast_node.left.pointer)
393
- ast_node.left = BelAstNode.new(
394
- bel_new_ast_node_value(:BEL_VALUE_PFX, ortholog[0].upcase)
395
- )
396
-
397
- BEL::LibBEL::bel_free_ast_node(ast_node.right.pointer)
398
- ast_node.right = BelAstNode.new(
399
- bel_new_ast_node_value(:BEL_VALUE_VAL, ortholog[1])
400
- )
401
- else
402
- # flag as ortholog error if this parameter has a namespace and
403
- # the namespace value is either not known or its species differs
404
- # from our target
405
- if ns_value[0] != nil
406
- namespace, value = ns_value
407
- namespace = @namespaces.find(namespace).first
408
- if namespace
409
- value = namespace.find(value).first
410
- if !value || value.fromSpecies != @species_tax_id
411
- @parameter_errors << value
412
- end
413
- end
414
- end
415
- end
416
- end
417
- end
418
-
419
- private
420
-
421
- def namespace_preference(ortholog)
422
- NAMESPACE_PREFERENCE.index(ortholog[0])
423
- end
424
- end
425
-
426
- class BELSerializationTransform
427
- include BEL::LibBEL
428
-
429
- attr_reader :bel_string
430
-
431
- def initialize
432
- @bel_string = ""
433
- end
434
-
435
- def call(ast_node)
436
- if ast_node.is_a?(BelAstNodeToken)
437
- case ast_node.token_type
438
- when :BEL_TOKEN_STATEMENT
439
- when :BEL_TOKEN_SUBJECT
440
- when :BEL_TOKEN_OBJECT
441
- when :BEL_TOKEN_REL
442
- when :BEL_TOKEN_TERM
443
- when :BEL_TOKEN_ARG
444
- when :BEL_TOKEN_NV
445
- end
446
- else
447
- case ast_node.value_type
448
- when :BEL_VALUE_FX
449
- function(ast_node.value)
450
- when :BEL_VALUE_REL
451
- relationship(ast_node.value)
452
- when :BEL_VALUE_PFX
453
- namespace_prefix(ast_node.value)
454
- when :BEL_VALUE_VAL
455
- namespace_value(ast_node.value)
456
- end
457
- end
458
- end
459
-
460
- def between(ast_node)
461
- if ast_node.is_a?(BelAstNodeToken)
462
- case ast_node.token_type
463
- when :BEL_TOKEN_STATEMENT
464
- when :BEL_TOKEN_SUBJECT
465
- when :BEL_TOKEN_OBJECT
466
- when :BEL_TOKEN_REL
467
- when :BEL_TOKEN_TERM
468
- when :BEL_TOKEN_ARG
469
- token_arg(ast_node)
470
- when :BEL_TOKEN_NV
471
- end
472
- end
473
- end
474
-
475
- def after(ast_node)
476
- if ast_node.is_a?(BelAstNodeToken)
477
- case ast_node.token_type
478
- when :BEL_TOKEN_STATEMENT
479
- when :BEL_TOKEN_SUBJECT
480
- when :BEL_TOKEN_OBJECT
481
- when :BEL_TOKEN_REL
482
- when :BEL_TOKEN_TERM
483
- @bel_string.concat(')')
484
- when :BEL_TOKEN_ARG
485
- when :BEL_TOKEN_NV
486
- end
487
- end
488
- end
489
-
490
- private
491
-
492
- def token_arg(ast_node)
493
- chained_arg_node = ast_node.right
494
- if !chained_arg_node.pointer.null?
495
- chained_arg_node = chained_arg_node.to_typed_node
496
- if !chained_arg_node.left.pointer.null? ||
497
- !chained_arg_node.right.pointer.null?
498
- @bel_string.concat(', ')
499
- end
500
- end
501
- end
502
-
503
- def function(fx)
504
- @bel_string.concat(fx).concat('(')
505
- end
506
-
507
- def relationship(rel)
508
- @bel_string.concat(" #{rel} ")
509
- end
510
-
511
- def namespace_prefix(prefix)
512
- return unless prefix
513
- @bel_string.concat(prefix).concat(':')
514
- end
515
-
516
- def namespace_value(value)
517
- @bel_string.concat(value)
518
- end
519
- end
520
-
521
- # Hash-like
522
- class OrthologAdapter
523
-
524
- EMPTY = [].freeze
525
-
526
- def initialize(namespaces, species_tax_id)
527
- @namespaces = namespaces
528
- @species_tax_id = species_tax_id
529
- end
530
-
531
- def [](key)
532
- namespace, value = key
234
+ # Produce validation result for BEL expression using the current language.
235
+ get '/api/expressions/*/validation/?' do
236
+ bel = params[:splat].first
237
+ response.headers['Content-Type'] = 'text/plain'
533
238
 
534
- if value.start_with?('"') && value.end_with?('"')
535
- value = value[1...-1]
536
- end
239
+ @expression_validator.each(
240
+ StringIO.new("#{bel}\n")
241
+ ) do |(num, line, ast, result)|
537
242
 
538
- namespace = @namespaces.find(namespace).first
539
- return EMPTY unless namespace
540
- value = namespace.find(value).first
541
- return EMPTY unless value
542
- orthologs = value.orthologs.select { |orth|
543
- orth.fromSpecies == @species_tax_id
544
- }.to_a
545
- return EMPTY if orthologs.empty?
546
-
547
- orthologs.map! { |ortholog_value|
548
- [
549
- ortholog_value.namespace.prefix,
550
- ortholog_value.prefLabel
551
- ]
552
- }
553
- orthologs
243
+ return result.to_s
554
244
  end
555
245
  end
556
246
  end