open_fda_api 0.0.4 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2a7513c32091c436f4641cf0e32a64b51e0ad8ae1f600579a54526c868ece7c
4
- data.tar.gz: 797198f936396fbe4a1610996ab7a58fe2fc744017ed4634c0347f345659ce15
3
+ metadata.gz: 3f55d1b8140f12ed25ecf99fd227e0c053a50455abcc2153e03ba7a5f5d724bb
4
+ data.tar.gz: 74f2219cfd2812e2576e9e748132332f049d9925b5afd763609528f0fa1a049e
5
5
  SHA512:
6
- metadata.gz: 468e135f99f59470fdad06055b1849978f9b95fe40aa4b415a8b9485ecc6a17fa4b18f23a06e233dd108f193e5cf2ef335756e327be8ecf5eebd94fd2b8ccb4a
7
- data.tar.gz: 2d42fdf47f92181a0d4ebaaaae6eab84c8d5e868ffec47fd2cfa30b41e66ac036bd2c0bb9cdf40eaf0fc1b5f597ad16921a786c5a883c60561a81f594b989556
6
+ metadata.gz: '06953c4682f9bd4948c23a24035094452b85ee73bcb9c3a37f5e16151c39bedb90afab6b11881f73d9106cdf052dcc4d3bb98747b4126409fcbf9fb57eb59082'
7
+ data.tar.gz: acbddbb9dc475a1243ef8415e411d1c365de227509d3dc7ceddd3221136b96329dea226acad155b4d87269dd754fcf59166ea880f2a053acccfc987b2374dc37
data/CHANGELOG.md CHANGED
@@ -1,9 +1,41 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.0.12] - 2022-01-24
4
+ - Animal & Veterinary Endpoint
5
+ - Tobacco Endpoint
6
+ - Other Endpoint
7
+
8
+ ## [0.0.11] - 2022-01-24
9
+ - Include Food endpoints
10
+
11
+ ## [0.0.10] - 2022-01-24
12
+ - Include Device endpoints
13
+ - Pull out Endpoint common behavior to a base class
14
+
15
+ ## [0.0.9] - 2022-01-24
16
+ - Filled out the following Drug endpoints:
17
+ - product_labeling
18
+ - ndc_directory
19
+ - recall_enforcement_reports
20
+ - drugs_at_fda
21
+
22
+ ## [0.0.8] - 2022-01-24
23
+ - Delete the `OpenFdaApi.client` method since its only function was to forward messages
24
+
25
+ ## [0.0.7] - 2022-01-23
26
+ - Use Faraday instead of using Net::HTTP directly
27
+ - Introduce `QueryInputs` to group query params passed in together
28
+
29
+ ## [0.0.6] - 2022-01-21
30
+ - Support for more query fields
31
+
32
+ ## [0.0.5] - 2022-01-20
33
+ - Validate, against search fields given to us from openFDA API, when building queries.
34
+
3
35
  ## [0.0.4] - 2022-01-19
4
- - Fix version string in changelog
5
- - Update Query Builder to group search arguments properly
6
- - Update Query Builder to replace spaces with pluses in search values as openFDA API documentation requires
36
+ - Fix version string in changelog.
37
+ - Update Query Builder to group search arguments properly.
38
+ - Update Query Builder to replace spaces with pluses in search values as openFDA API documentation requires.
7
39
 
8
40
  ## [0.0.3] - 2022-01-19
9
41
  - Update version again because CHANGELOG wasn't updated along with the 0.0.2 release.
data/Gemfile.lock CHANGED
@@ -1,16 +1,49 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- open_fda_api (0.0.4)
4
+ open_fda_api (0.0.12)
5
+ faraday (~> 1.9)
6
+ faraday_middleware (~> 1.2)
5
7
 
6
8
  GEM
7
9
  remote: https://rubygems.org/
8
10
  specs:
9
11
  ast (2.4.2)
12
+ coderay (1.1.3)
10
13
  diff-lcs (1.5.0)
