ApsidalSolid4 commited on
Commit
c2529af
·
verified ·
1 Parent(s): cf778fe

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +332 -25
app.py CHANGED
@@ -41,6 +41,336 @@ if not ADMIN_PASSWORD_HASH:
41
  # Excel file path for logs
42
  EXCEL_LOG_PATH = "/tmp/prediction_logs.xlsx"
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  def is_admin_password(input_text: str) -> bool:
45
  """
46
  Check if the input text matches the admin password using secure hash comparison.
@@ -564,31 +894,8 @@ def analyze_text(text: str, mode: str, classifier: TextClassifier) -> tuple:
564
  classifier = TextClassifier()
565
 
566
  # Create Gradio interface
567
- demo = gr.Interface(
568
- fn=lambda text, mode: analyze_text(text, mode, classifier),
569
- inputs=[
570
- gr.Textbox(
571
- lines=8,
572
- placeholder="Enter text to analyze...",
573
- label="Input Text"
574
- ),
575
- gr.Radio(
576
- choices=["quick", "detailed"],
577
- value="quick",
578
- label="Analysis Mode",
579
- info="Quick mode for faster analysis, Detailed mode for sentence-level analysis"
580
- )
581
- ],
582
- outputs=[
583
- gr.HTML(label="Highlighted Analysis"),
584
- gr.Textbox(label="Sentence-by-Sentence Analysis", lines=10),
585
- gr.Textbox(label="Overall Result", lines=4)
586
- ],
587
- title="AI Text Detector",
588
- description="Analyze text to detect if it was written by a human or AI. Choose between quick scan and detailed sentence-level analysis. 200+ words suggested for accurate predictions.",
589
- api_name="predict",
590
- flagging_mode="never"
591
- )
592
 
593
  # Get the FastAPI app from Gradio
594
  app = demo.app
 
41
  # Excel file path for logs
42
  EXCEL_LOG_PATH = "/tmp/prediction_logs.xlsx"
43
 
44
+
45
+ import requests
46
+ import base64
47
+ import os
48
+ import tempfile
49
+ from typing import Dict, List, Optional, Union, Tuple
50
+ import mimetypes
51
+ import logging
52
+ import time
53
+ from pathlib import Path
54
+
55
+ # OCR API settings
56
+ OCR_API_KEY = "9e11346f1288957" # This is a partial key - replace with the full one
57
+ OCR_API_ENDPOINT = "https://api.ocr.space/parse/image"
58
+ OCR_MAX_PDF_PAGES = 3
59
+ OCR_MAX_FILE_SIZE_MB = 1
60
+
61
+ # Configure logging for OCR module
62
+ ocr_logger = logging.getLogger("ocr_module")
63
+ ocr_logger.setLevel(logging.INFO)
64
+
65
+ class OCRProcessor:
66
+ """
67
+ Handles OCR processing of image and document files using OCR.space API
68
+ """
69
+ def __init__(self, api_key: str = OCR_API_KEY):
70
+ self.api_key = api_key
71
+ self.endpoint = OCR_API_ENDPOINT
72
+
73
+ def process_file(self, file_path: str) -> Dict:
74
+ """
75
+ Process a file using OCR.space API
76
+
77
+ Args:
78
+ file_path: Path to the file to be processed
79
+
80
+ Returns:
81
+ Dictionary containing the OCR results and status
82
+ """
83
+ start_time = time.time()
84
+ ocr_logger.info(f"Starting OCR processing for file: {os.path.basename(file_path)}")
85
+
86
+ # Validate file size
87
+ file_size_mb = os.path.getsize(file_path) / (1024 * 1024)
88
+ if file_size_mb > OCR_MAX_FILE_SIZE_MB:
89
+ ocr_logger.warning(f"File size ({file_size_mb:.2f} MB) exceeds limit of {OCR_MAX_FILE_SIZE_MB} MB")
90
+ return {
91
+ "success": False,
92
+ "error": f"File size ({file_size_mb:.2f} MB) exceeds limit of {OCR_MAX_FILE_SIZE_MB} MB",
93
+ "text": ""
94
+ }
95
+
96
+ # Determine file type and handle accordingly
97
+ file_type = self._get_file_type(file_path)
98
+ ocr_logger.info(f"Detected file type: {file_type}")
99
+
100
+ # Special handling for Word documents - convert to PDF if needed
101
+ if file_type.startswith('application/vnd.openxmlformats-officedocument') or file_type == 'application/msword':
102
+ ocr_logger.info("Word document detected, processing directly")
103
+ # Note: OCR.space may handle Word directly, but if not, conversion would be needed here
104
+
105
+ # Prepare the API request
106
+ with open(file_path, 'rb') as f:
107
+ file_data = f.read()
108
+
109
+ # Set up API parameters
110
+ payload = {
111
+ 'isOverlayRequired': 'false',
112
+ 'language': 'eng',
113
+ 'OCREngine': '2', # Use more accurate engine
114
+ 'scale': 'true',
115
+ 'detectOrientation': 'true',
116
+ }
117
+
118
+ # For PDF files, check page count limitations
119
+ if file_type == 'application/pdf':
120
+ ocr_logger.info("PDF document detected, enforcing page limit")
121
+ payload['filetype'] = 'PDF'
122
+
123
+ # Prepare file for OCR API
124
+ files = {
125
+ 'file': (os.path.basename(file_path), file_data, file_type)
126
+ }
127
+
128
+ headers = {
129
+ 'apikey': self.api_key,
130
+ }
131
+
132
+ # Make the OCR API request
133
+ try:
134
+ ocr_logger.info("Sending request to OCR.space API")
135
+ response = requests.post(
136
+ self.endpoint,
137
+ files=files,
138
+ data=payload,
139
+ headers=headers
140
+ )
141
+ response.raise_for_status()
142
+ result = response.json()
143
+
144
+ # Process the OCR results
145
+ if result.get('OCRExitCode') in [1, 2]: # Success or partial success
146
+ extracted_text = self._extract_text_from_result(result)
147
+ processing_time = time.time() - start_time
148
+ ocr_logger.info(f"OCR processing completed in {processing_time:.2f} seconds")
149
+
150
+ return {
151
+ "success": True,
152
+ "text": extracted_text,
153
+ "word_count": len(extracted_text.split()),
154
+ "processing_time_ms": int(processing_time * 1000)
155
+ }
156
+ else:
157
+ ocr_logger.error(f"OCR API error: {result.get('ErrorMessage', 'Unknown error')}")
158
+ return {
159
+ "success": False,
160
+ "error": result.get('ErrorMessage', 'OCR processing failed'),
161
+ "text": ""
162
+ }
163
+
164
+ except requests.exceptions.RequestException as e:
165
+ ocr_logger.error(f"OCR API request failed: {str(e)}")
166
+ return {
167
+ "success": False,
168
+ "error": f"OCR API request failed: {str(e)}",
169
+ "text": ""
170
+ }
171
+
172
+ def _extract_text_from_result(self, result: Dict) -> str:
173
+ """
174
+ Extract all text from the OCR API result
175
+
176
+ Args:
177
+ result: The OCR API response JSON
178
+
179
+ Returns:
180
+ Extracted text as a single string
181
+ """
182
+ extracted_text = ""
183
+
184
+ if 'ParsedResults' in result and result['ParsedResults']:
185
+ for parsed_result in result['ParsedResults']:
186
+ if parsed_result.get('ParsedText'):
187
+ extracted_text += parsed_result['ParsedText']
188
+
189
+ return extracted_text
190
+
191
+ def _get_file_type(self, file_path: str) -> str:
192
+ """
193
+ Determine MIME type of a file
194
+
195
+ Args:
196
+ file_path: Path to the file
197
+
198
+ Returns:
199
+ MIME type as string
200
+ """
201
+ mime_type, _ = mimetypes.guess_type(file_path)
202
+ if mime_type is None:
203
+ # Default to binary if MIME type can't be determined
204
+ return 'application/octet-stream'
205
+ return mime_type
206
+
207
+
208
+ # Function to be integrated with the main application
209
+ def handle_file_upload_and_analyze(file_obj, mode: str, classifier) -> tuple:
210
+ """
211
+ Handle file upload, OCR processing, and text analysis
212
+
213
+ Args:
214
+ file_obj: Uploaded file object from Gradio
215
+ mode: Analysis mode (quick or detailed)
216
+ classifier: The TextClassifier instance
217
+
218
+ Returns:
219
+ Analysis results as a tuple (same format as original analyze_text function)
220
+ """
221
+ if file_obj is None:
222
+ return (
223
+ "No file uploaded",
224
+ "Please upload a file to analyze",
225
+ "No file uploaded for analysis"
226
+ )
227
+
228
+ # Create a temporary file
229
+ with tempfile.NamedTemporaryFile(delete=False, suffix=Path(file_obj.name).suffix) as temp_file:
230
+ temp_file_path = temp_file.name
231
+ # Write uploaded file to the temporary file
232
+ temp_file.write(file_obj.read())
233
+
234
+ try:
235
+ # Process the file with OCR
236
+ ocr_processor = OCRProcessor()
237
+ ocr_result = ocr_processor.process_file(temp_file_path)
238
+
239
+ if not ocr_result["success"]:
240
+ return (
241
+ "OCR Processing Error",
242
+ ocr_result["error"],
243
+ "Failed to extract text from the uploaded file"
244
+ )
245
+
246
+ # Get the extracted text
247
+ extracted_text = ocr_result["text"]
248
+
249
+ # If no text was extracted
250
+ if not extracted_text.strip():
251
+ return (
252
+ "No text extracted",
253
+ "The OCR process did not extract any text from the uploaded file.",
254
+ "No text was found in the uploaded file"
255
+ )
256
+
257
+ # Call the original text analysis function with the extracted text
258
+ return analyze_text(extracted_text, mode, classifier)
259
+
260
+ finally:
261
+ # Clean up the temporary file
262
+ if os.path.exists(temp_file_path):
263
+ os.remove(temp_file_path)
264
+
265
+
266
+ # Modified Gradio interface setup function to include file upload
267
+ def setup_gradio_interface(classifier):
268
+ """
269
+ Set up Gradio interface with text input and file upload options
270
+
271
+ Args:
272
+ classifier: The TextClassifier instance
273
+
274
+ Returns:
275
+ Gradio Interface object
276
+ """
277
+ import gradio as gr
278
+
279
+ with gr.Blocks(title="AI Text Detector") as demo:
280
+ gr.Markdown("# AI Text Detector with Document Upload")
281
+ gr.Markdown("Analyze text to detect if it was written by a human or AI. You can paste text directly or upload images, PDFs, or Word documents.")
282
+
283
+ with gr.Tab("Text Input"):
284
+ text_input = gr.Textbox(
285
+ lines=8,
286
+ placeholder="Enter text to analyze...",
287
+ label="Input Text"
288
+ )
289
+
290
+ mode_selection = gr.Radio(
291
+ choices=["quick", "detailed"],
292
+ value="quick",
293
+ label="Analysis Mode",
294
+ info="Quick mode for faster analysis, Detailed mode for sentence-level analysis"
295
+ )
296
+
297
+ text_submit_button = gr.Button("Analyze Text")
298
+
299
+ output_html = gr.HTML(label="Highlighted Analysis")
300
+ output_sentences = gr.Textbox(label="Sentence-by-Sentence Analysis", lines=10)
301
+ output_result = gr.Textbox(label="Overall Result", lines=4)
302
+
303
+ text_submit_button.click(
304
+ analyze_text,
305
+ inputs=[text_input, mode_selection, classifier],
306
+ outputs=[output_html, output_sentences, output_result]
307
+ )
308
+
309
+ with gr.Tab("File Upload"):
310
+ file_upload = gr.File(
311
+ label="Upload Document",
312
+ file_types=["image", "pdf", "doc", "docx"],
313
+ type="file"
314
+ )
315
+
316
+ file_mode_selection = gr.Radio(
317
+ choices=["quick", "detailed"],
318
+ value="quick",
319
+ label="Analysis Mode",
320
+ info="Quick mode for faster analysis, Detailed mode for sentence-level analysis"
321
+ )
322
+
323
+ upload_submit_button = gr.Button("Process and Analyze")
324
+
325
+ file_output_html = gr.HTML(label="Highlighted Analysis")
326
+ file_output_sentences = gr.Textbox(label="Sentence-by-Sentence Analysis", lines=10)
327
+ file_output_result = gr.Textbox(label="Overall Result", lines=4)
328
+
329
+ upload_submit_button.click(
330
+ handle_file_upload_and_analyze,
331
+ inputs=[file_upload, file_mode_selection, classifier],
332
+ outputs=[file_output_html, file_output_sentences, file_output_result]
333
+ )
334
+
335
+ gr.Markdown("""
336
+ ### File Upload Limitations
337
+ - Maximum file size: 1MB
338
+ - PDF files: Maximum 3 pages (OCR.space API limitation)
339
+ - Supported formats: Images (PNG, JPG, GIF), PDF, Word documents (DOCX, DOC)
340
+ """)
341
+
342
+ return demo
343
+
344
+
345
+ # This function is a replacement for the original main app setup
346
+ def setup_app_with_ocr():
347
+ """
348
+ Setup the application with OCR capabilities
349
+ """
350
+ # Initialize the classifier (use existing code)
351
+ classifier = TextClassifier()
352
+
353
+ # Create the Gradio interface with file upload functionality
354
+ demo = setup_gradio_interface(classifier)
355
+
356
+ # Get the FastAPI app from Gradio
357
+ app = demo.app
358
+
359
+ # Add CORS middleware (same as original code)
360
+ from fastapi.middleware.cors import CORSMiddleware
361
+ app.add_middleware(
362
+ CORSMiddleware,
363
+ allow_origins=["*"], # For development
364
+ allow_credentials=True,
365
+ allow_methods=["GET", "POST", "OPTIONS"],
366
+ allow_headers=["*"],
367
+ )
368
+
369
+ # Return the demo for launching
370
+ return demo
371
+
372
+
373
+
374
  def is_admin_password(input_text: str) -> bool:
375
  """
376
  Check if the input text matches the admin password using secure hash comparison.
 
894
  classifier = TextClassifier()
895
 
896
  # Create Gradio interface
897
+ demo = setup_app_with_ocr()
898
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
899
 
900
  # Get the FastAPI app from Gradio
901
  app = demo.app