|
| 1 | +"""Standalone launcher page to generate GUI links.""" |
| 2 | + |
| 3 | +import urllib.parse |
| 4 | + |
| 5 | +import panel as pn |
| 6 | + |
| 7 | +from aind_ephys_portal.panel.logging import setup_logging |
| 8 | +from aind_ephys_portal.panel.utils import EPHYSGUI_LINK_PREFIX |
| 9 | + |
| 10 | +pn.extension() |
| 11 | + |
| 12 | + |
| 13 | +class EphysLauncher: |
| 14 | + def __init__(self): |
| 15 | + setup_logging() |
| 16 | + |
| 17 | + self.analyzer_input = pn.widgets.TextInput( |
| 18 | + name="Analyzer path", |
| 19 | + value="", |
| 20 | + height=50, |
| 21 | + sizing_mode="stretch_width", |
| 22 | + ) |
| 23 | + self.recording_input = pn.widgets.TextInput( |
| 24 | + name="Recording path (optional)", |
| 25 | + value="", |
| 26 | + height=50, |
| 27 | + sizing_mode="stretch_width", |
| 28 | + ) |
| 29 | + |
| 30 | + self.generate_button = pn.widgets.Button( |
| 31 | + name="Generate Link", |
| 32 | + button_type="primary", |
| 33 | + height=45, |
| 34 | + sizing_mode="stretch_width", |
| 35 | + ) |
| 36 | + |
| 37 | + self.fast_mode_checkbox = pn.widgets.Checkbox( |
| 38 | + name="Enable Fast Mode (skip waveforms and PCs)", |
| 39 | + value=False, |
| 40 | + height=30, |
| 41 | + sizing_mode="stretch_width", |
| 42 | + ) |
| 43 | + |
| 44 | + self.link_pane = pn.pane.HTML("No link generated yet.", sizing_mode="stretch_width") |
| 45 | + self.url_output = pn.widgets.TextInput( |
| 46 | + name="GUI URL", |
| 47 | + value="", |
| 48 | + disabled=True, |
| 49 | + sizing_mode="stretch_width", |
| 50 | + ) |
| 51 | + |
| 52 | + self.generate_button.on_click(self._update_link) |
| 53 | + |
| 54 | + self.layout = pn.Column( |
| 55 | + pn.pane.Markdown("## AIND Ephys Launcher"), |
| 56 | + self.analyzer_input, |
| 57 | + self.recording_input, |
| 58 | + pn.Row(self.generate_button, self.fast_mode_checkbox, sizing_mode="stretch_width"), |
| 59 | + self.link_pane, |
| 60 | + self.url_output, |
| 61 | + sizing_mode="stretch_width", |
| 62 | + ) |
| 63 | + |
| 64 | + def _build_gui_url(self): |
| 65 | + analyzer_path = self.analyzer_input.value.strip() |
| 66 | + recording_path = self.recording_input.value.strip() |
| 67 | + if not analyzer_path: |
| 68 | + return None |
| 69 | + |
| 70 | + analyzer_path_q = urllib.parse.quote(analyzer_path, safe="") |
| 71 | + recording_path_q = urllib.parse.quote(recording_path, safe="") if recording_path else "" |
| 72 | + path = EPHYSGUI_LINK_PREFIX.format(analyzer_path_q, recording_path_q) |
| 73 | + if self.fast_mode_checkbox.value: |
| 74 | + path += "&fast_mode=true" |
| 75 | + |
| 76 | + location = pn.state.location |
| 77 | + if location is not None: |
| 78 | + href = getattr(location, "href", None) |
| 79 | + if href: |
| 80 | + parsed = urllib.parse.urlparse(href) |
| 81 | + if parsed.scheme and parsed.netloc: |
| 82 | + return f"{parsed.scheme}://{parsed.netloc}{path}" |
| 83 | + |
| 84 | + protocol = getattr(location, "protocol", "") |
| 85 | + hostname = getattr(location, "hostname", "") |
| 86 | + port = getattr(location, "port", "") |
| 87 | + if protocol and hostname: |
| 88 | + netloc = f"{hostname}:{port}" if port else hostname |
| 89 | + return f"{protocol}//{netloc}{path}" |
| 90 | + |
| 91 | + return path |
| 92 | + |
| 93 | + def _update_link(self, event=None): |
| 94 | + url = self._build_gui_url() |
| 95 | + if not url: |
| 96 | + self.link_pane.object = "<span style='color: orange;'>⚠️ Analyzer path is required.</span>" |
| 97 | + self.url_output.value = "" |
| 98 | + return |
| 99 | + print(f"Generated URL: {url}") |
| 100 | + |
| 101 | + self.link_pane.object = f'<a href="{url}" target="_blank">Ephys Curation GUI</a>' |
| 102 | + self.url_output.value = url |
| 103 | + |
| 104 | + |
| 105 | +app = EphysLauncher() |
| 106 | +app.layout.servable(title="AIND Ephys Launcher") |
0 commit comments