Really-amin commited on
Commit
a3783e0
·
verified ·
1 Parent(s): 48b1974

Update app/main.py

Browse files
Files changed (1) hide show
  1. app/main.py +22 -147
app/main.py CHANGED
@@ -3,7 +3,7 @@ import tempfile
3
  import logging
4
  import traceback
5
  from pathlib import Path
6
- from typing import Optional, Dict, Any
7
  import asyncio
8
  from datetime import datetime
9
 
@@ -40,15 +40,6 @@ except ImportError as e:
40
 
41
  # Data models
42
  class OCRResponse(BaseModel):
43
- """
44
- OCR processing response model
45
-
46
- Fields:
47
- - success: Whether the OCR processing was successful
48
- - text: Extracted text content
49
- - method: Processing method used (PyMuPDF, TrOCR, Basic, etc.)
50
- - metadata: Additional processing information (pages, file size, image dimensions, etc.)
51
- """
52
  success: bool
53
  text: str
54
  method: str
@@ -71,43 +62,30 @@ class OCRService:
71
  try:
72
  logger.info("Loading TrOCR model...")
73
  model_name = "microsoft/trocr-base-printed"
74
-
75
  self.processor = TrOCRProcessor.from_pretrained(model_name)
76
  self.model = VisionEncoderDecoderModel.from_pretrained(model_name)
77
  self.model_loaded = True
78
-
79
  logger.info("✅ TrOCR model loaded successfully")
80
  except Exception as e:
81
  logger.error(f"❌ Failed to load TrOCR model: {e}")
82
  self.model_loaded = False
83
 
84
  async def extract_text_from_pdf(self, file_path: str) -> OCRResponse:
85
- """Extract text from PDF using PyMuPDF"""
86
  if not PDF_AVAILABLE:
87
- return OCRResponse(
88
- success=False,
89
- text="",
90
- method="error",
91
- metadata={"error": "PDF processing not available"}
92
- )
93
-
94
  try:
95
  doc = fitz.open(file_path)
96
  pages_text = []
97
  total_chars = 0
98
- total_pages = doc.page_count # Get total pages before closing
99
-
100
- # Process up to 10 pages to avoid timeout
101
  for page_num in range(min(total_pages, 10)):
102
  page = doc[page_num]
103
  text = page.get_text()
104
  pages_text.append(text)
105
  total_chars += len(text)
106
-
107
  doc.close()
108
-
109
  full_text = "\n\n--- Page Break ---\n\n".join(pages_text)
110
-
111
  return OCRResponse(
112
  success=True,
113
  text=full_text,
@@ -119,27 +97,17 @@ class OCRService:
119
  "file_size_kb": os.path.getsize(file_path) / 1024
120
  }
121
  )
122
-
123
  except Exception as e:
124
  logger.error(f"PDF processing error: {e}")
125
- return OCRResponse(
126
- success=False,
127
- text="",
128
- method="error",
129
- metadata={"error": str(e)}
130
- )
131
 
132
  async def extract_text_from_image(self, file_path: str) -> OCRResponse:
133
- """Extract text from image using TrOCR"""
134
  try:
135
  image = Image.open(file_path)
136
-
137
  if self.model_loaded and self.processor and self.model:
138
- # Use TrOCR
139
  pixel_values = self.processor(images=image, return_tensors="pt").pixel_values
140
  generated_ids = self.model.generate(pixel_values)
141
  generated_text = self.processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
142
-
143
  return OCRResponse(
144
  success=True,
145
  text=generated_text,
@@ -151,7 +119,6 @@ class OCRService:
151
  }
152
  )
153
  else:
154
- # Fallback method
155
  return OCRResponse(
156
  success=True,
157
  text=f"Image processed: {image.size} pixels, {image.mode} mode\nTrOCR model not loaded - text extraction limited",
@@ -162,20 +129,12 @@ class OCRService:
162
  "note": "TrOCR model not available"
163
  }
164
  )
165
-
166
  except Exception as e:
167
  logger.error(f"Image processing error: {e}")
168
- return OCRResponse(
169
- success=False,
170
- text="",
171
- method="error",
172
- metadata={"error": str(e)}
173
- )
174
 
175
- # Initialize services
176
  ocr_service = OCRService()
177
 
