Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -12,6 +12,7 @@ from io import BytesIO
|
|
| 12 |
from PIL import Image
|
| 13 |
import csv
|
| 14 |
from pathlib import Path
|
|
|
|
| 15 |
|
| 16 |
# ---------- 1. Configure AI Systems ----------
|
| 17 |
# Primary: Gemini (with vision support)
|
|
@@ -156,27 +157,32 @@ Each question should be:
|
|
| 156 |
Start immediately with Q1. Do not include any introduction or explanation."""
|
| 157 |
|
| 158 |
response, source = ask_ai(prompt, temperature=0.6)
|
| 159 |
-
|
| 160 |
-
# Parse questions using
|
| 161 |
questions = []
|
| 162 |
-
lines = response.split('\n')
|
| 163 |
-
|
| 164 |
current_question = None
|
| 165 |
-
|
| 166 |
-
|
|
|
|
|
|
|
| 167 |
if not line:
|
| 168 |
continue
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
if
|
|
|
|
| 172 |
if current_question:
|
| 173 |
-
questions.append(current_question)
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
current_question
|
| 177 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
if current_question:
|
| 179 |
-
questions.append(current_question)
|
| 180 |
|
| 181 |
# Ensure we got questions
|
| 182 |
if not questions:
|
|
@@ -284,6 +290,46 @@ Good luck! 🍀
|
|
| 284 |
|
| 285 |
return filepath
|
| 286 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
def download_feedback_file(feedback_text, topic, grade_level):
|
| 288 |
"""Download AI feedback/corrections"""
|
| 289 |
if not feedback_text:
|
|
@@ -445,9 +491,13 @@ with gr.Blocks(title="UNEB Exam Prep - Primary 6 & 7", theme=gr.themes.Soft(), c
|
|
| 445 |
if not session.current_questions:
|
| 446 |
return None, "⚠️ Generate questions first!"
|
| 447 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
filepath = download_questions_file(session.current_questions, session.current_topic, session.current_grade)
|
| 449 |
-
|
| 450 |
-
return filepath, f"✅ Downloaded! File: {os.path.abspath(filepath)}"
|
| 451 |
except Exception as e:
|
| 452 |
return None, f"❌ Error: {str(e)}"
|
| 453 |
|
|
|
|
| 12 |
from PIL import Image
|
| 13 |
import csv
|
| 14 |
from pathlib import Path
|
| 15 |
+
import re
|
| 16 |
|
| 17 |
# ---------- 1. Configure AI Systems ----------
|
| 18 |
# Primary: Gemini (with vision support)
|
|
|
|
| 157 |
Start immediately with Q1. Do not include any introduction or explanation."""
|
| 158 |
|
| 159 |
response, source = ask_ai(prompt, temperature=0.6)
|
| 160 |
+
|
| 161 |
+
# Parse questions robustly using regex to support multi-digit numbers (Q1..Q10..)
|
| 162 |
questions = []
|
|
|
|
|
|
|
| 163 |
current_question = None
|
| 164 |
+
q_re = re.compile(r'^Q(\d+)\.?\s*(.*)', re.IGNORECASE)
|
| 165 |
+
|
| 166 |
+
for raw_line in response.splitlines():
|
| 167 |
+
line = raw_line.strip()
|
| 168 |
if not line:
|
| 169 |
continue
|
| 170 |
+
|
| 171 |
+
m = q_re.match(line)
|
| 172 |
+
if m:
|
| 173 |
+
# Start of a new question
|
| 174 |
if current_question:
|
| 175 |
+
questions.append(current_question.strip())
|
| 176 |
+
# Include the rest of the line after the Qn. prefix
|
| 177 |
+
rest = m.group(2) or ''
|
| 178 |
+
current_question = f"Q{m.group(1)}. {rest.strip()}"
|
| 179 |
+
else:
|
| 180 |
+
# Continuation of previous question (append with space)
|
| 181 |
+
if current_question:
|
| 182 |
+
current_question += ' ' + line
|
| 183 |
+
|
| 184 |
if current_question:
|
| 185 |
+
questions.append(current_question.strip())
|
| 186 |
|
| 187 |
# Ensure we got questions
|
| 188 |
if not questions:
|
|
|
|
| 290 |
|
| 291 |
return filepath
|
| 292 |
|
| 293 |
+
|
| 294 |
+
def download_questions_pdf(questions_list, topic, grade_level):
|
| 295 |
+
"""Generate a simple PDF with the questions, return filepath."""
|
| 296 |
+
try:
|
| 297 |
+
from reportlab.lib.pagesizes import A4
|
| 298 |
+
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
| 299 |
+
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
|
| 300 |
+
from reportlab.lib.units import mm
|
| 301 |
+
except Exception:
|
| 302 |
+
return None
|
| 303 |
+
|
| 304 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 305 |
+
filename = f"questions_{grade_level}_{topic}_{timestamp}.pdf"
|
| 306 |
+
filepath = f"downloads/{filename}"
|
| 307 |
+
os.makedirs("downloads", exist_ok=True)
|
| 308 |
+
|
| 309 |
+
doc = SimpleDocTemplate(filepath, pagesize=A4, rightMargin=20*mm, leftMargin=20*mm, topMargin=20*mm, bottomMargin=20*mm)
|
| 310 |
+
styles = getSampleStyleSheet()
|
| 311 |
+
normal = styles['Normal']
|
| 312 |
+
heading = ParagraphStyle('Heading', parent=styles['Heading1'], alignment=0)
|
| 313 |
+
elems = []
|
| 314 |
+
|
| 315 |
+
elems.append(Paragraph("UNEB EXAM PRACTICE QUESTIONS", styles['Title']))
|
| 316 |
+
elems.append(Spacer(1, 4*mm))
|
| 317 |
+
meta = f"Grade Level: {grade_level} Topic: {topic} Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
| 318 |
+
elems.append(Paragraph(meta, normal))
|
| 319 |
+
elems.append(Spacer(1, 6*mm))
|
| 320 |
+
|
| 321 |
+
for i, q in enumerate(questions_list):
|
| 322 |
+
# Remove any leading Qn. if present
|
| 323 |
+
q_text = re.sub(r'^Q\d+\.?\s*', '', q)
|
| 324 |
+
elems.append(Paragraph(f"<b>Q{i+1}.</b> {q_text}", normal))
|
| 325 |
+
elems.append(Spacer(1, 3*mm))
|
| 326 |
+
|
| 327 |
+
try:
|
| 328 |
+
doc.build(elems)
|
| 329 |
+
return filepath
|
| 330 |
+
except Exception:
|
| 331 |
+
return None
|
| 332 |
+
|
| 333 |
def download_feedback_file(feedback_text, topic, grade_level):
|
| 334 |
"""Download AI feedback/corrections"""
|
| 335 |
if not feedback_text:
|
|
|
|
| 491 |
if not session.current_questions:
|
| 492 |
return None, "⚠️ Generate questions first!"
|
| 493 |
try:
|
| 494 |
+
# Prefer PDF export; fall back to plain text if PDF library missing
|
| 495 |
+
pdf_path = download_questions_pdf(session.current_questions, session.current_topic, session.current_grade)
|
| 496 |
+
if pdf_path:
|
| 497 |
+
return pdf_path, f"✅ Downloaded PDF: {os.path.abspath(pdf_path)}"
|
| 498 |
+
# Fallback to text
|
| 499 |
filepath = download_questions_file(session.current_questions, session.current_topic, session.current_grade)
|
| 500 |
+
return filepath, f"✅ Downloaded TXT: {os.path.abspath(filepath)}"
|
|
|
|
| 501 |
except Exception as e:
|
| 502 |
return None, f"❌ Error: {str(e)}"
|
| 503 |
|