Skip to content

Python PR with issues: Inventory Management and Cart Features #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions python_codebase/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,43 @@ def get_hashed_password(password):


async def very_token(token: str):
'''verify token from login'''
"""verify token from login"""
try:
payload = jwt.decode(token, get_settings().SECRET,
algorithms=["HS256"])
payload = jwt.decode(token, get_settings().SECRET, algorithms=["HS256"])
user = await User.get(id=payload.get("id"))

except:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid Token",
headers={"WWW-Authenticate": "Bearer"}
headers={"WWW-Authenticate": "Bearer"},
)
return await user


async def very_token_email(token: str):
'''verify token from email'''
"""verify token from email"""
try:
payload = jwt.decode(token, get_settings().SECRET,
algorithms=["HS256"])
payload = jwt.decode(token, get_settings().SECRET, algorithms=["HS256"])
user = await User.get(id=payload.get("id"), email=payload.get("email"))

except:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid Token",
headers={"WWW-Authenticate": "Bearer"}
headers={"WWW-Authenticate": "Bearer"},
)
return await user


# bug: sam_ple@gma.com:Trye ; sam_p_le@gma.com: False!!
regex = '^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$'
regex = "^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$"


def is_not_email(email):
"""if valid mail: return 'True' \n
** This is a simple way to do this and is not recommended for a real project ** """
if(re.search(regex, email)):
"""if valid mail: return 'True' \n
** This is a simple way to do this and is not recommended for a real project **"""
if re.search(regex, email):
return False
else:
return True
Expand All @@ -68,7 +67,7 @@ async def authenticate_user(username: str, password: str):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Email not verifide",
headers={"WWW-Authenticate": "Bearer"}
headers={"WWW-Authenticate": "Bearer"},
)
return user
return False
Expand All @@ -81,12 +80,13 @@ async def token_generator(username: str, password: str):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid Username or Password",
headers={"WWW-Authenticate": "Bearer"}
headers={"WWW-Authenticate": "Bearer"},
)

token_data = {
"id": user.id,
"username": user.username
}
token_data = {"id": user.id, "username": user.username}
token = jwt.encode(token_data, get_settings().SECRET, algorithm="HS256")
return token


async def get_current_user(token: str = Depends(oauth_scheme)):
return await very_token(token)
13 changes: 13 additions & 0 deletions python_codebase/inventory_management_api/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from flask import Flask
from config import configure_app
from rate_limiter import limiter
from routes.inventory import inventory_bp

app = Flask(__name__)
configure_app(app)
limiter.init_app(app)

app.register_blueprint(inventory_bp, url_prefix="/inventory")

if __name__ == "__main__":
app.run(debug=True)
10 changes: 10 additions & 0 deletions python_codebase/inventory_management_api/cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import redis
from flask import current_app


def get_cache():
return redis.Redis(
host=current_app.config["REDIS_HOST"],
port=current_app.config["REDIS_PORT"],
db=0,
)
11 changes: 11 additions & 0 deletions python_codebase/inventory_management_api/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import os


def configure_app(app):
app.config["REDIS_HOST"] = "localhost"
app.config["REDIS_PORT"] = 6379
app.config["DB_HOST"] = "localhost"
app.config["DB_USER"] = "user"
app.config["DB_PASSWORD"] = os.getenv("DB_PASSWORD", "default")
app.config["DB_NAME_1"] = "inventory_db1"
app.config["DB_NAME_2"] = "inventory_db2"
4 changes: 4 additions & 0 deletions python_codebase/inventory_management_api/rate_limiter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(key_func=get_remote_address, default_limits=["100 per minute"])
Empty file.
60 changes: 60 additions & 0 deletions python_codebase/inventory_management_api/routes/cart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from fastapi import APIRouter, Depends, HTTPException
from models import User, Cart, CartItem, Product
from authentication import get_current_user

router = APIRouter()


@router.post("/cart/add")
async def add_to_cart(
product_id: int, quantity: int, user: User = Depends(get_current_user)
):
product = await Product.get(id=product_id)
cart, _ = await Cart.get_or_create(user=user)
cart_item, created = await CartItem.get_or_create(cart=cart, product=product)

if not created:
cart_item.quantity += quantity
else:
cart_item.quantity = quantity

cart_item.price = product.new_price
await cart_item.save()
return {"message": "Item added to cart"}


@router.get("/cart")
async def view_cart(user: User = Depends(get_current_user)):
cart, _ = await Cart.get_or_create(user=user)
items = await CartItem.filter(cart=cart).prefetch_related("product")
total = sum(item.quantity * item.price for item in items)
return {
"items": [
{
"product": item.product.name,
"quantity": item.quantity,
"price": item.price,
"total": item.quantity * item.price,
}
for item in items
],
"total": total,
}


@router.put("/cart/update/{item_id}")
async def update_cart_item(
item_id: int, quantity: int, user: User = Depends(get_current_user)
):
cart = await Cart.get(user=user)
item = await CartItem.get(id=item_id, cart=cart)
item.quantity = quantity
await item.save()
return {"message": "Cart item updated"}


