open_fda_api 0.0.5 → 0.0.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b43a53d3d7cd5c3390f024979ac198ba6ee4d79ac0bd43cdf60caaaebfe230bc
4
- data.tar.gz: 7720bc8e73810774e0a88b2b0579b3cfa91d320952d6ab090dac1215c596164e
3
+ metadata.gz: ea876aa2b2ba1588293e37e59f95271ec0361215a5fa68ce1cbfb447eba764d0
4
+ data.tar.gz: 1c19caa9c5e140dcefa5cbeee8acaa1fd1c48d6553b36f55b86505664ef10301
5
5
  SHA512:
6
- metadata.gz: c2616cbb3fa9a3c648d6e46084a81e0e5f03e14ab57ca5b08628a3cfb7e16b1fa3b99fddea95d4e1b5ba749e4e946092ccb20b0e5caa6806ba3769a3b95d6839
7
- data.tar.gz: 902d27c4d41762a2557f5ac2a6126df84d3b71ffbe7b82a209210bb88b1acc5783e77f35506ec4d4f50758399a8adfb782c110a1f1b022c8eb975d6c2641b9d5
6
+ metadata.gz: 73786ceb0a046b754ad438c67393f3c7baa5c8205266d21df276be6e69ba06bbd5ed94488941364347bbccf0e69ca93874f7db1ad7f1c2bb942a5ae81798413c
7
+ data.tar.gz: 2f5b335ae39a62f77d72e436caf06a9b7304e02c3c0c81057a082ceb416ad2dff936003c98b7ac36c57fef47a3d64c58b6c4c215c0cbf7acc2ad6e2ecf33798c
data/CHANGELOG.md CHANGED
@@ -1,4 +1,36 @@
1
- ## [Unreleased]
1
+ ## openFDA API
2
+
3
+ ## [0.0.13] - 2022-01-27
4
+ - Set API key if it is available and send it in with every request
5
+
6
+ ## [0.0.12] - 2022-01-24
7
+ - Animal & Veterinary Endpoint
8
+ - Tobacco Endpoint
9
+ - Other Endpoint
10
+
11
+ ## [0.0.11] - 2022-01-24
12
+ - Include Food endpoints
13
+
14
+ ## [0.0.10] - 2022-01-24
15
+ - Include Device endpoints
16
+ - Pull out Endpoint common behavior to a base class
17
+
18
+ ## [0.0.9] - 2022-01-24
19
+ - Filled out the following Drug endpoints:
20
+ - product_labeling
21
+ - ndc_directory
22
+ - recall_enforcement_reports
23
+ - drugs_at_fda
24
+
25
+ ## [0.0.8] - 2022-01-24
26
+ - Delete the `OpenFdaApi.client` method since its only function was to forward messages
27
+
28
+ ## [0.0.7] - 2022-01-23
29
+ - Use Faraday instead of using Net::HTTP directly
30
+ - Introduce `QueryInputs` to group query params passed in together
31
+
32
+ ## [0.0.6] - 2022-01-21
33
+ - Support for more query fields
2
34
 
3
35
  ## [0.0.5] - 2022-01-20
4
36
  - Validate, against search fields given to us from openFDA API, when building queries.
data/Gemfile.lock CHANGED
@@ -1,7 +1,9 @@
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/
@@ -9,7 +11,33 @@ GEM
9
11
  ast (2.4.2)
10
12
  coderay (1.1.3)
11
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)
12
39
  method_source (1.0.0)
40
+ multipart-post (2.1.1)
13
41
  parallel (1.21.0)
14
42
  parser (3.1.0.0)
15
43
  ast (~> 2.4.1)
@@ -45,6 +73,7 @@ GEM
45
73
  rubocop-ast (1.15.1)
46
74
  parser (>= 3.0.1.1)
47
75
  ruby-progressbar (1.11.0)
76
+ ruby2_keywords (0.0.5)
48
77
  unicode-display_width (2.1.0)
49
78
 
