annator / src /ui /desktop_app.py
techprotrade's picture
Add src directory
734b5b4 verified
Raw
History Blame Contribute Delete
4.81 kB
from __future__ import annotations
import sys, os, threading, webbrowser, http.server, json
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from src.core.command_router import CommandRouter
from src.core.models import JobConfig, PDFOperation
from src.core.orchestrator import OfflinePDFOrchestrator
UI_HTML = Path(__file__).parent / "web" / "orchestrator.html"
PORT = 18472
COMMAND_ROUTER = CommandRouter(project_root=Path(__file__).parent.parent.parent)
class _Handler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *a, **kw):
super().__init__(*a, directory=str(UI_HTML.parent), **kw)
def log_message(self, *_): pass
def do_POST(self):
if self.path == "/run":
length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(length)
payload = json.loads(body)
result = _run_job(payload)
resp = json.dumps(result).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(resp)))
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write(resp)
elif self.path == "/team-plan":
length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(length)
payload = json.loads(body)
result = COMMAND_ROUTER.plan_command(payload)
resp = json.dumps(result).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(resp)))
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write(resp)
else:
self.send_response(404); self.end_headers()
def do_GET(self):
if self.path == "/team-config":
result = {
"success": True,
"team": COMMAND_ROUTER.describe_team(),
"available_pipelines": COMMAND_ROUTER.orchestrator.list_available_pipelines(),
}
resp = json.dumps(result).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(resp)))
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write(resp)
return
super().do_GET()
def do_OPTIONS(self):
self.send_response(204)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET,POST,OPTIONS")
self.send_header("Access-Control-Allow-Headers", "Content-Type")
self.end_headers()
def _run_job(payload: dict) -> dict:
try:
ops = []
for item in payload.get("operations", []):
ops.append(PDFOperation(**item))
input_pdf = Path(payload.get("input_pdf", ""))
output_dir = Path(payload.get("output_dir", Path.home() / "pdf-output"))
if not input_pdf.exists():
return {"success": False, "error": f"Input PDF not found: {input_pdf}"}
cfg = JobConfig(
input_pdf = input_pdf,
output_dir = output_dir,
run_ocr = payload.get("force_ocr", False),
force_ocr = payload.get("force_ocr", False),
ocr_language = payload.get("ocr_language", "eng"),
operations = ops,
)
orc = OfflinePDFOrchestrator(output_dir / "_jobs")
result = orc.run(cfg)
return {
"success": result.success,
"job_id": result.job_id,
"final_pdf": str(result.final_pdf) if result.final_pdf else "",
"log_file": str(result.log_file),
"stages": result.stages,
"errors": result.errors,
}
except Exception as exc:
return {"success": False, "error": str(exc)}
def _start_server():
server = http.server.HTTPServer(("127.0.0.1", PORT), _Handler)
server.serve_forever()
def launch():
t = threading.Thread(target=_start_server, daemon=True)
t.start()
url = f"http://127.0.0.1:{PORT}/orchestrator.html"
print(f"Opening PDF Orchestrator UI at {url}")
webbrowser.open(url)
# Keep process alive
import time
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("Shutting down.")
if __name__ == "__main__":
launch()