Files
smoothschedule/smoothschedule/analytics
poduck e4ad7fca87 feat: Plan-based feature permissions and quota enforcement
Backend:
- Add HasQuota() permission factory for quota limits (resources, users, services, appointments, email templates, automated tasks)
- Add HasFeaturePermission() factory for feature-based permissions (SMS, masked calling, custom domains, white label, plugins, webhooks, calendar sync, analytics)
- Add has_feature() method to Tenant model for flexible permission checking
- Add new tenant permission fields: can_create_plugins, can_use_webhooks, can_use_calendar_sync, can_export_data
- Create Data Export API with CSV/JSON support for appointments, customers, resources, services
- Create Analytics API with dashboard, appointments, revenue endpoints
- Add calendar sync views and URL configuration

Frontend:
- Add usePlanFeatures hook for checking feature availability
- Add UpgradePrompt components (inline, banner, overlay variants)
- Add LockedSection wrapper and LockedButton for feature gating
- Update settings pages with permission checks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 11:21:11 -05:00
..

Analytics API Documentation

Overview

The Analytics API provides detailed reporting and business insights for tenant businesses. All analytics endpoints are gated behind the advanced_analytics permission from the subscription plan.

Permission Gating

All analytics endpoints require:

  1. Authentication: User must be authenticated (IsAuthenticated)
  2. Feature Permission: Tenant must have advanced_analytics permission enabled in their subscription plan

If a tenant doesn't have the advanced_analytics permission, they will receive a 403 Forbidden response:

{
    "detail": "Your current plan does not include Advanced Analytics. Please upgrade your subscription to access this feature."
}

Endpoints

Base URL

GET /api/analytics/

1. Dashboard Summary Statistics

Endpoint: GET /api/analytics/analytics/dashboard/

Returns high-level metrics for the tenant's dashboard including appointment counts, resource utilization, and peak times.

Response Example:

{
    "total_appointments_this_month": 42,
    "total_appointments_all_time": 1250,
    "active_resources_count": 5,
    "active_services_count": 3,
    "upcoming_appointments_count": 8,
    "average_appointment_duration_minutes": 45.5,
    "peak_booking_day": "Friday",
    "peak_booking_hour": 14,
    "period": {
        "start_date": "2024-12-01T00:00:00Z",
        "end_date": "2024-12-31T23:59:59Z"
    }
}

Metrics Explained:

  • total_appointments_this_month: Count of confirmed appointments in current calendar month
  • total_appointments_all_time: Total count of all confirmed appointments ever
  • active_resources_count: Number of unique resources with future appointments
  • active_services_count: Number of unique services with future appointments
  • upcoming_appointments_count: Appointments in the next 7 days
  • average_appointment_duration_minutes: Average duration of all appointments (in minutes)
  • peak_booking_day: Day of week with most appointments (Sunday-Saturday)
  • peak_booking_hour: Hour of day (0-23) with most appointments

2. Appointment Analytics

Endpoint: GET /api/analytics/analytics/appointments/

Detailed appointment breakdown with trends and metrics.

Query Parameters:

  • days (optional, default: 30): Number of days to analyze
  • status (optional): Filter by status - 'confirmed', 'cancelled', 'no_show'
  • service_id (optional): Filter by service ID
  • resource_id (optional): Filter by resource ID

Response Example:

{
    "total": 285,
    "by_status": {
        "confirmed": 250,
        "cancelled": 25,
        "no_show": 10
    },
    "by_service": [
        {
            "service_id": 1,
            "service_name": "Haircut",
            "count": 150
        },
        {
            "service_id": 2,
            "service_name": "Color Treatment",
            "count": 135
        }
    ],
    "by_resource": [
        {
            "resource_id": 1,
            "resource_name": "Chair 1",
            "count": 145
        },
        {
            "resource_id": 2,
            "resource_name": "Chair 2",
            "count": 140
        }
    ],
    "daily_breakdown": [
        {
            "date": "2024-11-01",
            "count": 8,
            "status_breakdown": {
                "confirmed": 7,
                "cancelled": 1,
                "no_show": 0
            }
        },
        {
            "date": "2024-11-02",
            "count": 9,
            "status_breakdown": {
                "confirmed": 8,
                "cancelled": 1,
                "no_show": 0
            }
        }
    ],
    "booking_trend_percent": 12.5,
    "cancellation_rate_percent": 8.77,
    "no_show_rate_percent": 3.51,
    "period_days": 30
}

Metrics Explained:

  • total: Total appointments in the period
  • by_status: Count breakdown by appointment status
  • by_service: Appointment count per service
  • by_resource: Appointment count per resource
  • daily_breakdown: Day-by-day breakdown with status details
  • booking_trend_percent: Percentage change vs previous period (positive = growth)
  • cancellation_rate_percent: Percentage of appointments cancelled
  • no_show_rate_percent: Percentage of appointments where customer didn't show
  • period_days: Number of days analyzed

Usage Examples:

# Get appointment analytics for last 7 days
curl "http://lvh.me:8000/api/analytics/analytics/appointments/?days=7"

# Get analytics for specific service
curl "http://lvh.me:8000/api/analytics/analytics/appointments/?service_id=1"

# Get only cancelled appointments in last 30 days
curl "http://lvh.me:8000/api/analytics/analytics/appointments/?status=cancelled"

3. Revenue Analytics

Endpoint: GET /api/analytics/analytics/revenue/

Revenue breakdown and payment analytics. Requires both advanced_analytics AND can_accept_payments permissions.

Query Parameters:

  • days (optional, default: 30): Number of days to analyze
  • service_id (optional): Filter by service ID

Response Example:

