atz21 commited on
Commit
702b8bc
·
verified ·
1 Parent(s): 733048e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -89
app.py CHANGED
@@ -3,35 +3,43 @@ import gradio as gr
3
  import google.generativeai as genai
4
  from markdown_pdf import MarkdownPdf, Section
5
 
6
- # -------------------- CONFIG --------------------
7
- genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
8
-
9
- # ---------- PROMPTS (JSON style) ----------
10
-
11
  PROMPTS = {
12
- "TRANSCRIPTION_PROMPT": {
13
- "role": "system",
14
- "content": """Your Role: You are an expert technical transcriber specializing in mathematical and scientific documents.
15
- Your mission is to convert handwritten solutions from a provided image or PDF into a clean, accurate, and logically structured Markdown format.
16
- Instructions:
17
- - Use ## for questions, ### for subquestions.
18
- - Transcribe only the corrected, final version of the solution (ignore scribbles, cancellations, mistakes).
19
- - Keep mathematical expressions in fenced code blocks.
20
- - If something is illegible, use [illegible].
21
- - Do not recreate graphs, only describe them.
22
- """
23
- },
24
- "MARKSCHEME_TRANSCRIPTION_PROMPT": {
25
  "role": "system",
26
- "content": """Your Role: You are an expert transcriber.
27
- Convert the official marking scheme from the provided PDF into clean, structured Markdown.
28
- Instructions:
29
- - Preserve all structure (questions, subquestions).
30
- - Keep M, A, R annotations exactly as written.
31
- - Represent alternative methods clearly (METHOD 1, METHOD 2, etc.).
32
- - Preserve any accuracy requirements.
33
- - Format in Markdown using ## and ### for hierarchy.
34
- - Use code blocks for equations.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  """
36
  },
37
  "GRADING_PROMPT": {
@@ -60,7 +68,7 @@ Implied marks (M1) can only be awarded if correct work is seen or implied.
60
  - Penalize once if the candidate misreads a value.
61
  - Award other marks as appropriate.
62
  ## 6. Alternative methods
63
- - Accept valid alternatives unless \"Hence\" forbids it.
64
  ## 7. Alternative forms
65
  - Accept equivalent numeric/algebraic forms unless specified otherwise.
66
  ## 8. Format and accuracy of answers
@@ -75,7 +83,7 @@ Produce a GitHub-flavored Markdown table with 3 columns:
75
  | Student wrote | Marks Awarded | Reason |
76
  |---------------|---------------|--------|
77
  Special Formatting Rule:
78
- - Whenever a mark is lost (M0, A0, R0 etc.), wrap it in red using: `<span style=\"color:red\">M0</span>`.
79
  - Also wrap the corresponding Reason in red color.
80
  - Keep awarded marks (M1, A1, etc.) in plain text.
81
  - If mixed (e.g., M1A0A1), only highlight the lost marks (`A0`) and its reason.
@@ -88,6 +96,9 @@ After the table, provide:
88
  }
89
  }
90
 
 
 
 
91
  # ---------- HELPER: Save to PDF ----------
92
  def save_as_pdf(text, filename="output.pdf"):
93
  pdf = MarkdownPdf()
@@ -95,103 +106,90 @@ def save_as_pdf(text, filename="output.pdf"):
95
  pdf.save(filename)
96
  return filename
97
 
98
- # ---------- STEP 1: TRANSCRIBE STUDENT ----------
99
- def transcribe_student(ans_file):
100
  try:
101
- ans_uploaded = genai.upload_file(path=ans_file, display_name="Answer Sheet")
102
- model = genai.GenerativeModel("gemini-2.5-flash", generation_config={"temperature": 0})
103
-
104
- resp = model.generate_content([PROMPTS["TRANSCRIPTION_PROMPT"]["content"], ans_uploaded])
105
- transcription = getattr(resp, "text", None)
106
- if not transcription and resp.candidates:
107
- transcription = resp.candidates[0].content.parts[0].text
108
-
109
- pdf_path = save_as_pdf(transcription, "student_transcription.pdf")
110
- return transcription, pdf_path
111
- except Exception as e:
112
- return f"❌ Error during transcription: {e}", None
113
 
