Spaces:
Build error
Build error
Download app.py from Lazer5641/PdfToQuiz_Game: direct link, hf CLI and curl.
- Browser
- Download file 7.62 kB
-
https://huggingface.co/spaces/Lazer5641/PdfToQuiz_Game/resolve/main/app.py
- Command line
-
hf download hf://spaces/Lazer5641/PdfToQuiz_Game/app.py
-
curl -L -o app.py https://huggingface.co/spaces/Lazer5641/PdfToQuiz_Game/resolve/main/app.py
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() |