Commit
·
8a0e8d8
1
Parent(s):
bd41193
adding count down 1 2 3 4 and removing the null return error
Browse files- main.py +38 -47
- templates/index.html +47 -7
main.py
CHANGED
@@ -334,29 +334,34 @@ def validate_folder_structure(files):
|
|
334 |
@app.route('/notifications')
|
335 |
def notifications():
|
336 |
def generate():
|
337 |
-
|
338 |
-
|
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 |
-
|
|
|
|
|
351 |
except queue.Empty:
|
352 |
# If no notification, yield empty to keep connection alive
|
353 |
-
yield "data: {}\n\n"
|
354 |
-
time.sleep(0.5) #
|
355 |
|
356 |
except Exception as e:
|
357 |
-
|
358 |
-
|
359 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
360 |
|
361 |
return Response(generate(), mimetype='text/event-stream')
|
362 |
|
@@ -448,7 +453,9 @@ def compute_marks():
|
|
448 |
# Dictionary to store results by student folder and image name
|
449 |
results = {}
|
450 |
failed_files = []
|
451 |
-
extracted_texts = {}
|
|
|
|
|
452 |
|
453 |
try:
|
454 |
# Process each file
|
@@ -576,6 +583,16 @@ def compute_marks():
|
|
576 |
results[student_folder][filename] = round(marks, 2)
|
577 |
log_print(f"Assigned marks for {filename} in {student_folder}: {marks}")
|
578 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
579 |
except Exception as e:
|
580 |
error_msg = str(e).encode('ascii', 'ignore').decode('ascii')
|
581 |
log_print(f"Error processing file {file.filename}: {error_msg}", "ERROR")
|
@@ -627,7 +644,7 @@ def compute_marks():
|
|
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": {
|
631 |
filename: text.encode('ascii', 'ignore').decode('ascii')
|
632 |
for filename, text in extracted_texts.items()
|
633 |
}
|
@@ -725,51 +742,25 @@ def ensure_initialization():
|
|
725 |
wait_for_initialization()
|
726 |
|
727 |
def cleanup_temp_files():
|
728 |
-
"""Clean up temporary files with proper error handling
|
729 |
try:
|
730 |
-
# Clean up the temporary processing directory
|
731 |
temp_processing_dir = os.path.join(BASE_DIR, 'temp_processing')
|
732 |
if os.path.exists(temp_processing_dir):
|
733 |
-
|
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
|
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 |
-
|
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
|
@@ -778,7 +769,7 @@ if __name__ == '__main__':
|
|
778 |
|
779 |
# Configure server for long-running requests
|
780 |
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
|
781 |
-
app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024 #
|
782 |
|
783 |
# Add additional server configurations
|
784 |
app.config['PERMANENT_SESSION_LIFETIME'] = 3600 # 1 hour session lifetime
|
|
|
334 |
@app.route('/notifications')
|
335 |
def notifications():
|
336 |
def generate():
|
337 |
+
error_count = 0
|
338 |
+
max_errors = 3
|
339 |
|
340 |
while True:
|
341 |
try:
|
|
|
|
|
|
|
|
|
|
|
342 |
# Get notification from queue (non-blocking)
|
343 |
try:
|
344 |
notification = notification_queue.get_nowait()
|
345 |
+
if notification:
|
346 |
+
yield "data: " + json.dumps(notification) + "\n\n"
|
347 |
+
error_count = 0 # Reset error count on successful notification
|
348 |
except queue.Empty:
|
349 |
# If no notification, yield empty to keep connection alive
|
350 |
+
yield "data: " + json.dumps({"type": "ping"}) + "\n\n"
|
351 |
+
time.sleep(0.5) # Keep the connection alive
|
352 |
|
353 |
except Exception as e:
|
354 |
+
error_count += 1
|
355 |
+
error_msg = str(e).encode('ascii', 'ignore').decode('ascii')
|
356 |
+
log_print(f"Error in notification stream: {error_msg}", "ERROR")
|
357 |
+
|
358 |
+
yield "data: " + json.dumps({
|
359 |
+
"type": "error",
|
360 |
+
"message": f"Server error: {error_msg}"
|
361 |
+
}) + "\n\n"
|
362 |
+
|
363 |
+
if error_count >= max_errors:
|
364 |
+
break
|
365 |
|
366 |
return Response(generate(), mimetype='text/event-stream')
|
367 |
|
|
|
453 |
# Dictionary to store results by student folder and image name
|
454 |
results = {}
|
455 |
failed_files = []
|
456 |
+
extracted_texts = {}
|
457 |
+
processed_count = 0
|
458 |
+
total_files = len(files)
|
459 |
|
460 |
try:
|
461 |
# Process each file
|
|
|
583 |
results[student_folder][filename] = round(marks, 2)
|
584 |
log_print(f"Assigned marks for {filename} in {student_folder}: {marks}")
|
585 |
|
586 |
+
# Update progress
|
587 |
+
processed_count += 1
|
588 |
+
progress_data = {
|
589 |
+
"type": "progress",
|
590 |
+
"processed": processed_count,
|
591 |
+
"total": total_files,
|
592 |
+
"current_file": filename
|
593 |
+
}
|
594 |
+
notification_queue.put(progress_data)
|
595 |
+
|
596 |
except Exception as e:
|
597 |
error_msg = str(e).encode('ascii', 'ignore').decode('ascii')
|
598 |
log_print(f"Error processing file {file.filename}: {error_msg}", "ERROR")
|
|
|
644 |
"file": f["file"].encode('ascii', 'ignore').decode('ascii'),
|
645 |
"error": f["error"].encode('ascii', 'ignore').decode('ascii')
|
646 |
} for f in failed_files],
|
647 |
+
"extracted_texts": {
|
648 |
filename: text.encode('ascii', 'ignore').decode('ascii')
|
649 |
for filename, text in extracted_texts.items()
|
650 |
}
|
|
|
742 |
wait_for_initialization()
|
743 |
|
744 |
def cleanup_temp_files():
|
745 |
+
"""Clean up temporary files with proper error handling"""
|
746 |
try:
|
747 |
+
# Clean up the temporary processing directory
|
748 |
temp_processing_dir = os.path.join(BASE_DIR, 'temp_processing')
|
749 |
if os.path.exists(temp_processing_dir):
|
750 |
+
shutil.rmtree(temp_processing_dir, ignore_errors=True)
|
|
|
|
|
|
|
|
|
751 |
|
752 |
+
# Clean up the images directory
|
753 |
if os.path.exists(images_dir):
|
754 |
for file in os.listdir(images_dir):
|
755 |
try:
|
756 |
file_path = os.path.join(images_dir, file)
|
757 |
if os.path.isfile(file_path):
|
758 |
+
os.unlink(file_path)
|
|
|
|
|
|
|
759 |
except Exception as e:
|
760 |
log_print(f"Warning: Could not delete file {file_path}: {e}", "WARNING")
|
761 |
except Exception as e:
|
762 |
log_print(f"Error cleaning up temporary files: {e}", "ERROR")
|
763 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
764 |
if __name__ == '__main__':
|
765 |
try:
|
766 |
# Create essential directories
|
|
|
769 |
|
770 |
# Configure server for long-running requests
|
771 |
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
|
772 |
+
app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024 # 50MB max file size
|
773 |
|
774 |
# Add additional server configurations
|
775 |
app.config['PERMANENT_SESSION_LIFETIME'] = 3600 # 1 hour session lifetime
|
templates/index.html
CHANGED
@@ -1295,23 +1295,65 @@
|
|
1295 |
`;
|
1296 |
|
1297 |
try {
|
1298 |
-
const controller = new AbortController();
|
1299 |
const response = await fetch('/compute_marks', {
|
1300 |
method: 'POST',
|
1301 |
body: formData,
|
1302 |
-
signal: controller.signal,
|
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 |
-
|
1310 |
-
|
1311 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1312 |
}
|
1313 |
};
|
1314 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1315 |
let result;
|
1316 |
const contentType = response.headers.get('content-type');
|
1317 |
if (contentType && contentType.includes('application/json')) {
|
@@ -1349,8 +1391,6 @@
|
|
1349 |
console.error('Fetch error:', fetchError);
|
1350 |
if (!navigator.onLine) {
|
1351 |
throw new Error('No internet connection. Please check your connection and try again.');
|
1352 |
-
} else if (fetchError.name === 'AbortError') {
|
1353 |
-
throw new Error('Request was cancelled. Please try again.');
|
1354 |
} else {
|
1355 |
throw new Error(`Server error: ${fetchError.message}`);
|
1356 |
}
|
|
|
1295 |
`;
|
1296 |
|
1297 |
try {
|
|
|
1298 |
const response = await fetch('/compute_marks', {
|
1299 |
method: 'POST',
|
1300 |
body: formData,
|
|
|
1301 |
keepalive: true
|
1302 |
});
|
1303 |
|
1304 |
// Set up event source for real-time notifications
|
1305 |
const eventSource = new EventSource('/notifications');
|
1306 |
+
let errorCount = 0;
|
1307 |
+
const maxErrors = 3;
|
1308 |
+
|
1309 |
eventSource.onmessage = function(event) {
|
1310 |
+
try {
|
1311 |
+
const data = JSON.parse(event.data);
|
1312 |
+
if (!data || typeof data !== 'object') {
|
1313 |
+
console.warn('Received invalid notification data:', event.data);
|
1314 |
+
return;
|
1315 |
+
}
|
1316 |
+
|
1317 |
+
if (data.type === 'extracted_text') {
|
1318 |
+
notificationSystem.info(`Extracted text from ${data.filename}:\n${data.text}`, 8000);
|
1319 |
+
} else if (data.type === 'progress') {
|
1320 |
+
// Update progress in loading text
|
1321 |
+
const loadingText = document.querySelector('.loading-text');
|
1322 |
+
if (loadingText) {
|
1323 |
+
loadingText.innerHTML = `
|
1324 |
+
Processing ${data.total} files...<br>
|
1325 |
+
This may take several minutes depending on the number of files.<br>
|
1326 |
+
Please keep this window open.<br>
|
1327 |
+
<small>Progress: ${data.processed}/${data.total} files processed<br>
|
1328 |
+
Current file: ${data.current_file}</small>
|
1329 |
+
`;
|
1330 |
+
}
|
1331 |
+
// Reset error count on successful progress update
|
1332 |
+
errorCount = 0;
|
1333 |
+
} else if (data.type === 'error') {
|
1334 |
+
console.error('Server notification error:', data.message);
|
1335 |
+
errorCount++;
|
1336 |
+
if (errorCount >= maxErrors) {
|
1337 |
+
eventSource.close();
|
1338 |
+
notificationSystem.error('Lost connection to server. Please refresh the page.');
|
1339 |
+
}
|
1340 |
+
}
|
1341 |
+
} catch (e) {
|
1342 |
+
console.error('Error parsing notification data:', e);
|
1343 |
+
errorCount++;
|
1344 |
+
if (errorCount >= maxErrors) {
|
1345 |
+
eventSource.close();
|
1346 |
+
notificationSystem.error('Error processing server updates. Please refresh the page.');
|
1347 |
+
}
|
1348 |
}
|
1349 |
};
|
1350 |
|
1351 |
+
eventSource.onerror = function(error) {
|
1352 |
+
console.error('EventSource error:', error);
|
1353 |
+
eventSource.close();
|
1354 |
+
notificationSystem.error('Lost connection to server. Please refresh the page.');
|
1355 |
+
};
|
1356 |
+
|
1357 |
let result;
|
1358 |
const contentType = response.headers.get('content-type');
|
1359 |
if (contentType && contentType.includes('application/json')) {
|
|
|
1391 |
console.error('Fetch error:', fetchError);
|
1392 |
if (!navigator.onLine) {
|
1393 |
throw new Error('No internet connection. Please check your connection and try again.');
|
|
|
|
|
1394 |
} else {
|
1395 |
throw new Error(`Server error: ${fetchError.message}`);
|
1396 |
}
|