ctg 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 253da2955e02d909c0f837ce71754ff59db75ce89184ffbd4e88c57d78f69620
4
+ data.tar.gz: '09738e44927002ad9b592ffd5b681b4948c28b61a26bf4d2efc04f6bdf565864'
5
+ SHA512:
6
+ metadata.gz: ee424742892b4373f3c4738273c7a7f1b4aaa5e66a6f88c50a4f2d90b5e2e5c767ebd2ad35d630610b8399f6aafdd6161edc2512cf5a6898be695417c11b3e17
7
+ data.tar.gz: 66019a9f7d97dfb64a9f9f3e8d0b0b3dc9c2ce28bd8abf49dbdc1c528e202d3b9b7616bd6c4ea4ecc2493985c37a0a14760d1092c63a4cb15d7b7bb6abd12bdf
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Leonid Stoianov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,300 @@
1
+ # CTG
2
+
3
+ `CTG` is a Ruby client library for interacting with the ClinicalTrials.gov API. It allows you to fetch and query clinical study data, including study metadata, search areas, study sizes, field values, and more. The library supports both JSON and CSV response formats, with built-in support for pagination and advanced querying.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'ctg'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ gem install ctg
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Initializing the Client
28
+
29
+ To start using the `CTG`, first create a new client instance:
30
+
31
+ ```ruby
32
+ require 'ctg'
33
+
34
+ client = CTG.new
35
+ ```
36
+
37
+ ### Fetching Studies
38
+
39
+ You can fetch studies using the `studies` method. This will return a `CTG::Response` object that you can query.
40
+
41
+ ```ruby
42
+ response = client.studies('query.term' => 'cancer')
43
+ studies = response.query('studies')
44
+ puts studies
45
+ ```
46
+
47
+ ### Fetching a Single Study by NCT Number
48
+
49
+ You can fetch a single study by its NCT number:
50
+
51
+ ```ruby
52
+ response = client.number('NCT04050163')
53
+ study = response.query('nctId')
54
+ puts study
55
+ ```
56
+
57
+ ### Fetching Study Metadata
58
+
59
+ To fetch metadata information about study fields:
60
+
61
+ ```ruby
62
+ response = client.metadata
63
+ metadata = response.data
64
+ puts metadata
65
+ ```
66
+
67
+ ### Handling Pagination
68
+
69
+ If your query returns multiple pages of results, you can fetch the next page using the `next_page` method:
70
+
71
+ ```ruby
72
+ response = client.studies('query.term' => 'cancer', 'pageSize' => 5)
73
+ puts response.query('studies')
74
+
75
+ next_page_response = response.next_page
76
+ puts next_page_response.query('studies') if next_page_response
77
+ ```
78
+
79
+ ### Querying the Response
80
+
81
+ You can query JSON responses using keys or XPath expressions:
82
+
83
+ ```ruby
84
+ response = client.number('NCT04050163')
85
+ nct_id = response.query('nctId')
86
+ puts nct_id # Outputs 'NCT04050163'
87
+ ```
88
+
89
+ For CSV responses, query by column name:
90
+
91
+ ```ruby
92
+ response = client.number('NCT04050163', format: 'csv')
93
+ nct_ids = response.query('NCT Number')
94
+ puts nct_ids
95
+ ```
96
+
97
+ ### Finding a Specific Key in JSON Data
98
+
99
+ You can find the first occurrence of a specific key within the JSON data using the `find` method:
100
+
101
+ ```ruby
102
+ response = client.number('NCT04050163')
103
+ nct_id = response.find('nctId')
104
+ puts nct_id # Outputs 'NCT04050163'
105
+ ```
106
+
107
+ ## CTG::Query Object
108
+
109
+ The `CTG::Query` class is designed to help you build complex queries for searching clinical studies on ClinicalTrials.gov. It provides a convenient way to add different query parameters and filters, allowing for advanced search capabilities.
110
+
111
+ ### Creating a Query
112
+
113
+ To start building a query, you first need to create an instance of the `CTG::Query` class:
114
+
115
+ ```ruby
116
+ query = CTG::Query.new
117
+ ```
118
+
119
+ ### Adding Query Parameters
120
+
121
+ The `CTG::Query` object allows you to add various search parameters using method chaining. Below are the available methods:
122
+
123
+ #### `condition(condition)`
124
+ Adds a condition or disease to the query.
125
+
126
+ - **Parameter**: `condition` (String) - The condition or disease to search for.
127
+ - **Returns**: The updated `CTG::Query` object.
128
+
129
+ ```ruby
130
+ query = CTG::Query.new.condition('diabetes')
131
+ ```
132
+
133
+ #### `term(term)`
134
+ Adds additional search terms to the query.
135
+
136
+ - **Parameter**: `term` (String) - Additional search terms.
137
+ - **Returns**: The updated `CTG::Query` object.
138
+
139
+ ```ruby
140
+ query = CTG::Query.new.term('lung cancer')
141
+ ```
142
+
143
+ #### `location(location)`
144
+ Adds a location filter to the query.
145
+
146
+ - **Parameter**: `location` (String) - The location term to filter results by.
147
+ - **Returns**: The updated `CTG::Query` object.
148
+
149
+ ```ruby
150
+ query = CTG::Query.new.location('New York')
151
+ ```
152
+
153
+ #### `title(title)`
154
+ Adds a title or acronym to the query.
155
+
156
+ - **Parameter**: `title` (String) - The title or acronym to search for.
157
+ - **Returns**: The updated `CTG::Query` object.
158
+
159
+ ```ruby
160
+ query = CTG::Query.new.title('COVID-19 Vaccine')
161
+ ```
162
+
163
+ #### `intervention(intervention)`
164
+ Adds an intervention or treatment to the query.
165
+
166
+ - **Parameter**: `intervention` (String) - The intervention or treatment to search for.
167
+ - **Returns**: The updated `CTG::Query` object.
168
+
169
+ ```ruby
170
+ query = CTG::Query.new.intervention('remdesivir')
171
+ ```
172
+
173
+ #### `outcome(outcome)`
174
+ Adds an outcome measure to the query.
175
+
176
+ - **Parameter**: `outcome` (String) - The outcome measure to search for.
177
+ - **Returns**: The updated `CTG::Query` object.
178
+
179
+ ```ruby
180
+ query = CTG::Query.new.outcome('survival rate')
181
+ ```
182
+
183
+ #### `collaborator(collaborator)`
184
+ Adds a sponsor or collaborator to the query.
185
+
186
+ - **Parameter**: `collaborator` (String) - The sponsor or collaborator to search for.
187
+ - **Returns**: The updated `CTG::Query` object.
188
+
189
+ ```ruby
190
+ query = CTG::Query.new.collaborator('IQVIA')
191
+ ```
192
+
193
+ #### `sponsor(lead_sponsor)`
194
+ Adds a lead sponsor to the query.
195
+
196
+ - **Parameter**: `sponsor` (String) - The lead sponsor to search for.
197
+ - **Returns**: The updated `CTG::Query` object.
198
+
199
+ ```ruby
200
+ query = CTG::Query.new.sponsor('National Institutes of Health')
201
+ ```
202
+
203
+ #### `study_id(study_id)`
204
+ Adds a study ID filter to the query.
205
+
206
+ - **Parameter**: `study_id` (String) - The study ID or IDs to search for (comma- or space-separated).
207
+ - **Returns**: The updated `CTG::Query` object.
208
+
209
+ ```ruby
210
+ query = CTG::Query.new.study_id('NCT01234567')
211
+ ```
212
+
213
+ #### `status(*statuses)`
214
+ Adds an overall status filter to the query.
215
+
216
+ - **Parameter**: `statuses` (Array<String>) - The list of statuses to filter by (e.g., `['RECRUITING', 'COMPLETED']`).
217
+ - **Returns**: The updated `CTG::Query` object.
218
+
219
+ ```ruby
220
+ query = CTG::Query.new.status('RECRUITING', 'COMPLETED')
221
+ ```
222
+
223
+ #### `page_size(size)`
224
+ Specifies the number of results per page.
225
+
226
+ - **Parameter**: `size` (Integer) - The number of results to return per page (max 1000).
227
+ - **Returns**: The updated `CTG::Query` object.
228
+
229
+ ```ruby
230
+ query = CTG::Query.new.page_size(50)
231
+ ```
232
+
233
+ #### `format(format)`
234
+ Specifies the format of the response.
235
+
236
+ - **Parameter**: `format` (String) - The format of the response (`'json'` or `'csv'`).
237
+ - **Returns**: The updated `CTG::Query` object.
238
+
239
+ ```ruby
240
+ query = CTG::Query.new.format('json')
241
+ ```
242
+
243
+ ### Retrieving Query Parameters
244
+
245
+ Once you’ve built your query, you can retrieve the complete set of query parameters as a hash:
246
+
247
+ ```ruby
248
+ query_params = query.params
249
+ ```
250
+
251
+ This `params` method returns a hash of all the query parameters that have been set.
252
+
253
+ ### Example: Building a Complex Query
254
+
255
+ Here’s an example of how you might build a more complex query using method chaining:
256
+
257
+ ```ruby
258
+ query = CTG::Query.new
259
+ .condition('diabetes')
260
+ .term('insulin')
261
+ .location('California')
262
+ .status('RECRUITING')
263
+ .page_size(10)
264
+ .format('json')
265
+
266
+ # Use the query in a client request
267
+ response = client.studies(query)
268
+ puts response.query('studies', 'protocolSection', 'outcomesModule', 'secondaryOutcomes' )
269
+ ```
270
+
271
+ In this example, the query is set to search for recruiting studies related to diabetes and insulin in California, returning results in JSON format with a page size of 10.
272
+
273
+ ### Finding All Occurrences of a Key in JSON Data
274
+
275
+ If you need to find all occurrences of a specific key within the JSON data, use the `find_all` method:
276
+
277
+ ```ruby
278
+ references = response.find_all('references')
279
+ puts references
280
+ ```
281
+
282
+
283
+ ## Running Tests
284
+
285
+ To run the tests, use RSpec:
286
+
287
+ ```bash
288
+ rspec
289
+ ```
290
+
291
+ This will run all the tests located in the `spec/` directory and report on their success.
292
+
293
+ ## Contributing
294
+
295
+ Bug reports and pull requests are welcome on GitHub at https://github.com/trialize/ctg.
296
+
297
+ ## License
298
+
299
+ This library is available as open source under the terms of the MIT License.
300
+ Copyright 2024 Leonid Stoianov. Copyright 2024 Trialize.
data/lib/ctg/ctg.rb ADDED
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file defines the main CTG class, which serves as the interface to interact
4
+ # with the ClinicalTrials.gov API. It includes methods to fetch data for studies, metadata,
5
+ # areas, and statistics. Each method returns a Response object that encapsulates the data
6
+ # along with pagination capabilities.
7
+ #
8
+ # Methods:
9
+ # - `studies`: Fetches a list of studies based on query parameters with support for pagination.
10
+ # - `number`: Fetches data for a single study by its NCT number.
11
+ # - `metadata`: Fetches metadata information about the study fields.
12
+ # - `areas`: Fetches the list of search areas.
13
+
14
+ require 'active_support/core_ext/hash'
15
+ require 'httparty'
16
+
17
+ require_relative 'query'
18
+ require_relative 'response/json_response'
19
+ require_relative 'response/csv_response'
20
+ require_relative 'response'
21
+
22
+ class CTG
23
+ include HTTParty
24
+ base_uri 'https://clinicaltrials.gov/api/v2'
25
+ attr_reader :response
26
+
27
+ def initialize
28
+ @headers = { 'Content-Type' => 'application/json' }
29
+ @response = nil
30
+ end
31
+
32
+ # Fetch a list of studies based on query parameters with support for pagination
33
+ # @param [CTG::Query, Hash] query - Query parameters for filtering studies
34
+ # @return [CTG::Response] - Response object wrapping the API response
35
+ def studies(query)
36
+ params = if query.is_a?(CTG::Query)
37
+ query.params
38
+ else
39
+ query.stringify_keys
40
+ end
41
+
42
+ get('/studies', params)
43
+
44
+ CTG::Response.parse(@response.body, params['format'] || 'json', self)
45
+ end
46
+
47
+ # Fetch data for a single study by its NCT number
48
+ # @param [String] nct_id - The NCT ID of the study
49
+ # @param [Hash] params - Additional query parameters
50
+ # @option params [String] :format ('json') - The format of the response ('json', 'csv', etc.)
51
+ # @option params [Array<String>] :fields - A list of fields to include in the response
52
+ # @option params [String] :markupFormat - Format for fields that include markup ('markdown', 'legacy')
53
+ # @return [CTG::Response] - Response object wrapping the API response
54
+ def number(nct_id, params = {})
55
+ get("/studies/#{nct_id}", params)
56
+ CTG::Response.parse(@response.body, params.stringify_keys['format'] || 'json', self)
57
+ end
58
+
59
+ # Fetch metadata information about the study fields
60
+ # @param [Hash] params - Additional query parameters
61
+ # @option params [Boolean] :includeIndexedOnly (false) - If true, includes fields that are indexed only
62
+ # @option params [Boolean] :includeHistoricOnly (false) - If true, includes fields available only in historic data
63
+ # @return [CTG::Response] - Response object wrapping the API response
64
+ def metadata(params = {})
65
+ get('/studies/metadata', params)
66
+ CTG::Response.parse(@response.body, 'json', self)
67
+ end
68
+
69
+ # Fetch the list of search areas
70
+ # This method doesn't require any specific query parameters.
71
+ # @return [CTG::Response] - Response object wrapping the API response
72
+ def areas
73
+ get('/studies/search-areas')
74
+ CTG::Response.parse(@response.body, 'json', self)
75
+ end
76
+
77
+ # Fetch statistics of study JSON sizes
78
+ # @return [CTG::Response] - Response object wrapping the API response
79
+ def study_sizes
80
+ get('/stats/size')
81
+ CTG::Response.parse(@response.body, 'json', self)
82
+ end
83
+
84
+ # Fetch value statistics of study leaf fields
85
+ # @param [Hash] params - Query parameters for filtering field values
86
+ # @option params [Array<String>] :types - Filter by field types (e.g., 'ENUM', 'BOOLEAN', 'STRING')
87
+ # @option params [Array<String>] :fields - Filter by specific fields or field paths of leaf fields
88
+ # @return [CTG::Response] - Response object wrapping the API response
89
+ def field_values(params = {})
90
+ get('/stats/field/values', params)
91
+ CTG::Response.parse(@response.body, 'json', self)
92
+ end
93
+
94
+ # Fetch sizes of list/array fields
95
+ # @param [Hash] params - Query parameters for filtering field sizes
96
+ # @option params [Array<String>] :fields - Filter by specific list/array fields or field paths
97
+ # @return [CTG::Response] - Response object wrapping the API response
98
+ def field_sizes(params = {})
99
+ get('/stats/field/sizes', params)
100
+ CTG::Response.parse(@response.body, 'json', self)
101
+ end
102
+
103
+
104
+ private
105
+
106
+ # @param [String] path - The API endpoint path to send the request to.
107
+ # @param [Hash] params - Query parameters to include in the request.
108
+ # @option params [Hash<String, String>] :headers - Optional headers to include in the request.
109
+ # @return [HTTParty::Response] - The raw response object from the HTTP request.
110
+ def get(path, params = {})
111
+ options = { headers: @headers, query: params }
112
+ @response = self.class.get(path, options)
113
+ end
114
+
115
+ end
data/lib/ctg/query.rb ADDED
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The `CTG::Query` class provides a way to build queries for the ClinicalTrials.gov API.
4
+ # It allows chaining methods to specify various search parameters, including conditions,
5
+ # terms, locations, sponsors, and more. The class also supports pagination, sorting, and
6
+ # filtering of results. The built query can be used to fetch data in either JSON or CSV format.
7
+ #
8
+ # Example usage:
9
+ # query = CTG::Query.new
10
+ # .condition('diabetes')
11
+ # .location('New York')
12
+ # .status('RECRUITING', 'COMPLETED')
13
+ # .page_size(50)
14
+ # .sort_by('startDate', 'desc')
15
+ #
16
+ # client.studies(query.params)
17
+ #
18
+ # Methods:
19
+ # - `condition`: Adds a condition or disease to the query.
20
+ # - `term`: Adds a term to the query.
21
+ # - `location`: Adds a location filter to the query.
22
+ # - `title`: Adds a title or acronym to the query.
23
+ # - `intervention`: Adds an intervention or treatment to the query.
24
+ # - `outcome`: Adds an outcome measure to the query.
25
+ # - `sponsor`: Adds a sponsor or collaborator to the query.
26
+ # - `lead_sponsor`: Adds a lead sponsor to the query.
27
+ # - `study_id`: Adds a study ID filter to the query.
28
+ # - `patient`: Adds a patient-related search to the query.
29
+ # - `status`: Adds an overall status filter to the query.
30
+ # - `geo_filter`: Adds a geographical filter based on distance.
31
+ # - `nct_ids`: Adds a filter for specific NCT IDs.
32
+ # - `advanced_filter`: Adds an advanced filter using the Essie expression syntax.
33
+ # - `format`: Sets the response format (json or csv).
34
+ # - `page_size`: Sets the number of results per page.
35
+ # - `sort_by`: Sets the sorting order for the results.
36
+ # - `total`: Counts the total number of results.
37
+ # - `page_token`: Sets the token to get the next page of results.
38
+
39
+ class CTG
40
+ class Query
41
+ attr_reader :params
42
+
43
+ def initialize
44
+ @params = {}
45
+ end
46
+
47
+ # Add a condition or disease to the query
48
+ # @param [String] condition - The condition or disease to filter studies by
49
+ # @return [CTG::Query] - The current instance for method chaining
50
+ def condition(condition)
51
+ @params['query.cond'] = condition
52
+ self
53
+ end
54
+
55
+ # Add a term to the query
56
+ # @param [String] term - Additional term to filter studies by
57
+ # @return [CTG::Query] - The current instance for method chaining
58
+ def term(term)
59
+ @params['query.term'] = term
60
+ self
61
+ end
62
+
63
+ # Add a location filter to the query
64
+ # @param [String] location - The location term to filter studies by
65
+ # @return [CTG::Query] - The current instance for method chaining
66
+ def location(location)
67
+ @params['query.locn'] = location
68
+ self
69
+ end
70
+
71
+ # Add a title or acronym to the query
72
+ # @param [String] title - The title or acronym to filter studies by
73
+ # @return [CTG::Query] - The current instance for method chaining
74
+ def title(title)
75
+ @params['query.titles'] = title
76
+ self
77
+ end
78
+
79
+ # Add an intervention or treatment to the query
80
+ # @param [String] intervention - The intervention or treatment to filter studies by
81
+ # @return [CTG::Query] - The current instance for method chaining
82
+ def intervention(intervention)
83
+ @params['query.intr'] = intervention
84
+ self
85
+ end
86
+
87
+ # Add an outcome measure to the query
88
+ # @param [String] outcome - The outcome measure to filter studies by
89
+ # @return [CTG::Query] - The current instance for method chaining
90
+ def outcome(outcome)
91
+ @params['query.outc'] = outcome
92
+ self
93
+ end
94
+
95
+ # Add a sponsor or collaborator to the query
96
+ # @param [String] sponsor - The sponsor or collaborator to filter studies by
97
+ # @return [CTG::Query] - The current instance for method chaining
98
+ def sponsor(sponsor)
99
+ @params['query.spons'] = sponsor
100
+ self
101
+ end
102
+
103
+ # Add a lead sponsor to the query
104
+ # @param [String] lead_sponsor - The lead sponsor to filter studies by
105
+ # @return [CTG::Query] - The current instance for method chaining
106
+ def lead_sponsor(lead_sponsor)
107
+ @params['query.lead'] = lead_sponsor
108
+ self
109
+ end
110
+
111
+ # Adds a study ID filter to the query
112
+ # @param [String] study_id - The study ID or IDs to search for (comma- or space-separated)
113
+ # @return [CTG::Query] - The updated query object
114
+ def study_id(study_id)
115
+ @params['query.id'] = study_id
116
+ self
117
+ end
118
+
119
+ # Add a patient-related search to the query
120
+ # @param [String] patient - The patient-related search term
121
+ # @return [CTG::Query] - The current instance for method chaining
122
+ def patient(patient)
123
+ @params['query.patient'] = patient
124
+ self
125
+ end
126
+
127
+ # Adds an overall status filter to the query
128
+ # @param [Array<String>] statuses - The list of statuses to filter by (e.g., ['RECRUITING', 'COMPLETED'])
129
+ # @return [CTG::Query] - The updated query object
130
+ def status(*statuses)
131
+ @params['filter.overallStatus'] = statuses.join(',')
132
+ self
133
+ end
134
+
135
+ # Add a geographical filter based on distance
136
+ # @param [String] geo - The geo-function filter (e.g., "distance(39.0035707,-77.1013313,50mi)")
137
+ # @return [CTG::Query] - The current instance for method chaining
138
+ def geo_filter(geo)
139
+ @params['filter.geo'] = geo
140
+ self
141
+ end
142
+
143
+ # Add a filter for specific NCT IDs
144
+ # @param [Array<String>] ids - List of NCT IDs to filter by
145
+ # @return [CTG::Query] - The current instance for method chaining
146
+ def nct_ids(*ids)
147
+ @params['filter.ids'] = ids.join('|')
148
+ self
149
+ end
150
+
151
+ # Add an advanced filter using the Essie expression syntax
152
+ # @param [String] expression - The advanced filter expression
153
+ # @return [CTG::Query] - The current instance for method chaining
154
+ def advanced_filter(expression)
155
+ @params['filter.advanced'] = expression
156
+ self
157
+ end
158
+
159
+ # Set the response format (json or csv)
160
+ # @param [String] format - The format for the response, either 'json' or 'csv'
161
+ # @return [CTG::Query] - The current instance for method chaining
162
+ def format(format = 'json')
163
+ @params['format'] = format
164
+ self
165
+ end
166
+
167
+ # Set the number of results per page.
168
+ # If not specified or set to 0, the default value will be used. It will be coerced down to 1,000, if greater than that.
169
+ # @param [Integer] page_size - The number of results to return per page
170
+ # @return [CTG::Query] - The current instance for method chaining
171
+ def page_size(page_size = 1000)
172
+ @params['pageSize'] = page_size
173
+ self
174
+ end
175
+
176
+ # Set the sorting order for the results
177
+ # @param [String] sort_by - The field to sort by
178
+ # @param [String] direction - The direction to sort (asc or desc)
179
+ # @return [CTG::Query] - The current instance for method chaining
180
+ def sort_by(sort_by, direction = 'asc')
181
+ @params['sort'] = "#{sort_by}:#{direction}"
182
+ self
183
+ end
184
+
185
+ # Count total number of studies. The parameter is ignored for the subsequent pages.
186
+ # @return [CTG::Query] - The current instance for method chaining
187
+ def total
188
+ @params['countTotal'] = true
189
+ self
190
+ end
191
+
192
+ # Token to get next page. Set it to a nextPageToken value returned with the
193
+ # previous page in JSON format. For CSV, it can be found in x-next-page-token response header.
194
+ # Do not specify it for first page.
195
+ # @return [CTG::Query] - The current instance for method chaining
196
+ def page_token(token)
197
+ @params['pageToken'] = token
198
+ self
199
+ end
200
+
201
+ end
202
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file defines the CTG::Response::CSVResponse class, which is a subclass of CTG::Response.
4
+ # The CSVResponse class is responsible for handling responses in CSV format. It provides methods to query
5
+ # the CSV data by column name.
6
+ #
7
+ # Methods:
8
+ # - `initialize`: Initializes the CSVResponse object by parsing the CSV data.
9
+ # - `query`: Queries the CSV data by column name.
10
+
11
+ require 'csv'
12
+
13
+ class CTG
14
+ class Response
15
+ class CSVResponse < Response
16
+ attr_reader :data
17
+
18
+ # Initializes the CSVResponse object by parsing the CSV data
19
+ # @param [String] response_body - The raw CSV response body from the API
20
+ # @param [CTG] client - The client instance to use for fetching subsequent pages
21
+ def initialize(response_body, client)
22
+ super(client)
23
+ @data = CSV.parse(response_body, headers: true)
24
+ end
25
+
26
+ # Queries the CSV data by column name
27
+ # @param [String] column_name - The name of the column to query
28
+ # @return [Array<String>] - An array of values from the specified column
29
+ def query(column_name)
30
+ @data.map { |row| row[column_name] }.compact
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file defines the CTG::Response::JSONResponse class, which is a subclass of CTGClient::Response.
4
+ # The JSONResponse class is responsible for handling responses in JSON format. It provides methods to query
5
+ # the JSON data by keys, as well as to find specific values or all occurrences of a key in the data.
6
+ #
7
+ # Methods:
8
+ # - `initialize`: Initializes the JSONResponse object by parsing the JSON data.
9
+ # - `query`: Queries the JSON data by keys.
10
+ # - `find`: Finds the first occurrence of a specified key in the JSON data.
11
+ # - `find_all`: Finds all occurrences of a specified key in the JSON data.
12
+
13
+
14
+ require 'json'
15
+
16
+ class CTG
17
+ class Response
18
+ class JSONResponse < Response
19
+ attr_reader :data
20
+
21
+ # Initializes the JSONResponse object by parsing the JSON data
22
+ # @param [String] response_body - The raw JSON response body from the API
23
+ # @param [CTG] client - The client instance to use for fetching subsequent pages
24
+ def initialize(response_body, client)
25
+ super(client)
26
+ @data = JSON.parse(response_body)
27
+ end
28
+
29
+ # Queries the JSON data by keys
30
+ # @param [Array<String>] keys - The sequence of keys to query the data
31
+ # @return [Object] - The queried data
32
+ def query(*keys)
33
+ keys.reduce(@data) do |current_data, key|
34
+ case current_data
35
+ when Array
36
+ case key
37
+ when '*' # Handle wildcard for array access
38
+ current_data.map { |element| element.is_a?(Hash) ? element : {} }
39
+ when Integer
40
+ current_data[key]
41
+ else
42
+ # If key is not an integer but the current data is an array, try to map over it
43
+ current_data.map { |element| element[key] if element.is_a?(Hash) }.compact
44
+ end
45
+ when Hash
46
+ current_data[key]
47
+ end
48
+ end
49
+ end
50
+
51
+ # Recursively find the first occurrence of a key in the JSON data
52
+ # @param [String] key - The key to find in the JSON data
53
+ # @param [Object] data - The current data to search within (used for recursion)
54
+ # @return [Object] - The value associated with the key, or nil if not found
55
+ def find(key, data = @data)
56
+ case data
57
+ when Hash
58
+ return data[key] if data.key?(key)
59
+
60
+ data.each_value do |value|
61
+ result = find(key, value)
62
+ return result if result
63
+ end
64
+ when Array
65
+ data.each do |element|
66
+ result = find(key, element)
67
+ return result if result
68
+ end
69
+ end
70
+ nil
71
+ end
72
+
73
+ def find_all(key)
74
+ results = []
75
+ stack = [@data]
76
+
77
+ until stack.empty?
78
+ current_data = stack.pop
79
+
80
+ case current_data
81
+ when Hash
82
+ results << current_data[key] if current_data.key?(key)
83
+ stack.concat(current_data.values)
84
+ when Array
85
+ stack.concat(current_data)
86
+ end
87
+ end
88
+
89
+ results.compact
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file defines the CTG::Response class, which serves as a wrapper around the response
4
+ # returned by the ClinicalTrials.gov API. The Response class provides a common interface for working
5
+ # with different response formats (JSON, CSV) and supports functionalities like pagination, querying
6
+ # by keys.
7
+ #
8
+ # The CTG::Response class delegates the format-specific behavior to its subclasses,
9
+ # CTG::Response::JSONResponse and CTG::Response::CSVResponse.
10
+ #
11
+ # Methods:
12
+ # - `parse`: Factory method that returns an appropriate response object (JSON or CSV).
13
+ # - `query`: Queries the response data by keys.
14
+ # - `next_page`: Next page (if page token exists)
15
+
16
+ class CTG
17
+ class Response
18
+ attr_reader :client
19
+
20
+ # Factory method to create an appropriate response object based on format
21
+ # @param [String] response_body - The raw response body from the API
22
+ # @param [String] format - The format of the response ('json' or 'csv')
23
+ # @param [CTG] client - The client instance to use for fetching subsequent pages
24
+ # @return [CTG::Response] - An instance of either JSONResponse or CSVResponse
25
+ def self.parse(response_body, format, client)
26
+ case format
27
+ when 'json'
28
+ JSONResponse.new(response_body, client)
29
+ when 'csv'
30
+ CSVResponse.new(response_body, client)
31
+ else
32
+ raise "Unsupported format: #{format}"
33
+ end
34
+ end
35
+
36
+ # Initializes the Response object
37
+ # @param [CTG] client - The client instance that made the request
38
+ def initialize(client)
39
+ @client = client
40
+ end
41
+
42
+ # Queries the response data by keys (to be implemented in subclasses)
43
+ # @param [Array<String>] keys - The sequence of keys to query the data
44
+ # @return [Object] - The queried data
45
+ def query(*keys)
46
+ raise NotImplementedError, 'Subclasses must implement the `query` method'
47
+ end
48
+
49
+ # @return [CTG::Response, nil]
50
+ # - Returns a new Response object containing the next page of data.
51
+ # - Returns `nil` if no next page token is found, indicating there is no further data.
52
+ def next_page
53
+ next_page_token = @client.response.headers['x-next-page-token'] || @data['nextPageToken']
54
+ return unless next_page_token
55
+
56
+ request = @client.response.request
57
+ format = request.options[:format] || 'json'
58
+
59
+ request.options[:query].merge! CTG::Query.new
60
+ .page_token(next_page_token)
61
+ .params
62
+
63
+ CTG::Response.parse(request.perform.body, format, @client)
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,5 @@
1
+ # lib/ctg/version.rb
2
+
3
+ module CTG
4
+ VERSION = "0.1.0"
5
+ end
data/lib/ctg.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # Copyright (c) Leonid Stoianov
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+
26
+ require 'ctg/ctg'
27
+ require 'ctg/query'
28
+ require 'ctg/response'
29
+ require 'ctg/response/csv_response'
30
+ require 'ctg/response/json_response'
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ctg
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Leonid Stoianov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-08-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '7.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 7.1.3.2
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '7.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 7.1.3.2
33
+ - !ruby/object:Gem::Dependency
34
+ name: httparty
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.22'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.22'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.4'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.4'
61
+ - !ruby/object:Gem::Dependency
62
+ name: webmock
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.23'
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 3.23.1
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '3.23'
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 3.23.1
81
+ description: CTG is a Ruby library that provides an interface for querying the ClinicalTrials.gov
82
+ API. It supports both JSON and CSV formats, pagination, and complex queries.
83
+ email:
84
+ - leo@@trialize.io
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - LICENSE
90
+ - README.md
91
+ - lib/ctg.rb
92
+ - lib/ctg/ctg.rb
93
+ - lib/ctg/query.rb
94
+ - lib/ctg/response.rb
95
+ - lib/ctg/response/csv_response.rb
96
+ - lib/ctg/response/json_response.rb
97
+ - lib/ctg/version.rb
98
+ homepage: https://github.com/trialize/ctg
99
+ licenses:
100
+ - MIT
101
+ metadata:
102
+ source_code_uri: https://github.com/trialize/ctg
103
+ changelog_uri: https://github.com/trialize/ctg/CHANGELOG.md
104
+ allowed_push_host: https://rubygems.org
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubygems_version: 3.2.15
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: A Ruby client for interacting with the ClinicalTrials.gov API.
124
+ test_files: []