yamanavijayavardhan commited on
Commit
bd41193
·
1 Parent(s): 9d4e272

printing answers

Browse files
Files changed (2) hide show
  1. main.py +83 -7
  2. templates/index.html +12 -0
main.py CHANGED
@@ -6,12 +6,15 @@ import sys
6
  import builtins
7
  from datetime import datetime
8
  from flask_cors import CORS
9
- from flask import Flask, request, jsonify, render_template, make_response
10
  from werkzeug.utils import secure_filename
11
  from dotenv import load_dotenv
12
  import json
13
  import shutil
14
  from threading import Thread, Event
 
 
 
15
 
16
  # Load environment variables
17
  load_dotenv()
@@ -30,6 +33,7 @@ log_file = os.path.join(log_dir, 'app.log') # Add log file path
30
  # Global variables for model caching and initialization status
31
  global_models = {}
32
  initialization_complete = Event()
 
33
 
34
  def ensure_directory(path):
35
  """Create directory and ensure full permissions with better error handling"""
@@ -327,6 +331,35 @@ def validate_folder_structure(files):
327
  except Exception as e:
328
  return False, f"Error validating folder structure: {str(e)}"
329
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  @app.route('/compute_marks', methods=['POST'])
331
  def compute_marks():
332
  try:
@@ -415,6 +448,7 @@ def compute_marks():
415
  # Dictionary to store results by student folder and image name
416
  results = {}
417
  failed_files = []
 
418
 
419
  try:
420
  # Process each file
@@ -484,6 +518,18 @@ def compute_marks():
484
 
485
  log_print(f"Extracted text: {extracted_text[:100]}...")
486
 
 
 
 
 
 
 
 
 
 
 
 
 
487
  # Clean the extracted text for JSON
488
  extracted_text = extracted_text.encode('ascii', 'ignore').decode('ascii')
489
 
@@ -580,7 +626,11 @@ def compute_marks():
580
  "failed_files": [{
581
  "file": f["file"].encode('ascii', 'ignore').decode('ascii'),
582
  "error": f["error"].encode('ascii', 'ignore').decode('ascii')
583
- } for f in failed_files]
 
 
 
 
584
  }
585
 
586
  log_print(f"Sending response: {response_data}")
@@ -675,25 +725,51 @@ def ensure_initialization():
675
  wait_for_initialization()
676
 
677
  def cleanup_temp_files():
678
- """Clean up temporary files with proper error handling"""
679
  try:
680
- # Clean up the temporary processing directory
681
  temp_processing_dir = os.path.join(BASE_DIR, 'temp_processing')
682
  if os.path.exists(temp_processing_dir):
683
- shutil.rmtree(temp_processing_dir, ignore_errors=True)
 
 
 
 
684
 
685
- # Clean up the images directory
686
  if os.path.exists(images_dir):
687
  for file in os.listdir(images_dir):
688
  try:
689
  file_path = os.path.join(images_dir, file)
690
  if os.path.isfile(file_path):
691
- os.unlink(file_path)
 
 
 
692
  except Exception as e:
693
  log_print(f"Warning: Could not delete file {file_path}: {e}", "WARNING")
694
  except Exception as e:
695
  log_print(f"Error cleaning up temporary files: {e}", "ERROR")
696
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697
  if __name__ == '__main__':
698
  try:
699
  # Create essential directories
 
6
  import builtins
7
  from datetime import datetime
8
  from flask_cors import CORS
9
+ from flask import Flask, request, jsonify, render_template, make_response, Response
10
  from werkzeug.utils import secure_filename
11
  from dotenv import load_dotenv
12
  import json
13
  import shutil
14
  from threading import Thread, Event
15
+ import queue
16
+ import time
17
+ from contextlib import contextmanager
18
 
19
  # Load environment variables
20
  load_dotenv()
 
33
  # Global variables for model caching and initialization status
34
  global_models = {}
35
  initialization_complete = Event()
36
+ notification_queue = queue.Queue()
37
 
38
  def ensure_directory(path):
39
  """Create directory and ensure full permissions with better error handling"""
 
331
  except Exception as e:
332
  return False, f"Error validating folder structure: {str(e)}"
333
 
334
+ @app.route('/notifications')
335
+ def notifications():
336
+ def generate():
337
+ start_time = time.time()
338
+ timeout = 300 # 5 minutes timeout
339
+
340
+ while True:
341
+ try:
342
+ # Check timeout
343
+ if time.time() - start_time > timeout:
344
+ yield "data: {\"type\": \"timeout\", \"message\": \"Connection timed out\"}\n\n"
345
+ break
346
+
347
+ # Get notification from queue (non-blocking)
348
+ try:
349
+ notification = notification_queue.get_nowait()
350
+ yield f"data: {json.dumps(notification)}\n\n"
351
+ except queue.Empty:
352
+ # If no notification, yield empty to keep connection alive
353
+ yield "data: {}\n\n"
354
+ time.sleep(0.5) # Increased delay to reduce server load
355
+
356
+ except Exception as e:
357
+ log_print(f"Error in notification stream: {str(e)}", "ERROR")
358
+ yield f"data: {{\"type\": \"error\", \"message\": \"{str(e)}\"}}\n\n"
359
+ break
360
+
361
+ return Response(generate(), mimetype='text/event-stream')
362
+
363
  @app.route('/compute_marks', methods=['POST'])