14
+ faraday (1.9.3)
15
+ faraday-em_http (~> 1.0)
16
+ faraday-em_synchrony (~> 1.0)
17
+ faraday-excon (~> 1.1)
18
+ faraday-httpclient (~> 1.0)
19
+ faraday-multipart (~> 1.0)
20
+ faraday-net_http (~> 1.0)
21
+ faraday-net_http_persistent (~> 1.0)
22
+ faraday-patron (~> 1.0)
23
+ faraday-rack (~> 1.0)
24
+ faraday-retry (~> 1.0)
25
+ ruby2_keywords (>= 0.0.4)
26
+ faraday-em_http (1.0.0)
27
+ faraday-em_synchrony (1.0.0)
28
+ faraday-excon (1.1.0)
29
+ faraday-httpclient (1.0.1)
30
+ faraday-multipart (1.0.3)
31
+ multipart-post (>= 1.2, < 3)
32
+ faraday-net_http (1.0.1)
33
+ faraday-net_http_persistent (1.2.0)
34
+ faraday-patron (1.0.0)
35
+ faraday-rack (1.0.0)
36
+ faraday-retry (1.0.3)
37
+ faraday_middleware (1.2.0)
38
+ faraday (~> 1.0)
39
+ method_source (1.0.0)
40
+ multipart-post (2.1.1)
11
41
  parallel (1.21.0)
12
42
  parser (3.1.0.0)
13
43
  ast (~> 2.4.1)
44
+ pry (0.14.1)
45
+ coderay (~> 1.1)
46
+ method_source (~> 1.0)
14
47
  rainbow (3.1.1)
15
48
  rake (13.0.6)
16
49
  regexp_parser (2.2.0)
@@ -40,6 +73,7 @@ GEM
40
73
  rubocop-ast (1.15.1)
41
74
  parser (>= 3.0.1.1)
42
75
  ruby-progressbar (1.11.0)
76
+ ruby2_keywords (0.0.5)
43
77
  unicode-display_width (2.1.0)
44
78
 
45
79
  PLATFORMS
@@ -47,6 +81,7 @@ PLATFORMS
47
81
 
48
82
  DEPENDENCIES
49
83
  open_fda_api!
84
+ pry
50
85
  rake (~> 13.0)
51
86
  rspec (~> 3.0)
52
87
  rubocop (~> 1.7)
data/README.md CHANGED
@@ -16,37 +16,67 @@ And then execute:
16
16
  bundle install
17
17
  ```
18
18
 
19
- ## Documentation
19
+ ## Usage
20
20
 
21
- There are 5 main category of endpoints that openFDA API provides: Drug, Device, Food, Other, and Tobacco.
22
-
23
- Each category has further subcategories. Everything is accessible from the `OpenFdaApi.client`.
21
+ ```ruby
22
+ client = OpenFdaApi.client
24
23
 
24
+ # First 20 results where (fieldA=foo AND fieldB=bar) OR (fieldC=baz AND fieldA exists)
25
+ # Sorted by (fieldD) in descending order
26
+ # Skip the first 8 results
27
+ args = {
28
+ search: [{"fieldA" => "foo", "fieldB" => "bar"}, {"fieldC" => "baz", "_exists_" => "fieldA"}],
29
+ sort: [{"fieldD" => "desc"}],
30
+ skip: 8,
31
+ limit: 20
32
+ }
33
+
34
+ # Animal & Veterinary API
35
+ client.animal_and_veterinary.adverse_events(args)
36
+
37
+ # Drug API
38
+ client.drugs.adverse_events(args)
39
+ client.drugs.product_labeling(args)
40
+ client.drugs.ndc_directory(args)
41
+ client.drugs.recall_enforcement_reports(args)
42
+ client.drugs.drugs_at_fda(args)
43
+
44
+ # Device API
45
+ client.device.premarket_501ks(args)
46
+ client.device.classification(args)
47
+ client.device.recall_enforcement_reports(args)
48
+ client.device.adverse_events(args)
49
+ client.device.premarket_approval(args)
50
+ client.device.recalls(args)
51
+ client.device.registrations_and_listings(args)
52
+ client.device.covid19_serological_tests(args)
53
+ client.device.unique_device_identifier(args)
54
+
55
+ # Food API
56
+ client.food.recall_enforcement_reports(args)
57
+ client.food.adverse_events(args)
58
+
59
+ # Other API
60
+ client.other.nsde(args)
61
+ client.other.substance_data_reports(args)
62
+
63
+ # Tobacco API
64
+ client.tobacco.problem_reports(args)
65
+ ```
25
66
 
26
- ### Drug
67
+ ### Querying
27
68
 
28
- The Drug API has the following endpoints: Adverse Events, Product Labeling, NDC Directory, Recall Enforcement Reports, and Drugs@FDA.
69
+ The openFDA API can be queried with these arguments: `search`, `sort`, `count`, `skip`, and `limit`.
29
70
 
30
- Here's how you interact with each:
71
+ `search`, `sort`, and `count` have the same format. They are arrays of hashes. Each hash has a set of fields and values
72
+ that are ANDed together and all the elements in the array are ORed together. Here are some examples to illustrate:
31
73
 
32
- #### Adverse Events
33
74
  ```ruby