@router.delete("/cart/remove/{item_id}")
async def remove_from_cart(item_id: int, user: User = Depends(get_current_user)):
cart = await Cart.get(user=user)
await CartItem.filter(id=item_id, cart=cart).delete()
return {"message": "Item removed from cart"}
122 changes: 122 additions & 0 deletions python_codebase/inventory_management_api/routes/inventory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from flask import Blueprint, request, jsonify
import mysql.connector
from cache import get_cache
from db import get_shards, get_shard, execute_query
from rate_limiter import limiter


class InventoryRoutes:
def __init__(self):
self.shards = get_shards()
self.cache = get_cache()

def get_inventory(self):
try:
product_id = request.args.get("product_id")
page = int(request.args.get("page", 1))
per_page = int(request.args.get("per_page", 10))

offset = (page - 1) * per_page
cache_key = f"inventory_{product_id}_{page}_{per_page}"
cached_inventory = self.cache.get(cache_key)

if cached_inventory:
return jsonify({"inventory": cached_inventory.decode("utf-8")})

shard = get_shard(int(product_id), self.shards)
query = f"""
SELECT
i.product_id, i.quantity, p.name AS product_name, c.name AS category_name
FROM
inventory i
JOIN
products p ON i.product_id = p.id
JOIN
categories c ON p.category_id = c.id
WHERE
i.product_id = %s
LIMIT %s OFFSET %s
"""
cursor = execute_query(shard, query, (product_id, per_page, offset))
inventory = cursor.fetchall()

if inventory:
self.cache.setex(cache_key, 300, str(inventory)) # Cache for 5 minutes
return jsonify({"inventory": inventory})

return jsonify({"error": "Product not found"}), 404
except mysql.connector.Error as err:
return jsonify({"error": str(err)}), 500

def add_inventory(self):
try:
data = request.json
product_id = data.get("product_id")
quantity = data.get("quantity")

if not product_id or not quantity:
return jsonify({"error": "Invalid data"}), 400

shard = get_shard(int(product_id), self.shards)
query = "INSERT INTO inventory (product_id, quantity) VALUES (%s, %s)"
cursor = execute_query(shard, query, (product_id, quantity))
shard.commit()

return jsonify({"message": "Inventory added successfully"}), 201
except mysql.connector.Error as err:
return jsonify({"error": str(err)}), 500

def update_inventory(self):
try:
data = request.json
product_id = data.get("product_id")
quantity = data.get("quantity")

if not product_id or not quantity:
return jsonify({"error": "Invalid data"}), 400

shard = get_shard(int(product_id), self.shards)
query = "UPDATE inventory SET quantity = %s WHERE product_id = %s"
cursor = execute_query(shard, query, (quantity, product_id))
shard.commit()

# Invalidate cache after update
self.cache.delete(f"inventory_{product_id}")

return jsonify({"message": "Inventory updated successfully"}), 200
except mysql.connector.Error as err:
return jsonify({"error": str(err)}), 500

def delete_inventory(self):
try:
data = request.json
product_id = data.get("product_id")

if not product_id:
return jsonify({"error": "Invalid data"}), 400

shard = get_shard(int(product_id), self.shards)
query = "DELETE FROM inventory WHERE product_id = %s"
cursor = execute_query(shard, query, (product_id,))
shard.commit()

# Invalidate cache after delete
self.cache.delete(f"inventory_{product_id}")

# Confirm deletion
if cursor.rowcount == 0:
return jsonify({"error": "Product not found"}), 404

return jsonify({"message": "Inventory deleted successfully"}), 200
except mysql.connector.Error as err:
return jsonify({"error": str(err)}), 500


inventory_routes = InventoryRoutes()

inventory_bp = Blueprint("inventory", __name__)

inventory_bp.route("/", methods=["GET"])(inventory_routes.get_inventory)
inventory_bp.route("/add", methods=["POST"])(inventory_routes.add_inventory)
inventory_bp.route("/update", methods=["POST"])(inventory_routes.update_inventory)
inventory_bp.route("/delete", methods=["POST"])(inventory_routes.delete_inventory)
64 changes: 64 additions & 0 deletions python_codebase/inventory_management_api/routes/recommendation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from collections import defaultdict
from db import execute_query, get_shards, get_shard


class RecommendationEngine:
def __init__(self):
self.shards = get_shards()

def get_recommendations(self, user_id, num_recommendations=5):
try:
product_counts = defaultdict(int)

# Fetch past orders of the user
for shard in self.shards:
query = """
SELECT
oi.product_id, COUNT(*) AS count
FROM
orders o
JOIN
order_items oi ON o.id = oi.order_id
WHERE
o.user_id = %s
GROUP BY
oi.product_id
"""
cursor = execute_query(shard, query, (user_id,))
results = cursor.fetchall()

for product_id, count in results:
product_counts[product_id] += count

# Fetch recent search history of the user
for shard in self.shards:
query = """
SELECT
product_id
FROM
search_history
WHERE
user_id = %s
ORDER BY
search_time DESC
LIMIT %s
"""
cursor = execute_query(shard, query, (user_id, num_recommendations))
results = cursor.fetchall()

for (product_id,) in results:
product_counts[
product_id
] += 1 # Increment count for searched products

# Aggregate recommendations from past orders and recent searches
recommendations = sorted(
product_counts.items(), key=lambda x: x[1], reverse=True
)[:num_recommendations]
return [product_id for product_id, _ in recommendations]
except Exception as e:
print(f"Error occurred while fetching recommendations: {str(e)}")
return []


recommendation_engine = RecommendationEngine()
Loading