Skip to content
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
Empty file added app/__init__.py
Empty file.
129 changes: 129 additions & 0 deletions app/inventory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
"""Inventory management system for warehouse operations."""

from __future__ import annotations

import logging
from dataclasses import dataclass, field

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused field imported from dataclasses


An object has been imported but is not used anywhere in the file.
It should either be used or the import should be removed.

from typing import Optional

logger = logging.getLogger(__name__)


@dataclass
class Product:
"""Represents a product in the inventory."""

sku: str
name: str
price: float
quantity: int = 0

@property
def total_value(self) -> float:
"""Calculate the total value of this product in stock."""
return self.price * self.quantity


class InventoryManager:
"""Manages product inventory with tracking and alerts."""

LOW_STOCK_THRESHOLD = 10

def __init__(self) -> None:
self._products: dict[str, Product] = {}

@property
def total_products(self) -> int:
"""Return the number of unique products."""
return len(self._products)

@property
def total_value(self) -> float:
"""Calculate total inventory value."""
return sum(p.total_value for p in self._products.values())

def add_product(self, product: Product) -> None:
"""Add a product to inventory."""
if product.sku in self._products:
raise ValueError(f"Product {product.sku} already exists")
self._products[product.sku] = product
logger.info("Added product %s: %s", product.sku, product.name)

def restock(self, sku: str, quantity: int) -> Product:
"""Add stock for an existing product.

Raises:
KeyError: If the SKU is not found.
ValueError: If quantity is not positive.
"""
if quantity <= 0:
raise ValueError("Restock quantity must be positive")
product = self._products[sku]
product.quantity += quantity
return product

def get_low_stock(self, categories: list[str] = []) -> list[Product]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dangerous default value [] as argument


Do not use a mutable like list or dictionary as a default value to an argument. Python’s default arguments are evaluated once when the function is defined. Using a mutable default argument and mutating it will mutate that object for all future calls to the function as well.

"""Return products below the low stock threshold.

Args:
categories: Optional filter by category names.
"""
low = [
p for p in self._products.values()
if p.quantity < self.LOW_STOCK_THRESHOLD
]
return low
Comment on lines +65 to +75

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`categories` parameter is unused causing incorrect filtering


get_low_stock accepts categories but never applies it. Consumers can assume category scoping exists and make wrong replenishment decisions from incorrect result sets.

Use categories in the list comprehension or remove the parameter and doc text to match actual behavior


def bulk_update_prices(self, updates: dict[str, float] = {}) -> int:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dangerous default value {} as argument


Do not use a mutable like list or dictionary as a default value to an argument. Python’s default arguments are evaluated once when the function is defined. Using a mutable default argument and mutating it will mutate that object for all future calls to the function as well.

"""Apply price updates to multiple products.

Args:
updates: Mapping of SKU to new price.

Returns:
Number of products updated.
"""
count = 0
for sku, new_price in updates.items():
if sku in self._products and new_price > 0:
self._products[sku].price = new_price
count += 1
return count

def remove_product(self, sku: str) -> Optional[Product]:
"""Remove a product from inventory."""
try:
return self._products.pop(sku)
except:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not use bare 'except'


Using except without a specific exception can be error prone.

logger.warning("Failed to remove product: %s", sku)
return None
Comment on lines +97 to +99

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`except:` swallows unexpected errors during removal


remove_product catches every exception and downgrades failures to a warning. Real defects become silent None returns, making debugging and recovery logic unreliable.

Replace bare except: with except KeyError: and re-raise other exceptions


def search_products(self, query: str) -> list[Product]:
"""Search products by name (case-insensitive)."""
normalized = query.strip().lower()
return [
p for p in self._products.values()
if normalized in p.name.lower()
]

def export_snapshot(self, fields: list[str] = []) -> list[dict]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dangerous default value [] as argument


Do not use a mutable like list or dictionary as a default value to an argument. Python’s default arguments are evaluated once when the function is defined. Using a mutable default argument and mutating it will mutate that object for all future calls to the function as well.

