Files
smoothschedule/smoothschedule/analytics/README.md
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

400 lines
11 KiB
Markdown

# 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:
```json
{
"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:**
```json
{
"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:**
```json
{
"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:**
```bash
# 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:**
```json
{
"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:
```python
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**
```bash
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**
```bash
# 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
```json
{
"detail": "Authentication credentials were not provided."
}
```
### 403 Forbidden (Missing Permission)
```json
{
"detail": "Your current plan does not include Advanced Analytics. Please upgrade your subscription to access this feature."
}
```
### 403 Forbidden (Revenue Endpoint, Missing Payments Permission)
```json
{
"error": "Payment analytics not available",
"detail": "Your plan does not include payment processing."
}
```
## Testing
### Using cURL
```bash
# 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
```python
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