50
79
  PLATFORMS
data/README.md CHANGED
@@ -16,37 +16,95 @@ And then execute:
16
16
  bundle install
17
17
  ```
18
18
 
19
- ## Documentation
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`.
19
+ ## Usage
24
20
 
21
+ ```ruby
22
+ client = OpenFdaApi::Client.new
23
+ # or if you have registered an API key
24
+ client = OpenFdaApi::Client.new(api_key: ENV['OPEN_FDA_API_KEY'])
25
+
26
+ # First 20 results where (fieldA=foo AND fieldB=bar) OR (fieldC=baz AND fieldA exists)
27
+ # Sorted by (fieldD) in descending order
28
+ # Skip the first 8 results
29
+ args = {
30
+ search: [{"fieldA" => "foo", "fieldB" => "bar"}, {"fieldC" => "baz", "_exists_" => "fieldA"}],
31
+ sort: [{"fieldD" => "desc"}],
32
+ skip: 8,
33
+ limit: 20
34
+ }
35
+
36
+ # Animal & Veterinary API
37
+ client.animal_and_veterinary.adverse_events(args)
38
+
39
+ # Drug API
40
+ client.drugs.adverse_events(args)
41
+ client.drugs.product_labeling(args)
42
+ client.drugs.ndc_directory(args)
43
+ client.drugs.recall_enforcement_reports(args)
44
+ client.drugs.drugs_at_fda(args)
45
+
46
+ # Device API
47
+ client.device.premarket_510ks(args)
48
+ client.device.classification(args)
49
+ client.device.recall_enforcement_reports(args)
50
+ client.device.adverse_events(args)
51
+ client.device.premarket_approval(args)
52
+ client.device.recalls(args)
53
+ client.device.registrations_and_listings(args)
54
+ client.device.covid19_serological_tests(args)
55
+ client.device.unique_device_identifier(args)
56
+
57
+ # Food API
58
+ client.food.recall_enforcement_reports(args)
59
+ client.food.adverse_events(args)
60
+
61
+ # Other API
62
+ client.other.nsde(args)
63
+ client.other.substance_data_reports(args)
64
+
65
+ # Tobacco API
66
+ client.tobacco.problem_reports(args)
67
+ ```
25
68
 
26
- ### Drug
69
+ ### Querying
27
70
 
28
- The Drug API has the following endpoints: Adverse Events, Product Labeling, NDC Directory, Recall Enforcement Reports, and Drugs@FDA.
71
+ The openFDA API can be queried with these arguments: `search`, `sort`, `count`, `skip`, and `limit`.
29
72
 
30
- Here's how you interact with each:
73
+ `search`, `sort`, and `count` have the same format. They are arrays of hashes. Each hash has a set of fields and values
74
+ that are ANDed together and all the elements in the array are ORed together. Here are some examples to illustrate:
31
75
 
32
- #### Adverse Events
33
76
  ```ruby
34
- require 'open_fda_api'
35
-
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
+ # Default arguments
78
+ args = {
79
+ search: [],
80
+ sort: [],
81
+ count: [],
82
+ skip: 0,
83
+ limit: 1,
84
+ }
85
+
86
+ # Search for a single field
87
+ args = {
88
+ search: [{ fieldA: "value" }]
89
+ }
90
+
91
+ # Search for field A AND field B
92
+ args = {
93
+ search: [{ fieldA: "value", fieldB: "other value" }]
94
+ }
95
+
96
+ # Search for field A OR field B
97
+ args = {
98
+ search: [{ fieldA: "value"}, { fieldB: "other value" }]
99
+ }
100
+
101
+ # Search for field A or field B, and skip the first 10 results
102
+ args = {
103
+ search: [{ fieldA: "value"}, { fieldB: "other value" }],
104
+ skip: 10
105
+ }
41
106
  ```
42
107
 
