Skip to content

Commit baa9746

Browse files
Add basic LigPlot for server and fix paper DOIs (#47)
* Add basic LigPlot for server and fix paper DOIs
1 parent 72ac676 commit baa9746

6 files changed

Lines changed: 442 additions & 2 deletions

File tree

hbat/server/components/results_panel.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
)
2929
from ...visualization.chain_graph import render_chain_for_web
3030
from ...visualization import export_interactions_to_pymol
31+
from ...visualization.ligplot import LigplotGenerator
3132
from ...visualization.pymol3d import (
3233
generate_carbonyl_interaction_viewer_js,
3334
generate_halogen_bond_viewer_js,
@@ -1491,6 +1492,33 @@ def build_water_bridges_data(selected_value):
14911492
.classes("w-full mb-4")
14921493
)
14931494

1495+
# Ligplot visualization (2D structure with highlighted atoms)
1496+
ligplot_container = ui.html().classes("w-full mb-4")
1497+
1498+
def update_ligplot(ligand_res):
1499+
"""Update ligplot when ligand selection changes."""
1500+
try:
1501+
ligand_name = ligand_res.split(":")[1]
1502+
gen = LigplotGenerator(
1503+
ligand_name, self.analyzer, residue_id=ligand_res
1504+
)
1505+
html = gen.generate_svg(width=400, height=300)
1506+
ligplot_container.content = html
1507+
except Exception as e:
1508+
ligplot_container.content = (
1509+
f"<p>Could not generate ligplot: {str(e)}</p>"
1510+
)
1511+
1512+
# Initial ligplot
1513+
if first_ligand:
1514+
update_ligplot(first_ligand)
1515+
1516+
# Update ligplot when selection changes
1517+
def on_ligand_change(selected_value):
1518+
update_ligplot(selected_value.value)
1519+
1520+
selected_ligand.on_value_change(on_ligand_change)
1521+
14941522
# Action buttons for regular interactions
14951523
def show_all_interactions_viewer(selected_ligand_res):
14961524
"""Show 3D viewer dialog with all regular interactions for selected ligand."""

hbat/server/components/upload_panel.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,18 @@ async def download_structure():
114114
url = f"https://files.rcsb.org/download/{safe_pdb_id}.cif"
115115
filename = f"{pdb_id}.cif"
116116

117+
# Validate URL scheme and domain before opening
118+
parsed = urllib.parse.urlparse(url)
119+
if parsed.scheme != "https" or not parsed.netloc.endswith("rcsb.org"):
120+
ui.notify(
121+
"Invalid URL scheme or domain",
122+
type="negative",
123+
position="top-left",
124+
)
125+
return
126+
117127
# Add timeout for security (30 seconds)
128+
# URL is validated to be https://files.rcsb.org only
118129
with urllib.request.urlopen(url, timeout=30) as response: # nosec B310
119130
content = response.read()
120131

@@ -192,7 +203,9 @@ async def download_structure():
192203
label="PDB ID",
193204
placeholder="e.g., 1BHL or pdb_00001abc",
194205
validation={
195-
"Invalid PDB ID format": lambda v: validate_pdb_id(v)[0] if v else True
206+
"Invalid PDB ID format": lambda v: (
207+
validate_pdb_id(v)[0] if v else True
208+
)
196209
},
197210
)
198211
.props("maxlength=12")

0 commit comments

Comments
 (0)