Offline Dictionary App

import tkinter as tk

from tkinter import messagebox

import sqlite3

import pyttsx3


# -----------------------------

# Database Setup

# -----------------------------

def init_db():

    conn = sqlite3.connect("dictionary.db")

    cur = conn.cursor()


    cur.execute("""

        CREATE TABLE IF NOT EXISTS words (

            word TEXT PRIMARY KEY,

            meaning TEXT

        );

    """)


    cur.execute("""

        CREATE TABLE IF NOT EXISTS favorites (

            word TEXT PRIMARY KEY

        );

    """)


    conn.commit()

    conn.close()


# -----------------------------

# Insert sample words (optional)

# -----------------------------

def insert_sample_words():

    sample_data = {

        "python": "A high-level programming language used for general-purpose programming.",

        "algorithm": "A step-by-step procedure for solving a problem or performing a task.",

        "variable": "A storage location paired with a name used to store values.",

        "database": "A structured collection of data stored electronically.",

    }


    conn = sqlite3.connect("dictionary.db")

    cur = conn.cursor()

    

    for word, meaning in sample_data.items():

        cur.execute("INSERT OR IGNORE INTO words VALUES (?, ?)", (word, meaning))


    conn.commit()

    conn.close()


# -----------------------------

# Text-to-Speech

# -----------------------------

engine = pyttsx3.init()


def speak_word(word):

    engine.say(word)

    engine.runAndWait()


# -----------------------------

# Dictionary Operations

# -----------------------------

def search_word():

    word = entry_word.get().strip().lower()

    if not word:

        messagebox.showerror("Error", "Please enter a word to search.")

        return


    conn = sqlite3.connect("dictionary.db")

    cur = conn.cursor()


    cur.execute("SELECT meaning FROM words WHERE word=?", (word,))

    result = cur.fetchone()


    if result:

        text_meaning.config(state="normal")

        text_meaning.delete(1.0, tk.END)

        text_meaning.insert(tk.END, result[0])

        text_meaning.config(state="disabled")

    else:

        messagebox.showinfo("Not Found", "Word not found in offline dictionary.")

    

    conn.close()


def add_word():

    word = entry_add_word.get().strip().lower()

    meaning = text_add_meaning.get(1.0, tk.END).strip()


    if not word or not meaning:

        messagebox.showerror("Error", "Both word and meaning are required.")

        return


    conn = sqlite3.connect("dictionary.db")

    cur = conn.cursor()


    cur.execute("INSERT OR REPLACE INTO words VALUES (?, ?)", (word, meaning))

    conn.commit()

    conn.close()


    messagebox.showinfo("Success", f"'{word}' added to dictionary.")


def add_to_favorites():

    word = entry_word.get().strip().lower()


    if not word:

        messagebox.showerror("Error", "Search a word first before adding to favorites.")

        return


    conn = sqlite3.connect("dictionary.db")

    cur = conn.cursor()

    cur.execute("INSERT OR IGNORE INTO favorites VALUES (?)", (word,))

    conn.commit()

    conn.close()


    messagebox.showinfo("Added", f"'{word}' added to favorites!")


def show_favorites():

    conn = sqlite3.connect("dictionary.db")

    cur = conn.cursor()

    cur.execute("SELECT word FROM favorites")

    favs = cur.fetchall()

    conn.close()


    fav_list = "\n".join([w[0] for w in favs]) if favs else "No favorites added yet."


    messagebox.showinfo("Favorite Words", fav_list)


# -----------------------------

# GUI Setup

# -----------------------------

root = tk.Tk()

root.title("Offline Dictionary App")

root.geometry("650x500")

root.resizable(False, False)


# Search section

tk.Label(root, text="Enter Word:", font=("Arial", 14)).pack(pady=5)

entry_word = tk.Entry(root, font=("Arial", 14), width=30)

entry_word.pack()


btn_search = tk.Button(root, text="Search", font=("Arial", 12), command=search_word)

btn_search.pack(pady=5)


btn_speak = tk.Button(root, text="🔊 Speak", font=("Arial", 12), command=lambda: speak_word(entry_word.get()))

btn_speak.pack(pady=2)


btn_fav = tk.Button(root, text="⭐ Add to Favorites", font=("Arial", 12), command=add_to_favorites)

btn_fav.pack(pady=2)


# Meaning display

tk.Label(root, text="Meaning:", font=("Arial", 14)).pack()

