feat: add proper Pydantic response_model to all stats endpoints
- Create comprehensive stats schemas in models/schema/stats.py: - ImportStatsResponse, UserStatsResponse, ProductStatsResponse - PlatformStatsResponse, AdminDashboardResponse - VendorDashboardStatsResponse with nested models - VendorAnalyticsResponse, CodeQualityDashboardStatsResponse - Move DashboardStatsResponse from code_quality.py to schema file - Fix get_vendor_statistics() to return pending_vendors field - Fix get_vendor_stats() to return flat structure matching schema - Add response_model to all stats endpoints: - GET /admin/dashboard -> AdminDashboardResponse - GET /admin/dashboard/stats/platform -> PlatformStatsResponse - GET /admin/marketplace-import-jobs/stats -> ImportStatsResponse - GET /vendor/dashboard/stats -> VendorDashboardStatsResponse - GET /vendor/analytics -> VendorAnalyticsResponse - Enhance API-001 architecture rule with detailed guidance - Add SVC-007 rule for service/schema compatibility 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -130,36 +130,38 @@ class StatsService:
|
||||
db.query(Customer).filter(Customer.vendor_id == vendor_id).count()
|
||||
)
|
||||
|
||||
# Return flat structure compatible with VendorDashboardStatsResponse schema
|
||||
# The endpoint will restructure this into nested format
|
||||
return {
|
||||
"catalog": {
|
||||
"total_products": total_catalog_products,
|
||||
"featured_products": featured_products,
|
||||
"active_products": total_catalog_products,
|
||||
},
|
||||
"staging": {
|
||||
"imported_products": staging_products,
|
||||
},
|
||||
"inventory": {
|
||||
"total_quantity": int(total_inventory),
|
||||
"reserved_quantity": int(reserved_inventory),
|
||||
"available_quantity": int(total_inventory - reserved_inventory),
|
||||
"locations_count": inventory_locations,
|
||||
},
|
||||
"imports": {
|
||||
"total_imports": total_imports,
|
||||
"successful_imports": successful_imports,
|
||||
"success_rate": (
|
||||
(successful_imports / total_imports * 100)
|
||||
if total_imports > 0
|
||||
else 0
|
||||
),
|
||||
},
|
||||
"orders": {
|
||||
"total_orders": total_orders,
|
||||
},
|
||||
"customers": {
|
||||
"total_customers": total_customers,
|
||||
},
|
||||
# Product stats
|
||||
"total_products": total_catalog_products,
|
||||
"active_products": total_catalog_products,
|
||||
"featured_products": featured_products,
|
||||
# Order stats (TODO: implement when Order model has status field)
|
||||
"total_orders": total_orders,
|
||||
"pending_orders": 0, # TODO: filter by status
|
||||
"completed_orders": 0, # TODO: filter by status
|
||||
# Customer stats
|
||||
"total_customers": total_customers,
|
||||
"active_customers": 0, # TODO: implement active customer logic
|
||||
# Revenue stats (TODO: implement when Order model has amount field)
|
||||
"total_revenue": 0,
|
||||
"revenue_this_month": 0,
|
||||
# Import stats
|
||||
"total_imports": total_imports,
|
||||
"successful_imports": successful_imports,
|
||||
"import_success_rate": (
|
||||
(successful_imports / total_imports * 100)
|
||||
if total_imports > 0
|
||||
else 0
|
||||
),
|
||||
# Staging stats
|
||||
"imported_products": staging_products,
|
||||
# Inventory stats
|
||||
"total_inventory_quantity": int(total_inventory),
|
||||
"reserved_inventory_quantity": int(reserved_inventory),
|
||||
"available_inventory_quantity": int(total_inventory - reserved_inventory),
|
||||
"inventory_locations_count": inventory_locations,
|
||||
}
|
||||
|
||||
except VendorNotFoundException:
|
||||
@@ -255,7 +257,11 @@ class StatsService:
|
||||
)
|
||||
|
||||
def get_vendor_statistics(self, db: Session) -> dict:
|
||||
"""Get vendor statistics for admin dashboard."""
|
||||
"""Get vendor statistics for admin dashboard.
|
||||
|
||||
Returns dict compatible with VendorStatsResponse schema.
|
||||
Keys: total, verified, pending, inactive (mapped from internal names)
|
||||
"""
|
||||
try:
|
||||
total_vendors = db.query(Vendor).count()
|
||||
active_vendors = db.query(Vendor).filter(Vendor.is_active == True).count()
|
||||
@@ -263,12 +269,25 @@ class StatsService:
|
||||
db.query(Vendor).filter(Vendor.is_verified == True).count()
|
||||
)
|
||||
inactive_vendors = total_vendors - active_vendors
|
||||
# Pending = active but not yet verified
|
||||
pending_vendors = (
|
||||
db.query(Vendor)
|
||||
.filter(Vendor.is_active == True, Vendor.is_verified == False)
|
||||
.count()
|
||||
)
|
||||
|
||||
return {
|
||||
# Schema-compatible fields (VendorStatsResponse)
|
||||
"total": total_vendors,
|
||||
"verified": verified_vendors,
|
||||
"pending": pending_vendors,
|
||||
"inactive": inactive_vendors,
|
||||
# Legacy fields for backward compatibility
|
||||
"total_vendors": total_vendors,
|
||||
"active_vendors": active_vendors,
|
||||
"inactive_vendors": inactive_vendors,
|
||||
"verified_vendors": verified_vendors,
|
||||
"pending_vendors": pending_vendors,
|
||||
"verification_rate": (
|
||||
(verified_vendors / total_vendors * 100) if total_vendors > 0 else 0
|
||||
),
|
||||
@@ -431,9 +450,23 @@ class StatsService:
|
||||
"""
|
||||
try:
|
||||
total = db.query(MarketplaceImportJob).count()
|
||||
pending = (
|
||||
db.query(MarketplaceImportJob)
|
||||
.filter(MarketplaceImportJob.status == "pending")
|
||||
.count()
|
||||
)
|
||||
processing = (
|
||||
db.query(MarketplaceImportJob)
|
||||
.filter(MarketplaceImportJob.status == "processing")
|
||||
.count()
|
||||
)
|
||||
completed = (
|
||||
db.query(MarketplaceImportJob)
|
||||
.filter(MarketplaceImportJob.status == "completed")
|
||||
.filter(
|
||||
MarketplaceImportJob.status.in_(
|
||||
["completed", "completed_with_errors"]
|
||||
)
|
||||
)
|
||||
.count()
|
||||
)
|
||||
failed = (
|
||||
@@ -443,6 +476,13 @@ class StatsService:
|
||||
)
|
||||
|
||||
return {
|
||||
# Frontend-expected fields
|
||||
"total": total,
|
||||
"pending": pending,
|
||||
"processing": processing,
|
||||
"completed": completed,
|
||||
"failed": failed,
|
||||
# Legacy fields for backward compatibility
|
||||
"total_imports": total,
|
||||
"completed_imports": completed,
|
||||
"failed_imports": failed,
|
||||
@@ -451,6 +491,11 @@ class StatsService:
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get import statistics: {str(e)}")
|
||||
return {
|
||||
"total": 0,
|
||||
"pending": 0,
|
||||
"processing": 0,
|
||||
"completed": 0,
|
||||
"failed": 0,
|
||||
"total_imports": 0,
|
||||
"completed_imports": 0,
|
||||
"failed_imports": 0,
|
||||
|
||||
Reference in New Issue
Block a user