openbel-api 0.6.2-java → 1.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
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