Recipe Cost Estimator

import tkinter as tk

from tkinter import ttk, messagebox, filedialog

import pandas as pd

import re

import os

import requests

from bs4 import BeautifulSoup

import webbrowser

from io import StringIO


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

# Configuration & Helpers

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


PRICE_DB = "prices.csv"  # local prices DB: columns -> item,price,unit (unit: kg or piece)

DEFAULT_PRICE_DATA = """item,price,unit

flour,40,kg

sugar,55,kg

milk,60,l

egg,6,piece

butter,450,kg

salt,20,kg

olive oil,600,litre

rice,60,kg

onion,40,kg

garlic,400,kg

"""


# Unit conversions to grams or ml (approximate / generic)

# you can refine per-ingredient or add density based conversions

UNIT_TO_GRAMS = {

    "g": 1,

    "gram": 1,

    "grams": 1,

    "kg": 1000,

    "kilogram": 1000,

    "kilograms": 1000,

    "mg": 0.001,

    "lb": 453.592,

    "oz": 28.3495,

    # common kitchen volume to grams approximations (generic)

    "cup": 240,       # ml ~ grams for water-like density; adapt per-ingredient

    "cups": 240,

    "tbsp": 15,

    "tablespoon": 15,

    "tsp": 5,

    "teaspoon": 5,

    "ml": 1,          # ml ~ grams for water-like density

    "l": 1000,

    "litre": 1000,

    "liter": 1000,

    "piece": None,    # piece handled separately

    "pcs": None,

    "pc": None

}


# Normalize function

def normalize_item_name(name):

    return re.sub(r'[^a-z0-9 ]', '', name.lower()).strip()


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

# Price DB functions

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

def ensure_price_db_exists():

    if not os.path.exists(PRICE_DB):

        with open(PRICE_DB, "w", encoding="utf-8") as f:

            f.write(DEFAULT_PRICE_DATA)


def load_price_db():

    ensure_price_db_exists()

    df = pd.read_csv(PRICE_DB)

    # normalize item column to match lookups

    df['item_norm'] = df['item'].apply(lambda x: normalize_item_name(str(x)))

    return df


def save_price_db(df):

    df = df.copy()

    if 'item_norm' in df.columns:

        df = df.drop(columns=['item_norm'])

    df.to_csv(PRICE_DB, index=False)


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

# Parsing ingredient lines

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

# Accept lines like:

# 2 cups flour

# 150 g sugar

# 1.5 tbsp olive oil

# 3 eggs

ING_LINE_REGEX = re.compile(r'^\s*(?P<qty>[\d/.]+)\s*(?P<unit>[a-zA-Z]+)?\s*(?P<item>.+)$')


def parse_quantity(q):

    """Parse numeric quantities (support fractions like 1/2)."""

    try:

        if '/' in q:

            parts = q.split('/')

            if len(parts) == 2:

                return float(parts[0]) / float(parts[1])

        return float(q)

    except Exception:

        return None


def parse_ingredient_line(line):

    m = ING_LINE_REGEX.match(line)

    if not m:

        return None

    qty_raw = m.group('qty')

    unit = m.group('unit') or ""

    item = m.group('item')

    qty = parse_quantity(qty_raw)

    if qty is None:

        return None

    return {"qty": qty, "unit": unit.lower(), "item": item.strip()}


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

# Cost calculation

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

def compute_cost_for_row(row, prices_df):

    """

    row: dict with keys qty, unit, item

    prices_df: DataFrame with columns item, price, unit

    returns (cost, price_used, reason)

    """

    item_norm = normalize_item_name(row['item'])

    # find best match in prices

    match = prices_df[prices_df['item_norm'] == item_norm]

    if match.empty:

        # try partial matching (contains)

        match = prices_df[prices_df['item_norm'].str.contains(item_norm.split()[0])]


    if not match.empty:

        # pick first match

        m = match.iloc[0]

        price = float(m['price'])

        price_unit = str(m['unit']).lower()

        # If price_unit is kg, convert qty/unit to kg

        if row['unit'] in UNIT_TO_GRAMS and UNIT_TO_GRAMS[row['unit']] is not None:

            grams = row['qty'] * UNIT_TO_GRAMS[row['unit']]

            if price_unit in ('kg', 'kilogram', 'kilograms'):

                kg = grams / 1000.0

                cost = kg * price

                return cost, price, f"price per kg ({price_unit})"

            elif price_unit in ('l','litre','liter','ml'):

                # assume ml ~ grams for simplicity

                liters = grams / 1000.0

                cost = liters * price

                return cost, price, f"price per litre ({price_unit})"

            elif price_unit in ('g','gram','grams'):

                cost = (grams/1.0) * price

                return cost, price, f"price per gram ({price_unit})"

            else:

                # unknown price unit, fallback

                return None, price, f"unknown price unit: {price_unit}"

        else:

            # piece-like units

            if price_unit in ('piece','pc','pcs'):

                # qty is number of pieces

                cost = row['qty'] * price

                return cost, price, "price per piece"

            elif price_unit in ('kg','kilogram','kg'):

                # if unit is piece but price is per kg, we cannot convert; ask user

                return None, price, "needs manual conversion (piece vs kg)"

            else:

                return None, price, f"unhandled conversion: {row['unit']} -> {price_unit}"

    else:

        return None, None, "no price found"


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

