35 add streamlit UI#56
Conversation
…tions and remove the unused `reset_analysis` function.
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughAdds a new Streamlit-based web UI (app.py) with data ingestion, configurable multi-step analyses, report generation and ZIP download; a Streamlit theme config; README updates describing web and CLI usage; and a pyproject version bump adding Streamlit. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant StreamlitApp as Streamlit App
participant DataLoader as Data Loader
participant Analysis as Analysis Modules
participant Report as Report Generator
User->>StreamlitApp: Upload file or provide Kaggle ID
StreamlitApp->>DataLoader: Load data (local or Kaggle)
DataLoader-->>StreamlitApp: Return DataFrame
StreamlitApp->>User: Display preview
User->>StreamlitApp: Configure steps & target
User->>StreamlitApp: Execute analysis
StreamlitApp->>Analysis: Run Profile
Analysis-->>StreamlitApp: Profile results
StreamlitApp->>Analysis: Run Univariate
Analysis-->>StreamlitApp: Univariate plots
StreamlitApp->>Analysis: Run Bivariate
Analysis-->>StreamlitApp: Bivariate plots
StreamlitApp->>Analysis: Run Multivariate
Analysis-->>StreamlitApp: Multivariate outputs
StreamlitApp->>Report: Generate HTML report & ZIP
Report-->>StreamlitApp: Report path & ZIP
StreamlitApp->>User: Provide download link
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@app.py`:
- Around line 359-361: Remove the duplicate assignment of profile_data from
report_data: there are two identical lines setting profile_data =
report_data["profile"]; keep a single assignment and delete the redundant one so
only one assignment to profile_data from report_data["profile"] remains (locate
the duplicate in the block referencing variables profile_data and report_data).
- Around line 138-142: Remove or hide debug output that exposes internal paths:
eliminate the calls that display dataset_id and file_path to end users (the
st.text("Dataset ID: ...") and st.write("Downloaded to: ...") lines) or guard
them behind a server-side debug flag/setting (e.g., DEBUG or app.config.debug)
before calling them; keep the KaggleHandler.download_dataset(dataset_id) call
and use file_path only internally for subsequent processing, not for direct UI
output unless the debug flag is enabled.
- Around line 161-163: The file uploader variable uploaded_file in app.py
currently allows "xls" but read_data in src/dora/data_loader.py does not support
.xls, causing a runtime ValueError; either remove "xls" from the
st.file_uploader type list to prevent users uploading unsupported files, or
modify the read_data function to handle ".xls" (add a branch for ".xls"
extension in read_data and call pandas.read_excel with an appropriate engine
such as xlrd, and ensure the project dependencies include that engine).
In `@README.md`:
- Around line 15-21: The README contains a broken placeholder link token
PLACEHOLDER_LINK in the "Try DORA Online?" anchor; update that anchor so it
either points to the actual Streamlit app URL (https://dora-eda.streamlit.app/)
or remove the duplicate text link entirely (leaving only the badge anchor).
Locate the anchor containing the text "Try DORA Online?" and replace
PLACEHOLDER_LINK with the real URL or delete the entire <strong><a
href="PLACEHOLDER_LINK">Try DORA Online?</a></strong> element to avoid the
broken link.
🧹 Nitpick comments (4)
app.py (4)
24-38: Consider cleanup for accumulated session directories.Session-based output directories under
output/will accumulate over time, especially on Streamlit Cloud deployments. Consider implementing a cleanup mechanism (e.g., deleting directories older than a threshold) or using Python'stempfile.mkdtemp()for automatic cleanup.
204-208: Consider list unpacking syntax.Per Ruff RUF005, prefer
["None", *list(st.session_state.df.columns)]over concatenation. This is a minor style improvement.Proposed fix
target_variable = st.sidebar.selectbox( "Target Variable (for Bivariate Analysis)", - options=["None"] + list(st.session_state.df.columns), + options=["None", *st.session_state.df.columns], index=0, )
288-291: Minor: Inconsistent indentation.Lines 289 and 291 have extra leading space compared to surrounding code.
Proposed fix
# Remove extension if present (simple check) if "." in raw_name: - safe_name = raw_name.rsplit(".", 1)[0] + safe_name = raw_name.rsplit(".", 1)[0] else: - safe_name = raw_name + safe_name = raw_name
521-524: Consider moving session state initialization toinit_session_state.
analysis_completeandreport_dataare initialized here rather than ininit_session_state(). For consistency and single responsibility, move these initializations toinit_session_state().Proposed fix in init_session_state
Add to
init_session_state():if "analysis_complete" not in st.session_state: st.session_state.analysis_complete = False if "report_data" not in st.session_state: st.session_state.report_data = NoneThen remove lines 521-524 from
main().
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@app.py`:
- Around line 128-149: The current load_kaggle_data flow calls
KaggleHandler.download_dataset which can block because it uses IntPrompt.ask()
for CLI file selection; update load_kaggle_data to avoid interactive CLI prompts
by calling KaggleHandler.download_dataset with a non-interactive flag (e.g.,
auto_select=True) or by retrieving the file list via a new
KaggleHandler.list_dataset_files(dataset_id) and presenting choices to the user
with Streamlit UI (st.radio) and then passing the chosen filename into
KaggleHandler.download_dataset; ensure to keep the Kaggle URL handling
(KaggleHandler.is_kaggle_url and KaggleHandler.extract_dataset_id) and to set
st.session_state.df and st.session_state.input_source only after the
non-interactive download completes.
🧹 Nitpick comments (4)
app.py (4)
24-39: Consider adding session cleanup for old output directories.Each session creates a new directory under
output/that persists indefinitely. For production deployments, consider implementing a cleanup mechanism (e.g., a scheduled job or cleanup on app startup for directories older than N hours) to prevent disk space exhaustion.
111-126: Consider cleaning up the temporary file after loading.The uploaded file is saved to
temp_pathbut never deleted after being read into the DataFrame. For large files, this doubles disk usage unnecessarily.♻️ Proposed fix
try: with open(temp_path, "wb") as f: f.write(uploaded_file.getbuffer()) with st.spinner("Loading data..."): df = read_data(temp_path) st.session_state.df = df st.session_state.input_source = uploaded_file.name st.success(f"Successfully loaded '{uploaded_file.name}'") + finally: + # Clean up temp file after loading + if temp_path.exists(): + temp_path.unlink() - except Exception as e: - st.error(f"Error loading file: {e}")Note: You'd need to restructure to use try/except/finally or handle cleanup in both success and error paths.
201-205: Consider using list unpacking for cleaner syntax.Per Ruff RUF005, prefer unpacking over concatenation for readability.
♻️ Proposed fix
target_variable = st.sidebar.selectbox( "Target Variable (for Bivariate Analysis)", - options=["None"] + list(st.session_state.df.columns), + options=["None", *st.session_state.df.columns], index=0, )
502-536: Consider consolidating session state initialization.The
analysis_completeandreport_datasession state variables are lazily initialized inmain()(lines 517-520), while other session state variables are initialized ininit_session_state(). Moving all session state initialization to one place would improve maintainability.♻️ Proposed fix in init_session_state
def init_session_state(): """Initialize session state variables.""" if "session_id" not in st.session_state: st.session_state.session_id = str(uuid.uuid4()) if "df" not in st.session_state: st.session_state.df = None if "output_dir" not in st.session_state: # Create a unique output directory for this session base_output = Path("output") / st.session_state.session_id base_output.mkdir(parents=True, exist_ok=True) st.session_state.output_dir = base_output if "input_source" not in st.session_state: st.session_state.input_source = None if "zip_path" not in st.session_state: st.session_state.zip_path = None + if "analysis_complete" not in st.session_state: + st.session_state.analysis_complete = False + if "report_data" not in st.session_state: + st.session_state.report_data = NoneThen remove lines 517-520 from
main().
| def load_kaggle_data(kaggle_input): | ||
| """Handle loading of Kaggle datasets.""" | ||
| try: | ||
| with st.spinner("Connecting to Kaggle..."): | ||
| # Extract ID if it's a URL | ||
| if KaggleHandler.is_kaggle_url(kaggle_input): | ||
| dataset_id = KaggleHandler.extract_dataset_id(kaggle_input) | ||
| else: | ||
| dataset_id = kaggle_input | ||
|
|
||
| # Download using existing handler | ||
| file_path = KaggleHandler.download_dataset(dataset_id) | ||
|
|
||
| # Load data | ||
| df = read_data(file_path) | ||
| st.session_state.df = df | ||
| st.session_state.input_source = dataset_id | ||
| st.success(f"Successfully loaded data from '{dataset_id}'") | ||
|
|
||
| except Exception as e: | ||
| st.error(f"Error processing Kaggle dataset: {e}") | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for IntPrompt.ask usage in the codebase
rg "IntPrompt\.ask" -A 3 -B 3Repository: Asifdotexe/DORA
Length of output: 491
🏁 Script executed:
#!/bin/bash
# Find the KaggleHandler class and download_dataset method
fd "KaggleHandler" --type f -o find . -name "*kaggle*" -type f | head -20Repository: Asifdotexe/DORA
Length of output: 186
🏁 Script executed:
#!/bin/bash
# Search for the KaggleHandler class definition
rg "class KaggleHandler" -A 50 | head -100Repository: Asifdotexe/DORA
Length of output: 2905
🏁 Script executed:
#!/bin/bash
# Search for download_dataset method
rg "def download_dataset" -A 30 -B 2Repository: Asifdotexe/DORA
Length of output: 1962
Interactive file selection in KaggleHandler.download_dataset will fail in Streamlit context.
When a Kaggle dataset contains multiple supported files, KaggleHandler.download_dataset uses IntPrompt.ask() for interactive CLI selection. This requires stdin input and will block indefinitely or error in the Streamlit web environment.
Consider one of these approaches:
- Select the first or largest file automatically in the UI context
- Display the file list in Streamlit and let the user choose via radio buttons
- Add a parameter to
download_datasetto control selection behavior (e.g., auto-select first file when called from web context)
🧰 Tools
🪛 Ruff (0.14.13)
147-147: Do not catch blind exception: Exception
(BLE001)
🤖 Prompt for AI Agents
In `@app.py` around lines 128 - 149, The current load_kaggle_data flow calls
KaggleHandler.download_dataset which can block because it uses IntPrompt.ask()
for CLI file selection; update load_kaggle_data to avoid interactive CLI prompts
by calling KaggleHandler.download_dataset with a non-interactive flag (e.g.,
auto_select=True) or by retrieving the file list via a new
KaggleHandler.list_dataset_files(dataset_id) and presenting choices to the user
with Streamlit UI (st.radio) and then passing the chosen filename into
KaggleHandler.download_dataset; ensure to keep the Kaggle URL handling
(KaggleHandler.is_kaggle_url and KaggleHandler.extract_dataset_id) and to set
st.session_state.df and st.session_state.input_source only after the
non-interactive download completes.
Summary by CodeRabbit
New Features
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.