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()