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.
- checksums.yaml +4 -4
- data/.gemspec +11 -14
- data/CHANGELOG.md +18 -12
- data/README.md +25 -36
- data/VERSION +1 -1
- data/app/openbel/api/app.rb +13 -12
- data/app/openbel/api/config.rb +53 -11
- data/app/openbel/api/helpers/{evidence.rb → nanopub.rb} +13 -14
- data/app/openbel/api/middleware/auth.rb +22 -29
- data/app/openbel/api/resources/annotation.rb +7 -7
- data/app/openbel/api/resources/function.rb +12 -35
- data/app/openbel/api/resources/namespace.rb +13 -13
- data/app/openbel/api/resources/{evidence.rb → nanopub.rb} +23 -23
- data/app/openbel/api/resources/{evidence_transform.rb → nanopub_transform.rb} +8 -8
- data/app/openbel/api/resources/relationship.rb +74 -0
- data/app/openbel/api/routes/annotations.rb +1 -1
- data/app/openbel/api/routes/base.rb +11 -7
- data/app/openbel/api/routes/datasets.rb +74 -84
- data/app/openbel/api/routes/expressions.rb +86 -396
- data/app/openbel/api/routes/language.rb +118 -0
- data/app/openbel/api/routes/namespaces.rb +2 -2
- data/app/openbel/api/routes/{evidence.rb → nanopubs.rb} +68 -69
- data/app/openbel/api/routes/root.rb +2 -2
- data/app/openbel/api/routes/version.rb +37 -23
- data/app/openbel/api/schemas/annotation_resource.schema.json +1 -1
- data/app/openbel/api/schemas/{evidence.schema.json → nanopub.schema.json} +10 -10
- data/app/openbel/api/schemas/{evidence_collection.schema.json → nanopub_collection.schema.json} +5 -5
- data/app/openbel/api/schemas/{evidence_resource.schema.json → nanopub_resource.schema.json} +4 -4
- data/config/config.yml +15 -5
- data/lib/openbel/api/helpers/uuid_generator.rb +22 -0
- data/lib/openbel/api/{evidence → nanopub}/api.rb +9 -9
- data/lib/openbel/api/{evidence → nanopub}/facet_api.rb +2 -2
- data/lib/openbel/api/{evidence → nanopub}/facet_filter.rb +6 -6
- data/lib/openbel/api/{evidence → nanopub}/mongo.rb +54 -52
- data/lib/openbel/api/{evidence → nanopub}/mongo_facet.rb +17 -28
- data/lib/openbel/api/plugin/{evidence/evidence.rb → nanopub/nanopub.rb} +7 -7
- metadata +44 -56
- data/app/openbel/api/routes/functions.rb +0 -41
@@ -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/
|
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::
|
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
|
-
:
|
46
|
-
:
|
47
|
-
:
|
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
|
118
|
+
def read_nanopub
|
115
119
|
halt 415 unless ::BEL.translator(request.media_type)
|
116
|
-
::BEL.
|
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/
|
7
|
-
require 'openbel/api/
|
8
|
-
|
9
|
-
require_relative '../
|
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::
|
18
|
-
include OpenBEL::Resource::
|
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
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
74
|
+
nanopub = BEL.nanopub(io, type).each.first
|
67
75
|
|
68
|
-
unless
|
76
|
+
unless nanopub
|
69
77
|
halt(
|
70
78
|
400,
|
71
79
|
{ 'Content-Type' => 'application/json' },
|
72
|
-
render_json({ :status => 400, :msg => 'No BEL
|
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/#{
|
84
|
+
void_dataset_uri = RDF::URI("#{base_url}/api/datasets/#{generate_uuid}")
|
77
85
|
|
78
|
-
void_dataset =
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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
|
251
|
+
# Add batches of read nanopub objects; save to Mongo and RDF.
|
244
252
|
# TODO Add JRuby note regarding Enumerator threading.
|
245
|
-
|
246
|
-
|
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.
|
259
|
+
BEL.nanopub(io, type, :language => @bel_version).each do |nanopub|
|
252
260
|
# Standardize annotations from experiment_context.
|
253
|
-
@annotation_transform.
|
254
|
-
|
255
|
-
|
256
|
-
facets
|
257
|
-
|
258
|
-
|
259
|
-
|
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]
|
270
|
+
hash[:_dataset] = dataset_id
|
271
|
+
hash[:bel_statement] = hash[:bel_statement].to_s
|
264
272
|
|
265
|
-
|
273
|
+
nanopub_batch << hash
|
266
274
|
|
267
|
-
if
|
268
|
-
_ids = @api.
|
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,
|
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
|
-
|
283
|
+
nanopub_batch.clear
|
276
284
|
|
277
285
|
# Clear out all facets after FACET_THRESHOLD nanopubs have been seen.
|
278
|
-
|
279
|
-
if
|
286
|
+
nanopub_count += MONGO_BATCH
|
287
|
+
if nanopub_count >= FACET_THRESHOLD
|
280
288
|
@api.delete_facets
|
281
|
-
|
289
|
+
nanopub_count = 0
|
282
290
|
end
|
283
291
|
end
|
284
292
|
end
|
285
293
|
|
286
|
-
unless
|
287
|
-
_ids = @api.
|
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,
|
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
|
-
|
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
|
-
:
|
318
|
-
:type => '
|
319
|
-
:href => "#{base_url}/api/datasets/#{id}/
|
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/
|
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.
|
340
|
-
filtered_total = @api.
|
341
|
-
page_results = @api.
|
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
|
-
|
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,
|
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
|
-
:
|
367
|
-
:type => '
|
368
|
-
:href => "#{uri}/
|
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
|
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,
|
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
|
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
|
-
@
|
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/*/
|
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
|
77
|
-
|
78
|
-
|
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
|
81
|
-
|
82
|
-
|
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
|
-
|
89
|
-
|
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
|
-
:
|
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
|
-
:
|
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 =
|
149
|
-
|
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 =
|
167
|
-
|
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 =
|
173
|
-
|
174
|
-
|
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 =
|
181
|
-
|
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.
|
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
|
-
#
|
201
|
-
|
202
|
-
|
203
|
-
|
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
|
-
|
535
|
-
|
536
|
-
|
239
|
+
@expression_validator.each(
|
240
|
+
StringIO.new("#{bel}\n")
|
241
|
+
) do |(num, line, ast, result)|
|
537
242
|
|
538
|
-
|
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
|