Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -633,38 +633,250 @@ def align_and_grade_pipeline(qp_path, ms_path, ans_path, imprint=False):
|
|
| 633 |
print("β Pipeline error:", e)
|
| 634 |
return f"β Error: {e}", None, None, None, None
|
| 635 |
|
| 636 |
-
# ----------------
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
| 648 |
-
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 668 |
|
| 669 |
-
if __name__ == "__main__":
|
| 670 |
demo.launch()
|
|
|
|
| 633 |
print("β Pipeline error:", e)
|
| 634 |
return f"β Error: {e}", None, None, None, None
|
| 635 |
|
| 636 |
+
# ---------------- MASS GRADING HELPERS ----------------
|
| 637 |
+
def process_single_triplet(triplet_data, triplet_index):
|
| 638 |
+
"""Process a single QP+MS+AS triplet and return results."""
|
| 639 |
+
try:
|
| 640 |
+
qp_path = triplet_data["qp_file"]
|
| 641 |
+
ms_path = triplet_data["ms_file"]
|
| 642 |
+
ans_path = triplet_data["ans_file"]
|
| 643 |
+
imprint_flag = triplet_data["imprint"]
|
| 644 |
+
|
| 645 |
+
print(f"π Processing triplet {triplet_index + 1}: {os.path.basename(ans_path)}")
|
| 646 |
+
|
| 647 |
+
qpms_text, as_text, grading_text, grading_pdf_path, imprinted_pdf_path = align_and_grade_pipeline(
|
| 648 |
+
qp_path, ms_path, ans_path, imprint=imprint_flag
|
| 649 |
+
)
|
| 650 |
+
|
| 651 |
+
return {
|
| 652 |
+
"triplet_index": triplet_index,
|
| 653 |
+
"status": "β
Complete",
|
| 654 |
+
"grading_pdf": grading_pdf_path,
|
| 655 |
+
"imprinted_pdf": imprinted_pdf_path if imprint_flag else None,
|
| 656 |
+
"error": None
|
| 657 |
+
}
|
| 658 |
+
except Exception as e:
|
| 659 |
+
print(f"β Error processing triplet {triplet_index + 1}: {e}")
|
| 660 |
+
return {
|
| 661 |
+
"triplet_index": triplet_index,
|
| 662 |
+
"status": f"β Error: {str(e)[:50]}...",
|
| 663 |
+
"grading_pdf": None,
|
| 664 |
+
"imprinted_pdf": None,
|
| 665 |
+
"error": str(e)
|
| 666 |
+
}
|
| 667 |
+
|
| 668 |
+
def process_all_triplets(triplets_data):
|
| 669 |
+
"""Process all triplets sequentially and return results."""
|
| 670 |
+
results = []
|
| 671 |
+
for i, triplet in enumerate(triplets_data):
|
| 672 |
+
if triplet["qp_file"] and triplet["ms_file"] and triplet["ans_file"]:
|
| 673 |
+
result = process_single_triplet(triplet, i)
|
| 674 |
+
results.append(result)
|
| 675 |
+
yield results # Yield intermediate results for progress updates
|
| 676 |
+
return results
|
| 677 |
+
|
| 678 |
+
# ---------------- GRADIO UI ----------------
|
| 679 |
+
with gr.Blocks(title="π AI Mass Grading System", theme=gr.themes.Soft()) as demo:
|
| 680 |
+
gr.Markdown("# π AI Mass Grading System")
|
| 681 |
+
gr.Markdown("Upload multiple sets of Question Papers, Markschemes, and Answer Sheets for batch processing.")
|
| 682 |
+
|
| 683 |
+
# State to store triplets
|
| 684 |
+
triplets_state = gr.State([])
|
| 685 |
+
|
| 686 |
+
# Dynamic triplet container
|
| 687 |
+
triplets_container = gr.Column()
|
| 688 |
+
|
| 689 |
+
# Add triplet button
|
| 690 |
+
with gr.Row():
|
| 691 |
+
add_triplet_btn = gr.Button("β Add New Triplet", variant="secondary")
|
| 692 |
+
start_grading_btn = gr.Button("π Start Mass Grading", variant="primary", visible=False)
|
| 693 |
+
|
| 694 |
+
# Status table
|
| 695 |
+
status_table = gr.Dataframe(
|
| 696 |
+
headers=["Set", "QP File", "MS File", "Answer Sheets", "Status"],
|
| 697 |
+
datatype=["number", "str", "str", "str", "str"],
|
| 698 |
+
label="π Grading Status",
|
| 699 |
+
visible=False
|
| 700 |
+
)
|
| 701 |
+
|
| 702 |
+
# Results section
|
| 703 |
+
results_section = gr.Column(visible=False)
|
| 704 |
+
with results_section:
|
| 705 |
+
gr.Markdown("## π₯ Download Results")
|
| 706 |
+
results_files = gr.Column()
|
| 707 |
+
|
| 708 |
+
def create_triplet_row(triplet_index):
|
| 709 |
+
"""Create a new triplet upload row."""
|
| 710 |
+
with gr.Row() as row:
|
| 711 |
+
with gr.Column(scale=2):
|
| 712 |
+
qp_file = gr.File(label=f"π QP {triplet_index + 1}", file_types=[".pdf"])
|
| 713 |
+
with gr.Column(scale=2):
|
| 714 |
+
ms_file = gr.File(label=f"π MS {triplet_index + 1}", file_types=[".pdf"])
|
| 715 |
+
with gr.Column(scale=2):
|
| 716 |
+
ans_file = gr.File(label=f"π AS {triplet_index + 1}", file_types=[".pdf"])
|
| 717 |
+
with gr.Column(scale=1):
|
| 718 |
+
imprint_check = gr.Checkbox(label="β
Imprint", value=False)
|
| 719 |
+
with gr.Column(scale=1):
|
| 720 |
+
remove_btn = gr.Button("ποΈ", variant="stop", size="sm")
|
| 721 |
+
|
| 722 |
+
return row, qp_file, ms_file, ans_file, imprint_check, remove_btn
|
| 723 |
+
|
| 724 |
+
def add_triplet(current_triplets):
|
| 725 |
+
"""Add a new triplet to the list."""
|
| 726 |
+
new_triplet = {
|
| 727 |
+
"qp_file": None,
|
| 728 |
+
"ms_file": None,
|
| 729 |
+
"ans_file": None,
|
| 730 |
+
"imprint": False
|
| 731 |
+
}
|
| 732 |
+
current_triplets.append(new_triplet)
|
| 733 |
+
return current_triplets, gr.update(visible=len(current_triplets) > 0)
|
| 734 |
+
|
| 735 |
+
def update_status_table(triplets):
|
| 736 |
+
"""Update the status table with current triplets."""
|
| 737 |
+
if not triplets:
|
| 738 |
+
return gr.update(visible=False, value=[])
|
| 739 |
+
|
| 740 |
+
table_data = []
|
| 741 |
+
for i, triplet in enumerate(triplets):
|
| 742 |
+
qp_name = os.path.basename(triplet["qp_file"]) if triplet["qp_file"] else "Not uploaded"
|
| 743 |
+
ms_name = os.path.basename(triplet["ms_file"]) if triplet["ms_file"] else "Not uploaded"
|
| 744 |
+
ans_name = os.path.basename(triplet["ans_file"]) if triplet["ans_file"] else "Not uploaded"
|
| 745 |
+
|
| 746 |
+
# Determine status
|
| 747 |
+
if triplet["qp_file"] and triplet["ms_file"] and triplet["ans_file"]:
|
| 748 |
+
status = "π Ready"
|
| 749 |
+
else:
|
| 750 |
+
status = "β³ Pending uploads"
|
| 751 |
+
|
| 752 |
+
table_data.append([i + 1, qp_name, ms_name, ans_name, status])
|
| 753 |
+
|
| 754 |
+
return gr.update(visible=True, value=table_data)
|
| 755 |
+
|
| 756 |
+
def start_mass_grading(triplets):
|
| 757 |
+
"""Start processing all triplets."""
|
| 758 |
+
if not triplets:
|
| 759 |
+
return gr.update(), gr.update(visible=False)
|
| 760 |
+
|
| 761 |
+
# Filter only complete triplets
|
| 762 |
+
complete_triplets = [t for t in triplets if t["qp_file"] and t["ms_file"] and t["ans_file"]]
|
| 763 |
+
|
| 764 |
+
if not complete_triplets:
|
| 765 |
+
return gr.update(value=[["No complete triplets to process"]]), gr.update(visible=False)
|
| 766 |
+
|
| 767 |
+
# Process triplets and update table progressively
|
| 768 |
+
results = []
|
| 769 |
+
table_data = []
|
| 770 |
+
|
| 771 |
+
for i, triplet in enumerate(complete_triplets):
|
| 772 |
+
# Update status to processing
|
| 773 |
+
table_row = [
|
| 774 |
+
i + 1,
|
| 775 |
+
os.path.basename(triplet["qp_file"]),
|
| 776 |
+
os.path.basename(triplet["ms_file"]),
|
| 777 |
+
os.path.basename(triplet["ans_file"]),
|
| 778 |
+
"π Processing..."
|
| 779 |
+
]
|
| 780 |
+
table_data.append(table_row)
|
| 781 |
+
|
| 782 |
+
# Process the triplet
|
| 783 |
+
result = process_single_triplet(triplet, i)
|
| 784 |
+
results.append(result)
|
| 785 |
+
|
| 786 |
+
# Update status
|
| 787 |
+
table_data[i][4] = result["status"]
|
| 788 |
+
|
| 789 |
+
# Create download links
|
| 790 |
+
download_components = []
|
| 791 |
+
for i, result in enumerate(results):
|
| 792 |
+
if result["grading_pdf"]:
|
| 793 |
+
download_components.append(
|
| 794 |
+
gr.File(value=result["grading_pdf"], label=f"π Set {i+1} - Grading PDF", visible=True)
|
| 795 |
+
)
|
| 796 |
+
if result["imprinted_pdf"]:
|
| 797 |
+
download_components.append(
|
| 798 |
+
gr.File(value=result["imprinted_pdf"], label=f"βοΈ Set {i+1} - Imprinted PDF", visible=True)
|
| 799 |
+
)
|
| 800 |
+
|
| 801 |
+
return gr.update(value=table_data), gr.update(visible=True)
|
| 802 |
+
|
| 803 |
+
# Event handlers
|
| 804 |
+
add_triplet_btn.click(
|
| 805 |
+
fn=add_triplet,
|
| 806 |
+
inputs=[triplets_state],
|
| 807 |
+
outputs=[triplets_state, start_grading_btn]
|
| 808 |
+
).then(
|
| 809 |
+
fn=update_status_table,
|
| 810 |
+
inputs=[triplets_state],
|
| 811 |
+
outputs=[status_table]
|
| 812 |
+
)
|
| 813 |
+
|
| 814 |
+
# File upload handlers to update triplets state
|
| 815 |
+
def create_file_update_handler(triplet_idx, file_type):
|
| 816 |
+
def update_triplet_file(file_obj, current_triplets):
|
| 817 |
+
if triplet_idx < len(current_triplets):
|
| 818 |
+
current_triplets[triplet_idx][file_type] = file_obj.name if file_obj else None
|
| 819 |
+
return current_triplets
|
| 820 |
+
return update_triplet_file
|
| 821 |
+
|
| 822 |
+
def create_imprint_update_handler(triplet_idx):
|
| 823 |
+
def update_triplet_imprint(imprint_val, current_triplets):
|
| 824 |
+
if triplet_idx < len(current_triplets):
|
| 825 |
+
current_triplets[triplet_idx]["imprint"] = imprint_val
|
| 826 |
+
return current_triplets
|
| 827 |
+
return update_triplet_imprint
|
| 828 |
+
|
| 829 |
+
# Dynamic UI management
|
| 830 |
+
@gr.render(inputs=[triplets_state])
|
| 831 |
+
def render_triplets(triplets):
|
| 832 |
+
if not triplets:
|
| 833 |
+
return
|
| 834 |
+
|
| 835 |
+
for i in range(len(triplets)):
|
| 836 |
+
row, qp_file, ms_file, ans_file, imprint_check, remove_btn = create_triplet_row(i)
|
| 837 |
+
|
| 838 |
+
# Set up file upload handlers
|
| 839 |
+
qp_file.change(
|
| 840 |
+
fn=create_file_update_handler(i, "qp_file"),
|
| 841 |
+
inputs=[qp_file, triplets_state],
|
| 842 |
+
outputs=[triplets_state]
|
| 843 |
+
).then(
|
| 844 |
+
fn=update_status_table,
|
| 845 |
+
inputs=[triplets_state],
|
| 846 |
+
outputs=[status_table]
|
| 847 |
+
)
|
| 848 |
+
|
| 849 |
+
ms_file.change(
|
| 850 |
+
fn=create_file_update_handler(i, "ms_file"),
|
| 851 |
+
inputs=[ms_file, triplets_state],
|
| 852 |
+
outputs=[triplets_state]
|
| 853 |
+
).then(
|
| 854 |
+
fn=update_status_table,
|
| 855 |
+
inputs=[triplets_state],
|
| 856 |
+
outputs=[status_table]
|
| 857 |
+
)
|
| 858 |
+
|
| 859 |
+
ans_file.change(
|
| 860 |
+
fn=create_file_update_handler(i, "ans_file"),
|
| 861 |
+
inputs=[ans_file, triplets_state],
|
| 862 |
+
outputs=[triplets_state]
|
| 863 |
+
).then(
|
| 864 |
+
fn=update_status_table,
|
| 865 |
+
inputs=[triplets_state],
|
| 866 |
+
outputs=[status_table]
|
| 867 |
+
)
|
| 868 |
+
|
| 869 |
+
imprint_check.change(
|
| 870 |
+
fn=create_imprint_update_handler(i),
|
| 871 |
+
inputs=[imprint_check, triplets_state],
|
| 872 |
+
outputs=[triplets_state]
|
| 873 |
+
)
|
| 874 |
+
|
| 875 |
+
start_grading_btn.click(
|
| 876 |
+
fn=start_mass_grading,
|
| 877 |
+
inputs=[triplets_state],
|
| 878 |
+
outputs=[status_table, results_section]
|
| 879 |
+
)
|
| 880 |
|
| 881 |
+
if __name__ == "__main__":
|
| 882 |
demo.launch()
|