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 @@ DORA Demo

- -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)