6 Commits
aaqil ... main

Author SHA1 Message Date
Mann Patel
d6b5e8ff1b ref: update readme 2026-02-20 21:58:45 -07:00
Mann Patel
7ffba4c14c Update README.md 2025-04-30 13:46:04 -06:00
Mann Patel
c6d98b6d77 Update App.jsx 2025-04-22 18:47:20 -06:00
Mann Patel
b4ac53a8d0 code update 2025-04-22 18:01:10 -06:00
Mann Patel
b7937018e5 updating the transaction 2025-04-22 16:33:23 -06:00
Mann Patel
9d05adacfb removing unwanted table product_category 2025-04-22 14:27:32 -06:00
11 changed files with 142 additions and 260 deletions

View File

@@ -1,6 +1,9 @@
### Some ground rules ### Ground rules
1. Add both node_modules from Slient and Server to your ```gitignore``` file
2. Make a brach with the following naming conventionp, prefix it with your name ```Your-Name Branch-Name```. <img class='fullimage' alt='Fin-Track Application Screenshot' src='./assets/CampusPlug.png'/>
1. Add both node_modules from Client and Server to your ```gitignore``` file
2. Make a brach with the following naming convention, prefix it with your name ```Your-Name Branch-Name```.
--- ---
### Frontend ### Frontend
@@ -19,25 +22,19 @@
``` ```
--- ---
### Recommendation ### Recommendation system
1. `cd recommendation-engine` into the dir and then type command 1. Install the dependencies `pip install scikit-learn numpy mysql.connector flask flask-cors`
2. `cd recommendation-engine` into the dir and then type command
```Bash ```Bash
1. python3 server.py #Start The Server 1. python3 server.py #Start The Server
``` ```
--- ---
### Recommendation system
1. Install the dependencies
```Bash
pip install mysql.connector
```
---
### Database ### Database
1. MySql Version 9.2.0 1. MySql Version 9.2.0
2. To Create the DataBase use the command bellow: 2. To Create the DataBase use the command bellow:
```Bash ```Bash
1. mysql -u root 1. mysql -u root
2. \. PathToYour/Schema.sql 2. \. PathTo/Schema.sql
3. \. PathToYour/Init-Data.sql 3. \. PathTo/Init-Data.sql
``` ```

BIN
assets/CampusPlug.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

View File

