feat(loyalty): Phase 7 — advanced analytics (cohort, churn, revenue)
New analytics_service.py with three analytics features:
- Cohort retention: groups cards by enrollment month, tracks % with
any transaction in each subsequent month. Returns matrix suitable
for Chart.js heatmap. GET /analytics/cohorts?months_back=6
- Churn detection: flags cards as "at risk" when inactive > 2x their
average inter-transaction interval (default 60d for new cards).
Returns ranked list. GET /analytics/churn?limit=50
- Revenue attribution: monthly and per-store aggregation of point-
earning transactions. GET /analytics/revenue?months_back=6
Endpoints added to both admin API (/admin/loyalty/merchants/{id}/
analytics/*) and store API (/store/loyalty/analytics/*) so merchants
can see their own analytics.
342 tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,7 @@ from app.modules.loyalty.schemas import (
|
||||
TransactionResponse,
|
||||
)
|
||||
from app.modules.loyalty.services import card_service, pin_service, program_service
|
||||
from app.modules.loyalty.services.analytics_service import analytics_service
|
||||
from app.modules.tenancy.models import User # API-007
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -495,6 +496,50 @@ def get_platform_stats(
|
||||
return program_service.get_platform_stats(db)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Advanced Analytics
|
||||
# =============================================================================
|
||||
|
||||
|
||||
@router.get("/merchants/{merchant_id}/analytics/cohorts")
|
||||
def get_cohort_retention(
|
||||
merchant_id: int = Path(..., gt=0),
|
||||
months_back: int = Query(6, ge=1, le=24),
|
||||
current_user: User = Depends(get_current_admin_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Cohort retention matrix for a merchant's loyalty program."""
|
||||
return analytics_service.get_cohort_retention(
|
||||
db, merchant_id, months_back=months_back
|
||||
)
|
||||
|
||||
|
||||
@router.get("/merchants/{merchant_id}/analytics/churn")
|
||||
def get_at_risk_cards(
|
||||
merchant_id: int = Path(..., gt=0),
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
current_user: User = Depends(get_current_admin_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Cards at risk of churn for a merchant."""
|
||||
return analytics_service.get_at_risk_cards(
|
||||
db, merchant_id, limit=limit
|
||||
)
|
||||
|
||||
|
||||
@router.get("/merchants/{merchant_id}/analytics/revenue")
|
||||
def get_revenue_attribution(
|
||||
merchant_id: int = Path(..., gt=0),
|
||||
months_back: int = Query(6, ge=1, le=24),
|
||||
current_user: User = Depends(get_current_admin_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Revenue attribution from loyalty transactions."""
|
||||
return analytics_service.get_revenue_attribution(
|
||||
db, merchant_id, months_back=months_back
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Wallet Integration Status
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user