text_meaning = tk.Text(root, height=6, width=60, font=("Arial", 12), state="disabled")

text_meaning.pack(pady=5)


# Add new word section

tk.Label(root, text="Add New Word:", font=("Arial", 14)).pack(pady=5)

entry_add_word = tk.Entry(root, font=("Arial", 12), width=30)

entry_add_word.pack()


tk.Label(root, text="Meaning:", font=("Arial", 14)).pack()

text_add_meaning = tk.Text(root, height=4, width=60, font=("Arial", 12))

text_add_meaning.pack()


btn_add = tk.Button(root, text="Add Word to Dictionary", font=("Arial", 12), command=add_word)

btn_add.pack(pady=5)


# favorites

btn_show_fav = tk.Button(root, text="📌 Show Favorites", font=("Arial", 12), command=show_favorites)

btn_show_fav.pack(pady=5)


# Run

init_db()

insert_sample_words()

root.mainloop()


Resume ATS Scoring Tool

import fitz  # PyMuPDF

import spacy

import re

import pandas as pd


nlp = spacy.load("en_core_web_sm")


# ---------------------------------------

# Utility: Extract text from PDF or TXT

# ---------------------------------------

def extract_text(file_path):

    if file_path.lower().endswith(".pdf"):

        text = ""

        pdf = fitz.open(file_path)

        for page in pdf:

            text += page.get_text()

        return text

    else:

        # for .txt files

        with open(file_path, "r", encoding="utf-8") as f:

            return f.read()


# ---------------------------------------

# Clean & Normalize Text

# ---------------------------------------

def clean_text(text):

    text = text.lower()

    text = re.sub(r'[^a-zA-Z0-9\s]', ' ', text)

    text = re.sub(r'\s+', ' ', text)

    return text


# ---------------------------------------

# Extract Keywords Using spaCy

# ---------------------------------------

def extract_keywords(text):

    doc = nlp(text)

    keywords = []


    for token in doc:

        # Keep nouns, verbs, adjectives (important for ATS)

        if token.pos_ in ["NOUN", "PROPN", "VERB", "ADJ"]:

            if len(token.text) > 2:

                keywords.append(token.lemma_.lower())


    return list(set(keywords))


# ---------------------------------------

# ATS Scoring Logic

# ---------------------------------------

def calculate_ats_score(resume_text, jd_text):

    resume_clean = clean_text(resume_text)

    jd_clean = clean_text(jd_text)


    resume_keywords = extract_keywords(resume_clean)

    jd_keywords = extract_keywords(jd_clean)


    matched = [kw for kw in jd_keywords if kw in resume_keywords]

    missing = [kw for kw in jd_keywords if kw not in resume_keywords]


    score = (len(matched) / len(jd_keywords)) * 100 if jd_keywords else 0


    return {

        "ats_score": round(score, 2),

        "matched_keywords": matched,

        "missing_keywords": missing,

        "total_keywords": len(jd_keywords)

    }


# ---------------------------------------

# MAIN FUNCTION

# ---------------------------------------

def ats_tool(resume_file, jobdesc_file):

    resume_text = extract_text(resume_file)

    jd_text = extract_text(jobdesc_file)


    result = calculate_ats_score(resume_text, jd_text)


    print("\n ATS SCORING RESULTS")

    print("--------------------------------")

    print(f"ATS Score: {result['ats_score']}%")

    print(f"Total Keywords in Job Description: {result['total_keywords']}")

    print(f"Matched Keywords ({len(result['matched_keywords'])}):")

    print(result["matched_keywords"])

    print("\nMissing Keywords:")

    print(result["missing_keywords"])


    # Export to CSV (optional)

    df = pd.DataFrame({

        "Matched Keywords": pd.Series(result["matched_keywords"]),

        "Missing Keywords": pd.Series(result["missing_keywords"])

    })

    df.to_csv("ats_report.csv", index=False)

    print("\n Report saved as ats_report.csv")


# ---------------------------------------

# RUN

# ---------------------------------------

if __name__ == "__main__":

    resume_path = input("Enter Resume File Path (.pdf/.txt): ")

    jd_path = input("Enter Job Description File Path (.pdf/.txt): ")


    ats_tool(resume_path, jd_path)


Command Palette Launcher (VS Code Style)

 """

Command Palette Launcher (VS Code style)

Tech: tkinter, os, keyboard, difflib


Features:

- Ctrl+P to open palette (global using 'keyboard', and also inside Tk window)

- Index files from a folder for quick search

- Fuzzy search using difflib

- Open files (os.startfile on Windows / xdg-open on Linux / open on macOS)

- Add custom commands (open app, shell command)

- Demo includes uploaded file path: /mnt/data/image.png

"""


