Promotions - Analytics Service
Location: api/nextango/apps/promotions/analytics_service.py
Last Updated: 2026-07-08
Overview
Provides aggregated promotional analytics for the dashboard: promo code performance, campaign summary, campaign lift (test vs. control revenue), and auto-apply promotion performance. The service reads from the replica database when USE_REPLICA_DATABASE=True and caches every method's result for 5 minutes. Called by the promotions analytics endpoints in Views.
Class: PromotionAnalyticsService
Aggregation methods for promotional campaign analytics.
Method Reference
get_promo_code_performance()
Purpose: Returns per-promo-code analytics from completed transactions: usage count, total discount granted, revenue from orders where the code was applied, and average order value for those orders. Also returns the percentage of all completed transactions that used a promo code and the total discount amount distributed.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
store_id | str | No | Filter by store UUID or Sanity ID |
start_date | date | No | Filter transactions on or after this date |
end_date | date | No | Filter transactions on or before this date |
Returns:
{
'data': [
{
'code': str, # PromoCode.code
'code_name': str, # PromoCode.name
'is_active': bool,
'usage_count': int, # Transactions using this code
'total_discount_given': Decimal, # Sum of orderLevelDiscount
'revenue_from_promo_orders': Decimal, # Sum of totalAmount
'avg_order_with_promo': Decimal,
},
...
],
'total_promo_discount': Decimal, # Total discount across all codes in period
'orders_with_promo_pct': float, # % of all completed orders that used a promo
}
Rows are ordered by revenue_from_promo_orders descending.
Example:
from nextango.apps.promotions.analytics_service import PromotionAnalyticsService
from datetime import date
result = PromotionAnalyticsService.get_promo_code_performance(
start_date=date(2026, 1, 1),
end_date=date(2026, 1, 31),
)
print(f"Total promo discount issued: £{result['total_promo_discount']}")
for code in result['data']:
print(f" {code['code']}: {code['usage_count']} uses, £{code['total_discount_given']} discounted")
get_campaign_summary()
Purpose: Returns a summary of all promotional campaigns with their type, active status, number of associated promo codes, and total usage across those codes.
Parameters: None
Returns:
{
'data': [
{
'campaign_name': str,
'campaign_type': str, # e.g. 'seasonal_sale', 'flash_sale', 'clearance'
'is_active': bool,
'promo_codes_count': int, # Number of PromoCode records linked
'total_usage': int, # Sum of current_usage_count across codes
'performance_metrics': dict, # PromotionalCampaign.performance_metrics JSON
},
...
]
}
Example:
result = PromotionAnalyticsService.get_campaign_summary()
for campaign in result['data']:
print(f"{campaign['campaign_name']}: {campaign['total_usage']} total uses")
get_campaign_lift(campaign_id)
Purpose: Compares revenue during a campaign's active window (test period) against an equal-length period immediately before it (control period), for transactions carrying one of the campaign's promo codes.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
campaign_id | str | Yes | PromotionalCampaign primary key |
Returns:
{
'campaign_id': str,
'test_revenue': Decimal, # Revenue from completed transactions using the campaign's codes, within date_range
'control_revenue': Decimal, # Revenue from all completed transactions in the equal-length prior window
'lift_pct': float | None, # Percentage change, None if control_revenue is 0
'test_orders': int,
'control_orders': int,
}
Returns an empty result (all zeros, lift_pct: None) if the campaign does not exist or has no date_range.
get_auto_promotion_performance()
Purpose: Aggregates performance for auto-apply Promotion records by grouping SaleTransactionAppliedPromotion snapshot rows, so historical performance is preserved even after a Promotion is deleted.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
store_id | str | No | Filter by store UUID |
start_date | date | No | Filter transactions on or after this date |
end_date | date | No | Filter transactions on or before this date |
Returns:
{
'data': [
{
'promotion_snapshot_id': str,
'promotion_name': str, # Snapshot name at time of sale
'usage_count': int,
'total_discount_given': float,
'revenue_from_promo_orders': float,
'avg_discount_per_use': float,
},
...
]
}
Rows are ordered by usage_count descending.
Caching
All four methods use @cache_result with a 300-second (5-minute) TTL.
| Method | Cache Key Prefix | Vary On |
|---|---|---|
get_promo_code_performance | promo_analytics_codes | store_id, start_date, end_date |
get_campaign_summary | promo_analytics_campaigns | none, global |
get_campaign_lift | promo_analytics_lift | campaign_id |
get_auto_promotion_performance | promo_analytics_auto | store_id, start_date, end_date |