From e39bb28c7896c145a81a950e353c46f5bd8ec54d Mon Sep 17 00:00:00 2001
From: Asif Sayyed
Date: Fri, 23 Jan 2026 21:05:06 +0530
Subject: [PATCH 1/5] #57 added support for multiple files in streamlit
---
app.py | 43 ++++++++++++++++++++++++++++++++++++-------
src/dora/kaggle.py | 23 ++++++++++++++++++-----
2 files changed, 54 insertions(+), 12 deletions(-)
diff --git a/app.py b/app.py
index 7c88018..44538c4 100644
--- a/app.py
+++ b/app.py
@@ -135,18 +135,33 @@ def load_kaggle_data(kaggle_input):
else:
dataset_id = kaggle_input
- # Download using existing handler
- file_path = KaggleHandler.download_dataset(dataset_id)
+ # Fetch all supported files
+ files = KaggleHandler.download_files(dataset_id)
+
+ # Store found files in session state so we can let the user pick one if needed
+ st.session_state.kaggle_files = files
+ st.session_state.kaggle_dataset_id = 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}'")
+ # If there's only one file, load it immediately
+ if len(files) == 1:
+ _load_specific_kaggle_file(files[0], dataset_id)
except Exception as e:
st.error(f"Error processing Kaggle dataset: {e}")
+def _load_specific_kaggle_file(file_path, dataset_id):
+ """Helper to load a specific file from a Kaggle dataset."""
+ try:
+ with st.spinner(f"Loading {file_path.name}..."):
+ df = read_data(file_path)
+ st.session_state.df = df
+ st.session_state.input_source = f"{dataset_id}/{file_path.name}"
+ st.success(f"Successfully loaded '{file_path.name}' from '{dataset_id}'")
+ # Clear the file list selection state once loaded, if you prefer
+ # st.session_state.kaggle_files = None
+ except Exception as e:
+ st.error(f"Error loading file: {e}")
+
def render_ingestion():
"""Render the data ingestion section (Local File & Kaggle Tabs)."""
@@ -173,6 +188,20 @@ def render_ingestion():
load_kaggle_data(kaggle_input)
else:
st.warning("Please enter a valid Dataset ID or URL.")
+
+ # Check if we have multiple files to choose from
+ if "kaggle_files" in st.session_state and st.session_state.kaggle_files:
+ files = st.session_state.kaggle_files
+ if len(files) > 1:
+ st.info(f"Found {len(files)} files. Please select one:")
+ file_names = [f.name for f in files]
+ selected_filename = st.selectbox("Select file", file_names, key="kaggle_file_select")
+
+ if st.button("Load Selected File", key="btn_kaggle_multiload"):
+ # Find the path for the selected file
+ selected_path = next((f for f in files if f.name == selected_filename), None)
+ if selected_path:
+ _load_specific_kaggle_file(selected_path, st.session_state.kaggle_dataset_id)
def render_preview():
diff --git a/src/dora/kaggle.py b/src/dora/kaggle.py
index 80f4857..42c51e3 100644
--- a/src/dora/kaggle.py
+++ b/src/dora/kaggle.py
@@ -50,12 +50,12 @@ def extract_dataset_id(input_str: str) -> str:
return input_str
@staticmethod
- def download_dataset(dataset_id: str) -> Path:
+ def download_files(dataset_id: str) -> list[Path]:
"""
- Download a Kaggle dataset from kagglehub and return the path to the downloaded file.
-
- :param dataset_id: The 'owner/dataset-name' identifier of the dataset to download.
- :return: The path to the downloaded file.
+ Download a Kaggle dataset and return a list of all supported files.
+
+ :param dataset_id: The 'owner/dataset-name' identifier.
+ :return: List of Path objects for supported files.
"""
logging.info("Downloading dataset %s", dataset_id)
try:
@@ -74,6 +74,19 @@ def download_dataset(dataset_id: str) -> Path:
if not files:
raise ValueError("No supported files found in the downloaded dataset.")
+
+ return files
+
+ @staticmethod
+ def download_dataset(dataset_id: str) -> Path:
+ """
+ Download a Kaggle dataset from kagglehub and return the path to the downloaded file.
+ If multiple files are present, it prompts the user to select one (CLI mode).
+
+ :param dataset_id: The 'owner/dataset-name' identifier of the dataset to download.
+ :return: The path to the downloaded file.
+ """
+ files = KaggleHandler.download_files(dataset_id)
if len(files) == 1:
return files[0]
From 90ecb3011c10b2c6dacd22ad715f6fb70911f643 Mon Sep 17 00:00:00 2001
From: Asif Sayyed
Date: Fri, 23 Jan 2026 22:41:44 +0530
Subject: [PATCH 2/5] #57 updated readme and fixed correlation heatmap
---
README.md | 15 +++++++++++++--
src/dora/plots/multivariate.py | 4 +++-
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 2289cc4..b2fd534 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,7 @@
-
-An interactive command-line tool to automate Exploratory Data Analysis (EDA) and generate beautiful, insightful reports in seconds.
+An interactive power-tool to automate Exploratory Data Analysis (EDA) and generate beautiful, insightful reports in seconds.
@@ -39,6 +38,18 @@ Open your terminal and run the following command:
pip install dora-eda
```
+### Option 2: Run the Web App Locally
+
+If you prefer a visual interface, you can run the Streamlit app:
+
+1. Clone the repository
+2. Install dependencies: `pip install -r requirements.txt`
+3. Run the app:
+
+```bash
+streamlit run app.py
+```
+
2. Run DORA
Simply run the following command:
diff --git a/src/dora/plots/multivariate.py b/src/dora/plots/multivariate.py
index 74a6be1..6e8c44d 100644
--- a/src/dora/plots/multivariate.py
+++ b/src/dora/plots/multivariate.py
@@ -11,6 +11,7 @@
matplotlib.use("Agg")
import matplotlib.pyplot as plt
+import numpy as np
import pandas as pd
import seaborn as sns
@@ -48,8 +49,9 @@ def generate_plots(
plt.figure(figsize=(12, 10))
corr = df_numeric.corr()
+ mask = np.triu(np.ones_like(corr, dtype=bool))
cmap = sns.diverging_palette(230, 20, as_cmap=True)
- sns.heatmap(corr, annot=True, fmt=".2f", cmap=cmap, linewidths=0.5)
+ sns.heatmap(corr, mask=mask, annot=True, fmt=".2f", cmap=cmap, linewidths=0.5)
plt.title(
"Correlation Matrix of Numerical Features",
loc="left",
From ae847bde371f2a7070a5a235ff8a56072c6aee58 Mon Sep 17 00:00:00 2001
From: Asif Sayyed
Date: Fri, 23 Jan 2026 22:42:21 +0530
Subject: [PATCH 3/5] #57 removed the part for running web ui locally
---
README.md | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/README.md b/README.md
index b2fd534..96e4260 100644
--- a/README.md
+++ b/README.md
@@ -38,18 +38,6 @@ Open your terminal and run the following command:
pip install dora-eda
```
-### Option 2: Run the Web App Locally
-
-If you prefer a visual interface, you can run the Streamlit app:
-
-1. Clone the repository
-2. Install dependencies: `pip install -r requirements.txt`
-3. Run the app:
-
-```bash
-streamlit run app.py
-```
-
2. Run DORA
Simply run the following command:
From 1729de04d3e6d2e04ac983e7f023c003152a00ff Mon Sep 17 00:00:00 2001
From: Asif Sayyed
Date: Fri, 23 Jan 2026 22:54:37 +0530
Subject: [PATCH 4/5] #57 implemented unique identifiers instead of just using
filenames
---
app.py | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/app.py b/app.py
index 44538c4..6e07ae7 100644
--- a/app.py
+++ b/app.py
@@ -194,12 +194,24 @@ def render_ingestion():
files = st.session_state.kaggle_files
if len(files) > 1:
st.info(f"Found {len(files)} files. Please select one:")
- file_names = [f.name for f in files]
- selected_filename = st.selectbox("Select file", file_names, key="kaggle_file_select")
+
+ # Create a mapping of display name -> file object
+ # We use the relative path from the dataset directory to ensure uniqueness
+ # If that fails, we fallback to the full path string
+ file_mapping = {}
+ for f in files:
+ # Try to make path relative to the download directory for cleaner display
+ # Since we don't have the download dir handy here easily without re-deriving,
+ # we can use the parent folder name + filename as a unique-enough proxy for display
+ # or just the full path if needed.
+ unique_name = f"{f.parent.name}/{f.name}"
+ file_mapping[unique_name] = f
+
+ selected_display_name = st.selectbox("Select file", list(file_mapping.keys()), key="kaggle_file_select")
if st.button("Load Selected File", key="btn_kaggle_multiload"):
- # Find the path for the selected file
- selected_path = next((f for f in files if f.name == selected_filename), None)
+ # Find the path for the selected file using the mapping
+ selected_path = file_mapping.get(selected_display_name)
if selected_path:
_load_specific_kaggle_file(selected_path, st.session_state.kaggle_dataset_id)
From 76b990b28e6894326df7422aceee0c0077741f09 Mon Sep 17 00:00:00 2001
From: Asif Sayyed
Date: Fri, 23 Jan 2026 23:07:25 +0530
Subject: [PATCH 5/5] #57 I have refined the Kaggle file selection
---
app.py | 27 ++++++++++++++-------------
1 file changed, 14 insertions(+), 13 deletions(-)
diff --git a/app.py b/app.py
index 6e07ae7..ba309e2 100644
--- a/app.py
+++ b/app.py
@@ -195,23 +195,24 @@ def render_ingestion():
if len(files) > 1:
st.info(f"Found {len(files)} files. Please select one:")
- # Create a mapping of display name -> file object
- # We use the relative path from the dataset directory to ensure uniqueness
- # If that fails, we fallback to the full path string
- file_mapping = {}
- for f in files:
- # Try to make path relative to the download directory for cleaner display
- # Since we don't have the download dir handy here easily without re-deriving,
- # we can use the parent folder name + filename as a unique-enough proxy for display
- # or just the full path if needed.
- unique_name = f"{f.parent.name}/{f.name}"
- file_mapping[unique_name] = f
+ # Create a mapping of full path -> file object to ensure uniqueness
+ file_mapping = {str(f): f for f in files}
- selected_display_name = st.selectbox("Select file", list(file_mapping.keys()), key="kaggle_file_select")
+ def extract_display_name(path_str):
+ """Format the display name to be shorter if possible"""
+ f = file_mapping[path_str]
+ return f"{f.parent.name}/{f.name}"
+
+ selected_file_key = st.selectbox(
+ "Select file",
+ options=list(file_mapping.keys()),
+ format_func=extract_display_name,
+ key="kaggle_file_select"
+ )
if st.button("Load Selected File", key="btn_kaggle_multiload"):
# Find the path for the selected file using the mapping
- selected_path = file_mapping.get(selected_display_name)
+ selected_path = file_mapping.get(selected_file_key)
if selected_path:
_load_specific_kaggle_file(selected_path, st.session_state.kaggle_dataset_id)