Some checks failed
Enforce MOD-025/MOD-026 rules: zero top-level cross-module model imports remain in any service file. All 66 files migrated using deferred import patterns (method-body, _get_model() helpers, instance-cached self._Model) and new cross-module service methods in tenancy. Documentation updated with Pattern 6 (deferred imports), migration plan marked complete, and violations status reflects 84→0 service-layer violations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
254 lines
8.0 KiB
Python
254 lines
8.0 KiB
Python
# app/modules/catalog/services/catalog_metrics.py
|
|
"""
|
|
Metrics provider for the catalog module.
|
|
|
|
Provides metrics for:
|
|
- Product counts
|
|
- Active/inactive products
|
|
- Featured products
|
|
"""
|
|
|
|
import logging
|
|
from datetime import datetime, timedelta
|
|
from typing import TYPE_CHECKING
|
|
|
|
from sqlalchemy import func
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.modules.contracts.metrics import (
|
|
MetricsContext,
|
|
MetricValue,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
pass
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class CatalogMetricsProvider:
|
|
"""
|
|
Metrics provider for catalog module.
|
|
|
|
Provides product-related metrics for store and platform dashboards.
|
|
"""
|
|
|
|
@property
|
|
def metrics_category(self) -> str:
|
|
return "catalog"
|
|
|
|
def get_store_metrics(
|
|
self,
|
|
db: Session,
|
|
store_id: int,
|
|
context: MetricsContext | None = None,
|
|
) -> list[MetricValue]:
|
|
"""
|
|
Get product metrics for a specific store.
|
|
|
|
Provides:
|
|
- Total products
|
|
- Active products
|
|
- Featured products
|
|
- New products (in period)
|
|
"""
|
|
from app.modules.catalog.models import Product
|
|
|
|
try:
|
|
# Total products
|
|
total_products = (
|
|
db.query(Product).filter(Product.store_id == store_id).count()
|
|
)
|
|
|
|
# Active products
|
|
active_products = (
|
|
db.query(Product)
|
|
.filter(Product.store_id == store_id, Product.is_active == True)
|
|
.count()
|
|
)
|
|
|
|
# Featured products
|
|
featured_products = (
|
|
db.query(Product)
|
|
.filter(
|
|
Product.store_id == store_id,
|
|
Product.is_featured == True,
|
|
Product.is_active == True,
|
|
)
|
|
.count()
|
|
)
|
|
|
|
# New products (default to last 30 days)
|
|
date_from = context.date_from if context else None
|
|
if date_from is None:
|
|
date_from = datetime.utcnow() - timedelta(days=30)
|
|
|
|
new_products_query = db.query(Product).filter(
|
|
Product.store_id == store_id,
|
|
Product.created_at >= date_from,
|
|
)
|
|
if context and context.date_to:
|
|
new_products_query = new_products_query.filter(
|
|
Product.created_at <= context.date_to
|
|
)
|
|
new_products = new_products_query.count()
|
|
|
|
# Products with translations
|
|
(
|
|
db.query(func.count(func.distinct(Product.id)))
|
|
.filter(Product.store_id == store_id)
|
|
.join(Product.translations)
|
|
.scalar()
|
|
or 0
|
|
)
|
|
|
|
return [
|
|
MetricValue(
|
|
key="catalog.total_products",
|
|
value=total_products,
|
|
label="Total Products",
|
|
category="catalog",
|
|
icon="box",
|
|
description="Total products in catalog",
|
|
),
|
|
MetricValue(
|
|
key="catalog.active_products",
|
|
value=active_products,
|
|
label="Active Products",
|
|
category="catalog",
|
|
icon="check-circle",
|
|
description="Products that are active and visible",
|
|
),
|
|
MetricValue(
|
|
key="catalog.featured_products",
|
|
value=featured_products,
|
|
label="Featured Products",
|
|
category="catalog",
|
|
icon="star",
|
|
description="Products marked as featured",
|
|
),
|
|
MetricValue(
|
|
key="catalog.new_products",
|
|
value=new_products,
|
|
label="New Products",
|
|
category="catalog",
|
|
icon="plus-circle",
|
|
description="Products added in the period",
|
|
),
|
|
]
|
|
except Exception as e:
|
|
logger.warning(f"Failed to get catalog store metrics: {e}")
|
|
return []
|
|
|
|
def get_platform_metrics(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
context: MetricsContext | None = None,
|
|
) -> list[MetricValue]:
|
|
"""
|
|
Get product metrics aggregated for a platform.
|
|
|
|
Aggregates catalog data across all stores.
|
|
"""
|
|
from app.modules.catalog.models import Product
|
|
from app.modules.tenancy.services.platform_service import platform_service
|
|
|
|
try:
|
|
# Get all store IDs for this platform via platform service
|
|
store_ids = platform_service.get_store_ids_for_platform(db, platform_id)
|
|
|
|
# Total products
|
|
total_products = (
|
|
db.query(Product).filter(Product.store_id.in_(store_ids)).count()
|
|
)
|
|
|
|
# Active products
|
|
active_products = (
|
|
db.query(Product)
|
|
.filter(Product.store_id.in_(store_ids), Product.is_active == True)
|
|
.count()
|
|
)
|
|
|
|
# Featured products
|
|
featured_products = (
|
|
db.query(Product)
|
|
.filter(
|
|
Product.store_id.in_(store_ids),
|
|
Product.is_featured == True,
|
|
Product.is_active == True,
|
|
)
|
|
.count()
|
|
)
|
|
|
|
# Stores with products
|
|
stores_with_products = (
|
|
db.query(func.count(func.distinct(Product.store_id)))
|
|
.filter(Product.store_id.in_(store_ids))
|
|
.scalar()
|
|
or 0
|
|
)
|
|
|
|
# Average products per store
|
|
total_stores = (
|
|
db.query(StorePlatform)
|
|
.filter(
|
|
StorePlatform.platform_id == platform_id,
|
|
StorePlatform.is_active == True,
|
|
)
|
|
.count()
|
|
)
|
|
avg_products = round(total_products / total_stores, 1) if total_stores > 0 else 0
|
|
|
|
return [
|
|
MetricValue(
|
|
key="catalog.total_products",
|
|
value=total_products,
|
|
label="Total Products",
|
|
category="catalog",
|
|
icon="box",
|
|
description="Total products across all stores",
|
|
),
|
|
MetricValue(
|
|
key="catalog.active_products",
|
|
value=active_products,
|
|
label="Active Products",
|
|
category="catalog",
|
|
icon="check-circle",
|
|
description="Products that are active and visible",
|
|
),
|
|
MetricValue(
|
|
key="catalog.featured_products",
|
|
value=featured_products,
|
|
label="Featured Products",
|
|
category="catalog",
|
|
icon="star",
|
|
description="Products marked as featured",
|
|
),
|
|
MetricValue(
|
|
key="catalog.stores_with_products",
|
|
value=stores_with_products,
|
|
label="Stores with Products",
|
|
category="catalog",
|
|
icon="store",
|
|
description="Stores that have created products",
|
|
),
|
|
MetricValue(
|
|
key="catalog.avg_products_per_store",
|
|
value=avg_products,
|
|
label="Avg Products/Store",
|
|
category="catalog",
|
|
icon="calculator",
|
|
description="Average products per store",
|
|
),
|
|
]
|
|
except Exception as e:
|
|
logger.warning(f"Failed to get catalog platform metrics: {e}")
|
|
return []
|
|
|
|
|
|
# Singleton instance
|
|
catalog_metrics_provider = CatalogMetricsProvider()
|
|
|
|
__all__ = ["CatalogMetricsProvider", "catalog_metrics_provider"]
|