{
    "total_revenue_cents": 125000,
    "transaction_count": 50,
    "average_transaction_value_cents": 2500,
    "by_service": [
        {
            "service_id": 1,
            "service_name": "Haircut",
            "revenue_cents": 75000,
            "count": 30
        },
        {
            "service_id": 2,
            "service_name": "Color Treatment",
            "revenue_cents": 50000,
            "count": 20
        }
    ],
    "daily_breakdown": [
        {
            "date": "2024-11-01",
            "revenue_cents": 3500,
            "transaction_count": 7
        },
        {
            "date": "2024-11-02",
            "revenue_cents": 4200,
            "transaction_count": 8
        }
    ],
    "period_days": 30
}

Metrics Explained:

  • total_revenue_cents: Total revenue in cents (divide by 100 for dollars)
  • transaction_count: Number of completed transactions
  • average_transaction_value_cents: Average transaction value in cents
  • by_service: Revenue breakdown by service
  • daily_breakdown: Day-by-day revenue metrics
  • period_days: Number of days analyzed

Important Notes:

  • Amounts are in cents (multiply by 0.01 for dollars)
  • Only includes completed/confirmed payments
  • Requires tenant to have payment processing enabled
  • Returns 403 if can_accept_payments is not enabled

Permission Implementation

How Feature Gating Works

The analytics endpoints use the HasFeaturePermission permission class:

class AnalyticsViewSet(viewsets.ViewSet):
    permission_classes = [IsAuthenticated, HasFeaturePermission('advanced_analytics')]

This permission class:

  1. Checks Authentication: Ensures user is logged in
  2. Gets Tenant from Request: Uses request.tenant (set by django-tenants middleware)
  3. Calls tenant.has_feature('advanced_analytics'): Checks both:
    • Direct boolean field on Tenant model (if exists)
    • Subscription plan's permissions JSON field
  4. Raises 403 if Permission Not Found: Returns error with upgrade message

Adding Advanced Analytics to a Plan

To grant advanced_analytics permission to a subscription plan:

Option 1: Django Admin

1. Go to /admin/platform_admin/subscriptionplan/
2. Edit desired plan
3. In "Permissions" JSON field, add:
   {
       "advanced_analytics": true,
       ...other permissions...
   }
4. Save

Option 2: Django Management Command

docker compose -f docker-compose.local.yml exec django python manage.py shell

# In the shell:
from platform_admin.models import SubscriptionPlan
plan = SubscriptionPlan.objects.get(name='Professional')
permissions = plan.permissions or {}
permissions['advanced_analytics'] = True
plan.permissions = permissions
plan.save()

Option 3: Direct Tenant Field

# If using direct field on Tenant:
from core.models import Tenant
tenant = Tenant.objects.get(schema_name='demo')
tenant.advanced_analytics = True  # If field exists
tenant.save()

Architecture

File Structure

analytics/
├── __init__.py
├── apps.py           # Django app configuration
├── views.py          # AnalyticsViewSet with all endpoints
├── serializers.py    # Read-only serializers for response validation
├── urls.py           # URL routing
└── README.md         # This file

Key Classes

AnalyticsViewSet (views.py)

  • Inherits from viewsets.ViewSet (read-only, no database models)
  • Three action methods:
    • dashboard() - Summary statistics
    • appointments() - Detailed appointment analytics
    • revenue() - Payment analytics (conditional)
  • All methods return Response with calculated data

Permission Chain

Request → IsAuthenticated → HasFeaturePermission('advanced_analytics') → View

Error Responses

401 Unauthorized

{
    "detail": "Authentication credentials were not provided."
}

403 Forbidden (Missing Permission)

{
    "detail": "Your current plan does not include Advanced Analytics. Please upgrade your subscription to access this feature."
}

403 Forbidden (Revenue Endpoint, Missing Payments Permission)

{
    "error": "Payment analytics not available",
    "detail": "Your plan does not include payment processing."
}

Testing

Using cURL

# Get analytics with auth token
TOKEN="your_auth_token_here"

curl -H "Authorization: Token $TOKEN" \
    "http://lvh.me:8000/api/analytics/analytics/dashboard/"

curl -H "Authorization: Token $TOKEN" \
    "http://lvh.me:8000/api/analytics/analytics/appointments/?days=7"

curl -H "Authorization: Token $TOKEN" \
    "http://lvh.me:8000/api/analytics/analytics/revenue/"

Using Python Requests

import requests

TOKEN = "your_auth_token_here"
headers = {"Authorization": f"Token {TOKEN}"}

# Dashboard
response = requests.get(
    "http://lvh.me:8000/api/analytics/analytics/dashboard/",
    headers=headers
)
print(response.json())

# Appointments with filter
response = requests.get(
    "http://lvh.me:8000/api/analytics/analytics/appointments/",
    headers=headers,
    params={"days": 7, "service_id": 1}
)
print(response.json())

Performance Considerations

  • Dashboard: Performs multiple aggregate queries, suitable for ~10k+ appointments
  • Appointments: Filters and iterates over appointments, may be slow with 100k+ records
  • Revenue: Depends on payment transaction volume, usually fast
  • Caching: Consider implementing Redis caching for frequently accessed analytics

Future Enhancements

  1. Performance Optimization

    • Add database indexing on start_time, status, created_at
    • Implement query result caching
    • Use database aggregation instead of Python loops
  2. Additional Analytics

    • Customer demographics (repeat rate, lifetime value)
    • Staff performance metrics (revenue per staff member)
    • Channel attribution (how customers found you)
    • Resource utilization rate (occupancy percentage)
  3. Export Features

    • CSV/Excel export
    • PDF report generation
    • Email report scheduling
  4. Advanced Filtering

    • Date range selection
    • Multi-service filtering
    • Resource utilization trends
    • Seasonal analysis