open_fda_api 0.0.5 → 0.0.13

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: 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