import os

import sys

import threading

import platform

import subprocess

from pathlib import Path

import tkinter as tk

from tkinter import ttk, filedialog, messagebox

from difflib import get_close_matches


# Optional global hotkey package

try:

    import keyboard  # pip install keyboard

    KEYBOARD_AVAILABLE = True

except Exception:

    KEYBOARD_AVAILABLE = False


# Demo uploaded file path (from your session)

DEMO_FILE = "/mnt/data/image.png"


# ------------------------------

# Utility functions

# ------------------------------

def open_path(path):

    """Open a file or folder using the OS default application."""

    p = str(path)

    if platform.system() == "Windows":

        os.startfile(p)

    elif platform.system() == "Darwin":  # macOS

        subprocess.Popen(["open", p])

    else:  # Linux and others

        subprocess.Popen(["xdg-open", p])


def is_executable_file(path):

    try:

        return os.access(path, os.X_OK) and Path(path).is_file()

    except Exception:

        return False


# ------------------------------

# Indexer

# ------------------------------

class FileIndexer:

    def __init__(self):

        self.items = []  # list of {"title": ..., "path": ..., "type": "file"|"cmd"}

        # preload demo item if exists

        if Path(DEMO_FILE).exists():

            self.add_item(title=Path(DEMO_FILE).name, path=str(DEMO_FILE), typ="file")


    def add_item(self, title, path, typ="file"):

        rec = {"title": title, "path": path, "type": typ}

        self.items.append(rec)


    def index_folder(self, folder, max_files=5000):

        """Recursively index a folder (stop at max_files)."""

        folder = Path(folder)

        count = 0

        for root, dirs, files in os.walk(folder):

            for f in files:

                try:

                    fp = Path(root) / f

                    self.add_item(title=f, path=str(fp), typ="file")

                    count += 1

                    if count >= max_files:

                        return count

                except Exception:

                    continue

        return count


    def add_common_apps(self):

        """Add some common app commands (platform-specific)."""

        sysplat = platform.system()

        apps = []

        if sysplat == "Windows":

            # common Windows apps (paths may vary)

            apps = [

                ("Notepad", "notepad.exe"),

                ("Calculator", "calc.exe"),

                ("Paint", "mspaint.exe"),

            ]

        elif sysplat == "Darwin":

            apps = [

                ("TextEdit", "open -a TextEdit"),

                ("Calculator", "open -a Calculator"),

            ]

        else:  # Linux

            apps = [

                ("Gedit", "gedit"),

                ("Calculator", "gnome-calculator"),

            ]

        for name, cmd in apps:

            self.add_item(title=name, path=cmd, typ="cmd")


    def search(self, query, limit=15):

        """Simple fuzzy search: look for substrings first, then difflib matches."""

        q = query.strip().lower()

        if not q:

            # return top items

            return self.items[:limit]


        # substring matches (higher priority)

        substr_matches = [it for it in self.items if q in it["title"].lower() or q in it["path"].lower()]

        if len(substr_matches) >= limit:

            return substr_matches[:limit]


        # prepare list of titles for difflib

        titles = [it["title"] for it in self.items]

        close = get_close_matches(q, titles, n=limit, cutoff=0.4)

        # map back to records preserving order (titles may repeat)

        close_records = []

        for t in close:

            for it in self.items:

                if it["title"] == t and it not in close_records:

                    close_records.append(it)

                    break


        # combine substring + close matches, ensure uniqueness

        results = []

        seen = set()

        for it in substr_matches + close_records:

            key = (it["title"], it["path"])

            if key not in seen:

                results.append(it)

                seen.add(key)

            if len(results) >= limit:

                break

        return results


# ------------------------------

# GUI

# ------------------------------