34
- require 'open_fda_api'
75
+ search = [{"patient.drug.openfda.pharm_class_epc" => "nonsteroidal+anti-inflammatory+drug" }]
35
76
 
36
- client = OpenFdaApi.client
37
- drugs_api = client.drugs
38
-
39
- arguments = [{"patient.reaction.reactionmeddrapt"=>"fatigue"}, {"occurcountry"=>"ca"}]
40
- drugs_api.adverse_events(search_arguments: arguments) # => {"meta" => {...}, "results" => [...]}
77
+ # patient.drug.openfda.pharm_class_epc:"nonsteroidal+anti-inflammatory+drug"&count=patient.reaction.reactionmeddrapt.exact
41
78
  ```
42
79
 
43
- ### Device (Not Implemented Yet)
44
- ### Food (Not Implemented Yet)
45
- ### Other (Not Implemented Yet)
46
- ### Tobacco (Not Implemented Yet)
47
-
48
-
49
-
50
80
  ## Development
51
81
 
52
82
  After checking out the repo, run `bin/setup` to install dependencies.
data/bin/console CHANGED
@@ -11,5 +11,8 @@ require "open_fda_api"
11
11
  # require "pry"
12
12
  # Pry.start
13
13
 
14
+ client = OpenFdaApi::Client.new
15
+ puts "Client is ready: #{client}"
16
+
14
17
  require "irb"
15
18
  IRB.start(__FILE__)
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenFdaApi
4
+ # Interact with the Animal & Veterinary API Endpoint:
5
+ # - Adverse Events
6
+ class AnimalAndVeterinary < Endpoint
7
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/animalandveterinary/event/searchable-fields/
8
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/animalandveterinary/event/searchable-fields/
9
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/animalandveterinary/event/searchable-fields/
10
+ # @param skip [Integer] Number of results to skip
11
+ # @param limit [Integer] Number of results to return
12
+ # @return Response from the API parsed as JSON
13
+ def adverse_events(search: [], sort: [], count: [], skip: 0, limit: 1)
14
+ endpoint = "event.json"
15
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
16
+ query = build_query(inputs, {}) # TODO: Upload valid fields
17
+ make_request(endpoint, query)
18
+ end
19
+
20
+ private
21
+
22
+ def endpoint_path
23
+ "/animalandveterinary"
24
+ end
25
+ end
26
+ end
@@ -1,18 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./drugs"
3
+ require "faraday"
4
+ require "faraday_middleware"
4
5
 
5
6
  module OpenFdaApi
6
7
  # Gives you access to the main nouns in the openFDA API
7
8
  class Client
8
- attr_reader :api_key
9
+ attr_reader :api_key, :adapter
9
10
 
10
- def initialize(api_key: nil)
11
+ BASE_URL = "https://api.fda.gov"
12
+
13
+ def initialize(api_key: nil, adapter: Faraday.default_adapter, stubs: nil)
11
14
  @api_key = api_key
15
+ @adapter = adapter
16
+ @stubs = stubs
12
17
  end
13
18
 
14
19
  def drugs
15
- OpenFdaApi::Drugs.new
20
+ OpenFdaApi::Drugs.new(self)
21
+ end
22
+
23
+ def device
24
+ OpenFdaApi::Device.new(self)
25
+ end
26
+
27
+ def food
28
+ OpenFdaApi::Food.new(self)
29
+ end
30
+
31
+ def animal_and_veterinary
32
+ OpenFdaApi::AnimalAndVeterinary.new(self)
33
+ end
34
+
35
+ def tobacco
36
+ OpenFdaApi::Tobacco.new(self)
37
+ end
38
+
39
+ def other
40
+ OpenFdaApi::Other.new(self)
41
+ end
42
+
43
+ def connection
44
+ @connection ||= Faraday.new(BASE_URL) do |conn|
45
+ conn.request :json
46
+
47
+ conn.response :dates
48
+ conn.response :json, content_type: "application/json"
49
+
50
+ conn.adapter adapter, @stubs
51
+ end
16
52
  end
17
53
  end
18
54
  end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenFdaApi
4
+ # Interact with the Device API Endpoint:
5
+ # - 501(k)
6
+ # - Classification
7
+ # - Recall Enforcement Reports
8
+ # - Adverse Events
9
+ # - Pre-market Approval
10
+ # - Recalls
11
+ # - Registrations and Listings
12
+ # - Covid19 Serological Testing Evaluations
13
+ # - Unique Device Identifier
14
+ class Device < Endpoint
15
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/device/event/searchable-fields/
16
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/device/event/searchable-fields/
17
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/device/event/searchable-fields/
18
+ # @param skip [Integer] Number of results to skip
19
+ # @param limit [Integer] Number of results to return
20
+ # @return Response from the API parsed as JSON
21
+ def adverse_events(search: [], sort: [], count: [], skip: 0, limit: 1)
22
+ endpoint = "event.json"
23
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
24
+ query = build_query(inputs, {})
25
+ make_request(endpoint, query)
26
+ end
27
+
28
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/device/classification/searchable-fields/
29
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/device/classification/searchable-fields/
30
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/device/classification/searchable-fields/
31
+ # @param skip [Integer] Number of results to skip
32
+ # @param limit [Integer] Number of results to return
33
+ # @return Response from the API parsed as JSON
34
+ def classification(search: [], sort: [], count: [], skip: 0, limit: 1)
35
+ endpoint = "classification.json"
36
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
37
+ query = build_query(inputs, {}) # TODO: Upload valid fields
38
+ make_request(endpoint, query)
39
+ end
40
+
41
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/device/510k/searchable-fields/
42
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/device/510k/searchable-fields/
43
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/device/510k/searchable-fields/
44
+ # @param skip [Integer] Number of results to skip
45
+ # @param limit [Integer] Number of results to return
46
+ # @return Response from the API parsed as JSON
47
+ def premarket_510ks(search: [], sort: [], count: [], skip: 0, limit: 1)
48
+ endpoint = "510k.json"
49
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
50
+ query = build_query(inputs, {}) # TODO: Upload valid fields
51
+ make_request(endpoint, query)
52
+ end
53
+
54
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/device/enforcement/searchable-fields/
55
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/device/enforcement/searchable-fields/
56
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/device/enforcement/searchable-fields/
57
+ # @param skip [Integer] Number of results to skip
58
+ # @param limit [Integer] Number of results to return
59
+ # @return Response from the API parsed as JSON
60
+ def recall_enforcement_reports(search: [], sort: [], count: [], skip: 0, limit: 1)
61
+ endpoint = "enforcement.json"
62
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
63
+ query = build_query(inputs, {}) # TODO: Upload valid fields
64
+ make_request(endpoint, query)
65
+ end
66
+
67
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/device/pma/searchable-fields/
68
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/device/pma/searchable-fields/
69
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/device/pma/searchable-fields/
70
+ # @param skip [Integer] Number of results to skip
71
+ # @param limit [Integer] Number of results to return
72
+ # @return Response from the API parsed as JSON
73
+ def premarket_approval(search: [], sort: [], count: [], skip: 0, limit: 1)
74
+ endpoint = "pma.json"
75
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
76
+ query = build_query(inputs, {}) # TODO: Upload valid fields
77
+ make_request(endpoint, query)
78
+ end
79
+
80
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/device/recall/searchable-fields/
81
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/device/recall/searchable-fields/
82
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/device/recall/searchable-fields/
83
+ # @param skip [Integer] Number of results to skip
84
+ # @param limit [Integer] Number of results to return
85
+ # @return Response from the API parsed as JSON
86
+ def recalls(search: [], sort: [], count: [], skip: 0, limit: 1)
87
+ endpoint = "recall.json"
88
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
89
+ query = build_query(inputs, {}) # TODO: Upload valid fields
90
+ make_request(endpoint, query)
91
+ end
92
+
93
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/device/registrationlisting/searchable-fields/
94
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/device/registrationlisting/searchable-fields/
95
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/device/registrationlisting/searchable-fields/
96
+ # @param skip [Integer] Number of results to skip
97
+ # @param limit [Integer] Number of results to return
98
+ # @return Response from the API parsed as JSON
99
+ def registrations_and_listings(search: [], sort: [], count: [], skip: 0, limit: 1)
100
+ endpoint = "registrationlisting.json"
101
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
102
+ query = build_query(inputs, {}) # TODO: Upload valid fields
103
+ make_request(endpoint, query)
104
+ end
105
+
106
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/device/covid19serology/searchable-fields/
107
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/device/covid19serology/searchable-fields/
108
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/device/covid19serology/searchable-fields/
109
+ # @param skip [Integer] Number of results to skip
110
+ # @param limit [Integer] Number of results to return
111
+ # @return Response from the API parsed as JSON
112
+ def covid19_serological_tests(search: [], sort: [], count: [], skip: 0, limit: 1)
113
+ endpoint = "covid19serology.json"
114
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
115
+ query = build_query(inputs, {}) # TODO: Upload valid fields
116
+ make_request(endpoint, query)
117
+ end
118
+
119
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/device/udi/searchable-fields/
120
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/device/udi/searchable-fields/
121
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/device/udi/searchable-fields/
122
+ # @param skip [Integer] Number of results to skip
123
+ # @param limit [Integer] Number of results to return
124
+ # @return Response from the API parsed as JSON
125
+ def unique_device_identifier(search: [], sort: [], count: [], skip: 0, limit: 1)
126
+ endpoint = "udi.json"
127
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
128
+ query = build_query(inputs, {}) # TODO: Upload valid fields
129
+ make_request(endpoint, query)
130
+ end
131
+
132
+ private
133
+
134
+ def endpoint_path
135
+ "/device"
136
+ end
137
+ end
138
+ end
@@ -1,47 +1,88 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "net/http"
4
- require "json"
5
- require "open_fda_api/query_builder"
3
+ require "yaml"
6
4
 
7
5
  module OpenFdaApi
8
6
  # Interact with the Drugs API Endpoint:
9
- # - Adverse Events
7
+ # - Adverse Events
10
8
  # - Product Labeling
11
9
  # - NDC Directory
12
10
  # - Recall Enforcement Reports
13
11
  # - Drugs@FDA)
14
- class Drugs
15
- def initialize
16
- @host = "api.fda.gov"
17
- @path_base = "/drug"
12
+ class Drugs < Endpoint
13
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/drug/event/searchable-fields/
14
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/drug/event/searchable-fields/
15
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/drug/event/searchable-fields/
16
+ # @param skip [Integer] Number of results to skip
17
+ # @param limit [Integer] Number of results to return
18
+ # @return Response from the API parsed as JSON
19
+ def adverse_events(search: [], sort: [], count: [], skip: 0, limit: 1)
20
+ endpoint = "event.json"
21
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
22
+ query = build_query(inputs, self.class.valid_adverse_events_fields)
23
+ make_request(endpoint, query)
18
24
  end
19
25
 
20
- # The openFDA drug adverse event API returns data that has been collected from the
21
- # FDA Adverse Event Reporting System (FAERS), a database that contains information on
22
- # adverse event and medication error reports submitted to FDA.
23
- #
24
- # @param search_arguments [Array<Hash>] Search fields defined in https://open.fda.gov/apis/drug/event/searchable-fields/
26
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/drug/event/searchable-fields/
27
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/drug/event/searchable-fields/
28
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/drug/event/searchable-fields/
29
+ # @param skip [Integer] Number of results to skip
30
+ # @param limit [Integer] Number of results to return
25
31
  # @return Response from the API parsed as JSON
26
- def adverse_events(search_arguments: [])
27
- endpoint = "/event.json"
28
- query = build_query(search_arguments)
29
- url = build_url(endpoint, query)
30
- make_request(url)
32
+ def product_labeling(search: [], sort: [], count: [], skip: 0, limit: 1)
33
+ endpoint = "label.json"
34
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
35
+ query = build_query(inputs, {}) # TODO: Upload valid fields
36
+ make_request(endpoint, query)
31
37
  end
32
38
 
33
- private
39
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/drug/event/searchable-fields/
40
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/drug/event/searchable-fields/
41
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/drug/event/searchable-fields/
42
+ # @param skip [Integer] Number of results to skip
43
+ # @param limit [Integer] Number of results to return
44
+ # @return Response from the API parsed as JSON
45
+ def ndc_directory(search: [], sort: [], count: [], skip: 0, limit: 1)
46
+ endpoint = "ndc.json"
47
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
48
+ query = build_query(inputs, {}) # TODO: Upload valid fields
49
+ make_request(endpoint, query)
50
+ end
51
+
52
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/drug/event/searchable-fields/
53
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/drug/event/searchable-fields/
54
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/drug/event/searchable-fields/
55
+ # @param skip [Integer] Number of results to skip
56
+ # @param limit [Integer] Number of results to return
57
+ # @return Response from the API parsed as JSON
58
+ def recall_enforcement_reports(search: [], sort: [], count: [], skip: 0, limit: 1)
59
+ endpoint = "enforcement.json"
60
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
61
+ query = build_query(inputs, {}) # TODO: Upload valid fields
62
+ make_request(endpoint, query)
63
+ end
34
64
 
35
- def build_url(endpoint, query)
36
- URI::HTTPS.build(host: @host, path: @path_base + endpoint, query: query)
65
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/drug/event/searchable-fields/
66
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/drug/event/searchable-fields/
67
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/drug/event/searchable-fields/
68
+ # @param skip [Integer] Number of results to skip
69
+ # @param limit [Integer] Number of results to return
70
+ # @return Response from the API parsed as JSON
71
+ def drugs_at_fda(search: [], sort: [], count: [], skip: 0, limit: 1)
72
+ endpoint = "drugsfda.json"
73
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
74
+ query = build_query(inputs, {}) # TODO: Upload valid fields
75
+ make_request(endpoint, query)
37
76
  end
38
77
 
39
- def build_query(search_arguments)
40
- QueryBuilder.new(search: search_arguments).build_query
78
+ def self.valid_adverse_events_fields
79
+ @valid_adverse_events_fields ||= ::YAML.load_file("#{__dir__}/adverse_events_fields.yml")
41
80
  end
42
81
 
43
- def make_request(url)
44
- JSON.parse(Net::HTTP.get(url))
82
+ private
83
+
84
+ def endpoint_path
85
+ "/drug"
45
86
  end
46
87
  end
47
88
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenFdaApi
4
+ # Base class for all endpoints to share behavior like building queries and making requests
5
+ class Endpoint
6
+ attr_reader :client
7
+
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ def build_query(query_input, valid_search_fields)
13
+ QueryBuilder.new(query_input: query_input, valid_search_fields: valid_search_fields).build_query
14
+ end
15
+
16
+ def build_inputs(search:, sort:, count:, skip:, limit:)
17
+ QueryInputs.new(search: search, sort: sort, count: count, skip: skip, limit: limit)
18
+ end
19
+
20
+ def make_request(endpoint, query)
21
+ url = "#{endpoint_path}/#{endpoint}"
22
+ if query.empty?
23
+ client.connection.get(url)
24
+ else
25
+ client.connection.get(url, query)
26
+ end.body
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenFdaApi
4
+ # Interact with the Food API Endpoint:
5
+ # - Adverse Events
6
+ # - Recall Enforcement Reports
7
+ class Food < Endpoint
8
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/food/enforcement/searchable-fields/
9
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/food/enforcement/searchable-fields/
10
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/food/enforcement/searchable-fields/
11
+ # @param skip [Integer] Number of results to skip
12
+ # @param limit [Integer] Number of results to return
13
+ # @return Response from the API parsed as JSON
14
+ def adverse_events(search: [], sort: [], count: [], skip: 0, limit: 1)
15
+ endpoint = "event.json"
16
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
17
+ query = build_query(inputs, {}) # TODO: Upload valid fields
18
+ make_request(endpoint, query)
19
+ end
20
+
21
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/food/enforcement/searchable-fields/
22
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/food/enforcement/searchable-fields/
23
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/food/enforcement/searchable-fields/
24
+ # @param skip [Integer] Number of results to skip
25
+ # @param limit [Integer] Number of results to return
26
+ # @return Response from the API parsed as JSON
27
+ def recall_enforcement_reports(search: [], sort: [], count: [], skip: 0, limit: 1)
28
+ endpoint = "enforcement.json"
29
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
30
+ query = build_query(inputs, {}) # TODO: Upload valid fields
31
+ make_request(endpoint, query)
32
+ end
33
+
34
+ private
35
+
36
+ def endpoint_path
37
+ "/food"
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenFdaApi
4
+ # Interact with the Other API Endpoint:
5
+ # - NSDE
6
+ # - Substance Data Reports
7
+ class Other < Endpoint
8
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/other/nsde/searchable-fields/
9
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/other/nsde/searchable-fields/
10
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/other/nsde/searchable-fields/
11
+ # @param skip [Integer] Number of results to skip
12
+ # @param limit [Integer] Number of results to return
13
+ # @return Response from the API parsed as JSON
14
+ def nsde(search: [], sort: [], count: [], skip: 0, limit: 1)
15
+ endpoint = "nsde.json"
16
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
17
+ query = build_query(inputs, {}) # TODO: Upload valid fields
18
+ make_request(endpoint, query)
19
+ end
20
+
21
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/other/substance/searchable-fields/
22
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/other/substance/searchable-fields/
23
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/other/substance/searchable-fields/
24
+ # @param skip [Integer] Number of results to skip
25
+ # @param limit [Integer] Number of results to return
26
+ # @return Response from the API parsed as JSON
27
+ def substance_data_reports(search: [], sort: [], count: [], skip: 0, limit: 1)
28
+ endpoint = "substance.json"
29
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
30
+ query = build_query(inputs, {}) # TODO: Upload valid fields
31
+ make_request(endpoint, query)
32
+ end
33
+
34
+ private
35
+
36
+ def endpoint_path
37
+ "/other"
38
+ end
39
+ end
40
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenFdaApi
4
+ class InvalidQueryArgument < ArgumentError; end
5
+
4
6
  # A helper to build queries against the openFDA API
5
7
  #
6
8
  # The API supports five query parameters. The basic building block of queries is the search parameter.
@@ -23,30 +25,81 @@ module OpenFdaApi
23
25
  # Use in combination with limit to paginate results. Currently, the largest allowed value for the skip parameter
24
26
  # is 25000. See Paging if you require paging through larger result sets.
25
27
  class QueryBuilder
26
- # @param [Array<Hash>] search
27
- def initialize(search: [])
28
- @search = build_search_string(search)
28
+ # @param [Hash] valid_search_fields
29
+ # @param [QueryInput] query_input
30
+ def initialize(query_input:, valid_search_fields:)
31
+ # TODO: Turn validations back on once we get basic functionality working; need to flex on different field types
32
+ # validate_arguments!(valid_search_fields, query_input: query_input)
33
+ warn "You've passed in a valid_search_fields arg but it isn't being used right now..." if valid_search_fields
34
+ @search = build_query_string(query_fields: query_input.search)
35
+ @sort = build_query_string(query_fields: query_input.sort)
36
+ @count = build_query_string(query_fields: query_input.count)
37
+ @skip = build_skip_string(query_input.skip)
38
+ @limit = query_input.limit
29
39
  end
30
40
 
31
- # @return [String] the query string portion of a request
41
+ # @return [Hash] the query string portion of a request
32
42
  def build_query
33
- # TODO: We currently just build a very basic search string for simple examples like "search=a:b+AND+c:d",
34
- # but it is possible to construct more complex queries and we will need to support that. Sorting, counting,
35
- # setting limits, skipping records, pagination, converting spaces, phrase matching, grouping, and using dates and
36
- # ranges are examples of more complex queries that can be built.
37
- @search
43
+ {
44
+ search: @search,
45
+ sort: @sort,
46
+ count: @count,
47
+ skip: @skip,
48
+ limit: @limit
49
+ }.compact.reject { |_k, v| v.to_s.empty? }
38
50
  end
39
51
 
40
52
  private
41
53
 
42
- def build_search_string(search)
43
- return "" if search.empty?
54
+ def validate_arguments!(valid_search_fields, query_input:)
55
+ # `search` keys must exist in adverse_events_fields.yml
56
+ invalid_fields = get_invalid_fields(valid_search_fields: valid_search_fields, fields: query_input.search)
57
+ raise InvalidQueryArgument, "'search' has invalid fields: #{invalid_fields}" if invalid_fields.any?
58
+
59
+ # `sort` keys must exist in adverse_events_fields.yml
60
+ invalid_fields = get_invalid_fields(valid_search_fields: valid_search_fields, fields: query_input.sort)
61
+ raise InvalidQueryArgument, "'sort' has invalid fields: #{invalid_fields}" if invalid_fields.any?
62
+
63
+ # `count` keys must exist in adverse_events_fields.yml
64
+ invalid_fields = get_invalid_fields(valid_search_fields: valid_search_fields, fields: query_input.count)
65
+ raise InvalidQueryArgument, "'count' has invalid fields: #{invalid_fields}" if invalid_fields.any?
66
+
67
+ # `count` and `skip` cannot be set at the same time
68
+ return unless count_and_skip_set?(query_input.count, query_input.skip)
69
+
70
+ raise InvalidQueryArgument, "'count' and 'skip' cannot both be set at the same time!"
71
+ end
72
+
73
+ def build_query_string(query_fields:)
74
+ return "" if query_fields.empty?
44
75
 
45
- value = search.map do |and_args|
76
+ build_groupings(query_fields).to_s
77
+ end
78
+
79
+ def build_skip_string(skip)
80
+ skip.positive? ? skip.to_s : ""
81
+ end
82
+
83
+ def build_groupings(fields)
84
+ fields.map do |and_args|
46
85
  "(#{and_args.map { |k, v| "#{k}:#{v.gsub(" ", "+")}" }.join("+AND+")})"
47
86
  end.join("+")
87
+ end
88
+
89
+ def get_invalid_fields(valid_search_fields:, fields:)
90
+ # TODO: valid_search_fields define types and we need to check against those
91
+ fields.map(&:keys).flatten.select do |field|
92
+ if field.include?(".") # nested field (e.g. patient.patientagegroup)
93
+ dig_values = field.split(".").join(",properties,").split(",")
94
+ valid_search_fields["properties"].dig(*dig_values).nil?
95
+ else
96
+ !valid_search_fields["properties"].keys.include?(field.to_s)
97
+ end
98
+ end
99
+ end
48
100
 
49
- "search=#{value}"
101
+ def count_and_skip_set?(count, skip)
102
+ skip.positive? && !count.empty?
50
103
  end
51
104
  end
52
105
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenFdaApi
4
+ # Group of inputs to build the query against the API with
5
+ class QueryInputs
6
+ attr_reader :search, :sort, :count, :skip, :limit
7
+
8
+ def initialize(**params)
9
+ @search = params[:search] || []
10
+ @sort = params[:sort] || []
11
+ @count = params[:count] || []
12
+ @skip = params[:skip] || 0
13
+ @limit = params[:limit] || nil
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenFdaApi
4
+ # Interact with the Tobacco API Endpoint:
5
+ # - Problem Reports
6
+ class Tobacco < Endpoint
7
+ # @param search [Array<Hash>] Search fields defined in https://open.fda.gov/apis/tobacco/problem/searchable-fields/
8
+ # @param sort [Array<Hash>] Sort fields defined in https://open.fda.gov/apis/tobacco/problem/searchable-fields/
9
+ # @param count [Array<Hash>] Count fields defined https://open.fda.gov/apis/tobacco/problem/searchable-fields/
10
+ # @param skip [Integer] Number of results to skip
11
+ # @param limit [Integer] Number of results to return
12
+ # @return Response from the API parsed as JSON
13
+ def problem_reports(search: [], sort: [], count: [], skip: 0, limit: 1)
14
+ endpoint = "problem.json"
15
+ inputs = build_inputs(search: search, sort: sort, count: count, skip: skip, limit: limit)
16
+ query = build_query(inputs, {}) # TODO: Upload valid fields
17
+ make_request(endpoint, query)
18
+ end
19
+
20
+ private
21
+
22
+ def endpoint_path
23
+ "/tobacco"
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenFdaApi
4
- VERSION = "0.0.4"
4
+ VERSION = "0.0.12"
5
5
  end
data/lib/open_fda_api.rb CHANGED
@@ -1,13 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "open_fda_api/version"
4
- require_relative "open_fda_api/client"
5
4
 
6
5
  # A Ruby wrapper for the openFDA API: https://open.fda.gov/apis/
7
6
  module OpenFdaApi
8
7
  class Error < StandardError; end
9
8
 
10
- def self.client(api_key: nil)
11
- OpenFdaApi::Client.new(api_key: api_key)
12
- end
9
+ autoload :Client, "open_fda_api/client"
10
+ autoload :Endpoint, "open_fda_api/endpoint"
11
+ autoload :AnimalAndVeterinary, "open_fda_api/animal_and_veterinary"
12
+ autoload :Drugs, "open_fda_api/drugs"
13
+ autoload :Device, "open_fda_api/device"
14
+ autoload :Food, "open_fda_api/food"
15
+ autoload :Other, "open_fda_api/other"
16
+ autoload :Tobacco, "open_fda_api/tobacco"
17
+ autoload :QueryInputs, "open_fda_api/query_inputs"
18
+ autoload :QueryBuilder, "open_fda_api/query_builder"
13
19
  end
data/open_fda_api.gemspec CHANGED
@@ -27,7 +27,10 @@ Gem::Specification.new do |spec|
27
27
  spec.require_paths = ["lib"]
28
28
 
29
29
  # Uncomment to register a new dependency of your gem
30
- # spec.add_dependency "example-gem", "~> 1.0"
30
+ spec.add_dependency "faraday", "~> 1.9"
31
+ spec.add_dependency "faraday_middleware", "~> 1.2"
32
+
33
+ spec.add_development_dependency "pry"
31
34
 
32
35
  # For more information and examples about making a new gem, checkout our
33
36
  # guide at: https://bundler.io/guides/creating_gem.html
metadata CHANGED
@@ -1,15 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: open_fda_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hebron George
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-19 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2022-01-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.9'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday_middleware
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
13
55
  description:
14
56
  email:
15
57
  - hebrontgeorge@gmail.com
@@ -32,9 +74,16 @@ files:
32
74
  - bin/setup
33
75
  - lib/open_fda_api.rb
34
76
  - lib/open_fda_api/adverse_events_fields.yml
77
+ - lib/open_fda_api/animal_and_veterinary.rb
35
78
  - lib/open_fda_api/client.rb
79
+ - lib/open_fda_api/device.rb
36
80
  - lib/open_fda_api/drugs.rb
81
+ - lib/open_fda_api/endpoint.rb
82
+ - lib/open_fda_api/food.rb
83
+ - lib/open_fda_api/other.rb
37
84
  - lib/open_fda_api/query_builder.rb
85
+ - lib/open_fda_api/query_inputs.rb
86
+ - lib/open_fda_api/tobacco.rb
38
87
  - lib/open_fda_api/version.rb
39
88
  - open_fda_api.gemspec
40
89
  homepage: https://github.com/hebron-george/open_fda_api