import gradio as gr import os import io from google import generativeai as genai def process_exam_papers(question_paper, marking_scheme, answer_sheet, api_key, progress=gr.Progress()): """ Process uploaded exam papers and return transcription and grading """ if not api_key: return "Please provide a valid Gemini API key.", "", "Ready" if not all([question_paper, marking_scheme, answer_sheet]): return "Please upload all three files.", "", "Ready" try: # Configure Gemini API genai.configure(api_key=api_key) progress(0.1, desc="Uploading files to Gemini...") # Upload files to Gemini qp_file = genai.upload_file(path=question_paper.name, display_name="Question Paper") ms_file = genai.upload_file(path=marking_scheme.name, display_name="Marking Scheme") ans_file = genai.upload_file(path=answer_sheet.name, display_name="Answer Sheet") progress(0.3, desc="Files uploaded. Starting transcription...") # Transcription instructions transcription_instructions = """ Persona: You are an expert transcriptionist specializing in scientific and mathematical documents. Your primary goal is to convert handwritten mathematical work into a perfectly formatted, machine-readable Markdown document using LaTeX for all mathematical notation. Core Task: Your task is to transcribe the provided handwritten student solutions into a single, clean Markdown string. Key Directives & Rules: Absolute Fidelity: Transcribe exactly what is written. Do NOT correct mathematical errors, logical fallacies, or spelling mistakes. Your role is purely that of a scribe, not a grader or editor. LaTeX for All Math: All mathematical content—including single variables, numbers in equations, fractions, exponents, roots, and symbols—must be enclosed in LaTeX delimiters. Use inline $ ... $ for math within text and block $$ ... $$ for standalone equations. Handle Strikethroughs: Completely ignore and omit any text, numbers, or expressions that have been struck through by the student. Do not include them in the final output. Preserve Structure: Use Markdown bolding (e.g., **1.**, **2a.**) to clearly separate each question or sub-part. Maintain the vertical, step-by-step flow of the student's derivations. For multi-line aligned equations, use the \\begin{align*} ... \\end{align*} environment within a $$ ... $$ block. Handle Ambiguity: If a character or symbol is genuinely illegible or ambiguous, make your best interpretation and enclose it in square brackets. For example, if a variable could be u or v, write [u?]. Output Format: The final output must be a single Markdown string. Ensure all LaTeX renders correctly and the structure is clean and readable. Comprehensive Example: If the student's handwritten work for a question looks like this: 7. Find the value of y. y = (x² + 3) / 2 for x = 3 y = (3² + 3) / 2 y = (6+3) / 2 y = (9 + 3) / 2 y = 12 / 2 y = 6 Your expected output should be: **7.** Find the value of y. $$ y = \\frac{x^2 + 3}{2} $$ for $x = 3$ $$ \\begin{align*} y &= \\frac{3^2 + 3}{2} \\\\ y &= \\frac{9 + 3}{2} \\\\ y &= \\frac{12}{2} \\\\ y &= 6 \\end{align*} $$ """ # Initialize Gemini model for transcription model = genai.GenerativeModel( "gemini-2.5-pro", generation_config={"temperature": 0} ) progress(0.4, desc="Transcribing handwritten answers...") # Generate transcription response = model.generate_content([ transcription_instructions, ans_file ]) # Extract transcription safely student_transcription = getattr(response, "text", None) if not student_transcription: student_transcription = response.candidates[0].content.parts[0].text progress(0.7, desc="Transcription complete. Starting grading process...") # Return transcription first, then continue with grading yield student_transcription, "⏳ Grading in progress...", "Grading" # Grading system instructions grading_system = """ Instructions to Examiners: Abbreviations: - M: Marks for correct Method. - A: Marks for Answer or Accuracy (often depends on preceding M mark). - R: Marks for clear Reasoning. - AG: Answer given in the question; no marks awarded. - FT: Follow Through; award marks for correct method/answer using incorrect earlier results. Marking Rules: 1. Always follow the markscheme annotations (M1, A2, etc.). 2. M marks must be earned before dependent A marks are awarded (no M0 followed by A1 unless explicitly allowed). 3. If M and A marks are on the same line (e.g., M1A1), M is for the method attempt, A is for correct values. 4. Multiple A marks on the same line are awarded independently unless otherwise noted. 5. Do not split M2, A3, etc. unless instructed. 6. "Show that" responses do not need to restate the AG line unless noted. 7. Once a correct answer is seen, ignore further incorrect working unless it affects a later part (then apply FT as appropriate). 8. Do not award the final A mark if an incorrect approximation is used in the same part. Error Avoidance: - **No incorrect mark allocation:** Do not award marks unless they are explicitly justified by the markscheme. - **No misclassification of errors:** Distinguish correctly between "Conceptual Errors" and "Silly Mistakes." - **Follow markscheme logic exactly:** Especially regarding when to withhold accuracy marks if method marks are not earned. """ # Now start grading using the transcribed text # Generate grading grading_response = model.generate_content([ f"You are an official examiner. Use the following grading system and rules to assess the answers:\n\n{grading_system}\n\n" "Your output must:\n" "1. Apply marks exactly as per the markscheme.\n" "2. Justify each awarded or withheld mark with reference to the grading rules.\n" "3. Identify and classify all errors accurately (Conceptual Error, Silly Mistake, or None).\n" "4. Follow the dependency between M and A marks strictly.\n" "5. Avoid giving marks that the markscheme does not allow.\n" "6. Provide a step-by-step reasoning for each mark awarded or withheld, explaining your thought process clearly.\n", qp_file, ms_file, student_transcription # Use the transcribed text, not the original PDF ]) progress(0.9, desc="Finalizing grading results...") # Extract grading safely grading_text = getattr(grading_response, "text", None) if not grading_text and grading_response.candidates: grading_text = grading_response.candidates[0].content.parts[0].text elif not grading_text: grading_text = "No Response" progress(1.0, desc="Complete!") # Return final results yield student_transcription, grading_text, "Complete" except Exception as e: yield f"Error processing files: {str(e)}", "", "Error" # Create Gradio interface with gr.Blocks(title="Exam Paper Grading System", theme=gr.themes.Soft()) as demo: gr.Markdown(""" # 📚 Automated Exam Paper Grading System Upload your question paper, marking scheme, and answer sheet to get automated transcription and grading using Google's Gemini AI. """) with gr.Row(): with gr.Column(): gr.Markdown("### 📋 Upload Files") api_key = gr.Textbox( label="Gemini API Key", placeholder="Enter your Google Gemini API key", type="password" ) question_paper = gr.File( label="Question Paper (PDF)", file_types=[".pdf"] ) marking_scheme = gr.File( label="Marking Scheme (PDF)", file_types=[".pdf"] ) answer_sheet = gr.File( label="Answer Sheet (PDF)", file_types=[".pdf"] ) process_btn = gr.Button( "🚀 Process Papers", variant="primary", size="lg" ) with gr.Row(): with gr.Column(): gr.Markdown("### 📝 Student Answer Transcription") transcription_output = gr.Textbox( label="Transcribed Answers", lines=15, max_lines=25, show_copy_button=True, placeholder="Transcribed answers will appear here first..." ) with gr.Column(): gr.Markdown("### ✅ Grading Results") grading_output = gr.Textbox( label="Detailed Grading", lines=15, max_lines=25, show_copy_button=True, placeholder="Grading results will appear here after transcription is complete..." ) # Add status indicator with gr.Row(): status_display = gr.Textbox( label="Status", value="Ready", interactive=False, show_label=True ) # Set up the processing function process_btn.click( fn=process_exam_papers, inputs=[question_paper, marking_scheme, answer_sheet, api_key], outputs=[transcription_output, grading_output, status_display] ) gr.Markdown(""" ### 📖 How to Use: 1. **Get a Gemini API Key**: Visit [Google AI Studio](https://makersuite.google.com/app/apikey) to get your free API key 2. **Upload PDFs**: Upload your question paper, marking scheme, and student answer sheet 3. **Process**: Click the "Process Papers" button to get transcription and grading 4. **Review**: Check the transcribed answers and detailed grading results ### ⚠️ Notes: - All uploaded files are processed securely and not stored permanently - The system transcribes exactly what's written (including errors) for accurate grading - LaTeX mathematical notation is automatically formatted for clarity """) if __name__ == "__main__": demo.launch()