ctg 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +300 -0
- data/lib/ctg/ctg.rb +115 -0
- data/lib/ctg/query.rb +202 -0
- data/lib/ctg/response/csv_response.rb +35 -0
- data/lib/ctg/response/json_response.rb +94 -0
- data/lib/ctg/response.rb +67 -0
- data/lib/ctg/version.rb +5 -0
- data/lib/ctg.rb +30 -0
- metadata +124 -0
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
|
data/lib/ctg/response.rb
ADDED
@@ -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
|
data/lib/ctg/version.rb
ADDED
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: []
|