# Online scraping stub (adapt for your local grocery site)

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

def fetch_online_price_stub(item_name):

    """

    Placeholder demo using BeautifulSoup.

    This function is intentionally generic and will NOT work out-of-the-box for a real grocery site.

    To adapt:

      1. Choose a grocery site (that permits scraping).

      2. Inspect search result HTML and find the selector that contains price.

      3. Update SEARCH_URL and selector below.

      4. Handle pagination / blocking / headers and respect robots.txt.


    Example (pseudo):

    SEARCH_URL = f"https://yourgrocer.example/search?q={item_name_encoded}"

    r = requests.get(SEARCH_URL, headers={...})

    soup = BeautifulSoup(r.text, "html.parser")

    price_tag = soup.select_one(".price-class")

    price_text = price_tag.get_text()

    parse price_text to float and return.


    For demo: we'll return None and a helpful search url so the user can confirm manually.

    """

    # Provide a helpful web search link so user can check and paste price

    query = requests.utils.requote_uri(item_name + " price")

    search_url = f"https://www.google.com/search?q={query}"

    return None, f"Please check prices manually: {search_url}"


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

# GUI Application

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

class RecipeCostApp:

    def __init__(self, root):

        self.root = root

        self.root.title("Recipe Cost Estimator")

        self.root.geometry("900x700")


        # Load price DB

        self.prices = load_price_db()


        # Top frame: recipe input

        top = ttk.Frame(root)

        top.pack(fill='both', padx=10, pady=6)


        ttk.Label(top, text="Paste recipe (one ingredient per line):", font=("Arial", 11)).pack(anchor='w')

        self.recipe_text = tk.Text(top, height=10)

        self.recipe_text.pack(fill='x')


        btn_frame = ttk.Frame(top)

        btn_frame.pack(fill='x', pady=6)

        ttk.Button(btn_frame, text="Load Prices CSV", command=self.load_prices_csv).pack(side='left', padx=4)

        ttk.Button(btn_frame, text="Save Prices CSV", command=self.save_prices_csv).pack(side='left', padx=4)

        ttk.Button(btn_frame, text="Estimate Cost", command=self.estimate_cost).pack(side='left', padx=4)

        ttk.Button(btn_frame, text="Clear", command=lambda: self.recipe_text.delete("1.0", "end")).pack(side='left', padx=4)


        # Middle: results table and edit area

        mid = ttk.Frame(root)

        mid.pack(fill='both', expand=True, padx=10, pady=6)


        left_mid = ttk.Frame(mid)

        left_mid.pack(side='left', fill='both', expand=True)


        ttk.Label(left_mid, text="Cost Breakdown:", font=("Arial", 11)).pack(anchor='w')

        self.tree = ttk.Treeview(left_mid, columns=("qty", "unit", "item", "price_used", "cost", "note"), show='headings')

        for c in ("qty", "unit", "item", "price_used", "cost", "note"):

            self.tree.heading(c, text=c.capitalize())

            self.tree.column(c, width=110, anchor='center')

        self.tree.pack(fill='both', expand=True)


        right_mid = ttk.Frame(mid, width=320)

        right_mid.pack(side='right', fill='y', padx=6)


        ttk.Label(right_mid, text="Edit / Add Price (selected item):", font=("Arial", 11)).pack(anchor='w', pady=4)

        ttk.Label(right_mid, text="Item name:").pack(anchor='w')

        self.price_item_entry = ttk.Entry(right_mid)

        self.price_item_entry.pack(fill='x', pady=2)

        ttk.Label(right_mid, text="Price (numeric):").pack(anchor='w')

        self.price_value_entry = ttk.Entry(right_mid)

        self.price_value_entry.pack(fill='x', pady=2)

        ttk.Label(right_mid, text="Unit (kg/piece/litre):").pack(anchor='w')

        self.price_unit_entry = ttk.Entry(right_mid)

        self.price_unit_entry.pack(fill='x', pady=2)


        ttk.Button(right_mid, text="Save Price", command=self.save_price_from_entries).pack(pady=6)

        ttk.Button(right_mid, text="Fetch Online Price (open search)", command=self.fetch_online_for_selected).pack(pady=6)

        ttk.Button(right_mid, text="Export Breakdown CSV", command=self.export_breakdown_csv).pack(pady=6)


        # bottom: totals

        bottom = ttk.Frame(root)

        bottom.pack(fill='x', padx=10, pady=8)

        self.total_label = ttk.Label(bottom, text="Total Cost: ₹0.00", font=("Arial", 12, "bold"))

        self.total_label.pack(anchor='e')


        # Bind selection

        self.tree.bind("<<TreeviewSelect>>", self.on_row_select)


        # internal

        self.last_breakdown = []  # list of dicts


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

    # UI Callbacks

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

    def load_prices_csv(self):

        path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv"), ("All files", "*.*")])

        if not path:

            return

        try:

            df = pd.read_csv(path)

            if 'item' not in df.columns or 'price' not in df.columns or 'unit' not in df.columns:

                messagebox.showerror("Error", "CSV must have columns: item,price,unit")

                return

            save_price_db(df)

            self.prices = load_price_db()

            messagebox.showinfo("Loaded", f"Prices loaded and saved to {PRICE_DB}")

        except Exception as e:

            messagebox.showerror("Error", f"Failed to load file: {e}")


    def save_prices_csv(self):

        save_price_db(self.prices)

        messagebox.showinfo("Saved", f"Prices saved to {PRICE_DB}")


    def estimate_cost(self):

        # Clear tree

        for r in self.tree.get_children():

            self.tree.delete(r)

        text = self.recipe_text.get("1.0", "end").strip()

        if not text:

            messagebox.showwarning("Input", "Please paste recipe ingredient lines")

            return

        lines = [ln.strip() for ln in text.splitlines() if ln.strip()]

        parsed = []

        for ln in lines:

            p = parse_ingredient_line(ln)

            if p:

                parsed.append(p)

            else:

                parsed.append({"qty": None, "unit": "", "item": ln})

        prices_df = self.prices.copy()

        breakdown = []

        total = 0.0

        for row in parsed:

            if row['qty'] is None:

                cost, price_used, note = None, None, "Could not parse quantity"

            else:

                cost, price_used, note = compute_cost_for_row(row, prices_df)

            if cost is None:

                note = note or "price missing or conversion needed"

                cost_display = ""

            else:

                cost_display = f"{cost:.2f}"

                total += cost

            price_used_display = "" if price_used is None else str(price_used)

            breakdown_entry = {

                "qty": row.get('qty'),

                "unit": row.get('unit'),

                "item": row.get('item'),

                "price_used": price_used_display,

                "cost": cost_display,

                "note": note

            }

            breakdown.append(breakdown_entry)

            self.tree.insert("", "end", values=(breakdown_entry['qty'], breakdown_entry['unit'],

                                                breakdown_entry['item'], breakdown_entry['price_used'],

                                                breakdown_entry['cost'], breakdown_entry['note']))

        self.total_label.config(text=f"Total Cost: ₹{total:.2f}")

        self.last_breakdown = breakdown


    def on_row_select(self, event):

        sel = self.tree.selection()

        if not sel:

            return

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

        item_name = vals[2]

        # prefill edit fields

        self.price_item_entry.delete(0, 'end')

        self.price_item_entry.insert(0, item_name)

        # try to fill existing price if present

        norm = normalize_item_name(item_name)

        m = self.prices[self.prices['item_norm'] == norm]

        if not m.empty:

            row = m.iloc[0]

            self.price_value_entry.delete(0, 'end')

            self.price_value_entry.insert(0, str(row['price']))

            self.price_unit_entry.delete(0, 'end')

            self.price_unit_entry.insert(0, str(row['unit']))

        else:

            self.price_value_entry.delete(0, 'end')

            self.price_unit_entry.delete(0, 'end')


    def save_price_from_entries(self):

        item = self.price_item_entry.get().strip()

        price = self.price_value_entry.get().strip()

        unit = self.price_unit_entry.get().strip()

        if not item or not price or not unit:

            messagebox.showwarning("Input", "Please supply item, price, and unit")

            return

        try:

            price_f = float(price)

        except:

            messagebox.showerror("Input", "Price must be numeric")

            return

        norm = normalize_item_name(item)

        df = self.prices

        if (df['item_norm'] == norm).any():

            idx = df[df['item_norm'] == norm].index[0]

            df.at[idx, 'price'] = price_f

            df.at[idx, 'unit'] = unit

            df.at[idx, 'item'] = item

            df.at[idx, 'item_norm'] = norm

        else:

            new_row = {'item': item, 'price': price_f, 'unit': unit, 'item_norm': norm}

            self.prices = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)

        save_price_db(self.prices)

        messagebox.showinfo("Saved", "Price saved to local DB")

        self.estimate_cost()  # refresh


    def fetch_online_for_selected(self):

        sel = self.tree.selection()

        if not sel:

            messagebox.showwarning("Select", "Select an ingredient row first")

            return

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

        item_name = vals[2]

        price, hint = fetch_online_price_stub(item_name)

        if price is not None:

            # automatically fill fields

            self.price_item_entry.delete(0,'end'); self.price_item_entry.insert(0, item_name)

            self.price_value_entry.delete(0,'end'); self.price_value_entry.insert(0, str(price))

            self.price_unit_entry.delete(0,'end'); self.price_unit_entry.insert(0, 'kg')

            messagebox.showinfo("Fetched", f"Fetched price: {price}")

        else:

            # open browser with search link or show hint

            if hint:

                webbrowser.open(hint)

                messagebox.showinfo("Manual lookup", f"Opened browser to help find price.\n\n{hint}")

            else:

                messagebox.showinfo("No results", "No price found online (please enter manually)")


    def export_breakdown_csv(self):

        if not self.last_breakdown:

            messagebox.showwarning("No data", "Please estimate cost first")

            return

        df = pd.DataFrame(self.last_breakdown)

        path = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV files","*.csv")])

        if not path:

            return

        df.to_csv(path, index=False)

        messagebox.showinfo("Exported", f"Breakdown exported to {path}")


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