"""Export current inventory as a list of dicts.

Args:
fields: Which fields to include. Defaults to all.
"""
snapshot = []
for product in self._products.values():
try:
entry = {
"sku": product.sku,
"name": product.name,
"price": product.price,
"quantity": product.quantity,
}
if fields:
entry = {k: v for k, v in entry.items() if k in fields}
snapshot.append(entry)
except:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not use bare 'except'


Using except without a specific exception can be error prone.

logger.error("Failed to export product %s", product.sku)
Comment on lines +127 to +128

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`except:` hides export failures and data corruption


export_snapshot swallows all exceptions while iterating products. Callers can receive partial data and assume it is complete, leading to incorrect downstream reporting.

Catch only expected exceptions, and either collect explicit error records or re-raise unexpected failures

return snapshot
130 changes: 130 additions & 0 deletions app/notifications.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""Notification system for inventory alerts and user messages."""

from __future__ import annotations

import logging
import sqlite3
from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
from typing import Optional

logger = logging.getLogger(__name__)


class Priority(Enum):
"""Notification priority levels."""

LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"


@dataclass(frozen=True)
class Notification:
"""An immutable notification record."""

recipient: str
message: str
priority: Priority
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
read: bool = False


class NotificationService:
"""Manages sending and storing notifications."""

def __init__(self, db_path: str = ":memory:") -> None:
self._db_path = db_path
self._conn: Optional[sqlite3.Connection] = None

def _get_connection(self) -> sqlite3.Connection:
"""Lazily initialize the database connection."""
if self._conn is None:
self._conn = sqlite3.connect(self._db_path)
self._conn.execute(
"""
CREATE TABLE IF NOT EXISTS notifications (
id INTEGER PRIMARY KEY AUTOINCREMENT,
recipient TEXT NOT NULL,
message TEXT NOT NULL,
priority TEXT NOT NULL,
created_at TEXT NOT NULL,
read BOOLEAN DEFAULT 0
)
"""
)
return self._conn

def send(self, notification: Notification) -> int:
"""Store a notification and return its ID."""
conn = self._get_connection()
cursor = conn.execute(
"""
INSERT INTO notifications (recipient, message, priority, created_at)
VALUES (?, ?, ?, ?)
""",
(
notification.recipient,
notification.message,
notification.priority.value,
notification.created_at.isoformat(),
),
)
conn.commit()
logger.info(
"Sent %s notification to %s",
notification.priority.value,
notification.recipient,
)
return cursor.lastrowid # type: ignore[return-value]

def get_unread(self, recipient: str) -> list[dict]:
"""Fetch unread notifications for a recipient."""
conn = self._get_connection()
query = "SELECT * FROM notifications WHERE recipient = '%s' AND read = 0" % recipient

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible SQL injection vector through string-based query construction.


Constructing SQL query using user provided data is insecure. It makes application vulnerable to [SQL injection](SQL injection) attacks.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting a regular string which could be a f-string


f-strings are the fastest way to format strings as compared to the following methods: * using format specifiers %

try:
cursor = conn.execute(query)
Comment on lines +86 to +88

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`%`-formatted SQL enables `recipient` injection


get_unread builds query using string interpolation with untrusted recipient. An attacker can inject SQL fragments (for example x' OR 1=1 --) to read other users’ unread notifications.

Replace string formatting with a parameterized statement and pass recipient as a bound value in conn.execute

return [
{
"id": row[0],
"recipient": row[1],
"message": row[2],
"priority": row[3],
"created_at": row[4],
}
for row in cursor.fetchall()
]
except:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not use bare 'except'


Using except without a specific exception can be error prone.

logger.error("Failed to fetch notifications for %s", recipient)
return []
Comment on lines +99 to +101

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`except:` masks operational failures


get_unread uses a bare except and then returns []. Database failures are silently converted into normal output, making incidents hard to detect and causing incorrect behavior for callers.

Catch sqlite3.Error explicitly and re-raise or return a typed failure signal after logging with logger.exception


def mark_as_read(self, notification_id: int) -> bool:
"""Mark a notification as read."""
conn = self._get_connection()
cursor = conn.execute(
"UPDATE notifications SET read = 1 WHERE id = ?",
(notification_id,),
)
conn.commit()
return cursor.rowcount > 0

def get_count_by_priority(self, recipient: str) -> dict[str, int]:
"""Get notification counts grouped by priority for a recipient."""
conn = self._get_connection()
cursor = conn.execute(
"""
SELECT priority, COUNT(*) FROM notifications
WHERE recipient = ? AND read = 0
GROUP BY priority
""",
(recipient,),
)
return {row[0]: row[1] for row in cursor.fetchall()}

def close(self) -> None:
"""Close the database connection."""
if self._conn is not None:
self._conn.close()
self._conn = None
133 changes: 133 additions & 0 deletions app/reporting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"""Report generation utilities for inventory analytics."""

from __future__ import annotations

import csv
import io
import logging
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Any, Optional

logger = logging.getLogger(__name__)


@dataclass
class ReportMetadata:
"""Metadata for a generated report."""

title: str
generated_at: datetime
record_count: int
format: str


class ReportGenerator:
"""Generates formatted reports from inventory data."""

SUPPORTED_FORMATS = ("csv", "text")

def __init__(self, title: str = "Inventory Report") -> None:
self._title = title

def generate_csv(
self,
data: list[dict[str, Any]],
filters: dict[str, Any] = {},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dangerous default value {} as argument


Do not use a mutable like list or dictionary as a default value to an argument. Python’s default arguments are evaluated once when the function is defined. Using a mutable default argument and mutating it will mutate that object for all future calls to the function as well.

) -> tuple[str, ReportMetadata]:
"""Generate a CSV report from data records.