@@ -41,12 +41,7 @@ exports.removeProduct = async (req, res) => {
await db.execute(`DELETE FROM Image_URL WHERE ProductID = ?`, [productID]); await db.execute(`DELETE FROM Image_URL WHERE ProductID = ?`, [productID]);
await db.execute(`DELETE FROM History WHERE ProductID = ?`, [productID]); await db.execute(`DELETE FROM History WHERE ProductID = ?`, [productID]);
await db.execute(`DELETE FROM Favorites WHERE ProductID = ?`, [productID]); await db.execute(`DELETE FROM Favorites WHERE ProductID = ?`, [productID]);
await db.execute(`DELETE FROM Product_Category WHERE ProductID = ?`, [
productID,
]);
await db.execute(`DELETE FROM Product_Category WHERE ProductID = ?`, [
productID,
]);
await db.execute(`DELETE FROM Transaction WHERE ProductID = ?`, [ await db.execute(`DELETE FROM Transaction WHERE ProductID = ?`, [
productID, productID,
]); ]);

View File

@@ -96,7 +96,7 @@ exports.getTransactionsByProduct = async (req, res) => {
MIN(I.URL) AS Image_URL MIN(I.URL) AS Image_URL
FROM Transaction T FROM Transaction T
JOIN Product P ON T.ProductID = P.ProductID JOIN Product P ON T.ProductID = P.ProductID
LEFT JOIN Image_URL I ON P.ProductID = I.ProductID JOIN Image_URL I ON I.ProductID = T.ProductID
GROUP BY T.TransactionID, T.UserID, T.ProductID, T.Date, T.PaymentStatus, P.Name`, GROUP BY T.TransactionID, T.UserID, T.ProductID, T.Date, T.PaymentStatus, P.Name`,
); );
@@ -123,11 +123,19 @@ exports.getTransactionsByUser = async (req, res) => {
T.Date, T.Date,
T.PaymentStatus, T.PaymentStatus,
P.Name AS ProductName, P.Name AS ProductName,
I.URL AS Image_URL MIN(I.URL) AS Image_URL
FROM Transaction T FROM Transaction T
JOIN Product P ON T.ProductID = P.ProductID JOIN Product P ON T.ProductID = P.ProductID
LEFT JOIN Image_URL I ON P.ProductID = I.ProductID JOIN Image_URL I ON I.ProductID = T.ProductID
WHERE T.UserID = ?`, WHERE T.UserID = ?
GROUP BY
T.TransactionID,
T.UserID,
T.ProductID,
T.Date,
T.PaymentStatus,
P.Name;
`,
[userID], [userID],
); );
@@ -155,7 +163,7 @@ exports.getAllTransactions = async (req, res) => {
MIN(I.URL) AS Image_URL MIN(I.URL) AS Image_URL
FROM Transaction T FROM Transaction T
JOIN Product P ON T.ProductID = P.ProductID JOIN Product P ON T.ProductID = P.ProductID
LEFT JOIN Image_URL I ON P.ProductID = I.ProductID JOIN Image_URL I ON P.ProductID = I.ProductID
GROUP BY T.TransactionID, T.UserID, T.ProductID, T.Date, T.PaymentStatus, P.Name`, GROUP BY T.TransactionID, T.UserID, T.ProductID, T.Date, T.PaymentStatus, P.Name`,
); );
@@ -169,34 +177,6 @@ exports.getAllTransactions = async (req, res) => {
} }
}; };
// Update the payment status of a transaction
exports.updatePaymentStatus = async (req, res) => {
const { transactionID, paymentStatus } = req.body;
try {
const [result] = await db.execute(
`UPDATE Transaction
SET PaymentStatus = ?
WHERE TransactionID = ?`,
[paymentStatus, transactionID],
);
if (result.affectedRows === 0) {
return res
.status(404)
.json({ success: false, message: "Transaction not found" });
}
res.json({
success: true,
message: "Payment status updated successfully",
});
} catch (error) {
console.error("Error updating payment status:", error);
res.status(500).json({ error: "Could not update payment status" });
}
};
// Delete a transaction // Delete a transaction
exports.deleteTransaction = async (req, res) => { exports.deleteTransaction = async (req, res) => {
const { transactionID } = req.body; const { transactionID } = req.body;
@@ -223,3 +203,24 @@ exports.deleteTransaction = async (req, res) => {
res.status(500).json({ error: "Could not delete transaction" }); res.status(500).json({ error: "Could not delete transaction" });
} }
}; };
exports.updateTransactionStatus = async (req, res) => {
const { transactionID } = req.body;
try {
const [result] = await db.execute(
`UPDATE Transaction
SET PaymentStatus = 'completed'
WHERE TransactionID = ?;`,
[transactionID],
);
res.json({
success: true,
message: "Transaction updated successfully",
});
} catch (error) {
console.error("Error deleting transaction:", error);
res.status(500).json({ error: "Could not delete transaction" });
}
};

View File

@@ -5,10 +5,10 @@ const {
getTransactionsByProduct, getTransactionsByProduct,
getTransactionsByUser, getTransactionsByUser,
getAllTransactions, getAllTransactions,
updatePaymentStatus,
deleteTransaction, deleteTransaction,
getTransactionWithPagination, getTransactionWithPagination,
removeTransation, removeTransation,
updateTransactionStatus,
} = require("../controllers/transaction"); } = require("../controllers/transaction");
const router = express.Router(); const router = express.Router();
@@ -18,24 +18,12 @@ router.use((req, res, next) => {
next(); next();
}); });
// Create a new transaction
router.post("/createTransaction", createTransaction); router.post("/createTransaction", createTransaction);
// Get all transactions for a specific product
router.get("/getTransactionsByProduct/:productID", getTransactionsByProduct); router.get("/getTransactionsByProduct/:productID", getTransactionsByProduct);
// Get all transactions for a specific user
router.post("/getTransactionsByUser", getTransactionsByUser); router.post("/getTransactionsByUser", getTransactionsByUser);
router.post("/updateStatus", updateTransactionStatus);
// Get all transactions in the system
router.post("/getAllTransactions", getAllTransactions); router.post("/getAllTransactions", getAllTransactions);
// Update payment status on a transaction
router.patch("/updatePaymentStatus", updatePaymentStatus);
// Delete a transaction
router.delete("/deleteTransaction", deleteTransaction); router.delete("/deleteTransaction", deleteTransaction);
router.get("/getTransactions", getTransactionWithPagination); router.get("/getTransactions", getTransactionWithPagination);
router.delete("/:id", removeTransation); router.delete("/:id", removeTransation);

