IDP-Machine-learning / run_project.py
mrrobot2610's picture
Initial commit: IDP (Intelligent Document Processing) System
1a7ee60
Raw History Blame Contribute Delete
15.2 kB
#!/usr/bin/env python3
"""
IDP System Project Runner
=========================
Unified script to run the complete IDP (Intelligent Document Processing) system.
This script:
1. Checks prerequisites (Python, Node.js, dependencies)
2. Starts the FastAPI backend server
3. Starts the Next.js frontend development server
4. Monitors both processes
5. Handles graceful shutdown
Usage:
python run_project.py [options]
Options:
--backend-only Run only the backend server
--frontend-only Run only the frontend server
--port-backend Backend port (default: 7860)
--port-frontend Frontend port (default: 3000)
--no-check Skip dependency checks
--help Show this help message
"""
import os
import sys
import subprocess
import time
import signal
import argparse
import shutil
from pathlib import Path
from typing import Optional, List
# Color codes for terminal output
class Colors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
class ProjectRunner:
def __init__(self, args):
self.args = args
self.backend_process: Optional[subprocess.Popen] = None
self.frontend_process: Optional[subprocess.Popen] = None
self.project_root = Path(__file__).parent.absolute()
self.frontend_dir = self.project_root / "frontend"
# Environment variables for backend
self.backend_env = os.environ.copy()
self.backend_env.update({
'KMP_DUPLICATE_LIB_OK': 'TRUE',
'OMP_NUM_THREADS': '1',
'OPENBLAS_NUM_THREADS': '1',
'MKL_NUM_THREADS': '1',
'VECLIB_MAXIMUM_THREADS': '1',
'NUMEXPR_NUM_THREADS': '1',
'TF_CPP_MIN_LOG_LEVEL': '2'
})
def print_header(self, message: str):
"""Print a formatted header message."""
print(f"\n{Colors.HEADER}{Colors.BOLD}{'=' * 70}{Colors.ENDC}")
print(f"{Colors.HEADER}{Colors.BOLD}{message:^70}{Colors.ENDC}")
print(f"{Colors.HEADER}{Colors.BOLD}{'=' * 70}{Colors.ENDC}\n")
def print_success(self, message: str):
"""Print a success message."""
print(f"{Colors.OKGREEN}βœ“ {message}{Colors.ENDC}")
def print_info(self, message: str):
"""Print an info message."""
print(f"{Colors.OKCYAN}β„Ή {message}{Colors.ENDC}")
def print_warning(self, message: str):
"""Print a warning message."""
print(f"{Colors.WARNING}⚠ {message}{Colors.ENDC}")
def print_error(self, message: str):
"""Print an error message."""
print(f"{Colors.FAIL}βœ— {message}{Colors.ENDC}")
def check_command(self, command: str) -> bool:
"""Check if a command is available in PATH."""
return shutil.which(command) is not None
def check_prerequisites(self):
"""Check if all prerequisites are installed."""
if self.args.no_check:
self.print_info("Skipping prerequisite checks (--no-check flag)")
return True
self.print_header("Checking Prerequisites")
all_ok = True
# Check Python
if sys.version_info < (3, 10):
self.print_error(f"Python 3.10+ required (found {sys.version_info.major}.{sys.version_info.minor})")
all_ok = False
else:
self.print_success(f"Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
# Check Node.js (only if frontend needed)
if not self.args.backend_only:
if self.check_command("node"):
result = subprocess.run(["node", "--version"], capture_output=True, text=True)
version = result.stdout.strip()
self.print_success(f"Node.js {version}")
else:
self.print_error("Node.js not found (required for frontend)")
all_ok = False
# Check npm (only if frontend needed)
if not self.args.backend_only:
if self.check_command("npm"):
result = subprocess.run(["npm", "--version"], capture_output=True, text=True)
version = result.stdout.strip()
self.print_success(f"npm {version}")
else:
self.print_error("npm not found (required for frontend)")
all_ok = False
# Check Python dependencies
if not self.args.frontend_only:
try:
import fastapi
self.print_success(f"FastAPI {fastapi.__version__}")
except ImportError:
self.print_warning("FastAPI not installed - run: pip install -r requirements.txt")
all_ok = False
try:
import torch
self.print_success(f"PyTorch {torch.__version__}")
except ImportError:
self.print_warning("PyTorch not installed - run: pip install -r requirements.txt")
all_ok = False
# Check frontend dependencies
if not self.args.backend_only:
node_modules = self.frontend_dir / "node_modules"
if not node_modules.exists():
self.print_warning("Frontend dependencies not installed - run: cd frontend && npm install")
all_ok = False
else:
self.print_success("Frontend dependencies installed")
if not all_ok:
self.print_error("\nSome prerequisites are missing. Please install them and try again.")
return False
self.print_success("\nAll prerequisites check passed!")
return True
def start_backend(self):
"""Start the FastAPI backend server."""
if self.args.frontend_only:
return
self.print_header("Starting Backend Server")
self.print_info(f"Port: {self.args.port_backend}")
self.print_info(f"Working directory: {self.project_root}")
api_server_path = self.project_root / "api_server.py"
if not api_server_path.exists():
self.print_error(f"api_server.py not found at {api_server_path}")
sys.exit(1)
cmd = [
sys.executable,
"api_server.py"
]
try:
self.backend_process = subprocess.Popen(
cmd,
cwd=self.project_root,
env=self.backend_env,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True,
bufsize=1
)
self.print_success("Backend server started!")
self.print_info(f"API available at: http://localhost:{self.args.port_backend}")
self.print_info(f"API docs at: http://localhost:{self.args.port_backend}/docs")
# Give it a moment to start
time.sleep(2)
# Check if it's still running
if self.backend_process.poll() is not None:
self.print_error("Backend server failed to start")
return False
return True
except Exception as e:
self.print_error(f"Failed to start backend: {e}")
return False
def start_frontend(self):
"""Start the Next.js frontend development server."""
if self.args.backend_only:
return
self.print_header("Starting Frontend Server")
self.print_info(f"Port: {self.args.port_frontend}")
self.print_info(f"Working directory: {self.frontend_dir}")
if not self.frontend_dir.exists():
self.print_error(f"Frontend directory not found at {self.frontend_dir}")
sys.exit(1)
# Set the API URL environment variable
frontend_env = os.environ.copy()
frontend_env['NEXT_PUBLIC_API_URL'] = f"http://localhost:{self.args.port_backend}"
frontend_env['PORT'] = str(self.args.port_frontend)
cmd = ["npm", "run", "dev"]
try:
self.frontend_process = subprocess.Popen(
cmd,
cwd=self.frontend_dir,
env=frontend_env,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True,
bufsize=1
)
self.print_success("Frontend server started!")
self.print_info(f"Application available at: http://localhost:{self.args.port_frontend}")
# Give it a moment to start
time.sleep(3)
# Check if it's still running
if self.frontend_process.poll() is not None:
self.print_error("Frontend server failed to start")
return False
return True
except Exception as e:
self.print_error(f"Failed to start frontend: {e}")
return False
def monitor_processes(self):
"""Monitor running processes and display their output."""
self.print_header("System Running")
if not self.args.frontend_only:
self.print_info(f"Backend: http://localhost:{self.args.port_backend}")
if not self.args.backend_only:
self.print_info(f"Frontend: http://localhost:{self.args.port_frontend}")
print(f"\n{Colors.WARNING}Press Ctrl+C to stop all servers{Colors.ENDC}\n")
try:
while True:
# Check backend
if self.backend_process and self.backend_process.poll() is not None:
self.print_error("Backend process died unexpectedly!")
break
# Check frontend
if self.frontend_process and self.frontend_process.poll() is not None:
self.print_error("Frontend process died unexpectedly!")
break
# Read and display output from backend
if self.backend_process and self.backend_process.stdout:
line = self.backend_process.stdout.readline()
if line:
# Filter out mutex warnings
if "mutex.cc" not in line and "RAW: Lock blocking" not in line:
print(f"{Colors.OKBLUE}[Backend]{Colors.ENDC} {line.rstrip()}")
# Read and display output from frontend
if self.frontend_process and self.frontend_process.stdout:
line = self.frontend_process.stdout.readline()
if line:
print(f"{Colors.OKCYAN}[Frontend]{Colors.ENDC} {line.rstrip()}")
time.sleep(0.1)
except KeyboardInterrupt:
self.print_info("\nReceived shutdown signal...")
self.shutdown()
def shutdown(self):
"""Gracefully shutdown all processes."""
self.print_header("Shutting Down")
if self.backend_process:
self.print_info("Stopping backend server...")
self.backend_process.terminate()
try:
self.backend_process.wait(timeout=5)
self.print_success("Backend stopped")
except subprocess.TimeoutExpired:
self.print_warning("Backend didn't stop gracefully, forcing...")
self.backend_process.kill()
if self.frontend_process:
self.print_info("Stopping frontend server...")
self.frontend_process.terminate()
try:
self.frontend_process.wait(timeout=5)
self.print_success("Frontend stopped")
except subprocess.TimeoutExpired:
self.print_warning("Frontend didn't stop gracefully, forcing...")
self.frontend_process.kill()
self.print_success("\nAll services stopped. Goodbye!")
def run(self):
"""Main execution method."""
# Register signal handlers
signal.signal(signal.SIGINT, lambda s, f: None) # Handle in monitor_processes
signal.signal(signal.SIGTERM, lambda s, f: self.shutdown())
# Print banner
print(f"\n{Colors.BOLD}{Colors.HEADER}")
print("╔════════════════════════════════════════════════════════════════════╗")
print("β•‘ IDP System - Project Runner β•‘")
print("β•‘ Intelligent Document Processing Platform β•‘")
print("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•")
print(f"{Colors.ENDC}\n")
# Check prerequisites
if not self.check_prerequisites():
sys.exit(1)
# Start services
success = True
if not self.args.frontend_only:
success = success and self.start_backend()
if not self.args.backend_only:
success = success and self.start_frontend()
if not success:
self.print_error("\nFailed to start all services")
self.shutdown()
sys.exit(1)
# Monitor processes
self.monitor_processes()
def main():
parser = argparse.ArgumentParser(
description='Run the IDP System (Backend + Frontend)',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__
)
parser.add_argument(
'--backend-only',
action='store_true',
help='Run only the backend server'
)
parser.add_argument(
'--frontend-only',
action='store_true',
help='Run only the frontend server'
)
parser.add_argument(
'--port-backend',
type=int,
default=7860,
help='Backend server port (default: 7860)'
)
parser.add_argument(
'--port-frontend',
type=int,
default=3000,
help='Frontend server port (default: 3000)'
)
parser.add_argument(
'--no-check',
action='store_true',
help='Skip prerequisite checks'
)
args = parser.parse_args()
# Validate arguments
if args.backend_only and args.frontend_only:
print(f"{Colors.FAIL}Error: Cannot use --backend-only and --frontend-only together{Colors.ENDC}")
sys.exit(1)
runner = ProjectRunner(args)
runner.run()
if __name__ == "__main__":
main()