""" Command-line interface for AutoAttendance system. Provides a professional CLI with subcommands. """ import argparse import sys import os from pathlib import Path from datetime import datetime from logger import setup_logger, get_logger, log_system_event from database import AttendanceDatabase from face_recognition import FaceRecognitionModule from config import ( DATABASE_PATH, FACE_DATA_DIR, ATTENDANCE_DIR, MODELS_DIR, CAMERA_ID, ) # Setup logger setup_logger() logger = get_logger() class Colors: """ANSI color codes for terminal output.""" HEADER = "\033[95m" BLUE = "\033[94m" CYAN = "\033[96m" GREEN = "\033[92m" WARNING = "\033[93m" RED = "\033[91m" ENDC = "\033[0m" BOLD = "\033[1m" def print_header(text: str) -> None: """Print a formatted header.""" print(f"\n{Colors.HEADER}{'=' * 60}{Colors.ENDC}") print(f"{Colors.BOLD}{text:^60}{Colors.ENDC}") print(f"{Colors.HEADER}{'=' * 60}{Colors.ENDC}\n") def print_success(text: str) -> None: """Print success message.""" print(f"{Colors.GREEN}✓ {text}{Colors.ENDC}") def print_error(text: str) -> None: """Print error message.""" print(f"{Colors.RED}✗ {text}{Colors.ENDC}") def print_warning(text: str) -> None: """Print warning message.""" print(f"{Colors.WARNING}⚠ {text}{Colors.ENDC}") def print_info(text: str) -> None: """Print info message.""" print(f"{Colors.CYAN}ℹ {text}{Colors.ENDC}") def cmd_collect(args) -> int: """Collect face samples for training.""" from data_collection import DataCollectionModule print_header("Face Data Collection") collection = DataCollectionModule() person_name = args.name if not person_name: person_name = input(f"{Colors.CYAN}Enter person's name: {Colors.ENDC}").strip() if not person_name: print_error("Name cannot be empty") return 1 num_samples = args.samples or 80 print_info(f"Capturing {num_samples} samples for '{person_name}'...") collection.capture_face_samples(person_name, num_samples=num_samples, camera_id=CAMERA_ID) print_success(f"Data collection complete for {person_name}") log_system_event("info", "Face data collected", person=person_name, samples=num_samples) return 0 def cmd_train(args) -> int: """Train the face recognition model.""" print_header("Model Training") recognizer = FaceRecognitionModule() print_info("Registering face embeddings...") people_count, embedding_count = recognizer.train_from_directory() if embedding_count == 0: print_error("No usable face images found!") print_info("Run 'python cli.py collect --name ' first") return 1 print_success(f"Training complete!") print(f" People registered: {Colors.GREEN}{people_count}{Colors.ENDC}") print(f" Embeddings created: {Colors.GREEN}{embedding_count}{Colors.ENDC}") log_system_event("info", "Model trained", people=people_count, embeddings=embedding_count) return 0 def cmd_run(args) -> int: """Run the attendance system.""" from main import AttendanceSystem print_header("Starting Attendance System") system = AttendanceSystem() print_info("Press 'q' to quit, 's' to export report") print_info(f"Camera ID: {CAMERA_ID}") print_info(f"Database: {DATABASE_PATH}") system.run() print_success("System shutdown complete") log_system_event("info", "System stopped") return 0 def cmd_status(args) -> int: """Show system status and statistics.""" print_header("System Status") db = AttendanceDatabase() # Student statistics students = db.list_students() print(f"{Colors.BOLD}Students:{Colors.ENDC} {len(students)}") for student in students: name = student.get("name", "Unknown") embeddings = student.get("embedding_count", 0) status = student.get("status", "unknown") status_color = Colors.GREEN if status == "active" else Colors.WARNING print(f" • {name}: {embeddings} embeddings [{status_color}{status}{Colors.ENDC}]") # Today's attendance today = datetime.now().strftime("%Y-%m-%d") attendance = db.list_attendance(date=today, limit=1000) print(f"\n{Colors.BOLD}Today's Attendance ({today}):{Colors.ENDC} {len(attendance)}") for record in attendance: name = record.get("student_name", "Unknown") time = record.get("time", "N/A") print(f" • {name} at {time}") # Recent alerts alerts = db.list_alerts(limit=5) print(f"\n{Colors.BOLD}Recent Alerts:{Colors.ENDC} {len(alerts)}") if alerts: for alert in alerts: alert_type = alert.get("alert_type", "unknown") message = alert.get("message", "")[:50] created = alert.get("created_at", "")[:19] print(f" • [{alert_type}] {message}... ({created})") else: print(" No recent alerts") # Storage info db_path = Path(DATABASE_PATH) if db_path.exists(): size_mb = db_path.stat().st_size / (1024 * 1024) print(f"\n{Colors.BOLD}Database Size:{Colors.ENDC} {size_mb:.2f} MB") log_system_event("info", "Status checked") return 0 def cmd_export(args) -> int: """Export attendance data.""" print_header("Exporting Attendance Data") db = AttendanceDatabase() date = args.date or datetime.now().strftime("%Y-%m-%d") attendance = db.list_attendance(date=date, limit=10000) if not attendance: print_warning(f"No attendance records for {date}") return 1 # Export to CSV import pandas as pd output_dir = Path(ATTENDANCE_DIR) output_dir.mkdir(parents=True, exist_ok=True) filename = f"attendance_export_{date}.csv" filepath = output_dir / filename df = pd.DataFrame(attendance) df.to_csv(filepath, index=False) print_success(f"Exported {len(attendance)} records") print(f" File: {filepath}") log_system_event("info", "Attendance exported", date=date, records=len(attendance)) return 0 def cmd_api(args) -> int: """Start the API server.""" import uvicorn print_header("Starting API Server") host = args.host or "0.0.0.0" port = args.port or 8000 print_info(f"Server starting at http://{host}:{port}") print_info("Dashboard: http://localhost:8000/") print_info("API docs: http://localhost:8000/docs") print_info("Press Ctrl+C to stop") log_system_event("info", "API server starting", host=host, port=port) uvicorn.run( "api:app", host=host, port=port, reload=args.reload, log_level="info" ) return 0 def cmd_setup(args) -> int: """Run system setup wizard.""" import subprocess print_header("Running Setup") try: result = subprocess.run([sys.executable, "setup.py"], capture_output=False) return result.returncode except Exception as e: print_error(f"Setup failed: {e}") return 1 def cmd_test(args) -> int: """Run system diagnostics.""" print_header("System Diagnostics") errors = [] warnings = [] # Check Python version print_info("Checking Python version...") version = sys.version_info if version.major < 3 or (version.major == 3 and version.minor < 8): errors.append("Python 3.8+ required") else: print_success(f"Python {version.major}.{version.minor}.{version.micro}") # Check dependencies print_info("Checking dependencies...") required = { "cv2": "opencv-python", "numpy": "numpy", "pandas": "pandas", "insightface": "insightface", "fastapi": "fastapi", "uvicorn": "uvicorn", } for import_name, package_name in required.items(): try: __import__(import_name) print_success(f"{package_name}") except ImportError: errors.append(f"{package_name} not installed") print_error(f"{package_name} NOT installed") # Check directories print_info("Checking directories...") dirs = [ ("data/faces", "Face data"), ("data/attendance", "Attendance records"), ("models", "Model storage"), ] for path, desc in dirs: if os.path.isdir(path): print_success(f"{desc}: {path}") else: warnings.append(f"{desc} directory missing: {path}") print_warning(f"{desc}: {path} (missing)") # Check database print_info("Checking database...") db_path = Path(DATABASE_PATH) if db_path.exists(): print_success(f"Database exists: {db_path}") db = AttendanceDatabase() students = db.list_students() print(f" {len(students)} registered students") else: warnings.append("Database not initialized") print_warning("Database not initialized (run 'python train_model.py')") # Summary print("\n" + "=" * 60) if errors: print(f"{Colors.RED}Errors: {len(errors)}{Colors.ENDC}") for e in errors: print(f" • {e}") if warnings: print(f"{Colors.WARNING}Warnings: {len(warnings)}{Colors.ENDC}") for w in warnings: print(f" • {w}") if not errors and not warnings: print_success("All checks passed!") return 0 elif not errors: return 0 else: return 1 def main(): """Main CLI entry point.""" parser = argparse.ArgumentParser( prog="autoattendance", description="Face Recognition Attendance System CLI", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: python cli.py status Show system status python cli.py collect --name John Collect face data python cli.py train Train the model python cli.py run Start attendance system python cli.py api Start API server python cli.py test Run diagnostics """ ) subparsers = parser.add_subparsers(dest="command", help="Available commands") # Collect command collect_parser = subparsers.add_parser("collect", help="Collect face samples") collect_parser.add_argument("--name", "-n", help="Person's name") collect_parser.add_argument("--samples", "-s", type=int, help="Number of samples (default: 80)") collect_parser.set_defaults(func=cmd_collect) # Train command train_parser = subparsers.add_parser("train", help="Train face recognition model") train_parser.set_defaults(func=cmd_train) # Run command run_parser = subparsers.add_parser("run", help="Run the attendance system") run_parser.set_defaults(func=cmd_run) # Status command status_parser = subparsers.add_parser("status", help="Show system status") status_parser.set_defaults(func=cmd_status) # Export command export_parser = subparsers.add_parser("export", help="Export attendance data") export_parser.add_argument("--date", "-d", help="Date (YYYY-MM-DD, default: today)") export_parser.set_defaults(func=cmd_export) # API command api_parser = subparsers.add_parser("api", help="Start API server") api_parser.add_argument("--host", default="0.0.0.0", help="Host address") api_parser.add_argument("--port", "-p", type=int, default=8000, help="Port number") api_parser.add_argument("--reload", action="store_true", help="Enable auto-reload") api_parser.set_defaults(func=cmd_api) # Setup command setup_parser = subparsers.add_parser("setup", help="Run setup wizard") setup_parser.set_defaults(func=cmd_setup) # Test command test_parser = subparsers.add_parser("test", help="Run system diagnostics") test_parser.set_defaults(func=cmd_test) # Parse arguments args = parser.parse_args() if args.command is None: parser.print_help() return 0 # Execute command try: return args.func(args) except KeyboardInterrupt: print("\n\nOperation cancelled by user.") return 130 except Exception as e: print_error(f"Error: {e}") logger.exception("CLI error") return 1 if __name__ == "__main__": sys.exit(main())