Really-amin commited on
Commit
bd97f78
·
verified ·
1 Parent(s): 43ccd9f

Upload main.py

Browse files
Files changed (1) hide show
  1. main.py +273 -0
main.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import tempfile
3
+ import logging
4
+ import traceback
5
+ from pathlib import Path
6
+ from typing import Dict, Any
7
+ from datetime import datetime
8
+
9
+ from fastapi import FastAPI, File, UploadFile, HTTPException, Request
10
+ from fastapi.staticfiles import StaticFiles
11
+ from fastapi.responses import HTMLResponse, FileResponse, JSONResponse
12
+ from fastapi.middleware.cors import CORSMiddleware
13
+ from pydantic import BaseModel
14
+
15
+ # تنظیم logging بر اساس متغیر محیطی LOG_LEVEL
16
+ log_level = os.getenv("LOG_LEVEL", "INFO").upper()
17
+ logging.basicConfig(level=getattr(logging, log_level, logging.INFO))
18
+ logger = logging.getLogger(__name__)
19
+
20
+ # بارگذاری کتابخانه‌های پردازش PDF و تصویر
21
+ try:
22
+ import fitz # PyMuPDF
23
+ from PIL import Image
24
+ import numpy as np
25
+ PDF_AVAILABLE = True
26
+ logger.info("✅ PDF processing libraries loaded")
27
+ except ImportError as e:
28
+ PDF_AVAILABLE = False
29
+ logger.warning(f"⚠️ PDF libraries not available: {e}")
30
+
31
+ # بارگذاری مدل‌های ML
32
+ try:
33
+ from transformers import TrOCRProcessor, VisionEncoderDecoderModel
34
+ import torch
35
+ ML_AVAILABLE = True
36
+ logger.info("✅ ML libraries loaded")
37
+ except ImportError as e:
38
+ ML_AVAILABLE = False
39
+ logger.warning(f"⚠️ ML libraries not available: {e}")
40
+
41
+ # مدل پاسخ OCR
42
+ class OCRResponse(BaseModel):
43
+ success: bool
44
+ text: str
45
+ method: str
46
+ metadata: Dict[str, Any]
47
+
48
+ # مدل وضعیت سیستم
49
+ class SystemStatus(BaseModel):
50
+ status: str
51
+ services: Dict[str, Any]
52
+ timestamp: str
53
+
54
+ # سرویس OCR
55
+ class OCRService:
56
+ def __init__(self):
57
+ self.model = None
58
+ self.processor = None
59
+ self.model_loaded = False
60
+
61
+ async def _load_model_async(self):
62
+ try:
63
+ logger.info("Loading TrOCR model...")
64
+ model_name = "microsoft/trocr-base-printed"
65
+ self.processor = TrOCRProcessor.from_pretrained(model_name)
66
+ self.model = VisionEncoderDecoderModel.from_pretrained(model_name)
67
+ self.model_loaded = True
68
+ logger.info("✅ TrOCR model loaded successfully")
69
+ except Exception as e:
70
+ logger.error(f"❌ Failed to load TrOCR model: {e}")
71
+ self.model_loaded = False
72
+
73
+ async def extract_text_from_pdf(self, file_path: str) -> OCRResponse:
74
+ if not PDF_AVAILABLE:
75
+ return OCRResponse(success=False, text="", method="error", metadata={"error": "PDF processing not available"})
76
+ try:
77
+ doc = fitz.open(file_path)
78
+ pages_text = []
79
+ total_chars = 0
80
+ total_pages = doc.page_count
81
+ for page_num in range(min(total_pages, 10)):
82
+ page = doc[page_num]
83
+ text = page.get_text()
84
+ pages_text.append(text)
85
+ total_chars += len(text)
86
+ doc.close()
87
+ full_text = "\n\n--- Page Break ---\n\n".join(pages_text)
88
+ return OCRResponse(
89
+ success=True,
90
+ text=full_text,
91
+ method="PyMuPDF",
92
+ metadata={
93
+ "pages_processed": len(pages_text),
94
+ "total_pages": total_pages,
95
+ "total_characters": total_chars,
96
+ "file_size_kb": os.path.getsize(file_path) / 1024
97
+ }
98
+ )
99
+ except Exception as e:
100
+ logger.error(f"PDF processing error: {e}")
101
+ return OCRResponse(success=False, text="", method="error", metadata={"error": str(e)})
102
+
103
+ async def extract_text_from_image(self, file_path: str) -> OCRResponse:
104
+ try:
105
+ image = Image.open(file_path)
106
+ if self.model_loaded and self.processor and self.model:
107
+ pixel_values = self.processor(images=image, return_tensors="pt").pixel_values
108
+ generated_ids = self.model.generate(pixel_values)
109
+ generated_text = self.processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
110
+ return OCRResponse(
111
+ success=True,
112
+ text=generated_text,
113
+ method="TrOCR",
114
+ metadata={
115
+ "image_size": image.size,
116
+ "image_mode": image.mode,
117
+ "model": "microsoft/trocr-base-printed"
118
+ }
119
+ )
120
+ else:
121
+ return OCRResponse(
122
+ success=True,
123
+ text=f"Image processed: {image.size} pixels, {image.mode} mode\nTrOCR model not loaded - text extraction limited",
124
+ method="Basic",
125
+ metadata={
126
+ "image_size": image.size,
127
+ "image_mode": image.mode,
128
+ "note": "TrOCR model not available"
129
+ }
130
+ )
131
+ except Exception as e:
132
+ logger.error(f"Image processing error: {e}")
133
+ return OCRResponse(success=False, text="", method="error", metadata={"error": str(e)})
134
+
135
+ ocr_service = OCRService()
136
+
137
+ app = FastAPI(
138
+ title="Legal Dashboard API",
139
+ description="Advanced Legal Document Processing System",
140
+ version="2.0.0",
141
+ docs_url="/api/docs",
142
+ redoc_url="/api/redoc"
143
+ )
144
+
145
+ app.add_middleware(
146
+ CORSMiddleware,
147
+ allow_origins=["*"],
148
+ allow_credentials=True,
149
+ allow_methods=["*"],
150
+ allow_headers=["*"],
151
+ )
152
+
153
+ # تنظیم مسیر دایرکتوری فرانت‌اند استاتیک
154
+ BASE_DIR = Path(__file__).parent
155
+ frontend_dir = BASE_DIR / "frontend"
156
+
157
+ if frontend_dir.exists():
158
+ logger.info(f"✅ Frontend directory found: {frontend_dir}")
159
+ app.mount("/static", StaticFiles(directory=frontend_dir), name="static")
160
+ else:
161
+ logger.warning("⚠️ Frontend directory not found. UI will not load correctly.")
162
+
163
+ @app.on_event("startup")
164
+ async def startup_event():
165
+ if ML_AVAILABLE:
166
+ try:
167
+ logger.info("🚀 Loading OCR models on startup...")
168
+ await ocr_service._load_model_async()
169
+ except Exception as e:
170
+ logger.error(f"❌ Failed to load models on startup: {e}")
171
+
172
+ @app.get("/", response_class=HTMLResponse)
173
+ async def root():
174
+ html_file = frontend_dir / "index.html"
175
+ if html_file.exists():
176
+ return FileResponse(html_file)
177
+ return HTMLResponse("""
178
+ <h1>⚠️ Frontend not found</h1>
179
+ <p>Please ensure 'frontend/index.html' exists in the project root.</p>
180
+ """)
181
+
182
+ @app.get("/health")
183
+ async def health_check():
184
+ return {
185
+ "status": "healthy",
186
+ "message": "Legal Dashboard is running",
187
+ "timestamp": datetime.now().isoformat(),
188
+ "services": {
189
+ "pdf_processing": PDF_AVAILABLE,
190
+ "ml_models": ML_AVAILABLE,
191
+ "ocr_model_loaded": ocr_service.model_loaded
192
+ }
193
+ }
194
+
195
+ @app.get("/system/status", response_model=SystemStatus)
196
+ async def get_system_status():
197
+ return SystemStatus(
198
+ status="healthy",
199
+ services={
200
+ "pdf_processing": {
201
+ "available": PDF_AVAILABLE,
202
+ "status": "✅ Available" if PDF_AVAILABLE else "❌ Not Available"
203
+ },
204
+ "ml_models": {
205
+ "available": ML_AVAILABLE,
206
+ "status": "✅ Available" if ML_AVAILABLE else "❌ Not Available"
207
+ },
208
+ "ocr_model": {
209
+ "loaded": ocr_service.model_loaded,
210
+ "status": "✅ Loaded" if ocr_service.model_loaded else "⏳ Loading..." if ML_AVAILABLE else "❌ Not Available"
211
+ }
212
+ },
213
+ timestamp=datetime.now().isoformat()
214
+ )
215
+
216
+ @app.post("/api/ocr/extract-pdf", response_model=OCRResponse)
217
+ async def extract_pdf_text(file: UploadFile = File(...)):
218
+ if not file.filename.lower().endswith('.pdf'):
219
+ raise HTTPException(status_code=400, detail="File must be a PDF")
220
+ temp_path = None
221
+ try:
222
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as temp_file:
223
+ content = await file.read()
224
+ temp_file.write(content)
225
+ temp_path = temp_file.name
226
+ return await ocr_service.extract_text_from_pdf(temp_path)
227
+ finally:
228
+ if temp_path and os.path.exists(temp_path):
229
+ os.unlink(temp_path)
230
+
231
+ @app.post("/api/ocr/extract-image", response_model=OCRResponse)
232
+ async def extract_image_text(file: UploadFile = File(...)):
233
+ allowed_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff']
234
+ if not any(file.filename.lower().endswith(ext) for ext in allowed_extensions):
235
+ raise HTTPException(status_code=400, detail="File must be an image (JPG, PNG, BMP, TIFF)")
236
+ temp_path = None
237
+ try:
238
+ file_extension = Path(file.filename).suffix
239
+ with tempfile.NamedTemporaryFile(delete=False, suffix=file_extension) as temp_file:
240
+ content = await file.read()
241
+ temp_file.write(content)
242
+ temp_path = temp_file.name
243
+ return await ocr_service.extract_text_from_image(temp_path)
244
+ finally:
245
+ if temp_path and os.path.exists(temp_path):
246
+ os.unlink(temp_path)
247
+
248
+ @app.get("/api/test")
249
+ async def test_endpoint():
250
+ return {
251
+ "message": "API is working!",
252
+ "pdf_available": PDF_AVAILABLE,
253
+ "ml_available": ML_AVAILABLE,
254
+ "ocr_model_loaded": ocr_service.model_loaded,
255
+ "timestamp": datetime.now().isoformat()
256
+ }
257
+
258
+ @app.exception_handler(Exception)
259
+ async def global_exception_handler(request: Request, exc: Exception):
260
+ logger.error(f"Global exception: {exc}")
261
+ logger.error(traceback.format_exc())
262
+ return JSONResponse(
263
+ status_code=500,
264
+ content={
265
+ "error": "Internal server error",
266
+ "message": str(exc),
267
+ "path": str(request.url)
268
+ }
269
+ )
270
+
271
+ if __name__ == "__main__":
272
+ import uvicorn
273
+ uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=False, log_level="info")