43
- ### Device (Not Implemented Yet)
44
- ### Food (Not Implemented Yet)
45
- ### Other (Not Implemented Yet)
46
- ### Tobacco (Not Implemented Yet)
47
-
48
-
49
-
50
108
  ## Development
51
109
 
52
110
  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,52 +1,88 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "net/http"
4
- require "json"
5
3
  require "yaml"
6
- require "open_fda_api/query_builder"
7
4
 
8
5
  module OpenFdaApi
9
6
  # Interact with the Drugs API Endpoint:
10
- # - Adverse Events
7
+ # - Adverse Events
11
8
  # - Product Labeling
12
9
  # - NDC Directory
13
10
  # - Recall Enforcement Reports
14
11
  # - Drugs@FDA)
15
- class Drugs
16
- def initialize
17
- @host = "api.fda.gov"
18
- @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)
19
24
  end
20
25
 
21
- # The openFDA drug adverse event API returns data that has been collected from the
22
- # FDA Adverse Event Reporting System (FAERS), a database that contains information on
23
- # adverse event and medication error reports submitted to FDA.
24
- #
25
- # @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
26
31
  # @return Response from the API parsed as JSON
27
- def adverse_events(search_arguments: [])
28
- endpoint = "/event.json"
29
- query = build_query(search_arguments, self.class.valid_adverse_events_fields)
30
- url = build_url(endpoint, query)
31
- 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)
32
37
  end
33
38
 
34
- def self.valid_adverse_events_fields
35
- @valid_adverse_events_fields ||= ::YAML.load_file("#{__dir__}/adverse_events_fields.yml")
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)
36
50
  end
37
51
 
38
- private
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
39
64
 
40
- def build_url(endpoint, query)
41
- 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)
42
76
  end
43
77
 
44
- def build_query(search_arguments, valid_search_fields)
45
- QueryBuilder.new(search: search_arguments, valid_search_fields: valid_search_fields).build_query
78
+ def self.valid_adverse_events_fields
79
+ @valid_adverse_events_fields ||= ::YAML.load_file("#{__dir__}/adverse_events_fields.yml")
46
80
  end
47
81
 
48
- def make_request(url)
49
- JSON.parse(Net::HTTP.get(url))
82
+ private
83
+
84
+ def endpoint_path
85
+ "/drug"
50
86
  end
51
87
  end
52
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, api_key: client.api_key)
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
@@ -25,53 +25,71 @@ module OpenFdaApi
25
25
  # Use in combination with limit to paginate results. Currently, the largest allowed value for the skip parameter
26
26
  # is 25000. See Paging if you require paging through larger result sets.
27
27
  class QueryBuilder
28
- # @param [Array<Hash>] search
29
- def initialize(valid_search_fields:, search: [], sort: [], count: [], skip: 0)
30
- validate_arguments!(valid_search_fields, search: search, sort: sort, count: count, skip: skip)
31
- @search = build_search_string(search)
28
+ # @param [Hash] valid_search_fields
29
+ # @param [QueryInputs] 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
39
+ @api_key = query_input.api_key
32
40
  end
33
41
 
34
- # @return [String] the query string portion of a request
42
+ # @return [Hash] the query string portion of a request
35
43
  def build_query
36
- # TODO: We currently just build a very basic search string for simple examples like "search=a:b+AND+c:d",
37
- # but it is possible to construct more complex queries and we will need to support that. Sorting, counting,
38
- # setting limits, skipping records, pagination, converting spaces, phrase matching, grouping, and using dates and
39
- # ranges are examples of more complex queries that can be built.
40
- @search
44
+ {
45
+ api_key: @api_key,
46
+ search: @search,
47
+ sort: @sort,
48
+ count: @count,
49
+ skip: @skip,
50
+ limit: @limit
51
+ }.compact.reject { |_k, v| v.to_s.empty? }
41
52
  end
42
53
 
43
54
  private
44
55
 
45
- def validate_arguments!(valid_search_fields, search:, sort:, count:, skip:)
56
+ def validate_arguments!(valid_search_fields, query_input:)
46
57
  # `search` keys must exist in adverse_events_fields.yml
