# frontend/app.py (Streamlit version) # app.py import gradio as gr import cv2 import numpy as np from datetime import datetime import pandas as pd import plotly.graph_objects as go from plotly.subplots import make_subplots import random import time # Global variables for demo employees = [ {"id": 1, "name": "Alice Johnson", "department": "Engineering", "status": "present"}, {"id": 2, "name": "Bob Smith", "department": "Sales", "status": "present"}, {"id": 3, "name": "Carol White", "department": "HR", "status": "absent"}, {"id": 4, "name": "David Brown", "department": "Engineering", "status": "present"}, {"id": 5, "name": "Eve Davis", "department": "Marketing", "status": "late"}, {"id": 6, "name": "Frank Wilson", "department": "Sales", "status": "present"}, {"id": 7, "name": "Grace Lee", "department": "Engineering", "status": "absent"}, {"id": 8, "name": "Henry Kim", "department": "HR", "status": "present"}, ] # Demo detector class DemoFaceDetector: def __init__(self): self.confidence = 0.95 self.faces_detected = 0 self.last_names = [] def detect(self, image): """Simulate face detection for demo""" if image is None: return [], [] # Simulate processing self.faces_detected += 1 # Mock detection - draw boxes on image img_copy = image.copy() h, w = img_copy.shape[:2] # Draw some fake face boxes import random faces = [] names = [] num_faces = random.randint(2, 4) for i in range(num_faces): # Random positions x = random.randint(50, w-150) y = random.randint(50, h-150) face_w = random.randint(80, 120) face_h = random.randint(80, 120) # Draw rectangle cv2.rectangle(img_copy, (x, y), (x+face_w, y+face_h), (0, 255, 0), 2) # Get random employee emp = random.choice(employees) name = emp['name'] names.append(name) # Add name label cv2.putText(img_copy, name, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) # Add status status_emoji = "✅" if emp['status'] == 'present' else "⏰" if emp['status'] == 'late' else "❌" cv2.putText(img_copy, status_emoji, (x+face_w-30, y+face_h-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) faces.append({'bbox': (x, y, x+face_w, y+face_h), 'name': name}) self.last_names = names return img_copy, names # Initialize detector detector = DemoFaceDetector() def get_attendance_df(): """Get attendance as DataFrame""" return pd.DataFrame(employees) def process_frame(frame): """Process video frame for live feed""" if frame is None: return None # Convert to RGB if needed if len(frame.shape) == 2: frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2RGB) # Detect faces processed_frame, names = detector.detect(frame) # Update status simulation for emp in employees: if emp['name'] in names: emp['status'] = 'present' if random.random() > 0.1 else 'late' return processed_frame def create_dashboard(): """Create analytics dashboard with Plotly""" df = get_attendance_df() # Create subplots fig = make_subplots( rows=2, cols=2, subplot_titles=( 'Attendance by Department', 'Status Distribution', 'Daily Trend', 'Department Coverage' ), specs=[[{'type': 'bar'}, {'type': 'pie'}], [{'type': 'scatter'}, {'type': 'bar'}]] ) # 1. Department breakdown dept_counts = df.groupby('department')['status'].value_counts().unstack().fillna(0) for status in dept_counts.columns: fig.add_trace( go.Bar(name=status, x=dept_counts.index, y=dept_counts[status]), row=1, col=1 ) # 2. Status distribution status_counts = df['status'].value_counts() fig.add_trace( go.Pie(labels=status_counts.index, values=status_counts.values), row=1, col=2 ) # 3. Daily trend (simulated) dates = pd.date_range(end=datetime.now(), periods=7) trend_data = { 'date': dates, 'present': [random.randint(5, 8) for _ in range(7)], 'absent': [random.randint(0, 3) for _ in range(7)], 'late': [random.randint(0, 2) for _ in range(7)] } for key in ['present', 'absent', 'late']: fig.add_trace( go.Scatter(x=dates, y=trend_data[key], name=key, mode='lines+markers'), row=2, col=1 ) # 4. Department coverage total_dept = df.groupby('department').size().reset_index(name='total') present_dept = df[df['status'] == 'present'].groupby('department').size().reset_index(name='present') coverage = total_dept.merge(present_dept, on='department', how='left').fillna(0) coverage['coverage_pct'] = (coverage['present'] / coverage['total'] * 100).round(1) fig.add_trace( go.Bar(x=coverage['department'], y=coverage['coverage_pct'], name='Coverage %', text=coverage['coverage_pct'], textposition='auto'), row=2, col=2 ) fig.update_layout(height=800, showlegend=True, title_text="📊 Personnel Analytics Dashboard") fig.update_xaxes(title_text="Department", row=2, col=2) fig.update_yaxes(title_text="Coverage %", row=2, col=2) return fig def get_metrics(): """Get dashboard metrics""" df = get_attendance_df() total = len(df) present = len(df[df['status'] == 'present']) late = len(df[df['status'] == 'late']) absent = len(df[df['status'] == 'absent']) return { 'total': total, 'present': present, 'late': late, 'absent': absent, 'coverage': f"{(present/total*100):.1f}%" } def get_missing_personnel(): """Get list of missing employees""" df = get_attendance_df() absent_emps = df[df['status'] == 'absent']['name'].tolist() late_emps = df[df['status'] == 'late']['name'].tolist() return absent_emps, late_emps def live_feed(): """Gradio interface for live feed""" return gr.Interface( fn=process_frame, inputs=gr.Image(source="webcam", streaming=True, label="Live Feed"), outputs=gr.Image(label="Detection Results", type="numpy"), title="📹 Live Personnel Monitoring", description="Real-time face detection and recognition", live=True ) def attendance_table(): """Show attendance table""" df = get_attendance_df() return df def dashboard_view(): """Dashboard view""" metrics = get_metrics() # Create HTML for metrics html = f"""

