Spaces:
Sleeping
Sleeping
| """ | |
| Analytics & Statistics Endpoints | |
| """ | |
| from fastapi import APIRouter, Depends, HTTPException, status, Query | |
| from sqlalchemy.orm import Session | |
| from sqlalchemy import func, and_ | |
| from typing import Dict, Any | |
| from datetime import datetime, timedelta | |
| import logging | |
| from app.api.deps import get_db, get_current_user, get_current_active_user | |
| from app.core.permissions import require_role | |
| from app.models.user import User | |
| from app.models.client import Client | |
| from app.models.contractor import Contractor | |
| from app.models.ticket import Ticket | |
| from app.models.ticket_assignment import TicketAssignment | |
| from app.models.project import Project | |
| from app.models.timesheet import Timesheet | |
| from app.models.audit_log import AuditLog | |
| logger = logging.getLogger(__name__) | |
| router = APIRouter() | |
| def get_platform_admin_dashboard_stats( | |
| db: Session = Depends(get_db), | |
| current_user: User = Depends(get_current_user) | |
| ) -> Dict[str, Any]: | |
| """ | |
| Get comprehensive statistics for Platform Admin dashboard | |
| Returns: | |
| - User statistics (total, active, by role) | |
| - Organization statistics (clients, contractors) | |
| - Ticket statistics (total, by status, by type) | |
| - Project statistics (total, active) | |
| - Recent activity (latest audit logs) | |
| """ | |
| # User Statistics | |
| total_users = db.query(func.count(User.id)).scalar() | |
| active_users = db.query(func.count(User.id)).filter(User.is_active == True).scalar() | |
| users_by_role = db.query( | |
| User.role, | |
| func.count(User.id) | |
| ).group_by(User.role).all() | |
| user_stats = { | |
| "total": total_users, | |
| "active": active_users, | |
| "inactive": total_users - active_users, | |
| "by_role": {role: count for role, count in users_by_role} | |
| } | |
| # Organization Statistics | |
| total_clients = db.query(func.count(Client.id)).scalar() | |
| active_clients = db.query(func.count(Client.id)).filter(Client.is_active == True).scalar() | |
| total_contractors = db.query(func.count(Contractor.id)).scalar() | |
| active_contractors = db.query(func.count(Contractor.id)).filter(Contractor.is_active == True).scalar() | |
| organization_stats = { | |
| "clients": { | |
| "total": total_clients, | |
| "active": active_clients, | |
| "inactive": total_clients - active_clients | |
| }, | |
| "contractors": { | |
| "total": total_contractors, | |
| "active": active_contractors, | |
| "inactive": total_contractors - active_contractors | |
| } | |
| } | |
| # Ticket Statistics | |
| total_tickets = db.query(func.count(Ticket.id)).scalar() | |
| tickets_by_status = db.query( | |
| Ticket.status, | |
| func.count(Ticket.id) | |
| ).group_by(Ticket.status).all() | |
| tickets_by_type = db.query( | |
| Ticket.ticket_type, | |
| func.count(Ticket.id) | |
| ).group_by(Ticket.ticket_type).all() | |
| ticket_stats = { | |
| "total": total_tickets, | |
| "by_status": {status: count for status, count in tickets_by_status}, | |
| "by_type": {ticket_type: count for ticket_type, count in tickets_by_type} | |
| } | |
| # Project Statistics | |
| total_projects = db.query(func.count(Project.id)).scalar() | |
| active_projects = db.query(func.count(Project.id)).filter( | |
| Project.status.in_(["planning", "active", "on_hold"]) | |
| ).scalar() | |
| project_stats = { | |
| "total": total_projects, | |
| "active": active_projects | |
| } | |
| # Assignment Statistics | |
| total_assignments = db.query(func.count(TicketAssignment.id)).scalar() | |
| active_assignments = db.query(func.count(TicketAssignment.id)).filter( | |
| TicketAssignment.status.in_(["assigned", "en_route", "in_progress"]) | |
| ).scalar() | |
| assignment_stats = { | |
| "total": total_assignments, | |
| "active": active_assignments | |
| } | |
| # Recent Activity (last 10 audit logs) | |
| recent_activity = db.query(AuditLog).order_by( | |
| AuditLog.created_at.desc() | |
| ).limit(10).all() | |
| activity_items = [{ | |
| "id": str(log.id), | |
| "user_email": log.user_email, | |
| "action": log.action, | |
| "entity_type": log.entity_type, | |
| "description": log.description, | |
| "created_at": log.created_at | |
| } for log in recent_activity] | |
| # System Health | |
| thirty_days_ago = (datetime.utcnow() - timedelta(days=30)).isoformat() | |
| new_users_30d = db.query(func.count(User.id)).filter( | |
| User.created_at >= thirty_days_ago | |
| ).scalar() | |
| new_tickets_30d = db.query(func.count(Ticket.id)).filter( | |
| Ticket.created_at >= thirty_days_ago | |
| ).scalar() | |
| system_health = { | |
| "new_users_last_30_days": new_users_30d, | |
| "new_tickets_last_30_days": new_tickets_30d | |
| } | |
| return { | |
| "users": user_stats, | |
| "organizations": organization_stats, | |
| "tickets": ticket_stats, | |
| "projects": project_stats, | |
| "assignments": assignment_stats, | |
| "recent_activity": activity_items, | |
| "system_health": system_health | |
| } | |
| def get_user_overview( | |
| limit: int = Query(50, ge=1, le=100, description="Max items in work queue (for field agents)"), | |
| db: Session = Depends(get_db), | |
| current_user: User = Depends(get_current_active_user) | |
| ) -> Dict[str, Any]: | |
| """ | |
| Get user overview dashboard (cross-project aggregated stats) | |
| Perfect for landing page / home dashboard. | |
| Shows aggregated statistics across all projects the user has access to. | |
| Returns different stats based on user role: | |
| - Projects (total, active) | |
| - Team members (for managers) | |
| - Tickets (total, open, in_progress) | |
| - Unread notifications | |
| - Expenses (for managers/admins) | |
| - Sales orders (for sales roles) | |
| - Inventory (for managers/admins) | |
| **For Field Agents/Sales Agents:** | |
| - field_agent_stats: hours worked, pending expenses, inventory on hand, tickets completed this week | |
| - work_queue: List of pending ticket assignments sorted by execution order | |
| **Query Parameters:** | |
| - limit: Max items in work queue (default 50, max 100) | |
| **Authorization:** Any authenticated user | |
| """ | |
| try: | |
| from app.services.dashboard_service import DashboardService | |
| overview = DashboardService.get_user_overview(db, current_user, limit) | |
| return overview | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Failed to get user overview: {str(e)}", exc_info=True) | |
| raise HTTPException( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| detail=f"Failed to get user overview: {str(e)}" | |
| ) | |