diff --git a/Domains/AI-ML/MiniProjects/email_spam_filter/README.md b/Domains/AI-ML/MiniProjects/email_spam_filter/README.md new file mode 100644 index 00000000..ac0da242 Binary files /dev/null and b/Domains/AI-ML/MiniProjects/email_spam_filter/README.md differ diff --git a/Domains/AI-ML/MiniProjects/email_spam_filter/main.py b/Domains/AI-ML/MiniProjects/email_spam_filter/main.py new file mode 100644 index 00000000..ccfad275 --- /dev/null +++ b/Domains/AI-ML/MiniProjects/email_spam_filter/main.py @@ -0,0 +1,147 @@ +# Simple Spam Classifier based on Word Frequency (Easy ML/Statistics) +import numpy as np +import csv + +# --- 1. DATA LOADING FUNCTION --- + +def load_data_from_csv(filename="sample.csv"): + """ + Reads text and label data from a specified CSV file. + Expects two columns: Text (str) and Label (int 0 or 1). + """ + data = [] + try: + with open(filename, mode='r', encoding='utf-8') as file: + reader = csv.reader(file) + next(reader) # Skip the header row (e.g., "Text,Label") + for row in reader: + if len(row) == 2: + text = row[0].strip() + # Convert label to integer + try: + label = int(row[1].strip()) + data.append((text, label)) + except ValueError: + print(f"[Warning] Skipping row with non-integer label: {row}") + except FileNotFoundError: + print(f"[CRITICAL ERROR] The file '{filename}' was not found.") + print("Please ensure the CSV file is in the same directory as the script.") + return [] + except Exception as e: + print(f"[ERROR] An error occurred while reading the CSV: {e}") + return [] + + return data + +# --- 2. TRAIN THE CLASSIFIER (Counting) --- + +def train_classifier(data): + """ + Counts the frequency of words in spam and ham messages. + """ + spam_word_counts = {} + ham_word_counts = {} + total_spam_messages = 0 + total_ham_messages = 0 + + print("--- Training Model (Counting Words) ---") + + for text, label in data: + # Convert text to lowercase and split into words + words = text.lower().split() + + if label == 1: + total_spam_messages += 1 + counts = spam_word_counts + else: + total_ham_messages += 1 + counts = ham_word_counts + + for word in words: + # Use a basic dictionary to store word frequency + counts[word] = counts.get(word, 0) + 1 + + print(f"Total Spam Messages: {total_spam_messages}") + print(f"Total Ham Messages: {total_ham_messages}") + print("[Training Complete]") + + return spam_word_counts, ham_word_counts, total_spam_messages, total_ham_messages + +# --- 3. PREDICTION LOGIC --- + +def predict_spam_score(text, spam_counts, ham_counts, total_spam, total_ham): + """ + Scores a new message based on how many "spam words" it contains. + The score is the ratio of spam-words found. + """ + words = text.lower().split() + spam_score = 0 + + print(f"\nAnalyzing Text: '{text}'") + + for word in words: + # Calculate the simple probability of this word being in spam vs ham + + # Count how many times this word appeared in spam and ham training data + spam_hits = spam_counts.get(word, 0) + ham_hits = ham_counts.get(word, 0) + + # Simple probability: P(word|Spam) + # Add '1' (Laplace smoothing) to avoid dividing by zero if a word wasn't seen + p_word_given_spam = (spam_hits + 1) / (total_spam + 2) + p_word_given_ham = (ham_hits + 1) / (total_ham + 2) + + # Calculate the ratio: How much more likely is this word to be spam? + if p_word_given_spam > p_word_given_ham: + ratio = p_word_given_spam / p_word_given_ham + # Log the ratio to prevent numbers from exploding, and add to the score + spam_score += np.log(ratio) + print(f" - '{word}': Spam Likely (Score added: {np.log(ratio):.2f})") + else: + print(f" - '{word}': Ham Likely") + + return spam_score + +# --- MAIN EXECUTION --- +if __name__ == "__main__": + try: + # NumPy is needed for the log function, which keeps numbers manageable + import numpy as np + + # 1. Load Data from CSV + DATA = load_data_from_csv() + + if not DATA: + print("\n[Execution Halted] Cannot proceed without training data.") + else: + # 2. Train the Classifier + s_counts, h_counts, t_spam, t_ham = train_classifier(DATA) + + # 3. Set the threshold (a score above this is classified as SPAM) + SPAM_THRESHOLD = 1.0 + + # 4. Interactive Testing + print("\n--- Interactive Spam Tester ---") + print(f"Prediction Threshold: Score > {SPAM_THRESHOLD} is SPAM") + print("Enter 'quit' to exit.") + + while True: + test_text = input("\nEnter email subject/body: ") + if test_text.lower() == 'quit': + break + + final_score = predict_spam_score(test_text, s_counts, h_counts, t_spam, t_ham) + + print(f"\nFINAL SPAM SCORE: {final_score:.3f}") + + if final_score > SPAM_THRESHOLD: + print("CLASSIFICATION: 🚨 SPAM") + else: + print("CLASSIFICATION: ✅ HAM") + print("-" * 30) + + except ImportError: + print("\n[CRITICAL ERROR] NumPy is required for this script (for log function).") + print("Please ensure your Python environment has NumPy installed.") + except Exception as e: + print(f"[ERROR] An unexpected error occurred: {e}") diff --git a/Domains/AI-ML/MiniProjects/email_spam_filter/sample.csv b/Domains/AI-ML/MiniProjects/email_spam_filter/sample.csv new file mode 100644 index 00000000..4c3ee993 --- /dev/null +++ b/Domains/AI-ML/MiniProjects/email_spam_filter/sample.csv @@ -0,0 +1,16 @@ +Text,Label +Win a free gift card now,1 +Meeting scheduled for 2 PM,0 +URGENT: Your account verification,1 +Please review the project report,0 +Claim your guaranteed prize money,1 +Lunch plans confirmed tomorrow,0 +Special offer, click now,1 +Remember to submit your hours,0 +Click to renew your subscription immediately,1 +Congratulations you've won a $1000 voucher,1 +Here is the link to the quarterly budget,0 +Earn millions working from home today,1 +Your package delivery failed, update address,1 +Quick question about your vacation dates,0 +Did you send the attachment I asked for,0 \ No newline at end of file diff --git a/Domains/Frontend/MiniProjects/expense_tracker/README.md b/Domains/Frontend/MiniProjects/expense_tracker/README.md new file mode 100644 index 00000000..09d51f63 --- /dev/null +++ b/Domains/Frontend/MiniProjects/expense_tracker/README.md @@ -0,0 +1,42 @@ +**Contributor:** alisha1510 + +# Expense Tracker + +A dynamic web application to help users track their income, expenses, and balance in real-time. Features interactive charts, a clean interface, and a user-friendly way to manage personal finances. + +## 🌟 Features + +* **Real-Time Tracking** – Add income and expenses instantly, see updates in your balance. +* **Transaction History** – View, sort, and delete past transactions easily. + + +## 🎯 How to Use + +1.Open the web application in a browser. +2.Add a transaction using the Add Transaction form (income or expense). +3.Enter description, amount, and type. +4.Submit to update your balance and charts instantly. +5.View your transaction history below and delete items if needed. + + +## ⚠️ Important Disclaimer + +**For personal finance tracking only!** + +This project is intended to help manage budgets and track expenses. Please do not use this app for financial advice or professional accounting purposes. + + +## 🤝 Contributing + +Contributions, issues, and feature requests are welcome! Feel free to check the issues page. + +## 👤 Author + +**Alisha Sheikh** +- GitHub: [@alisha1510](https://github.com/alisha1510) + + +--- + + +Made with 💜 for Hacktoberfest and the Open Source Community \ No newline at end of file diff --git a/Domains/Frontend/MiniProjects/expense_tracker/index.html b/Domains/Frontend/MiniProjects/expense_tracker/index.html new file mode 100644 index 00000000..9eeca12e --- /dev/null +++ b/Domains/Frontend/MiniProjects/expense_tracker/index.html @@ -0,0 +1,100 @@ + + + + + + Dynamic Expense Tracker + + + + + + + +
+
+ +

