autoform-backend / app /routes /analytics.py
Arslan1997's picture
made it faster and added color picker
39bbebf
Raw
History Blame Contribute Delete
5.99 kB
"""
Analytics API Routes
====================
Routes for form analytics and tracking.
"""
from fastapi import APIRouter, Depends, HTTPException, status, Request
from sqlalchemy.orm import Session
from typing import Optional
from datetime import datetime
from pydantic import BaseModel
from ..core.db import get_db
from ..core.security import get_current_user
from ..models import User, Form, PublicForm
from ..services.analytics_service import analytics_service
import logging
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api", tags=["analytics"])
class TrackEventRequest(BaseModel):
"""Request model for tracking analytics events"""
event_type: str
session_id: str
question_id: Optional[int] = None
time_spent: Optional[int] = None
@router.post("/public/forms/{token}/track")
async def track_public_event(
token: str,
payload: TrackEventRequest, # Expects JSON body with event_type, session_id, question_id, time_spent
request: Request,
db: Session = Depends(get_db)
):
"""
Public endpoint for client-side analytics tracking.
No authentication required. Accepts JSON body with tracking data.
"""
# Find public form by token
public_form = db.query(PublicForm).filter(
PublicForm.share_token == token,
PublicForm.is_public == True
).first()
if not public_form:
# Silently fail for analytics - don't break form
logger.warning(f"Analytics tracking: form not found for token {token}")
return {"success": False, "message": "Form not found"}
try:
# Track event
await analytics_service.track_event(
db=db,
event_type=payload.event_type,
form_id=public_form.form_id,
session_id=payload.session_id,
request=request,
question_id=payload.question_id,
time_spent_seconds=payload.time_spent
)
return {"success": True, "message": "Event tracked"}
except Exception as e:
logger.error(f"Analytics tracking failed: {e}", exc_info=True)
# Silently fail - analytics shouldn't break form
return {"success": False, "message": "Tracking failed"}
@router.get("/forms/{form_id}/analytics/funnel")
async def get_form_funnel(
form_id: int,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
start_date: Optional[str] = None,
end_date: Optional[str] = None,
question_ids: Optional[str] = None,
countries: Optional[str] = None,
utm_source: Optional[str] = None
):
"""
Get funnel analytics for form owner with advanced filtering.
Requires authentication.
"""
# Verify form ownership
form = db.query(Form).filter(
Form.id == form_id,
Form.user_id == current_user.id
).first()
if not form:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Form not found"
)
# Parse dates
start_dt = datetime.fromisoformat(start_date) if start_date else None
end_dt = datetime.fromisoformat(end_date) if end_date else None
# Parse question IDs
question_id_list = [int(x) for x in question_ids.split(',')] if question_ids else None
# Parse countries
country_list = [x.strip() for x in countries.split(',')] if countries else None
# Get funnel analytics
funnel_data = await analytics_service.get_funnel_analytics(
db=db,
form_id=form_id,
start_date=start_dt,
end_date=end_dt,
question_ids=question_id_list,
countries=country_list,
utm_source=utm_source
)
return funnel_data
@router.get("/forms/{form_id}/analytics/timeseries")
async def get_time_series(
form_id: int,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
start_date: Optional[str] = None,
end_date: Optional[str] = None,
question_ids: Optional[str] = None,
countries: Optional[str] = None,
utm_source: Optional[str] = None
):
"""
Get time-series data for views and submissions with filtering.
Requires authentication.
"""
# Verify form ownership
form = db.query(Form).filter(
Form.id == form_id,
Form.user_id == current_user.id
).first()
if not form:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Form not found"
)
# Parse dates
start_dt = datetime.fromisoformat(start_date) if start_date else None
end_dt = datetime.fromisoformat(end_date) if end_date else None
# Parse question IDs
question_id_list = [int(x) for x in question_ids.split(',')] if question_ids else None
# Parse countries
country_list = [x.strip() for x in countries.split(',')] if countries else None
# Get time-series data
time_series_data = await analytics_service.get_time_series_data(
db=db,
form_id=form_id,
start_date=start_dt,
end_date=end_dt,
question_ids=question_id_list,
countries=country_list,
utm_source=utm_source
)
return time_series_data
@router.get("/forms/{form_id}/analytics/summary")
async def get_analytics_summary(
form_id: int,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""
Get high-level analytics summary.
Requires authentication.
"""
# Verify form ownership
form = db.query(Form).filter(
Form.id == form_id,
Form.user_id == current_user.id
).first()
if not form:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Form not found"
)
# Get summary analytics
summary_data = await analytics_service.get_summary_analytics(
db=db,
form_id=form_id
)
return summary_data