File size: 3,608 Bytes
0fcd1da
78e0383
0fcd1da
 
14da265
78e0383
 
0fcd1da
78e0383
 
 
 
 
0fcd1da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14da265
 
78e0383
 
0fcd1da
78e0383
 
14da265
 
 
 
78e0383
 
 
 
 
 
 
 
 
14da265
78e0383
14da265
78e0383
14da265
 
 
 
 
 
 
78e0383
14da265
 
 
 
 
0fcd1da
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import html
import logging
from typing import List, Tuple

import numpy as np
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Draw

logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())


def _draw_molecule_svg(smiles: str, size: Tuple[int, int]) -> str:
    mol = Chem.MolFromSmiles(smiles)
    if mol is None:
        return f"<pre class='gt4sd-smiles'>{html.escape(smiles)}</pre>"
    svg = Draw.MolsToGridImage([mol], molsPerRow=1, subImgSize=size, useSVG=True)
    return str(svg).replace("<?xml version='1.0' encoding='iso-8859-1'?>", "")


def _draw_static_grid(
    result_df: pd.DataFrame, n_cols: int, size: Tuple[int, int]
) -> str:
    columns = [column for column in result_df.columns if column != "SMILES"]
    cards = []
    for _, row in result_df.iterrows():
        smiles = str(row["SMILES"])
        details = "".join(
            f"<dt>{html.escape(str(column))}</dt>"
            f"<dd>{html.escape(str(row[column]))}</dd>"
            for column in columns
        )
        cards.append(
            "<section class='gt4sd-molecule-card'>"
            f"<div class='gt4sd-molecule-image'>{_draw_molecule_svg(smiles, size)}</div>"
            f"<dl class='gt4sd-molecule-details'>{details}</dl>"
            "</section>"
        )

    return f"""
<style>
.gt4sd-molecule-grid {{
  display: grid;
  grid-template-columns: repeat({max(1, n_cols)}, minmax(180px, 1fr));
  gap: 12px;
  width: 100%;
  max-height: 900px;
  overflow: auto;
}}
.gt4sd-molecule-card {{
  border: 1px solid #e5e7eb;
  border-radius: 6px;
  background: #fff;
  padding: 12px;
  min-width: 0;
}}
.gt4sd-molecule-image svg {{
  width: 100%;
  max-width: {size[0]}px;
  height: auto;
  display: block;
  margin: 0 auto 8px;
}}
.gt4sd-molecule-details {{
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 4px 10px;
  margin: 0;
  font: 13px/1.35 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}}
.gt4sd-molecule-details dt {{ color: #6b7280; font-weight: 600; }}
.gt4sd-molecule-details dd {{ margin: 0; min-width: 0; overflow-wrap: anywhere; }}
.gt4sd-smiles {{ white-space: pre-wrap; overflow-wrap: anywhere; }}
@media (max-width: 720px) {{
  .gt4sd-molecule-grid {{ grid-template-columns: 1fr; }}
}}
</style>
<div class='gt4sd-molecule-grid'>
{''.join(cards)}
</div>
"""


def draw_grid_predict(
    sequences: List[str], properties: np.array, property_names: List[str], domain: str
) -> str:
    """
    Uses RDKit SVGs to draw a HTML grid for the prediction

    Args:
        sequences: Sequences for which properties are predicted.
        properties: Predicted properties. Array of shape (n_samples, n_properties).
        names: List of property names
        domain: Domain of the prediction (molecules or proteins).

    Returns:
        HTML to display
    """

    if domain not in ["Molecules", "Proteins"]:
        raise ValueError(f"Unsupported domain {domain}")

    if domain == "Proteins":
        converter = lambda x: Chem.MolToSmiles(Chem.MolFromFASTA(x))
    else:
        converter = lambda x: x

    smiles = []
    for sequence in sequences:
        try:
            seq = converter(sequence)
            smiles.append(seq)
        except Exception:
            logger.warning(f"Could not draw sequence {seq}")

    result = pd.DataFrame({"SMILES": smiles})
    for i, name in enumerate(property_names):
        result[name] = properties[:, i]
    n_cols = min(3, len(result))
    size = (140, 200) if len(result) > 3 else (600, 700)
    return _draw_static_grid(result, n_cols=n_cols, size=size)