yamanavijayavardhan commited on
Commit
3f6ca1b
·
1 Parent(s): 5ed4851

fix index.html new new

Browse files
Files changed (2) hide show
  1. main.py +30 -6
  2. templates/index.html +281 -45
main.py CHANGED
@@ -322,7 +322,10 @@ def compute_marks():
322
  try:
323
  # Get correct answers
324
  correct_answers = request.form.getlist('correct_answers[]')
 
 
325
  if not correct_answers:
 
326
  return jsonify({
327
  "error": "Missing data",
328
  "message": "No correct answers provided"
@@ -331,7 +334,9 @@ def compute_marks():
331
  # Create TFIDF values for correct answers
332
  try:
333
  max_tfidf = create_tfidf_values(correct_answers)
 
334
  except Exception as e:
 
335
  return jsonify({
336
  "error": "TFIDF error",
337
  "message": f"Error creating TFIDF values: {str(e)}"
@@ -339,7 +344,10 @@ def compute_marks():
339
 
340
  # Get all uploaded files
341
  files = request.files.getlist('file')
 
 
342
  if not files:
 
343
  return jsonify({
344
  "error": "Missing data",
345
  "message": "No files uploaded"
@@ -347,13 +355,14 @@ def compute_marks():
347
 
348
  # Validate folder structure
349
  is_valid, message = validate_folder_structure(files)
 
 
350
  if not is_valid:
 
351
  return jsonify({
352
  "error": "Invalid folder structure",
353
  "message": message
354
  }), 400
355
-
356
- log_print(f"Folder structure validation: {message}")
357
 
358
  # Create a temporary directory for processing
359
  base_temp_dir = tempfile.mkdtemp()
@@ -372,6 +381,8 @@ def compute_marks():
372
  log_print("Skipping invalid file", "WARNING")
373
  continue
374
 
 
 
375
  # Get folder structure from file path
376
  path_parts = file.filename.split('/')
377
  if len(path_parts) < 2:
@@ -380,6 +391,7 @@ def compute_marks():
380
 
381
  student_folder = path_parts[-2]
382
  filename = path_parts[-1]
 
383
 
384
  # Validate file type
385
  if not is_valid_image_file(filename):
@@ -393,6 +405,7 @@ def compute_marks():
393
  # Initialize student results if not exists
394
  if student_folder not in results:
395
  results[student_folder] = {}
 
396
 
397
  # Save and process file
398
  student_dir = os.path.join(base_temp_dir, student_folder)
@@ -402,7 +415,7 @@ def compute_marks():
402
  # Ensure filepath is secure
403
  filepath = secure_filename(filepath)
404
  file.save(filepath)
405
- log_print(f"Saved file: {filepath}")
406
 
407
  # Extract text
408
  extracted_text = extract_text_from_image(filepath)
@@ -414,6 +427,8 @@ def compute_marks():
414
  })
415
  continue
416
 
 
 
417
  # Clean the extracted text for JSON
418
  extracted_text = extracted_text.encode('ascii', 'ignore').decode('ascii')
419
 
@@ -441,6 +456,8 @@ def compute_marks():
441
  llm_marks * 0.1
442
  )
443
 
 
 
444
  if combined_score > best_score:
445
  best_score = combined_score
446
  best_answer_index = i
@@ -456,7 +473,7 @@ def compute_marks():
456
 
457
  marks = new_value(best_score, 0, 1, 0, 5)
458
  results[student_folder][filename] = round(marks, 2)
459
- log_print(f"Processed {filename} for {student_folder}: {marks} marks")
460
 
461
  except Exception as e:
462
  error_msg = str(e).encode('ascii', 'ignore').decode('ascii')
@@ -476,6 +493,7 @@ def compute_marks():
476
  log_print(f"Warning: Could not clean up temporary directory: {e}", "WARNING")
477
 
478
  if not results:
 
479
  return jsonify({
480
  "error": "Processing error",
481
  "message": "No results computed"
@@ -491,13 +509,19 @@ def compute_marks():
491
  clean_scores[clean_filename] = mark
492
  clean_results[clean_student] = clean_scores
493
 
494
- response = jsonify({
 
 
495
  "results": clean_results,
496
  "failed_files": [{
497
  "file": f["file"].encode('ascii', 'ignore').decode('ascii'),
498
  "error": f["error"].encode('ascii', 'ignore').decode('ascii')
499
  } for f in failed_files]
500
- })
 
 
 
 
501
  response.headers['Content-Type'] = 'application/json'
502
  return response
503
 
 
322
  try:
323
  # Get correct answers
324
  correct_answers = request.form.getlist('correct_answers[]')
325
+ log_print(f"Received correct answers: {correct_answers}")
326
+
327
  if not correct_answers:
328
+ log_print("No correct answers provided", "ERROR")
329
  return jsonify({
330
  "error": "Missing data",
331
  "message": "No correct answers provided"
 
334
  # Create TFIDF values for correct answers
335
  try:
336
  max_tfidf = create_tfidf_values(correct_answers)
337
+ log_print("Created TFIDF values successfully")
338
  except Exception as e:
339
+ log_print(f"TFIDF error: {str(e)}", "ERROR")
340
  return jsonify({
341
  "error": "TFIDF error",
342
  "message": f"Error creating TFIDF values: {str(e)}"
 
344
 
345
  # Get all uploaded files
346
  files = request.files.getlist('file')
347
+ log_print(f"Received {len(files)} files")
348
+
349
  if not files:
350
+ log_print("No files uploaded", "ERROR")
351
  return jsonify({
352
  "error": "Missing data",
353
  "message": "No files uploaded"
 
355
 
356
  # Validate folder structure
357
  is_valid, message = validate_folder_structure(files)
358
+ log_print(f"Folder structure validation: {message}")
359
+
360
  if not is_valid:
361
+ log_print(f"Invalid folder structure: {message}", "ERROR")
362
  return jsonify({
363
  "error": "Invalid folder structure",
364
  "message": message
365
  }), 400
 
 
366
 
367
  # Create a temporary directory for processing
368
  base_temp_dir = tempfile.mkdtemp()
 
381
  log_print("Skipping invalid file", "WARNING")
382
  continue
383
 
384
+ log_print(f"Processing file: {file.filename}")
385
+
386
  # Get folder structure from file path
387
  path_parts = file.filename.split('/')
388
  if len(path_parts) < 2:
 
391
 
392
  student_folder = path_parts[-2]
393
  filename = path_parts[-1]
394
+ log_print(f"Student folder: {student_folder}, Filename: {filename}")
395
 
396
  # Validate file type
397
  if not is_valid_image_file(filename):
 
405
  # Initialize student results if not exists
406
  if student_folder not in results:
407
  results[student_folder] = {}
408
+ log_print(f"Initialized results for student: {student_folder}")
409
 
410
  # Save and process file
411
  student_dir = os.path.join(base_temp_dir, student_folder)
 
415
  # Ensure filepath is secure
416
  filepath = secure_filename(filepath)
417
  file.save(filepath)
418
+ log_print(f"Saved file to: {filepath}")
419
 
420
  # Extract text
421
  extracted_text = extract_text_from_image(filepath)
 
427
  })
428
  continue
429
 
430
+ log_print(f"Extracted text: {extracted_text[:100]}...")
431
+
432
  # Clean the extracted text for JSON
433
  extracted_text = extracted_text.encode('ascii', 'ignore').decode('ascii')
434
 
 
456
  llm_marks * 0.1
457
  )
458
 
459
+ log_print(f"Scores for answer {i+1}: semantic={semantic_score}, word={word_score}, tfidf={tfidf_score}, ft={ft_score}, llm={llm_marks}, combined={combined_score}")
460
+
461
  if combined_score > best_score:
462
  best_score = combined_score
463
  best_answer_index = i
 
473
 
474
  marks = new_value(best_score, 0, 1, 0, 5)
475
  results[student_folder][filename] = round(marks, 2)
476
+ log_print(f"Assigned marks for {filename} in {student_folder}: {marks}")
477
 
478
  except Exception as e:
479
  error_msg = str(e).encode('ascii', 'ignore').decode('ascii')
 
493
  log_print(f"Warning: Could not clean up temporary directory: {e}", "WARNING")
494
 
495
  if not results:
496
+ log_print("No results computed", "ERROR")
497
  return jsonify({
498
  "error": "Processing error",
499
  "message": "No results computed"
 
509
  clean_scores[clean_filename] = mark
510
  clean_results[clean_student] = clean_scores
511
 
512
+ log_print(f"Final results: {clean_results}")
513
+
514
+ response_data = {
515
  "results": clean_results,
516
  "failed_files": [{
517
  "file": f["file"].encode('ascii', 'ignore').decode('ascii'),
518
  "error": f["error"].encode('ascii', 'ignore').decode('ascii')
519
  } for f in failed_files]
520
+ }
521
+
522
+ log_print(f"Sending response: {response_data}")
523
+
524
+ response = jsonify(response_data)
525
  response.headers['Content-Type'] = 'application/json'
526
  return response
527
 
templates/index.html CHANGED
@@ -532,9 +532,138 @@
532
  0% { transform: rotate(0deg); }
533
  100% { transform: rotate(360deg); }
534
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535
  </style>
536
  </head>
537
  <body>
 
 
538
  <div class="container">
539
  <div class="section">
540
  <h2>Upload Query CSV File</h2>
@@ -599,7 +728,19 @@
599
  <div class="section">
600
  <div id="answers-container"></div>
601
  <button id="compute-marks-btn" onclick="computeMarks()">Compute Marks</button>
602
- <div id="marks-table-container"></div>
 
 
 
 
 
 
 
 
 
 
 
 
603
  </div>
604
  </div>
605
 
@@ -611,6 +752,64 @@
611
  </div>
612
 
613
  <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
  document.addEventListener('DOMContentLoaded', function() {
615
  const fileTypeSelect = document.getElementById('file-type');
616
  const pdfSection = document.getElementById('pdf-section');
@@ -670,31 +869,34 @@
670
  const pdfFiles = document.getElementById('pdf-files').files;
671
 
672
  if (!queryfile) {
673
- alert("Please upload a query file first!");
674
  hideLoading();
675
  return;
676
  }
677
 
 
678
  const formData = new FormData();
679
  formData.append('file_type', fileType);
680
  formData.append('query_file', queryfile);
681
 
682
  if (fileType === 'csv') {
683
  if (!anscsvFile) {
684
- alert("Please upload a CSV file for answers!");
685
  hideLoading();
686
  return;
687
  }
688
  formData.append('ans_csv_file', anscsvFile);
 
689
  } else if (fileType === 'pdf') {
690
  if (!pdfFiles || pdfFiles.length < 2) {
691
- alert("Please upload at least 2 PDF files!");
692
  hideLoading();
693
  return;
694
  }
695
  for (let file of pdfFiles) {
696
  formData.append('pdf_files[]', file);
697
  }
 
698
  }
699
 
700
  const computeBtn = document.getElementById('compute-btn');
@@ -716,13 +918,14 @@
716
 
717
  if (result.answers) {
718
  displayAnswers(result.answers);
 
719
  } else {
720
  throw new Error('No answers received from server');
721
  }
722
 
723
  } catch (error) {
724
  console.error('Error:', error);
725
- alert('Error: ' + error.message);
726
  } finally {
727
  hideLoading();
728
  const computeBtn = document.getElementById('compute-btn');
@@ -953,42 +1156,42 @@
953
  try {
954
  showLoading();
955
  const answerBoxes = document.querySelectorAll('.answer-box');
 
956
  if (answerBoxes.length === 0) {
957
- alert("Please generate answers first!");
958
  hideLoading();
959
  return;
960
  }
961
 
962
  const answerValues = Array.from(answerBoxes).map(box => box.value.trim());
 
963
  if (answerValues.some(answer => !answer)) {
964
- alert("Please ensure all answer boxes are filled!");
965
  hideLoading();
966
  return;
967
  }
968
 
969
  if (selectedFiles.size === 0) {
970
- alert("Please upload student answer files!");
971
  hideLoading();
972
  return;
973
  }
974
 
 
 
975
  const computeBtn = document.getElementById('compute-marks-btn');
976
  computeBtn.disabled = true;
977
 
978
  const formData = new FormData();
979
 
980
- // Add each answer as a separate correct_answers[] entry
981
- answerValues.forEach(answer => {
982
  formData.append('correct_answers[]', answer);
983
  });
984
 
985
- // Add files with their folder structure
986
  let validFiles = 0;
987
  selectedFiles.forEach((fileInfo, path) => {
988
  if (fileInfo.file.type.startsWith('image/')) {
989
- // Maintain the folder structure in the file path
990
- const relativePath = fileInfo.fullPath;
991
- formData.append('file', fileInfo.file, relativePath);
992
  validFiles++;
993
  }
994
  });
@@ -997,7 +1200,8 @@
997
  throw new Error("No valid image files found in the uploaded folder");
998
  }
999
 
1000
- console.log('Sending request to compute marks...');
 
1001
  const response = await fetch('/compute_marks', {
1002
  method: 'POST',
1003
  headers: {
@@ -1006,15 +1210,15 @@
1006
  body: formData
1007
  });
1008
 
1009
- if (!response.ok) {
1010
- const errorText = await response.text();
1011
- console.error('Server error:', errorText);
1012
- throw new Error(`Server returned error ${response.status}: ${errorText}`);
 
 
 
1013
  }
1014
 
1015
- const result = await response.json();
1016
- console.log('Parsed response:', result);
1017
-
1018
  if (result.error) {
1019
  throw new Error(result.message || result.error);
1020
  }
@@ -1024,18 +1228,18 @@
1024
  }
1025
 
1026
  displayMarks(result.results);
 
1027
 
1028
  if (result.failed_files && result.failed_files.length > 0) {
1029
- console.warn('Some files failed to process:', result.failed_files);
1030
  const failedMessage = result.failed_files
1031
  .map(f => `${f.file}: ${f.error}`)
1032
  .join('\n');
1033
- alert(`Warning: Some files failed to process:\n${failedMessage}`);
1034
  }
1035
 
1036
  } catch (error) {
1037
  console.error('Error details:', error);
1038
- alert('Error computing marks: ' + error.message);
1039
  } finally {
1040
  hideLoading();
1041
  const computeBtn = document.getElementById('compute-marks-btn');
@@ -1045,29 +1249,61 @@
1045
 
1046
  function displayMarks(results) {
1047
  const tableBody = document.getElementById('marks-table-body');
 
 
 
 
 
1048
  tableBody.innerHTML = '';
1049
 
1050
- // Create table header
1051
- const headerRow = document.createElement('tr');
1052
- headerRow.innerHTML = `
1053
- <th>Student Folder</th>
1054
- <th>Image Name</th>
1055
- <th>Marks</th>
1056
- `;
1057
- tableBody.appendChild(headerRow);
1058
 
1059
- // Add results
1060
- Object.entries(results).forEach(([student, scores]) => {
1061
- Object.entries(scores).forEach(([filename, mark]) => {
1062
- const row = document.createElement('tr');
1063
- row.innerHTML = `
1064
- <td>${student}</td>
1065
- <td>${filename}</td>
1066
- <td>${mark}</td>
1067
- `;
1068
- tableBody.appendChild(row);
1069
- });
1070
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1071
  }
1072
  </script>
1073
  </body>
 
532
  0% { transform: rotate(0deg); }
533
  100% { transform: rotate(360deg); }
534
  }
535
+
536
+ #marks-table-container {
537
+ margin-top: 20px;
538
+ overflow-x: auto;
539
+ }
540
+
541
+ #marks-table {
542
+ width: 100%;
543
+ border-collapse: collapse;
544
+ margin-top: 20px;
545
+ background: white;
546
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
547
+ }
548
+
549
+ #marks-table th,
550
+ #marks-table td {
551
+ padding: 12px;
552
+ text-align: left;
553
+ border-bottom: 1px solid #ddd;
554
+ }
555
+
556
+ #marks-table th {
557
+ background-color: var(--primary-color);
558
+ color: white;
559
+ font-weight: 500;
560
+ }
561
+
562
+ #marks-table tr:hover {
563
+ background-color: #f5f5f5;
564
+ }
565
+
566
+ #marks-table tbody tr:nth-child(even) {
567
+ background-color: #f8f9fa;
568
+ }
569
+
570
+ /* Notification System Styles */
571
+ .notification-container {
572
+ position: fixed;
573
+ top: 20px;
574
+ right: 20px;
575
+ z-index: 1000;
576
+ max-width: 400px;
577
+ max-height: 80vh;
578
+ overflow-y: auto;
579
+ }
580
+
581
+ .notification {
582
+ padding: 15px 20px;
583
+ margin-bottom: 10px;
584
+ border-radius: 8px;
585
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
586
+ animation: slideIn 0.3s ease-out;
587
+ position: relative;
588
+ display: flex;
589
+ justify-content: space-between;
590
+ align-items: flex-start;
591
+ }
592
+
593
+ .notification.info {
594
+ background-color: #e3f2fd;
595
+ border-left: 4px solid #2196f3;
596
+ color: #0d47a1;
597
+ }
598
+
599
+ .notification.success {
600
+ background-color: #e8f5e9;
601
+ border-left: 4px solid #4caf50;
602
+ color: #1b5e20;
603
+ }
604
+
605
+ .notification.warning {
606
+ background-color: #fff3e0;
607
+ border-left: 4px solid #ff9800;
608
+ color: #e65100;
609
+ }
610
+
611
+ .notification.error {
612
+ background-color: #ffebee;
613
+ border-left: 4px solid #f44336;
614
+ color: #b71c1c;
615
+ }
616
+
617
+ .notification-content {
618
+ flex-grow: 1;
619
+ margin-right: 10px;
620
+ word-break: break-word;
621
+ }
622
+
623
+ .notification-close {
624
+ background: none;
625
+ border: none;
626
+ color: inherit;
627
+ cursor: pointer;
628
+ font-size: 20px;
629
+ padding: 0 5px;
630
+ opacity: 0.7;
631
+ }
632
+
633
+ .notification-close:hover {
634
+ opacity: 1;
635
+ }
636
+
637
+ @keyframes slideIn {
638
+ from {
639
+ transform: translateX(100%);
640
+ opacity: 0;
641
+ }
642
+ to {
643
+ transform: translateX(0);
644
+ opacity: 1;
645
+ }
646
+ }
647
+
648
+ @keyframes slideOut {
649
+ from {
650
+ transform: translateX(0);
651
+ opacity: 1;
652
+ }
653
+ to {
654
+ transform: translateX(100%);
655
+ opacity: 0;
656
+ }
657
+ }
658
+
659
+ .notification.fade-out {
660
+ animation: slideOut 0.3s ease-out forwards;
661
+ }
662
  </style>
663
  </head>
664
  <body>
665
+ <div class="notification-container" id="notification-container"></div>
666
+
667
  <div class="container">
668
  <div class="section">
669
  <h2>Upload Query CSV File</h2>
 
728
  <div class="section">
729
  <div id="answers-container"></div>
730
  <button id="compute-marks-btn" onclick="computeMarks()">Compute Marks</button>
731
+ <div id="marks-table-container">
732
+ <table id="marks-table">
733
+ <thead>
734
+ <tr>
735
+ <th>Student Folder</th>
736
+ <th>Image Name</th>
737
+ <th>Marks</th>
738
+ </tr>
739
+ </thead>
740
+ <tbody id="marks-table-body">
741
+ </tbody>
742
+ </table>
743
+ </div>
744
  </div>
745
  </div>
746
 
 
752
  </div>
753
 
754
  <script>
755
+ const notificationSystem = {
756
+ container: null,
757
+ init() {
758
+ this.container = document.getElementById('notification-container');
759
+ },
760
+
761
+ show(message, type = 'info', duration = 5000) {
762
+ if (!this.container) this.init();
763
+
764
+ const notification = document.createElement('div');
765
+ notification.className = `notification ${type}`;
766
+
767
+ const content = document.createElement('div');
768
+ content.className = 'notification-content';
769
+ content.textContent = message;
770
+
771
+ const closeBtn = document.createElement('button');
772
+ closeBtn.className = 'notification-close';
773
+ closeBtn.innerHTML = '&times;';
774
+ closeBtn.onclick = () => this.remove(notification);
775
+
776
+ notification.appendChild(content);
777
+ notification.appendChild(closeBtn);
778
+ this.container.appendChild(notification);
779
+
780
+ if (duration > 0) {
781
+ setTimeout(() => this.remove(notification), duration);
782
+ }
783
+
784
+ return notification;
785
+ },
786
+
787
+ remove(notification) {
788
+ notification.classList.add('fade-out');
789
+ setTimeout(() => {
790
+ if (notification.parentElement === this.container) {
791
+ this.container.removeChild(notification);
792
+ }
793
+ }, 300);
794
+ },
795
+
796
+ success(message, duration = 5000) {
797
+ return this.show(message, 'success', duration);
798
+ },
799
+
800
+ error(message, duration = 8000) {
801
+ return this.show(message, 'error', duration);
802
+ },
803
+
804
+ warning(message, duration = 6000) {
805
+ return this.show(message, 'warning', duration);
806
+ },
807
+
808
+ info(message, duration = 4000) {
809
+ return this.show(message, 'info', duration);
810
+ }
811
+ };
812
+
813
  document.addEventListener('DOMContentLoaded', function() {
814
  const fileTypeSelect = document.getElementById('file-type');
815
  const pdfSection = document.getElementById('pdf-section');
 
869
  const pdfFiles = document.getElementById('pdf-files').files;
870
 
871
  if (!queryfile) {
872
+ notificationSystem.error("Please upload a query file first!");
873
  hideLoading();
874
  return;
875
  }
876
 
877
+ notificationSystem.info("Processing files...");
878
  const formData = new FormData();
879
  formData.append('file_type', fileType);
880
  formData.append('query_file', queryfile);
881
 
882
  if (fileType === 'csv') {
883
  if (!anscsvFile) {
884
+ notificationSystem.error("Please upload a CSV file for answers!");
885
  hideLoading();
886
  return;
887
  }
888
  formData.append('ans_csv_file', anscsvFile);
889
+ notificationSystem.info("Processing CSV file...");
890
  } else if (fileType === 'pdf') {
891
  if (!pdfFiles || pdfFiles.length < 2) {
892
+ notificationSystem.error("Please upload at least 2 PDF files!");
893
  hideLoading();
894
  return;
895
  }
896
  for (let file of pdfFiles) {
897
  formData.append('pdf_files[]', file);
898
  }
899
+ notificationSystem.info(`Processing ${pdfFiles.length} PDF files...`);
900
  }
901
 
902
  const computeBtn = document.getElementById('compute-btn');
 
918
 
919
  if (result.answers) {
920
  displayAnswers(result.answers);
921
+ notificationSystem.success("Successfully generated answers!");
922
  } else {
923
  throw new Error('No answers received from server');
924
  }
925
 
926
  } catch (error) {
927
  console.error('Error:', error);
928
+ notificationSystem.error('Error: ' + error.message);
929
  } finally {
930
  hideLoading();
931
  const computeBtn = document.getElementById('compute-btn');
 
1156
  try {
1157
  showLoading();
1158
  const answerBoxes = document.querySelectorAll('.answer-box');
1159
+
1160
  if (answerBoxes.length === 0) {
1161
+ notificationSystem.error("Please generate answers first!");
1162
  hideLoading();
1163
  return;
1164
  }
1165
 
1166
  const answerValues = Array.from(answerBoxes).map(box => box.value.trim());
1167
+
1168
  if (answerValues.some(answer => !answer)) {
1169
+ notificationSystem.error("Please ensure all answer boxes are filled!");
1170
  hideLoading();
1171
  return;
1172
  }
1173
 
1174
  if (selectedFiles.size === 0) {
1175
+ notificationSystem.error("Please upload student answer files!");
1176
  hideLoading();
1177
  return;
1178
  }
1179
 
1180
+ notificationSystem.info(`Processing ${selectedFiles.size} student files...`);
1181
+
1182
  const computeBtn = document.getElementById('compute-marks-btn');
1183
  computeBtn.disabled = true;
1184
 
1185
  const formData = new FormData();
1186
 
1187
+ answerValues.forEach((answer, index) => {
 
1188
  formData.append('correct_answers[]', answer);
1189
  });
1190
 
 
1191
  let validFiles = 0;
1192
  selectedFiles.forEach((fileInfo, path) => {
1193
  if (fileInfo.file.type.startsWith('image/')) {
1194
+ formData.append('file', fileInfo.file, fileInfo.fullPath);
 
 
1195
  validFiles++;
1196
  }
1197
  });
 
1200
  throw new Error("No valid image files found in the uploaded folder");
1201
  }
1202
 
1203
+ notificationSystem.info(`Processing ${validFiles} valid image files...`);
1204
+
1205
  const response = await fetch('/compute_marks', {
1206
  method: 'POST',
1207
  headers: {
 
1210
  body: formData
1211
  });
1212
 
1213
+ const responseText = await response.text();
1214
+ let result;
1215
+ try {
1216
+ result = JSON.parse(responseText);
1217
+ } catch (e) {
1218
+ console.error('Error parsing JSON:', e);
1219
+ throw new Error('Invalid JSON response from server');
1220
  }
1221
 
 
 
 
1222
  if (result.error) {
1223
  throw new Error(result.message || result.error);
1224
  }
 
1228
  }
1229
 
1230
  displayMarks(result.results);
1231
+ notificationSystem.success("Successfully computed marks!");
1232
 
1233
  if (result.failed_files && result.failed_files.length > 0) {
 
1234
  const failedMessage = result.failed_files
1235
  .map(f => `${f.file}: ${f.error}`)
1236
  .join('\n');
1237
+ notificationSystem.warning(`Some files failed to process:\n${failedMessage}`);
1238
  }
1239
 
1240
  } catch (error) {
1241
  console.error('Error details:', error);
1242
+ notificationSystem.error('Error computing marks: ' + error.message);
1243
  } finally {
1244
  hideLoading();
1245
  const computeBtn = document.getElementById('compute-marks-btn');
 
1249
 
1250
  function displayMarks(results) {
1251
  const tableBody = document.getElementById('marks-table-body');
1252
+ if (!tableBody) {
1253
+ throw new Error('Table body element not found');
1254
+ }
1255
+
1256
+ // Clear existing rows
1257
  tableBody.innerHTML = '';
1258
 
1259
+ // Sort student folders for consistent display
1260
+ const sortedStudents = Object.keys(results).sort();
 
 
 
 
 
 
1261
 
1262
+ for (const student of sortedStudents) {
1263
+ const scores = results[student];
1264
+ const row = document.createElement('tr');
1265
+
1266
+ // Add student name cell
1267
+ const studentCell = document.createElement('td');
1268
+ studentCell.textContent = student;
1269
+ row.appendChild(studentCell);
1270
+
1271
+ // Add scores cell
1272
+ const scoresCell = document.createElement('td');
1273
+ const scoresList = document.createElement('ul');
1274
+ scoresList.className = 'list-unstyled mb-0';
1275
+
1276
+ // Sort filenames for consistent display
1277
+ const sortedFiles = Object.keys(scores).sort();
1278
+
1279
+ for (const filename of sortedFiles) {
1280
+ const score = scores[filename];
1281
+ const scoreItem = document.createElement('li');
1282
+ scoreItem.textContent = `${filename}: ${score}`;
1283
+ scoresList.appendChild(scoreItem);
1284
+ }
1285
+
1286
+ scoresCell.appendChild(scoresList);
1287
+ row.appendChild(scoresCell);
1288
+
1289
+ // Calculate and add average score cell
1290
+ const scoreValues = Object.values(scores);
1291
+ const average = scoreValues.length > 0
1292
+ ? (scoreValues.reduce((a, b) => a + b, 0) / scoreValues.length).toFixed(2)
1293
+ : 'N/A';
1294
+
1295
+ const averageCell = document.createElement('td');
1296
+ averageCell.textContent = average;
1297
+ row.appendChild(averageCell);
1298
+
1299
+ tableBody.appendChild(row);
1300
+ }
1301
+
1302
+ // Show the results table
1303
+ const resultsSection = document.getElementById('results-section');
1304
+ if (resultsSection) {
1305
+ resultsSection.style.display = 'block';
1306
+ }
1307
  }
1308
  </script>
1309
  </body>