class CommandPalette(tk.Toplevel):

    def __init__(self, master, indexer: FileIndexer):

        super().__init__(master)

        self.indexer = indexer

        self.title("Command Palette")

        self.geometry("700x380")

        self.transient(master)

        self.grab_set()  # modal

        self.resizable(False, False)


        # Styling

        self.configure(bg="#2b2b2b")


        # Search box

        self.search_var = tk.StringVar()

        search_entry = ttk.Entry(self, textvariable=self.search_var, font=("Consolas", 14), width=60)

        search_entry.pack(padx=12, pady=(12,6))

        search_entry.focus_set()

        search_entry.bind("<KeyRelease>", self.on_search_key)

        search_entry.bind("<Escape>", lambda e: self.close())

        search_entry.bind("<Return>", lambda e: self.open_selected())


        # Results list

        self.tree = ttk.Treeview(self, columns=("title","path","type"), show="headings", height=12)

        self.tree.heading("title", text="Title")

        self.tree.heading("path", text="Path / Command")

        self.tree.heading("type", text="Type")

        self.tree.column("title", width=250)

        self.tree.column("path", width=350)

        self.tree.column("type", width=80, anchor="center")

        self.tree.pack(padx=12, pady=6, fill="both", expand=True)

        self.tree.bind("<Double-1>", lambda e: self.open_selected())

        self.tree.bind("<Return>", lambda e: self.open_selected())


        # Bottom buttons

        btn_frame = ttk.Frame(self)

        btn_frame.pack(fill="x", padx=12, pady=(0,12))

        ttk.Button(btn_frame, text="Open Folder to Index", command=self.browse_and_index).pack(side="left")

        ttk.Button(btn_frame, text="Add Command", command=self.add_command_dialog).pack(side="left", padx=6)

        ttk.Button(btn_frame, text="Close (Esc)", command=self.close).pack(side="right")


        # initial populate

        self.update_results(self.indexer.items[:50])


    def on_search_key(self, event=None):

        q = self.search_var.get()

        results = self.indexer.search(q, limit=50)

        self.update_results(results)

        # keep the first row selected

        children = self.tree.get_children()

        if children:

            self.tree.selection_set(children[0])

            self.tree.focus(children[0])


    def update_results(self, records):

        # clear

        for r in self.tree.get_children():

            self.tree.delete(r)

        for rec in records:

            self.tree.insert("", "end", values=(rec["title"], rec["path"], rec["type"]))


    def open_selected(self):

        sel = self.tree.selection()

        if not sel:

            return

        vals = self.tree.item(sel[0])["values"]

        title, path, typ = vals

        try:

            if typ == "file":

                open_path(path)

            elif typ == "cmd":

                # if it's a shell command, run it

                # Allow both simple exe names and complex shell commands

                if platform.system() == "Windows":

                    subprocess.Popen(path, shell=True)

                else:

                    subprocess.Popen(path.split(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

            else:

                # fallback attempt

                open_path(path)

        except Exception as e:

            messagebox.showerror("Open failed", f"Could not open {path}\n\n{e}")

        finally:

            self.close()


    def browse_and_index(self):

        folder = filedialog.askdirectory()

        if not folder:

            return

        count = self.indexer.index_folder(folder)

        messagebox.showinfo("Indexed", f"Indexed approx {count} files from {folder}")

        # refresh results

        self.on_search_key()


    def add_command_dialog(self):

        dlg = tk.Toplevel(self)

        dlg.title("Add Command / App")

        dlg.geometry("500x150")

        tk.Label(dlg, text="Title:").pack(anchor="w", padx=8, pady=(8,0))

        title_e = ttk.Entry(dlg, width=60)

        title_e.pack(padx=8)

        tk.Label(dlg, text="Command or Path:").pack(anchor="w", padx=8, pady=(8,0))

        path_e = ttk.Entry(dlg, width=60)

        path_e.pack(padx=8)

        def add():

            t = title_e.get().strip() or Path(path_e.get()).name

            p = path_e.get().strip()

            if not p:

                messagebox.showwarning("Input", "Please provide a command or path")

                return

            typ = "cmd" if (" " in p or os.sep not in p and not Path(p).exists()) else "file"

            self.indexer.add_item(title=t, path=p, typ=typ)

            dlg.destroy()

            self.on_search_key()

        ttk.Button(dlg, text="Add", command=add).pack(pady=8)


    def close(self):

        try:

            self.grab_release()

        except:

            pass

        self.destroy()


# ------------------------------

# Main App Window

# ------------------------------

class PaletteApp:

    def __init__(self, root):

        self.root = root

        root.title("Command Palette Launcher")

        root.geometry("700x120")


        self.indexer = FileIndexer()

        self.indexer.add_common_apps()

        # Add a few demo entries (including uploaded file path)

        if Path(DEMO_FILE).exists():

            self.indexer.add_item(title=Path(DEMO_FILE).name, path=str(DEMO_FILE), typ="file")


        # Top UI

        frame = ttk.Frame(root, padding=12)

        frame.pack(fill="both", expand=True)


        ttk.Label(frame, text="Press Ctrl+P to open command palette", font=("Arial", 12)).pack(anchor="w")

        ttk.Button(frame, text="Open Palette (Ctrl+P)", command=self.open_palette).pack(pady=10, anchor="w")

        ttk.Button(frame, text="Index Folder", command=self.index_folder).pack(side="left")

        ttk.Button(frame, text="Exit", command=root.quit).pack(side="right")


        # register global hotkey in a background thread (if available)

        if KEYBOARD_AVAILABLE:

            t = threading.Thread(target=self.register_global_hotkey, daemon=True)

            t.start()

        else:

            print("keyboard package not available — global hotkey disabled. Use app's Ctrl+P instead.")


        # bind Ctrl+P inside the Tk window too

        root.bind_all("<Control-p>", lambda e: self.open_palette())


    def open_palette(self):

        # open modal CommandPalette

        cp = CommandPalette(self.root, self.indexer)


    def index_folder(self):

        folder = filedialog.askdirectory()

        if not folder:

            return

        count = self.indexer.index_folder(folder)

        messagebox.showinfo("Indexed", f"Indexed approx {count} files")


    def register_global_hotkey(self):

        """

        Register Ctrl+P as a global hotkey using keyboard module.

        When pressed, we must bring the Tk window to front and open palette.

        """

        try:

            # On some systems, keyboard requires admin privileges. If it fails, we catch and disable.

            keyboard.add_hotkey("ctrl+p", lambda: self.trigger_from_global())

            keyboard.wait()  # keep the listener alive

        except Exception as e:

            print("Global hotkey registration failed:", e)


    def trigger_from_global(self):

        # Because keyboard runs in another thread, schedule UI work in Tk mainloop

        try:

            self.root.after(0, self.open_palette)

            # Try to bring window to front

            try:

                self.root.lift()

                self.root.attributes("-topmost", True)

                self.root.after(500, lambda: self.root.attributes("-topmost", False))

            except Exception:

                pass

        except Exception as e:

            print("Error triggering palette:", e)


# ------------------------------

# Run

# ------------------------------

def main():

    root = tk.Tk()

    app = PaletteApp(root)

    root.mainloop()


if __name__ == "__main__":

    main()


AI Image Tag Generator

 """

AI Image Tag Generator (transformers + PIL)


- Uses Hugging Face transformers image-classification pipeline (ViT)

- Returns top-k labels as tags with confidence scores

- Default input path is the uploaded file: /mnt/data/image.png

"""


from transformers import pipeline

from PIL import Image

import argparse

import os


# Default path (file uploaded in this session)

DEFAULT_IMAGE_PATH = "/mnt/data/image.png"


def generate_tags(image_path, model_name="google/vit-base-patch16-224", top_k=5):

    """

    Generate tags for an image using a HF transformers image-classification pipeline.

    Returns a list of (label, score) tuples.

    """

    if not os.path.exists(image_path):

        raise FileNotFoundError(f"Image not found: {image_path}")


    classifier = pipeline("image-classification", model=model_name)

    img = Image.open(image_path).convert("RGB")

    preds = classifier(img, top_k=top_k)


    # preds is a list of dicts: [{"label": "...", "score": 0.XX}, ...]

    tags = [(p["label"], float(p["score"])) for p in preds]

    return tags


def pretty_print_tags(tags):

    print("Generated tags:")

    for label, score in tags:

        print(f" - {label}  ({score:.2f})")


def parse_args():

    p = argparse.ArgumentParser(description="AI Image Tag Generator")

    p.add_argument("--image", "-i", default=DEFAULT_IMAGE_PATH, help="Path to input image")

    p.add_argument("--model", "-m", default="google/vit-base-patch16-224", help="HuggingFace model name")

    p.add_argument("--topk", type=int, default=5, help="Number of top tags to return")

    return p.parse_args()


def main():

    args = parse_args()

    try:

        tags = generate_tags(args.image, model_name=args.model, top_k=args.topk)

        pretty_print_tags(tags)

    except Exception as e:

        print("Error:", e)


if __name__ == "__main__":

    main()


Local ML Model Trainer Interface

import streamlit as st

import pandas as pd

import numpy as np

import matplotlib.pyplot as plt

import seaborn as sns


from sklearn.model_selection import train_test_split

from sklearn.metrics import (

    accuracy_score, precision_score, recall_score, f1_score,

    mean_squared_error, confusion_matrix

)


from sklearn.preprocessing import StandardScaler


from sklearn.linear_model import LogisticRegression, LinearRegression

from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor

from sklearn.svm import SVC, SVR

from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor



st.set_page_config(page_title="Local ML Model Trainer", layout="wide")

st.title("Local ML Model Trainer Interface")

st.write("Upload a dataset → choose an algorithm → train → view results")



# ────────────────────────────────────────────────

# Upload Dataset

# ────────────────────────────────────────────────

uploaded_file = st.file_uploader("📤 Upload CSV Dataset", type=["csv"])


if uploaded_file:

    df = pd.read_csv(uploaded_file)

    st.success("Dataset Loaded Successfully!")

    st.write("###  Data Preview")

    st.dataframe(df.head())


    st.write("###  Dataset Info")

    st.write(df.describe())


    # Target column selection

    target_col = st.selectbox(" Select Target Column (Y)", df.columns)


    # Feature columns

    X = df.drop(columns=[target_col])

    y = df[target_col]


    # Auto detect problem type

    if df[target_col].dtype == object or df[target_col].nunique() < 15:

        problem_type = "classification"

    else:

        problem_type = "regression"


    st.info(f"Detected Problem Type: **{problem_type.upper()}**")


    # Choose model based on problem type

    if problem_type == "classification":

        model_choice = st.selectbox(

            "Choose Model",

            ["Logistic Regression", "Random Forest Classifier", "SVM Classifier", "KNN Classifier"]

        )

    else:

        model_choice = st.selectbox(

            "Choose Model",

            ["Linear Regression", "Random Forest Regressor", "SVM Regressor", "KNN Regressor"]

        )


    test_size = st.slider("Test Size (Train %)", 0.1, 0.5, 0.2)


    # Train button

    if st.button(" Train Model"):

        # Preprocessing

        scaler = StandardScaler()

        X_scaled = scaler.fit_transform(X.select_dtypes(include=np.number))


        X_train, X_test, y_train, y_test = train_test_split(

            X_scaled, y, test_size=test_size, random_state=42

        )


        # Model Selection

        if model_choice == "Logistic Regression":

            model = LogisticRegression()

        elif model_choice == "Random Forest Classifier":

            model = RandomForestClassifier()

        elif model_choice == "SVM Classifier":

            model = SVC()

        elif model_choice == "KNN Classifier":

            model = KNeighborsClassifier()

        elif model_choice == "Linear Regression":

            model = LinearRegression()

        elif model_choice == "Random Forest Regressor":

            model = RandomForestRegressor()

        elif model_choice == "SVM Regressor":

            model = SVR()

        elif model_choice == "KNN Regressor":

            model = KNeighborsRegressor()


        # Train

        model.fit(X_train, y_train)

        y_pred = model.predict(X_test)


        st.success("Model Trained Successfully!")


        # ────────────────────────────────────────────────

        # Show Metrics

        # ────────────────────────────────────────────────

        st.write("## 📈 Model Performance")


        if problem_type == "classification":

            st.write("### 🔹 Classification Metrics")

            st.write(f"Accuracy: **{accuracy_score(y_test, y_pred):.4f}**")

            st.write(f"Precision: **{precision_score(y_test, y_pred, average='weighted'):.4f}**")

            st.write(f"Recall: **{recall_score(y_test, y_pred, average='weighted'):.4f}**")

            st.write(f"F1 Score: **{f1_score(y_test, y_pred, average='weighted'):.4f}**")


            # Confusion Matrix

            cm = confusion_matrix(y_test, y_pred)

            fig, ax = plt.subplots(figsize=(5, 4))

            sns.heatmap(cm, annot=True, fmt="d", cmap="Blues", ax=ax)

            st.write("###  Confusion Matrix")

            st.pyplot(fig)


        else:

            st.write("### 🔹 Regression Metrics")

            rmse = np.sqrt(mean_squared_error(y_test, y_pred))

            st.write(f"RMSE: **{rmse:.4f}**")


        # ────────────────────────────────────────────────

        # Feature Importance (for tree models)

        # ────────────────────────────────────────────────

        if "Forest" in model_choice:

            st.write("##  Feature Importance")

            importance = model.feature_importances_

            fig, ax = plt.subplots(figsize=(6, 4))

            sns.barplot(x=importance, y=X.columns, ax=ax)

            st.pyplot(fig)