Download backend/app/api/v1/analytics.py from NetrAai07/netra: direct link, hf CLI and curl.
- Browser
- Download file 6.23 kB
-
https://huggingface.co/spaces/NetrAai07/netra/resolve/main/backend/app/api/v1/analytics.py
- Command line
-
hf download hf://spaces/NetrAai07/netra/backend/app/api/v1/analytics.py
-
curl -L -o analytics.py https://huggingface.co/spaces/NetrAai07/netra/resolve/main/backend/app/api/v1/analytics.py
6.23 kB
| from fastapi import APIRouter, Depends, HTTPException, status | |
| from fastapi.responses import StreamingResponse | |
| from sqlalchemy.orm import Session | |
| from sqlalchemy import select, func, and_, desc | |
| from datetime import date, datetime, timedelta | |
| from typing import Dict, Any, List | |
| import asyncio | |
| import json | |
| from app.core.database import get_db | |
| from app.core import security | |
| from app.core.security import RoleCheckerSSE | |
| from app.core import event_bus | |
| from app.crud import crud | |
| from app.schemas import schemas | |
| from app.models import models | |
| router = APIRouter() | |
| checker_view = security.RoleChecker(["Super Admin", "Admin", "HR"]) | |
| def get_dashboard_summary( | |
| db: Session = Depends(get_db), | |
| current_user: models.User = Depends(checker_view) | |
| ): | |
| today = date.today() | |
| # 1. Total Employees | |
| total_employees = db.query(func.count(models.Employee.id)).filter(models.Employee.status == "Active").scalar() or 0 | |
| # 2. Present Today (Status = Present or Late or Half Day) | |
| present_today = db.query(func.count(models.Attendance.id)).filter( | |
| and_( | |
| models.Attendance.date == today, | |
| models.Attendance.status.in_(["Present", "Late", "Half Day"]) | |
| ) | |
| ).scalar() or 0 | |
| # 3. Late Today | |
| late_today = db.query(func.count(models.Attendance.id)).filter( | |
| and_( | |
| models.Attendance.date == today, | |
| models.Attendance.status == "Late" | |
| ) | |
| ).scalar() or 0 | |
| # 4. Checked Out | |
| checked_out_today = db.query(func.count(models.Attendance.id)).filter( | |
| and_( | |
| models.Attendance.date == today, | |
| models.Attendance.check_out.isnot(None) | |
| ) | |
| ).scalar() or 0 | |
| # 5. Absent Today | |
| # Active employees minus present today | |
| absent_today = max(0, total_employees - present_today) | |
| # 6. Attendance Rate | |
| attendance_percentage = (present_today / total_employees * 100) if total_employees > 0 else 0.0 | |
| return { | |
| "total_employees": total_employees, | |
| "present_today": present_today, | |
| "absent_today": absent_today, | |
| "late_today": late_today, | |
| "checked_out_today": checked_out_today, | |
| "attendance_percentage": round(attendance_percentage, 1) | |
| } | |
| def get_attendance_trends( | |
| days: int = 7, | |
| db: Session = Depends(get_db), | |
| current_user: models.User = Depends(checker_view) | |
| ): | |
| """ | |
| Returns daily stats for the last N days (date, present, late, absent) for ECharts. | |
| """ | |
| end_date = date.today() | |
| start_date = end_date - timedelta(days=days - 1) | |
| # Generate list of dates | |
| date_list = [start_date + timedelta(days=i) for i in range(days)] | |
| # Get total active employees | |
| total_active = db.query(func.count(models.Employee.id)).filter(models.Employee.status == "Active").scalar() or 0 | |
| trends = [] | |
| for d in date_list: | |
| present = db.query(func.count(models.Attendance.id)).filter( | |
| and_( | |
| models.Attendance.date == d, | |
| models.Attendance.status.in_(["Present", "Late", "Half Day"]) | |
| ) | |
| ).scalar() or 0 | |
| late = db.query(func.count(models.Attendance.id)).filter( | |
| and_( | |
| models.Attendance.date == d, | |
| models.Attendance.status == "Late" | |
| ) | |
| ).scalar() or 0 | |
| absent = max(0, total_active - present) | |
| trends.append({ | |
| "date": d.isoformat(), | |
| "present": present, | |
| "late": late, | |
| "absent": absent | |
| }) | |
| return trends | |
| def get_department_distribution( | |
| db: Session = Depends(get_db), | |
| current_user: models.User = Depends(checker_view) | |
| ): | |
| """ | |
| Returns employee and attendance counts by department for ECharts. | |
| """ | |
| today = date.today() | |
| departments = db.query(models.Department).all() | |
| dist = [] | |
| for dept in departments: | |
| # Total active in dept | |
| total_in_dept = db.query(func.count(models.Employee.id)).filter( | |
| and_( | |
| models.Employee.department_id == dept.id, | |
| models.Employee.status == "Active" | |
| ) | |
| ).scalar() or 0 | |
| # Present today in dept | |
| present_in_dept = db.query(func.count(models.Attendance.id)).join(models.Employee).filter( | |
| and_( | |
| models.Employee.department_id == dept.id, | |
| models.Attendance.date == today, | |
| models.Attendance.status.in_(["Present", "Late", "Half Day"]) | |
| ) | |
| ).scalar() or 0 | |
| dist.append({ | |
| "id": dept.id, | |
| "department": dept.name, | |
| "code": dept.code, | |
| "total_employees": total_in_dept, | |
| "present_today": present_in_dept | |
| }) | |
| return dist | |
| def get_recent_activity( | |
| limit: int = 10, | |
| db: Session = Depends(get_db), | |
| current_user: models.User = Depends(checker_view) | |
| ): | |
| """ | |
| Returns latest kiosk scan logs for the dashboard tickfeed. | |
| """ | |
| cutoff = datetime.utcnow() - timedelta(hours=24) | |
| return db.query(models.AttendanceLog).filter(models.AttendanceLog.timestamp >= cutoff).order_by(desc(models.AttendanceLog.timestamp)).limit(limit).all() | |
| async def live_stream( | |
| current_user: models.User = Depends(RoleCheckerSSE(["Super Admin", "Admin", "HR"])) | |
| ): | |
| """ | |
| Real-time Server-Sent Events (SSE) feed of kiosk scans. | |
| """ | |
| async def event_generator(): | |
| queue = event_bus.subscribe() | |
| try: | |
| while True: | |
| # Wait for next event published to the bus | |
| event_data = await queue.get() | |
| # Yield standard SSE formatted message | |
| yield f"data: {json.dumps(event_data)}\n\n" | |
| except asyncio.CancelledError: | |
| # Client disconnected | |
| pass | |
| finally: | |
| event_bus.unsubscribe(queue) | |
| return StreamingResponse(event_generator(), media_type="text/event-stream") | |