Spaces:
Sleeping
Sleeping
Update app.py
Browse filesmass grading v1.2
app.py
CHANGED
|
@@ -680,274 +680,193 @@ with gr.Blocks(title="๐ AI Mass Grading System", theme=gr.themes.Soft()) as d
|
|
| 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 |
-
# Initialize with one triplet
|
| 684 |
-
initial_triplet = {
|
| 685 |
-
"qp_file": None,
|
| 686 |
-
"ms_file": None,
|
| 687 |
-
"ans_file": None,
|
| 688 |
-
"imprint": False
|
| 689 |
-
}
|
| 690 |
-
triplets_state = gr.State([initial_triplet])
|
| 691 |
-
|
| 692 |
-
# Initial triplet row (always visible)
|
| 693 |
with gr.Row():
|
| 694 |
-
|
| 695 |
-
|
| 696 |
-
|
| 697 |
-
|
| 698 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 699 |
|
| 700 |
-
|
| 701 |
-
additional_triplets = gr.Column()
|
| 702 |
|
| 703 |
-
# Buttons row
|
| 704 |
with gr.Row():
|
| 705 |
-
|
| 706 |
-
start_grading_btn = gr.Button("๐ Run Pipeline", variant="primary")
|
| 707 |
|
| 708 |
-
# Status
|
|
|
|
| 709 |
status_table = gr.Dataframe(
|
| 710 |
-
headers=["Set", "QP File", "MS File", "Answer
|
| 711 |
datatype=["number", "str", "str", "str", "str"],
|
| 712 |
-
label="๐ Grading
|
| 713 |
visible=False
|
| 714 |
)
|
| 715 |
|
| 716 |
-
# Results section
|
| 717 |
results_section = gr.Column(visible=False)
|
| 718 |
with results_section:
|
| 719 |
gr.Markdown("## ๐ฅ Download Results")
|
| 720 |
-
|
| 721 |
-
|
| 722 |
-
def update_first_triplet_qp(file_obj, current_triplets):
|
| 723 |
-
"""Update QP file for first triplet."""
|
| 724 |
-
current_triplets[0]["qp_file"] = file_obj.name if file_obj else None
|
| 725 |
-
return current_triplets
|
| 726 |
-
|
| 727 |
-
def update_first_triplet_ms(file_obj, current_triplets):
|
| 728 |
-
"""Update MS file for first triplet."""
|
| 729 |
-
current_triplets[0]["ms_file"] = file_obj.name if file_obj else None
|
| 730 |
-
return current_triplets
|
| 731 |
-
|
| 732 |
-
def update_first_triplet_ans(file_obj, current_triplets):
|
| 733 |
-
"""Update AS file for first triplet."""
|
| 734 |
-
current_triplets[0]["ans_file"] = file_obj.name if file_obj else None
|
| 735 |
-
return current_triplets
|
| 736 |
|
| 737 |
-
def
|
| 738 |
-
"""
|
| 739 |
-
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
|
| 743 |
-
|
| 744 |
-
|
| 745 |
-
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 758 |
|
| 759 |
-
def
|
| 760 |
-
"""Update the status
|
| 761 |
-
if
|
| 762 |
-
return gr.update(visible=False, value=[])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 763 |
|
|
|
|
|
|
|
| 764 |
table_data = []
|
| 765 |
-
|
| 766 |
-
|
| 767 |
-
|
| 768 |
-
|
|
|
|
| 769 |
|
| 770 |
# Determine status
|
| 771 |
-
if
|
| 772 |
-
status = "
|
|
|
|
| 773 |
else:
|
| 774 |
-
status = "โณ
|
| 775 |
|
| 776 |
table_data.append([i + 1, qp_name, ms_name, ans_name, status])
|
| 777 |
|
| 778 |
-
|
| 779 |
-
|
| 780 |
-
def start_mass_grading(triplets):
|
| 781 |
-
"""Start processing all triplets."""
|
| 782 |
-
if not triplets:
|
| 783 |
-
return gr.update(), gr.update(visible=False)
|
| 784 |
|
| 785 |
-
|
| 786 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 787 |
|
| 788 |
-
if not
|
| 789 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
| 790 |
|
| 791 |
-
#
|
| 792 |
-
results = []
|
| 793 |
table_data = []
|
| 794 |
-
|
| 795 |
-
|
| 796 |
-
# Update status to processing
|
| 797 |
-
table_row = [
|
| 798 |
i + 1,
|
| 799 |
os.path.basename(triplet["qp_file"]),
|
| 800 |
os.path.basename(triplet["ms_file"]),
|
| 801 |
os.path.basename(triplet["ans_file"]),
|
| 802 |
"๐ Processing..."
|
| 803 |
-
]
|
| 804 |
-
table_data.append(table_row)
|
| 805 |
-
|
| 806 |
-
# Process the triplet
|
| 807 |
-
result = process_single_triplet(triplet, i)
|
| 808 |
-
results.append(result)
|
| 809 |
-
|
| 810 |
-
# Update status
|
| 811 |
-
table_data[i][4] = result["status"]
|
| 812 |
|
| 813 |
-
|
| 814 |
-
|
| 815 |
-
|
| 816 |
-
qp_file_1.change(
|
| 817 |
-
fn=update_first_triplet_qp,
|
| 818 |
-
inputs=[qp_file_1, triplets_state],
|
| 819 |
-
outputs=[triplets_state]
|
| 820 |
-
).then(
|
| 821 |
-
fn=update_status_table,
|
| 822 |
-
inputs=[triplets_state],
|
| 823 |
-
outputs=[status_table]
|
| 824 |
-
)
|
| 825 |
-
|
| 826 |
-
ms_file_1.change(
|
| 827 |
-
fn=update_first_triplet_ms,
|
| 828 |
-
inputs=[ms_file_1, triplets_state],
|
| 829 |
-
outputs=[triplets_state]
|
| 830 |
-
).then(
|
| 831 |
-
fn=update_status_table,
|
| 832 |
-
inputs=[triplets_state],
|
| 833 |
-
outputs=[status_table]
|
| 834 |
-
)
|
| 835 |
-
|
| 836 |
-
ans_file_1.change(
|
| 837 |
-
fn=update_first_triplet_ans,
|
| 838 |
-
inputs=[ans_file_1, triplets_state],
|
| 839 |
-
outputs=[triplets_state]
|
| 840 |
-
).then(
|
| 841 |
-
fn=update_status_table,
|
| 842 |
-
inputs=[triplets_state],
|
| 843 |
-
outputs=[status_table]
|
| 844 |
-
)
|
| 845 |
-
|
| 846 |
-
imprint_check_1.change(
|
| 847 |
-
fn=update_first_triplet_imprint,
|
| 848 |
-
inputs=[imprint_check_1, triplets_state],
|
| 849 |
-
outputs=[triplets_state]
|
| 850 |
-
)
|
| 851 |
-
|
| 852 |
-
# Dynamic UI management for additional triplets
|
| 853 |
-
@gr.render(inputs=[triplets_state])
|
| 854 |
-
def render_additional_triplets(triplets):
|
| 855 |
-
if len(triplets) <= 1:
|
| 856 |
-
return
|
| 857 |
|
| 858 |
-
for i in
|
| 859 |
-
|
| 860 |
-
qp_file = gr.File(label=f"๐ QP Set {i + 1}", file_types=[".pdf"])
|
| 861 |
-
ms_file = gr.File(label=f"๐ MS Set {i + 1}", file_types=[".pdf"])
|
| 862 |
-
ans_file = gr.File(label=f"๐ AS Set {i + 1}", file_types=[".pdf"])
|
| 863 |
-
|
| 864 |
-
with gr.Row():
|
| 865 |
-
imprint_check = gr.Checkbox(label=f"โ Imprint Set {i + 1}", value=False)
|
| 866 |
-
remove_btn = gr.Button("๐๏ธ Remove", variant="stop", size="sm")
|
| 867 |
-
|
| 868 |
-
# File update handlers
|
| 869 |
-
def create_file_handler(idx, file_type):
|
| 870 |
-
def handler(file_obj, current_triplets):
|
| 871 |
-
if idx < len(current_triplets):
|
| 872 |
-
current_triplets[idx][file_type] = file_obj.name if file_obj else None
|
| 873 |
-
return current_triplets
|
| 874 |
-
return handler
|
| 875 |
-
|
| 876 |
-
def create_imprint_handler(idx):
|
| 877 |
-
def handler(imprint_val, current_triplets):
|
| 878 |
-
if idx < len(current_triplets):
|
| 879 |
-
current_triplets[idx]["imprint"] = imprint_val
|
| 880 |
-
return current_triplets
|
| 881 |
-
return handler
|
| 882 |
-
|
| 883 |
-
def create_remove_handler(idx):
|
| 884 |
-
def handler(current_triplets):
|
| 885 |
-
return remove_triplet(idx, current_triplets)
|
| 886 |
-
return handler
|
| 887 |
-
|
| 888 |
-
# Set up event handlers
|
| 889 |
-
qp_file.change(
|
| 890 |
-
fn=create_file_handler(i, "qp_file"),
|
| 891 |
-
inputs=[qp_file, triplets_state],
|
| 892 |
-
outputs=[triplets_state]
|
| 893 |
-
).then(
|
| 894 |
-
fn=update_status_table,
|
| 895 |
-
inputs=[triplets_state],
|
| 896 |
-
outputs=[status_table]
|
| 897 |
-
)
|
| 898 |
-
|
| 899 |
-
ms_file.change(
|
| 900 |
-
fn=create_file_handler(i, "ms_file"),
|
| 901 |
-
inputs=[ms_file, triplets_state],
|
| 902 |
-
outputs=[triplets_state]
|
| 903 |
-
).then(
|
| 904 |
-
fn=update_status_table,
|
| 905 |
-
inputs=[triplets_state],
|
| 906 |
-
outputs=[status_table]
|
| 907 |
-
)
|
| 908 |
-
|
| 909 |
-
ans_file.change(
|
| 910 |
-
fn=create_file_handler(i, "ans_file"),
|
| 911 |
-
inputs=[ans_file, triplets_state],
|
| 912 |
-
outputs=[triplets_state]
|
| 913 |
-
).then(
|
| 914 |
-
fn=update_status_table,
|
| 915 |
-
inputs=[triplets_state],
|
| 916 |
-
outputs=[status_table]
|
| 917 |
-
)
|
| 918 |
-
|
| 919 |
-
imprint_check.change(
|
| 920 |
-
fn=create_imprint_handler(i),
|
| 921 |
-
inputs=[imprint_check, triplets_state],
|
| 922 |
-
outputs=[triplets_state]
|
| 923 |
-
)
|
| 924 |
|
| 925 |
-
|
| 926 |
-
|
| 927 |
-
|
| 928 |
-
|
| 929 |
-
|
| 930 |
-
|
| 931 |
-
|
| 932 |
-
|
| 933 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 934 |
|
| 935 |
-
#
|
| 936 |
-
|
| 937 |
-
|
| 938 |
-
|
| 939 |
-
|
| 940 |
-
|
| 941 |
-
|
| 942 |
-
inputs=[triplets_state],
|
| 943 |
-
outputs=[status_table]
|
| 944 |
-
)
|
| 945 |
|
| 946 |
-
# Start grading button handler
|
| 947 |
start_grading_btn.click(
|
| 948 |
-
fn=
|
| 949 |
-
inputs=[
|
| 950 |
-
outputs=[status_table, results_section]
|
| 951 |
)
|
| 952 |
|
| 953 |
if __name__ == "__main__":
|
|
|
|
| 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 |
with gr.Row():
|
| 684 |
+
with gr.Column():
|
| 685 |
+
qp_files = gr.File(
|
| 686 |
+
label="๐ Upload Question Papers (PDF)",
|
| 687 |
+
file_count="multiple",
|
| 688 |
+
file_types=[".pdf"]
|
| 689 |
+
)
|
| 690 |
+
with gr.Column():
|
| 691 |
+
ms_files = gr.File(
|
| 692 |
+
label="๐ Upload Markschemes (PDF)",
|
| 693 |
+
file_count="multiple",
|
| 694 |
+
file_types=[".pdf"]
|
| 695 |
+
)
|
| 696 |
+
with gr.Column():
|
| 697 |
+
ans_files = gr.File(
|
| 698 |
+
label="๐ Upload Answer Sheets (PDF)",
|
| 699 |
+
file_count="multiple",
|
| 700 |
+
file_types=[".pdf"]
|
| 701 |
+
)
|
| 702 |
|
| 703 |
+
imprint_all = gr.Checkbox(label="โ Imprint Marks on All Answer Sheets", value=False)
|
|
|
|
| 704 |
|
|
|
|
| 705 |
with gr.Row():
|
| 706 |
+
start_grading_btn = gr.Button("๐ Start Mass Grading", variant="primary", size="lg")
|
|
|
|
| 707 |
|
| 708 |
+
# Status and results
|
| 709 |
+
status_info = gr.Markdown("", visible=False)
|
| 710 |
status_table = gr.Dataframe(
|
| 711 |
+
headers=["Set", "QP File", "MS File", "Answer Sheet", "Status"],
|
| 712 |
datatype=["number", "str", "str", "str", "str"],
|
| 713 |
+
label="๐ Grading Progress",
|
| 714 |
visible=False
|
| 715 |
)
|
| 716 |
|
|
|
|
| 717 |
results_section = gr.Column(visible=False)
|
| 718 |
with results_section:
|
| 719 |
gr.Markdown("## ๐ฅ Download Results")
|
| 720 |
+
results_container = gr.Column()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 721 |
|
| 722 |
+
def create_triplets_from_files(qp_list, ms_list, ans_list, imprint_setting):
|
| 723 |
+
"""Create triplets from uploaded file lists."""
|
| 724 |
+
if not qp_list or not ms_list or not ans_list:
|
| 725 |
+
return []
|
| 726 |
+
|
| 727 |
+
# Convert single files to lists
|
| 728 |
+
if not isinstance(qp_list, list):
|
| 729 |
+
qp_list = [qp_list] if qp_list else []
|
| 730 |
+
if not isinstance(ms_list, list):
|
| 731 |
+
ms_list = [ms_list] if ms_list else []
|
| 732 |
+
if not isinstance(ans_list, list):
|
| 733 |
+
ans_list = [ans_list] if ans_list else []
|
| 734 |
+
|
| 735 |
+
# Create triplets by matching files by index
|
| 736 |
+
triplets = []
|
| 737 |
+
max_files = max(len(qp_list), len(ms_list), len(ans_list))
|
| 738 |
+
|
| 739 |
+
for i in range(max_files):
|
| 740 |
+
qp_file = qp_list[i].name if i < len(qp_list) else None
|
| 741 |
+
ms_file = ms_list[i].name if i < len(ms_list) else None
|
| 742 |
+
ans_file = ans_list[i].name if i < len(ans_list) else None
|
| 743 |
+
|
| 744 |
+
# Only create triplet if all three files are present
|
| 745 |
+
if qp_file and ms_file and ans_file:
|
| 746 |
+
triplets.append({
|
| 747 |
+
"qp_file": qp_file,
|
| 748 |
+
"ms_file": ms_file,
|
| 749 |
+
"ans_file": ans_file,
|
| 750 |
+
"imprint": imprint_setting
|
| 751 |
+
})
|
| 752 |
+
|
| 753 |
+
return triplets
|
| 754 |
|
| 755 |
+
def update_status_display(qp_list, ms_list, ans_list):
|
| 756 |
+
"""Update the status display showing file matching."""
|
| 757 |
+
if not qp_list and not ms_list and not ans_list:
|
| 758 |
+
return gr.update(visible=False), gr.update(visible=False, value=[])
|
| 759 |
+
|
| 760 |
+
# Convert single files to lists
|
| 761 |
+
if not isinstance(qp_list, list):
|
| 762 |
+
qp_list = [qp_list] if qp_list else []
|
| 763 |
+
if not isinstance(ms_list, list):
|
| 764 |
+
ms_list = [ms_list] if ms_list else []
|
| 765 |
+
if not isinstance(ans_list, list):
|
| 766 |
+
ans_list = [ans_list] if ans_list else []
|
| 767 |
+
|
| 768 |
+
max_files = max(len(qp_list), len(ms_list), len(ans_list))
|
| 769 |
+
|
| 770 |
+
if max_files == 0:
|
| 771 |
+
return gr.update(visible=False), gr.update(visible=False, value=[])
|
| 772 |
|
| 773 |
+
# Create status info
|
| 774 |
+
complete_sets = 0
|
| 775 |
table_data = []
|
| 776 |
+
|
| 777 |
+
for i in range(max_files):
|
| 778 |
+
qp_name = os.path.basename(qp_list[i].name) if i < len(qp_list) else "โ Missing"
|
| 779 |
+
ms_name = os.path.basename(ms_list[i].name) if i < len(ms_list) else "โ Missing"
|
| 780 |
+
ans_name = os.path.basename(ans_list[i].name) if i < len(ans_list) else "โ Missing"
|
| 781 |
|
| 782 |
# Determine status
|
| 783 |
+
if i < len(qp_list) and i < len(ms_list) and i < len(ans_list):
|
| 784 |
+
status = "โ
Ready"
|
| 785 |
+
complete_sets += 1
|
| 786 |
else:
|
| 787 |
+
status = "โณ Incomplete"
|
| 788 |
|
| 789 |
table_data.append([i + 1, qp_name, ms_name, ans_name, status])
|
| 790 |
|
| 791 |
+
status_text = f"**๐ File Summary:** {complete_sets} complete set(s) ready for grading out of {max_files} total."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 792 |
|
| 793 |
+
return (
|
| 794 |
+
gr.update(visible=True, value=status_text),
|
| 795 |
+
gr.update(visible=True, value=table_data)
|
| 796 |
+
)
|
| 797 |
+
|
| 798 |
+
def process_mass_grading(qp_list, ms_list, ans_list, imprint_setting):
|
| 799 |
+
"""Process all complete triplets."""
|
| 800 |
+
triplets = create_triplets_from_files(qp_list, ms_list, ans_list, imprint_setting)
|
| 801 |
|
| 802 |
+
if not triplets:
|
| 803 |
+
return (
|
| 804 |
+
gr.update(value="โ No complete sets found. Please ensure each set has QP, MS, and AS files."),
|
| 805 |
+
gr.update(visible=False),
|
| 806 |
+
gr.update(visible=False)
|
| 807 |
+
)
|
| 808 |
|
| 809 |
+
# Update status to show processing
|
|
|
|
| 810 |
table_data = []
|
| 811 |
+
for i, triplet in enumerate(triplets):
|
| 812 |
+
table_data.append([
|
|
|
|
|
|
|
| 813 |
i + 1,
|
| 814 |
os.path.basename(triplet["qp_file"]),
|
| 815 |
os.path.basename(triplet["ms_file"]),
|
| 816 |
os.path.basename(triplet["ans_file"]),
|
| 817 |
"๐ Processing..."
|
| 818 |
+
])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 819 |
|
| 820 |
+
# Process each triplet
|
| 821 |
+
results = []
|
| 822 |
+
result_components = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 823 |
|
| 824 |
+
for i, triplet in enumerate(triplets):
|
| 825 |
+
print(f"\n๐ Processing Set {i + 1}/{len(triplets)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 826 |
|
| 827 |
+
try:
|
| 828 |
+
result = process_single_triplet(triplet, i)
|
| 829 |
+
results.append(result)
|
| 830 |
+
|
| 831 |
+
# Update table status
|
| 832 |
+
table_data[i][4] = result["status"]
|
| 833 |
+
|
| 834 |
+
# Create download components
|
| 835 |
+
if result["grading_pdf"]:
|
| 836 |
+
result_components.append(
|
| 837 |
+
gr.File(value=result["grading_pdf"], label=f"๐ Set {i+1} - Grading PDF", visible=True)
|
| 838 |
+
)
|
| 839 |
+
if result["imprinted_pdf"]:
|
| 840 |
+
result_components.append(
|
| 841 |
+
gr.File(value=result["imprinted_pdf"], label=f"โ๏ธ Set {i+1} - Imprinted PDF", visible=True)
|
| 842 |
+
)
|
| 843 |
+
|
| 844 |
+
except Exception as e:
|
| 845 |
+
print(f"โ Error processing set {i + 1}: {e}")
|
| 846 |
+
table_data[i][4] = f"โ Error: {str(e)[:30]}..."
|
| 847 |
+
|
| 848 |
+
# Final status
|
| 849 |
+
successful = len([r for r in results if r.get("status") == "โ
Complete"])
|
| 850 |
+
status_text = f"**๐ Processing Complete:** {successful}/{len(triplets)} sets processed successfully."
|
| 851 |
+
|
| 852 |
+
return (
|
| 853 |
+
gr.update(value=status_text),
|
| 854 |
+
gr.update(value=table_data),
|
| 855 |
+
gr.update(visible=True)
|
| 856 |
+
)
|
| 857 |
|
| 858 |
+
# Event handlers
|
| 859 |
+
for file_input in [qp_files, ms_files, ans_files]:
|
| 860 |
+
file_input.change(
|
| 861 |
+
fn=update_status_display,
|
| 862 |
+
inputs=[qp_files, ms_files, ans_files],
|
| 863 |
+
outputs=[status_info, status_table]
|
| 864 |
+
)
|
|
|
|
|
|
|
|
|
|
| 865 |
|
|
|
|
| 866 |
start_grading_btn.click(
|
| 867 |
+
fn=process_mass_grading,
|
| 868 |
+
inputs=[qp_files, ms_files, ans_files, imprint_all],
|
| 869 |
+
outputs=[status_info, status_table, results_section]
|
| 870 |
)
|
| 871 |
|
| 872 |
if __name__ == "__main__":
|