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

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.

Bare `except` masks non-recoverable runtime failures


A bare except catches KeyboardInterrupt and SystemExit alongside expected lookup errors. This hides real defects and can keep the process running in corrupted states while callers only see None.

Catch KeyError explicitly and log unexpected exceptions separately with except Exception before re-raising.


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.

Bare `except` hides export data corruption causes


Catching every exception makes export reliability opaque and can silently lose inventory records. Operators only get a generic log message, while the root error and stack context are discarded.

Use except Exception as exc with logger.exception(...), and consider re-raising or collecting failed SKUs for explicit caller handling.

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 %

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

`%` SQL formatting enables `recipient` query injection


get_unread constructs query with % interpolation of untrusted recipient. Attackers can alter the WHERE clause and read notifications not belonging to them.

Replace string formatting with parameterized SQL using ? placeholders and pass (recipient,) to execute

try:
cursor = conn.execute(query)
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:` hides runtime failures and returns stale behavior


get_unread swallows every exception and returns [], making real database or schema failures look like no unread notifications. This can suppress alerts and mislead downstream logic.

Replace except: with except sqlite3.Error as exc, log exc, and re-raise or return a typed error outcome


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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Mutable `filters={}` default risks shared state bugs


filters uses a mutable default object. Even though current code does not mutate it, later edits can accidentally persist values across calls and create hard-to-debug cross-request coupling.

Replace with None and initialize inside the method using filters = {} if filters is None else filters.

) -> 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.

Unused `group_by` parameter breaks advertised grouped statistics


The API accepts group_by but never applies grouping. Callers expecting per-group metrics silently receive global numbers, which can corrupt dashboards and downstream decisions.

Implement grouping when group_by is provided, or remove the parameter and update docstrings to match actual behavior.

) -> 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