Source code for mkt.databases.app.structures

"""Structure visualization backing the Streamlit app.

Provides :class:`StructureVisualizer`, which builds the interactive structure views
rendered in the Streamlit app.
"""

import logging
from typing import TYPE_CHECKING, Any

from Bio.PDB.Structure import Structure
from mkt.databases.colors import map_aa_to_single_letter_code
from mkt.databases.utils import convert_mmcifdict2structure, convert_structure2string

if TYPE_CHECKING:
    from mkt.databases.app.schema import StructureConfig

logger = logging.getLogger(__name__)


[docs] class StructureVisualizer: """Load and process kinase structures for visualization. This class handles structure loading from KinaseInfo CIF data and provides highlight data for visualization. Style/color logic is delegated to StructureConfig objects. Parameters ---------- config : StructureConfig Configuration object containing seq_align (with kinase info) and pre-computed list_idx, list_color, list_style for highlighting. Attributes ---------- config : StructureConfig The configuration object. obj_kinase : KinaseInfo KinaseInfo object from config.seq_align.obj_kinase. structure : Structure Bio.PDB Structure object loaded from CIF. pdb_text : str PDB-formatted string of the structure. residues : list List of residues from the structure. """
[docs] def __init__(self, config: "StructureConfig"): self.config = config self.obj_kinase = config.seq_align.obj_kinase self.structure = self._convert_mmcifdict2structure() self.pdb_text = self._convert_structure2string() self.residues = list(self.structure.get_residues())
[docs] @staticmethod def parse_pdb_line(line: str) -> dict[str, Any] | None: """Parse a line from a PDB file and extract relevant information. Parameters ---------- line : str Line from a PDB file. Returns ------- dict[str, Any] | None Dictionary containing extracted information or None if the line does not match the criteria (ATOM line with CA atom). """ match = line.startswith("ATOM") and (line[13:15] == "CA") if match: list_line = [i for i in line.split(" ") if i != ""] dict_out = { "res_no": list_line[5], "res_name": map_aa_to_single_letter_code(list_line[3]), "coords": ( float(list_line[6]), float(list_line[7]), float(list_line[8]), ), } return dict_out else: return None
[docs] def _convert_mmcifdict2structure(self) -> Structure: """Convert this kinase's MMCIF2Dict to a Bio.PDB Structure. Returns ------- Structure Bio.PDB Structure object. """ return convert_mmcifdict2structure( self.obj_kinase.kincore.cif.cif, structure_id=self.obj_kinase.hgnc_name, )
[docs] def _convert_structure2string(self) -> str: """Convert this Bio.PDB Structure object to a PDB format string. Returns ------- str Structure in PDB string format. """ return convert_structure2string(self.structure)
[docs] def get_highlight_data( self, ) -> tuple[list[int], dict[int, str], dict[int, str], dict[int, str | None]]: """Get highlight indices and color/style/label dictionaries for visualization. The config provides list_idx (1-indexed), list_color, list_style, and list_label. This method converts them to the dict format expected by consumers. Returns ------- tuple[list[int], dict[int, str], dict[int, str], dict[int, str | None]] - list_highlight: List of 1-indexed residue positions to highlight. - dict_color: Mapping from residue position to color. - dict_style: Mapping from residue position to style. - dict_label: Mapping from residue position to label (None for no label). """ list_highlight = self.config.list_idx dict_color = dict(zip(self.config.list_idx, self.config.list_color)) dict_style = dict(zip(self.config.list_idx, self.config.list_style)) dict_label = dict(zip(self.config.list_idx, self.config.list_label)) return list_highlight, dict_color, dict_style, dict_label
# Keep old method name as alias for backwards compatibility during transition
[docs] def _generate_highlight_idx( self, ) -> tuple[list[int], dict[int, str], dict[int, str], dict[int, str | None]]: """Alias for get_highlight_data() for backwards compatibility. .. deprecated:: Use get_highlight_data() instead. """ return self.get_highlight_data()