178
- # Create FastAPI app
179
  app = FastAPI(
180
  title="Legal Dashboard API",
181
  description="Advanced Legal Document Processing System",
@@ -184,7 +143,6 @@ app = FastAPI(
184
  redoc_url="/api/redoc"
185
  )
186
 
187
- # Add CORS middleware
188
  app.add_middleware(
189
  CORSMiddleware,
190
  allow_origins=["*"],
@@ -193,17 +151,15 @@ app.add_middleware(
193
  allow_headers=["*"],
194
  )
195
 
196
- # Create directories
197
- os.makedirs("static", exist_ok=True)
198
- os.makedirs("temp", exist_ok=True)
199
-
200
- # Mount static files
201
- app.mount("/static", StaticFiles(directory="static"), name="static")
202
 
203
- # Startup event to load ML models
204
  @app.on_event("startup")
205
  async def startup_event():
206
- """Load ML models on application startup"""
207
  if ML_AVAILABLE:
208
  try:
209
  logger.info("🚀 Loading OCR models on startup...")
@@ -211,58 +167,16 @@ async def startup_event():
211
  except Exception as e:
212
  logger.error(f"❌ Failed to load models on startup: {e}")
213
 
214
- # Routes
215
  @app.get("/", response_class=HTMLResponse)
216
  async def root():
217
- """Serve main dashboard"""
218
- try:
219
- html_file = Path("static/index.html")
220
- if html_file.exists():
221
- return FileResponse(html_file)
222
- else:
223
- # Return inline HTML if file doesn't exist
224
- return HTMLResponse("""
225
- <!DOCTYPE html>
226
- <html>
227
- <head>
228
- <title>Legal Dashboard</title>
229
- <meta charset="UTF-8">
230
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
231
- <style>
232
- body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
233
- .container { max-width: 800px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
234
- .header { text-align: center; color: #333; margin-bottom: 30px; }
235
- .status { padding: 15px; background: #e8f5e8; border-left: 4px solid #4CAF50; margin: 20px 0; }
236
- .nav { display: flex; gap: 10px; margin: 20px 0; flex-wrap: wrap; }
237
- .nav a { padding: 10px 20px; background: #4CAF50; color: white; text-decoration: none; border-radius: 5px; }
238
- .nav a:hover { background: #45a049; }
239
- </style>
240
- </head>
241
- <body>
242
- <div class="container">
243
- <div class="header">
244
- <h1>🏛️ Legal Dashboard</h1>
245
- <p>Advanced Legal Document Processing System</p>
246
- </div>
247
- <div class="status">
248
- <strong>✅ System Status:</strong> FastAPI backend is running successfully!
249
- </div>
250
- <div class="nav">
251
- <a href="/api/docs">📚 API Documentation</a>
252
- <a href="/health">❤️ Health Check</a>
253
- <a href="/system/status">📊 System Status</a>
254
- </div>
255
- <p><strong>Note:</strong> Please create static/index.html for the full frontend interface.</p>
256
- </div>
257
- </body>
258
- </html>
259
- """)
260
- except Exception as e:
261
- raise HTTPException(status_code=500, detail=f"Error serving main page: {str(e)}")
262
 
263
  @app.get("/health")
264
  async def health_check():
265
- """Health check endpoint"""
266
  return {
267
  "status": "healthy",
268
  "message": "Legal Dashboard is running",
@@ -276,7 +190,6 @@ async def health_check():
276
 
277
  @app.get("/system/status", response_model=SystemStatus)
278
  async def get_system_status():
279
- """Get detailed system status"""
280
  return SystemStatus(
281
  status="healthy",
282
  services={
@@ -298,67 +211,38 @@ async def get_system_status():
298
 
299
  @app.post("/api/ocr/extract-pdf", response_model=OCRResponse)
300
  async def extract_pdf_text(file: UploadFile = File(...)):
301
- """Extract text from PDF file"""
302
  if not file.filename.lower().endswith('.pdf'):
303
  raise HTTPException(status_code=400, detail="File must be a PDF")
304
-
305
  temp_path = None
306
  try:
307
- # Save uploaded file temporarily
308
  with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as temp_file:
309
  content = await file.read()
310
  temp_file.write(content)
311
  temp_path = temp_file.name
312
-
313
- # Process PDF
314
- result = await ocr_service.extract_text_from_pdf(temp_path)
315
- return result
316
-
317
- except Exception as e:
318
- logger.error(f"PDF extraction error: {e}")
319
- raise HTTPException(status_code=500, detail=f"PDF processing failed: {str(e)}")
320
  finally:
321
- # Clean up temp file
322
  if temp_path and os.path.exists(temp_path):
323
- try:
324
- os.unlink(temp_path)
325
- except Exception as e:
326
- logger.warning(f"Failed to cleanup temp file {temp_path}: {e}")
327
 
328
  @app.post("/api/ocr/extract-image", response_model=OCRResponse)
329
  async def extract_image_text(file: UploadFile = File(...)):
330
- """Extract text from image file"""
331
  allowed_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff']
332
  if not any(file.filename.lower().endswith(ext) for ext in allowed_extensions):
333
  raise HTTPException(status_code=400, detail="File must be an image (JPG, PNG, BMP, TIFF)")
334
-
335
  temp_path = None
336
  try:
337
- # Save uploaded file temporarily
338
  file_extension = Path(file.filename).suffix
339
  with tempfile.NamedTemporaryFile(delete=False, suffix=file_extension) as temp_file:
340
  content = await file.read()
341
  temp_file.write(content)
342
  temp_path = temp_file.name
343
-
344
- # Process image
345
- result = await ocr_service.extract_text_from_image(temp_path)
346
- return result
347
-
348
- except Exception as e:
349
- logger.error(f"Image extraction error: {e}")
350
- raise HTTPException(status_code=500, detail=f"Image processing failed: {str(e)}")
351
  finally:
352
- # Clean up temp file
353
  if temp_path and os.path.exists(temp_path):
354
- try:
355
- os.unlink(temp_path)
356
- except Exception as e:
357
- logger.warning(f"Failed to cleanup temp file {temp_path}: {e}")
358
 
359
  @app.get("/api/test")
360
  async def test_endpoint():
361
- """Test endpoint for debugging"""
362
  return {
363
  "message": "API is working!",
364
  "pdf_available": PDF_AVAILABLE,
@@ -367,7 +251,6 @@ async def test_endpoint():
367
  "timestamp": datetime.now().isoformat()
368
  }
369
 
370
- # Error handlers
371
  @app.exception_handler(Exception)
372
  async def global_exception_handler(request: Request, exc: Exception):
373
  logger.error(f"Global exception: {exc}")
@@ -383,12 +266,4 @@ async def global_exception_handler(request: Request, exc: Exception):
383
 
384
  if __name__ == "__main__":
385
  import uvicorn
386
-
387
- # Run on port 7860 for Hugging Face Spaces
388
- uvicorn.run(
389
- "main:app",
390
- host="0.0.0.0",
391
- port=7860,
392
- reload=False,
393
- log_level="info"
394
- )
 
3
  import logging
4
  import traceback
5
  from pathlib import Path
6
+ from typing import Dict, Any
7
  import asyncio
8
  from datetime import datetime
9
 
 
40
 
41
  # Data models
42
  class OCRResponse(BaseModel):
 
 
 
 
 
 
 
 
 
43
  success: bool
44
  text: str
45
  method: str
 
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",
76
+ metadata={"error": "PDF processing not available"})
 
 
 
 
 
77
  try:
78
  doc = fitz.open(file_path)
79
  pages_text = []
80
  total_chars = 0
81
+ total_pages = doc.page_count
 
 
82
  for page_num in range(min(total_pages, 10)):
83
  page = doc[page_num]
84
  text = page.get_text()
85
  pages_text.append(text)
86
  total_chars += len(text)
 
87
  doc.close()
 
88
  full_text = "\n\n--- Page Break ---\n\n".join(pages_text)
 
89
  return OCRResponse(
90
  success=True,
91
  text=full_text,
 
97
  "file_size_kb": os.path.getsize(file_path) / 1024
98
  }
99
  )
 
100
  except Exception as e:
101
  logger.error(f"PDF processing error: {e}")
102
+ return OCRResponse(success=False, text="", method="error", metadata={"error": str(e)})
 
 
 
 
 
103
 
104
  async def extract_text_from_image(self, file_path: str) -> OCRResponse:
 
105
  try:
106
  image = Image.open(file_path)
 
107
  if self.model_loaded and self.processor and self.model:
 
108
  pixel_values = self.processor(images=image, return_tensors="pt").pixel_values
109
  generated_ids = self.model.generate(pixel_values)
110
  generated_text = self.processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
 
111
  return OCRResponse(
112
  success=True,
113
  text=generated_text,
 
119
  }
120
  )
121
  else:
 
122
  return OCRResponse(
123
  success=True,
124
  text=f"Image processed: {image.size} pixels, {image.mode} mode\nTrOCR model not loaded - text extraction limited",
 
129
  "note": "TrOCR model not available"
130
  }
131
  )
 
132
  except Exception as e:
133
  logger.error(f"Image processing error: {e}")
134
+ return OCRResponse(success=False, text="", method="error", metadata={"error": str(e)})
 
 
 
 
 
135
 
 
136
  ocr_service = OCRService()
137
 
 
138
  app = FastAPI(
139
  title="Legal Dashboard API",
140
  description="Advanced Legal Document Processing System",
 
143
  redoc_url="/api/redoc"
144
  )
145
 
 
146
  app.add_middleware(
147
  CORSMiddleware,
148
  allow_origins=["*"],
 
151
  allow_headers=["*"],
152
  )
153
 
154
+ # Use frontend folder as static files
155
+ frontend_dir = Path("frontend")
156
+ if not frontend_dir.exists():
157
+ logger.warning("⚠️ Frontend directory not found. UI may not load correctly.")
158
+ else:
159
+ app.mount("/static", StaticFiles(directory=frontend_dir), name="static")
160
 
 
161
  @app.on_event("startup")
162
  async def startup_event():
 
163
  if ML_AVAILABLE:
164
  try:
165
  logger.info("🚀 Loading OCR models on startup...")
 
167
  except Exception as e:
168
  logger.error(f"❌ Failed to load models on startup: {e}")
169
 
 
170
  @app.get("/", response_class=HTMLResponse)
171
  async def root():
172
+ """Serve main frontend file"""
173
+ html_file = Path("frontend/index.html")
174
+ if html_file.exists():
175
+ return FileResponse(html_file)
176
+ return HTMLResponse("<h1>⚠️ Frontend not found</h1><p>Please ensure frontend/index.html exists.</p>")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
 
178
  @app.get("/health")
179
  async def health_check():
 
180
  return {
181
  "status": "healthy",
182
  "message": "Legal Dashboard is running",
 
190
 
191
  @app.get("/system/status", response_model=SystemStatus)
192
  async def get_system_status():
 
193
  return SystemStatus(
194
  status="healthy",
195
  services={
 
211
 
212
  @app.post("/api/ocr/extract-pdf", response_model=OCRResponse)
213
  async def extract_pdf_text(file: UploadFile = File(...)):
 
214
  if not file.filename.lower().endswith('.pdf'):
215
  raise HTTPException(status_code=400, detail="File must be a PDF")
 
216
  temp_path = None
217
  try:
 
218
  with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as temp_file:
219
  content = await file.read()
220
  temp_file.write(content)
221
  temp_path = temp_file.name
222
+ return await ocr_service.extract_text_from_pdf(temp_path)
 
 
 
 
 
 
 
223
  finally:
 
224
  if temp_path and os.path.exists(temp_path):
225
+ os.unlink(temp_path)
 
 
 
226
 
227
  @app.post("/api/ocr/extract-image", response_model=OCRResponse)
228
  async def extract_image_text(file: UploadFile = File(...)):
 
229
  allowed_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff']
230
  if not any(file.filename.lower().endswith(ext) for ext in allowed_extensions):
231
  raise HTTPException(status_code=400, detail="File must be an image (JPG, PNG, BMP, TIFF)")
 
232
  temp_path = None
233
  try:
 
234
  file_extension = Path(file.filename).suffix
235
  with tempfile.NamedTemporaryFile(delete=False, suffix=file_extension) as temp_file:
236
  content = await file.read()
237
  temp_file.write(content)
238
  temp_path = temp_file.name
239
+ return await ocr_service.extract_text_from_image(temp_path)
 
 
 
 
 
 
 
240
  finally:
 
241
  if temp_path and os.path.exists(temp_path):
242
+ os.unlink(temp_path)
 
 
 
243
 
244
  @app.get("/api/test")
245
  async def test_endpoint():
 
246
  return {
247
  "message": "API is working!",
248
  "pdf_available": PDF_AVAILABLE,
 
251
  "timestamp": datetime.now().isoformat()
252
  }
253
 
 
254
  @app.exception_handler(Exception)
255
  async def global_exception_handler(request: Request, exc: Exception):
256
  logger.error(f"Global exception: {exc}")
 
266
 
267
  if __name__ == "__main__":
268
  import uvicorn
269
+ uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=False, log_level="info")