ipaapi 1.0.0__py3-none-any.whl
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.
- ipaapi/__init__.py +92 -0
- ipaapi/_payload.py +150 -0
- ipaapi/auth.py +575 -0
- ipaapi/cli.py +971 -0
- ipaapi/client.py +658 -0
- ipaapi/dataset.py +285 -0
- ipaapi/errors.py +101 -0
- ipaapi/history.py +145 -0
- ipaapi/mapping.py +485 -0
- ipaapi/models.py +193 -0
- ipaapi/triage.py +136 -0
- ipaapi-1.0.0.dist-info/METADATA +833 -0
- ipaapi-1.0.0.dist-info/RECORD +16 -0
- ipaapi-1.0.0.dist-info/WHEEL +4 -0
- ipaapi-1.0.0.dist-info/entry_points.txt +2 -0
- ipaapi-1.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,833 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ipaapi
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Command-line client for QIAGEN Ingenuity Pathway Analysis: map arbitrary column layouts, submit datasets in bulk, and resume cleanly when the analysis allowance runs out.
|
|
5
|
+
Author-email: Ken Jones <Ken.Jones@bioinformaticsolutions.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Ken Jones
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Keywords: bioinformatics,ingenuity,ipa,pathway,qiagen
|
|
29
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
30
|
+
Classifier: Environment :: Console
|
|
31
|
+
Classifier: Intended Audience :: Science/Research
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Operating System :: OS Independent
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
40
|
+
Requires-Python: >=3.9
|
|
41
|
+
Requires-Dist: pandas>=1.5
|
|
42
|
+
Requires-Dist: requests-oauthlib>=1.3
|
|
43
|
+
Requires-Dist: requests>=2.28
|
|
44
|
+
Provides-Extra: dev
|
|
45
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
46
|
+
Provides-Extra: progress
|
|
47
|
+
Requires-Dist: tqdm>=4.64; extra == 'progress'
|
|
48
|
+
Description-Content-Type: text/markdown
|
|
49
|
+
|
|
50
|
+
# ipaapi
|
|
51
|
+
|
|
52
|
+
A Python package and command-line tool for QIAGEN Ingenuity Pathway Analysis
|
|
53
|
+
(IPA). Upload datasets into an IPA project using an explicit column mapping,
|
|
54
|
+
submit them for analysis, and track the results — one file or several hundred.
|
|
55
|
+
|
|
56
|
+
Free software (MIT). Built on QIAGEN's `python-api-demo` example code — **not
|
|
57
|
+
an official QIAGEN product**, and not endorsed by QIAGEN.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
ipaapi submit ~/data --ID 1:hugo --FC 4:logratio --skip-rows 1 \
|
|
61
|
+
--reference-set ipkb --project MyStudy --pattern _DEG
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Contents
|
|
67
|
+
|
|
68
|
+
- [Why this exists](#why-this-exists)
|
|
69
|
+
- [Installation](#installation)
|
|
70
|
+
- [Quick start](#quick-start)
|
|
71
|
+
- [How the mapping works](#how-the-mapping-works)
|
|
72
|
+
- [Command-line reference](#command-line-reference)
|
|
73
|
+
- [Recipes](#recipes)
|
|
74
|
+
- [Working with IPA](#working-with-ipa) — the undocumented parts
|
|
75
|
+
- [Authentication](#authentication)
|
|
76
|
+
- [Python API](#python-api)
|
|
77
|
+
- [Troubleshooting](#troubleshooting)
|
|
78
|
+
- [How a submission is encoded](#how-a-submission-is-encoded)
|
|
79
|
+
- [Development](#development)
|
|
80
|
+
- [Contributing](#contributing)
|
|
81
|
+
- [Licence](#licence)
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Why this exists
|
|
86
|
+
|
|
87
|
+
QIAGEN's demo script works, but assumes a rigid file layout: the gene ID in
|
|
88
|
+
column 0, then `n_observations × n_measurements` value columns in strict
|
|
89
|
+
repeating order, every observation carrying the same measurement types in the
|
|
90
|
+
same positions. Real analysis output rarely looks like that.
|
|
91
|
+
|
|
92
|
+
This package replaces that assumption with a declaration. You name the
|
|
93
|
+
identifier column and describe each observation as a set of
|
|
94
|
+
`(column, measurement type)` pairs. Columns may be in any order, named
|
|
95
|
+
anything, and interleaved with columns the analysis should ignore.
|
|
96
|
+
|
|
97
|
+
It also fixes a number of things the demo got wrong or left out — see
|
|
98
|
+
[Differences from the demo](#differences-from-the-demo).
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Installation
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
git clone <this-repo> ipaapi && cd ipaapi
|
|
106
|
+
pip install -e .
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Or build and install a wheel:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
python3 -m pip wheel . --no-deps -w dist
|
|
113
|
+
python3 -m pip install dist/ipaapi-*.whl
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Requires Python 3.9+, `requests`, `requests-oauthlib`, `pandas`.
|
|
117
|
+
|
|
118
|
+
Confirm what you're running — this reports the version, the install location,
|
|
119
|
+
and whether it's an editable checkout rather than a built wheel:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
$ ipaapi --version
|
|
123
|
+
ipaapi 1.0.0
|
|
124
|
+
installed at /usr/lib/python3.11/site-packages/ipaapi
|
|
125
|
+
python 3.11.5 (/usr/bin/python3)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Quick start
|
|
131
|
+
|
|
132
|
+
Say your file looks like this — a comment line, then a header, then data:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
# generated by pipeline v3
|
|
136
|
+
Gene,Common_name,Control_mean,Treatment_mean,Fold_change,P-value,Q-value
|
|
137
|
+
ENSG00000229807,XIST,4.21,2.88,-1.33,0.001,0.02
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Column positions are **0-based** and counted from the *header* row:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
0 Gene 1 Common_name 2 Control_mean 3 Treatment_mean 4 Fold_change 5 P-value 6 Q-value
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Check the mapping without contacting IPA:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
ipaapi validate results.csv --ID 1:hugo --FC 4:logratio --skip-rows 1
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
results: 2,338 rows
|
|
154
|
+
gene id: 'Common_name' (hugo)
|
|
155
|
+
observations: 1
|
|
156
|
+
results:
|
|
157
|
+
'Fold_change' -> Log Ratio
|
|
158
|
+
|
|
159
|
+
Common_name Fold_change
|
|
160
|
+
0 XIST -1.33
|
|
161
|
+
...
|
|
162
|
+
1 file valid. Nothing was uploaded.
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
When that looks right, submit:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
ipaapi submit results.csv --ID 1:hugo --FC 4:logratio --skip-rows 1 \
|
|
169
|
+
--reference-set ipkb --project MyStudy
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
submitted results: 43595871
|
|
174
|
+
|
|
175
|
+
Submitted 1 analysis.
|
|
176
|
+
Analyses are running in IPA. Check on them with:
|
|
177
|
+
ipaapi status 43595871
|
|
178
|
+
ipaapi report 43595871
|
|
179
|
+
Recorded in ~/.local/state/ipaapi/submissions.tsv -- see 'ipaapi history'.
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## How the mapping works
|
|
185
|
+
|
|
186
|
+
Three ideas, and they mirror how IPA thinks about a dataset.
|
|
187
|
+
|
|
188
|
+
**Measurement** — one value column: which column, what kind of number it holds,
|
|
189
|
+
and an optional cutoff.
|
|
190
|
+
|
|
191
|
+
**Observation** — a named sample or contrast, and the measurement columns
|
|
192
|
+
belonging to it. One analysis is created per observation.
|
|
193
|
+
|
|
194
|
+
**ColumnMapping** — the identifier column, its type, and the observations.
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
ColumnMapping(
|
|
198
|
+
gene_id_column="Common_name",
|
|
199
|
+
gene_id_type="hugo",
|
|
200
|
+
observations=[
|
|
201
|
+
Observation("drug A vs ctrl", [
|
|
202
|
+
Measurement("A_log2fc", MeasurementType.LOG_RATIO),
|
|
203
|
+
Measurement("A_padj", MeasurementType.FALSE_DISCOVERY, cutoff=0.05),
|
|
204
|
+
]),
|
|
205
|
+
Observation("drug B vs ctrl", [
|
|
206
|
+
# declared in a different order on purpose -- this is fine
|
|
207
|
+
Measurement("B_padj", MeasurementType.FALSE_DISCOVERY, cutoff=0.05),
|
|
208
|
+
Measurement("B_log2fc", MeasurementType.LOG_RATIO),
|
|
209
|
+
]),
|
|
210
|
+
],
|
|
211
|
+
)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**One constraint is imposed by IPA, not by this package.** The wire format
|
|
215
|
+
declares `expvaltype`, `expvaltype2`, … and `cutoff`, `cutoff2`, … *once for the
|
|
216
|
+
whole submission*, then supplies per-observation column names against those
|
|
217
|
+
slots. So every observation must contribute exactly one column per measurement
|
|
218
|
+
type, and a given type carries one cutoff throughout. Both are checked before
|
|
219
|
+
anything is uploaded, with an error that explains why.
|
|
220
|
+
|
|
221
|
+
Within those limits, order and naming are free — observations declared in
|
|
222
|
+
different column orders are normalised automatically.
|
|
223
|
+
|
|
224
|
+
Everything is validated against the actual data before upload: columns exist,
|
|
225
|
+
none is claimed twice, and values fall in the range IPA expects for their type.
|
|
226
|
+
That last check matters more than it looks — see
|
|
227
|
+
[measurement types](#measurement-types).
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Command-line reference
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
ipaapi validate check a mapping against file(s) without uploading
|
|
235
|
+
ipaapi submit upload into a project and start analyses
|
|
236
|
+
ipaapi status check the state of existing analyses
|
|
237
|
+
ipaapi report print IPA Interpret links
|
|
238
|
+
ipaapi history list analyses submitted through this tool
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Mapping arguments
|
|
242
|
+
|
|
243
|
+
Used by `validate` and `submit`.
|
|
244
|
+
|
|
245
|
+
| Flag | Form | Meaning |
|
|
246
|
+
| --- | --- | --- |
|
|
247
|
+
| `PATH` | positional | a data file, or a directory to search |
|
|
248
|
+
| `--ID` | `COLUMN:TYPE` | 0-based identifier column and its IPA gene ID type. May be given twice — see [two identifier columns](#two-identifier-columns) |
|
|
249
|
+
| `--FC` | `COLUMN:TYPE[:CUTOFF]` | 0-based value column, [measurement type](#measurement-types), optional cutoff |
|
|
250
|
+
| `--skip-rows` | `N` | discard N lines above the header row |
|
|
251
|
+
| `--sep` | `CHAR` | field delimiter (sniffed from the header line by default) |
|
|
252
|
+
| `--pattern` | `TEXT` | when PATH is a directory: substring or glob selecting files |
|
|
253
|
+
| `--recursive` | flag | search subdirectories too |
|
|
254
|
+
| `--observation` | `NAME` | observation name in IPA (default: the filename). Single file only |
|
|
255
|
+
| `--no-range-check` | flag | skip the value-range validation |
|
|
256
|
+
| `--list-id-types` | flag | print all 33 gene ID types and exit |
|
|
257
|
+
|
|
258
|
+
### `submit`
|
|
259
|
+
|
|
260
|
+
| Flag | Default | Meaning |
|
|
261
|
+
| --- | --- | --- |
|
|
262
|
+
| `--project` | *required* | destination IPA project. **Created if it doesn't exist**, so a typo silently makes a new one |
|
|
263
|
+
| `--reference-set` | `omit` | `ipkb`, `dataset`, or `omit`. See [the reference set](#the-reference-set) |
|
|
264
|
+
| `--wait` | off | poll until analyses finish and print report links |
|
|
265
|
+
| `--interval` / `--timeout` | 30s / 3600s | polling, only with `--wait` |
|
|
266
|
+
| `--dry-run` | off | validate and stop before login |
|
|
267
|
+
| `--analysis-name` / `--dataset-name` | filename | single file only |
|
|
268
|
+
| `--log-file` | `~/.local/state/ipaapi/submissions.tsv` | submission log |
|
|
269
|
+
|
|
270
|
+
### Authentication arguments
|
|
271
|
+
|
|
272
|
+
Used by every command that contacts IPA.
|
|
273
|
+
|
|
274
|
+
| Flag | Meaning |
|
|
275
|
+
| --- | --- |
|
|
276
|
+
| `--no-cache` | ignore any cached token |
|
|
277
|
+
| `--token-file` | token cache path (default `~/.cache/ipaapi/token.json`) |
|
|
278
|
+
| `--application-name` | `applicationname` IPA scopes the session to (default `PythonAPI`) |
|
|
279
|
+
| `--browser` | browser to launch for login, e.g. `firefox` |
|
|
280
|
+
|
|
281
|
+
### `history`
|
|
282
|
+
|
|
283
|
+
| Flag | Meaning |
|
|
284
|
+
| --- | --- |
|
|
285
|
+
| `--project` / `--since` / `--limit` | filters |
|
|
286
|
+
| `--status` | look up each analysis's current state (requires login) |
|
|
287
|
+
| `--log-file` | read a different log |
|
|
288
|
+
|
|
289
|
+
### Environment variables
|
|
290
|
+
|
|
291
|
+
| Variable | Purpose |
|
|
292
|
+
| --- | --- |
|
|
293
|
+
| `IPAAPI_TOKEN_FILE` | token cache location — set this if `$HOME` isn't writable |
|
|
294
|
+
| `IPAAPI_LOG_FILE` | submission log location |
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Recipes
|
|
299
|
+
|
|
300
|
+
### Many files, one analysis each
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
ipaapi submit ~/data --pattern _DEG --ID 1:hugo --FC 4:logratio \
|
|
304
|
+
--skip-rows 1 --reference-set ipkb --project Study1
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
`--pattern` takes plain text or a glob. Text with no `*`, `?` or `[` matches as
|
|
308
|
+
a **substring**, so `--pattern SampleA` finds `SampleA_DEG.txt` and
|
|
309
|
+
`SampleA_raw.tsv`. With no `--pattern`, `*.txt`/`*.tsv`/`*.csv` are searched.
|
|
310
|
+
Hidden files are skipped and results sorted, so run order is predictable.
|
|
311
|
+
|
|
312
|
+
Every matched file must fit the same `--ID`/`--FC` positions.
|
|
313
|
+
|
|
314
|
+
### Files are filed as they're processed
|
|
315
|
+
|
|
316
|
+
When `PATH` is a directory, each file moves as its outcome becomes known:
|
|
317
|
+
|
|
318
|
+
| Outcome | Destination |
|
|
319
|
+
| --- | --- |
|
|
320
|
+
| IPA accepted it | `submitted/` |
|
|
321
|
+
| The file is at fault | `failed/`, with a `.error.txt` note beside it |
|
|
322
|
+
| Allowance exhausted, or IPA declined | left in place for the next run |
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
submitted SampleA_DEG: 43595001
|
|
326
|
+
submitted SampleB_DEG: 43595002
|
|
327
|
+
|
|
328
|
+
Allowance exhausted while submitting SampleC_DEG:
|
|
329
|
+
REJECTED: the analysis allowance appears to be exhausted.
|
|
330
|
+
IPA said: 'Unable to run analysis: Analysis limit exceeded'
|
|
331
|
+
|
|
332
|
+
2 file(s) moved to submitted/
|
|
333
|
+
2 file(s) left in place for the next run
|
|
334
|
+
Re-run the same command later; the files left in place are exactly the ones
|
|
335
|
+
still to do.
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
The source directory shrinks to exactly the work outstanding, and re-running the
|
|
339
|
+
identical command resumes. `submitted/` and `failed/` are excluded from
|
|
340
|
+
discovery, so a run can't re-ingest its own output.
|
|
341
|
+
|
|
342
|
+
Nothing is moved when the *command* is at fault — a bad `--ID` type or a mapping
|
|
343
|
+
that fails every file leaves the directory untouched, because that's a mistake
|
|
344
|
+
to fix rather than data to quarantine. Single-file submits are never moved.
|
|
345
|
+
|
|
346
|
+
### Draining a backlog against a daily allowance
|
|
347
|
+
|
|
348
|
+
Because a stopped run resumes cleanly, this is safe to leave unattended:
|
|
349
|
+
|
|
350
|
+
```cron
|
|
351
|
+
0 6 * * * cd ~/data && ipaapi submit ./ --pattern _DEG --ID 1:hugo \
|
|
352
|
+
--FC 4:logratio --skip-rows 1 --reference-set ipkb --project Study1 \
|
|
353
|
+
>> ~/ipaapi-cron.log 2>&1
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
It submits until the allowance runs out, files what succeeded, leaves the rest.
|
|
357
|
+
Check the log after the first few runs — a cron job whose *refresh* token has
|
|
358
|
+
expired fails into that file rather than prompting anyone.
|
|
359
|
+
|
|
360
|
+
### Finding analysis IDs later
|
|
361
|
+
|
|
362
|
+
IPA's API cannot list the analyses on an account, so the package keeps its own
|
|
363
|
+
log — every submission appends a timestamped row.
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
ipaapi history
|
|
367
|
+
ipaapi history --project Study1 --since 2026-08-01
|
|
368
|
+
ipaapi history --status
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
```
|
|
372
|
+
2026-08-05T08:35:53-06:00 43595039 Study1 SampleA_DEG
|
|
373
|
+
2026-08-05T08:35:53-06:00 43595041 Study1 SampleB_DEG
|
|
374
|
+
|
|
375
|
+
2 submission(s). Report links: ipaapi report 43595039 43595041
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Plain TSV — grep it, open it in a spreadsheet. It only covers submissions made
|
|
379
|
+
through this tool; anything submitted from the IPA client won't appear.
|
|
380
|
+
|
|
381
|
+
### Comment lines above the header
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
# generated by pipeline v3, 2026-08-05
|
|
385
|
+
EnsemblID log2FC pval
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
`--skip-rows 1` discards the preamble. Column numbers count from the header, so
|
|
389
|
+
they don't change when you add it.
|
|
390
|
+
|
|
391
|
+
Skipping also fixes delimiter detection: the delimiter is sniffed from the
|
|
392
|
+
header line, and a comment line is a bad thing to sniff — the one above has
|
|
393
|
+
commas but no tabs, so without `--skip-rows` the file would be read as CSV and
|
|
394
|
+
collapse into nonsense. Rather than let that through, a header that looks like a
|
|
395
|
+
comment is rejected with a message pointing at this flag.
|
|
396
|
+
|
|
397
|
+
### Two identifier columns
|
|
398
|
+
|
|
399
|
+
`--ID` may be given twice. The first is the primary; the second fills rows where
|
|
400
|
+
the primary is blank (`.`, `NA`, empty, and similar are all treated as missing).
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
ipaapi submit data.csv --ID 0:ensembl --ID 1:hugo --FC 4:logratio --project S1
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
> **Read this before relying on it.** IPA accepts one `geneidtype` per
|
|
407
|
+
> submission. Rows filled from the second column are still uploaded under the
|
|
408
|
+
> *primary's* type, so they may fail to map. The fill count is always reported:
|
|
409
|
+
>
|
|
410
|
+
> ```
|
|
411
|
+
> Warning: 344 of 2,338 rows took their identifier from the fallback column
|
|
412
|
+
> 'Common_name' (hugo). IPA is told a single gene ID type for the submission --
|
|
413
|
+
> 'ensembl' -- so those rows are uploaded under that declaration and may not map.
|
|
414
|
+
> ```
|
|
415
|
+
>
|
|
416
|
+
> If a large fraction is being filled, using the fallback column as the *only*
|
|
417
|
+
> identifier is usually better than mixing.
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Working with IPA
|
|
422
|
+
|
|
423
|
+
Most of this is either undocumented or documented somewhere hard to find. It's
|
|
424
|
+
recorded here because getting it wrong is expensive — analyses consume a
|
|
425
|
+
metered allowance.
|
|
426
|
+
|
|
427
|
+
### Gene ID types
|
|
428
|
+
|
|
429
|
+
`--ID COLUMN:TYPE` takes any value from IPA's `geneidtype` list (Integration
|
|
430
|
+
Module §3.1). `ipaapi submit --list-id-types` prints all 33.
|
|
431
|
+
|
|
432
|
+
Common ones: `ensembl`, `hugo`, `entrezgene`, `refseq`, `swissprot`,
|
|
433
|
+
`affymetrix`, `illumina`, `agilent`.
|
|
434
|
+
|
|
435
|
+
Two things are not guessable:
|
|
436
|
+
|
|
437
|
+
- **Human gene symbols are `hugo`.** Not `genesymbol`, not `hgnc`, and not the
|
|
438
|
+
desktop client's own label `Gene Symbol` — all three are rejected outright.
|
|
439
|
+
- **Species rides on the identifier type.** There is no species parameter:
|
|
440
|
+
`hugo` human, `mousesymeg` mouse, `ratsymeg` rat.
|
|
441
|
+
|
|
442
|
+
A type outside the documented list produces a warning with a near-match
|
|
443
|
+
suggestion but is still sent, since IPA is the authority and the list will age.
|
|
444
|
+
An unrecognised value fails before anything is uploaded, and IPA names it.
|
|
445
|
+
|
|
446
|
+
### The reference set
|
|
447
|
+
|
|
448
|
+
The background enrichment is scored against — the denominator of the Fisher's
|
|
449
|
+
exact test behind every p-value.
|
|
450
|
+
|
|
451
|
+
| Value | Background |
|
|
452
|
+
| --- | --- |
|
|
453
|
+
| `ipkb` | Ingenuity Knowledge Base (Genes Only, or + Endogenous Chemicals if chemicals are present) |
|
|
454
|
+
| `dataset` | the genes you uploaded |
|
|
455
|
+
| `omit` (default) | IPA chooses |
|
|
456
|
+
|
|
457
|
+
Which to use depends on **what you uploaded**:
|
|
458
|
+
|
|
459
|
+
- Uploading a **complete measured transcriptome** with a cutoff? `dataset` is
|
|
460
|
+
the better science — the background is what your assay could actually detect,
|
|
461
|
+
which controls for detection bias.
|
|
462
|
+
- Uploading a **pre-filtered hit list**? `dataset` makes the background nearly
|
|
463
|
+
identical to the foreground. Use `ipkb`.
|
|
464
|
+
|
|
465
|
+
§4.1.3.1 states that with the parameter omitted IPA picks by size — `ipkb` below
|
|
466
|
+
2000 identifiers, `dataset` at 2000 or more. **In practice this has not been
|
|
467
|
+
observed to hold**: files of 1,804–6,245 rows all came back as
|
|
468
|
+
`Ingenuity Knowledge Base (Genes Only)`. Since the behaviour is unpredictable,
|
|
469
|
+
set it explicitly for anything you intend to compare against itself.
|
|
470
|
+
|
|
471
|
+
Verify after the fact — the setting is recorded in every IPA export:
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
grep -h "^Reference set" *_IPA_output.txt | sort | uniq -c
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Array platforms can also be named as reference sets, paired with a
|
|
478
|
+
`referencesettype`. Not exposed here; see §4.1.3.
|
|
479
|
+
|
|
480
|
+
### Measurement types
|
|
481
|
+
|
|
482
|
+
| Value | Meaning | Valid range |
|
|
483
|
+
| --- | --- | --- |
|
|
484
|
+
| `ratio` | Ratio | `[0, +∞)` |
|
|
485
|
+
| `foldchange` | Fold Change | `(-∞, -1]` and `[1, +∞)` |
|
|
486
|
+
| `logratio` | Log Ratio | `(-∞, +∞)` |
|
|
487
|
+
| `pvalue` | p-value | `[0, 1]` |
|
|
488
|
+
| `falsediscovery` | FDR / q-value | `[0, 100]` |
|
|
489
|
+
| `intensity` | Intensity | `[0, +∞)` |
|
|
490
|
+
| `other` | Other (normalised around zero) | `(-∞, +∞)` |
|
|
491
|
+
| `gain_loss` | Variant Gain/Loss | `-2, -1, 0, 1, 2` |
|
|
492
|
+
| `classification` | Variant ACMG Classification | `-2, -1, 0, 1, 2` |
|
|
493
|
+
|
|
494
|
+
> **Out-of-range values are silently discarded by IPA.** §3.1: "analysis will
|
|
495
|
+
> still proceed without errors or warning diagnostics" — offending entries are
|
|
496
|
+
> simply dropped. This is why the range check exists and why it refuses rather
|
|
497
|
+
> than warns. Declaring log2 fold changes as `foldchange`, for instance, would
|
|
498
|
+
> quietly discard every gene between −1 and 1, which in a typical scRNA-seq
|
|
499
|
+
> table is most of them.
|
|
500
|
+
|
|
501
|
+
The package helps in both directions:
|
|
502
|
+
|
|
503
|
+
- Values declared `foldchange` that cluster inside (−1, 1) → suggests `logratio`.
|
|
504
|
+
- A column declared `logratio` with *no* values in (−1, 1) → warns that it looks
|
|
505
|
+
like signed fold change, since a real log ratio is centred on zero.
|
|
506
|
+
|
|
507
|
+
A column called `Fold_change` may hold either. Check the data, not the name.
|
|
508
|
+
|
|
509
|
+
### What the API cannot do
|
|
510
|
+
|
|
511
|
+
- **List your projects.** `--project` creates one if the name doesn't exist, so
|
|
512
|
+
a typo silently makes a new project rather than erroring.
|
|
513
|
+
- **List your analyses.** Every endpoint needs an ID you already hold — hence
|
|
514
|
+
the local submission log.
|
|
515
|
+
- **Tell you your remaining allowance.** You discover the limit by hitting it.
|
|
516
|
+
|
|
517
|
+
### Errors IPA actually returns
|
|
518
|
+
|
|
519
|
+
IPA answers a rejected submission with an **HTML error page**, not plain text.
|
|
520
|
+
The reason is at the *end*, after support boilerplate. This package strips the
|
|
521
|
+
boilerplate and the page footer, and classifies what's left:
|
|
522
|
+
|
|
523
|
+
| IPA's message | Class | What the tool does |
|
|
524
|
+
| --- | --- | --- |
|
|
525
|
+
| `Unknown GeneId Type (X)` | `MalformedRequestError` | stops; names the flag; moves nothing |
|
|
526
|
+
| `Unable to run analysis: Analysis limit exceeded` | `QuotaExceededError` | stops; leaves remaining files for the next run |
|
|
527
|
+
| `Unable to run analysis: …` (other) | `AnalysisRefusedError` | as above — reached the analysis logic, so not a parameter fault |
|
|
528
|
+
| anything else | `SubmissionError` | files that one under `failed/` |
|
|
529
|
+
|
|
530
|
+
Quota matching is deliberately broad (`ipaapi.client.QUOTA_PATTERNS` plus HTTP
|
|
531
|
+
429): a false positive only leaves a file for the next run, while a false
|
|
532
|
+
negative would quarantine a retryable submission. The raw response is always
|
|
533
|
+
printed, so a misclassification is visible.
|
|
534
|
+
|
|
535
|
+
### Interpret links
|
|
536
|
+
|
|
537
|
+
`ipaapi report <id>` fetches the IPA Interpret URL for a finished analysis. It
|
|
538
|
+
checks status first, so an unfinished analysis says so rather than surfacing a
|
|
539
|
+
bare HTTP 500.
|
|
540
|
+
|
|
541
|
+
**These have been observed to return HTTP 500 even for succeeded analyses.**
|
|
542
|
+
The cause is unconfirmed — possibly the commercial add-on licence, possibly a
|
|
543
|
+
stale endpoint path inherited from the demo. `examples/probe_interpret.py`
|
|
544
|
+
prints the raw response for diagnosis. Analyses open fine in IPA itself.
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## Authentication
|
|
549
|
+
|
|
550
|
+
Browser-based OAuth 2.0 with PKCE. Your password never reaches this package.
|
|
551
|
+
|
|
552
|
+
1. A short-lived HTTP server binds `127.0.0.1:8000`.
|
|
553
|
+
2. Your browser opens QIAGEN's authorization page; you log in there.
|
|
554
|
+
3. QIAGEN redirects back to `localhost:8000` with a one-time code. The `state`
|
|
555
|
+
parameter is verified, then the code plus the PKCE verifier is exchanged for
|
|
556
|
+
a token.
|
|
557
|
+
4. The token is used as `Authorization: Bearer …` and the server shuts down.
|
|
558
|
+
|
|
559
|
+
Whichever account you log in as owns the datasets and projects.
|
|
560
|
+
|
|
561
|
+
The client ID is the public one any IPA user may use — it is not a secret.
|
|
562
|
+
|
|
563
|
+
### Token caching and refresh
|
|
564
|
+
|
|
565
|
+
Tokens are cached at `~/.cache/ipaapi/token.json`, owner-only (0600). **Access
|
|
566
|
+
tokens are short-lived**, but a refresh token comes with them and is spent
|
|
567
|
+
automatically: an expired cache is renewed over HTTP with no browser and no
|
|
568
|
+
prompt. A browser login is only needed when the refresh token itself is
|
|
569
|
+
rejected.
|
|
570
|
+
|
|
571
|
+
Deleting the cache is effectively logging out. `--no-cache` forces a fresh
|
|
572
|
+
login. Be aware the token is plaintext on disk — anyone who can read your home
|
|
573
|
+
directory can use it until it expires.
|
|
574
|
+
|
|
575
|
+
### Headless servers
|
|
576
|
+
|
|
577
|
+
Because refresh is automatic, a token copied from a machine with a browser keeps
|
|
578
|
+
renewing itself indefinitely:
|
|
579
|
+
|
|
580
|
+
```bash
|
|
581
|
+
# once, on a machine with a browser
|
|
582
|
+
ipaapi submit ... # or any command that logs in
|
|
583
|
+
|
|
584
|
+
scp ~/.cache/ipaapi/token.json server:~/.cache/ipaapi/token.json
|
|
585
|
+
ssh server chmod 600 ~/.cache/ipaapi/token.json
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
**If `$HOME` isn't writable**, the cache can't be saved and every run needs a
|
|
589
|
+
fresh login — crippling on a headless box. Point it somewhere writable:
|
|
590
|
+
|
|
591
|
+
```bash
|
|
592
|
+
export IPAAPI_TOKEN_FILE=$HOME/ipaapi-token.json
|
|
593
|
+
export IPAAPI_LOG_FILE=$HOME/ipaapi-submissions.tsv
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
Both failures are reported loudly rather than swallowed, because a cache that
|
|
597
|
+
never writes looks exactly like a token that expires instantly.
|
|
598
|
+
|
|
599
|
+
When an interactive login is genuinely needed, X forwarding is the cleanest
|
|
600
|
+
route — the server-side browser renders locally *and* `localhost:8000` resolves
|
|
601
|
+
server-side where the callback listens, so no port forwarding is required:
|
|
602
|
+
|
|
603
|
+
```bash
|
|
604
|
+
ssh -X you@server # ssh -Y from macOS, with XQuartz running
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
Failing that, forward the callback port and use your own browser:
|
|
608
|
+
|
|
609
|
+
```bash
|
|
610
|
+
ssh -L 8000:localhost:8000 you@server
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
The error message distinguishes `DISPLAY` unset from no browser found.
|
|
614
|
+
|
|
615
|
+
> The redirect URI is pinned to `http://localhost:8000` by the OAuth client
|
|
616
|
+
> registration, so the port is not configurable in practice.
|
|
617
|
+
|
|
618
|
+
### Using a token obtained elsewhere
|
|
619
|
+
|
|
620
|
+
```python
|
|
621
|
+
import os
|
|
622
|
+
from ipaapi import Credentials, IPAClient
|
|
623
|
+
|
|
624
|
+
client = IPAClient(Credentials.from_token(os.environ["IPA_TOKEN"]))
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
---
|
|
628
|
+
|
|
629
|
+
## Python API
|
|
630
|
+
|
|
631
|
+
```python
|
|
632
|
+
from ipaapi import (
|
|
633
|
+
ColumnMapping, Dataset, IPAClient, Measurement, MeasurementType,
|
|
634
|
+
Observation, ReferenceSet, TokenCache,
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
mapping = ColumnMapping(
|
|
638
|
+
gene_id_column="Common_name",
|
|
639
|
+
gene_id_type="hugo",
|
|
640
|
+
observations=[
|
|
641
|
+
Observation("HIV vs NEG", [
|
|
642
|
+
Measurement("Fold_change", MeasurementType.LOG_RATIO),
|
|
643
|
+
]),
|
|
644
|
+
],
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
dataset = Dataset.from_file("results.csv", mapping, skip_rows=1)
|
|
648
|
+
print(dataset.describe()) # confirm before uploading
|
|
649
|
+
|
|
650
|
+
client = IPAClient.login(cache=TokenCache())
|
|
651
|
+
ids = client.submit(dataset, project="MyStudy", reference_set=ReferenceSet.IPKB)
|
|
652
|
+
|
|
653
|
+
for analysis_id, status in client.wait_for(ids).items():
|
|
654
|
+
if status.succeeded:
|
|
655
|
+
print(client.report_url(analysis_id))
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
Key objects:
|
|
659
|
+
|
|
660
|
+
| Object | Purpose |
|
|
661
|
+
| --- | --- |
|
|
662
|
+
| `ColumnMapping`, `Observation`, `Measurement` | describe the file |
|
|
663
|
+
| `Dataset.from_file` / `.from_frame` | load and validate |
|
|
664
|
+
| `IPAClient.login()` | OAuth, with caching and refresh |
|
|
665
|
+
| `.submit()` `.status()` `.wait_for()` `.results()` `.report_url()` | the API |
|
|
666
|
+
| `GENE_ID_TYPES` | all 33 identifier types and what they mean |
|
|
667
|
+
| `ipaapi.history` | the submission log |
|
|
668
|
+
| `ipaapi.errors` | everything derives from `IPAError` |
|
|
669
|
+
|
|
670
|
+
### Results
|
|
671
|
+
|
|
672
|
+
```python
|
|
673
|
+
results = client.results(analysis_id)
|
|
674
|
+
print(results.canonical_pathways.head())
|
|
675
|
+
cp, ur, df = results # unpacks like the demo's ipa_results()
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
> Programmatic result retrieval is a **commercial IPA add-on**. Without it these
|
|
679
|
+
> calls raise `ResultsUnavailableError`. Submission, status polling and report
|
|
680
|
+
> links are unaffected.
|
|
681
|
+
|
|
682
|
+
---
|
|
683
|
+
|
|
684
|
+
## Troubleshooting
|
|
685
|
+
|
|
686
|
+
| Symptom | Cause | Fix |
|
|
687
|
+
| --- | --- | --- |
|
|
688
|
+
| `REJECTED: IPA does not recognise the gene ID type 'X'` | not in IPA's vocabulary | `--list-id-types`; human symbols are `hugo` |
|
|
689
|
+
| `declared 'foldchange' but holds N out-of-range value(s)` | log2 values declared as linear fold change | `--FC N:logratio` |
|
|
690
|
+
| `Could not find a header row … looks like a comment` | preamble above the header | `--skip-rows N` |
|
|
691
|
+
| `--FC refers to column N, but the file has only M column(s)` | 1-based counting, or wrong `--skip-rows` | positions are 0-based, from the header |
|
|
692
|
+
| `Every row is missing an identifier` | wrong column, or no header | check with `head -1 file \| tr '\t' '\n' \| nl -v0` |
|
|
693
|
+
| `the analysis allowance appears to be exhausted` | daily/period limit | re-run later; files left in place resume |
|
|
694
|
+
| Login prompt on every run | token cache not writable | `export IPAAPI_TOKEN_FILE=...`; check for a root-owned cache |
|
|
695
|
+
| `Could not open a browser automatically` | headless | `ssh -X`, or copy a token across |
|
|
696
|
+
| `report` returns HTTP 500 on a succeeded analysis | unconfirmed; possibly add-on licence | open the analysis in IPA; see `examples/probe_interpret.py` |
|
|
697
|
+
| Analyses have z-scores but no p-values | reference set equals the gene list | `--reference-set ipkb` |
|
|
698
|
+
| Half of all pathways significant | list too large for the background | apply a cutoff, or upload unfiltered data with a cutoff |
|
|
699
|
+
|
|
700
|
+
Useful first move for any column problem:
|
|
701
|
+
|
|
702
|
+
```bash
|
|
703
|
+
head -1 yourfile.csv | tr ',\t' '\n' | nl -v0
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
## How a submission is encoded
|
|
709
|
+
|
|
710
|
+
Worth knowing when debugging. `--ID 1:hugo` becomes three separate things:
|
|
711
|
+
|
|
712
|
+
| From `--ID` | Wire parameter | Sent |
|
|
713
|
+
| --- | --- | --- |
|
|
714
|
+
| the type | `geneidtype=hugo` | once |
|
|
715
|
+
| the column, resolved from position to header name | `genecolname=Common_name` | once |
|
|
716
|
+
| that column's values | `geneid=XIST`, `geneid=UTY`, … | once per row |
|
|
717
|
+
|
|
718
|
+
The column *number* never leaves your machine.
|
|
719
|
+
|
|
720
|
+
The whole dataset travels in one `application/x-www-form-urlencoded` POST to
|
|
721
|
+
`/pa/api/v2/multiobsanalysis`, which both creates the dataset in the project and
|
|
722
|
+
starts one analysis per observation. Parameter naming is positional and
|
|
723
|
+
irregular — for measurement slot *k* and observation *i*, both zero-based:
|
|
724
|
+
|
|
725
|
+
| Parameter | Meaning |
|
|
726
|
+
| --- | --- |
|
|
727
|
+
| `expvaltype`, `expvaltypeK+1` | measurement type for slot *k* (global) |
|
|
728
|
+
| `cutoff`, `cutoffK+1` | cutoff for slot *k* (global, optional) |
|
|
729
|
+
| `obsI+1name` | observation name |
|
|
730
|
+
| `expvalname`, `expvalK+1name` | column label, first observation |
|
|
731
|
+
| `obsI+1expvalname`, `obsI+1expvalK+1name` | column label, later observations |
|
|
732
|
+
| `geneid` | one per data row |
|
|
733
|
+
| `expvalue`, `expvalK+1` | one per slot per observation, per row |
|
|
734
|
+
|
|
735
|
+
Per-row value parameters carry no observation prefix — they cycle through the
|
|
736
|
+
slots of observation 1, then observation 2, and so on. Order is load-bearing.
|
|
737
|
+
|
|
738
|
+
The body is properly percent-encoded. The demo concatenated it by hand, so any
|
|
739
|
+
value containing a space, `&`, `=`, `+` or `%` corrupted the request — including
|
|
740
|
+
the `Group Max Intensity` column in the demo's own sample dataset.
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
## Development
|
|
745
|
+
|
|
746
|
+
```
|
|
747
|
+
src/ipaapi/
|
|
748
|
+
__init__.py public API and the version (single source of truth)
|
|
749
|
+
models.py MeasurementType, AnalysisStatus, ReferenceSet, GENE_ID_TYPES
|
|
750
|
+
mapping.py Measurement, Observation, ColumnMapping
|
|
751
|
+
dataset.py Dataset, load_table
|
|
752
|
+
_payload.py multiobsanalysis body construction
|
|
753
|
+
auth.py OAuth 2.0 + PKCE, Credentials, TokenCache, refresh
|
|
754
|
+
client.py IPAClient, error classification
|
|
755
|
+
history.py the submission log
|
|
756
|
+
triage.py submitted/ and failed/ filing
|
|
757
|
+
cli.py the ipaapi console script
|
|
758
|
+
errors.py exception hierarchy
|
|
759
|
+
tests/ offline; no network required
|
|
760
|
+
examples/ runnable scripts and diagnostics
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
```bash
|
|
764
|
+
pip install -e ".[dev]"
|
|
765
|
+
pytest
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
The suite is fully offline — mapping validation, the exact parameter layout of
|
|
769
|
+
the submission body, encoding of hostile characters, error classification,
|
|
770
|
+
triage behaviour, token cache and refresh logic.
|
|
771
|
+
|
|
772
|
+
**Versioning.** The version lives only in `src/ipaapi/__init__.py`;
|
|
773
|
+
`pyproject.toml` reads it at build time. Bump it there and nowhere else, and add
|
|
774
|
+
a `CHANGELOG.md` entry. `ipaapi --version` reports the install path too, which
|
|
775
|
+
is what actually answers "am I running the wheel I think I am".
|
|
776
|
+
|
|
777
|
+
### Differences from the demo
|
|
778
|
+
|
|
779
|
+
- Column mapping by name in any order, validated before upload.
|
|
780
|
+
- Request bodies are percent-encoded.
|
|
781
|
+
- OAuth: no CPU-spinning wait loop, `state` is verified, logins time out, the
|
|
782
|
+
callback server is shut down, error redirects are handled, tokens are cached
|
|
783
|
+
and refreshed.
|
|
784
|
+
- Submissions are never retried automatically — a retried POST could create a
|
|
785
|
+
duplicate analysis. GETs retry with backoff.
|
|
786
|
+
- Typed exceptions; access tokens excluded from `repr()`.
|
|
787
|
+
- No `install_dependencies()` shelling out to `pip3`.
|
|
788
|
+
|
|
789
|
+
---
|
|
790
|
+
|
|
791
|
+
## Contributing
|
|
792
|
+
|
|
793
|
+
Issues and pull requests are welcome. The most useful contributions are
|
|
794
|
+
**corrections to the [Working with IPA](#working-with-ipa) section** — much of
|
|
795
|
+
it was established by trial against a live account, and a few points have
|
|
796
|
+
already had to be corrected more than once. If IPA behaves differently for you,
|
|
797
|
+
that is worth reporting even without a code change.
|
|
798
|
+
|
|
799
|
+
```bash
|
|
800
|
+
pip install -e ".[dev]"
|
|
801
|
+
pytest
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
Tests are fully offline; none of them contact IPA.
|
|
805
|
+
|
|
806
|
+
---
|
|
807
|
+
|
|
808
|
+
## Status
|
|
809
|
+
|
|
810
|
+
**1.0** — stable and in production use against live IPA. The command line and
|
|
811
|
+
the Python API are settled; breaking changes from here mean a major version
|
|
812
|
+
bump. See `CHANGELOG.md`.
|
|
813
|
+
|
|
814
|
+
Known open questions, none of which affect submission:
|
|
815
|
+
|
|
816
|
+
- Interpret links (`ipaapi report`) have returned HTTP 500 for analyses that
|
|
817
|
+
succeeded. Cause unconfirmed; possibly the commercial add-on licence.
|
|
818
|
+
- Programmatic result retrieval (`client.results()`) requires that same add-on
|
|
819
|
+
and is largely untested here.
|
|
820
|
+
- The documented reference-set size rule does not match observed behaviour;
|
|
821
|
+
set `--reference-set` explicitly.
|
|
822
|
+
|
|
823
|
+
---
|
|
824
|
+
|
|
825
|
+
## Licence
|
|
826
|
+
|
|
827
|
+
MIT — see [LICENSE](LICENSE). Free to use, modify and redistribute.
|
|
828
|
+
|
|
829
|
+
Not affiliated with, endorsed by, or supported by QIAGEN. IPA is QIAGEN's
|
|
830
|
+
product; this is an independent client for its public API, built on the
|
|
831
|
+
`python-api-demo` example code QIAGEN publishes. For questions about the API
|
|
832
|
+
itself, QIAGEN's contact is `AdvancedGenomicsSupport@qiagen.com` — please don't
|
|
833
|
+
send them bug reports about this package.
|