# Run

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

def main():

    ensure_price_db_exists()

    root = tk.Tk()

    app = RecipeCostApp(root)

    root.mainloop()


if __name__ == "__main__":

    main()


Smart Code Review Assistant

 smart_code_review/

├── app.py

├── templates/

│   ├── index.html

│   └── result.html

└── uploads/



app.py

import os
from flask import Flask, render_template, request
from pylint import epylint as lint
import openai

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'uploads'
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

# šŸ”‘ Set your OpenAI API key
openai.api_key = "YOUR_OPENAI_API_KEY"

def analyze_with_pylint(file_path):
    """Run pylint analysis and return report."""
    (pylint_stdout, _) = lint.py_run(file_path + " --disable=R,C", return_std=True)
    return pylint_stdout.getvalue()

def analyze_with_ai(code):
    """Send code to OpenAI for smart suggestions."""
    prompt = f"""
    You are a senior Python reviewer.
    Review the following code and provide:
    1. Code quality feedback
    2. Security issues
    3. Performance suggestions
    4. Refactoring tips

    Code:
    {code}
    """
    response = openai.ChatCompletion.create(
        model="gpt-4o-mini",  # or 'gpt-4' if available
        messages=[{"role": "user", "content": prompt}],
        max_tokens=600,
        temperature=0.4,
    )
    return response.choices[0].message["content"]

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        if 'file' not in request.files:
            return "No file uploaded"

        file = request.files['file']
        if file.filename == "":
            return "No selected file"

        file_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
        file.save(file_path)

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

        pylint_result = analyze_with_pylint(file_path)
        ai_feedback = analyze_with_ai(code_content)

        return render_template("result.html",
                               ai_feedback=ai_feedback,
                               pylint_result=pylint_result)

    return render_template("index.html")

