infermedica 0.0.1
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 +7 -0
- data/.gitignore +7 -0
- data/examples/condition.rb +46 -0
- data/examples/diagnosis.rb +43 -0
- data/examples/explain.rb +42 -0
- data/examples/info.rb +17 -0
- data/examples/lab_test.rb +42 -0
- data/examples/risk_factor.rb +45 -0
- data/examples/search.rb +16 -0
- data/examples/symptom.rb +49 -0
- data/infermedica.gemspec +15 -0
- data/lib/infermedica/conditions.rb +13 -0
- data/lib/infermedica/connection.rb +80 -0
- data/lib/infermedica/diagnosis.rb +73 -0
- data/lib/infermedica/info.rb +16 -0
- data/lib/infermedica/lab_tests.rb +12 -0
- data/lib/infermedica/model.rb +16 -0
- data/lib/infermedica/risk_factors.rb +12 -0
- data/lib/infermedica/symptoms.rb +13 -0
- data/lib/infermedica.rb +170 -0
- data/test/Rakefile +9 -0
- data/test/unit/condition.rb +39 -0
- data/test/unit/diagnosis.rb +44 -0
- data/test/unit/info.rb +27 -0
- data/test/unit/lab_test.rb +30 -0
- data/test/unit/risk_factor.rb +31 -0
- data/test/unit/symptom.rb +46 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 24b61b9bf801f88cc86903f5234fccdf80990856
|
4
|
+
data.tar.gz: e8a6749ee08a349c3b15a06113767d4b0e47354f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c2647ded5c5f09e2ff7b58b2f9c23e99fb8fdbee3c6dcef4dee11b4e410a1c64b1875fc6a138138b6c8ec5fe74e7a6eaf12d4e0ea2a1b3d15c9172492b42e67f
|
7
|
+
data.tar.gz: 671d82890a14f988e08d2fba0fa35a72def0a460ee2e77419b9bd4fda71c11562d93073c51264632f5d39f910ec11f7729a82097945ba0deeb823cfaa2132e37
|
data/.gitignore
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'yaml'
|
3
|
+
require 'infermedica'
|
4
|
+
require 'byebug'
|
5
|
+
|
6
|
+
access = YAML.load(File.read('./config.yaml'))
|
7
|
+
api = Infermedica::Api.new(access)
|
8
|
+
|
9
|
+
def dump_condition(cond)
|
10
|
+
puts "Id: #{cond.id}"
|
11
|
+
puts "Name: #{cond.name}"
|
12
|
+
puts "Categories: #{cond.categories}"
|
13
|
+
puts "Prevalence: #{cond.prevalence}"
|
14
|
+
puts "Acuteness: #{cond.acuteness}"
|
15
|
+
puts "Severity: #{cond.severity}"
|
16
|
+
puts "Extras: #{cond.extras}"
|
17
|
+
puts "Sex filter: #{cond.sex_filter}"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Look for a specific condition
|
21
|
+
|
22
|
+
cond = api.get_condition('c_537')
|
23
|
+
dump_condition(cond)
|
24
|
+
|
25
|
+
# Look for a non existent condition
|
26
|
+
|
27
|
+
begin
|
28
|
+
cond = api.get_condition('fail_test')
|
29
|
+
rescue Infermedica::HttpError => e
|
30
|
+
puts
|
31
|
+
puts e
|
32
|
+
puts
|
33
|
+
end
|
34
|
+
|
35
|
+
# Get hash of all conditions.
|
36
|
+
# Note that this is not an Hash of Condition objects, but a hash of
|
37
|
+
# hashes, with the condition id as key
|
38
|
+
|
39
|
+
conditions = api.get_conditions
|
40
|
+
keys = conditions.keys
|
41
|
+
puts "The API currently has #{keys.size} conditions"
|
42
|
+
|
43
|
+
# Create an Infermedica::Condition object from one of the hash entry
|
44
|
+
|
45
|
+
my_cond = Infermedica::Condition.new(conditions['c_537'])
|
46
|
+
dump_condition(my_cond)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'yaml'
|
3
|
+
require 'infermedica'
|
4
|
+
|
5
|
+
access = YAML.load(File.read('./config.yaml'))
|
6
|
+
api = Infermedica::Api.new(access)
|
7
|
+
|
8
|
+
# Create a diagnosis object with some initial patient info
|
9
|
+
|
10
|
+
diag = Infermedica::Diagnosis.new(sex: 'male', age: '35')
|
11
|
+
|
12
|
+
diag.add_symptom('s_21', 'present')
|
13
|
+
diag.add_symptom('s_98', 'present')
|
14
|
+
diag.add_symptom('s_107', 'present')
|
15
|
+
|
16
|
+
diag.add_pursued_conditions(['c_33', 'c_49']) # Optional
|
17
|
+
|
18
|
+
# Call the diagnosis endpoint
|
19
|
+
|
20
|
+
resp = api.diagnosis(diag)
|
21
|
+
|
22
|
+
# update diag with answers to questions in resp
|
23
|
+
|
24
|
+
diag.add_symptom('s_99', 'present')
|
25
|
+
diag.add_symptom('s_8', 'absent')
|
26
|
+
diag.add_symptom('s_25', 'present')
|
27
|
+
|
28
|
+
# Call diagnosis with updated symptoms
|
29
|
+
|
30
|
+
resp = api.diagnosis(diag)
|
31
|
+
|
32
|
+
diag.add_symptom('s_1193', 'present')
|
33
|
+
diag.add_symptom('s_604', 'present')
|
34
|
+
diag.add_symptom('s_23', 'absent')
|
35
|
+
|
36
|
+
resp = api.diagnosis(diag)
|
37
|
+
|
38
|
+
diag.add_symptom('s_122', 'absent')
|
39
|
+
resp = api.diagnosis(diag)
|
40
|
+
|
41
|
+
# Print the resulting data structure
|
42
|
+
|
43
|
+
pp resp
|
data/examples/explain.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'yaml'
|
3
|
+
require 'infermedica'
|
4
|
+
|
5
|
+
access = YAML.load(File.read('./config.yaml'))
|
6
|
+
api = Infermedica::Api.new(access)
|
7
|
+
|
8
|
+
# Create a diagnosis object
|
9
|
+
|
10
|
+
req = Infermedica::Diagnosis.new(sex: 'female', age: '35')
|
11
|
+
|
12
|
+
# Add some symptoms
|
13
|
+
|
14
|
+
req.add_symptom('s_10', 'present')
|
15
|
+
req.add_symptom('s_608', 'present')
|
16
|
+
req.add_symptom('s_1394', 'absent')
|
17
|
+
req.add_symptom('s_216', 'present')
|
18
|
+
req.add_symptom('s_9', 'present')
|
19
|
+
req.add_symptom('s_188', 'present')
|
20
|
+
|
21
|
+
# Can't get a explaination unless we have a target
|
22
|
+
|
23
|
+
begin
|
24
|
+
resp = api.explain(req)
|
25
|
+
rescue Infermedica::MissingField => e
|
26
|
+
puts
|
27
|
+
puts e
|
28
|
+
puts
|
29
|
+
end
|
30
|
+
|
31
|
+
# Add a target condition
|
32
|
+
|
33
|
+
req.add_target('c_371')
|
34
|
+
|
35
|
+
# See if the symptoms fit the condition
|
36
|
+
|
37
|
+
resp = api.explain(req)
|
38
|
+
|
39
|
+
# Print the resulting data structure
|
40
|
+
|
41
|
+
pp resp
|
42
|
+
|
data/examples/info.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'yaml'
|
3
|
+
require 'infermedica'
|
4
|
+
|
5
|
+
access = YAML.load(File.read('./config.yaml'))
|
6
|
+
api = Infermedica::Api.new(access)
|
7
|
+
|
8
|
+
# Get info about the API
|
9
|
+
|
10
|
+
info = api.get_info
|
11
|
+
|
12
|
+
puts "API updated at: #{info.updated_at}"
|
13
|
+
puts "Condition count: #{info.conditions_count}"
|
14
|
+
puts "Lab test count: #{info.lab_tests_count}"
|
15
|
+
puts "Risk factor count: #{info.risk_factors_count}"
|
16
|
+
puts "Symptom count: #{info.symptoms_count}"
|
17
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'yaml'
|
3
|
+
require 'infermedica'
|
4
|
+
require 'byebug'
|
5
|
+
|
6
|
+
access = YAML.load(File.read('./config.yaml'))
|
7
|
+
api = Infermedica::Api.new(access)
|
8
|
+
|
9
|
+
def dump_lab_test(lt)
|
10
|
+
puts "Id: #{lt.id}"
|
11
|
+
puts "Name: #{lt.name}"
|
12
|
+
puts "Category: #{lt.category}"
|
13
|
+
puts "Result: #{lt.results}"
|
14
|
+
end
|
15
|
+
|
16
|
+
# Look for a specific lab test
|
17
|
+
|
18
|
+
lt = api.get_lab_test('lt_81')
|
19
|
+
dump_lab_test(lt)
|
20
|
+
|
21
|
+
# Look for a non existent symptom
|
22
|
+
|
23
|
+
begin
|
24
|
+
sym = api.get_lab_test('fail_test')
|
25
|
+
rescue Infermedica::HttpError => e
|
26
|
+
puts
|
27
|
+
puts e
|
28
|
+
puts
|
29
|
+
end
|
30
|
+
|
31
|
+
# Get a Hash of all lab tests
|
32
|
+
# Note that this is not an Hash of LabTest objects, but a hash of
|
33
|
+
# hashes, with the lab test id as key
|
34
|
+
|
35
|
+
lts = api.get_lab_tests
|
36
|
+
keys = lts.keys
|
37
|
+
puts "The API currently has #{keys.size} lab tests"
|
38
|
+
|
39
|
+
# Create an Infermedica::LabTest object from one of the hash entry
|
40
|
+
|
41
|
+
my_lt = Infermedica::LabTest.new(lts['lt_81'])
|
42
|
+
dump_lab_test(my_lt)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'yaml'
|
3
|
+
require 'infermedica'
|
4
|
+
require 'byebug'
|
5
|
+
|
6
|
+
access = YAML.load(File.read('./config.yaml'))
|
7
|
+
api = Infermedica::Api.new(access)
|
8
|
+
|
9
|
+
def dump_risk_factor(rf)
|
10
|
+
puts "Id: #{rf.id}"
|
11
|
+
puts "Name: #{rf.name}"
|
12
|
+
puts "Category: #{rf.category}"
|
13
|
+
puts "Extras: #{rf.extras}"
|
14
|
+
puts "Sex filter: #{rf.sex_filter}"
|
15
|
+
puts "Image url: #{rf.image_url}"
|
16
|
+
puts "Image source: #{rf.image_source}"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Look for a specific risk factor
|
20
|
+
|
21
|
+
rf = api.get_risk_factor('p_37')
|
22
|
+
dump_risk_factor(rf)
|
23
|
+
|
24
|
+
# Look for a non existent risk factor
|
25
|
+
|
26
|
+
begin
|
27
|
+
sym = api.get_risk_factor('fail_test')
|
28
|
+
rescue Infermedica::HttpError => e
|
29
|
+
puts
|
30
|
+
puts e
|
31
|
+
puts
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get a Hash of all risk factors
|
35
|
+
# Note that this is not a Hash of LabTest objects, but a hash of
|
36
|
+
# hashes, with the lab test id as key
|
37
|
+
|
38
|
+
rfs = api.get_risk_factors
|
39
|
+
keys = rfs.keys
|
40
|
+
puts "The API currently has #{keys.size} risk factors"
|
41
|
+
|
42
|
+
# Create an Infermedica::RiskFactor object from one of the hash entry
|
43
|
+
|
44
|
+
my_rf = Infermedica::RiskFactor.new(rfs['p_37'])
|
45
|
+
dump_risk_factor(my_rf)
|
data/examples/search.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'yaml'
|
3
|
+
require 'infermedica'
|
4
|
+
|
5
|
+
access = YAML.load(File.read('./config.yaml'))
|
6
|
+
api = Infermedica::Api.new(access)
|
7
|
+
|
8
|
+
puts 'Look for evidences containing phrase headache:'
|
9
|
+
puts api.search('headache')
|
10
|
+
puts 'Look for evidences containing phrase breast, only for female symptoms:'
|
11
|
+
puts api.search('breast', sex: 'female')
|
12
|
+
puts 'Same, but only 5 results'
|
13
|
+
puts api.search('breast', sex: 'female', max_results: 5)
|
14
|
+
puts 'With filters now. Only search in symptom and risk_factor'
|
15
|
+
puts api.search('trauma', filters: ['symptom', 'risk_factor'])
|
16
|
+
|
data/examples/symptom.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'yaml'
|
3
|
+
require 'infermedica'
|
4
|
+
require 'byebug'
|
5
|
+
|
6
|
+
access = YAML.load(File.read('./config.yaml'))
|
7
|
+
api = Infermedica::Api.new(access)
|
8
|
+
|
9
|
+
def dump_symptom(s)
|
10
|
+
puts "Id: #{s.id}"
|
11
|
+
puts "Name: #{s.name}"
|
12
|
+
puts "Category: #{s.category}"
|
13
|
+
puts "Children: #{s.children}"
|
14
|
+
puts "Extras: #{s.extras}"
|
15
|
+
puts "Image Source: #{s.image_source}"
|
16
|
+
puts "Image url: #{s.image_url}"
|
17
|
+
puts "Parent Id: #{s.parent_id}"
|
18
|
+
puts "Parent Relation: #{s.parent_relation}"
|
19
|
+
puts "Sex filter: #{s.sex_filter}"
|
20
|
+
puts "Question: #{s.question}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Look for a specific symptom
|
24
|
+
|
25
|
+
sym = api.get_symptom('s_56')
|
26
|
+
dump_symptom(sym)
|
27
|
+
|
28
|
+
# Look for a non existent symptom
|
29
|
+
|
30
|
+
begin
|
31
|
+
sym = api.get_symptom('fail_test')
|
32
|
+
rescue Infermedica::HttpError => e
|
33
|
+
puts
|
34
|
+
puts e
|
35
|
+
puts
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get a Hash of all symptoms
|
39
|
+
# Note that this is not a Hash of symptom objects, but a hash of
|
40
|
+
# hashes, with the symptom id as key
|
41
|
+
|
42
|
+
symptoms = api.get_symptoms
|
43
|
+
keys = symptoms.keys
|
44
|
+
puts "The API currently has #{keys.size} symptoms"
|
45
|
+
|
46
|
+
# Create an Infermedica::Symptom object from one of the hash entry
|
47
|
+
|
48
|
+
my_sym = Infermedica::Symptom.new(symptoms['s_551'])
|
49
|
+
dump_symptom(my_sym)
|
data/infermedica.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'infermedica'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.date = '2017-01-09'
|
5
|
+
s.summary = 'Provide a Ruby interface to the Infermedica REST API'
|
6
|
+
s.description = 'A Ruby interface to the Infermedica REST API'
|
7
|
+
s.authors = ['Bruno Melli']
|
8
|
+
s.email = 'mjskier@tegali.com'
|
9
|
+
s.files = `git ls-files`.split($\)
|
10
|
+
s.require_paths = ['lib']
|
11
|
+
s.license = 'MIT'
|
12
|
+
s.homepage = 'https://github.com/mjskier/infermedica'
|
13
|
+
s.add_runtime_dependency 'json', '~>1.8', '>= 1.8.3'
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Infermedica
|
2
|
+
|
3
|
+
# = Connection
|
4
|
+
# handles the http communication
|
5
|
+
# No assumption is made about content or return values
|
6
|
+
# The caller is responsible for converting to/from json
|
7
|
+
|
8
|
+
class Connection
|
9
|
+
|
10
|
+
# Default host. Can be overwritten with *endpoint: '/some/endpoint'
|
11
|
+
# When creating the Connection
|
12
|
+
|
13
|
+
ENDPOINT = 'https://api.infermedica.com'
|
14
|
+
|
15
|
+
def initialize(args)
|
16
|
+
raise ArgumentError,
|
17
|
+
'Infermedica::Connection::initialize argument needs to be a Hash' unless
|
18
|
+
args.is_a?(Hash)
|
19
|
+
raise ArgumentError, 'api_id is required' unless args.key?(:api_id)
|
20
|
+
raise ArgumentError, 'api_key is required' unless args.key?(:api_key)
|
21
|
+
args[:endpoint] = ENDPOINT unless args.key?(:endpoint)
|
22
|
+
|
23
|
+
# Probably need more argument validation here...
|
24
|
+
args.each do |k, v|
|
25
|
+
instance_variable_set(:"@#{k}", v)
|
26
|
+
end
|
27
|
+
|
28
|
+
uri = URI.parse(@endpoint)
|
29
|
+
@http = Net::HTTP.new(uri.host, uri.port)
|
30
|
+
@http.use_ssl = true
|
31
|
+
|
32
|
+
@headers = {
|
33
|
+
'App-Id': @api_id,
|
34
|
+
'App-Key': @api_key,
|
35
|
+
}
|
36
|
+
@headers['Dev-Mode'] = args[:dev_mode] if args.key?(:dev_mode)
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO: Might have to deal with args
|
40
|
+
|
41
|
+
# Send a get request to the given path
|
42
|
+
def get(path)
|
43
|
+
# TODO: do more hardening on the path
|
44
|
+
request = Net::HTTP::Get.new('/v2' + path, @headers)
|
45
|
+
send_request(request)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Send a post request to the given path
|
49
|
+
# json is the data to be passed to the post command
|
50
|
+
# params are additional header entries
|
51
|
+
|
52
|
+
def post(path, json, params = {})
|
53
|
+
request = Net::HTTP::Post.new('/v2' + path, @headers)
|
54
|
+
request.add_field('Content-Type', 'application/json')
|
55
|
+
request.body = json
|
56
|
+
params.each do |k, v|
|
57
|
+
request[k] = v
|
58
|
+
end
|
59
|
+
send_request(request)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Check the response code, raise an exception in case of error
|
65
|
+
|
66
|
+
def validate_response(response) # :nodoc:
|
67
|
+
code = response.code.to_i
|
68
|
+
raise HttpError, "#{code} #{response.msg}" if code < 200 || code > 299
|
69
|
+
end
|
70
|
+
|
71
|
+
# Send the request, validate it
|
72
|
+
# Parse the JSON response into a data structure.
|
73
|
+
|
74
|
+
def send_request(request) # :nodoc:
|
75
|
+
response = @http.request(request)
|
76
|
+
validate_response(response)
|
77
|
+
JSON.parse(response.body)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Infermedica
|
2
|
+
|
3
|
+
# = Diagnosis
|
4
|
+
#
|
5
|
+
# Interface to the /diagnosis and /explain endpoints
|
6
|
+
# See examples/diagnosis.rb and examples/explain.rb for example use
|
7
|
+
|
8
|
+
class Diagnosis
|
9
|
+
attr_accessor :sex, :age, :symptoms, :case, :extras, :extras_permanent,
|
10
|
+
:lab_tests, :pursued, :risk_factors,
|
11
|
+
:case_id, :target
|
12
|
+
|
13
|
+
def initialize(args)
|
14
|
+
raise ArgumentError, 'Patient sex is required' unless args.key?(:sex)
|
15
|
+
raise ArgumentError, 'Patient age is required' unless args.key?(:age)
|
16
|
+
|
17
|
+
args[:time] = Time.now unless args.key?(:time)
|
18
|
+
args[:case_id] = nil unless args.key?(:case_id)
|
19
|
+
|
20
|
+
args.each do |k, v|
|
21
|
+
instance_variable_set(:"@#{k}", v)
|
22
|
+
end
|
23
|
+
|
24
|
+
%w[ symptoms lab_tests risk_factors pursued ].each do |k|
|
25
|
+
args.key?(k) || instance_variable_set(:"@#{k}", Array.new)
|
26
|
+
end
|
27
|
+
|
28
|
+
@extras = {}
|
29
|
+
@extras_permanent = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
# Add a symptom to the list of symptom kept in this object
|
33
|
+
# k: symptom code (for example 's_10'
|
34
|
+
# v: symptom appropriate value (for example 'present')
|
35
|
+
|
36
|
+
def add_symptom(k, v)
|
37
|
+
@symptoms << { 'id': k, 'choice_id': v }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Add a pursued_conditions
|
41
|
+
# TODO: find out what these are and how they are used
|
42
|
+
|
43
|
+
def add_pursued_conditions(args)
|
44
|
+
@pursued.push(*args)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Set a target condition that will be used by the /explain endpoint
|
48
|
+
# v: The target condition (for example 'c_371')
|
49
|
+
|
50
|
+
def add_target(v)
|
51
|
+
@target = v
|
52
|
+
end
|
53
|
+
|
54
|
+
# Convert self to a json representation
|
55
|
+
|
56
|
+
def to_json(*a)
|
57
|
+
h = {
|
58
|
+
'sex': @sex,
|
59
|
+
'age': @age,
|
60
|
+
'evidence': @symptoms,
|
61
|
+
'extras': @extras,
|
62
|
+
}
|
63
|
+
h['target'] = @target unless @target.nil?
|
64
|
+
h['pursued'] = @pursued unless @pursued.empty?
|
65
|
+
h['lab_tests'] = @lab_tests unless @lab_tests.empty?
|
66
|
+
h['risk_factors'] = @risk_factors unless @risk_factors.empty?
|
67
|
+
h['extras_permanent'] = @extras_permanent unless @extras_permanent.empty?
|
68
|
+
h.to_json(*a)
|
69
|
+
end
|
70
|
+
|
71
|
+
end # class Diagnosis
|
72
|
+
|
73
|
+
end # module
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Infermedica
|
2
|
+
|
3
|
+
# = Info
|
4
|
+
#
|
5
|
+
# Class holding the response to an /info request
|
6
|
+
#
|
7
|
+
# This includes the date and version of the API, as well as counts for the
|
8
|
+
# various collections available from the API
|
9
|
+
|
10
|
+
class Info
|
11
|
+
include Infermedica::Model
|
12
|
+
attr_reader :updated_at, :conditions_count, :symptoms_count,
|
13
|
+
:risk_factors_count, :lab_tests_count
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Infermedica
|
2
|
+
|
3
|
+
# = Model
|
4
|
+
# Common methods used by other classes
|
5
|
+
|
6
|
+
module Model
|
7
|
+
|
8
|
+
# Common way to initialize an object from an Hash
|
9
|
+
|
10
|
+
def initialize(h)
|
11
|
+
h.keys.each do |k|
|
12
|
+
instance_variable_set(:"@#{k}", h[k])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Infermedica
|
2
|
+
|
3
|
+
# = Symptom
|
4
|
+
# Represents an Infermedica symptom
|
5
|
+
|
6
|
+
class Symptom
|
7
|
+
include Infermedica::Model
|
8
|
+
|
9
|
+
attr_accessor :id, :name, :category, :children, :extras,
|
10
|
+
:image_source, :image_url, :parent_id,
|
11
|
+
:parent_relation, :question, :sex_filter
|
12
|
+
end
|
13
|
+
end
|
data/lib/infermedica.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
require_relative 'infermedica/model'
|
6
|
+
require_relative 'infermedica/conditions'
|
7
|
+
require_relative 'infermedica/diagnosis'
|
8
|
+
require_relative 'infermedica/info'
|
9
|
+
require_relative 'infermedica/lab_tests'
|
10
|
+
require_relative 'infermedica/risk_factors'
|
11
|
+
require_relative 'infermedica/symptoms'
|
12
|
+
require_relative 'infermedica/connection'
|
13
|
+
|
14
|
+
# = Infermedica: A ruby interface to the infermedica REST API
|
15
|
+
#
|
16
|
+
# == Quick Start
|
17
|
+
#
|
18
|
+
# You will need a valid *api_id* and *api_key*.
|
19
|
+
# Get one from https://developer.infermedica.com/docs/api
|
20
|
+
#
|
21
|
+
# To start using the API, require the infermedica gem and create an
|
22
|
+
# Infermedica::Api object, passing the api_id and api_key to the constructor
|
23
|
+
#
|
24
|
+
# The constructor takes a hash as argument, so you have different options
|
25
|
+
# to pass the id and key:
|
26
|
+
#
|
27
|
+
# require 'infermedica'
|
28
|
+
# api = Infermedica::Api.new(api_id: 'xxxxx', api_key: 'xxxxxxxxxxx')
|
29
|
+
#
|
30
|
+
# or put the key and id in a .yaml file, read it and pass the resulting hash
|
31
|
+
#
|
32
|
+
# In config.yaml
|
33
|
+
# :api:id: xxxxx
|
34
|
+
# :api_key: xxxxxxxxxxx
|
35
|
+
#
|
36
|
+
# In your script
|
37
|
+
#
|
38
|
+
# require 'infermedica'
|
39
|
+
# require 'yaml'
|
40
|
+
# access = YAML.load(File.read('./config.yaml'))
|
41
|
+
# api = Infermedica::Api.new(access)
|
42
|
+
|
43
|
+
module Infermedica
|
44
|
+
|
45
|
+
# Exceptions
|
46
|
+
|
47
|
+
# HTTP error raised when we don't get the expected result from an API call
|
48
|
+
class HttpError < StandardError; end
|
49
|
+
|
50
|
+
# Missing field or field not set in a request
|
51
|
+
class MissingField < StandardError; end
|
52
|
+
|
53
|
+
# Api defines all operations available from the REST API
|
54
|
+
|
55
|
+
class Api
|
56
|
+
|
57
|
+
def get_conditions # return a Hash of known conditions
|
58
|
+
get_collection('/conditions')
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_condition(id) # return a Condition object
|
62
|
+
response = @connection.get("/conditions/#{id}")
|
63
|
+
return Condition.new(response)
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_lab_tests # erturn a Hash of lab_tests
|
67
|
+
get_collection('/lab_tests')
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_lab_test(id) # return a LabTest object
|
71
|
+
response = @connection.get("/lab_tests/#{id}")
|
72
|
+
return LabTest.new(response)
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_risk_factors # return a Hash of risk_factors
|
76
|
+
get_collection('/risk_factors')
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_risk_factor(id) # return a RiskFactor object
|
80
|
+
response = @connection.get("/risk_factors/#{id}")
|
81
|
+
return RiskFactor.new(response)
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_symptoms # return a list of symptoms
|
85
|
+
get_collection('/symptoms')
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_symptom(id) # return a Symptom object
|
89
|
+
response = @connection.get("/symptoms/#{id}")
|
90
|
+
return Symptom.new(response)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get the Api info (version, date,
|
94
|
+
# number of conditions, lab_tests, risk_factors, symptoms
|
95
|
+
|
96
|
+
def get_info
|
97
|
+
response = @connection.get("/info")
|
98
|
+
return Info.new(response)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Submit a diagnosis object to get a diagnosis, or a list of additional
|
102
|
+
# conditions required to refine the diagnosis
|
103
|
+
# See examples/diagnosis.rb for an example
|
104
|
+
def diagnosis(diag)
|
105
|
+
response = @connection.post('/diagnosis', diag.to_json)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Submit a diagnosis object to get an explanation
|
109
|
+
# See examples/explain.rb for an example
|
110
|
+
def explain(req, args = {})
|
111
|
+
raise Infermedica::MissingField, 'target must be set' if
|
112
|
+
req.target.nil?
|
113
|
+
response = @connection.post('/explain', req.to_json, args)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Submit a search request, possibly filtered by where to look
|
117
|
+
# See examples/search.rb for an example
|
118
|
+
|
119
|
+
def search(phrase, args = {})
|
120
|
+
url = '/search?phrase=' + phrase
|
121
|
+
args['max_results'] = 8 unless args.key?('max_results')
|
122
|
+
args.each do |k, v|
|
123
|
+
puts "'#{k}': #{v.class} #{v}"
|
124
|
+
if v.is_a?(Array) && k.to_s == 'filters'
|
125
|
+
v.each { |e| url << "&type=#{e}" }
|
126
|
+
else
|
127
|
+
url << "&#{k}=#{v}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
puts "url: #{url}"
|
131
|
+
response = @connection.get(url)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Create a new Infermedica::Api object.
|
135
|
+
# Takes a hash as argument.
|
136
|
+
# The *api_id* and *api_key* entries are required.
|
137
|
+
|
138
|
+
def initialize(args)
|
139
|
+
raise ArgumentError,
|
140
|
+
'Infermedica::Api::initialize argument needs to be a Hash)' unless
|
141
|
+
args.is_a?(Hash)
|
142
|
+
raise ArgumentError, 'api_id is required' unless args.key?(:api_id)
|
143
|
+
raise ArgumentError, 'api_key is required' unless args.key?(:api_key)
|
144
|
+
|
145
|
+
connection_args = { api_id: args[:api_id], api_key: args[:api_key] }
|
146
|
+
connection_args[:endpoint] = args[:endpoint] if args.key?(:endpoint)
|
147
|
+
@connection = Connection.new(connection_args)
|
148
|
+
|
149
|
+
# Probably need more argument validation here...
|
150
|
+
args.each do |k, v|
|
151
|
+
instance_variable_set(:"@#{k}", v)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
# Common frontend to the public methods that require collections
|
158
|
+
|
159
|
+
def get_collection(path) # :nodoc:
|
160
|
+
response = @connection.get(path)
|
161
|
+
# response is an array
|
162
|
+
collection = {}
|
163
|
+
response.each do |item|
|
164
|
+
collection[item['id']] = item
|
165
|
+
end
|
166
|
+
collection
|
167
|
+
end
|
168
|
+
|
169
|
+
end # class Api
|
170
|
+
end # module
|
data/test/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'infermedica'
|
3
|
+
|
4
|
+
class TestCondition < Minitest::Test
|
5
|
+
|
6
|
+
def setup
|
7
|
+
a_condition = {
|
8
|
+
"id"=>"c_537",
|
9
|
+
"name"=>"Abdominal obesity",
|
10
|
+
"prevalence"=>"common",
|
11
|
+
"acuteness"=>"chronic",
|
12
|
+
"severity"=>"moderate",
|
13
|
+
"sex_filter"=>"both",
|
14
|
+
"categories"=>["Internal Medicine"],
|
15
|
+
"extras"=> {
|
16
|
+
"hint"=>"Please consult your family doctor.",
|
17
|
+
"icd10_code"=>"E65"
|
18
|
+
},
|
19
|
+
}
|
20
|
+
|
21
|
+
@condition = Infermedica::Condition.new(a_condition)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def test_condition_creation
|
26
|
+
assert_equal @condition.id, 'c_537'
|
27
|
+
assert_equal @condition.name, 'Abdominal obesity'
|
28
|
+
assert_equal @condition.prevalence, 'common'
|
29
|
+
assert_equal @condition.acuteness, 'chronic'
|
30
|
+
assert_equal @condition.severity, 'moderate'
|
31
|
+
assert_equal @condition.sex_filter, 'both'
|
32
|
+
assert_equal @condition.categories.class, Array
|
33
|
+
assert_equal @condition.categories[0], 'Internal Medicine'
|
34
|
+
assert_equal @condition.extras.class, Hash
|
35
|
+
assert_includes @condition.extras, 'hint'
|
36
|
+
assert_equal @condition.extras['icd10_code'], 'E65'
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'infermedica'
|
3
|
+
|
4
|
+
class TestCondition < Minitest::Test
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@diag = Infermedica::Diagnosis.new(sex: 'male', age: '35')
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_diagnosis_creation
|
11
|
+
assert_equal @diag.sex, 'male'
|
12
|
+
assert_equal @diag.age, '35'
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_symptom_addition
|
16
|
+
assert_equal @diag.symptoms.class, Array
|
17
|
+
assert_equal @diag.symptoms.empty?, true
|
18
|
+
@diag.add_symptom('s_10', 'present')
|
19
|
+
assert_equal @diag.symptoms[0][:id], 's_10'
|
20
|
+
assert_equal @diag.symptoms[0][:choice_id], 'present'
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_pursued_addition
|
24
|
+
assert_equal @diag.pursued.class, Array
|
25
|
+
assert_equal @diag.pursued.empty?, true
|
26
|
+
@diag.add_pursued_conditions(['c_33', 'c_49'])
|
27
|
+
assert_equal @diag.pursued.size, 2
|
28
|
+
assert_equal @diag.pursued[0], 'c_33'
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_target_addition
|
32
|
+
assert_equal @diag.target, nil
|
33
|
+
@diag.add_target('c_371')
|
34
|
+
assert_equal @diag.target, 'c_371'
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_the_to_json_method
|
38
|
+
json = @diag.to_json
|
39
|
+
assert_equal json.class, String
|
40
|
+
match = json =~ /\"age\":\"(\d+)\"/
|
41
|
+
assert_equal $1, '35'
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/test/unit/info.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'infermedica'
|
3
|
+
|
4
|
+
class TestCondition < Minitest::Test
|
5
|
+
|
6
|
+
def setup
|
7
|
+
the_info = {
|
8
|
+
updated_at: '2016-11-21T13:26:04Z',
|
9
|
+
conditions_count: '504',
|
10
|
+
symptoms_count: '1110',
|
11
|
+
risk_factors_count: '42',
|
12
|
+
lab_tests_count: '457'
|
13
|
+
}
|
14
|
+
|
15
|
+
@info = Infermedica::Info.new(the_info)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def test_info_creation
|
20
|
+
assert_equal @info.updated_at, '2016-11-21T13:26:04Z'
|
21
|
+
assert_equal @info.conditions_count, '504'
|
22
|
+
assert_equal @info.symptoms_count, '1110'
|
23
|
+
assert_equal @info.risk_factors_count, '42'
|
24
|
+
assert_equal @info.lab_tests_count, '457'
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'infermedica'
|
3
|
+
|
4
|
+
class TestCondition < Minitest::Test
|
5
|
+
|
6
|
+
def setup
|
7
|
+
a_lab_test = {
|
8
|
+
'id': 'lt_81',
|
9
|
+
'name': 'Blood urea nitrogen (BUN)',
|
10
|
+
'category': 'Biochemistry',
|
11
|
+
'results' => [
|
12
|
+
{'id' => 'lt_81_1', 'type' => 'low'},
|
13
|
+
{'id' => 'lt_81_2', 'type' => 'normal'},
|
14
|
+
{'id' => 'lt_81_3', 'type' => 'high'}]}
|
15
|
+
|
16
|
+
@lab_test = Infermedica::LabTest.new(a_lab_test)
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def test_lab_test_creation
|
21
|
+
assert_equal @lab_test.id, 'lt_81'
|
22
|
+
assert_equal @lab_test.name, 'Blood urea nitrogen (BUN)'
|
23
|
+
assert_equal @lab_test.category, 'Biochemistry'
|
24
|
+
assert_equal @lab_test.results.class, Array
|
25
|
+
assert_equal @lab_test.results[0].class, Hash
|
26
|
+
assert_equal @lab_test.results[0]['id'], 'lt_81_1'
|
27
|
+
assert_equal @lab_test.results[1]['type'], 'normal'
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'infermedica'
|
3
|
+
|
4
|
+
class TestCondition < Minitest::Test
|
5
|
+
|
6
|
+
def setup
|
7
|
+
a_risk_factor = {
|
8
|
+
'id': 'p_37',
|
9
|
+
'name': 'Mushroom consumption',
|
10
|
+
'category': 'Risk factors',
|
11
|
+
'extras': {},
|
12
|
+
'sex_filter': 'both',
|
13
|
+
'image_url': nil,
|
14
|
+
'image_source': nil
|
15
|
+
}
|
16
|
+
|
17
|
+
@risk_factor = Infermedica::RiskFactor.new(a_risk_factor)
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def test_risk_factor_creation
|
22
|
+
assert_equal @risk_factor.id, 'p_37'
|
23
|
+
assert_equal @risk_factor.name, 'Mushroom consumption'
|
24
|
+
assert_equal @risk_factor.category, 'Risk factors'
|
25
|
+
assert_equal @risk_factor.extras.class, Hash
|
26
|
+
assert_equal @risk_factor.sex_filter, 'both'
|
27
|
+
assert_equal @risk_factor.image_url, nil
|
28
|
+
assert_equal @risk_factor.image_source, nil
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'infermedica'
|
3
|
+
|
4
|
+
class TestCondition < Minitest::Test
|
5
|
+
|
6
|
+
def setup
|
7
|
+
a_symptom = {
|
8
|
+
'id'=>'s_551',
|
9
|
+
'name'=>'Toothache',
|
10
|
+
'category'=>'Signs and symptoms',
|
11
|
+
'extras'=>{},
|
12
|
+
'children'=> [
|
13
|
+
{'id'=>'s_339', 'parent_relation'=>'diminishing_factor'},
|
14
|
+
{'id'=>'s_55', 'parent_relation'=>'exacerbating_factor'},
|
15
|
+
{'id'=>'s_231', 'parent_relation'=>'exacerbating_factor'},
|
16
|
+
{'id'=>'s_56', 'parent_relation'=>'exacerbating_factor'},
|
17
|
+
{'id'=>'s_240', 'parent_relation'=>'severity'},
|
18
|
+
{'id'=>'s_233', 'parent_relation'=>'severity'},
|
19
|
+
{'id'=>'s_279', 'parent_relation'=>'character'},
|
20
|
+
{'id'=>'s_267', 'parent_relation'=>'location'}],
|
21
|
+
'sex_filter'=>'both',
|
22
|
+
'image_url'=>nil,
|
23
|
+
'image_source'=>nil,
|
24
|
+
'parent_id'=>nil,
|
25
|
+
'parent_relation'=>nil
|
26
|
+
}
|
27
|
+
|
28
|
+
@symptom = Infermedica::Symptom.new(a_symptom)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def test_symptom_creation
|
33
|
+
assert_equal @symptom.id, 's_551'
|
34
|
+
assert_equal @symptom.name, 'Toothache'
|
35
|
+
assert_equal @symptom.category, 'Signs and symptoms'
|
36
|
+
assert_equal @symptom.extras.class, Hash
|
37
|
+
assert_equal @symptom.sex_filter, 'both'
|
38
|
+
assert_equal @symptom.image_url, nil
|
39
|
+
assert_equal @symptom.parent_id, nil
|
40
|
+
assert_equal @symptom.parent_relation, nil
|
41
|
+
assert_equal @symptom.children.class, Array
|
42
|
+
assert_equal @symptom.children[1]['id'], 's_55'
|
43
|
+
assert_equal @symptom.children[4]['parent_relation'], 'severity'
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: infermedica
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bruno Melli
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-01-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.8.3
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.8'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.8.3
|
33
|
+
description: A Ruby interface to the Infermedica REST API
|
34
|
+
email: mjskier@tegali.com
|
35
|
+
executables: []
|
36
|
+
extensions: []
|
37
|
+
extra_rdoc_files: []
|
38
|
+
files:
|
39
|
+
- ".gitignore"
|
40
|
+
- examples/condition.rb
|
41
|
+
- examples/diagnosis.rb
|
42
|
+
- examples/explain.rb
|
43
|
+
- examples/info.rb
|
44
|
+
- examples/lab_test.rb
|
45
|
+
- examples/risk_factor.rb
|
46
|
+
- examples/search.rb
|
47
|
+
- examples/symptom.rb
|
48
|
+
- infermedica.gemspec
|
49
|
+
- lib/infermedica.rb
|
50
|
+
- lib/infermedica/conditions.rb
|
51
|
+
- lib/infermedica/connection.rb
|
52
|
+
- lib/infermedica/diagnosis.rb
|
53
|
+
- lib/infermedica/info.rb
|
54
|
+
- lib/infermedica/lab_tests.rb
|
55
|
+
- lib/infermedica/model.rb
|
56
|
+
- lib/infermedica/risk_factors.rb
|
57
|
+
- lib/infermedica/symptoms.rb
|
58
|
+
- test/Rakefile
|
59
|
+
- test/unit/condition.rb
|
60
|
+
- test/unit/diagnosis.rb
|
61
|
+
- test/unit/info.rb
|
62
|
+
- test/unit/lab_test.rb
|
63
|
+
- test/unit/risk_factor.rb
|
64
|
+
- test/unit/symptom.rb
|
65
|
+
homepage: https://github.com/mjskier/infermedica
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 2.5.1
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Provide a Ruby interface to the Infermedica REST API
|
89
|
+
test_files: []
|