insightfy-bloom-ms-ans / KPI_API_README.md
MukeshKapoor25's picture
feat(ans): Implement comprehensive KPI and analytics microservice architecture
a7f703e

KPI Total Sales Widget API

Overview

This document describes the KPI (Key Performance Indicator) Total Sales Widget API implementation in the Analytics and Notification Service (ANS). The API provides endpoints for retrieving sales analytics data with daily, weekly, and monthly granularity.

Architecture

The implementation follows the TMS (Transaction Management Service) coding style with a clean separation of concerns:

app/
β”œβ”€β”€ schemas/
β”‚   └── kpi_schema.py          # Pydantic models for request/response
β”œβ”€β”€ repositories/
β”‚   └── kpi_repository.py      # Data access layer (SQL queries)
β”œβ”€β”€ services/
β”‚   └── kpi_service.py         # Business logic layer
β”œβ”€β”€ routers/
β”‚   └── kpi_router.py          # API endpoints
└── utils/
    └── request_id_utils.py    # Request correlation utilities

API Endpoints

Base URL

/api/v1/kpi

1. Get Total Sales KPI (POST)

Endpoint: POST /api/v1/kpi/total-sales

Description: Retrieve total sales KPI data with specified time granularity.

Authentication: Required (Bearer token)

Permissions: view_analytics

Request Body:

{
  "merchant_id": "MERCH123",
  "branch_id": "BRANCH001",
  "metric_type": "total_sales",
  "granularity": "daily",
  "start_date": "2024-01-01T00:00:00Z",
  "end_date": "2024-01-31T23:59:59Z"
}

Request Parameters:

  • merchant_id (string, required): Merchant identifier
  • branch_id (string, optional): Branch/Store identifier for filtering
  • metric_type (string, required): Type of KPI metric (currently supports "total_sales")
  • granularity (string, required): Time granularity - "daily", "weekly", or "monthly"
  • start_date (datetime, required): Start date for KPI calculation
  • end_date (datetime, required): End date for KPI calculation

Response:

{
  "status": "success",
  "data": {
    "merchant_id": "MERCH123",
    "branch_id": "BRANCH001",
    "metric_type": "total_sales",
    "granularity": "daily",
    "start_date": "2024-01-01T00:00:00Z",
    "end_date": "2024-01-31T23:59:59Z",
    "data_points": [
      {
        "period": "2024-01-01",
        "value": 15000.50,
        "transaction_count": 45,
        "period_start": "2024-01-01T00:00:00Z",
        "period_end": "2024-01-01T23:59:59Z"
      },
      {
        "period": "2024-01-02",
        "value": 18500.75,
        "transaction_count": 52,
        "period_start": "2024-01-02T00:00:00Z",
        "period_end": "2024-01-02T23:59:59Z"
      }
    ],
    "summary": {
      "total": 465000.00,
      "average": 15000.00,
      "min_value": 8500.00,
      "max_value": 22000.00,
      "period_count": 31,
      "total_transactions": 1350
    },
    "generated_at": "2024-02-01T10:30:00Z"
  },
  "message": "Total sales KPI retrieved successfully",
  "correlation_id": "550e8400-e29b-41d4-a716-446655440000"
}

2. Get Total Sales KPI (GET)

Endpoint: GET /api/v1/kpi/total-sales

Description: Alternative endpoint using query parameters instead of request body.

Authentication: Required (Bearer token)

Permissions: view_analytics

Query Parameters:

  • granularity (string, required): Time granularity - "daily", "weekly", or "monthly"
  • start_date (datetime, required): Start date for KPI calculation
  • end_date (datetime, required): End date for KPI calculation
  • branch_id (string, optional): Branch/Store ID filter

Example Request:

GET /api/v1/kpi/total-sales?granularity=daily&start_date=2024-01-01T00:00:00Z&end_date=2024-01-31T23:59:59Z&branch_id=BRANCH001

Response: Same as POST endpoint

3. Get Widget KPI

Endpoint: POST /api/v1/kpi/widget/{widget_id}

Description: Get KPI data for a specific dashboard widget with automatic date range calculation.

Authentication: Required (Bearer token)

Permissions: view_analytics

Path Parameters:

  • widget_id (string, required): Widget identifier

Request Body:

{
  "widget_id": "widget_123",
  "granularity": "weekly",
  "start_date": "2024-01-01T00:00:00Z",
  "end_date": "2024-03-31T23:59:59Z"
}

Request Parameters:

  • widget_id (string, required): Widget identifier
  • granularity (string, required): Time granularity - "daily", "weekly", or "monthly"
  • start_date (datetime, optional): Start date (defaults based on granularity)
  • end_date (datetime, optional): End date (defaults to now)

