Source code for gain.genomic_resources.implementations.genomic_scores_impl.position

""":class:`PositionScoreImplementation` -- the position score's page.

The genomic-score page plus a Coverage section: the accessors that
section calls, and nothing else.  Covered positions are this kind's
statistic and only this kind's -- the union of the rows' spans measures
what a resource covers exactly when they are pairwise disjoint
(gain#1118, gain#1127).  The resource protocol every kind answers alike
is on :class:`~.base.GenomicScoreImplementation`.
"""
from __future__ import annotations

from typing import ClassVar

from gain.genomic_resources.genomic_scores.chrom_lengths import ChromLength
from gain.genomic_resources.resource_implementation import (
    DerivedFilesState,
)
from gain.genomic_resources.statistics.coverage import (
    COVERAGE_SEGMENT_LENGTHS_IMAGE_FILE,
    COVERAGE_STATISTICS_FILE,
    CoverageDisplay,
    CoverageStatistics,
    build_coverage_display,
    resolve_chrom_lengths,
)

from .base import GenomicScoreImplementation


[docs] class PositionScoreImplementation(GenomicScoreImplementation): """Assists in the management of a position score resource. Its page is the genomic-score page plus a Coverage section, which ``position_score.jinja`` fills as the other kinds' templates fill theirs. """ template_name: ClassVar[str] = "position_score.jinja"
[docs] @staticmethod def get_coverage_segment_lengths_image_filename() -> str: """The info page's one statement of the global histogram's path.""" return COVERAGE_SEGMENT_LENGTHS_IMAGE_FILE
[docs] def get_coverage_statistics(self) -> CoverageStatistics | None: """The resource's coverage statistics, or ``None`` if not built. Absence is an expected state, not an error: statistics roll out lazily as resources are rebuilt (``calc_statistics_hash`` does not know about this file), so a resource built before the statistic existed simply has nothing to show yet. Read on each call, like its two siblings. It was memoized while the Coverage and Fragments sections both read this file; since gain#1127 gave fragments a file of their own there is one caller, called once per render, and the memo saved nothing. """ try: content = self.resource.get_file_content( COVERAGE_STATISTICS_FILE) except FileNotFoundError: return None return CoverageStatistics.deserialize(content)
[docs] def get_coverage_display(self) -> CoverageDisplay | None: """The Coverage section's payload: raw counts plus fractions. ``None`` when the statistic is not built. This frame's whole job is the genome rung of the denominator ladder -- it needs the repository handed to the enclosing :meth:`get_info` / :meth:`get_statistics_info` call, and the cache it goes through is shared with the scan's contig splitting. Invoked outside a page build no repository is available and that rung resolves nothing, which degrades to raw counts rather than failing. """ coverage = self.get_coverage_statistics() if coverage is None: return None lengths = resolve_chrom_lengths( self.resource, self._resolve_labelled_genome(self._render_repo), self._score_chrom_lengths, coverage.covered_by_chromosome()) return build_coverage_display( self.resource.resource_id, coverage, lengths)
def _score_chrom_lengths(self) -> dict[str, ChromLength]: """The second rung's records: what the score's own file can say. Asked only once the genome rung has resolved nothing. The stored lengths answer first, as :meth:`get_chrom_lengths` reads them, for ANY backend (gain#1578): a genome answer in a tabix score's record prices the contig, its probe's bound never does, and ``is_exact`` sorts the two in the ladder. So a labelled score whose genome has left the repository since the repair is still priced, over its OWN genome-listed contigs rather than the genome's whole list: the record is the record, and the roll-up of contigs the score never touched is not in it. Otherwise the ladder runs live without a genome, and is not asked LIVE of a backend whose lengths are never exact -- finding that out would run the tabix probe per contig at every render, and ``repo-repair`` renders every page (gain#1448). A bigWig header is exact, and an unrepaired bigWig whose payload is here is the one reason a render opens a table; unpulled, it has no header to read, and the render is not where that is reported. """ check = self._check_derived_files(self._render_repo) if (stored := check.current) is not None: return stored.lengths if check.state is DerivedFilesState.PAYLOAD_ABSENT or \ not self.score.chrom_length_source.is_exact: return {} return self._derive_chrom_lengths(None)