PdfToQuiz_Game / app.py
Lazer5641's picture
Test3
1d507f3 verified
Raw History Blame Contribute Delete
7.62 kB
import gradio as gr
import PyPDF2
import pdf2image
import pytesseract
from transformers import pipeline
import logging
import re
import numpy as np
from PIL import Image
import io
import random
import os
# Configure logging
logging.basicConfig(level=logging.INFO, filename="quiz_game.log")
logger = logging.getLogger(__name__)
# Initialize models
try:
question_generator = pipeline("text2text-generation", model="t5-small", device=-1)
except Exception as e:
logger.error(f"Failed to load T5 model: {str(e)}")
question_generator = None
try:
ocr_pipeline = pipeline("image-to-text", model="microsoft/trocr-small-printed", device=-1)
except Exception as e:
logger.error(f"Failed to load OCR model: {str(e)}")
ocr_pipeline = None
# Global state
questions = []
current_question = 0
score = 0
game_state = {"questions": [], "current_question": 0, "score": 0}
def extract_text_from_pdf(pdf_file):
"""Extract text from a PDF using PyPDF2, with OCR as fallback for scanned PDFs."""
try:
# Handle Gradio file input
if isinstance(pdf_file, str): # Gradio may pass file path
with open(pdf_file, "rb") as f:
pdf_bytes = f.read()
else: # Assume file-like object with content
pdf_bytes = pdf_file # Gradio passes bytes directly
# Try PyPDF2 for text-based PDFs
pdf_reader = PyPDF2.PdfReader(io.BytesIO(pdf_bytes))
text = ""
for page in pdf_reader.pages:
page_text = page.extract_text()
if page_text:
text += page_text + "\n"
if text.strip():
return text.strip()
# Fallback to OCR for scanned PDFs
if not ocr_pipeline:
return "OCR model unavailable. Please use a text-based PDF."
images = pdf2image.convert_from_bytes(pdf_bytes)
ocr_text = ""
for image in images:
ocr_result = ocr_pipeline(image)[0]["generated_text"]
ocr_text += ocr_result + "\n"
return ocr_text.strip() if ocr_text.strip() else "No text extracted from scanned PDF."
except Exception as e:
logger.error(f"PDF text extraction error: {str(e)}")
return f"Error: {str(e)}"
def generate_questions(text, num_questions=5):
"""Generate multiple-choice questions from text using T5."""
if not question_generator:
return [{"question": "Question generation unavailable.", "options": [], "correct": ""}]
try:
questions = []
chunks = [text[i:i+500] for i in range(0, len(text), 500)]
for _ in range(min(num_questions, len(chunks))):
chunk = random.choice(chunks)
prompt = f"Generate a multiple-choice question with 4 options based on this text: {chunk}"
result = question_generator(prompt, max_length=200)[0]["generated_text"]
question_match = re.search(r"(.*?)\?(\s*a\)\s*(.*?)\s*b\)\s*(.*?)\s*c\)\s*(.*?)\s*d\)\s*(.*?)$", result, re.DOTALL)
if question_match:
question = question_match.group(1).strip() + "?"
options = [
question_match.group(3).strip(),
question_match.group(4).strip(),
question_match.group(5).strip(),
question_match.group(6).strip()
]
correct = random.choice(options)
questions.append({"question": question, "options": options, "correct": correct})
return questions if questions else [{"question": "No questions generated.", "options": [], "correct": ""}]
except Exception as e:
logger.error(f"Question generation error: {str(e)}")
return [{"question": f"Error: {str(e)}", "options": [], "correct": ""}]
def start_game(pdf_file):
"""Start a new game by generating questions from the PDF."""
global questions, current_question, score, game_state
text = extract_text_from_pdf(pdf_file)
if "Error" in text or "unavailable" in text or "No text extracted" in text:
return text, "", [], "", 0
questions = generate_questions(text, num_questions=5)
current_question = 0
score = 0
game_state = {"questions": questions, "current_question": 0, "score": 0}
if not questions or not questions[0]["options"]:
return "Failed to generate questions.", "", [], "", 0
question = questions[0]["question"]
options = questions[0]["options"]
return "Game started!", question, options, "", score
def answer_question(selected_option):
"""Handle answer submission and game logic."""
global current_question, score, game_state
if current_question >= len(questions) or not questions:
return "Game over!", "", [], "Start a new game.", score
current = questions[current_question]
correct_answer = current["correct"]
if selected_option == correct_answer:
score += 100
current_question += 1
game_state["current_question"] = current_question
game_state["score"] = score
if current_question < len(questions):
next_question = questions[current_question]["question"]
next_options = questions[current_question]["options"]
return f"Correct! +100 points", next_question, next_options, "", score
else:
return "Congratulations! You completed the quiz!", "", [], "Start a new game.", score
else:
score = max(0, score - 50)
current_question = 0
game_state["current_question"] = 0
game_state["score"] = score
return f"Wrong! -50 points. Starting over.", questions[0]["question"], questions[0]["options"], "", score
def restart_game():
"""Restart the game manually."""
global current_question, score, game_state
current_question = 0
score = 0
game_state["current_question"] = 0
game_state["score"] = 0
if questions:
return "Game restarted!", questions[0]["question"], questions[0]["options"], "", score
return "No questions available. Upload a PDF.", "", [], "", 0
# Gradio interface
with gr.Blocks(css=".gradio-container {font-family: 'Comic Sans MS', Arial; background: #f0f8ff;} h1 {color: #ff4500; text-align: center;}") as demo:
gr.Markdown("# Quiz Planet: AI-Powered PDF Quiz Game")
gr.Markdown("Upload a PDF to generate a quiz. Answer correctly to advance (+100 points), or restart on wrong answers (-50 points).")
with gr.Tabs():
with gr.Tab("Upload PDF"):
pdf_input = gr.File(label="Upload PDF", file_types=[".pdf"])
upload_button = gr.Button("Generate Quiz")
upload_status = gr.Textbox(label="Status")
upload_button.click(start_game, pdf_input, [upload_status, gr.Textbox(label="Question"), gr.Radio(label="Options"), gr.Textbox(label="Feedback"), gr.Textbox(label="Score")])
with gr.Tab("Play Quiz"):
question_display = gr.Textbox(label="Question")
options_radio = gr.Radio(label="Options", choices=[])
feedback_display = gr.Textbox(label="Feedback")
score_display = gr.Textbox(label="Score", value="0")
answer_button = gr.Button("Submit Answer")
restart_button = gr.Button("Restart Game")
answer_button.click(answer_question, options_radio, [feedback_display, question_display, options_radio, gr.Textbox(label="Feedback"), score_display])
restart_button.click(restart_game, None, [feedback_display, question_display, options_radio, gr.Textbox(label="Feedback"), score_display])
if __name__ == "__main__":
demo.launch()