Spaces:
Sleeping
Sleeping
File size: 10,416 Bytes
b9e41b9 1c7ddd9 2d32051 b9e41b9 1c7ddd9 e49b802 b9e41b9 e49b802 b9e41b9 e49b802 b9e41b9 e49b802 b9e41b9 2d32051 b9e41b9 2d32051 b9e41b9 2d32051 b9e41b9 2d32051 b9e41b9 2d32051 b9e41b9 2d32051 b9e41b9 2d32051 b9e41b9 2d32051 1c7ddd9 b9e41b9 e49b802 b9e41b9 e49b802 b9e41b9 2d32051 b9e41b9 1c7ddd9 b9e41b9 e49b802 b9e41b9 2d32051 b9e41b9 e49b802 b9e41b9 e49b802 b9e41b9 e49b802 b9e41b9 2d32051 e49b802 b9e41b9 f93c15f b9e41b9 2d32051 b9e41b9 e49b802 b9e41b9 e49b802 b9e41b9 e49b802 b9e41b9 e49b802 b9e41b9 f93c15f 1c7ddd9 b9e41b9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | 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() |