364
  def compute_marks():
365
  try:
 
448
  # Dictionary to store results by student folder and image name
449
  results = {}
450
  failed_files = []
451
+ extracted_texts = {} # Add this line to store extracted texts
452
 
453
  try:
454
  # Process each file
 
518
 
519
  log_print(f"Extracted text: {extracted_text[:100]}...")
520
 
521
+ # Send notification for extracted text immediately
522
+ notification_data = {
523
+ "type": "extracted_text",
524
+ "filename": filename,
525
+ "text": extracted_text[:200] + ("..." if len(extracted_text) > 200 else "")
526
+ }
527
+ notification_queue.put(notification_data)
528
+ log_print(f"Notification queued for {filename}")
529
+
530
+ # Store extracted text for final response
531
+ extracted_texts[filename] = extracted_text
532
+
533
  # Clean the extracted text for JSON
534
  extracted_text = extracted_text.encode('ascii', 'ignore').decode('ascii')
535
 
 
626
  "failed_files": [{
627
  "file": f["file"].encode('ascii', 'ignore').decode('ascii'),
628
  "error": f["error"].encode('ascii', 'ignore').decode('ascii')
629
+ } for f in failed_files],
630
+ "extracted_texts": { # Add this section to include extracted texts
631
+ filename: text.encode('ascii', 'ignore').decode('ascii')
632
+ for filename, text in extracted_texts.items()
633
+ }
634
  }
635
 
636
  log_print(f"Sending response: {response_data}")
 
725
  wait_for_initialization()
726
 
727
  def cleanup_temp_files():
728
+ """Clean up temporary files with proper error handling and timeout"""
729
  try:
730
+ # Clean up the temporary processing directory with timeout
731
  temp_processing_dir = os.path.join(BASE_DIR, 'temp_processing')
732
  if os.path.exists(temp_processing_dir):
733
+ try:
734
+ with timeout(30): # 30 second timeout for cleanup
735
+ shutil.rmtree(temp_processing_dir, ignore_errors=True)
736
+ except TimeoutError:
737
+ log_print("Warning: Cleanup of temp_processing_dir timed out", "WARNING")
738
 
739
+ # Clean up the images directory with timeout
740
  if os.path.exists(images_dir):
741
  for file in os.listdir(images_dir):
742
  try:
743
  file_path = os.path.join(images_dir, file)
744
  if os.path.isfile(file_path):
745
+ with timeout(5): # 5 second timeout per file
746
+ os.unlink(file_path)
747
+ except TimeoutError:
748
+ log_print(f"Warning: Cleanup of file {file_path} timed out", "WARNING")
749
  except Exception as e:
750
  log_print(f"Warning: Could not delete file {file_path}: {e}", "WARNING")
751
  except Exception as e:
752
  log_print(f"Error cleaning up temporary files: {e}", "ERROR")
753
 
754
+ # Add timeout context manager
755
+ class timeout:
756
+ def __init__(self, seconds):
757
+ self.seconds = seconds
758
+
759
+ def __enter__(self):
760
+ import signal
761
+ def timeout_handler(signum, frame):
762
+ raise TimeoutError("Timed out!")
763
+ # Set the signal handler and a 5-second alarm
764
+ self.original_handler = signal.signal(signal.SIGALRM, timeout_handler)
765
+ signal.alarm(self.seconds)
766
+ return self
767
+
768
+ def __exit__(self, type, value, traceback):
769
+ import signal
770
+ signal.alarm(0)
771
+ signal.signal(signal.SIGALRM, self.original_handler)
772
+
773
  if __name__ == '__main__':
774
  try:
775
  # Create essential directories
templates/index.html CHANGED
@@ -1303,6 +1303,15 @@
1303
  keepalive: true
1304
  });
1305
 
 
 
 
 
 
 
 
 
 
1306
  let result;
1307
  const contentType = response.headers.get('content-type');
1308
  if (contentType && contentType.includes('application/json')) {
@@ -1311,6 +1320,9 @@
1311
  throw new Error('Server returned non-JSON response');
1312
  }
1313
 
 
 
 
1314
  if (!response.ok) {
1315
  throw new Error(result.message || result.error || `Server error: ${response.status}`);
1316
  }
 
1303
  keepalive: true
1304
  });
1305
 
1306
+ // Set up event source for real-time notifications
1307
+ const eventSource = new EventSource('/notifications');
1308
+ eventSource.onmessage = function(event) {
1309
+ const data = JSON.parse(event.data);
1310
+ if (data.type === 'extracted_text') {
1311
+ notificationSystem.info(`Extracted text from ${data.filename}:\n${data.text}`, 8000);
1312
+ }
1313
+ };
1314
+
1315
  let result;
1316
  const contentType = response.headers.get('content-type');
1317
  if (contentType && contentType.includes('application/json')) {
 
1320
  throw new Error('Server returned non-JSON response');
1321
  }
1322
 
1323
+ // Close event source after getting final response
1324
+ eventSource.close();
1325
+
1326
  if (!response.ok) {
1327
  throw new Error(result.message || result.error || `Server error: ${response.status}`);
1328
  }