Source code for codonadaptpy.models

"""
CodonAdaptPy Data Models

This module contains dependency-free dataclasses shared by parsing,
validation, analysis, optimization, reporting, and command-line workflows.
Models expose ``to_dict`` methods so results can be serialized without a
framework-specific schema library.

Classes:
    - SequenceRecord: One coding sequence and its metadata.
    - ValidationConfig: User-selected validation policy.
    - ValidationIssue: One structured validation observation.
    - ValidationReport: Validation outcome and normalized sequence.
    - AnalysisResult: Metrics and metadata for a single sequence.

:Created: July 20, 2026
:Updated: July 20, 2026
:Author: Naveen Duhan
:Version: 1.0.2
"""

from __future__ import annotations

from dataclasses import asdict, dataclass, field
from typing import Any, Literal


[docs] @dataclass(slots=True) class SequenceRecord: """Store a named nucleotide sequence and arbitrary sample metadata.""" identifier: str sequence: str description: str = "" metadata: dict[str, Any] = field(default_factory=dict)
[docs] def to_dict(self) -> dict[str, Any]: """Return a JSON-compatible representation of the record.""" return asdict(self)
[docs] @dataclass(slots=True) class ValidationConfig: """Control normalization and coding-sequence quality checks. Reading frames use zero-based offsets ``0``, ``1``, or ``2``. Ambiguous codons may be rejected, skipped by downstream analyses, or retained with warnings. Partial sequences relax start, stop, and divisible-by-three requirements while preserving all other diagnostics. """ reading_frame: Literal[0, 1, 2] = 0 genetic_code: int = 1 require_start: bool = True require_stop: bool = True ambiguous_policy: Literal["reject", "skip", "allow"] = "reject" allow_partial: bool = False convert_rna: bool = True
@dataclass(slots=True) class ValidationIssue: """Describe one validation warning or error with optional sequence locus.""" code: str message: str severity: Literal["warning", "error"] position: int | None = None
[docs] @dataclass(slots=True) class ValidationReport: """Collect normalized sequence data and structured validation findings.""" identifier: str normalized_sequence: str issues: list[ValidationIssue] = field(default_factory=list) genetic_code: int = 1 reading_frame: int = 0 @property def is_valid(self) -> bool: """Return ``True`` when the report contains no error-level issues.""" return not any(issue.severity == "error" for issue in self.issues) @property def errors(self) -> list[ValidationIssue]: """Return only error-level findings.""" return [issue for issue in self.issues if issue.severity == "error"] @property def warnings(self) -> list[ValidationIssue]: """Return only warning-level findings.""" return [issue for issue in self.issues if issue.severity == "warning"]
[docs] def to_dict(self) -> dict[str, Any]: """Return a JSON-compatible representation of the report.""" return asdict(self) | {"is_valid": self.is_valid}
@dataclass(slots=True) class AnalysisResult: """Store validation, scalar metrics, profiles, and count tables.""" identifier: str validation: ValidationReport metrics: dict[str, Any] codon_counts: dict[str, int] rscu: dict[str, float] amino_acid_composition: dict[str, float] dinucleotide_frequencies: dict[str, float] dinucleotide_odds: dict[str, float | None] dinucleotide_bias: dict[str, str] trinucleotide_frequencies: dict[str, float] codon_pair_scores: dict[str, float] = field(default_factory=dict) sliding_windows: dict[str, list[dict[str, float]]] = field(default_factory=dict) host_comparisons: dict[str, dict[str, float]] = field(default_factory=dict) metadata: dict[str, Any] = field(default_factory=dict) def to_dict(self) -> dict[str, Any]: """Return all nested result fields in JSON-compatible form.""" return asdict(self)