114
- # ---------- STEP 2: TRANSCRIBE MARKSCHEME ----------
115
- def transcribe_ms(ms_file):
116
  try:
 
117
  ms_uploaded = genai.upload_file(path=ms_file, display_name="Markscheme")
118
- model = genai.GenerativeModel("gemini-2.5-flash", generation_config={"temperature": 0})
119
 
120
- resp = model.generate_content([PROMPTS["MARKSCHEME_TRANSCRIPTION_PROMPT"]["content"], ms_uploaded])
121
- ms_transcription = getattr(resp, "text", None)
122
- if not ms_transcription and resp.candidates:
123
- ms_transcription = resp.candidates[0].content.parts[0].text
 
 
 
124
 
125
- pdf_path = save_as_pdf(ms_transcription, "ms_transcription.pdf")
126
- return ms_transcription, pdf_path
 
 
 
 
127
  except Exception as e:
128
- return f"❌ Error during MS transcription: {e}", None
129
 
130
- # ---------- STEP 3: GRADING ----------
131
- def grade(qp_file, ms_transcription, student_transcription):
132
  try:
133
- qp_uploaded = genai.upload_file(path=qp_file, display_name="Question Paper")
134
- model = genai.GenerativeModel("gemini-2.5-flash", generation_config={"temperature": 0})
135
-
136
  response = model.generate_content([
137
  PROMPTS["GRADING_PROMPT"]["content"],
138
- qp_uploaded,
139
- "### Markscheme Transcription:\n" + ms_transcription,
140
- "### Student Transcription:\n" + student_transcription
141
  ])
142
 
143
  grading = getattr(response, "text", None)
144
  if not grading and response.candidates:
145
  grading = response.candidates[0].content.parts[0].text
146
 
147
- pdf_path = save_as_pdf(grading, "grading.pdf")
 
 
 
 
148
  return grading, pdf_path
149
  except Exception as e:
150
  return f"❌ Error during grading: {e}", None
151
 
152
  # ---------- GRADIO APP ----------
153
- with gr.Blocks(title="LeadIB AI Grading (3-Step)") as demo:
154
- gr.Markdown("## LeadIB AI Grading (3-Step)\nUpload exam documents to transcribe and grade step by step.")
155
 
156
  with gr.Row():
157
  qp_file = gr.File(label="Upload Question Paper (PDF)", type="filepath")
158
- ms_file = gr.File(label="Upload Mark Scheme (PDF)", type="filepath")
159
  ans_file = gr.File(label="Upload Student Answer Sheet (PDF)", type="filepath")
160
 
161
- # Step 1: Transcribe Student
162
- transcribe_student_btn = gr.Button("Step 1: Transcribe Student Answer Sheet")
163
- with gr.Row():
164
- student_out = gr.Textbox(label="📄 Student Transcription", lines=20)
165
- student_pdf = gr.File(label="⬇️ Download Student Transcription (PDF)")
166
-
167
- # Step 2: Transcribe Markscheme
168
- transcribe_ms_btn = gr.Button("Step 2: Transcribe Markscheme")
169
  with gr.Row():
170
- ms_out = gr.Textbox(label="📄 Markscheme Transcription", lines=20)
171
- ms_pdf = gr.File(label="⬇️ Download Markscheme Transcription (PDF)")
172
 
173
- # Step 3: Grading
174
- grade_btn = gr.Button("Step 3: Grade the Student")
175
  with gr.Row():
176
- grading_out = gr.Textbox(label="✅ Grading Report (Step-by-Step)", lines=20)
177
  grading_pdf = gr.File(label="⬇️ Download Grading Report (PDF)")
178
 
179
  # Button Logic
