import os import logging from sqlalchemy.orm import Session from app.config import settings from app.models.section import Section from app.models.pdf_document import PDFDocument from app.models.quiz import Quiz from app.models.question import Question from app.services import ai_service, vector_service, pdf_service, embedding_service from app.utils.quiz_questions import add_questions_to_quiz logger = logging.getLogger(__name__) def create_quiz_from_section( db: Session, user_id: int, section_id: int, title: str, mode: str = "timed", time_limit_minutes: int | None = None, model_id: str | None = None, question_category_id: int | None = None, ) -> Quiz: """Extract questions from a section's page range using AI.""" section = db.query(Section).filter(Section.id == section_id).first() if not section: raise ValueError("Section not found") document = db.query(PDFDocument).filter(PDFDocument.id == section.document_id).first() if not document: raise ValueError("Document not found") # Get text from vector store for this page range content = vector_service.get_pages_text( document_id=section.document_id, start_page=section.start_page, end_page=section.end_page, ) if not content: raise ValueError("No content found for this section's page range") # Get configured model (use override if provided) if model_id: from app.models.ai_model_config import AIModelConfig config = db.query(AIModelConfig).filter(AIModelConfig.model_id == model_id).first() api_key = config.api_key if config and config.api_key else None else: model_id, api_key = ai_service.get_model_for_task(db, "extraction") # Extract questions via AI (not generate — questions already exist in PDF) page_info = f"{section.start_page}-{section.end_page}" question_data = ai_service.extract_questions( content, page_info=page_info, model_id=model_id, api_key=api_key, ) # Extract images for the page range file_path = os.path.join(settings.UPLOAD_DIR, document.filename) page_images = {} if os.path.exists(file_path): try: page_images = pdf_service.extract_all_images( file_path, document.id, section.start_page, section.end_page ) except Exception as e: logger.warning(f"Image extraction failed: {e}") # Collect skipped questions (those without a correct answer) import json skipped = [] if question_data and question_data[0].get("skipped"): skipped = question_data[0].pop("skipped") valid_questions = [q for q in question_data if q.get("correct_answer")] # Create quiz quiz = Quiz( section_id=section_id, user_id=user_id, title=title, questions_count=len(valid_questions), mode=mode, time_limit_minutes=time_limit_minutes, skipped_questions=json.dumps(skipped) if skipped else None, ) db.add(quiz) db.flush() # Create question records, associating images where possible for pos, q in enumerate(valid_questions): page_ref = q.get("page_reference") image_path = None # Try to associate an image with this question if page_ref and page_ref in page_images and page_images[page_ref]: # Take the first unassigned image from this page image_path = page_images[page_ref].pop(0) if not page_images[page_ref]: del page_images[page_ref] question = Question( source_quiz_id=quiz.id, question_category_id=question_category_id, question_text=q["question_text"], question_type=q["question_type"], options=q.get("options"), correct_answer=q["correct_answer"], explanation=q.get("explanation", ""), page_reference=page_ref, image_path=image_path, ) db.add(question) db.flush() # Register in junction table from app.models.quiz_question_link import QuizQuestionLink db.add(QuizQuestionLink(quiz_id=quiz.id, question_id=question.id, position=pos)) try: embedding_service.embed_question(question) except Exception as e: logger.warning(f"Embedding generation failed for question {question.id}: {e}") db.commit() db.refresh(quiz) return quiz