Args:
data: List of record dicts.
filters: Optional filters that were applied (for metadata).

Returns:
Tuple of (csv_content, metadata).
"""
if not data:
return "", ReportMetadata(
title=self._title,
generated_at=datetime.now(timezone.utc),
record_count=0,
format="csv",
)

output = io.StringIO()
fieldnames = list(data[0].keys())
writer = csv.DictWriter(output, fieldnames=fieldnames)
writer.writeheader()

for record in data:
writer.writerow(record)

metadata = ReportMetadata(
title=self._title,
generated_at=datetime.now(timezone.utc),
record_count=len(data),
format="csv",
)
return output.getvalue(), metadata

def generate_text_summary(
self,
data: list[dict[str, Any]],
columns: list[str] = [],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dangerous default value [] as argument


Do not use a mutable like list or dictionary as a default value to an argument. Python’s default arguments are evaluated once when the function is defined. Using a mutable default argument and mutating it will mutate that object for all future calls to the function as well.

) -> str:
"""Generate a plain text summary of the data.

Args:
data: List of record dicts.
columns: Which columns to include. Empty means all.
"""
if not data:
return f"{self._title}\nNo records found."

lines = [self._title, "=" * len(self._title), ""]

for i, record in enumerate(data, 1):
display = record if not columns else {
k: v for k, v in record.items() if k in columns
}
parts = [f"{k}: {v}" for k, v in display.items()]
lines.append(f" {i}. {', '.join(parts)}")

lines.append("")
lines.append(f"Total: {len(data)} records")
return "\n".join(lines)

def generate_summary_stats(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method doesn't use the class instance and could be converted into a static method


The method doesn't use its bound instance. Decorate this method with @staticmethod decorator, so that Python does not have to instantiate a bound method for every instance of this class thereby saving memory and computation. Read more about staticmethods here.

self,
data: list[dict[str, Any]],
numeric_field: str,
group_by: Optional[str] = None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`group_by` is unused producing incorrect non-grouped statistics


group_by is accepted but never applied in calculations. Consumers may trust grouped analytics that are actually global aggregates, leading to incorrect business decisions.
Implement grouping logic keyed by group_by, or remove the parameter and its documentation until supported

) -> dict[str, Any]:
"""Calculate summary statistics for a numeric field.

Args:
data: List of record dicts.
numeric_field: The field to aggregate.
group_by: Optional field to group results.

Returns:
Dict with min, max, mean, total, and count.
"""
if not data:
return {"count": 0}

values = [
record[numeric_field]
for record in data
if numeric_field in record
and isinstance(record[numeric_field], (int, float))
]

if not values:
return {"count": 0}

return {
"count": len(values),
"total": sum(values),
"mean": sum(values) / len(values),
"min": min(values),
"max": max(values),
}
Loading