180
- transcribe_student_btn.click(
181
- fn=transcribe_student,
182
- inputs=[ans_file],
183
- outputs=[student_out, student_pdf],
184
- show_progress=True
185
- )
186
- transcribe_ms_btn.click(
187
- fn=transcribe_ms,
188
- inputs=[ms_file],
189
- outputs=[ms_out, ms_pdf],
190
  show_progress=True
191
  )
192
  grade_btn.click(
193
  fn=grade,
194
- inputs=[qp_file, ms_out, student_out],
195
  outputs=[grading_out, grading_pdf],
196
  show_progress=True
197
  )
 
3
  import google.generativeai as genai
4
  from markdown_pdf import MarkdownPdf, Section
5
 
6
+ # ---------- PROMPTS ----------
 
 
 
 
7
  PROMPTS = {
8
+ "ALIGNMENT_PROMPT": {
 
 
 
 
 
 
 
 
 
 
 
 
9
  "role": "system",
10
+ "content": """Your Role: You are an expert examiner and transcription specialist.
11
+ Your task is to **align three sources**:
12
+ - Question Paper (QP)
13
+ - Markscheme (MS)
14
+ - Student Answer Sheet (AS)
15
+
16
+ ### Instructions
17
+ 1. Parse all documents carefully and align them **per question and sub-question**.
18
+ 2. For each question/sub-question, produce a structured block:
19
+
20
+ ---
21
+ ## Question X (and sub-question if applicable)
22
+ **QP:** [Insert the exact question text]
23
+ **MS:** [Insert the relevant part of the markscheme]
24
+ **AS:** [Insert the student's final cleaned answer transcription]
25
+ ---
26
+
27
+ 3. Formatting Rules:
28
+ - Use `##` for main questions and `###` for sub-questions.
29
+ - Write **QP | MS | AS** exactly in that order.
30
+ - Preserve all mathematical expressions inside fenced code blocks.
31
+ - Do not re-create diagrams/graphs. Write `[Graph omitted]`.
32
+ - If part of the student's answer is unreadable, write `[illegible]`.
33
+ - If a student skipped a question, write `[No response]`.
34
+ - Keep MS annotations (M1, A1, R1, etc.) exactly as in the original.
35
+
36
+ 4. Output must be **clean, deterministic, and consistent** — so that another model can grade directly using this aligned representation.
37
+
38
+ ### Example
39
+ ## Question 1
40
+ **QP:** Expand `(1+x)^3`
41
+ **MS:** M1 for binomial expansion, A1 for coefficients, A1 for final form
42
+ **AS:**
43
  """
44
  },
45
  "GRADING_PROMPT": {
 
68
  - Penalize once if the candidate misreads a value.
69
  - Award other marks as appropriate.
70
  ## 6. Alternative methods
71
+ - Accept valid alternatives unless "Hence" forbids it.
72
  ## 7. Alternative forms
73
  - Accept equivalent numeric/algebraic forms unless specified otherwise.
74
  ## 8. Format and accuracy of answers
 
83
  | Student wrote | Marks Awarded | Reason |
84
  |---------------|---------------|--------|
85
  Special Formatting Rule:
86
+ - Whenever a mark is lost (M0, A0, R0 etc.), wrap it in red using: `<span style="color:red">M0</span>`.
87
  - Also wrap the corresponding Reason in red color.
88
  - Keep awarded marks (M1, A1, etc.) in plain text.
89
  - If mixed (e.g., M1A0A1), only highlight the lost marks (`A0`) and its reason.
 
96
  }
97
  }
98
 
99
+ # -------------------- CONFIG --------------------
100
+ genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
101
+
102
  # ---------- HELPER: Save to PDF ----------
103
  def save_as_pdf(text, filename="output.pdf"):
104
  pdf = MarkdownPdf()
 
106
  pdf.save(filename)
107
  return filename
108
 
109
+ # ---------- HELPER: Create Model with Fallback ----------
110
+ def create_model():
111
  try:
112
+ return genai.GenerativeModel("gemini-2.5-pro", generation_config={"temperature": 0})
113
+ except Exception:
114
+ return genai.GenerativeModel("gemini-2.5-flash", generation_config={"temperature": 0})
 
 
 
 
 
 
 
 
 