View File

@@ -86,54 +86,9 @@ function App() {
// Generate product recommendations // Generate product recommendations
const generateProductRecommendations = async () => { const generateProductRecommendations = async () => {
try {
setIsGeneratingRecommendations(true); setIsGeneratingRecommendations(true);
// Add a short delay to simulate calculation time
await new Promise((resolve) => setTimeout(resolve, 500)); await new Promise((resolve) => setTimeout(resolve, 500));
console.log("Generating product recommendations for user:", user.ID);
// Make API call to get recommendations
const response = await fetch(
"http://localhost:3030/api/recommendations/generate",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
userId: user.ID,
}),
},
);
if (!response.ok) {
throw new Error("Failed to generate recommendations");
}
const result = await response.json();
if (result.success) {
console.log(
"Recommendations generated successfully:",
result.recommendations,
);
setRecommendations(result.recommendations);
// Store recommendations in session storage for access across the app
sessionStorage.setItem(
"userRecommendations",
JSON.stringify(result.recommendations),
);
} else {
console.error("Error generating recommendations:", result.message);
}
} catch (err) {
console.error("Error generating product recommendations:", err);
} finally {
setIsGeneratingRecommendations(false); setIsGeneratingRecommendations(false);
}
}; };
useEffect(() => { useEffect(() => {
@@ -256,7 +211,6 @@ function App() {
// Complete signup // Complete signup
const completeSignUp = async (userData) => { const completeSignUp = async (userData) => {
try { try {
setIsLoading(true);
setError(""); setError("");
console.log("Completing signup for:", userData.email); console.log("Completing signup for:", userData.email);

View File

@@ -1,32 +1,45 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Calendar, CreditCard, Trash2 } from "lucide-react"; import { Calendar, CreditCard, Trash2 } from "lucide-react";
import FloatingAlert from "../components/FloatingAlert"; // adjust path if needed
const Transactions = () => { const Transactions = () => {
const [transactions, setTransactions] = useState([]); const [transactions, setTransactions] = useState([]);
const [showAlert, setShowAlert] = useState(false);
const storedUser = JSON.parse(sessionStorage.getItem("user"));
function reloadPage() {
const docTimestamp = new Date(performance.timing.domLoading).getTime();
const now = Date.now();
if (now > docTimestamp) {
location.reload();
}
}
useEffect(() => { useEffect(() => {
const fetchTransactions = async () => { const fetchTransactions = async () => {
try { try {
const response = await fetch( const response = await fetch(
"http://localhost:3030/api/transaction/getAllTransactions", "http://localhost:3030/api/transaction/getTransactionsByUser",
{ {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userID: 1 }), // replace with actual userID body: JSON.stringify({ userID: storedUser.ID }),
}, },
); );
if (!response.ok) throw new Error(`HTTP ${response.status}`); if (!response.ok) throw new Error(`HTTP ${response.status}`);
const { transactions: txData } = await response.json(); const { transactions: txData } = await response.json();
if (!Array.isArray(txData)) return; if (!Array.isArray(txData)) return;
console.log(txData);
setTransactions( setTransactions(
txData.map((tx) => ({ txData.map((tx) => ({
id: tx.TransactionID, id: tx.TransactionID,
productId: tx.ProductID, productId: tx.ProductID,
name: tx.ProductName || "Unnamed Product", name: tx.ProductName,
price: tx.Price != null ? parseFloat(tx.Price) : null, price: tx.Price != null ? parseFloat(tx.Price) : null,
image: tx.Image_URL || "/default-image.jpg", image: tx.Image_URL,
date: tx.Date, date: tx.Date,
status: tx.PaymentStatus, status: tx.PaymentStatus,
})), })),
@@ -52,6 +65,7 @@ const Transactions = () => {
const data = await res.json(); const data = await res.json();
if (data.success) { if (data.success) {
setTransactions((prev) => prev.filter((tx) => tx.id !== id)); setTransactions((prev) => prev.filter((tx) => tx.id !== id));
reloadPage();
} else { } else {
console.error("Delete failed:", data.message); console.error("Delete failed:", data.message);
} }
@@ -60,6 +74,26 @@ const Transactions = () => {
} }
}; };
const updateTransaction = async (id) => {
try {
const res = await fetch(
"http://localhost:3030/api/transaction/updateStatus",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ transactionID: id }),
},
);
const data = await res.json();
if (data) {
setShowAlert(true);
reloadPage();
}
} catch (err) {
console.error("Error deleting transaction:", err);
}
};
const formatDate = (dateString) => { const formatDate = (dateString) => {
const d = new Date(dateString); const d = new Date(dateString);
return d.toLocaleDateString("en-US", { return d.toLocaleDateString("en-US", {
@@ -71,6 +105,12 @@ const Transactions = () => {
return ( return (
<div className="max-w-6xl mx-auto"> <div className="max-w-6xl mx-auto">
{showAlert && (
<FloatingAlert
message="Status Updated"
onClose={() => setShowAlert(false)}
/>
)}
<div className="flex justify-between items-center mb-6"> <div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-bold text-gray-800">My Transactions</h1> <h1 className="text-2xl font-bold text-gray-800">My Transactions</h1>
</div> </div>
@@ -99,28 +139,34 @@ const Transactions = () => {
key={tx.id} key={tx.id}
className="relative border-2 border-gray-200 overflow-hidden hover:shadow-md transition-shadow" className="relative border-2 border-gray-200 overflow-hidden hover:shadow-md transition-shadow"
> >
{/* Delete Button */} <div className="absolute bottom-2 right-2 flex gap-2 z-10">
<button
onClick={(e) => {
e.preventDefault();
updateTransaction(tx.id);
}}
className="text-emerald-600 hover:text-emerald-700 text-sm font-medium"
>
Complete
</button>
<button <button
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
deleteTransaction(tx.id); deleteTransaction(tx.id);
}} }}
className="absolute bottom-2 right-2 text-red-500 hover:text-red-600 z-10" className="text-red-500 hover:text-red-600"
> >
<Trash2 size={20} /> <Trash2 size={20} />
</button> </button>
</div>
<Link to={`/product/${tx.productId}`}> <Link to={`/product/${tx.productId}`}>
<div className="h-48 bg-gray-200 flex items-center justify-center"> <div className="h-48 bg-gray-200 flex items-center justify-center">
{tx.image ? (
<img <img
src={tx.image} src={tx.image}
alt={tx.name} alt={tx.name}
className="w-full h-full object-cover" className="w-full h-full object-cover"
/> />
) : (
<div className="text-gray-400">No image</div>
)}
</div> </div>
<div className="p-4"> <div className="p-4">
<h3 className="text-lg font-semibold text-gray-800"> <h3 className="text-lg font-semibold text-gray-800">

View File

@@ -2,8 +2,6 @@
SET SET
FOREIGN_KEY_CHECKS = 0; FOREIGN_KEY_CHECKS = 0;
TRUNCATE TABLE Product_Category;
TRUNCATE TABLE Favorites; TRUNCATE TABLE Favorites;
TRUNCATE TABLE History; TRUNCATE TABLE History;
@@ -344,62 +342,6 @@ VALUES
('/Uploads/Bluetooth-Speaker.jpg', 19), ('/Uploads/Bluetooth-Speaker.jpg', 19),
('/Uploads/Backpack.jpg', 20); ('/Uploads/Backpack.jpg', 20);
-- Insert Product-Category relationships (products with multiple categories)
INSERT INTO
Product_Category (ProductID, CategoryID)
VALUES
(1, 1),
(1, 17),
(1, 20), -- Calculus book: Textbooks, School Supplies, Math Resources
(2, 2),
(2, 11),
(2, 25), -- Laptop: Electronics, Computer Accessories, Smartphones & Tablets
(3, 3),
(3, 18),
(3, 24), -- Desk: Furniture, Office Furniture, Dorm Essentials
(4, 4),
(4, 26), -- Hoodie: Clothing, Winter Clothing
(5, 5),
(5, 13), -- Basketball: Sports Equipment, Fitness Equipment
(6, 6),
(6, 23), -- Guitar: Musical Instruments, Audio Equipment
(7, 1),
(7, 15),
(7, 20), -- Physics book: Textbooks, Lab Equipment, Math & Science Resources
(8, 8),
(8, 24), -- Mini Fridge: Kitchen Appliances, Dorm Essentials
(9, 9),
(9, 2), -- PS5 Controller: Gaming, Electronics
(10, 10),
(10, 5),
(10, 13), -- Mountain Bike: Bicycles, Sports Equipment, Fitness Equipment
(11, 11),
(11, 2), -- Mouse: Computer Accessories, Electronics
(12, 15),
(12, 17), -- Lab Coat: Lab Equipment, School Supplies
(13, 12),
(13, 17),
(13, 20), -- Calculator: Stationery, School Supplies, Math & Science Resources
(14, 13),
(14, 5), -- Yoga Mat: Fitness Equipment, Sports Equipment
(15, 26),
(15, 4),
(15, 14), -- Winter Jacket: Winter Clothing, Clothing, Winter Sports
(16, 1),
(16, 17),
(16, 19), -- CS Book: Textbooks, School Supplies, Books (Non-textbook)
(17, 24),
(17, 2), -- Desk Lamp: Dorm Essentials, Electronics
(18, 12),
(18, 17),
(18, 20), -- Scientific Calculator: Stationery, School Supplies, Math & Science
(19, 23),
(19, 2),
(19, 24), -- Bluetooth Speaker: Audio Equipment, Electronics, Dorm Essentials
(20, 22),
(20, 17),
(20, 24);
-- Insert History records -- Insert History records
INSERT INTO INSERT INTO
History (HistoryID, UserID, ProductID) History (HistoryID, UserID, ProductID)
@@ -428,7 +370,6 @@ VALUES
(1, 5), -- User 4 likes Basketball (1, 5), -- User 4 likes Basketball
(2, 8); (2, 8);
-- User 5 likes Mini Fridge
-- Insert Transactions -- Insert Transactions
INSERT INTO INSERT INTO
Transaction ( Transaction (

View File

@@ -105,15 +105,6 @@ CREATE TABLE Favorites (
UNIQUE (UserID, ProductID) UNIQUE (UserID, ProductID)
); );
-- Product-Category Junction Table (Many-to-Many)
CREATE TABLE Product_Category (
ProductID INT,
CategoryID INT,
PRIMARY KEY (ProductID, CategoryID),
FOREIGN KEY (ProductID) REFERENCES Product (ProductID) ON DELETE CASCADE,
FOREIGN KEY (CategoryID) REFERENCES Category (CategoryID) ON DELETE CASCADE
);
-- Login Authentication table -- Login Authentication table
CREATE TABLE AuthVerification ( CREATE TABLE AuthVerification (
UserID INT AUTO_INCREMENT PRIMARY KEY, UserID INT AUTO_INCREMENT PRIMARY KEY,

View File

@@ -19,9 +19,9 @@ def delete_user_recommendations(user_id):
cursor = db_con.cursor() cursor = db_con.cursor()
try: try:
cursor.execute("DELETE FROM Recommendation WHERE UserID = %s", (user_id,))
db_con.commit()
print(f"Deleted existing recommendations for user {user_id}") print(f"Deleted existing recommendations for user {user_id}")
cursor.execute(f"DELETE FROM Recommendation WHERE UserID = {user_id}")
db_con.commit()
logging.info(f"Deleted existing recommendations for user {user_id}") logging.info(f"Deleted existing recommendations for user {user_id}")
return True return True
except Exception as e: except Exception as e:
@@ -32,14 +32,12 @@ def delete_user_recommendations(user_id):
cursor.close() cursor.close()
db_con.close() db_con.close()
def get_random_products(count=10, exclude_list=None): def get_random_products(count=0, exclude_list=None):
"""Get random products from the database, excluding any in the exclude_list"""
db_con = database() db_con = database()
cursor = db_con.cursor() cursor = db_con.cursor()
try: try:
if exclude_list and len(exclude_list) > 0: if exclude_list and len(exclude_list) > 0:
# Convert exclude_list to string for SQL IN clause
exclude_str = ', '.join(map(str, exclude_list)) exclude_str = ', '.join(map(str, exclude_list))
cursor.execute(f"SELECT ProductID FROM Product WHERE ProductID NOT IN ({exclude_str}) ORDER BY RAND() LIMIT {count}") cursor.execute(f"SELECT ProductID FROM Product WHERE ProductID NOT IN ({exclude_str}) ORDER BY RAND() LIMIT {count}")
else: else:
@@ -55,13 +53,11 @@ def get_random_products(count=10, exclude_list=None):
cursor.close() cursor.close()
db_con.close() db_con.close()
def get_popular_products(count=10): def get_popular_products(count=5):
"""Get popular products based on history table frequency"""
db_con = database() db_con = database()
cursor = db_con.cursor() cursor = db_con.cursor()
try: try:
# Get products that appear most frequently in history
cursor.execute(""" cursor.execute("""
SELECT ProductID, COUNT(*) as count SELECT ProductID, COUNT(*) as count
FROM History FROM History
@@ -72,7 +68,6 @@ def get_popular_products(count=10):
popular_products = [row[0] for row in cursor.fetchall()] popular_products = [row[0] for row in cursor.fetchall()]
# If not enough popular products, supplement with random ones
if len(popular_products) < count: if len(popular_products) < count:
random_products = get_random_products(count - len(popular_products), popular_products) random_products = get_random_products(count - len(popular_products), popular_products)
popular_products.extend(random_products) popular_products.extend(random_products)
@@ -81,23 +76,20 @@ def get_popular_products(count=10):
except Exception as e: except Exception as e:
logging.error(f"Error getting popular products: {str(e)}") logging.error(f"Error getting popular products: {str(e)}")
return get_random_products(count) # Fallback to random products return get_random_products(count)
finally: finally:
cursor.close() cursor.close()
db_con.close() db_con.close()
def has_user_history_or_recommendations(user_id): def has_user_history_or_recommendations(user_id):
"""Check if user exists in History or Recommendation table"""
db_con = database() db_con = database()
cursor = db_con.cursor() cursor = db_con.cursor()
try: try:
# Check if user has history cursor.execute(f"SELECT COUNT(*) FROM History WHERE UserID = {user_id}" )
cursor.execute("SELECT COUNT(*) FROM History WHERE UserID = %s", (user_id,))
history_count = cursor.fetchone()[0] history_count = cursor.fetchone()[0]
# Check if user has recommendations cursor.execute(f"SELECT COUNT(*) FROM Recommendation WHERE UserID = {user_id}")
cursor.execute("SELECT COUNT(*) FROM Recommendation WHERE UserID = %s", (user_id,))
recommendation_count = cursor.fetchone()[0] recommendation_count = cursor.fetchone()[0]
return history_count > 0 or recommendation_count > 0 return history_count > 0 or recommendation_count > 0
@@ -120,13 +112,11 @@ def get_all_products():
select_clause = "SELECT p.ProductID" select_clause = "SELECT p.ProductID"
for category in categories: for category in categories:
category_id = category[0] category_id = category[0]
select_clause += f", MAX(CASE WHEN pc.CategoryID = {category_id} THEN 1 ELSE 0 END) AS `Cat_{category_id}`" select_clause += f", MAX(CASE WHEN p.CategoryID = {category_id} THEN 1 ELSE 0 END) AS `Cat_{category_id}`"
final_query = f""" final_query = f"""
{select_clause} {select_clause}
FROM Product p FROM Product p
LEFT JOIN Product_Category pc ON p.ProductID = pc.ProductID
LEFT JOIN Category c ON pc.CategoryID = c.CategoryID
GROUP BY p.ProductID; GROUP BY p.ProductID;
""" """
@@ -137,13 +127,13 @@ def get_all_products():
product_ids = [] product_ids = []
for row in results: for row in results:
text_list = list(row) text_list = list(row)
product_id = text_list.pop(0) # Save the product ID before removing it product_id = text_list.pop(0)
final.append(text_list) final.append(text_list)
product_ids.append(product_id) product_ids.append(product_id)
cursor.close() cursor.close()
db_con.close() db_con.close()
return final, product_ids # Return both feature vectors and product IDs return final, product_ids
except Exception as e: except Exception as e:
logging.error(f"Error getting all products: {str(e)}") logging.error(f"Error getting all products: {str(e)}")
cursor.close() cursor.close()
@@ -160,15 +150,13 @@ def get_user_history(user_id):
select_clause = "SELECT p.ProductID" select_clause = "SELECT p.ProductID"
for category in categories: for category in categories:
category_id = category[0] # get the uid of the category and then append that to the new column category_id = category[0]
select_clause += f", MAX(CASE WHEN pc.CategoryID = {category_id} THEN 1 ELSE 0 END) AS `Cat_{category_id}`" select_clause += f", MAX(CASE WHEN p.CategoryID = {category_id} THEN 1 ELSE 0 END) AS `Cat_{category_id}`"
final_query = f""" final_query = f"""
{select_clause} {select_clause}
FROM Product p FROM Product p
LEFT JOIN Product_Category pc ON p.ProductID = pc.ProductID WHERE p.ProductID IN (SELECT ProductID FROM History WHERE UserID = {user_id})
LEFT JOIN Category c ON pc.CategoryID = c.CategoryID
where p.ProductID in (select ProductID from History where UserID = {user_id})
GROUP BY p.ProductID; GROUP BY p.ProductID;
""" """
@@ -191,82 +179,63 @@ def get_user_history(user_id):
def get_recommendations(user_id, top_n=5): def get_recommendations(user_id, top_n=5):
try: try:
# Always delete existing recommendations first
delete_user_recommendations(user_id) delete_user_recommendations(user_id)
# Check if user has history or recommendations
if not has_user_history_or_recommendations(user_id): if not has_user_history_or_recommendations(user_id):
# Cold start: return random products
random_recs = get_random_products(top_n) random_recs = get_random_products(top_n)
# Store these random recommendations recommendation_upload(user_id, random_recs)
history_upload(user_id, random_recs)
# Add 5 more unique random products
additional_random = get_random_products(5, random_recs) additional_random = get_random_products(5, random_recs)
history_upload(user_id, additional_random) recommendation_upload(user_id, additional_random)
return random_recs + additional_random return random_recs + additional_random
# Get all products and user history with their category vectors
all_product_features, all_product_ids = get_all_products() all_product_features, all_product_ids = get_all_products()
user_history = get_user_history(user_id) user_history = get_user_history(user_id)
if not user_history: if not user_history:
# User exists but has no history yet
popular_recs = get_popular_products(top_n) popular_recs = get_popular_products(top_n)
history_upload(user_id, popular_recs) recommendation_upload(user_id, popular_recs)
# Add 5 more unique random products
additional_random = get_random_products(5, popular_recs) additional_random = get_random_products(5, popular_recs)
history_upload(user_id, additional_random) recommendation_upload(user_id, additional_random)
return popular_recs + additional_random return popular_recs + additional_random
# Calculate similarity between all products and user history user_profile = np.mean(user_history, axis=0)
user_profile = np.mean(user_history, axis=0) # Average user preferences
similarities = cosine_similarity([user_profile], all_product_features) similarities = cosine_similarity([user_profile], all_product_features)
print(similarities)
# Get indices of the top N products sorted by similarity
product_indices = similarities[0].argsort()[-top_n:][::-1] product_indices = similarities[0].argsort()[-top_n:][::-1]
# Get the actual product IDs using the indices
recommended_product_ids = [all_product_ids[i] for i in product_indices] recommended_product_ids = [all_product_ids[i] for i in product_indices]
print(recommended_product_ids)
# Upload the core recommendations to the database recommendation_upload(user_id, recommended_product_ids)
history_upload(user_id, recommended_product_ids)
# Add 5 more unique random products that aren't in the recommendations
additional_random = get_random_products(5, recommended_product_ids) additional_random = get_random_products(5, recommended_product_ids)
history_upload(user_id, additional_random) recommendation_upload(user_id, additional_random)
# Return both the similarity-based recommendations and the random ones
return recommended_product_ids + additional_random return recommended_product_ids + additional_random
except Exception as e: except Exception as e:
logging.error(f"Recommendation error for user {user_id}: {str(e)}") logging.error(f"Recommendation error for user {user_id}: {str(e)}")
# Fallback to random products
random_products = get_random_products(top_n + 5) random_products = get_random_products(top_n + 5)
return random_products return random_products
def history_upload(userID, products): def recommendation_upload(userID, products):
"""Upload product recommendations to the database"""
db_con = database() db_con = database()
cursor = db_con.cursor() cursor = db_con.cursor()
try: try:
for product_id in products: for product_id in products:
# Use parameterized queries to prevent SQL injection
cursor.execute("INSERT INTO Recommendation (UserID, RecommendedProductID) VALUES (%s, %s)", cursor.execute("INSERT INTO Recommendation (UserID, RecommendedProductID) VALUES (%s, %s)",
(userID, product_id)) (userID, product_id))
# Commit the changes
db_con.commit() db_con.commit()
except Exception as e: except Exception as e:
logging.error(f"Error uploading recommendations: {str(e)}") logging.error(f"Error uploading recommendations: {str(e)}")
db_con.rollback() db_con.rollback()
finally: finally:
# Close the cursor and connection
cursor.close() cursor.close()
db_con.close() db_con.close()