File size: 7,615 Bytes
8eba687
354e619
8eba687
 
 
 
 
 
 
 
 
1d507f3
8eba687
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354e619
8eba687
1d507f3
 
 
 
 
 
 
354e619
 
8eba687
354e619
 
 
 
 
 
 
 
 
 
 
 
 
8eba687
 
354e619
 
8eba687
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1d507f3
8eba687
 
 
 
 
 
 
 
 
 
354e619
8eba687
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354e619
8eba687
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1d507f3
8eba687
354e619
8eba687
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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()