Default Date Ranges:

  • Daily: Last 30 days
  • Weekly: Last 12 weeks
  • Monthly: Last 12 months

Response:

{
  "status": "success",
  "data": {
    "widget_id": "widget_123",
    "kpi_data": {
      "merchant_id": "MERCH123",
      "branch_id": "BRANCH001",
      "metric_type": "total_sales",
      "granularity": "weekly",
      "data_points": [...],
      "summary": {...},
      "generated_at": "2024-02-01T10:30:00Z"
    }
  },
  "message": "Widget KPI retrieved successfully",
  "correlation_id": "550e8400-e29b-41d4-a716-446655440000"
}

4. Health Check

Endpoint: GET /api/v1/kpi/health

Description: Health check endpoint for KPI service.

Authentication: Not required

Response:

{
  "status": "healthy",
  "service": "kpi",
  "timestamp": "2024-02-01T10:30:00Z"
}

Data Models

KPIRequest

{
  "merchant_id": str,           # Merchant identifier
  "branch_id": str | None,      # Optional branch identifier
  "metric_type": str,           # "total_sales", "transaction_count", "average_order_value"
  "granularity": str,           # "daily", "weekly", "monthly"
  "start_date": datetime,       # Start date for calculation
  "end_date": datetime          # End date for calculation
}

KPIDataPoint

{
  "period": str,                # Period label (e.g., "2024-01-15", "2024-W03", "2024-01")
  "value": float,               # Metric value for the period
  "transaction_count": int,     # Number of transactions in period
  "period_start": datetime,     # Period start timestamp
  "period_end": datetime        # Period end timestamp
}

KPISummary

{
  "total": float,               # Total value across all periods
  "average": float,             # Average value per period
  "min_value": float,           # Minimum value in any period
  "max_value": float,           # Maximum value in any period
  "period_count": int,          # Number of periods
  "total_transactions": int     # Total number of transactions
}

KPIResponse

{
  "merchant_id": str,
  "branch_id": str | None,
  "metric_type": str,
  "granularity": str,
  "start_date": datetime,
  "end_date": datetime,
  "data_points": List[KPIDataPoint],
  "summary": KPISummary,
  "generated_at": datetime
}

Time Granularity

Daily

  • Aggregates sales by calendar day
  • Period format: YYYY-MM-DD (e.g., "2024-01-15")
  • Best for: Short-term analysis (last 30-90 days)

Weekly

  • Aggregates sales by ISO week (Monday to Sunday)
  • Period format: YYYY-WNN (e.g., "2024-W03")
  • Best for: Medium-term trends (last 12-26 weeks)

Monthly

  • Aggregates sales by calendar month
  • Period format: YYYY-MM (e.g., "2024-01")
  • Best for: Long-term trends (last 12-24 months)

Database Schema

The KPI API queries the sales_trans table from the TMS database:

SELECT 
    DATE_TRUNC(:granularity, transaction_date) as period_start,
    SUM(total_amount) as total_sales,
    COUNT(*) as transaction_count,
    AVG(total_amount) as avg_order_value
FROM sales_trans
WHERE merchant_id = :merchant_id
    AND transaction_date >= :start_date
    AND transaction_date <= :end_date
    AND status = 'completed'
    AND (:branch_id IS NULL OR branch_id = :branch_id)
GROUP BY DATE_TRUNC(:granularity, transaction_date)
ORDER BY period_start ASC

Error Handling

Validation Errors (400)

{
  "status": "error",
  "message": "Validation error message",
  "correlation_id": "550e8400-e29b-41d4-a716-446655440000"
}

Authentication Errors (401)

{
  "detail": "Invalid authentication credentials"
}

Permission Errors (403)

{
  "detail": "Forbidden"
}

Internal Errors (500)

{
  "status": "error",
  "message": "Failed to retrieve total sales KPI",
  "correlation_id": "550e8400-e29b-41d4-a716-446655440000"
}

Metrics and Monitoring

The API tracks the following metrics using the insightfy_utils telemetry:

  • kpi_total_sales_requests - Counter for total sales KPI requests
  • kpi_widget_requests - Counter for widget KPI requests
  • kpi_validation_errors - Counter for validation errors
  • kpi_errors - Counter for internal errors
  • kpi_calculation_duration - Histogram of KPI calculation duration
  • kpi_widget_duration - Histogram of widget KPI duration
  • kpi_total_sales_value - Histogram of total sales values

Logging

All operations are logged with structured logging including:

  • Merchant ID
  • Branch ID
  • Granularity
  • Duration
  • Correlation ID
  • Error details (when applicable)

Authentication

All endpoints (except health check) require:

  1. Valid JWT token in Authorization header: Bearer <token>
  2. Token must contain:
    • merchant_id
    • associate_id
    • branch_id
    • role_id

