Update app.py
Browse files
app.py
CHANGED
|
@@ -5,13 +5,12 @@ from google.generativeai.types import HarmCategory, HarmBlockThreshold
|
|
| 5 |
from markdown_pdf import MarkdownPdf, Section
|
| 6 |
|
| 7 |
# -------------------- CONFIG --------------------
|
| 8 |
-
|
| 9 |
-
genai.configure(api_key=API_KEY)
|
| 10 |
|
| 11 |
-
# ---------- PROMPTS
|
| 12 |
PROMPTS = {
|
| 13 |
"TRANSCRIPTION_PROMPT": {
|
| 14 |
-
"role": "
|
| 15 |
"content": """Your Role: You are an expert technical transcriber specializing in mathematical and scientific documents.
|
| 16 |
Your mission is to convert handwritten solutions from a provided image or PDF into a clean, accurate, and logically structured Markdown format.
|
| 17 |
Instructions:
|
|
@@ -22,9 +21,8 @@ Instructions:
|
|
| 22 |
- Do not recreate graphs, only describe them.
|
| 23 |
"""
|
| 24 |
},
|
| 25 |
-
|
| 26 |
"MARKSCHEME_TRANSCRIPTION_PROMPT": {
|
| 27 |
-
"role": "
|
| 28 |
"content": """Your Role: You are an expert transcriber.
|
| 29 |
Convert the official marking scheme from the provided PDF into clean, structured Markdown.
|
| 30 |
Instructions:
|
|
@@ -36,9 +34,8 @@ Instructions:
|
|
| 36 |
- Use code blocks for equations.
|
| 37 |
"""
|
| 38 |
},
|
| 39 |
-
|
| 40 |
"GRADING_PROMPT": {
|
| 41 |
-
"role": "
|
| 42 |
"content": """You are an official examiner. Use the following grading rules strictly.
|
| 43 |
Abbreviations:
|
| 44 |
- M: Marks awarded for attempting to use a correct Method.
|
|
@@ -79,9 +76,9 @@ Produce a GitHub-flavored Markdown table with 3 columns:
|
|
| 79 |
|---------------|---------------|--------|
|
| 80 |
Special Formatting Rule:
|
| 81 |
- Whenever a mark is lost (M0, A0, R0 etc.), wrap it in red using: `<span style=\"color:red\">M0</span>`.
|
| 82 |
-
- Also
|
| 83 |
- Keep awarded marks (M1, A1, etc.) in plain text.
|
| 84 |
-
- If mixed (e.g., M1A0A1), only highlight the lost marks (`A0`).
|
| 85 |
After the table, provide:
|
| 86 |
### Summary & Final Mark
|
| 87 |
- Total marks obtained vs total available
|
|
@@ -98,24 +95,27 @@ def save_as_pdf(text, filename="output.pdf"):
|
|
| 98 |
pdf.save(filename)
|
| 99 |
return filename
|
| 100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
# ---------- STEP 1: TRANSCRIBE STUDENT ----------
|
| 102 |
def transcribe_student(ans_file):
|
| 103 |
try:
|
| 104 |
ans_uploaded = genai.upload_file(path=ans_file, display_name="Answer Sheet")
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
resp = model.generate_content(
|
| 108 |
-
[PROMPTS["TRANSCRIPTION_PROMPT"]["content"], ans_uploaded],
|
| 109 |
-
safety_settings={
|
| 110 |
-
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
|
| 111 |
-
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
|
| 112 |
-
}
|
| 113 |
-
)
|
| 114 |
-
|
| 115 |
-
transcription = getattr(resp, "text", None)
|
| 116 |
-
if not transcription and resp.candidates:
|
| 117 |
-
transcription = resp.candidates[0].content.parts[0].text
|
| 118 |
-
|
| 119 |
pdf_path = save_as_pdf(transcription, "student_transcription.pdf")
|
| 120 |
return transcription, pdf_path
|
| 121 |
except Exception as e:
|
|
@@ -125,20 +125,7 @@ def transcribe_student(ans_file):
|
|
| 125 |
def transcribe_ms(ms_file):
|
| 126 |
try:
|
| 127 |
ms_uploaded = genai.upload_file(path=ms_file, display_name="Markscheme")
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
resp = model.generate_content(
|
| 131 |
-
[PROMPTS["MARKSCHEME_TRANSCRIPTION_PROMPT"]["content"], ms_uploaded],
|
| 132 |
-
safety_settings={
|
| 133 |
-
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
|
| 134 |
-
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
|
| 135 |
-
}
|
| 136 |
-
)
|
| 137 |
-
|
| 138 |
-
ms_transcription = getattr(resp, "text", None)
|
| 139 |
-
if not ms_transcription and resp.candidates:
|
| 140 |
-
ms_transcription = resp.candidates[0].content.parts[0].text
|
| 141 |
-
|
| 142 |
pdf_path = save_as_pdf(ms_transcription, "ms_transcription.pdf")
|
| 143 |
return ms_transcription, pdf_path
|
| 144 |
except Exception as e:
|
|
@@ -148,22 +135,12 @@ def transcribe_ms(ms_file):
|
|
| 148 |
def grade(qp_file, ms_transcription, student_transcription):
|
| 149 |
try:
|
| 150 |
qp_uploaded = genai.upload_file(path=qp_file, display_name="Question Paper")
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
safety_settings={
|
| 158 |
-
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
|
| 159 |
-
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
|
| 160 |
-
}
|
| 161 |
-
)
|
| 162 |
-
|
| 163 |
-
grading = getattr(response, "text", None)
|
| 164 |
-
if not grading and response.candidates:
|
| 165 |
-
grading = response.candidates[0].content.parts[0].text
|
| 166 |
-
|
| 167 |
pdf_path = save_as_pdf(grading, "grading.pdf")
|
| 168 |
return grading, pdf_path
|
| 169 |
except Exception as e:
|
|
|
|
| 5 |
from markdown_pdf import MarkdownPdf, Section
|
| 6 |
|
| 7 |
# -------------------- CONFIG --------------------
|
| 8 |
+
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
|
|
|
|
| 9 |
|
| 10 |
+
# ---------- PROMPTS (JSON Style) ----------
|
| 11 |
PROMPTS = {
|
| 12 |
"TRANSCRIPTION_PROMPT": {
|
| 13 |
+
"role": "Student Transcription",
|
| 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:
|
|
|
|
| 21 |
- Do not recreate graphs, only describe them.
|
| 22 |
"""
|
| 23 |
},
|
|
|
|
| 24 |
"MARKSCHEME_TRANSCRIPTION_PROMPT": {
|
| 25 |
+
"role": "Markscheme Transcription",
|
| 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:
|
|
|
|
| 34 |
- Use code blocks for equations.
|
| 35 |
"""
|
| 36 |
},
|
|
|
|
| 37 |
"GRADING_PROMPT": {
|
| 38 |
+
"role": "Examiner Grading",
|
| 39 |
"content": """You are an official examiner. Use the following grading rules strictly.
|
| 40 |
Abbreviations:
|
| 41 |
- M: Marks awarded for attempting to use a correct Method.
|
|
|
|
| 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 using `<span style=\"color:red\">reason text</span>`.
|
| 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.
|
| 82 |
After the table, provide:
|
| 83 |
### Summary & Final Mark
|
| 84 |
- Total marks obtained vs total available
|
|
|
|
| 95 |
pdf.save(filename)
|
| 96 |
return filename
|
| 97 |
|
| 98 |
+
# ---------- COMMON MODEL CALL ----------
|
| 99 |
+
def call_model(inputs):
|
| 100 |
+
model = genai.GenerativeModel("gemini-2.5-pro", generation_config={"temperature": 0})
|
| 101 |
+
response = model.generate_content(
|
| 102 |
+
inputs,
|
| 103 |
+
safety_settings={
|
| 104 |
+
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
|
| 105 |
+
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
|
| 106 |
+
}
|
| 107 |
+
)
|
| 108 |
+
if getattr(response, "text", None):
|
| 109 |
+
return response.text
|
| 110 |
+
elif response.candidates:
|
| 111 |
+
return response.candidates[0].content.parts[0].text
|
| 112 |
+
return None
|
| 113 |
+
|
| 114 |
# ---------- STEP 1: TRANSCRIBE STUDENT ----------
|
| 115 |
def transcribe_student(ans_file):
|
| 116 |
try:
|
| 117 |
ans_uploaded = genai.upload_file(path=ans_file, display_name="Answer Sheet")
|
| 118 |
+
transcription = call_model([PROMPTS["TRANSCRIPTION_PROMPT"]["content"], ans_uploaded])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
pdf_path = save_as_pdf(transcription, "student_transcription.pdf")
|
| 120 |
return transcription, pdf_path
|
| 121 |
except Exception as e:
|
|
|
|
| 125 |
def transcribe_ms(ms_file):
|
| 126 |
try:
|
| 127 |
ms_uploaded = genai.upload_file(path=ms_file, display_name="Markscheme")
|
| 128 |
+
ms_transcription = call_model([PROMPTS["MARKSCHEME_TRANSCRIPTION_PROMPT"]["content"], ms_uploaded])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
pdf_path = save_as_pdf(ms_transcription, "ms_transcription.pdf")
|
| 130 |
return ms_transcription, pdf_path
|
| 131 |
except Exception as e:
|
|
|
|
| 135 |
def grade(qp_file, ms_transcription, student_transcription):
|
| 136 |
try:
|
| 137 |
qp_uploaded = genai.upload_file(path=qp_file, display_name="Question Paper")
|
| 138 |
+
grading = call_model([
|
| 139 |
+
PROMPTS["GRADING_PROMPT"]["content"],
|
| 140 |
+
qp_uploaded,
|
| 141 |
+
"### Markscheme Transcription:\n" + ms_transcription,
|
| 142 |
+
"### Student Transcription:\n" + student_transcription
|
| 143 |
+
])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
pdf_path = save_as_pdf(grading, "grading.pdf")
|
| 145 |
return grading, pdf_path
|
| 146 |
except Exception as e:
|