if __name__ == "__main__":
    app.run(debug=True)


templates/index.html


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Smart Code Review Assistant</title>
    <style>
        body { font-family: Arial; margin: 50px; background-color: #f9f9f9; }
        h1 { color: #333; }
        .upload-box { background: white; padding: 30px; border-radius: 10px; width: 400px; }
        input[type=file] { margin: 20px 0; }
        button { padding: 10px 20px; background: #4CAF50; color: white; border: none; border-radius: 5px; }
    </style>
</head>
<body>
    <h1>šŸ¤– Smart Code Review Assistant</h1>
    <div class="upload-box">
        <form method="POST" enctype="multipart/form-data">
            <label>Upload your Python file (.py):</label><br>
            <input type="file" name="file" accept=".py" required><br>
            <button type="submit">Analyze Code</button>
        </form>
    </div>
</body>
</html>


templates/result.html


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Review Result</title>
    <style>
        body { font-family: Arial; margin: 40px; background-color: #f4f4f4; }
        pre { background: white; padding: 20px; border-radius: 8px; overflow-x: auto; }
        h2 { color: #444; }
    </style>
</head>
<body>
    <h1>✅ Code Review Results</h1>

    <h2>🧠 AI Feedback</h2>
    <pre>{{ ai_feedback }}</pre>

    <h2>🧩 Pylint Static Analysis</h2>
    <pre>{{ pylint_result }}</pre>

    <a href="/">⬅ Back</a>
</body>
</html>

Data Structure Visualizer

import tkinter as tk

from tkinter import ttk, messagebox

import networkx as nx

import matplotlib

matplotlib.use("TkAgg")

import matplotlib.pyplot as plt

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

import time

import threading


# Small sleep used to allow GUI to update between steps (very short)

STEP_DELAY = 0.15


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

# Data structure backends

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

class Stack:

    def __init__(self):

        self.items = []


    def push(self, v):

        self.items.append(v)


    def pop(self):

        if self.items:

            return self.items.pop()

        return None


    def to_list(self):

        # top at right

        return list(self.items)


class Queue:

    def __init__(self):

        self.items = []


    def enqueue(self, v):

        self.items.append(v)


    def dequeue(self):

        if self.items:

            return self.items.pop(0)

        return None


    def to_list(self):

        # front at left

        return list(self.items)


class LinkedListNode:

    def __init__(self, val):

        self.val = val

        self.next = None


class LinkedList:

    def __init__(self):

        self.head = None


    def to_list(self):

        out = []

        cur = self.head

        while cur:

            out.append(cur.val)

            cur = cur.next

        return out


    def insert_at(self, index, value):

        node = LinkedListNode(value)

        if index <= 0 or not self.head:

            # insert at head

            node.next = self.head

            self.head = node

            return

        cur = self.head

        i = 0

        while cur.next and i < index-1:

            cur = cur.next

            i += 1

        node.next = cur.next

        cur.next = node


    def delete_at(self, index):

        if not self.head:

            return None

        if index <= 0:

            removed = self.head

            self.head = self.head.next

            return removed.val

        cur = self.head

        i = 0

        while cur.next and i < index-1:

            cur = cur.next

            i += 1

        if cur.next:

            removed = cur.next

            cur.next = removed.next

            return removed.val

        return None


class BSTNode:

    def __init__(self, val):

        self.val = val

        self.left = None

        self.right = None


class BST:

    def __init__(self):

        self.root = None


    def insert(self, val):

        if self.root is None:

            self.root = BSTNode(val)

            return

        cur = self.root

        while True:

            if val == cur.val:

                # ignore duplicates

                return

            if val < cur.val:

                if cur.left:

                    cur = cur.left

                else:

                    cur.left = BSTNode(val)

                    return

            else:

                if cur.right:

                    cur = cur.right

                else:

                    cur.right = BSTNode(val)

                    return


    def search(self, val):

        cur = self.root

        while cur:

            if val == cur.val:

                return True

            elif val < cur.val:

                cur = cur.left

            else:

                cur = cur.right

        return False


    def delete(self, val):

        # standard BST delete (recursively)

        def _delete(node, key):

            if node is None:

                return node, None

            if key < node.val:

                node.left, removed = _delete(node.left, key)

                return node, removed

            elif key > node.val:

                node.right, removed = _delete(node.right, key)

                return node, removed

            else:

                # found node

                removed_val = node.val

                if node.left is None:

                    return node.right, removed_val

                elif node.right is None:

                    return node.left, removed_val

                else:

                    # find inorder successor (smallest in right subtree)

                    succ_parent = node

                    succ = node.right

                    while succ.left:

                        succ_parent = succ

                        succ = succ.left

                    node.val = succ.val

                    node.right, _ = _delete(node.right, succ.val)

                    return node, removed_val

        self.root, removed = _delete(self.root, val)

        return removed


    def to_graph(self):

        # output nodes and edges for visualization

        g = nx.DiGraph()

        def add_nodes(n):

            if not n:

                return

            g.add_node(str(n.val), label=str(n.val))

            if n.left:

                g.add_node(str(n.left.val), label=str(n.left.val))

                g.add_edge(str(n.val), str(n.left.val))

                add_nodes(n.left)

            if n.right:

                g.add_node(str(n.right.val), label=str(n.right.val))

                g.add_edge(str(n.val), str(n.right.val))

                add_nodes(n.right)

        add_nodes(self.root)

        return g


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

# Visualization helpers

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

def draw_stack(ax, stack: Stack):

    ax.clear()

    items = stack.to_list()

    g = nx.DiGraph()

    # nodes left->right, top is rightmost

    for i, it in enumerate(items):

        g.add_node(f"{i}:{it}", label=str(it))

        if i > 0:

            g.add_edge(f"{i-1}:{items[i-1]}", f"{i}:{it}")

    pos = {}

    # horizontal positions

    for i in range(len(items)):

        pos[f"{i}:{items[i]}"] = (i, 0)

    nx.draw_networkx_nodes(g, pos, ax=ax, node_color='skyblue', node_size=1800)

    labels = {n: g.nodes[n]['label'] for n in g.nodes()}

    nx.draw_networkx_labels(g, pos, labels, ax=ax, font_size=12)

    nx.draw_networkx_edges(g, pos, ax=ax, arrows=False)

    ax.set_title("Stack (top on the right)")

    ax.set_axis_off()


def draw_queue(ax, queue: Queue):

    ax.clear()

    items = queue.to_list()

    g = nx.DiGraph()

    for i, it in enumerate(items):

        g.add_node(f"{i}:{it}", label=str(it))

        if i > 0:

            g.add_edge(f"{i-1}:{items[i-1]}", f"{i}:{it}")

    pos = {f"{i}:{items[i]}": (i, 0) for i in range(len(items))}

    nx.draw_networkx_nodes(g, pos, ax=ax, node_color='lightgreen', node_size=1800)

    labels = {n: g.nodes[n]['label'] for n in g.nodes()}

    nx.draw_networkx_labels(g, pos, labels, ax=ax, font_size=12)

    nx.draw_networkx_edges(g, pos, ax=ax, arrows=False)

    ax.set_title("Queue (front on the left)")

    ax.set_axis_off()


def draw_linked_list(ax, ll: LinkedList):

    ax.clear()

    items = ll.to_list()

    g = nx.DiGraph()

    for i, it in enumerate(items):

        g.add_node(f"{i}:{it}", label=str(it))

        if i > 0:

            g.add_edge(f"{i-1}:{items[i-1]}", f"{i}:{it}")

    pos = {f"{i}:{items[i]}": (i, 0) for i in range(len(items))}

    nx.draw_networkx_nodes(g, pos, ax=ax, node_color='lightyellow', node_size=1600)

    labels = {n: g.nodes[n]['label'] for n in g.nodes()}

    nx.draw_networkx_labels(g, pos, labels, ax=ax, font_size=12)

    nx.draw_networkx_edges(g, pos, ax=ax, arrows=True, arrowstyle='-|>', arrowsize=20)

    ax.set_title("Singly Linked List (head on left)")

    ax.set_axis_off()


def hierarchical_pos(G, root=None, width=1.0, vert_gap=0.2, vert_loc=0, xcenter=0.5, pos=None, parent=None):

    """

    Create a hierarchical position layout for a tree (recursive).

    G: networkx graph

    root: root node

    returns dict node->(x,y)

    """

    if pos is None:

        pos = {root: (xcenter, vert_loc)}

    children = list(G.successors(root))

    if len(children) != 0:

        dx = width / len(children)

        nextx = xcenter - width/2 - dx/2

        for child in children:

            nextx += dx

            pos[child] = (nextx, vert_loc - vert_gap)

            hierarchical_pos(G, root=child, width=dx, vert_gap=vert_gap, vert_loc=vert_loc-vert_gap, xcenter=nextx, pos=pos, parent=root)

    return pos


def draw_bst(ax, bst: BST):

    ax.clear()

    g = bst.to_graph()

    if g.number_of_nodes() == 0:

        ax.text(0.5, 0.5, "Empty BST", horizontalalignment='center')

        ax.set_axis_off()

        return

    # pick root

    root = list(g.nodes())[0]

    pos = hierarchical_pos(g, root=root, width=1.0, vert_gap=0.2, vert_loc=0.9, xcenter=0.5)

    nx.draw_networkx_nodes(g, pos, ax=ax, node_size=1400, node_color='lightcoral')

    labels = {n: g.nodes[n]['label'] for n in g.nodes()}

    nx.draw_networkx_labels(g, pos, labels, ax=ax, font_size=12)

    nx.draw_networkx_edges(g, pos, ax=ax, arrows=True)

    ax.set_title("Binary Search Tree (root at top)")

    ax.set_axis_off()


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

# GUI Application

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

class DSVisualizerApp:

    def __init__(self, root):

        self.root = root

        self.root.title("Interactive Data Structure Visualizer")

        self.root.geometry("1000x700")


        self.notebook = ttk.Notebook(root)

        self.notebook.pack(fill='both', expand=True)


        # Data structures

        self.stack = Stack()

        self.queue = Queue()

        self.ll = LinkedList()

        self.bst = BST()


        # Create tabs

        self.create_stack_tab()

        self.create_queue_tab()

        self.create_linkedlist_tab()

        self.create_bst_tab()


    def make_canvas_frame(self, parent):

        frame = ttk.Frame(parent)

        # matplotlib figure

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

        canvas = FigureCanvasTkAgg(fig, master=frame)

        canvas_widget = canvas.get_tk_widget()

        canvas_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        return frame, fig, ax, canvas


    def add_log(self, text_widget, message):

        text_widget.config(state='normal')

        text_widget.insert('end', message + "\n")

        text_widget.see('end')

        text_widget.config(state='disabled')


    # ---- Stack Tab ----

    def create_stack_tab(self):

        tab = ttk.Frame(self.notebook)

        self.notebook.add(tab, text="Stack")


        left = ttk.Frame(tab)

        left.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        right = ttk.Frame(tab, width=300)

        right.pack(side=tk.RIGHT, fill=tk.Y)


        canvas_frame, fig, ax, canvas = self.make_canvas_frame(left)

        canvas_frame.pack(fill=tk.BOTH, expand=True)

        self.stack_fig = fig; self.stack_ax = ax; self.stack_canvas = canvas


        # Controls

        ttk.Label(right, text="Push value:").pack(pady=6)

        val_entry = ttk.Entry(right); val_entry.pack(pady=6)

        def on_push():

            v = val_entry.get().strip()

            if not v:

                messagebox.showwarning("Input", "Enter a value to push")

                return

            self.stack.push(v)

            self.add_log(stack_log, f"PUSH {v}")

            val_entry.delete(0, 'end')

            self.redraw_stack()

        ttk.Button(right, text="Push", command=on_push).pack(pady=6)


        def on_pop():

            val = self.stack.pop()

            if val is None:

                messagebox.showinfo("Stack", "Stack is empty")

            else:

                self.add_log(stack_log, f"POP {val}")

            self.redraw_stack()

        ttk.Button(right, text="Pop", command=on_pop).pack(pady=6)


        ttk.Button(right, text="Clear", command=lambda: (self.stack.items.clear(), self.redraw_stack())).pack(pady=6)


        # Log

        ttk.Label(right, text="Operations:").pack(pady=6)

        stack_log = tk.Text(right, height=20, width=30, state='disabled')

        stack_log.pack(pady=6)


        self.redraw_stack()


    def redraw_stack(self):

        draw_stack(self.stack_ax, self.stack)

        self.stack_canvas.draw_idle()


    # ---- Queue Tab ----

    def create_queue_tab(self):

        tab = ttk.Frame(self.notebook)

        self.notebook.add(tab, text="Queue")


        left = ttk.Frame(tab)

        left.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        right = ttk.Frame(tab, width=300)

        right.pack(side=tk.RIGHT, fill=tk.Y)


        canvas_frame, fig, ax, canvas = self.make_canvas_frame(left)

        canvas_frame.pack(fill=tk.BOTH, expand=True)

        self.queue_fig = fig; self.queue_ax = ax; self.queue_canvas = canvas


        ttk.Label(right, text="Enqueue value:").pack(pady=6)

        val_entry = ttk.Entry(right); val_entry.pack(pady=6)

        def on_enqueue():

            v = val_entry.get().strip()

            if not v:

                messagebox.showwarning("Input", "Enter a value to enqueue")

                return

            self.queue.enqueue(v)

            self.add_log(queue_log, f"ENQUEUE {v}")

            val_entry.delete(0, 'end')

            self.redraw_queue()

        ttk.Button(right, text="Enqueue", command=on_enqueue).pack(pady=6)


        def on_dequeue():

            v = self.queue.dequeue()

            if v is None:

                messagebox.showinfo("Queue", "Queue is empty")

            else:

                self.add_log(queue_log, f"DEQUEUE {v}")

            self.redraw_queue()

        ttk.Button(right, text="Dequeue", command=on_dequeue).pack(pady=6)


        ttk.Button(right, text="Clear", command=lambda: (self.queue.items.clear(), self.redraw_queue())).pack(pady=6)


        ttk.Label(right, text="Operations:").pack(pady=6)

        queue_log = tk.Text(right, height=20, width=30, state='disabled')

        queue_log.pack(pady=6)


        self.redraw_queue()


    def redraw_queue(self):

        draw_queue(self.queue_ax, self.queue)

        self.queue_canvas.draw_idle()


    # ---- Linked List Tab ----

    def create_linkedlist_tab(self):

        tab = ttk.Frame(self.notebook)

        self.notebook.add(tab, text="Linked List")


        left = ttk.Frame(tab)

        left.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        right = ttk.Frame(tab, width=320)

        right.pack(side=tk.RIGHT, fill=tk.Y)


        canvas_frame, fig, ax, canvas = self.make_canvas_frame(left)

        canvas_frame.pack(fill=tk.BOTH, expand=True)

        self.ll_fig = fig; self.ll_ax = ax; self.ll_canvas = canvas


        ttk.Label(right, text="Value:").pack(pady=6)

        val_entry = ttk.Entry(right); val_entry.pack(pady=6)

        ttk.Label(right, text="Index (0-based):").pack(pady=6)

        idx_entry = ttk.Entry(right); idx_entry.pack(pady=6)


        def on_insert():

            v = val_entry.get().strip()

            idx = idx_entry.get().strip()

            if not v:

                messagebox.showwarning("Input", "Enter a value")

                return

            try:

                idxi = int(idx) if idx != "" else 0

            except:

                messagebox.showwarning("Input", "Index must be integer")

                return

            self.ll.insert_at(idxi, v)

            self.add_log(ll_log, f"INSERT {v} at {idxi}")

            val_entry.delete(0, 'end'); idx_entry.delete(0,'end')

            self.redraw_ll()

        ttk.Button(right, text="Insert", command=on_insert).pack(pady=6)


        def on_delete():

            idx = idx_entry.get().strip()

            try:

                idxi = int(idx) if idx != "" else 0

            except:

                messagebox.showwarning("Input", "Index must be integer")

                return

            removed = self.ll.delete_at(idxi)

            if removed is None:

                messagebox.showinfo("LinkedList", "No node at that index")

            else:

                self.add_log(ll_log, f"DELETE {removed} from {idxi}")

            val_entry.delete(0, 'end'); idx_entry.delete(0,'end')

            self.redraw_ll()

        ttk.Button(right, text="Delete", command=on_delete).pack(pady=6)

        ttk.Button(right, text="Clear", command=lambda: (self.ll.__init__(), self.redraw_ll())).pack(pady=6)


        ttk.Label(right, text="Operations:").pack(pady=6)

        ll_log = tk.Text(right, height=20, width=36, state='disabled')

        ll_log.pack(pady=6)


        self.redraw_ll()


    def redraw_ll(self):

        draw_linked_list(self.ll_ax, self.ll)

        self.ll_canvas.draw_idle()


    # ---- BST Tab ----

    def create_bst_tab(self):

        tab = ttk.Frame(self.notebook)

        self.notebook.add(tab, text="Binary Search Tree")


        left = ttk.Frame(tab)

        left.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        right = ttk.Frame(tab, width=350)

        right.pack(side=tk.RIGHT, fill=tk.Y)


        canvas_frame, fig, ax, canvas = self.make_canvas_frame(left)

        canvas_frame.pack(fill=tk.BOTH, expand=True)

        self.bst_fig = fig; self.bst_ax = ax; self.bst_canvas = canvas


        ttk.Label(right, text="Value (integer):").pack(pady=6)

        val_entry = ttk.Entry(right); val_entry.pack(pady=6)


        def on_insert():

            v = val_entry.get().strip()

            if not v:

                messagebox.showwarning("Input", "Enter a value")

                return

            try:

                vi = int(v)

            except:

                messagebox.showwarning("Input", "BST requires integer values")

                return

            self.bst.insert(vi)

            self.add_log(bst_log, f"INSERT {vi}")

            val_entry.delete(0,'end')

            self.redraw_bst()

        ttk.Button(right, text="Insert", command=on_insert).pack(pady=6)


        def on_delete():

            v = val_entry.get().strip()

            if not v:

                messagebox.showwarning("Input", "Enter a value")

                return

            try:

                vi = int(v)

            except:

                messagebox.showwarning("Input", "BST requires integer values")

                return

            removed = self.bst.delete(vi)

            if removed is None:

                messagebox.showinfo("BST", f"Value {vi} not found")

            else:

                self.add_log(bst_log, f"DELETE {vi}")

            val_entry.delete(0,'end')

            self.redraw_bst()

        ttk.Button(right, text="Delete", command=on_delete).pack(pady=6)


        def on_search():

            v = val_entry.get().strip()

            if not v:

                messagebox.showwarning("Input", "Enter a value")

                return

            try:

                vi = int(v)

            except:

                messagebox.showwarning("Input", "BST requires integer values")

                return

            found = self.bst.search(vi)

            self.add_log(bst_log, f"SEARCH {vi} -> {'Found' if found else 'Not found'}")

            messagebox.showinfo("Search", f"{vi} {'found' if found else 'not found'} in BST")

            val_entry.delete(0,'end')

            # small highlight trick: we can redraw quickly (no highlight implemented)

            self.redraw_bst()

        ttk.Button(right, text="Search", command=on_search).pack(pady=6)


        ttk.Button(right, text="Clear", command=lambda: (self.bst.__init__(), self.redraw_bst())).pack(pady=6)


        ttk.Label(right, text="Operations:").pack(pady=6)

        bst_log = tk.Text(right, height=18, width=36, state='disabled')

        bst_log.pack(pady=6)


        self.redraw_bst()


    def redraw_bst(self):

        draw_bst(self.bst_ax, self.bst)

        self.bst_canvas.draw_idle()


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

# Run the app

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

def main():

    root = tk.Tk()

    app = DSVisualizerApp(root)

    root.mainloop()


if __name__ == "__main__":

    main()


Expense Forecasting Tool

import pandas as pd

import numpy as np

import matplotlib.pyplot as plt

from sklearn.linear_model import LinearRegression

from sklearn.metrics import mean_absolute_error, r2_score


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

# STEP 1: Create Sample Expense Data

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

# Example: Monthly total expenses (can be replaced with CSV input)

data = {

    "Month": [

        "Jan", "Feb", "Mar", "Apr", "May", "Jun",

        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"

    ],

    "Expense": [21000, 19500, 22000, 25000, 24500, 26000, 27500, 28500, 29500, 31000, 32500, 34000]

}


df = pd.DataFrame(data)

df["Month_Number"] = np.arange(1, len(df) + 1)


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

# STEP 2: Train the Model

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

X = df[["Month_Number"]]

y = df["Expense"]


model = LinearRegression()

model.fit(X, y)


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

# STEP 3: Forecast Next 3 Months

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

future_months = np.arange(len(df) + 1, len(df) + 4).reshape(-1, 1)

future_predictions = model.predict(future_months)


# Combine results

forecast_df = pd.DataFrame({

    "Month_Number": future_months.flatten(),

    "Predicted_Expense": future_predictions

})


print("\nšŸ”® Expense Forecast:")

print(forecast_df)


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

# STEP 4: Visualize Results

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

plt.figure(figsize=(10, 6))

plt.plot(df["Month_Number"], df["Expense"], marker='o', label="Actual Expenses")

plt.plot(forecast_df["Month_Number"], forecast_df["Predicted_Expense"], marker='o', linestyle='--', color='orange', label="Predicted")


plt.title("Expense Forecasting Tool")

plt.xlabel("Month")

plt.ylabel("Expense (₹)")

plt.legend()

plt.grid(True)

plt.show()


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

# STEP 5: Evaluate Model

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

predictions = model.predict(X)

print("\nšŸ“Š Model Evaluation:")

print("MAE:", round(mean_absolute_error(y, predictions), 2))

print("R² Score:", round(r2_score(y, predictions), 3))


AI Flash Fiction Generator

import streamlit as st

from openai import OpenAI

import textwrap


# Initialize API

client = OpenAI(api_key="YOUR_OPENAI_API_KEY")


st.set_page_config(page_title="AI Flash Fiction Generator", page_icon="šŸ“–", layout="centered")


st.title("šŸ“– AI Flash Fiction Generator")

st.caption("Enter a theme or mood → Get a 100-word short story instantly!")


theme = st.text_input("✨ Enter a theme (e.g., hope, mystery, space, love):")


if st.button("Generate Story"):

    if theme.strip() == "":

        st.warning("Please enter a theme!")

    else:

        with st.spinner("Generating your story..."):

            prompt = f"Write a 100-word flash fiction story about '{theme}'. The story should be complete, emotional, and end with a twist."


            response = client.chat.completions.create(

                model="gpt-3.5-turbo",

                messages=[

                    {"role": "system", "content": "You are a creative storyteller who writes short fiction."},

                    {"role": "user", "content": prompt}

                ],

                temperature=0.9,

                max_tokens=200

            )


            story = response.choices[0].message.content.strip()

            story = textwrap.fill(story, width=80)


            st.subheader("šŸŖ„ Your 100-Word Story:")

            st.write(story)


            st.success("Done! You can regenerate by changing the theme.")