Spaces:
Sleeping
Sleeping
File size: 5,355 Bytes
21c9bd3 | 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 124 125 126 127 128 129 130 131 132 | """
Builds the HTML visualization block shown after a prediction — the
sequence node grid, metrics cards, alignment view, and info/fun-fact card.
"""
import html
def build_result_html(sequence, pred_str, true_ss=None, q3_score=None, description="", fun_fact=""):
"""Renders the custom HTML result panel matching the app's design system."""
# 1. Interactive Node Grid (first 60 residues shown as scannable nodes)
grid_nodes_html = "<div class='sequence-grid'>"
display_limit = min(len(sequence), 60)
for i in range(display_limit):
aa = sequence[i]
st = pred_str[i]
grid_nodes_html += f"""
<div class="seq-node" data-state="{st}">
<span class="seq-aa">{aa}</span>
<span class="seq-state">{st}</span>
</div>
"""
grid_nodes_html += "</div>"
if len(sequence) > 60:
grid_nodes_html += f"""
<p style='text-align: center; color: #8e8e93; font-size: 0.78rem; font-family: "JetBrains Mono"; margin-top: 12px;'>
+ {len(sequence) - 60} additional amino acid residues sequence mapped below
</p>
"""
# 2. Structure Composition Analysis
h_cnt, e_cnt, c_cnt = pred_str.count('H'), pred_str.count('E'), pred_str.count('C')
tot = max(len(pred_str), 1)
h_pct, e_pct, c_pct = (h_cnt / tot) * 100, (e_cnt / tot) * 100, (c_cnt / tot) * 100
# 3. Alignment Rows (Monospaced Sequence Alignment)
pred_align_html = ""
truth_align_html = ""
for i in range(len(sequence)):
p_char = pred_str[i]
if true_ss and i < len(true_ss):
t_char = true_ss[i]
if p_char == t_char:
pred_align_html += f"<span class='match'>{p_char}</span>"
else:
pred_align_html += f"<span class='miss'>{p_char}</span>"
truth_align_html += t_char
else:
pred_align_html += p_char
alignment_card_html = f"""
<div class="alignment-card">
{f'''
<div class="align-row">
<div class="row-label">Ground Truth</div>
<div class="row-seq">{truth_align_html}</div>
</div>
''' if true_ss else ''}
<div class="align-row">
<div class="row-label">SERAPH Pred</div>
<div class="row-seq">{pred_align_html}</div>
</div>
<div class="align-row">
<div class="row-label">AA Sequence</div>
<div class="row-seq" style="color: #8e8e93;">{sequence}</div>
</div>
</div>
"""
# 4. Metric Header Card
if q3_score is not None:
metrics_card_html = f"""
<div class="metrics-grid">
<div class="metric-card winner">
<div class="metric-value">{q3_score:.1f}%</div>
<div class="metric-label">Q3 Accuracy Score</div>
</div>
<div class="metric-card">
<div class="metric-value">{len(sequence)}</div>
<div class="metric-label">Residue Length</div>
</div>
</div>
"""
else:
metrics_card_html = f"""
<div class="metrics-grid">
<div class="metric-card winner">
<div class="metric-value">{len(sequence)}</div>
<div class="metric-label">Residues Analyzed</div>
</div>
<div class="metric-card">
<div class="metric-value">{h_pct:.0f}% / {e_pct:.0f}% / {c_pct:.0f}%</div>
<div class="metric-label">Helix / Sheet / Coil</div>
</div>
</div>
"""
# 5. Protein Info & Fun Fact Card
info_card_html = ""
if description or fun_fact:
info_card_html = f"""
<div style="background: rgba(255,255,255,0.025); border: 1px solid rgba(255,255,255,0.08); border-radius: 16px; padding: 24px; margin-top: 20px;">
{f'<p style="color: #f5f5f7; font-size: 0.92rem; margin-bottom: 12px; line-height: 1.5;"><strong>Overview:</strong> {html.escape(description)}</p>' if description else ''}
{f'<p style="color: #38bdf8; font-size: 0.88rem; background: rgba(56, 189, 248, 0.08); border: 1px solid rgba(56, 189, 248, 0.2); padding: 12px 16px; border-radius: 10px; margin-top: 8px;"><strong>💡 Fun Fact:</strong> {html.escape(fun_fact)}</p>' if fun_fact else ''}
</div>
"""
# Full Combined Output Block
return f"""
<div style="animation: fadeIn 0.5s ease-out;">
{metrics_card_html}
<div class="sequence-board" style="margin-bottom: 24px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<span class="section-label" style="margin: 0;">Predicted Secondary Structure Mapping</span>
<div class="board-legend" style="margin: 0;">
<div class="legend-item"><div class="legend-dot h"></div> Helix (H) {h_cnt} ({h_pct:.0f}%)</div>
<div class="legend-item"><div class="legend-dot e"></div> Sheet (E) {e_cnt} ({e_pct:.0f}%)</div>
<div class="legend-item"><div class="legend-dot c"></div> Coil (C) {c_cnt} ({c_pct:.0f}%)</div>
</div>
</div>
{grid_nodes_html}
</div>
{alignment_card_html}
{info_card_html}
</div>
"""
|