47
- invalid_fields = get_invalid_fields(valid_search_fields: valid_search_fields, fields: search)
58
+ invalid_fields = get_invalid_fields(valid_search_fields: valid_search_fields, fields: query_input.search)
48
59
  raise InvalidQueryArgument, "'search' has invalid fields: #{invalid_fields}" if invalid_fields.any?
49
60
 
50
61
  # `sort` keys must exist in adverse_events_fields.yml
51
- invalid_fields = get_invalid_fields(valid_search_fields: valid_search_fields, fields: sort)
62
+ invalid_fields = get_invalid_fields(valid_search_fields: valid_search_fields, fields: query_input.sort)
52
63
  raise InvalidQueryArgument, "'sort' has invalid fields: #{invalid_fields}" if invalid_fields.any?
53
64
 
54
65
  # `count` keys must exist in adverse_events_fields.yml
55
- invalid_fields = get_invalid_fields(valid_search_fields: valid_search_fields, fields: count)
66
+ invalid_fields = get_invalid_fields(valid_search_fields: valid_search_fields, fields: query_input.count)
56
67
  raise InvalidQueryArgument, "'count' has invalid fields: #{invalid_fields}" if invalid_fields.any?
57
68
 
58
69
  # `count` and `skip` cannot be set at the same time
59
- return unless count_and_skip_set?(count, skip)
70
+ return unless count_and_skip_set?(query_input.count, query_input.skip)
60
71
 
61
72
  raise InvalidQueryArgument, "'count' and 'skip' cannot both be set at the same time!"
62
73
  end
63
74
 
64
- def build_search_string(search)
65
- return "" if search.empty?
75
+ def build_query_string(query_fields:)
76
+ return "" if query_fields.empty?
66
77
 
67
- value = search.map do |and_args|
78
+ build_groupings(query_fields).to_s
79
+ end
80
+
81
+ def build_skip_string(skip)
82
+ skip.positive? ? skip.to_s : ""
83
+ end
84
+
85
+ def build_groupings(fields)
86
+ fields.map do |and_args|
68
87
  "(#{and_args.map { |k, v| "#{k}:#{v.gsub(" ", "+")}" }.join("+AND+")})"
69
88
  end.join("+")
70
-
71
- "search=#{value}"
72
89
  end
73
90
 
74
91
  def get_invalid_fields(valid_search_fields:, fields:)
92
+ # TODO: valid_search_fields define types and we need to check against those
75
93
  fields.map(&:keys).flatten.select do |field|
76
94
  if field.include?(".") # nested field (e.g. patient.patientagegroup)
77
95
  dig_values = field.split(".").join(",properties,").split(",")
@@ -0,0 +1,17 @@
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, :api_key
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
+ @api_key = params[:api_key] || nil
15
+ end
16
+ end
17
+ 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.5"
4
+ VERSION = "0.0.13"
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,9 @@ 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
+
31
33
  spec.add_development_dependency "pry"
32
34
 
33
35
  # For more information and examples about making a new gem, checkout our
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: open_fda_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.13
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-20 00:00:00.000000000 Z
11
+ date: 2022-01-27 00:00:00.000000000 Z
12
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'
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: pry
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -46,9 +74,16 @@ files:
46
74
  - bin/setup
47
75
  - lib/open_fda_api.rb
48
76
  - lib/open_fda_api/adverse_events_fields.yml
77
+ - lib/open_fda_api/animal_and_veterinary.rb
49
78
  - lib/open_fda_api/client.rb
79
+ - lib/open_fda_api/device.rb
50
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
51
84
  - lib/open_fda_api/query_builder.rb
85
+ - lib/open_fda_api/query_inputs.rb
86
+ - lib/open_fda_api/tobacco.rb
52
87
  - lib/open_fda_api/version.rb
53
88
  - open_fda_api.gemspec
54
89
  homepage: https://github.com/hebron-george/open_fda_api