Permissions

The API uses role-based access control (RBAC):

  • view_analytics - Required for all KPI endpoints

Usage Examples

cURL Examples

Get Daily Sales KPI

curl -X POST "http://localhost:8000/api/v1/kpi/total-sales" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "MERCH123",
    "branch_id": "BRANCH001",
    "metric_type": "total_sales",
    "granularity": "daily",
    "start_date": "2024-01-01T00:00:00Z",
    "end_date": "2024-01-31T23:59:59Z"
  }'

Get Weekly Sales KPI (Query Parameters)

curl -X GET "http://localhost:8000/api/v1/kpi/total-sales?granularity=weekly&start_date=2024-01-01T00:00:00Z&end_date=2024-03-31T23:59:59Z" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Get Widget KPI

curl -X POST "http://localhost:8000/api/v1/kpi/widget/widget_123" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "widget_id": "widget_123",
    "granularity": "monthly"
  }'

Python Examples

import requests
from datetime import datetime, timedelta

# Configuration
BASE_URL = "http://localhost:8000/api/v1/kpi"
TOKEN = "YOUR_JWT_TOKEN"
HEADERS = {
    "Authorization": f"Bearer {TOKEN}",
    "Content-Type": "application/json"
}

# Get daily sales for last 30 days
end_date = datetime.utcnow()
start_date = end_date - timedelta(days=30)

payload = {
    "merchant_id": "MERCH123",
    "branch_id": "BRANCH001",
    "metric_type": "total_sales",
    "granularity": "daily",
    "start_date": start_date.isoformat() + "Z",
    "end_date": end_date.isoformat() + "Z"
}

response = requests.post(
    f"{BASE_URL}/total-sales",
    headers=HEADERS,
    json=payload
)

if response.status_code == 200:
    data = response.json()
    kpi_data = data["data"]
    print(f"Total Sales: ${kpi_data['summary']['total']:,.2f}")
    print(f"Average: ${kpi_data['summary']['average']:,.2f}")
    print(f"Transactions: {kpi_data['summary']['total_transactions']}")
else:
    print(f"Error: {response.status_code} - {response.text}")

JavaScript/TypeScript Examples

interface KPIRequest {
  merchant_id: string;
  branch_id?: string;
  metric_type: 'total_sales';
  granularity: 'daily' | 'weekly' | 'monthly';
  start_date: string;
  end_date: string;
}

async function getTotalSalesKPI(
  token: string,
  request: KPIRequest
): Promise<any> {
  const response = await fetch(
    'http://localhost:8000/api/v1/kpi/total-sales',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(request)
    }
  );
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
}

// Usage
const kpiData = await getTotalSalesKPI('YOUR_JWT_TOKEN', {
  merchant_id: 'MERCH123',
  branch_id: 'BRANCH001',
  metric_type: 'total_sales',
  granularity: 'daily',
  start_date: '2024-01-01T00:00:00Z',
  end_date: '2024-01-31T23:59:59Z'
});

console.log('Total Sales:', kpiData.data.summary.total);

Performance Considerations

  1. Query Optimization: The repository uses PostgreSQL's DATE_TRUNC function for efficient date aggregation
  2. Index Recommendations: Ensure indexes on:
    • merchant_id
    • branch_id
    • transaction_date
    • status
  3. Caching: Consider implementing Redis caching for frequently accessed KPI data
  4. Pagination: For large date ranges, consider implementing pagination or limiting the maximum date range

Future Enhancements

  1. Additional Metrics:

    • Transaction count KPI
    • Average order value KPI
    • Customer acquisition metrics
    • Product performance metrics
  2. Advanced Features:

    • Comparison with previous periods
    • Trend analysis and forecasting
    • Real-time KPI updates
    • Export to CSV/Excel
    • Scheduled KPI reports
  3. Optimization:

    • Materialized views for faster queries
    • Background job for pre-calculating KPIs
    • WebSocket support for real-time updates

Testing

Run the service and test the endpoints:

# Start the service
cd insightfy-bloom-ms-ans
uvicorn app.main:app --reload --port 8000

# Test health check
curl http://localhost:8000/api/v1/kpi/health

# Test KPI endpoint (requires valid token)
curl -X POST http://localhost:8000/api/v1/kpi/total-sales \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d @test_kpi_request.json

Support

For issues or questions:

  • Check logs in the ANS service
  • Review correlation_id in error responses for debugging
  • Contact the development team

Version History

  • v1.0.0 (2024-02-01): Initial implementation
    • Total sales KPI with daily, weekly, monthly granularity
    • Widget-specific KPI endpoints
    • Authentication and authorization
    • Metrics and logging