đŸ‘Ĩ Present

{metrics['present']}

{metrics['coverage']} coverage

⏰ Late

{metrics['late']}

❌ Absent

{metrics['absent']}

📊 Coverage

{metrics['coverage']}

""" return html def alerts_view(): """Alerts and notifications""" absent_emps, late_emps = get_missing_personnel() html = "
" html += "

âš ī¸ Alerts & Notifications

" if absent_emps: html += f"""

🚨 {len(absent_emps)} Employee(s) Absent Today

" if late_emps: html += f"""

⏰ {len(late_emps)} Employee(s) Arrived Late

" if not absent_emps and not late_emps: html += """

✅ All employees present and on time!

""" html += f"""
🕐 {datetime.now().strftime("%H:%M:%S")}
""" html += "
" return html # Build Gradio Interface with gr.Blocks(title="AI Personnel Monitoring Dashboard", theme=gr.themes.Soft()) as demo: gr.Markdown(""" # đŸ‘Ĩ AI-Powered Personnel Monitoring & Attendance Dashboard Real-time face detection, attendance tracking, and workforce analytics using AI. """) # Tabs with gr.Tabs(): with gr.TabItem("📹 Live Feed"): gr.Markdown("### 📹 Real-time Face Detection") gr.Markdown("*Note: This demo simulates face detection. In production, this would use YOLO + Face Recognition.*") # Live feed interface with gr.Row(): with gr.Column(scale=2): live_output = gr.Image(label="Detection Results", type="numpy") with gr.Column(scale=1): live_status = gr.JSON(label="Status Updates") # Controls with gr.Row(): start_btn = gr.Button("â–ļī¸ Start Camera", variant="primary") stop_btn = gr.Button("âšī¸ Stop Camera", variant="stop") refresh_btn = gr.Button("🔄 Refresh") # Process video function def process_video(): cap = cv2.VideoCapture(0) if not cap.isOpened(): # Use simulated frames for i in range(30): # Create simulated frame frame = np.zeros((480, 640, 3), dtype=np.uint8) frame[:] = (30, 30, 30) # Add some face boxes for j, emp in enumerate(employees[:3]): x = 100 + j * 180 y = 150 cv2.rectangle(frame, (x, y), (x+120, y+160), (0, 255, 0), 2) cv2.putText(frame, emp['name'].split()[0], (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) yield frame, {"frame": i, "status": "simulating"} time.sleep(0.1) else: while True: ret, frame = cap.read() if not ret: break processed = process_frame(frame) yield processed, {"timestamp": datetime.now().isoformat()} time.sleep(0.05) cap.release() start_btn.click( process_video, outputs=[live_output, live_status] ) with gr.TabItem("📊 Dashboard"): gr.Markdown("### 📊 Real-time Analytics Dashboard") # Metrics row metrics_html = gr.HTML() # Dashboard chart with gr.Row(): with gr.Column(scale=2): dashboard_plot = gr.Plot(label="Analytics Dashboard") with gr.Column(scale=1): metrics_html_2 = gr.HTML() # Refresh button refresh_dashboard = gr.Button("🔄 Update Dashboard") def update_dashboard(): return create_dashboard(), dashboard_view() refresh_dashboard.click( update_dashboard, outputs=[dashboard_plot, metrics_html] ) # Initial load gr.load(fn=create_dashboard, outputs=dashboard_plot) gr.load(fn=dashboard_view, outputs=metrics_html) with gr.TabItem("📋 Attendance"): gr.Markdown("### 📋 Today's Attendance Log") attendance_table = gr.Dataframe( headers=["ID", "Name", "Department", "Status"], interactive=False ) with gr.Row(): export_btn = gr.Button("đŸ“Ĩ Export CSV", variant="primary") filter_btn = gr.Button("🔍 Filter") def update_attendance(): df = get_attendance_df() return df gr.load(fn=update_attendance, outputs=attendance_table) with gr.TabItem("âš ī¸ Alerts"): gr.Markdown("### âš ī¸ Missing Personnel Alerts") alerts_html = gr.HTML() refresh_alerts = gr.Button("🔄 Check Alerts") refresh_alerts.click( alerts_view, outputs=alerts_html ) gr.load(fn=alerts_view, outputs=alerts_html) with gr.TabItem("📈 Analytics"): gr.Markdown("### 📈 Staffing Trends & Analytics") # Additional analytics with gr.Row(): with gr.Column(): dept_chart = gr.Plot(label="Department Performance") with gr.Column(): trend_chart = gr.Plot(label="Attendance Trend") def create_analytics(): df = get_attendance_df() # Department performance dept_fig = go.Figure() dept_data = df.groupby('department').size().reset_index(name='total') dept_present = df[df['status'] == 'present'].groupby('department').size().reset_index(name='present') dept_merged = dept_data.merge(dept_present, on='department', how='left').fillna(0) dept_merged['coverage'] = (dept_merged['present'] / dept_merged['total'] * 100).round(1) dept_fig.add_trace(go.Bar(x=dept_merged['department'], y=dept_merged['coverage'], text=dept_merged['coverage'], textposition='auto', marker_color=dept_merged['coverage'].apply( lambda x: '#00cc66' if x >= 80 else '#ffa500' if x >= 50 else '#ff4444' ))) dept_fig.update_layout(title="Department Coverage %", yaxis_title="Coverage %") # Trend chart trend_fig = go.Figure() dates = pd.date_range(end=datetime.now(), periods=7) for status in ['present', 'late', 'absent']: trend_fig.add_trace(go.Scatter( x=dates, y=[random.randint(2, 8) for _ in range(7)], name=status.capitalize(), mode='lines+markers' )) trend_fig.update_layout(title="7-Day Attendance Trend", yaxis_title="Employees") return dept_fig, trend_fig gr.load(fn=create_analytics, outputs=[dept_chart, trend_chart]) # Footer gr.Markdown(""" --- ### đŸŽ¯ About This Demo This is a **simulated demonstration** of an AI-powered attendance system. The live feed uses simulated face detection to showcase the dashboard interface. **Real implementation features:** - ✅ YOLOv8 for face detection - ✅ FaceNet/ArcFace for recognition - ✅ PostgreSQL attendance database - ✅ Real-time analytics and alerts - ✅ Shift coverage and staffing trends Built with â¤ī¸ using Gradio, OpenCV, and Plotly """) # Launch the app if __name__ == "__main__": demo.launch()