Stats management revamping
This commit is contained in:
@@ -12,6 +12,7 @@ from sqlalchemy.orm import Session
|
|||||||
from app.api.deps import get_current_admin_user
|
from app.api.deps import get_current_admin_user
|
||||||
from app.core.database import get_db
|
from app.core.database import get_db
|
||||||
from app.services.admin_service import admin_service
|
from app.services.admin_service import admin_service
|
||||||
|
from app.services.stats_service import stats_service
|
||||||
from models.schema.marketplace_import_job import MarketplaceImportJobResponse
|
from models.schema.marketplace_import_job import MarketplaceImportJobResponse
|
||||||
from models.database.user import User
|
from models.database.user import User
|
||||||
|
|
||||||
@@ -46,4 +47,4 @@ def get_import_statistics(
|
|||||||
current_admin: User = Depends(get_current_admin_user),
|
current_admin: User = Depends(get_current_admin_user),
|
||||||
):
|
):
|
||||||
"""Get marketplace import statistics (Admin only)."""
|
"""Get marketplace import statistics (Admin only)."""
|
||||||
return admin_service.get_import_statistics(db)
|
return stats_service.get_import_statistics(db)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from sqlalchemy.orm import Session
|
|||||||
from app.api.deps import get_current_admin_user
|
from app.api.deps import get_current_admin_user
|
||||||
from app.core.database import get_db
|
from app.core.database import get_db
|
||||||
from app.services.admin_service import admin_service
|
from app.services.admin_service import admin_service
|
||||||
|
from app.services.stats_service import stats_service
|
||||||
from models.schema.auth import UserResponse
|
from models.schema.auth import UserResponse
|
||||||
from models.database.user import User
|
from models.database.user import User
|
||||||
|
|
||||||
@@ -48,4 +49,4 @@ def get_user_statistics(
|
|||||||
current_admin: User = Depends(get_current_admin_user),
|
current_admin: User = Depends(get_current_admin_user),
|
||||||
):
|
):
|
||||||
"""Get user statistics for admin dashboard (Admin only)."""
|
"""Get user statistics for admin dashboard (Admin only)."""
|
||||||
return admin_service.get_user_statistics(db)
|
return stats_service.get_user_statistics(db)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from sqlalchemy.orm import Session
|
|||||||
from app.api.deps import get_current_admin_user
|
from app.api.deps import get_current_admin_user
|
||||||
from app.core.database import get_db
|
from app.core.database import get_db
|
||||||
from app.services.admin_service import admin_service
|
from app.services.admin_service import admin_service
|
||||||
|
from models.schema.stats import VendorStatsResponse
|
||||||
from models.schema.vendor import (
|
from models.schema.vendor import (
|
||||||
VendorListResponse,
|
VendorListResponse,
|
||||||
VendorResponse,
|
VendorResponse,
|
||||||
@@ -106,6 +107,21 @@ def get_all_vendors_admin(
|
|||||||
return VendorListResponse(vendors=vendors, total=total, skip=skip, limit=limit)
|
return VendorListResponse(vendors=vendors, total=total, skip=skip, limit=limit)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/stats", response_model=VendorStatsResponse)
|
||||||
|
def get_vendor_statistics(
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
current_admin: User = Depends(get_current_admin_user),
|
||||||
|
):
|
||||||
|
"""Get vendor statistics for admin dashboard (Admin only)."""
|
||||||
|
stats = get_vendor_statistics(db)
|
||||||
|
|
||||||
|
return VendorStatsResponse(
|
||||||
|
total=stats["total_vendors"],
|
||||||
|
verified=stats["verified_vendors"],
|
||||||
|
pending=stats["total_vendors"] - stats["verified_vendors"],
|
||||||
|
inactive=stats["inactive_vendors"],
|
||||||
|
)
|
||||||
|
|
||||||
@router.get("/{vendor_id}", response_model=VendorDetailResponse)
|
@router.get("/{vendor_id}", response_model=VendorDetailResponse)
|
||||||
def get_vendor_details(
|
def get_vendor_details(
|
||||||
vendor_id: int,
|
vendor_id: int,
|
||||||
@@ -296,11 +312,3 @@ def delete_vendor(
|
|||||||
message = admin_service.delete_vendor(db, vendor_id)
|
message = admin_service.delete_vendor(db, vendor_id)
|
||||||
return {"message": message}
|
return {"message": message}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/stats/vendors")
|
|
||||||
def get_vendor_statistics(
|
|
||||||
db: Session = Depends(get_db),
|
|
||||||
current_admin: User = Depends(get_current_admin_user),
|
|
||||||
):
|
|
||||||
"""Get vendor statistics for admin dashboard (Admin only)."""
|
|
||||||
return admin_service.get_vendor_statistics(db)
|
|
||||||
|
|||||||
@@ -621,50 +621,6 @@ class AdminService:
|
|||||||
# STATISTICS
|
# STATISTICS
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
def get_user_statistics(self, db: Session) -> dict:
|
|
||||||
"""Get user statistics for admin dashboard."""
|
|
||||||
try:
|
|
||||||
total_users = db.query(User).count()
|
|
||||||
active_users = db.query(User).filter(User.is_active == True).count()
|
|
||||||
inactive_users = total_users - active_users
|
|
||||||
admin_users = db.query(User).filter(User.role == "admin").count()
|
|
||||||
|
|
||||||
return {
|
|
||||||
"total_users": total_users,
|
|
||||||
"active_users": active_users,
|
|
||||||
"inactive_users": inactive_users,
|
|
||||||
"admin_users": admin_users,
|
|
||||||
"activation_rate": (active_users / total_users * 100) if total_users > 0 else 0
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to get user statistics: {str(e)}")
|
|
||||||
raise AdminOperationException(
|
|
||||||
operation="get_user_statistics",
|
|
||||||
reason="Database query failed"
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_vendor_statistics(self, db: Session) -> dict:
|
|
||||||
"""Get vendor statistics for admin dashboard."""
|
|
||||||
try:
|
|
||||||
total_vendors = db.query(Vendor).count()
|
|
||||||
active_vendors = db.query(Vendor).filter(Vendor.is_active == True).count()
|
|
||||||
verified_vendors = db.query(Vendor).filter(Vendor.is_verified == True).count()
|
|
||||||
inactive_vendors = total_vendors - active_vendors
|
|
||||||
|
|
||||||
return {
|
|
||||||
"total_vendors": total_vendors,
|
|
||||||
"active_vendors": active_vendors,
|
|
||||||
"inactive_vendors": inactive_vendors,
|
|
||||||
"verified_vendors": verified_vendors,
|
|
||||||
"verification_rate": (verified_vendors / total_vendors * 100) if total_vendors > 0 else 0
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to get vendor statistics: {str(e)}")
|
|
||||||
raise AdminOperationException(
|
|
||||||
operation="get_vendor_statistics",
|
|
||||||
reason="Database query failed"
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_recent_vendors(self, db: Session, limit: int = 5) -> List[dict]:
|
def get_recent_vendors(self, db: Session, limit: int = 5) -> List[dict]:
|
||||||
"""Get recently created vendors."""
|
"""Get recently created vendors."""
|
||||||
try:
|
try:
|
||||||
@@ -716,45 +672,6 @@ class AdminService:
|
|||||||
logger.error(f"Failed to get recent import jobs: {str(e)}")
|
logger.error(f"Failed to get recent import jobs: {str(e)}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_product_statistics(self, db: Session) -> dict:
|
|
||||||
"""Get product statistics."""
|
|
||||||
# TODO: Implement when Product model is available
|
|
||||||
return {
|
|
||||||
"total_products": 0,
|
|
||||||
"active_products": 0,
|
|
||||||
"out_of_stock": 0
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_order_statistics(self, db: Session) -> dict:
|
|
||||||
"""Get order statistics."""
|
|
||||||
# TODO: Implement when Order model is available
|
|
||||||
return {
|
|
||||||
"total_orders": 0,
|
|
||||||
"pending_orders": 0,
|
|
||||||
"completed_orders": 0
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_import_statistics(self, db: Session) -> dict:
|
|
||||||
"""Get import job statistics."""
|
|
||||||
try:
|
|
||||||
total = db.query(MarketplaceImportJob).count()
|
|
||||||
completed = db.query(MarketplaceImportJob).filter(
|
|
||||||
MarketplaceImportJob.status == "completed"
|
|
||||||
).count()
|
|
||||||
failed = db.query(MarketplaceImportJob).filter(
|
|
||||||
MarketplaceImportJob.status == "failed"
|
|
||||||
).count()
|
|
||||||
|
|
||||||
return {
|
|
||||||
"total_imports": total,
|
|
||||||
"completed_imports": completed,
|
|
||||||
"failed_imports": failed,
|
|
||||||
"success_rate": (completed / total * 100) if total > 0 else 0
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to get import statistics: {str(e)}")
|
|
||||||
return {"total_imports": 0, "completed_imports": 0, "failed_imports": 0, "success_rate": 0}
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# PRIVATE HELPER METHODS
|
# PRIVATE HELPER METHODS
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ from app.exceptions import (
|
|||||||
VendorNotFoundException,
|
VendorNotFoundException,
|
||||||
AdminOperationException,
|
AdminOperationException,
|
||||||
)
|
)
|
||||||
|
|
||||||
from models.database.marketplace_product import MarketplaceProduct
|
from models.database.marketplace_product import MarketplaceProduct
|
||||||
from models.database.product import Product
|
from models.database.product import Product
|
||||||
from models.database.inventory import Inventory
|
from models.database.inventory import Inventory
|
||||||
from models.database.vendor import Vendor
|
from models.database.vendor import Vendor
|
||||||
from models.database.order import Order
|
from models.database.order import Order
|
||||||
|
from models.database.user import User
|
||||||
from models.database.customer import Customer
|
from models.database.customer import Customer
|
||||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||||
|
|
||||||
@@ -158,7 +160,7 @@ class StatsService:
|
|||||||
self, db: Session, vendor_id: int, period: str = "30d"
|
self, db: Session, vendor_id: int, period: str = "30d"
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Get vendor analytics for a time period.
|
Get a specific vendor analytics for a time period.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
db: Database session
|
db: Database session
|
||||||
@@ -224,6 +226,28 @@ class StatsService:
|
|||||||
target_id=str(vendor_id)
|
target_id=str(vendor_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_vendor_statistics(self, db: Session) -> dict:
|
||||||
|
"""Get vendor statistics for admin dashboard."""
|
||||||
|
try:
|
||||||
|
total_vendors = db.query(Vendor).count()
|
||||||
|
active_vendors = db.query(Vendor).filter(Vendor.is_active == True).count()
|
||||||
|
verified_vendors = db.query(Vendor).filter(Vendor.is_verified == True).count()
|
||||||
|
inactive_vendors = total_vendors - active_vendors
|
||||||
|
|
||||||
|
return {
|
||||||
|
"total_vendors": total_vendors,
|
||||||
|
"active_vendors": active_vendors,
|
||||||
|
"inactive_vendors": inactive_vendors,
|
||||||
|
"verified_vendors": verified_vendors,
|
||||||
|
"verification_rate": (verified_vendors / total_vendors * 100) if total_vendors > 0 else 0
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to get vendor statistics: {str(e)}")
|
||||||
|
raise AdminOperationException(
|
||||||
|
operation="get_vendor_statistics",
|
||||||
|
reason="Database query failed"
|
||||||
|
)
|
||||||
|
|
||||||
# ========================================================================
|
# ========================================================================
|
||||||
# SYSTEM-WIDE STATISTICS (ADMIN)
|
# SYSTEM-WIDE STATISTICS (ADMIN)
|
||||||
# ========================================================================
|
# ========================================================================
|
||||||
@@ -321,12 +345,128 @@ class StatsService:
|
|||||||
reason=f"Database query failed: {str(e)}"
|
reason=f"Database query failed: {str(e)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_user_statistics(self, db: Session) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Get user statistics for admin dashboard.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db: Database session
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary with user statistics
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AdminOperationException: If database query fails
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
total_users = db.query(User).count()
|
||||||
|
active_users = db.query(User).filter(User.is_active == True).count()
|
||||||
|
inactive_users = total_users - active_users
|
||||||
|
admin_users = db.query(User).filter(User.role == "admin").count()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"total_users": total_users,
|
||||||
|
"active_users": active_users,
|
||||||
|
"inactive_users": inactive_users,
|
||||||
|
"admin_users": admin_users,
|
||||||
|
"activation_rate": (active_users / total_users * 100) if total_users > 0 else 0
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to get user statistics: {str(e)}")
|
||||||
|
raise AdminOperationException(
|
||||||
|
operation="get_user_statistics",
|
||||||
|
reason="Database query failed"
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_import_statistics(self, db: Session) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Get import job statistics.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db: Database session
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary with import statistics
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AdminOperationException: If database query fails
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
total = db.query(MarketplaceImportJob).count()
|
||||||
|
completed = db.query(MarketplaceImportJob).filter(
|
||||||
|
MarketplaceImportJob.status == "completed"
|
||||||
|
).count()
|
||||||
|
failed = db.query(MarketplaceImportJob).filter(
|
||||||
|
MarketplaceImportJob.status == "failed"
|
||||||
|
).count()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"total_imports": total,
|
||||||
|
"completed_imports": completed,
|
||||||
|
"failed_imports": failed,
|
||||||
|
"success_rate": (completed / total * 100) if total > 0 else 0
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to get import statistics: {str(e)}")
|
||||||
|
return {
|
||||||
|
"total_imports": 0,
|
||||||
|
"completed_imports": 0,
|
||||||
|
"failed_imports": 0,
|
||||||
|
"success_rate": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_order_statistics(self, db: Session) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Get order statistics.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db: Database session
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary with order statistics
|
||||||
|
|
||||||
|
Note:
|
||||||
|
TODO: Implement when Order model is fully available
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"total_orders": 0,
|
||||||
|
"pending_orders": 0,
|
||||||
|
"completed_orders": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_product_statistics(self, db: Session) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Get product statistics.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db: Database session
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary with product statistics
|
||||||
|
|
||||||
|
Note:
|
||||||
|
TODO: Implement when Product model is fully available
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"total_products": 0,
|
||||||
|
"active_products": 0,
|
||||||
|
"out_of_stock": 0
|
||||||
|
}
|
||||||
|
|
||||||
# ========================================================================
|
# ========================================================================
|
||||||
# PRIVATE HELPER METHODS
|
# PRIVATE HELPER METHODS
|
||||||
# ========================================================================
|
# ========================================================================
|
||||||
|
|
||||||
def _parse_period(self, period: str) -> int:
|
def _parse_period(self, period: str) -> int:
|
||||||
"""Parse period string to days."""
|
"""
|
||||||
|
Parse period string to days.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
period: Period string (7d, 30d, 90d, 1y)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Number of days
|
||||||
|
"""
|
||||||
period_map = {
|
period_map = {
|
||||||
"7d": 7,
|
"7d": 7,
|
||||||
"30d": 30,
|
"30d": 30,
|
||||||
@@ -336,7 +476,15 @@ class StatsService:
|
|||||||
return period_map.get(period, 30)
|
return period_map.get(period, 30)
|
||||||
|
|
||||||
def _get_unique_brands_count(self, db: Session) -> int:
|
def _get_unique_brands_count(self, db: Session) -> int:
|
||||||
"""Get count of unique brands."""
|
"""
|
||||||
|
Get count of unique brands.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db: Database session
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Count of unique brands
|
||||||
|
"""
|
||||||
return (
|
return (
|
||||||
db.query(MarketplaceProduct.brand)
|
db.query(MarketplaceProduct.brand)
|
||||||
.filter(
|
.filter(
|
||||||
@@ -348,7 +496,15 @@ class StatsService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _get_unique_categories_count(self, db: Session) -> int:
|
def _get_unique_categories_count(self, db: Session) -> int:
|
||||||
"""Get count of unique categories."""
|
"""
|
||||||
|
Get count of unique categories.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db: Database session
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Count of unique categories
|
||||||
|
"""
|
||||||
return (
|
return (
|
||||||
db.query(MarketplaceProduct.google_product_category)
|
db.query(MarketplaceProduct.google_product_category)
|
||||||
.filter(
|
.filter(
|
||||||
@@ -360,7 +516,15 @@ class StatsService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _get_inventory_statistics(self, db: Session) -> Dict[str, int]:
|
def _get_inventory_statistics(self, db: Session) -> Dict[str, int]:
|
||||||
"""Get inventory-related statistics."""
|
"""
|
||||||
|
Get inventory-related statistics.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db: Database session
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary with inventory statistics
|
||||||
|
"""
|
||||||
total_entries = db.query(Inventory).count()
|
total_entries = db.query(Inventory).count()
|
||||||
total_quantity = db.query(func.sum(Inventory.quantity)).scalar() or 0
|
total_quantity = db.query(func.sum(Inventory.quantity)).scalar() or 0
|
||||||
total_reserved = db.query(func.sum(Inventory.reserved_quantity)).scalar() or 0
|
total_reserved = db.query(func.sum(Inventory.reserved_quantity)).scalar() or 0
|
||||||
|
|||||||
Reference in New Issue
Block a user