impc-api 1.0.1__tar.gz → 1.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- impc_api-1.0.4/MANIFEST.in +1 -0
- impc_api-1.0.4/PKG-INFO +211 -0
- impc_api-1.0.4/README.md +194 -0
- impc_api-1.0.4/impc_api/__init__.py +6 -0
- impc_api-1.0.4/impc_api/batch_solr_request.py +266 -0
- impc_api-1.0.4/impc_api/solr_request.py +191 -0
- impc_api-1.0.4/impc_api/utils/__init__.py +0 -0
- impc_api-1.0.4/impc_api/utils/core_fields.json +15 -0
- impc_api-1.0.4/impc_api/utils/validators.py +117 -0
- impc_api-1.0.4/impc_api/utils/warnings.py +32 -0
- impc_api-1.0.4/impc_api.egg-info/PKG-INFO +211 -0
- impc_api-1.0.4/impc_api.egg-info/SOURCES.txt +20 -0
- impc_api-1.0.4/impc_api.egg-info/top_level.txt +1 -0
- impc_api-1.0.4/pyproject.toml +34 -0
- impc_api-1.0.4/setup.py +39 -0
- impc_api-1.0.4/tests/test_batch_solr_request.py +746 -0
- impc_api-1.0.4/tests/test_helpers.py +33 -0
- impc_api-1.0.4/tests/test_solr_request.py +318 -0
- impc_api-1.0.1/PKG-INFO +0 -100
- impc_api-1.0.1/README.md +0 -84
- impc_api-1.0.1/impc_api.egg-info/PKG-INFO +0 -100
- impc_api-1.0.1/impc_api.egg-info/SOURCES.txt +0 -7
- impc_api-1.0.1/impc_api.egg-info/top_level.txt +0 -1
- {impc_api-1.0.1 → impc_api-1.0.4}/LICENSE +0 -0
- {impc_api-1.0.1 → impc_api-1.0.4}/impc_api.egg-info/dependency_links.txt +0 -0
- {impc_api-1.0.1 → impc_api-1.0.4}/impc_api.egg-info/requires.txt +0 -0
- {impc_api-1.0.1 → impc_api-1.0.4}/setup.cfg +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include impc_api/utils/core_fields.json
|
impc_api-1.0.4/PKG-INFO
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: impc_api
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: A package to facilitate making API requests to the IMPC Solr API
|
|
5
|
+
Home-page: https://github.com/mpi2/impc-data-api-workshop
|
|
6
|
+
Author: MPI2, Marina Kan, Diego Pava
|
|
7
|
+
Project-URL: Homepage, https://github.com/mpi2/impc-data-api-workshop
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: pandas>=2.2.0
|
|
12
|
+
Requires-Dist: requests>=2.31.0
|
|
13
|
+
Requires-Dist: tqdm>=4.66.4
|
|
14
|
+
Requires-Dist: pydantic>=2.9
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest>=8.2.2; extra == "dev"
|
|
17
|
+
|
|
18
|
+
# IMPC_API
|
|
19
|
+
|
|
20
|
+
`impc_api` is a Python package.
|
|
21
|
+
|
|
22
|
+
The functions in this package are intended for use in a Jupyter Notebook.
|
|
23
|
+
|
|
24
|
+
## Installation Instructions
|
|
25
|
+
|
|
26
|
+
1. **Create a virtual environment (optional but recommended)**:
|
|
27
|
+
On Mac:
|
|
28
|
+
```bash
|
|
29
|
+
python3 -m venv .venv
|
|
30
|
+
source .venv/bin/activate
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
2. **Install the package**: `pip install impc_api`
|
|
34
|
+
3. **Install Jupyter**: `pip install jupyter`
|
|
35
|
+
4. **Run the Jupyter Notebook**: `jupyter notebook`
|
|
36
|
+
|
|
37
|
+
After executing the command, the Jupyter interface should open in your browser. If it does not, follow the instructions provided in the terminal.
|
|
38
|
+
|
|
39
|
+
5. **Try it out**:
|
|
40
|
+
|
|
41
|
+
Create a [Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/latest/) and try some of the examples below:
|
|
42
|
+
|
|
43
|
+
## Available functions
|
|
44
|
+
|
|
45
|
+
The available functions can be imported as:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from impc_api import solr_request, batch_solr_request
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
# 1. Solr request
|
|
52
|
+
|
|
53
|
+
The most basic request to the IMPC solr API
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
num_found, df = solr_request(
|
|
57
|
+
core='genotype-phenotype',
|
|
58
|
+
params={
|
|
59
|
+
'q': '*:*',
|
|
60
|
+
'rows': 10,
|
|
61
|
+
'fl': 'marker_symbol,allele_symbol,parameter_stable_id'
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## a. Facet request
|
|
67
|
+
|
|
68
|
+
`solr_request` allows facet requests
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
num_found, df = solr_request(
|
|
72
|
+
core="genotype-phenotype",
|
|
73
|
+
params={
|
|
74
|
+
"q": "*:*",
|
|
75
|
+
"rows": 0,
|
|
76
|
+
"facet": "on",
|
|
77
|
+
"facet.field": "zygosity",
|
|
78
|
+
"facet.limit": 15,
|
|
79
|
+
"facet.mincount": 1,
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## b. Solr request validation
|
|
85
|
+
|
|
86
|
+
A common pitfall when writing a query is the misspelling of `core` and `fields` arguments. For this, we have included a `validate` argument that raises a warning when these values are not as expected. Note this does not prevent you from executing a query; it just alerts you to a potential issue.
|
|
87
|
+
|
|
88
|
+
### Core validation
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
num_found, df = solr_request(
|
|
92
|
+
core='invalid_core',
|
|
93
|
+
params={
|
|
94
|
+
'q': '*:*',
|
|
95
|
+
'rows': 10
|
|
96
|
+
},
|
|
97
|
+
validate=True
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
> InvalidCoreWarning: Invalid core: "genotype-phenotyp", select from the available cores:
|
|
101
|
+
> dict_keys(['experiment', 'genotype-phenotype', 'impc_images', 'phenodigm', 'statistical-result'])
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Field list validation
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
num_found, df = solr_request(
|
|
108
|
+
core='genotype-phenotype',
|
|
109
|
+
params={
|
|
110
|
+
'q': '*:*',
|
|
111
|
+
'rows': 10,
|
|
112
|
+
'fl': 'invalid_field,marker_symbol,allele_symbol'
|
|
113
|
+
},
|
|
114
|
+
validate=True
|
|
115
|
+
)
|
|
116
|
+
> InvalidFieldWarning: Unexpected field name: "invalid_field". Check the spelling of fields.
|
|
117
|
+
> To see expected fields check the documentation at: https://www.ebi.ac.uk/mi/impc/solrdoc/
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
# 2. Batch Solr Request
|
|
121
|
+
|
|
122
|
+
`batch_solr_request` is available for large queries. This solves issues where a request is too large to fit into memory or where it puts a lot of strain on the API.
|
|
123
|
+
|
|
124
|
+
Use `batch_solr_request` for:
|
|
125
|
+
|
|
126
|
+
- Large queries (>1,000,000)
|
|
127
|
+
- Querying multiple items in a list
|
|
128
|
+
- Downloading data in `json` or `csv` format.
|
|
129
|
+
|
|
130
|
+
## Large queries
|
|
131
|
+
|
|
132
|
+
For large queries you can choose between seeing them in a DataFrame or downloading them in `json` or `csv` format.
|
|
133
|
+
|
|
134
|
+
## a. Large query - see in DataFrame
|
|
135
|
+
|
|
136
|
+
This will fetch your data using the API responsibly and return a Pandas DataFrame
|
|
137
|
+
|
|
138
|
+
When your request is larger than recommended and you have not opted for downloading the data, a warning will be presented and you should follow the instructions to proceed.
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
df = batch_solr_request(
|
|
142
|
+
core='genotype-phenotype',
|
|
143
|
+
params={
|
|
144
|
+
'q':'*:*'
|
|
145
|
+
},
|
|
146
|
+
download=False,
|
|
147
|
+
batch_size=30000
|
|
148
|
+
)
|
|
149
|
+
print(df.head())
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## b. Large query - Download
|
|
153
|
+
|
|
154
|
+
When using the `download=True` option, a file with the requested information will be saved as `filename`. The format is selected based on the `wt` parameter.
|
|
155
|
+
A DataFrame may be returned, provided it does not exceed the memory available on your laptop. If the DataFrame is too large, an error will be raised. For these cases, we recommend you read the downloaded file in batches/chunks.
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
df = batch_solr_request(
|
|
159
|
+
core='genotype-phenotype',
|
|
160
|
+
params={
|
|
161
|
+
'q':'*:*',
|
|
162
|
+
'wt':'csv'
|
|
163
|
+
},
|
|
164
|
+
download=True,
|
|
165
|
+
filename='geno_pheno_query',
|
|
166
|
+
batch_size=100000
|
|
167
|
+
)
|
|
168
|
+
print(df.head())
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## c. Query by multiple values
|
|
172
|
+
|
|
173
|
+
`batch_solr_request` also allows to search multiple items in a list provided they belong to them same field.
|
|
174
|
+
Pass the list to the `field_list` param and specify the type of `fl` in `field_type`.
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
# List of gene symbols
|
|
178
|
+
genes = ["Zfp580", "Firrm", "Gpld1", "Mbip"]
|
|
179
|
+
|
|
180
|
+
df = batch_solr_request(
|
|
181
|
+
core='genotype-phenotype',
|
|
182
|
+
params={
|
|
183
|
+
'q':'*:*',
|
|
184
|
+
'fl': 'marker_symbol,mp_term_name,p_value',
|
|
185
|
+
'field_list': genes,
|
|
186
|
+
'field_type': 'marker_symbol'
|
|
187
|
+
},
|
|
188
|
+
download = False
|
|
189
|
+
)
|
|
190
|
+
print(df.head())
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
This can be downloaded too:
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
# List of gene symbols
|
|
197
|
+
genes = ["Zfp580", "Firrm", "Gpld1", "Mbip"]
|
|
198
|
+
|
|
199
|
+
df = batch_solr_request(
|
|
200
|
+
core='genotype-phenotype',
|
|
201
|
+
params={
|
|
202
|
+
'q':'*:*',
|
|
203
|
+
'fl': 'marker_symbol,mp_term_name,p_value',
|
|
204
|
+
'field_list': genes,
|
|
205
|
+
'field_type': 'marker_symbol'
|
|
206
|
+
},
|
|
207
|
+
download = True,
|
|
208
|
+
filename='gene_list_query'
|
|
209
|
+
)
|
|
210
|
+
print(df.head())
|
|
211
|
+
```
|
impc_api-1.0.4/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# IMPC_API
|
|
2
|
+
|
|
3
|
+
`impc_api` is a Python package.
|
|
4
|
+
|
|
5
|
+
The functions in this package are intended for use in a Jupyter Notebook.
|
|
6
|
+
|
|
7
|
+
## Installation Instructions
|
|
8
|
+
|
|
9
|
+
1. **Create a virtual environment (optional but recommended)**:
|
|
10
|
+
On Mac:
|
|
11
|
+
```bash
|
|
12
|
+
python3 -m venv .venv
|
|
13
|
+
source .venv/bin/activate
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
2. **Install the package**: `pip install impc_api`
|
|
17
|
+
3. **Install Jupyter**: `pip install jupyter`
|
|
18
|
+
4. **Run the Jupyter Notebook**: `jupyter notebook`
|
|
19
|
+
|
|
20
|
+
After executing the command, the Jupyter interface should open in your browser. If it does not, follow the instructions provided in the terminal.
|
|
21
|
+
|
|
22
|
+
5. **Try it out**:
|
|
23
|
+
|
|
24
|
+
Create a [Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/latest/) and try some of the examples below:
|
|
25
|
+
|
|
26
|
+
## Available functions
|
|
27
|
+
|
|
28
|
+
The available functions can be imported as:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from impc_api import solr_request, batch_solr_request
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
# 1. Solr request
|
|
35
|
+
|
|
36
|
+
The most basic request to the IMPC solr API
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
num_found, df = solr_request(
|
|
40
|
+
core='genotype-phenotype',
|
|
41
|
+
params={
|
|
42
|
+
'q': '*:*',
|
|
43
|
+
'rows': 10,
|
|
44
|
+
'fl': 'marker_symbol,allele_symbol,parameter_stable_id'
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## a. Facet request
|
|
50
|
+
|
|
51
|
+
`solr_request` allows facet requests
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
num_found, df = solr_request(
|
|
55
|
+
core="genotype-phenotype",
|
|
56
|
+
params={
|
|
57
|
+
"q": "*:*",
|
|
58
|
+
"rows": 0,
|
|
59
|
+
"facet": "on",
|
|
60
|
+
"facet.field": "zygosity",
|
|
61
|
+
"facet.limit": 15,
|
|
62
|
+
"facet.mincount": 1,
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## b. Solr request validation
|
|
68
|
+
|
|
69
|
+
A common pitfall when writing a query is the misspelling of `core` and `fields` arguments. For this, we have included a `validate` argument that raises a warning when these values are not as expected. Note this does not prevent you from executing a query; it just alerts you to a potential issue.
|
|
70
|
+
|
|
71
|
+
### Core validation
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
num_found, df = solr_request(
|
|
75
|
+
core='invalid_core',
|
|
76
|
+
params={
|
|
77
|
+
'q': '*:*',
|
|
78
|
+
'rows': 10
|
|
79
|
+
},
|
|
80
|
+
validate=True
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
> InvalidCoreWarning: Invalid core: "genotype-phenotyp", select from the available cores:
|
|
84
|
+
> dict_keys(['experiment', 'genotype-phenotype', 'impc_images', 'phenodigm', 'statistical-result'])
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Field list validation
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
num_found, df = solr_request(
|
|
91
|
+
core='genotype-phenotype',
|
|
92
|
+
params={
|
|
93
|
+
'q': '*:*',
|
|
94
|
+
'rows': 10,
|
|
95
|
+
'fl': 'invalid_field,marker_symbol,allele_symbol'
|
|
96
|
+
},
|
|
97
|
+
validate=True
|
|
98
|
+
)
|
|
99
|
+
> InvalidFieldWarning: Unexpected field name: "invalid_field". Check the spelling of fields.
|
|
100
|
+
> To see expected fields check the documentation at: https://www.ebi.ac.uk/mi/impc/solrdoc/
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
# 2. Batch Solr Request
|
|
104
|
+
|
|
105
|
+
`batch_solr_request` is available for large queries. This solves issues where a request is too large to fit into memory or where it puts a lot of strain on the API.
|
|
106
|
+
|
|
107
|
+
Use `batch_solr_request` for:
|
|
108
|
+
|
|
109
|
+
- Large queries (>1,000,000)
|
|
110
|
+
- Querying multiple items in a list
|
|
111
|
+
- Downloading data in `json` or `csv` format.
|
|
112
|
+
|
|
113
|
+
## Large queries
|
|
114
|
+
|
|
115
|
+
For large queries you can choose between seeing them in a DataFrame or downloading them in `json` or `csv` format.
|
|
116
|
+
|
|
117
|
+
## a. Large query - see in DataFrame
|
|
118
|
+
|
|
119
|
+
This will fetch your data using the API responsibly and return a Pandas DataFrame
|
|
120
|
+
|
|
121
|
+
When your request is larger than recommended and you have not opted for downloading the data, a warning will be presented and you should follow the instructions to proceed.
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
df = batch_solr_request(
|
|
125
|
+
core='genotype-phenotype',
|
|
126
|
+
params={
|
|
127
|
+
'q':'*:*'
|
|
128
|
+
},
|
|
129
|
+
download=False,
|
|
130
|
+
batch_size=30000
|
|
131
|
+
)
|
|
132
|
+
print(df.head())
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## b. Large query - Download
|
|
136
|
+
|
|
137
|
+
When using the `download=True` option, a file with the requested information will be saved as `filename`. The format is selected based on the `wt` parameter.
|
|
138
|
+
A DataFrame may be returned, provided it does not exceed the memory available on your laptop. If the DataFrame is too large, an error will be raised. For these cases, we recommend you read the downloaded file in batches/chunks.
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
df = batch_solr_request(
|
|
142
|
+
core='genotype-phenotype',
|
|
143
|
+
params={
|
|
144
|
+
'q':'*:*',
|
|
145
|
+
'wt':'csv'
|
|
146
|
+
},
|
|
147
|
+
download=True,
|
|
148
|
+
filename='geno_pheno_query',
|
|
149
|
+
batch_size=100000
|
|
150
|
+
)
|
|
151
|
+
print(df.head())
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## c. Query by multiple values
|
|
155
|
+
|
|
156
|
+
`batch_solr_request` also allows to search multiple items in a list provided they belong to them same field.
|
|
157
|
+
Pass the list to the `field_list` param and specify the type of `fl` in `field_type`.
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
# List of gene symbols
|
|
161
|
+
genes = ["Zfp580", "Firrm", "Gpld1", "Mbip"]
|
|
162
|
+
|
|
163
|
+
df = batch_solr_request(
|
|
164
|
+
core='genotype-phenotype',
|
|
165
|
+
params={
|
|
166
|
+
'q':'*:*',
|
|
167
|
+
'fl': 'marker_symbol,mp_term_name,p_value',
|
|
168
|
+
'field_list': genes,
|
|
169
|
+
'field_type': 'marker_symbol'
|
|
170
|
+
},
|
|
171
|
+
download = False
|
|
172
|
+
)
|
|
173
|
+
print(df.head())
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
This can be downloaded too:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
# List of gene symbols
|
|
180
|
+
genes = ["Zfp580", "Firrm", "Gpld1", "Mbip"]
|
|
181
|
+
|
|
182
|
+
df = batch_solr_request(
|
|
183
|
+
core='genotype-phenotype',
|
|
184
|
+
params={
|
|
185
|
+
'q':'*:*',
|
|
186
|
+
'fl': 'marker_symbol,mp_term_name,p_value',
|
|
187
|
+
'field_list': genes,
|
|
188
|
+
'field_type': 'marker_symbol'
|
|
189
|
+
},
|
|
190
|
+
download = True,
|
|
191
|
+
filename='gene_list_query'
|
|
192
|
+
)
|
|
193
|
+
print(df.head())
|
|
194
|
+
```
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import warnings
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import requests
|
|
7
|
+
from tqdm import tqdm
|
|
8
|
+
from IPython.display import display
|
|
9
|
+
|
|
10
|
+
from impc_api.utils.validators import DownloadFormatValidator
|
|
11
|
+
from impc_api.utils.warnings import (
|
|
12
|
+
warning_config,
|
|
13
|
+
RowsParamIgnored,
|
|
14
|
+
UnsupportedDownloadFormatError,
|
|
15
|
+
)
|
|
16
|
+
from .solr_request import solr_request
|
|
17
|
+
|
|
18
|
+
# Initialise warning config
|
|
19
|
+
warning_config()
|
|
20
|
+
|
|
21
|
+
def batch_solr_request(
|
|
22
|
+
core, params, download=False, batch_size=5000, filename="batch_request"
|
|
23
|
+
):
|
|
24
|
+
"""Function for large API requests (>1,000,000 results). Fetches the data in batches and
|
|
25
|
+
produces a Pandas DataFrame or downloads a file in json or csv formats.
|
|
26
|
+
|
|
27
|
+
Additionally, allows to search multiple items in a list provided they belong to them same field.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
core (str): name of IMPC solr core.
|
|
31
|
+
params (dict): dictionary containing the API call parameters.
|
|
32
|
+
download (bool, optional): True for download a local file, False to display results as a DataFrame. Defaults to False.
|
|
33
|
+
batch_size (int, optional): Size of batches to fetch the data. Defaults to 5000.
|
|
34
|
+
filename (str, optional): When download=True, select the name of the file. Defaults to 'batch_request'.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
pd.DataFrame: if download=False, displays a DataFrame with the results.
|
|
39
|
+
None: if download=True, displays a statement on the console and returns None.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
# If params["rows"] is passed, the user is warned about no effect
|
|
43
|
+
if params.get("rows") is not None:
|
|
44
|
+
warnings.warn(
|
|
45
|
+
message='The "rows" param will be ignored in batch_solr_request. To set a batch size, specify a "batch_size" argument.',
|
|
46
|
+
category=RowsParamIgnored,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Set params for batch request
|
|
50
|
+
params["start"] = 0 # Start at the first result
|
|
51
|
+
# Override param rows in case there was input. Defines batch size to 5000 by default
|
|
52
|
+
params["rows"] = batch_size
|
|
53
|
+
|
|
54
|
+
# If user did not specify format, defaults to json.
|
|
55
|
+
if params.get("wt") is None:
|
|
56
|
+
params["wt"] = "json"
|
|
57
|
+
|
|
58
|
+
# Check if it's multiple request
|
|
59
|
+
if params.get("field_list") is not None:
|
|
60
|
+
# Extract entities_list and entity_type from params
|
|
61
|
+
field_list = params.pop("field_list")
|
|
62
|
+
field_type = params.pop("field_type")
|
|
63
|
+
|
|
64
|
+
# Construct the filter query with grouped model IDs
|
|
65
|
+
fq = "{}:({})".format(
|
|
66
|
+
field_type, " OR ".join(["{}".format(id) for id in field_list])
|
|
67
|
+
)
|
|
68
|
+
# Show users the field and field values they passed to the function
|
|
69
|
+
print("Queried field:", fq)
|
|
70
|
+
# Set internal params the users should not change
|
|
71
|
+
params["fq"] = fq
|
|
72
|
+
|
|
73
|
+
# Determine the total number of rows. Note that we do not request any data (rows = 0).
|
|
74
|
+
num_results, _ = solr_request(
|
|
75
|
+
core=core, params={**params, "start": 0, "rows": 0, "wt": "json"}, silent=True
|
|
76
|
+
)
|
|
77
|
+
print(f"Number of found documents: {num_results}")
|
|
78
|
+
|
|
79
|
+
# # Download only logic
|
|
80
|
+
# If user decides to download, a generator is used to fetch data in batches without storing results in memory.
|
|
81
|
+
if download:
|
|
82
|
+
try:
|
|
83
|
+
# Check if the format is supported
|
|
84
|
+
DownloadFormatValidator(wt=params["wt"])
|
|
85
|
+
|
|
86
|
+
# Implement loop behaviour
|
|
87
|
+
print("Downloading file...")
|
|
88
|
+
filename_path = Path(f"{filename}.{params['wt']}")
|
|
89
|
+
gen = _batch_solr_generator(core, params, num_results)
|
|
90
|
+
_solr_downloader(params, filename_path, gen)
|
|
91
|
+
print(f"File saved as: {filename_path}")
|
|
92
|
+
except UnsupportedDownloadFormatError as e:
|
|
93
|
+
raise e
|
|
94
|
+
except Exception as e:
|
|
95
|
+
raise (f"An error ocurred while downloading the data:{e}")
|
|
96
|
+
|
|
97
|
+
# Try to read the downloaded file
|
|
98
|
+
try:
|
|
99
|
+
print("Reading downloaded file...")
|
|
100
|
+
return _read_downloaded_file(filename_path, params["wt"])
|
|
101
|
+
except Exception as e:
|
|
102
|
+
raise Exception(f"An unexpected error occured:{e}")
|
|
103
|
+
|
|
104
|
+
# If the number of results is small enough and download is off, it's okay to show as df
|
|
105
|
+
if num_results < 1000000 and not download:
|
|
106
|
+
return _batch_to_df(core, params, num_results)
|
|
107
|
+
|
|
108
|
+
# If it's too big, warn the user and ask if they want to proceed.
|
|
109
|
+
else:
|
|
110
|
+
print(
|
|
111
|
+
"Your request might exceed the available memory. We suggest setting 'download=True' and reading the file in batches"
|
|
112
|
+
)
|
|
113
|
+
prompt = input(
|
|
114
|
+
"Do you wish to proceed anyway? press ('y' or enter to proceed) / type('n' or 'exit' to cancel)"
|
|
115
|
+
)
|
|
116
|
+
match prompt:
|
|
117
|
+
case "n" | "exit":
|
|
118
|
+
print("Exiting gracefully")
|
|
119
|
+
exit()
|
|
120
|
+
case "y" | "":
|
|
121
|
+
print("Fetching data...")
|
|
122
|
+
return _batch_to_df(core, params, num_results)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# Helper batch_to_df
|
|
126
|
+
def _batch_to_df(core, params, num_results):
|
|
127
|
+
"""Helper function to fetch data in batches and display them in a DataFrame
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
core (str): name of IMPC solr core.
|
|
131
|
+
params (dict): dictionary containing the API call parameters.
|
|
132
|
+
num_results (int): Number of docs available
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
pd.DataFrame: DataFrame with the results.
|
|
136
|
+
"""
|
|
137
|
+
start = params["start"]
|
|
138
|
+
batch_size = params["rows"]
|
|
139
|
+
chunks = []
|
|
140
|
+
# If the 'wt' param was changed by error, we set it to 'json'
|
|
141
|
+
params["wt"] = "json"
|
|
142
|
+
|
|
143
|
+
# Request chunks until we have complete data.
|
|
144
|
+
with tqdm(total=num_results) as pbar:
|
|
145
|
+
while start < num_results:
|
|
146
|
+
# Request chunk. We don't need num_results anymore because it does not change.
|
|
147
|
+
_, df_chunk = solr_request(
|
|
148
|
+
core=core,
|
|
149
|
+
params={**params, "start": start, "rows": batch_size},
|
|
150
|
+
silent=True,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# Update progress bar with the number of rows requested.
|
|
154
|
+
pbar.update(batch_size)
|
|
155
|
+
pbar.refresh()
|
|
156
|
+
# Record chunk.
|
|
157
|
+
chunks.append(df_chunk)
|
|
158
|
+
# Increment start.
|
|
159
|
+
start += batch_size
|
|
160
|
+
# Prepare final dataframe.
|
|
161
|
+
return pd.concat(chunks, ignore_index=True)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _batch_solr_generator(core, params, num_results):
|
|
165
|
+
"""Generator function to fetch results from the SOLR API in batches using pagination.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
core (str): name of IMPC solr core.
|
|
169
|
+
params (dict): dictionary containing the API call parameters.
|
|
170
|
+
num_results (int): Number of docs available
|
|
171
|
+
|
|
172
|
+
Raises:
|
|
173
|
+
Exception: If a problem occurs during the download, an exception is raised.
|
|
174
|
+
|
|
175
|
+
Yields:
|
|
176
|
+
([dict, str]): A JSON object or plain text with the results.
|
|
177
|
+
"""
|
|
178
|
+
base_url = "https://www.ebi.ac.uk/mi/impc/solr/"
|
|
179
|
+
solr_url = base_url + core + "/select"
|
|
180
|
+
start = params["start"]
|
|
181
|
+
batch_size = params["rows"]
|
|
182
|
+
|
|
183
|
+
with tqdm(total=num_results) as pbar:
|
|
184
|
+
while start <= num_results:
|
|
185
|
+
params["start"] = start
|
|
186
|
+
response = requests.get(solr_url, params=params, timeout=10)
|
|
187
|
+
|
|
188
|
+
if response.status_code == 200:
|
|
189
|
+
if params.get("wt") == "json":
|
|
190
|
+
data = response.json()["response"]["docs"]
|
|
191
|
+
else:
|
|
192
|
+
data = response.text
|
|
193
|
+
|
|
194
|
+
# Update and refresh the progress bar after getting the data
|
|
195
|
+
pbar.update(batch_size)
|
|
196
|
+
pbar.refresh()
|
|
197
|
+
yield data
|
|
198
|
+
|
|
199
|
+
else:
|
|
200
|
+
raise Exception(f"Request failed. Status code: {response.status_code}")
|
|
201
|
+
|
|
202
|
+
# pbar.update(batch_size)
|
|
203
|
+
start += batch_size
|
|
204
|
+
print(f"Your request URL after the last call:{response.url}")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# File writer
|
|
208
|
+
def _solr_downloader(params, filename, solr_generator):
|
|
209
|
+
"""Function to write the data from the generator into the specified format.
|
|
210
|
+
Supports json and csv only.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
params (dict): dictionary containing the API call parameters.
|
|
214
|
+
filename (Path): name for the file to be downloaded. Defaults to "core.format" as passed by parent function.
|
|
215
|
+
solr_generator ([dict, str]): Generator object with the results.
|
|
216
|
+
"""
|
|
217
|
+
with open(filename, "w", encoding="UTF-8") as f:
|
|
218
|
+
if params.get("wt") == "json":
|
|
219
|
+
f.write("[\n")
|
|
220
|
+
first_chunk = True
|
|
221
|
+
|
|
222
|
+
for chunk in solr_generator:
|
|
223
|
+
for item in chunk:
|
|
224
|
+
if not first_chunk:
|
|
225
|
+
f.write(",\n")
|
|
226
|
+
json.dump(item, f, ensure_ascii=False)
|
|
227
|
+
first_chunk = False
|
|
228
|
+
f.write("\n]\n")
|
|
229
|
+
|
|
230
|
+
elif params.get("wt") == "csv":
|
|
231
|
+
first_chunk = True
|
|
232
|
+
for chunk in solr_generator:
|
|
233
|
+
lines = chunk.splitlines()
|
|
234
|
+
if first_chunk:
|
|
235
|
+
# Write all lines in the first chunk
|
|
236
|
+
f.write(chunk)
|
|
237
|
+
first_chunk = False
|
|
238
|
+
else:
|
|
239
|
+
# Skip the first line (header) in subsequent chunks
|
|
240
|
+
f.write("\n" + "\n".join(lines[1:]) + "\n")
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# File reader
|
|
244
|
+
def _read_downloaded_file(filename: Path, request_format):
|
|
245
|
+
"""Wrapper for reading files into Pandas DataFrames
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
filename (Path): Name of the file to read
|
|
249
|
+
request_format (str): Format of the file to read. Only 'json' and 'csv' are supported.
|
|
250
|
+
|
|
251
|
+
Raises:
|
|
252
|
+
MemoryError: When there is not enough memory to read the file.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
pd.DataFrame: Returns a pd.DataFrame with the data from the file.
|
|
256
|
+
"""
|
|
257
|
+
try:
|
|
258
|
+
match request_format:
|
|
259
|
+
case "json":
|
|
260
|
+
return pd.read_json(filename)
|
|
261
|
+
case "csv":
|
|
262
|
+
return pd.read_csv(filename)
|
|
263
|
+
except MemoryError as exc:
|
|
264
|
+
raise MemoryError(
|
|
265
|
+
"MemoryError: Insuficient memory to read the file. Consider reading file in batches using Pandas or Polars."
|
|
266
|
+
) from exc
|