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.
Files changed (210) hide show
  1. dnakit/__init__.py +108 -0
  2. dnakit/_version.py +5 -0
  3. dnakit/alignment/__init__.py +6 -0
  4. dnakit/alignment/pairwise.py +512 -0
  5. dnakit/alignment/results.py +188 -0
  6. dnakit/backends/__init__.py +80 -0
  7. dnakit/backends/external.py +517 -0
  8. dnakit/backends/registry.py +207 -0
  9. dnakit/batch.py +452 -0
  10. dnakit/cache/__init__.py +5 -0
  11. dnakit/cache/store.py +424 -0
  12. dnakit/chunking.py +920 -0
  13. dnakit/cli/__init__.py +5 -0
  14. dnakit/cli/__main__.py +6 -0
  15. dnakit/cli/app.py +814 -0
  16. dnakit/cli/workflow.py +94 -0
  17. dnakit/config/__init__.py +5 -0
  18. dnakit/config/loader.py +381 -0
  19. dnakit/core/__init__.py +80 -0
  20. dnakit/core/_json.py +206 -0
  21. dnakit/core/backend_info.py +70 -0
  22. dnakit/core/collection.py +163 -0
  23. dnakit/core/coordinates.py +295 -0
  24. dnakit/core/enums.py +95 -0
  25. dnakit/core/facade.py +719 -0
  26. dnakit/core/feature.py +90 -0
  27. dnakit/core/gap.py +82 -0
  28. dnakit/core/issues.py +62 -0
  29. dnakit/core/provenance.py +305 -0
  30. dnakit/core/record.py +161 -0
  31. dnakit/core/results.py +134 -0
  32. dnakit/core/sequence.py +327 -0
  33. dnakit/datasets/__init__.py +100 -0
  34. dnakit/datasets/_advanced_shared.py +198 -0
  35. dnakit/datasets/_metadata.py +33 -0
  36. dnakit/datasets/advanced_deduplicate.py +183 -0
  37. dnakit/datasets/advanced_split.py +302 -0
  38. dnakit/datasets/clustering.py +286 -0
  39. dnakit/datasets/config.py +531 -0
  40. dnakit/datasets/deduplicate.py +303 -0
  41. dnakit/datasets/evaluation.py +244 -0
  42. dnakit/datasets/filter.py +135 -0
  43. dnakit/datasets/neural_clustering.py +221 -0
  44. dnakit/datasets/results.py +520 -0
  45. dnakit/datasets/split.py +450 -0
  46. dnakit/descriptors/__init__.py +102 -0
  47. dnakit/descriptors/_dinucleotide.py +345 -0
  48. dnakit/descriptors/_shared.py +180 -0
  49. dnakit/descriptors/all.py +917 -0
  50. dnakit/descriptors/basic.py +193 -0
  51. dnakit/descriptors/codon.py +195 -0
  52. dnakit/descriptors/complexity.py +201 -0
  53. dnakit/descriptors/entropy.py +101 -0
  54. dnakit/descriptors/homopolymer.py +105 -0
  55. dnakit/descriptors/kmer.py +88 -0
  56. dnakit/descriptors/results.py +286 -0
  57. dnakit/descriptors/schema.py +414 -0
  58. dnakit/descriptors/window.py +203 -0
  59. dnakit/download/__init__.py +54 -0
  60. dnakit/download/catalogs.py +166 -0
  61. dnakit/download/ena.py +131 -0
  62. dnakit/download/encode.py +103 -0
  63. dnakit/download/files.py +469 -0
  64. dnakit/download/indexes.py +233 -0
  65. dnakit/download/metadata.py +164 -0
  66. dnakit/download/models.py +227 -0
  67. dnakit/download/ncbi.py +333 -0
  68. dnakit/download/sequence.py +133 -0
  69. dnakit/download/ucsc.py +99 -0
  70. dnakit/evaluation/__init__.py +81 -0
  71. dnakit/evaluation/_shared.py +288 -0
  72. dnakit/evaluation/collection.py +270 -0
  73. dnakit/evaluation/config.py +591 -0
  74. dnakit/evaluation/distribution.py +287 -0
  75. dnakit/evaluation/frechet.py +223 -0
  76. dnakit/evaluation/generative.py +381 -0
  77. dnakit/evaluation/reference.py +409 -0
  78. dnakit/evaluation/results.py +137 -0
  79. dnakit/evaluation/scorecard.py +211 -0
  80. dnakit/evaluation/sequence.py +403 -0
  81. dnakit/evaluation/synthesis.py +249 -0
  82. dnakit/exceptions.py +189 -0
  83. dnakit/fingerprints/__init__.py +109 -0
  84. dnakit/fingerprints/_shared.py +116 -0
  85. dnakit/fingerprints/advanced.py +829 -0
  86. dnakit/fingerprints/bit.py +229 -0
  87. dnakit/fingerprints/encoding.py +351 -0
  88. dnakit/fingerprints/kmer.py +170 -0
  89. dnakit/fingerprints/results.py +531 -0
  90. dnakit/fingerprints/sketch.py +246 -0
  91. dnakit/io/__init__.py +112 -0
  92. dnakit/io/_advanced_common.py +105 -0
  93. dnakit/io/_formats.py +994 -0
  94. dnakit/io/annotations.py +1179 -0
  95. dnakit/io/api.py +759 -0
  96. dnakit/io/config.py +315 -0
  97. dnakit/io/genbank.py +523 -0
  98. dnakit/io/indexed.py +1146 -0
  99. dnakit/io/metadata.py +452 -0
  100. dnakit/io/parquet.py +56 -0
  101. dnakit/io/results.py +131 -0
  102. dnakit/io/source.py +138 -0
  103. dnakit/io/tables.py +1237 -0
  104. dnakit/molbio/__init__.py +101 -0
  105. dnakit/molbio/_shared.py +266 -0
  106. dnakit/molbio/assembly.py +279 -0
  107. dnakit/molbio/crispr.py +181 -0
  108. dnakit/molbio/optimization.py +897 -0
  109. dnakit/molbio/primer3_design.py +667 -0
  110. dnakit/molbio/primers.py +899 -0
  111. dnakit/molbio/restriction.py +573 -0
  112. dnakit/molbio/results.py +583 -0
  113. dnakit/ops/__init__.py +588 -0
  114. dnakit/ops/_common.py +201 -0
  115. dnakit/ops/circular.py +193 -0
  116. dnakit/ops/concat.py +204 -0
  117. dnakit/ops/direction.py +27 -0
  118. dnakit/ops/edit.py +443 -0
  119. dnakit/ops/evolution.py +991 -0
  120. dnakit/ops/mutation.py +151 -0
  121. dnakit/ops/records.py +1012 -0
  122. dnakit/ops/translation.py +257 -0
  123. dnakit/patterns/__init__.py +81 -0
  124. dnakit/patterns/_shared.py +282 -0
  125. dnakit/patterns/coding.py +360 -0
  126. dnakit/patterns/crispr.py +337 -0
  127. dnakit/patterns/motif.py +721 -0
  128. dnakit/patterns/regions.py +341 -0
  129. dnakit/patterns/repeats.py +480 -0
  130. dnakit/patterns/restriction.py +281 -0
  131. dnakit/patterns/results.py +411 -0
  132. dnakit/py.typed +1 -0
  133. dnakit/references/__init__.py +27 -0
  134. dnakit/references/models.py +155 -0
  135. dnakit/references/ncbi.py +785 -0
  136. dnakit/representations/__init__.py +29 -0
  137. dnakit/representations/backends.py +928 -0
  138. dnakit/representations/checkpoints.py +356 -0
  139. dnakit/representations/extraction.py +220 -0
  140. dnakit/representations/models.py +395 -0
  141. dnakit/representations/results.py +79 -0
  142. dnakit/search/__init__.py +132 -0
  143. dnakit/search/_http.py +271 -0
  144. dnakit/search/_shared.py +108 -0
  145. dnakit/search/blast.py +516 -0
  146. dnakit/search/ena.py +245 -0
  147. dnakit/search/encode.py +102 -0
  148. dnakit/search/ensembl.py +874 -0
  149. dnakit/search/models.py +159 -0
  150. dnakit/search/ncbi.py +756 -0
  151. dnakit/search/ucsc.py +388 -0
  152. dnakit/secondary_structure/__init__.py +38 -0
  153. dnakit/secondary_structure/dotbracket.py +410 -0
  154. dnakit/secondary_structure/nupack.py +589 -0
  155. dnakit/secondary_structure/results.py +142 -0
  156. dnakit/similarity/__init__.py +125 -0
  157. dnakit/similarity/_shared.py +152 -0
  158. dnakit/similarity/approximate.py +258 -0
  159. dnakit/similarity/compare.py +162 -0
  160. dnakit/similarity/dashing.py +936 -0
  161. dnakit/similarity/distance.py +279 -0
  162. dnakit/similarity/index.py +365 -0
  163. dnakit/similarity/matrix.py +254 -0
  164. dnakit/similarity/results.py +676 -0
  165. dnakit/similarity/search.py +286 -0
  166. dnakit/similarity/sketch.py +96 -0
  167. dnakit/similarity/vector.py +662 -0
  168. dnakit/standardize/__init__.py +77 -0
  169. dnakit/standardize/_shared.py +100 -0
  170. dnakit/standardize/config.py +411 -0
  171. dnakit/standardize/gaps.py +1060 -0
  172. dnakit/standardize/normalize.py +565 -0
  173. dnakit/standardize/results.py +247 -0
  174. dnakit/standardize/validate.py +487 -0
  175. dnakit/structure3d/__init__.py +47 -0
  176. dnakit/structure3d/_shared.py +47 -0
  177. dnakit/structure3d/geometry.py +773 -0
  178. dnakit/structure3d/pdb.py +253 -0
  179. dnakit/structure3d/results.py +233 -0
  180. dnakit/structure3d/threedna.py +174 -0
  181. dnakit/thermodynamics/__init__.py +112 -0
  182. dnakit/thermodynamics/_shared.py +92 -0
  183. dnakit/thermodynamics/backends.py +653 -0
  184. dnakit/thermodynamics/calculations.py +837 -0
  185. dnakit/thermodynamics/config.py +175 -0
  186. dnakit/thermodynamics/equilibrium.py +340 -0
  187. dnakit/thermodynamics/optics.py +385 -0
  188. dnakit/thermodynamics/parameters.py +79 -0
  189. dnakit/thermodynamics/results.py +445 -0
  190. dnakit/visualization/__init__.py +43 -0
  191. dnakit/visualization/_svg.py +185 -0
  192. dnakit/visualization/advanced.py +314 -0
  193. dnakit/visualization/config.py +255 -0
  194. dnakit/visualization/export.py +349 -0
  195. dnakit/visualization/matrix.py +279 -0
  196. dnakit/visualization/report.py +431 -0
  197. dnakit/visualization/results.py +236 -0
  198. dnakit/visualization/sequence.py +459 -0
  199. dnakit/workflows/__init__.py +45 -0
  200. dnakit/workflows/manifest.py +365 -0
  201. dnakit/workflows/runner.py +1548 -0
  202. dnakit/workflows/schema.py +898 -0
  203. dnakit-0.1.0.dev0.dist-info/METADATA +126 -0
  204. dnakit-0.1.0.dev0.dist-info/RECORD +210 -0
  205. dnakit-0.1.0.dev0.dist-info/WHEEL +5 -0
  206. dnakit-0.1.0.dev0.dist-info/entry_points.txt +2 -0
  207. dnakit-0.1.0.dev0.dist-info/licenses/DISCLAIMER.md +26 -0
  208. dnakit-0.1.0.dev0.dist-info/licenses/LICENSE +21 -0
  209. dnakit-0.1.0.dev0.dist-info/licenses/THIRD_PARTY_NOTICES.md +73 -0
  210. 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
+ ]
@@ -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"]