-
Notifications
You must be signed in to change notification settings - Fork 203
Implement Vizro dashboard generation POC #2082
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 1 commit
79fe29e
3b55c1d
49d39c6
d567a5d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,56 +1,81 @@ | ||
| # Copyright (c) Saga Inc. | ||
| # Distributed under the terms of the GNU Affero General Public License v3.0 License. | ||
|
|
||
| streamlit_system_prompt = """You are a code conversion specialist who converts Jupyter notebooks into Streamlit applications with ABSOLUTE FIDELITY. | ||
| streamlit_system_prompt = """You are a code conversion specialist who converts Jupyter notebooks into Vizro dashboard applications with ABSOLUTE FIDELITY. | ||
|
|
||
| ROLE AND EXPERTISE: | ||
| - Expert in Python, Jupyter notebooks, Streamlit, and data visualization | ||
| - Expert in Python, Jupyter notebooks, Vizro, and data visualization | ||
| - Experienced in creating executive-ready dashboards for business stakeholders | ||
| - Skilled in translating technical analysis into clear, interactive presentations | ||
|
|
||
| TASK REQUIREMENTS: | ||
| 1. Convert Jupyter notebook content into a complete Streamlit application (app.py) | ||
| 1. Convert Jupyter notebook content into a complete Vizro dashboard application (app.py) | ||
| 2. Preserve ALL outputs from code cells and markdown cells as they appear in the notebook | ||
| 3. Maintain the logical flow and structure of the original analysis | ||
| 4. Create an executive-friendly dashboard suitable for company leadership | ||
|
|
||
| STREAMLIT IMPLEMENTATION GUIDELINES: | ||
| - Use appropriate Streamlit components (st.title, st.header, st.subheader, st.markdown, etc.) | ||
| - Display all visualizations using st.pyplot(), st.plotly_chart(), or st.altair_chart() as appropriate | ||
| - Do not convert database connections into Streamlit's secret.toml format. If the user inlined their database credentials, are importing from an environment variable, or reading from a connections file, assume that same approach will work in the streamlit app. | ||
| - Show dataframes and tables using st.dataframe() or st.table() | ||
| - Include all text explanations and insights from markdown cells | ||
| - Add interactive elements where beneficial (filters, selectors, etc.) | ||
| VIZRO IMPLEMENTATION GUIDELINES: | ||
| - Use Vizro's declarative structure: vm.Page, vm.Dashboard, vm.Graph, vm.Table, vm.Card, etc. | ||
| - Import vizro.plotly.express as px for charts (scatter, histogram, line, bar, etc.) | ||
| - Display visualizations using vm.Graph(figure=px.chart_type(...)) | ||
| - Show dataframes and tables using vm.Table(figure=df) | ||
| - Include text content using vm.Card(text="...") | ||
| - Add interactive elements using vm.Filter() and vm.Parameter() where beneficial | ||
| - Do not convert database connections. If the user inlined their database credentials, are importing from an environment variable, or reading from a connections file, assume that same approach will work in the vizro app. | ||
| - Ensure professional styling and layout suitable for executives | ||
| - Just create the streamlit app code, do not include a _main_ function block. The file will be run directly using `streamlit run app.py`. | ||
| - Structure the app as: load data → create page(s) with components → build dashboard → run | ||
|
|
||
| VIZRO APP STRUCTURE: | ||
| ```python | ||
| import vizro.plotly.express as px | ||
| from vizro import Vizro | ||
| import vizro.models as vm | ||
|
|
||
| # Data loading and processing code here | ||
|
|
||
| # Create page with components | ||
| page = vm.Page( | ||
| title="Dashboard Title", | ||
| components=[ | ||
| vm.Graph(figure=px.scatter(...)), | ||
| vm.Table(figure=df), | ||
| vm.Card(text="Insights and explanations") | ||
| ], | ||
| controls=[ | ||
| vm.Filter(column="category"), | ||
| ], | ||
| ) | ||
|
|
||
| # Build and run dashboard | ||
| dashboard = vm.Dashboard(pages=[page]) | ||
| Vizro().build(dashboard).run() | ||
| ``` | ||
|
|
||
| CRITICAL REQUIREMENTS: | ||
| 1. **PRESERVE ALL CODE EXACTLY**: Every line of code, every data structure, every import must be included in full | ||
| 2. **NO PLACEHOLDERS**: Never use comments like "# Add more data here" or "# Fill in the rest" | ||
| 3. **NO SIMPLIFICATION**: Do not replace actual data with sample data or hardcoded examples | ||
| 4. **COMPLETE DATA STRUCTURES**: If a notebook has a 1000-line dictionary, include all 1000 lines | ||
| 5. **PRESERVE DATA LOADING**: If the notebook reads from files, the Streamlit app must read from the same files | ||
| 6. **NO IMPROVIZAITION**: Do not provide your own interpretations of the analysis. Just convert the existing analysis into a streamlit app. | ||
| 5. **PRESERVE DATA LOADING**: If the notebook reads from files, the Vizro app must read from the same files | ||
| 6. **NO IMPROVIZAITION**: Do not provide your own interpretations of the analysis. Just convert the existing analysis into a vizro dashboard. | ||
|
|
||
| COMPONENT MAPPING: | ||
| - Code cells that generate plots → vm.Graph(figure=px.chart_type(...)) | ||
| - DataFrames → vm.Table(figure=df) | ||
| - Markdown cells with text → vm.Card(text="...") | ||
| - Variables that users might want to filter → vm.Filter(column="...") | ||
| - Maintain the order of outputs as they appear in the notebook | ||
|
|
||
| STYLE GUIDELINES: | ||
| STYLE GUIDELINES: | ||
| - Create a professional, executive-friendly dashboard | ||
| - If there are variables in the notebook that the streamlit app viewer would likely want to configure, then use the appropriate streamlit component to allow them to do so. For examples, if the notebook has a variable called "start_date" and "end_date", then use the st.date_input component to allow the user to select the start and end dates. | ||
| - If there are variables in the notebook that viewers would likely want to configure, use vm.Filter or vm.Parameter | ||
| - Do not use emojis unless they are in the notebook already | ||
| - Do not modify the graphs or analysis. If the notebook has a graph, use the same graph in the streamlit app. | ||
| - Always include the following code at the top of the file so the user does not use the wrong deploy button | ||
| ```python | ||
| st.markdown(\"\"\" | ||
| <style> | ||
| #MainMenu {visibility: hidden;} | ||
| .stAppDeployButton {display:none;} | ||
| footer {visibility: hidden;} | ||
| .stMainBlockContainer {padding: 2rem 1rem 2rem 1rem;} | ||
| </style> | ||
| \"\"\", unsafe_allow_html=True) | ||
| ``` | ||
| - Do not modify the graphs or analysis. If the notebook has a graph, use the same graph in the vizro app | ||
| - Always end with: Vizro().build(dashboard).run() | ||
|
|
||
| OUTPUT FORMAT: | ||
| - Output the complete, runnable app.py file. | ||
| - Do not output any extra text, just give the python code. | ||
| - Output the complete, runnable app.py file | ||
| - Do not output any extra text, just give the python code | ||
| - The file will be run directly using Python (not streamlit run) | ||
|
|
||
| """ | ||
| """ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,36 +38,47 @@ def get_free_port(self) -> int: | |
| return port | ||
|
|
||
| def start_streamlit_preview(self, app_directory: AbsoluteNotebookDirPath, app_file_name: AppFileName, preview_id: str) -> int: | ||
| """Start a streamlit preview process. | ||
| """Start a vizro preview process. | ||
|
|
||
| Args: | ||
| app_code: The streamlit app code to run | ||
| app_code: The vizro app code to run | ||
| preview_id: Unique identifier for this preview | ||
|
|
||
| Returns: | ||
| Tuple of (success, message, port) | ||
| """ | ||
|
|
||
| try: | ||
|
|
||
| # Get free port | ||
| port = self.get_free_port() | ||
|
|
||
| # Start streamlit process | ||
|
|
||
| # For Vizro, we need to modify the app.py to use the port we allocated | ||
| # Read the app file and inject the port configuration | ||
| import os | ||
| app_path = os.path.join(app_directory, app_file_name) | ||
| with open(app_path, 'r') as f: | ||
| app_code = f.read() | ||
|
|
||
| # Replace the run() call with run(port=X) | ||
| # Handle both .run() and .run(...) | ||
| import re | ||
| if re.search(r'\.run\(\s*\)', app_code): | ||
| # Simple case: .run() with no arguments | ||
| app_code = re.sub(r'\.run\(\s*\)', f'.run(port={port})', app_code) | ||
| elif re.search(r'\.run\(', app_code): | ||
| # Has arguments - insert port as first argument | ||
| app_code = re.sub(r'\.run\(', f'.run(port={port}, ', app_code) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Regex replaces all .run() calls, not just VizroThe regex patterns |
||
|
|
||
| # Write back the modified code | ||
| with open(app_path, 'w') as f: | ||
| f.write(app_code) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Race condition when modifying app file for portThe file read/modify/write sequence for port injection is not atomic or protected by a lock. If two concurrent preview requests target the same app file, one request can read the file, a second request can overwrite it with a different port, and then the first request's subprocess reads the modified file with the wrong port. The first request will then wait on a port where nothing is listening, causing a timeout. The original Streamlit implementation passed the port via command-line argument which didn't have this issue. |
||
|
|
||
| # Start python process to run the Vizro app | ||
| cmd = [ | ||
| "streamlit", "run", app_file_name, | ||
| "--server.port", str(port), | ||
| "--server.headless", "true", | ||
| "--server.address", "localhost", | ||
| "--server.enableXsrfProtection", "false", | ||
| "--server.runOnSave", "true", # auto-reload when app is saved | ||
| "--logger.level", "error" | ||
| "python", app_file_name | ||
| ] | ||
|
|
||
| # TODO: Security considerations for production: | ||
| # - Consider enabling XSRF protection if needed, but we might already get this with the APIHandler? | ||
| # - Add authentication headers to streamlit | ||
|
|
||
|
|
||
| proc = subprocess.Popen( | ||
| cmd, | ||
| stdout=subprocess.PIPE, | ||
|
|
@@ -81,7 +92,7 @@ def start_streamlit_preview(self, app_directory: AbsoluteNotebookDirPath, app_fi | |
| if not ready: | ||
| proc.terminate() | ||
| proc.wait() | ||
| raise StreamlitPreviewError("Streamlit app failed to start as app is not ready", 500) | ||
| raise StreamlitPreviewError("Vizro app failed to start as app is not ready", 500) | ||
|
|
||
| # Register the process | ||
| with self._lock: | ||
|
|
@@ -90,11 +101,11 @@ def start_streamlit_preview(self, app_directory: AbsoluteNotebookDirPath, app_fi | |
| port=port, | ||
| ) | ||
|
|
||
| self.log.info(f"Started streamlit preview {preview_id} on port {port}") | ||
| self.log.info(f"Started vizro preview {preview_id} on port {port}") | ||
| return port | ||
|
|
||
| except Exception as e: | ||
| self.log.error(f"Error starting streamlit preview: {e}") | ||
| self.log.error(f"Error starting vizro preview: {e}") | ||
| raise StreamlitPreviewError(f"Failed to start preview: {str(e)}", 500) | ||
|
|
||
| def _wait_for_app_ready(self, port: int, timeout: int = 30) -> bool: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Missing UTF-8 encoding when reading/writing app file
The file operations at lines 61 and 82 use
open()without specifyingencoding='utf-8', but the app file was originally created with UTF-8 encoding increate_app_file(). On Windows, Python uses the system default encoding (often not UTF-8) when no encoding is specified. This can cause encoding errors or data corruption when the generated code contains non-ASCII characters like unicode symbols, accented characters, or emoji.Additional Locations (1)
mito-ai/mito_ai/streamlit_preview/manager.py#L81-L83