66from ovo .app .components .molstar_custom_component import molstar_custom_component , StructureVisualization
77from ovo .app .components .molstar_custom_component .dataclasses import ContigSegment
88from ovo .core .database import WorkflowTypes , Workflow
9- from ovo .core .utils .residue_selection import parse_selections
9+ from ovo .core .utils .residue_selection import parse_selections , from_segments_to_hotspots
1010from ovo .core .utils .formatting import safe_filename
1111from ovo .core .utils .pdb import get_pdb , filter_pdb_str
1212
1313from ovo import storage
1414from ovo .core .utils .pdb import add_glycan_to_pdb
1515
1616
17- def initialize_workflow (page_key : str , workflow_name : str , include_subclasses = False ) -> Workflow :
17+ def initialize_workflow (page_key : str , workflow_name : str , include_subclasses : bool = False ) -> Workflow :
1818 """Initialize a workflow in the session state based on dropdown with available workflow variants (subclasses).
1919
2020 :param page_key: Key identifying the workflow page, used as key in session_state.workflows
2121 :param workflow_name: Base name of the workflow (e.g. "rfdiffusion-end-to-end")
22-
22+ :param include_subclasses: Whether to let user choose from subclasses of the base workflow as variants
2323 """
2424 variant_names = WorkflowTypes .get_subclass_names (workflow_name ) if include_subclasses else [workflow_name ]
2525 if len (variant_names ) > 1 :
@@ -47,8 +47,14 @@ def initialize_workflow(page_key: str, workflow_name: str, include_subclasses=Fa
4747
4848def pdb_input_component (old_pdb_code : str | None ) -> tuple [str , bytes ] | None :
4949 st .write ("Enter PDB code, UniProt ID or upload your own structure file" )
50- with st .columns (2 )[0 ]:
51- new_pdb_code = st .text_input ("PDB code or UniProt ID" , value = old_pdb_code , key = "input_pdb_code" )
50+ with st .container (horizontal = True , vertical_alignment = "bottom" ):
51+ new_pdb_code = st .text_input (
52+ "Download by ID" ,
53+ placeholder = "Input PDB code or UniProt ID..." ,
54+ value = old_pdb_code ,
55+ key = "input_pdb_code" ,
56+ width = 270 ,
57+ )
5258 st .button ("Confirm" )
5359
5460 uploaded_file = st .file_uploader ("...or upload a PDB file" , type = ["pdb" , "pdb1" ], key = "input_pdb_file" )
@@ -72,9 +78,21 @@ def sequence_selection_fragment(
7278 input_name : str ,
7379 color = "chain-id" ,
7480 representation_type = "cartoon+ball-and-stick" ,
81+ write_segments : bool = True ,
7582 fixed_segments : list [ContigSegment ] | None = None ,
7683 ** selection_kwargs ,
7784):
85+ """Fragment to display input structure and allow residue selection via molstar component.
86+
87+ :param page_key: Key identifying the workflow page, typically __file__
88+ :param input_name: Name of the input structure, used for download filename
89+ :param color: Initial color scheme for molstar component
90+ :param representation_type: Initial representation type for molstar component
91+ :param write_segments: Whether to write selected segments (True) or individual residues (False)
92+ :param fixed_segments: If provided, only these segments will be shown in the structure (others hidden)
93+ :param selection_kwargs: Additional kwargs passed to workflow.get/set_selected_segments methods
94+ """
95+
7896 workflow = st .session_state .workflows [page_key ]
7997
8098 if page_key not in st .session_state .selection_history :
@@ -85,25 +103,11 @@ def sequence_selection_fragment(
85103
86104 selection_history = st .session_state .selection_history [page_key ][input_name ]
87105
88- st .write (
89- "Click residues in sequence or structure to add them to the selection. Shift+Click to select a residue range."
90- )
91-
92- force_reload = False
93- if (
94- st .button (":material/undo: Undo selection" , disabled = len (selection_history ) <= 1 )
95- and len (selection_history ) >= 2
96- ):
97- # Go one selection backwards
98- selection_history .pop (- 1 )
99- previous_value = selection_history .pop (- 1 )
100- st .write (f"Setting selection back to: { ', ' .join (previous_value )} " )
101- workflow .set_selected_segments (previous_value , ** selection_kwargs )
102- force_reload = True
103-
104106 pdb_input_string = storage .read_file_str (workflow .get_input_pdb_path ())
105107
106- if fixed_segments and not st .checkbox ("Show full input structure" ):
108+ if fixed_segments and not st .toggle (
109+ "Show full input structure" , help = "By default, only the fixed input segments are shown."
110+ ):
107111 pdb_input_string = filter_pdb_str (
108112 pdb_input_string ,
109113 segments = [seg .value for seg in fixed_segments ],
@@ -115,8 +119,9 @@ def sequence_selection_fragment(
115119 if st .toggle (
116120 "Show glycosylation sites" ,
117121 key = "show_glycosylation_sites_input" ,
118- help = "The positions of glycans are strictly based on the 5eli NAG template; "
119- "nonexistent links might be displayed." ,
122+ help = "Visualize glycosylation sites on the structure based on glycosylation motifs in the sequence (NXS, NXT, X = any amino acid except P). "
123+ "The positions of glycans are approximate, based on the 5eli NAG template, "
124+ "nonexistent bonds might be displayed." ,
120125 ):
121126 pdb_input_string , glycosylated_residues = add_glycan_to_pdb (pdb_input_string )
122127 if glycosylated_residues :
@@ -153,6 +158,23 @@ def sequence_selection_fragment(
153158 label_visibility = "collapsed" ,
154159 )
155160
161+ with st .container (horizontal = True , vertical_alignment = "center" ):
162+ force_reload = False
163+ if (
164+ st .button (":material/undo: Undo selection" , disabled = len (selection_history ) <= 1 )
165+ and len (selection_history ) >= 2
166+ ):
167+ # Go one selection backwards
168+ selection_history .pop (- 1 )
169+ previous_value = selection_history .pop (- 1 )
170+ workflow .set_selected_segments (previous_value , ** selection_kwargs )
171+ force_reload = True
172+
173+ selection_container = st .empty ()
174+ selection_container .write (
175+ "Click residues in sequence or structure to add them to the selection. Shift+Click to select a residue range."
176+ )
177+
156178 selected_str = molstar_custom_component (
157179 structures = [
158180 StructureVisualization (
@@ -186,4 +208,9 @@ def sequence_selection_fragment(
186208 if not force_reload and selection and (not selection_history or selection != selection_history [- 1 ]):
187209 selection_history .append (selection )
188210
189- st .write (f"Selected segments: { ', ' .join (selection )} " )
211+ if selection :
212+ if write_segments :
213+ selection_container .write (f"Selected segments: { '/' .join (selection )} " )
214+ else :
215+ residues = from_segments_to_hotspots (selection )
216+ selection_container .write (f"Selected residues: { residues } " )
0 commit comments