+ BudgetFlow Tracker +

+ +

Manage your money in real-time.

+

Authenticating...

+
+ + +
+
+ +

Balance

+

--

+
+
+ +

Income

+

--

+
+
+ +

Expenses

+

--

+
+
+ + +
+ +

Add New Transaction

+
+
+ + + + +
+
+
+ + + + +
+
+ + + + +
+
+ + +
+ + +
+ + +
+ +

Transaction History

+
+ + +

Loading transactions...

+
+
+
+ + + + + diff --git a/Domains/Frontend/MiniProjects/expense_tracker/script.js b/Domains/Frontend/MiniProjects/expense_tracker/script.js new file mode 100644 index 00000000..778820e1 --- /dev/null +++ b/Domains/Frontend/MiniProjects/expense_tracker/script.js @@ -0,0 +1,122 @@ +document.addEventListener("DOMContentLoaded", () => { + let localTransactions = []; + + const userInfo = document.getElementById("user-info"); + const form = document.getElementById("transaction-form"); + const list = document.getElementById("transactions-list"); + const errorMessage = document.getElementById("error-message"); + + const balanceEl = document.getElementById("balance"); + const incomeEl = document.getElementById("income"); + const expenseEl = document.getElementById("expense"); + + userInfo.textContent = "Local Mode (Data will not save)"; + console.log("Running in Local Mode. Data is temporary."); + + const renderTransactions = () => { + list.innerHTML = ""; + + if (localTransactions.length === 0) { + list.innerHTML = `

No transactions yet. Data is temporary.

`; + return; + } + + localTransactions.forEach((t) => { + const isExpense = t.amount < 0; + const sign = isExpense ? "-" : "+"; + const colorClass = isExpense ? "text-rose-400" : "text-emerald-400"; + const formattedAmount = `$${Math.abs(t.amount).toFixed(2)}`; + + const item = document.createElement("div"); + item.id = `transaction-${t.id}`; + item.className = + "flex justify-between items-center p-4 rounded-xl main-card transition-all duration-300 hover:bg-gray-700/50 group"; + item.innerHTML = ` +
+

${t.description}

+

${new Date(t.timestamp).toLocaleDateString()}

+
+
+ + ${sign}${formattedAmount} + + +
+ `; + list.appendChild(item); + }); + }; + + const updateSummary = () => { + const income = localTransactions + .filter((t) => t.amount > 0) + .reduce((acc, t) => acc + t.amount, 0); + const expense = localTransactions + .filter((t) => t.amount < 0) + .reduce((acc, t) => acc + t.amount, 0); + + const totalExpense = Math.abs(expense); + const balance = income + expense; + + incomeEl.textContent = `$${income.toFixed(2)}`; + expenseEl.textContent = `$${totalExpense.toFixed(2)}`; + balanceEl.textContent = `$${balance.toFixed(2)}`; + balanceEl.className = `text-3xl font-bold mt-1 ${ + balance >= 0 ? "text-blue-400" : "text-rose-500" + }`; + }; + + const addTransaction = (e) => { + e.preventDefault(); + const description = document.getElementById("description").value.trim(); + const amountInput = document.getElementById("amount"); + const type = document.getElementById("type").value; + + let amount = parseFloat(amountInput.value); + if (!description || isNaN(amount) || amount <= 0) { + errorMessage.textContent = + "Please enter a valid description and amount."; + errorMessage.classList.remove("hidden"); + return; + } + + errorMessage.classList.add("hidden"); + const sign = type === "expense" ? -1 : 1; + amount = amount * sign; + + const transaction = { + id: crypto.randomUUID(), + description, + amount, + type, + timestamp: new Date().toISOString(), + }; + + localTransactions.unshift(transaction); + renderTransactions(); + updateSummary(); + + form.reset(); + errorMessage.textContent = + "Added successfully! (Temporary data - local mode)"; + errorMessage.classList.remove("hidden"); + setTimeout(() => errorMessage.classList.add("hidden"), 3000); + }; + + window.deleteTransaction = (id) => { + localTransactions = localTransactions.filter((t) => t.id !== id); + renderTransactions(); + updateSummary(); + }; + + form.addEventListener("submit", addTransaction); + + // Initial render + renderTransactions(); + updateSummary(); +}); diff --git a/Domains/Frontend/MiniProjects/expense_tracker/style.css b/Domains/Frontend/MiniProjects/expense_tracker/style.css new file mode 100644 index 00000000..1fda8d6d --- /dev/null +++ b/Domains/Frontend/MiniProjects/expense_tracker/style.css @@ -0,0 +1,45 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap'); +body { + font-family: 'Inter', sans-serif; + /* Blue gradient background */ + background: linear-gradient(135deg, #1f2937 0%, #0f172a 100%); + min-height: 100vh; + /* Light text color */ + color: #e5e7eb; + overflow-x: hidden; /* Prevent horizontal scroll */ + /* Padding to allow scrolling space at the bottom */ + padding-bottom: 2rem; +} +/* Custom Scrollbar for dark aesthetics */ +::-webkit-scrollbar { + width: 8px; +} +::-webkit-scrollbar-track { + background: #111827; +} +::-webkit-scrollbar-thumb { + background: #4b5563; + border-radius: 10px; +} +::-webkit-scrollbar-thumb:hover { + background: #6b7280; +} + +/* Custom Button Animation */ +.animated-btn { + transition: all 0.2s ease-in-out; +} +.animated-btn:hover { + transform: translateY(-2px) scale(1.02); + box-shadow: 0 10px 15px -3px rgba(16, 185, 129, 0.2), 0 4px 6px -2px rgba(16, 185, 129, 0.1); +} +.animated-btn:active { + transform: translateY(0) scale(0.98); + box-shadow: none; +} +/* Custom styling for the main card - dark background */ +.main-card { + background-color: #1f2937; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.3), 0 10px 10px -5px rgba(0, 0, 0, 0.1); + border: 1px solid #374151; +}