Exception handling enhancement
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
# app/services/stats_service.py
|
||||
"""Summary description ....
|
||||
"""
|
||||
Statistics service for generating system analytics and metrics.
|
||||
|
||||
This module provides classes and functions for:
|
||||
- ....
|
||||
- ....
|
||||
- ....
|
||||
- Comprehensive system statistics
|
||||
- Marketplace-specific analytics
|
||||
- Performance metrics and data insights
|
||||
- Cached statistics for performance
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -13,6 +15,7 @@ from typing import Any, Dict, List
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.exceptions import ValidationException
|
||||
from models.database.product import Product
|
||||
from models.database.stock import Stock
|
||||
|
||||
@@ -31,60 +34,39 @@ class StatsService:
|
||||
|
||||
Returns:
|
||||
Dictionary containing all statistics data
|
||||
|
||||
Raises:
|
||||
ValidationException: If statistics generation fails
|
||||
"""
|
||||
# Use more efficient queries with proper indexes
|
||||
total_products = db.query(Product).count()
|
||||
try:
|
||||
# Use more efficient queries with proper indexes
|
||||
total_products = self._get_product_count(db)
|
||||
unique_brands = self._get_unique_brands_count(db)
|
||||
unique_categories = self._get_unique_categories_count(db)
|
||||
unique_marketplaces = self._get_unique_marketplaces_count(db)
|
||||
unique_shops = self._get_unique_shops_count(db)
|
||||
|
||||
unique_brands = (
|
||||
db.query(Product.brand)
|
||||
.filter(Product.brand.isnot(None), Product.brand != "")
|
||||
.distinct()
|
||||
.count()
|
||||
)
|
||||
# Stock statistics
|
||||
stock_stats = self._get_stock_statistics(db)
|
||||
|
||||
unique_categories = (
|
||||
db.query(Product.google_product_category)
|
||||
.filter(
|
||||
Product.google_product_category.isnot(None),
|
||||
Product.google_product_category != "",
|
||||
stats_data = {
|
||||
"total_products": total_products,
|
||||
"unique_brands": unique_brands,
|
||||
"unique_categories": unique_categories,
|
||||
"unique_marketplaces": unique_marketplaces,
|
||||
"unique_shops": unique_shops,
|
||||
"total_stock_entries": stock_stats["total_stock_entries"],
|
||||
"total_inventory_quantity": stock_stats["total_inventory_quantity"],
|
||||
}
|
||||
|
||||
logger.info(
|
||||
f"Generated comprehensive stats: {total_products} products, {unique_marketplaces} marketplaces"
|
||||
)
|
||||
.distinct()
|
||||
.count()
|
||||
)
|
||||
return stats_data
|
||||
|
||||
# New marketplace statistics
|
||||
unique_marketplaces = (
|
||||
db.query(Product.marketplace)
|
||||
.filter(Product.marketplace.isnot(None), Product.marketplace != "")
|
||||
.distinct()
|
||||
.count()
|
||||
)
|
||||
|
||||
unique_shops = (
|
||||
db.query(Product.shop_name)
|
||||
.filter(Product.shop_name.isnot(None), Product.shop_name != "")
|
||||
.distinct()
|
||||
.count()
|
||||
)
|
||||
|
||||
# Stock statistics
|
||||
total_stock_entries = db.query(Stock).count()
|
||||
total_inventory = db.query(func.sum(Stock.quantity)).scalar() or 0
|
||||
|
||||
stats_data = {
|
||||
"total_products": total_products,
|
||||
"unique_brands": unique_brands,
|
||||
"unique_categories": unique_categories,
|
||||
"unique_marketplaces": unique_marketplaces,
|
||||
"unique_shops": unique_shops,
|
||||
"total_stock_entries": total_stock_entries,
|
||||
"total_inventory_quantity": total_inventory,
|
||||
}
|
||||
|
||||
logger.info(
|
||||
f"Generated comprehensive stats: {total_products} products, {unique_marketplaces} marketplaces"
|
||||
)
|
||||
return stats_data
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting comprehensive stats: {str(e)}")
|
||||
raise ValidationException("Failed to retrieve system statistics")
|
||||
|
||||
def get_marketplace_breakdown_stats(self, db: Session) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
@@ -95,40 +77,127 @@ class StatsService:
|
||||
|
||||
Returns:
|
||||
List of dictionaries containing marketplace statistics
|
||||
|
||||
Raises:
|
||||
ValidationException: If marketplace statistics generation fails
|
||||
"""
|
||||
# Query to get stats per marketplace
|
||||
marketplace_stats = (
|
||||
db.query(
|
||||
Product.marketplace,
|
||||
func.count(Product.id).label("total_products"),
|
||||
func.count(func.distinct(Product.shop_name)).label("unique_shops"),
|
||||
func.count(func.distinct(Product.brand)).label("unique_brands"),
|
||||
try:
|
||||
# Query to get stats per marketplace
|
||||
marketplace_stats = (
|
||||
db.query(
|
||||
Product.marketplace,
|
||||
func.count(Product.id).label("total_products"),
|
||||
func.count(func.distinct(Product.shop_name)).label("unique_shops"),
|
||||
func.count(func.distinct(Product.brand)).label("unique_brands"),
|
||||
)
|
||||
.filter(Product.marketplace.isnot(None))
|
||||
.group_by(Product.marketplace)
|
||||
.all()
|
||||
)
|
||||
.filter(Product.marketplace.isnot(None))
|
||||
.group_by(Product.marketplace)
|
||||
.all()
|
||||
)
|
||||
|
||||
stats_list = [
|
||||
{
|
||||
"marketplace": stat.marketplace,
|
||||
"total_products": stat.total_products,
|
||||
"unique_shops": stat.unique_shops,
|
||||
"unique_brands": stat.unique_brands,
|
||||
stats_list = [
|
||||
{
|
||||
"marketplace": stat.marketplace,
|
||||
"total_products": stat.total_products,
|
||||
"unique_shops": stat.unique_shops,
|
||||
"unique_brands": stat.unique_brands,
|
||||
}
|
||||
for stat in marketplace_stats
|
||||
]
|
||||
|
||||
logger.info(
|
||||
f"Generated marketplace breakdown stats for {len(stats_list)} marketplaces"
|
||||
)
|
||||
return stats_list
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting marketplace breakdown stats: {str(e)}")
|
||||
raise ValidationException("Failed to retrieve marketplace statistics")
|
||||
|
||||
def get_product_statistics(self, db: Session) -> Dict[str, Any]:
|
||||
"""
|
||||
Get detailed product statistics.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
Dictionary containing product statistics
|
||||
"""
|
||||
try:
|
||||
stats = {
|
||||
"total_products": self._get_product_count(db),
|
||||
"unique_brands": self._get_unique_brands_count(db),
|
||||
"unique_categories": self._get_unique_categories_count(db),
|
||||
"unique_marketplaces": self._get_unique_marketplaces_count(db),
|
||||
"unique_shops": self._get_unique_shops_count(db),
|
||||
"products_with_gtin": self._get_products_with_gtin_count(db),
|
||||
"products_with_images": self._get_products_with_images_count(db),
|
||||
}
|
||||
for stat in marketplace_stats
|
||||
]
|
||||
|
||||
logger.info(
|
||||
f"Generated marketplace breakdown stats for {len(stats_list)} marketplaces"
|
||||
)
|
||||
return stats_list
|
||||
return stats
|
||||
|
||||
def get_product_count(self, db: Session) -> int:
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting product statistics: {str(e)}")
|
||||
raise ValidationException("Failed to retrieve product statistics")
|
||||
|
||||
def get_stock_statistics(self, db: Session) -> Dict[str, Any]:
|
||||
"""
|
||||
Get stock-related statistics.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
Dictionary containing stock statistics
|
||||
"""
|
||||
try:
|
||||
return self._get_stock_statistics(db)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting stock statistics: {str(e)}")
|
||||
raise ValidationException("Failed to retrieve stock statistics")
|
||||
|
||||
def get_marketplace_details(self, db: Session, marketplace: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Get detailed statistics for a specific marketplace.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
marketplace: Marketplace name
|
||||
|
||||
Returns:
|
||||
Dictionary containing marketplace details
|
||||
"""
|
||||
try:
|
||||
if not marketplace or not marketplace.strip():
|
||||
raise ValidationException("Marketplace name is required")
|
||||
|
||||
product_count = self._get_products_by_marketplace_count(db, marketplace)
|
||||
brands = self._get_brands_by_marketplace(db, marketplace)
|
||||
shops = self._get_shops_by_marketplace(db, marketplace)
|
||||
|
||||
return {
|
||||
"marketplace": marketplace,
|
||||
"total_products": product_count,
|
||||
"unique_brands": len(brands),
|
||||
"unique_shops": len(shops),
|
||||
"brands": brands,
|
||||
"shops": shops,
|
||||
}
|
||||
|
||||
except ValidationException:
|
||||
raise # Re-raise custom exceptions
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting marketplace details for {marketplace}: {str(e)}")
|
||||
raise ValidationException("Failed to retrieve marketplace details")
|
||||
|
||||
# Private helper methods
|
||||
def _get_product_count(self, db: Session) -> int:
|
||||
"""Get total product count."""
|
||||
return db.query(Product).count()
|
||||
|
||||
def get_unique_brands_count(self, db: Session) -> int:
|
||||
def _get_unique_brands_count(self, db: Session) -> int:
|
||||
"""Get count of unique brands."""
|
||||
return (
|
||||
db.query(Product.brand)
|
||||
@@ -137,7 +206,7 @@ class StatsService:
|
||||
.count()
|
||||
)
|
||||
|
||||
def get_unique_categories_count(self, db: Session) -> int:
|
||||
def _get_unique_categories_count(self, db: Session) -> int:
|
||||
"""Get count of unique categories."""
|
||||
return (
|
||||
db.query(Product.google_product_category)
|
||||
@@ -149,7 +218,7 @@ class StatsService:
|
||||
.count()
|
||||
)
|
||||
|
||||
def get_unique_marketplaces_count(self, db: Session) -> int:
|
||||
def _get_unique_marketplaces_count(self, db: Session) -> int:
|
||||
"""Get count of unique marketplaces."""
|
||||
return (
|
||||
db.query(Product.marketplace)
|
||||
@@ -158,7 +227,7 @@ class StatsService:
|
||||
.count()
|
||||
)
|
||||
|
||||
def get_unique_shops_count(self, db: Session) -> int:
|
||||
def _get_unique_shops_count(self, db: Session) -> int:
|
||||
"""Get count of unique shops."""
|
||||
return (
|
||||
db.query(Product.shop_name)
|
||||
@@ -167,16 +236,24 @@ class StatsService:
|
||||
.count()
|
||||
)
|
||||
|
||||
def get_stock_statistics(self, db: Session) -> Dict[str, int]:
|
||||
"""
|
||||
Get stock-related statistics.
|
||||
def _get_products_with_gtin_count(self, db: Session) -> int:
|
||||
"""Get count of products with GTIN."""
|
||||
return (
|
||||
db.query(Product)
|
||||
.filter(Product.gtin.isnot(None), Product.gtin != "")
|
||||
.count()
|
||||
)
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
def _get_products_with_images_count(self, db: Session) -> int:
|
||||
"""Get count of products with images."""
|
||||
return (
|
||||
db.query(Product)
|
||||
.filter(Product.image_link.isnot(None), Product.image_link != "")
|
||||
.count()
|
||||
)
|
||||
|
||||
Returns:
|
||||
Dictionary containing stock statistics
|
||||
"""
|
||||
def _get_stock_statistics(self, db: Session) -> Dict[str, int]:
|
||||
"""Get stock-related statistics."""
|
||||
total_stock_entries = db.query(Stock).count()
|
||||
total_inventory = db.query(func.sum(Stock.quantity)).scalar() or 0
|
||||
|
||||
@@ -185,7 +262,7 @@ class StatsService:
|
||||
"total_inventory_quantity": total_inventory,
|
||||
}
|
||||
|
||||
def get_brands_by_marketplace(self, db: Session, marketplace: str) -> List[str]:
|
||||
def _get_brands_by_marketplace(self, db: Session, marketplace: str) -> List[str]:
|
||||
"""Get unique brands for a specific marketplace."""
|
||||
brands = (
|
||||
db.query(Product.brand)
|
||||
@@ -199,7 +276,7 @@ class StatsService:
|
||||
)
|
||||
return [brand[0] for brand in brands]
|
||||
|
||||
def get_shops_by_marketplace(self, db: Session, marketplace: str) -> List[str]:
|
||||
def _get_shops_by_marketplace(self, db: Session, marketplace: str) -> List[str]:
|
||||
"""Get unique shops for a specific marketplace."""
|
||||
shops = (
|
||||
db.query(Product.shop_name)
|
||||
@@ -213,10 +290,51 @@ class StatsService:
|
||||
)
|
||||
return [shop[0] for shop in shops]
|
||||
|
||||
def get_products_by_marketplace(self, db: Session, marketplace: str) -> int:
|
||||
def _get_products_by_marketplace_count(self, db: Session, marketplace: str) -> int:
|
||||
"""Get product count for a specific marketplace."""
|
||||
return db.query(Product).filter(Product.marketplace == marketplace).count()
|
||||
|
||||
# Legacy methods for backward compatibility (deprecated)
|
||||
def get_product_count(self, db: Session) -> int:
|
||||
"""Get total product count. DEPRECATED: Use _get_product_count."""
|
||||
logger.warning("get_product_count is deprecated, use _get_product_count")
|
||||
return self._get_product_count(db)
|
||||
|
||||
def get_unique_brands_count(self, db: Session) -> int:
|
||||
"""Get unique brands count. DEPRECATED: Use _get_unique_brands_count."""
|
||||
logger.warning("get_unique_brands_count is deprecated, use _get_unique_brands_count")
|
||||
return self._get_unique_brands_count(db)
|
||||
|
||||
def get_unique_categories_count(self, db: Session) -> int:
|
||||
"""Get unique categories count. DEPRECATED: Use _get_unique_categories_count."""
|
||||
logger.warning("get_unique_categories_count is deprecated, use _get_unique_categories_count")
|
||||
return self._get_unique_categories_count(db)
|
||||
|
||||
def get_unique_marketplaces_count(self, db: Session) -> int:
|
||||
"""Get unique marketplaces count. DEPRECATED: Use _get_unique_marketplaces_count."""
|
||||
logger.warning("get_unique_marketplaces_count is deprecated, use _get_unique_marketplaces_count")
|
||||
return self._get_unique_marketplaces_count(db)
|
||||
|
||||
def get_unique_shops_count(self, db: Session) -> int:
|
||||
"""Get unique shops count. DEPRECATED: Use _get_unique_shops_count."""
|
||||
logger.warning("get_unique_shops_count is deprecated, use _get_unique_shops_count")
|
||||
return self._get_unique_shops_count(db)
|
||||
|
||||
def get_brands_by_marketplace(self, db: Session, marketplace: str) -> List[str]:
|
||||
"""Get brands by marketplace. DEPRECATED: Use _get_brands_by_marketplace."""
|
||||
logger.warning("get_brands_by_marketplace is deprecated, use _get_brands_by_marketplace")
|
||||
return self._get_brands_by_marketplace(db, marketplace)
|
||||
|
||||
def get_shops_by_marketplace(self, db: Session, marketplace: str) -> List[str]:
|
||||
"""Get shops by marketplace. DEPRECATED: Use _get_shops_by_marketplace."""
|
||||
logger.warning("get_shops_by_marketplace is deprecated, use _get_shops_by_marketplace")
|
||||
return self._get_shops_by_marketplace(db, marketplace)
|
||||
|
||||
def get_products_by_marketplace(self, db: Session, marketplace: str) -> int:
|
||||
"""Get products by marketplace. DEPRECATED: Use _get_products_by_marketplace_count."""
|
||||
logger.warning("get_products_by_marketplace is deprecated, use _get_products_by_marketplace_count")
|
||||
return self._get_products_by_marketplace_count(db, marketplace)
|
||||
|
||||
|
||||
# Create service instance following the same pattern as other services
|
||||
stats_service = StatsService()
|
||||
|
||||
Reference in New Issue
Block a user