dnakit 0.1.0.dev0__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.
- dnakit/__init__.py +108 -0
- dnakit/_version.py +5 -0
- dnakit/alignment/__init__.py +6 -0
- dnakit/alignment/pairwise.py +512 -0
- dnakit/alignment/results.py +188 -0
- dnakit/backends/__init__.py +80 -0
- dnakit/backends/external.py +517 -0
- dnakit/backends/registry.py +207 -0
- dnakit/batch.py +452 -0
- dnakit/cache/__init__.py +5 -0
- dnakit/cache/store.py +424 -0
- dnakit/chunking.py +920 -0
- dnakit/cli/__init__.py +5 -0
- dnakit/cli/__main__.py +6 -0
- dnakit/cli/app.py +814 -0
- dnakit/cli/workflow.py +94 -0
- dnakit/config/__init__.py +5 -0
- dnakit/config/loader.py +381 -0
- dnakit/core/__init__.py +80 -0
- dnakit/core/_json.py +206 -0
- dnakit/core/backend_info.py +70 -0
- dnakit/core/collection.py +163 -0
- dnakit/core/coordinates.py +295 -0
- dnakit/core/enums.py +95 -0
- dnakit/core/facade.py +719 -0
- dnakit/core/feature.py +90 -0
- dnakit/core/gap.py +82 -0
- dnakit/core/issues.py +62 -0
- dnakit/core/provenance.py +305 -0
- dnakit/core/record.py +161 -0
- dnakit/core/results.py +134 -0
- dnakit/core/sequence.py +327 -0
- dnakit/datasets/__init__.py +100 -0
- dnakit/datasets/_advanced_shared.py +198 -0
- dnakit/datasets/_metadata.py +33 -0
- dnakit/datasets/advanced_deduplicate.py +183 -0
- dnakit/datasets/advanced_split.py +302 -0
- dnakit/datasets/clustering.py +286 -0
- dnakit/datasets/config.py +531 -0
- dnakit/datasets/deduplicate.py +303 -0
- dnakit/datasets/evaluation.py +244 -0
- dnakit/datasets/filter.py +135 -0
- dnakit/datasets/neural_clustering.py +221 -0
- dnakit/datasets/results.py +520 -0
- dnakit/datasets/split.py +450 -0
- dnakit/descriptors/__init__.py +102 -0
- dnakit/descriptors/_dinucleotide.py +345 -0
- dnakit/descriptors/_shared.py +180 -0
- dnakit/descriptors/all.py +917 -0
- dnakit/descriptors/basic.py +193 -0
- dnakit/descriptors/codon.py +195 -0
- dnakit/descriptors/complexity.py +201 -0
- dnakit/descriptors/entropy.py +101 -0
- dnakit/descriptors/homopolymer.py +105 -0
- dnakit/descriptors/kmer.py +88 -0
- dnakit/descriptors/results.py +286 -0
- dnakit/descriptors/schema.py +414 -0
- dnakit/descriptors/window.py +203 -0
- dnakit/download/__init__.py +54 -0
- dnakit/download/catalogs.py +166 -0
- dnakit/download/ena.py +131 -0
- dnakit/download/encode.py +103 -0
- dnakit/download/files.py +469 -0
- dnakit/download/indexes.py +233 -0
- dnakit/download/metadata.py +164 -0
- dnakit/download/models.py +227 -0
- dnakit/download/ncbi.py +333 -0
- dnakit/download/sequence.py +133 -0
- dnakit/download/ucsc.py +99 -0
- dnakit/evaluation/__init__.py +81 -0
- dnakit/evaluation/_shared.py +288 -0
- dnakit/evaluation/collection.py +270 -0
- dnakit/evaluation/config.py +591 -0
- dnakit/evaluation/distribution.py +287 -0
- dnakit/evaluation/frechet.py +223 -0
- dnakit/evaluation/generative.py +381 -0
- dnakit/evaluation/reference.py +409 -0
- dnakit/evaluation/results.py +137 -0
- dnakit/evaluation/scorecard.py +211 -0
- dnakit/evaluation/sequence.py +403 -0
- dnakit/evaluation/synthesis.py +249 -0
- dnakit/exceptions.py +189 -0
- dnakit/fingerprints/__init__.py +109 -0
- dnakit/fingerprints/_shared.py +116 -0
- dnakit/fingerprints/advanced.py +829 -0
- dnakit/fingerprints/bit.py +229 -0
- dnakit/fingerprints/encoding.py +351 -0
- dnakit/fingerprints/kmer.py +170 -0
- dnakit/fingerprints/results.py +531 -0
- dnakit/fingerprints/sketch.py +246 -0
- dnakit/io/__init__.py +112 -0
- dnakit/io/_advanced_common.py +105 -0
- dnakit/io/_formats.py +994 -0
- dnakit/io/annotations.py +1179 -0
- dnakit/io/api.py +759 -0
- dnakit/io/config.py +315 -0
- dnakit/io/genbank.py +523 -0
- dnakit/io/indexed.py +1146 -0
- dnakit/io/metadata.py +452 -0
- dnakit/io/parquet.py +56 -0
- dnakit/io/results.py +131 -0
- dnakit/io/source.py +138 -0
- dnakit/io/tables.py +1237 -0
- dnakit/molbio/__init__.py +101 -0
- dnakit/molbio/_shared.py +266 -0
- dnakit/molbio/assembly.py +279 -0
- dnakit/molbio/crispr.py +181 -0
- dnakit/molbio/optimization.py +897 -0
- dnakit/molbio/primer3_design.py +667 -0
- dnakit/molbio/primers.py +899 -0
- dnakit/molbio/restriction.py +573 -0
- dnakit/molbio/results.py +583 -0
- dnakit/ops/__init__.py +588 -0
- dnakit/ops/_common.py +201 -0
- dnakit/ops/circular.py +193 -0
- dnakit/ops/concat.py +204 -0
- dnakit/ops/direction.py +27 -0
- dnakit/ops/edit.py +443 -0
- dnakit/ops/evolution.py +991 -0
- dnakit/ops/mutation.py +151 -0
- dnakit/ops/records.py +1012 -0
- dnakit/ops/translation.py +257 -0
- dnakit/patterns/__init__.py +81 -0
- dnakit/patterns/_shared.py +282 -0
- dnakit/patterns/coding.py +360 -0
- dnakit/patterns/crispr.py +337 -0
- dnakit/patterns/motif.py +721 -0
- dnakit/patterns/regions.py +341 -0
- dnakit/patterns/repeats.py +480 -0
- dnakit/patterns/restriction.py +281 -0
- dnakit/patterns/results.py +411 -0
- dnakit/py.typed +1 -0
- dnakit/references/__init__.py +27 -0
- dnakit/references/models.py +155 -0
- dnakit/references/ncbi.py +785 -0
- dnakit/representations/__init__.py +29 -0
- dnakit/representations/backends.py +928 -0
- dnakit/representations/checkpoints.py +356 -0
- dnakit/representations/extraction.py +220 -0
- dnakit/representations/models.py +395 -0
- dnakit/representations/results.py +79 -0
- dnakit/search/__init__.py +132 -0
- dnakit/search/_http.py +271 -0
- dnakit/search/_shared.py +108 -0
- dnakit/search/blast.py +516 -0
- dnakit/search/ena.py +245 -0
- dnakit/search/encode.py +102 -0
- dnakit/search/ensembl.py +874 -0
- dnakit/search/models.py +159 -0
- dnakit/search/ncbi.py +756 -0
- dnakit/search/ucsc.py +388 -0
- dnakit/secondary_structure/__init__.py +38 -0
- dnakit/secondary_structure/dotbracket.py +410 -0
- dnakit/secondary_structure/nupack.py +589 -0
- dnakit/secondary_structure/results.py +142 -0
- dnakit/similarity/__init__.py +125 -0
- dnakit/similarity/_shared.py +152 -0
- dnakit/similarity/approximate.py +258 -0
- dnakit/similarity/compare.py +162 -0
- dnakit/similarity/dashing.py +936 -0
- dnakit/similarity/distance.py +279 -0
- dnakit/similarity/index.py +365 -0
- dnakit/similarity/matrix.py +254 -0
- dnakit/similarity/results.py +676 -0
- dnakit/similarity/search.py +286 -0
- dnakit/similarity/sketch.py +96 -0
- dnakit/similarity/vector.py +662 -0
- dnakit/standardize/__init__.py +77 -0
- dnakit/standardize/_shared.py +100 -0
- dnakit/standardize/config.py +411 -0
- dnakit/standardize/gaps.py +1060 -0
- dnakit/standardize/normalize.py +565 -0
- dnakit/standardize/results.py +247 -0
- dnakit/standardize/validate.py +487 -0
- dnakit/structure3d/__init__.py +47 -0
- dnakit/structure3d/_shared.py +47 -0
- dnakit/structure3d/geometry.py +773 -0
- dnakit/structure3d/pdb.py +253 -0
- dnakit/structure3d/results.py +233 -0
- dnakit/structure3d/threedna.py +174 -0
- dnakit/thermodynamics/__init__.py +112 -0
- dnakit/thermodynamics/_shared.py +92 -0
- dnakit/thermodynamics/backends.py +653 -0
- dnakit/thermodynamics/calculations.py +837 -0
- dnakit/thermodynamics/config.py +175 -0
- dnakit/thermodynamics/equilibrium.py +340 -0
- dnakit/thermodynamics/optics.py +385 -0
- dnakit/thermodynamics/parameters.py +79 -0
- dnakit/thermodynamics/results.py +445 -0
- dnakit/visualization/__init__.py +43 -0
- dnakit/visualization/_svg.py +185 -0
- dnakit/visualization/advanced.py +314 -0
- dnakit/visualization/config.py +255 -0
- dnakit/visualization/export.py +349 -0
- dnakit/visualization/matrix.py +279 -0
- dnakit/visualization/report.py +431 -0
- dnakit/visualization/results.py +236 -0
- dnakit/visualization/sequence.py +459 -0
- dnakit/workflows/__init__.py +45 -0
- dnakit/workflows/manifest.py +365 -0
- dnakit/workflows/runner.py +1548 -0
- dnakit/workflows/schema.py +898 -0
- dnakit-0.1.0.dev0.dist-info/METADATA +126 -0
- dnakit-0.1.0.dev0.dist-info/RECORD +210 -0
- dnakit-0.1.0.dev0.dist-info/WHEEL +5 -0
- dnakit-0.1.0.dev0.dist-info/entry_points.txt +2 -0
- dnakit-0.1.0.dev0.dist-info/licenses/DISCLAIMER.md +26 -0
- dnakit-0.1.0.dev0.dist-info/licenses/LICENSE +21 -0
- dnakit-0.1.0.dev0.dist-info/licenses/THIRD_PARTY_NOTICES.md +73 -0
- dnakit-0.1.0.dev0.dist-info/top_level.txt +1 -0
dnakit/search/ena.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""European Nucleotide Archive Portal API query adapters."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
|
|
8
|
+
from dnakit.exceptions import ConfigurationError, QueryError
|
|
9
|
+
|
|
10
|
+
from ._http import (
|
|
11
|
+
build_url,
|
|
12
|
+
limited_records,
|
|
13
|
+
mapping_records,
|
|
14
|
+
redact_url,
|
|
15
|
+
request_json,
|
|
16
|
+
require_text,
|
|
17
|
+
)
|
|
18
|
+
from ._shared import adapter_provenance, resolved_config
|
|
19
|
+
from .models import QueryResult, SearchConfig
|
|
20
|
+
|
|
21
|
+
DEFAULT_ENA_PORTAL_URL = "https://www.ebi.ac.uk/ena/portal/api"
|
|
22
|
+
_RESULT_TYPES = frozenset(
|
|
23
|
+
{
|
|
24
|
+
"analysis",
|
|
25
|
+
"assembly",
|
|
26
|
+
"read_experiment",
|
|
27
|
+
"read_run",
|
|
28
|
+
"sample",
|
|
29
|
+
"sequence",
|
|
30
|
+
"study",
|
|
31
|
+
"taxon",
|
|
32
|
+
"tsa_set",
|
|
33
|
+
"wgs_set",
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
_FIELD = re.compile(r"^[A-Za-z][A-Za-z0-9_]*$")
|
|
37
|
+
DEFAULT_READ_FIELDS = (
|
|
38
|
+
"accession",
|
|
39
|
+
"study_accession",
|
|
40
|
+
"sample_accession",
|
|
41
|
+
"experiment_accession",
|
|
42
|
+
"scientific_name",
|
|
43
|
+
"tax_id",
|
|
44
|
+
"library_strategy",
|
|
45
|
+
"library_source",
|
|
46
|
+
"library_selection",
|
|
47
|
+
"library_layout",
|
|
48
|
+
"instrument_platform",
|
|
49
|
+
"instrument_model",
|
|
50
|
+
"nominal_length",
|
|
51
|
+
"read_count",
|
|
52
|
+
"base_count",
|
|
53
|
+
"fastq_ftp",
|
|
54
|
+
"fastq_md5",
|
|
55
|
+
"fastq_bytes",
|
|
56
|
+
"submitted_ftp",
|
|
57
|
+
"submitted_md5",
|
|
58
|
+
"submitted_bytes",
|
|
59
|
+
"first_public",
|
|
60
|
+
"last_updated",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def ena_search(
|
|
65
|
+
result: str,
|
|
66
|
+
query: str,
|
|
67
|
+
*,
|
|
68
|
+
fields: Sequence[str],
|
|
69
|
+
limit: int = 100,
|
|
70
|
+
offset: int = 0,
|
|
71
|
+
config: SearchConfig | None = None,
|
|
72
|
+
api_base_url: str = DEFAULT_ENA_PORTAL_URL,
|
|
73
|
+
query_type: str | None = None,
|
|
74
|
+
) -> QueryResult:
|
|
75
|
+
"""Run a bounded ENA Portal query and return selected metadata fields."""
|
|
76
|
+
|
|
77
|
+
resolved = resolved_config(config)
|
|
78
|
+
result_type = require_text(result, "result", max_length=64)
|
|
79
|
+
if result_type not in _RESULT_TYPES:
|
|
80
|
+
raise ConfigurationError(
|
|
81
|
+
"Unsupported ENA result type.",
|
|
82
|
+
code="INVALID_QUERY",
|
|
83
|
+
context={"allowed": sorted(_RESULT_TYPES)},
|
|
84
|
+
)
|
|
85
|
+
expression = require_text(query, "query")
|
|
86
|
+
field_values = tuple(require_text(field, "field", max_length=128) for field in fields)
|
|
87
|
+
if (
|
|
88
|
+
not field_values
|
|
89
|
+
or len(field_values) > 128
|
|
90
|
+
or any(_FIELD.fullmatch(field) is None for field in field_values)
|
|
91
|
+
):
|
|
92
|
+
raise ConfigurationError(
|
|
93
|
+
"ENA fields must contain 1-128 valid field names.", code="INVALID_QUERY"
|
|
94
|
+
)
|
|
95
|
+
if (
|
|
96
|
+
isinstance(limit, bool)
|
|
97
|
+
or not isinstance(limit, int)
|
|
98
|
+
or not 1 <= limit <= resolved.max_records
|
|
99
|
+
):
|
|
100
|
+
raise ConfigurationError(
|
|
101
|
+
"limit exceeds SearchConfig max_records.", code="QUERY_RECORD_LIMIT"
|
|
102
|
+
)
|
|
103
|
+
if isinstance(offset, bool) or not isinstance(offset, int) or offset < 0:
|
|
104
|
+
raise ConfigurationError("offset must be a non-negative integer.", code="INVALID_QUERY")
|
|
105
|
+
url = build_url(
|
|
106
|
+
api_base_url,
|
|
107
|
+
"/search",
|
|
108
|
+
(
|
|
109
|
+
("result", result_type),
|
|
110
|
+
("query", expression),
|
|
111
|
+
("fields", ",".join(field_values)),
|
|
112
|
+
("format", "json"),
|
|
113
|
+
("limit", limit),
|
|
114
|
+
("offset", offset),
|
|
115
|
+
),
|
|
116
|
+
)
|
|
117
|
+
payload = request_json(url, resolved, provider="ENA")
|
|
118
|
+
if not isinstance(payload, list):
|
|
119
|
+
raise QueryError("ENA returned an unexpected response.", code="QUERY_RESPONSE_ERROR")
|
|
120
|
+
records = limited_records(mapping_records(payload), resolved)
|
|
121
|
+
return QueryResult(
|
|
122
|
+
query_type or result_type,
|
|
123
|
+
"ENA",
|
|
124
|
+
redact_url(url),
|
|
125
|
+
records,
|
|
126
|
+
adapter_provenance(
|
|
127
|
+
"European Nucleotide Archive",
|
|
128
|
+
citation_url="https://www.ebi.ac.uk/ena/portal/api/",
|
|
129
|
+
filters={
|
|
130
|
+
"result": result_type,
|
|
131
|
+
"query": expression,
|
|
132
|
+
"fields": field_values,
|
|
133
|
+
"limit": limit,
|
|
134
|
+
"offset": offset,
|
|
135
|
+
},
|
|
136
|
+
),
|
|
137
|
+
total_count=len(records),
|
|
138
|
+
metadata={
|
|
139
|
+
"result_type": result_type,
|
|
140
|
+
"offset": offset,
|
|
141
|
+
"page_is_full": len(records) == limit,
|
|
142
|
+
},
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def reads(
|
|
147
|
+
query: str,
|
|
148
|
+
*,
|
|
149
|
+
fields: Sequence[str] = DEFAULT_READ_FIELDS,
|
|
150
|
+
result: str = "read_run",
|
|
151
|
+
limit: int = 100,
|
|
152
|
+
offset: int = 0,
|
|
153
|
+
config: SearchConfig | None = None,
|
|
154
|
+
api_base_url: str = DEFAULT_ENA_PORTAL_URL,
|
|
155
|
+
) -> QueryResult:
|
|
156
|
+
"""Query sequencing Study/Experiment/Run metadata and public file locations."""
|
|
157
|
+
|
|
158
|
+
return ena_search(
|
|
159
|
+
result,
|
|
160
|
+
query,
|
|
161
|
+
fields=fields,
|
|
162
|
+
limit=limit,
|
|
163
|
+
offset=offset,
|
|
164
|
+
config=config,
|
|
165
|
+
api_base_url=api_base_url,
|
|
166
|
+
query_type="reads",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def project(
|
|
171
|
+
query: str,
|
|
172
|
+
*,
|
|
173
|
+
fields: Sequence[str] = (
|
|
174
|
+
"study_accession",
|
|
175
|
+
"secondary_study_accession",
|
|
176
|
+
"study_title",
|
|
177
|
+
"study_type",
|
|
178
|
+
"study_abstract",
|
|
179
|
+
"center_name",
|
|
180
|
+
"first_public",
|
|
181
|
+
"last_updated",
|
|
182
|
+
),
|
|
183
|
+
limit: int = 100,
|
|
184
|
+
offset: int = 0,
|
|
185
|
+
config: SearchConfig | None = None,
|
|
186
|
+
api_base_url: str = DEFAULT_ENA_PORTAL_URL,
|
|
187
|
+
) -> QueryResult:
|
|
188
|
+
"""Search public ENA Study/BioProject records."""
|
|
189
|
+
|
|
190
|
+
return ena_search(
|
|
191
|
+
"study",
|
|
192
|
+
query,
|
|
193
|
+
fields=fields,
|
|
194
|
+
limit=limit,
|
|
195
|
+
offset=offset,
|
|
196
|
+
config=config,
|
|
197
|
+
api_base_url=api_base_url,
|
|
198
|
+
query_type="project",
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def sample(
|
|
203
|
+
query: str,
|
|
204
|
+
*,
|
|
205
|
+
fields: Sequence[str] = (
|
|
206
|
+
"sample_accession",
|
|
207
|
+
"secondary_sample_accession",
|
|
208
|
+
"scientific_name",
|
|
209
|
+
"tax_id",
|
|
210
|
+
"description",
|
|
211
|
+
"collection_date",
|
|
212
|
+
"country",
|
|
213
|
+
"location",
|
|
214
|
+
"host",
|
|
215
|
+
"host_tax_id",
|
|
216
|
+
"first_public",
|
|
217
|
+
"last_updated",
|
|
218
|
+
),
|
|
219
|
+
limit: int = 100,
|
|
220
|
+
offset: int = 0,
|
|
221
|
+
config: SearchConfig | None = None,
|
|
222
|
+
api_base_url: str = DEFAULT_ENA_PORTAL_URL,
|
|
223
|
+
) -> QueryResult:
|
|
224
|
+
"""Search public ENA Sample/BioSample metadata."""
|
|
225
|
+
|
|
226
|
+
return ena_search(
|
|
227
|
+
"sample",
|
|
228
|
+
query,
|
|
229
|
+
fields=fields,
|
|
230
|
+
limit=limit,
|
|
231
|
+
offset=offset,
|
|
232
|
+
config=config,
|
|
233
|
+
api_base_url=api_base_url,
|
|
234
|
+
query_type="sample",
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
__all__ = [
|
|
239
|
+
"DEFAULT_ENA_PORTAL_URL",
|
|
240
|
+
"DEFAULT_READ_FIELDS",
|
|
241
|
+
"ena_search",
|
|
242
|
+
"project",
|
|
243
|
+
"reads",
|
|
244
|
+
"sample",
|
|
245
|
+
]
|
dnakit/search/encode.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""ENCODE Portal REST search adapter."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
|
|
8
|
+
from dnakit.exceptions import ConfigurationError, QueryError
|
|
9
|
+
|
|
10
|
+
from ._http import (
|
|
11
|
+
build_url,
|
|
12
|
+
limited_records,
|
|
13
|
+
mapping_records,
|
|
14
|
+
redact_url,
|
|
15
|
+
request_json,
|
|
16
|
+
require_text,
|
|
17
|
+
)
|
|
18
|
+
from ._shared import adapter_provenance, resolved_config
|
|
19
|
+
from .models import QueryResult, SearchConfig
|
|
20
|
+
|
|
21
|
+
DEFAULT_ENCODE_URL = "https://www.encodeproject.org"
|
|
22
|
+
_FILTER_KEY = re.compile(r"^[A-Za-z0-9_.@!-]+$")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def encode_search(
|
|
26
|
+
*,
|
|
27
|
+
object_type: str = "Experiment",
|
|
28
|
+
search_term: str | None = None,
|
|
29
|
+
filters: Mapping[str, object] | None = None,
|
|
30
|
+
limit: int = 25,
|
|
31
|
+
config: SearchConfig | None = None,
|
|
32
|
+
api_base_url: str = DEFAULT_ENCODE_URL,
|
|
33
|
+
query_type: str = "regulation",
|
|
34
|
+
) -> QueryResult:
|
|
35
|
+
"""Search released ENCODE experiments, files, biosamples, or annotations."""
|
|
36
|
+
|
|
37
|
+
resolved = resolved_config(config)
|
|
38
|
+
type_value = require_text(object_type, "object_type", max_length=128)
|
|
39
|
+
if (
|
|
40
|
+
isinstance(limit, bool)
|
|
41
|
+
or not isinstance(limit, int)
|
|
42
|
+
or not 1 <= limit <= resolved.max_records
|
|
43
|
+
):
|
|
44
|
+
raise ConfigurationError(
|
|
45
|
+
"limit exceeds SearchConfig max_records.", code="QUERY_RECORD_LIMIT"
|
|
46
|
+
)
|
|
47
|
+
params: list[tuple[str, object]] = [
|
|
48
|
+
("type", type_value),
|
|
49
|
+
("format", "json"),
|
|
50
|
+
("frame", "object"),
|
|
51
|
+
("limit", limit),
|
|
52
|
+
]
|
|
53
|
+
if search_term is not None:
|
|
54
|
+
params.append(("searchTerm", require_text(search_term, "search_term")))
|
|
55
|
+
safe_filters: dict[str, object] = {}
|
|
56
|
+
for key, value in dict(filters or {}).items():
|
|
57
|
+
if not isinstance(key, str) or _FILTER_KEY.fullmatch(key) is None:
|
|
58
|
+
raise ConfigurationError("ENCODE filter name is invalid.", code="INVALID_QUERY")
|
|
59
|
+
if isinstance(value, (str, int, float, bool)) and not isinstance(value, complex):
|
|
60
|
+
safe_filters[key] = value
|
|
61
|
+
elif isinstance(value, (tuple, list)) and all(
|
|
62
|
+
isinstance(item, (str, int, float, bool)) and not isinstance(item, complex)
|
|
63
|
+
for item in value
|
|
64
|
+
):
|
|
65
|
+
safe_filters[key] = tuple(value)
|
|
66
|
+
else:
|
|
67
|
+
raise ConfigurationError(
|
|
68
|
+
"ENCODE filter values must be scalars or scalar sequences.", code="INVALID_QUERY"
|
|
69
|
+
)
|
|
70
|
+
params.append((key, safe_filters[key]))
|
|
71
|
+
url = build_url(api_base_url, "/search/", params)
|
|
72
|
+
payload = request_json(url, resolved, provider="ENCODE")
|
|
73
|
+
if not isinstance(payload, Mapping):
|
|
74
|
+
raise QueryError("ENCODE returned an unexpected response.", code="QUERY_RESPONSE_ERROR")
|
|
75
|
+
records = limited_records(mapping_records(payload, key="@graph"), resolved)
|
|
76
|
+
raw_total = payload.get("total")
|
|
77
|
+
total = (
|
|
78
|
+
raw_total
|
|
79
|
+
if isinstance(raw_total, int) and not isinstance(raw_total, bool)
|
|
80
|
+
else len(records)
|
|
81
|
+
)
|
|
82
|
+
return QueryResult(
|
|
83
|
+
query_type,
|
|
84
|
+
"ENCODE",
|
|
85
|
+
redact_url(url),
|
|
86
|
+
records,
|
|
87
|
+
adapter_provenance(
|
|
88
|
+
"ENCODE Portal",
|
|
89
|
+
citation_url="https://www.encodeproject.org/help/rest-api/",
|
|
90
|
+
filters={
|
|
91
|
+
"object_type": type_value,
|
|
92
|
+
"search_term": search_term,
|
|
93
|
+
"filters": safe_filters,
|
|
94
|
+
"limit": limit,
|
|
95
|
+
},
|
|
96
|
+
),
|
|
97
|
+
total_count=max(total, len(records)),
|
|
98
|
+
metadata={"object_type": type_value, "returned_count": len(records)},
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
__all__ = ["DEFAULT_ENCODE_URL", "encode_search"]
|