115
 
116
+ # ---------- STEP 1: ALIGNMENT ----------
117
+ def align_documents(qp_file, ms_file, ans_file):
118
  try:
119
+ qp_uploaded = genai.upload_file(path=qp_file, display_name="Question Paper")
120
  ms_uploaded = genai.upload_file(path=ms_file, display_name="Markscheme")
121
+ ans_uploaded = genai.upload_file(path=ans_file, display_name="Answer Sheet")
122
 
123
+ model = create_model()
124
+ resp = model.generate_content([
125
+ PROMPTS["ALIGNMENT_PROMPT"]["content"],
126
+ qp_uploaded,
127
+ ms_uploaded,
128
+ ans_uploaded
129
+ ])
130
 
131
+ aligned_text = getattr(resp, "text", None)
132
+ if not aligned_text and resp.candidates:
133
+ aligned_text = resp.candidates[0].content.parts[0].text
134
+
135
+ pdf_path = save_as_pdf(aligned_text, "aligned_qp_ms_as.pdf")
136
+ return aligned_text, pdf_path
137
  except Exception as e:
138
+ return f"❌ Error during alignment: {e}", None
139
 
140
+ # ---------- STEP 2: GRADING ----------
141
+ def grade(aligned_text, ans_file):
142
  try:
143
+ model = create_model()
 
 
144
  response = model.generate_content([
145
  PROMPTS["GRADING_PROMPT"]["content"],
146
+ aligned_text
 
 
147
  ])
148
 
149
  grading = getattr(response, "text", None)
150
  if not grading and response.candidates:
151
  grading = response.candidates[0].content.parts[0].text
152
 
153
+ # Use answer sheet filename for graded PDF
154
+ base_name = os.path.splitext(os.path.basename(ans_file))[0]
155
+ pdf_name = f"{base_name}_graded.pdf"
156
+
157
+ pdf_path = save_as_pdf(grading, pdf_name)
158
  return grading, pdf_path
159
  except Exception as e:
160
  return f"❌ Error during grading: {e}", None
161
 
162
  # ---------- GRADIO APP ----------
163
+ with gr.Blocks(title="LeadIB AI Grading (Alignment + Grading)") as demo:
164
+ gr.Markdown("## LeadIB AI Grading\nUpload Question Paper, Markscheme, and Student Answer Sheet to align and grade.")
165
 
166
  with gr.Row():
167
  qp_file = gr.File(label="Upload Question Paper (PDF)", type="filepath")
168
+ ms_file = gr.File(label="Upload Markscheme (PDF)", type="filepath")
169
  ans_file = gr.File(label="Upload Student Answer Sheet (PDF)", type="filepath")
170
 
171
+ # Step 1: Alignment
172
+ align_btn = gr.Button("Step 1: Align QP | MS | AS")
 
 
 
 
 
 
173
  with gr.Row():
174
+ aligned_out = gr.Textbox(label="📄 Aligned QP | MS | AS", lines=20)
175
+ aligned_pdf = gr.File(label="⬇️ Download Aligned (PDF)")
176
 
177
+ # Step 2: Grading
178
+ grade_btn = gr.Button("Step 2: Grade the Student")
179
  with gr.Row():
180
+ grading_out = gr.Textbox(label="✅ Grading Report", lines=20)
181
  grading_pdf = gr.File(label="⬇️ Download Grading Report (PDF)")
182
 
183
  # Button Logic
184
+ align_btn.click(
185
+ fn=align_documents,
186
+ inputs=[qp_file, ms_file, ans_file],
187
+ outputs=[aligned_out, aligned_pdf],
 
 
 
 
 
 
188
  show_progress=True
189
  )
190
  grade_btn.click(
191
  fn=grade,
192
+ inputs=[aligned_out, ans_file],
193
  outputs=[grading_out, grading_pdf],
194
  show_progress=True
195
  )