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
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""Structured result objects returned by standardization and validation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Literal, cast
|
|
7
|
+
|
|
8
|
+
from dnakit.core._json import to_json_compatible
|
|
9
|
+
from dnakit.core.gap import Gap
|
|
10
|
+
from dnakit.core.issues import Issue
|
|
11
|
+
from dnakit.core.provenance import Provenance
|
|
12
|
+
from dnakit.core.sequence import DNASequence
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from .config import DatasetValidationConfig, NormalizationConfig, ValidationConfig
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True, slots=True)
|
|
19
|
+
class InputPosition:
|
|
20
|
+
"""A zero-based character position in the original, possibly multipart, input."""
|
|
21
|
+
|
|
22
|
+
part_index: int
|
|
23
|
+
offset: int
|
|
24
|
+
absolute_offset: int
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True, slots=True)
|
|
28
|
+
class NormalizationChange:
|
|
29
|
+
"""One auditable edit tied to an original input coordinate."""
|
|
30
|
+
|
|
31
|
+
operation: str
|
|
32
|
+
position: InputPosition
|
|
33
|
+
before: str
|
|
34
|
+
after: str
|
|
35
|
+
normalized_offset: int
|
|
36
|
+
reason: str
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True, slots=True)
|
|
40
|
+
class NormalizationStep:
|
|
41
|
+
"""Summary of one deterministic step in the normalization pipeline."""
|
|
42
|
+
|
|
43
|
+
name: str
|
|
44
|
+
status: Literal["applied", "no-op", "failed"]
|
|
45
|
+
change_count: int
|
|
46
|
+
parameters: tuple[tuple[str, str], ...] = ()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True, slots=True)
|
|
50
|
+
class RawInputSnapshot:
|
|
51
|
+
"""Full in-memory input snapshot plus a persistence-safe digest."""
|
|
52
|
+
|
|
53
|
+
input_type: Literal["str", "bytes", "parts", "DNASequence"]
|
|
54
|
+
content: str | bytes | tuple[str | Gap, ...] | DNASequence
|
|
55
|
+
sha256: str
|
|
56
|
+
character_count: int
|
|
57
|
+
|
|
58
|
+
def to_dict(self, *, include_content: bool = False) -> dict[str, Any]:
|
|
59
|
+
"""Return metadata and include sensitive raw content only when requested."""
|
|
60
|
+
result: dict[str, Any] = {
|
|
61
|
+
"input_type": self.input_type,
|
|
62
|
+
"sha256": self.sha256,
|
|
63
|
+
"character_count": self.character_count,
|
|
64
|
+
}
|
|
65
|
+
if include_content:
|
|
66
|
+
result["content"] = _raw_content_to_json(self.content)
|
|
67
|
+
return result
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass(frozen=True, slots=True)
|
|
71
|
+
class SymbolOccurrence:
|
|
72
|
+
"""A symbol occurrence in normalized sequence coordinates."""
|
|
73
|
+
|
|
74
|
+
symbol: str
|
|
75
|
+
symbol_offset: int
|
|
76
|
+
part_index: int
|
|
77
|
+
part_offset: int
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass(frozen=True, slots=True)
|
|
81
|
+
class SymbolCount:
|
|
82
|
+
"""Count and positions for one symbol."""
|
|
83
|
+
|
|
84
|
+
symbol: str
|
|
85
|
+
count: int
|
|
86
|
+
positions: tuple[int, ...]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass(frozen=True, slots=True)
|
|
90
|
+
class ProbabilityResolution:
|
|
91
|
+
"""Per-position base probabilities for an IUPAC ambiguity symbol."""
|
|
92
|
+
|
|
93
|
+
symbol: str
|
|
94
|
+
symbol_offset: int
|
|
95
|
+
probabilities: tuple[tuple[str, float], ...]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass(frozen=True, slots=True)
|
|
99
|
+
class AmbiguityReport:
|
|
100
|
+
"""Counts, fraction, positions and optional probability resolutions."""
|
|
101
|
+
|
|
102
|
+
total_count: int
|
|
103
|
+
denominator: int | None
|
|
104
|
+
fraction: float | None
|
|
105
|
+
by_symbol: tuple[SymbolCount, ...]
|
|
106
|
+
occurrences: tuple[SymbolOccurrence, ...]
|
|
107
|
+
probability_resolutions: tuple[ProbabilityResolution, ...] = ()
|
|
108
|
+
|
|
109
|
+
def count(self, symbol: str) -> int:
|
|
110
|
+
"""Return the count for ``symbol`` without exposing mutable mappings."""
|
|
111
|
+
upper = symbol.upper()
|
|
112
|
+
return next((item.count for item in self.by_symbol if item.symbol == upper), 0)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass(frozen=True, slots=True)
|
|
116
|
+
class InvalidSymbol:
|
|
117
|
+
"""An invalid Unicode code point and every original occurrence."""
|
|
118
|
+
|
|
119
|
+
symbol: str
|
|
120
|
+
codepoint: str
|
|
121
|
+
positions: tuple[InputPosition, ...]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass(frozen=True, slots=True)
|
|
125
|
+
class NormalizationResult:
|
|
126
|
+
"""Complete, non-mutating normalization output and audit trail."""
|
|
127
|
+
|
|
128
|
+
raw_input: RawInputSnapshot
|
|
129
|
+
sequence: DNASequence | None
|
|
130
|
+
config: NormalizationConfig
|
|
131
|
+
algorithm_version: str
|
|
132
|
+
provenance: Provenance
|
|
133
|
+
normalized_parts: tuple[str | Gap, ...]
|
|
134
|
+
steps: tuple[NormalizationStep, ...]
|
|
135
|
+
changes: tuple[NormalizationChange, ...]
|
|
136
|
+
issues: tuple[Issue, ...]
|
|
137
|
+
ambiguity: AmbiguityReport
|
|
138
|
+
invalid_symbols: tuple[InvalidSymbol, ...]
|
|
139
|
+
u_positions: tuple[InputPosition, ...]
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def is_valid(self) -> bool:
|
|
143
|
+
"""Whether a valid :class:`DNASequence` was produced."""
|
|
144
|
+
return self.sequence is not None and not _has_error(self.issues)
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def was_modified(self) -> bool:
|
|
148
|
+
"""Whether at least one input character was edited or removed."""
|
|
149
|
+
return bool(self.changes)
|
|
150
|
+
|
|
151
|
+
def to_dict(self, *, include_raw_content: bool = False) -> dict[str, Any]:
|
|
152
|
+
"""Serialize safely; raw sequence content is opt-in."""
|
|
153
|
+
result = cast(dict[str, Any], to_json_compatible(self))
|
|
154
|
+
result["raw_input"] = self.raw_input.to_dict(include_content=include_raw_content)
|
|
155
|
+
return result
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@dataclass(frozen=True, slots=True)
|
|
159
|
+
class QualitySummary:
|
|
160
|
+
"""Basic numeric summary for a PHRED quality annotation."""
|
|
161
|
+
|
|
162
|
+
count: int
|
|
163
|
+
minimum: float | None
|
|
164
|
+
maximum: float | None
|
|
165
|
+
mean: float | None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@dataclass(frozen=True, slots=True)
|
|
169
|
+
class ValidationReport:
|
|
170
|
+
"""Validation and QC findings for one sequence or record."""
|
|
171
|
+
|
|
172
|
+
record_id: str | None
|
|
173
|
+
sequence: DNASequence
|
|
174
|
+
config: ValidationConfig
|
|
175
|
+
algorithm_version: str
|
|
176
|
+
provenance: Provenance
|
|
177
|
+
is_valid: bool
|
|
178
|
+
symbol_length: int
|
|
179
|
+
coordinate_span: int | None
|
|
180
|
+
ambiguity: AmbiguityReport
|
|
181
|
+
quality: QualitySummary | None
|
|
182
|
+
issues: tuple[Issue, ...]
|
|
183
|
+
|
|
184
|
+
def to_dict(self) -> dict[str, Any]:
|
|
185
|
+
"""Return a JSON-compatible representation."""
|
|
186
|
+
return cast(dict[str, Any], to_json_compatible(self))
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@dataclass(frozen=True, slots=True)
|
|
190
|
+
class DuplicateID:
|
|
191
|
+
"""A repeated record identifier and its stable input indices."""
|
|
192
|
+
|
|
193
|
+
id: str
|
|
194
|
+
indices: tuple[int, ...]
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@dataclass(frozen=True, slots=True)
|
|
198
|
+
class DatasetValidationReport:
|
|
199
|
+
"""Collection-level validation outcome."""
|
|
200
|
+
|
|
201
|
+
record_count: int
|
|
202
|
+
config: DatasetValidationConfig
|
|
203
|
+
algorithm_version: str
|
|
204
|
+
provenance: Provenance
|
|
205
|
+
ids_unique: bool
|
|
206
|
+
duplicate_ids: tuple[DuplicateID, ...]
|
|
207
|
+
is_valid: bool
|
|
208
|
+
issues: tuple[Issue, ...]
|
|
209
|
+
record_reports: tuple[ValidationReport, ...] | None
|
|
210
|
+
|
|
211
|
+
def to_dict(self) -> dict[str, Any]:
|
|
212
|
+
"""Return a JSON-compatible representation."""
|
|
213
|
+
return cast(dict[str, Any], to_json_compatible(self))
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _has_error(issues: tuple[Issue, ...]) -> bool:
|
|
217
|
+
return any(getattr(issue.severity, "value", issue.severity) == "error" for issue in issues)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _raw_content_to_json(content: object) -> object:
|
|
221
|
+
if isinstance(content, bytes):
|
|
222
|
+
return {"encoding": "hex", "data": content.hex()}
|
|
223
|
+
if isinstance(content, DNASequence):
|
|
224
|
+
return _parts_to_json(content.parts)
|
|
225
|
+
if isinstance(content, tuple):
|
|
226
|
+
return _parts_to_json(content)
|
|
227
|
+
return content
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _parts_to_json(parts: tuple[str | Gap, ...]) -> list[object]:
|
|
231
|
+
result: list[object] = []
|
|
232
|
+
for part in parts:
|
|
233
|
+
if isinstance(part, str):
|
|
234
|
+
result.append(part)
|
|
235
|
+
else:
|
|
236
|
+
result.append(
|
|
237
|
+
{
|
|
238
|
+
"gap": {
|
|
239
|
+
"length": part.length,
|
|
240
|
+
"kind": getattr(part.kind, "value", part.kind),
|
|
241
|
+
"crossable": part.crossable,
|
|
242
|
+
"evidence": list(part.evidence),
|
|
243
|
+
"metadata": to_json_compatible(part.metadata),
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
)
|
|
247
|
+
return result
|