-
Notifications
You must be signed in to change notification settings - Fork 1
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove debug mode for production security
Running the Flask app with debug=True exposes sensitive application internals, stack traces, and configuration details, which can lead to security vulnerabilities in production. The Code suggestionCheck the AI-generated fix before applying
Suggested change
Code Review Run #803e49 Should Bito avoid suggestions like this for future reviews? (Manage Rules)
|
||||||
| 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, | ||
| ) |
| 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") | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Insecure default database password
The database password configuration uses an insecure default value of 'default' if the environment variable is not set. This poses a significant security risk as it could allow unauthorized access with a weak password. The Code suggestionCheck the AI-generated fix before applying
Suggested change
Code Review Run #803e49 Should Bito avoid suggestions like this for future reviews? (Manage Rules)
|
||||||
| app.config["DB_NAME_1"] = "inventory_db1" | ||||||
| app.config["DB_NAME_2"] = "inventory_db2" | ||||||
| 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"]) |
| 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) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing quantity validation in add_to_cart
The add_to_cart function lacks validation for the quantity parameter, allowing negative or zero values that corrupt cart state. Add a check to ensure quantity > 0 before processing. This prevents invalid quantities from affecting cart totals in Code suggestionCheck the AI-generated fix before applying
Suggested change
Code Review Run #803e49 Should Bito avoid suggestions like this for future reviews? (Manage Rules)
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Race condition in cart quantity updates
The add_to_cart function has a race condition where concurrent requests adding the same product can result in lost quantity updates due to non-atomic operations. Replace the manual quantity increment with atomic database updates using Tortoise's F expression to ensure consistency. This affects downstream cart total calculations in Code suggestionCheck the AI-generated fix before applying
Suggested change
Code Review Run #803e49 Should Bito avoid suggestions like this for future reviews? (Manage Rules)
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @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() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+49
to
+52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing quantity validation in update_cart_item
The update_cart_item function lacks validation for the quantity parameter, allowing negative or zero values that corrupt cart state. Add a check to ensure quantity > 0 before updating. This prevents invalid quantities from affecting cart totals in Code suggestionCheck the AI-generated fix before applying
Suggested change
Code Review Run #803e49 Should Bito avoid suggestions like this for future reviews? (Manage Rules)
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache serialization type mismatch
The inventory data is cached as a string representation of the list, but when retrieved from cache, it's returned as a decoded string instead of the original list structure. This causes type inconsistency where cached responses return a string while non-cached responses return a list of tuples, potentially breaking JSON serialization or client expectations. The Code suggestionCheck the AI-generated fix before applying
Suggested change
Code Review Run #803e49 Should Bito avoid suggestions like this for future reviews? (Manage Rules)
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| 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}" | ||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incomplete cache invalidation on updates
Cache invalidation in Code suggestionCheck the AI-generated fix before applying
Suggested change
Code Review Run #803e49 Should Bito avoid suggestions like this for future reviews? (Manage Rules)
|
||||||||||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+27
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SQL injection vulnerability and formatting issues
SQL query uses f-string which poses SQL injection risk (S608) and has trailing whitespace issues. The query is already using parameterized queries correctly, so remove f-string formatting. Code suggestionCheck the AI-generated fix before applying
Suggested change
Code Review Run #803e49 Should Bito avoid suggestions like this for future reviews? (Manage Rules)
|
||||||||||||||||||||||||||||||||||||||||||||||
| 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) | ||||||||||||||||||||||||||||||||||||||||||||||
| 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 [] | ||||||||||||||||||||
|
Comment on lines
+58
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Broad exception handling and print statement
Catching broad Code suggestionCheck the AI-generated fix before applying
Suggested change
Code Review Run #803e49 Should Bito avoid suggestions like this for future reviews? (Manage Rules)
|
||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| recommendation_engine = RecommendationEngine() | ||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The email regex is overly restrictive and fails to validate emails with multiple dots or underscores in the local part, as highlighted by the bug comment showing
sam_p_le@gma.comincorrectly rejected. This can cause valid user registrations to fail inmain.pywhereis_not_emailis used. Update the regex to a more permissive pattern that allows multiple special characters while maintaining basic validation.Code suggestion
Code Review Run #803e49
Should Bito avoid suggestions like this for future reviews? (Manage Rules)