Question sharing (edits now propagate): - quiz_question_links junction table: quizzes reference questions, not own copies - Existing questions migrated to junction (idempotent ON CONFLICT DO NOTHING) - from-bank and from-category create quiz by adding junction links (no copying) - Editing a question via QuizEditPage or bank Edit modal updates the ONE record visible in all quizzes that reference it - Delete from quiz: only deletes Question record if no other quiz references it - quiz_service: fixed position counter (was using len() incorrectly, now enumerate) - question.quiz_id renamed to source_quiz_id in Python (DB column still quiz_id) - All attempts/quizzes/search code updated to use junction helper or source_quiz_id Bug fixes: - PostgreSQL en_US.utf8 collation B-tree index corruption on users.email Fixed by ALTERing column to COLLATE "C" in migration + immediate repair - get_current_user now blocks unverified users on ALL endpoints (403 + X-Unverified header) api/client.js intercepts X-Unverified=true and auto-logs out to /login - Search (quiz + bank): correct_answer and explanation now included in matching_questions so study modals work correctly - Keyword search: order_by updated to use source_quiz_id - Bulk category: changed from PATCH with body array to POST with Pydantic body (fixes null category_id removal) - Question bank: edit button added, answer highlighting removed from list view - QuestionBankPage: Keyword/Semantic/Hybrid search toggle - Login: login error message stays visible (removed immediate reload) Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
29 lines
1.5 KiB
Python
29 lines
1.5 KiB
Python
from pgvector.sqlalchemy import Vector
|
|
from sqlalchemy import Column, Integer, String, Text, JSON, ForeignKey
|
|
from sqlalchemy.orm import relationship
|
|
|
|
from app.config import settings
|
|
from app.database import Base
|
|
from app.models.question_category import QuestionCategory # noqa — ensures mapper resolves
|
|
from app.models.quiz_question_link import QuizQuestionLink # noqa
|
|
|
|
|
|
class Question(Base):
|
|
__tablename__ = "questions"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
# source_quiz_id: which quiz this question was originally extracted for (informational).
|
|
# Content membership is tracked via quiz_question_links junction table.
|
|
source_quiz_id = Column("quiz_id", Integer, ForeignKey("quizzes.id", ondelete="SET NULL"), nullable=True)
|
|
question_category_id = Column(Integer, ForeignKey("question_categories.id", ondelete="SET NULL"), nullable=True)
|
|
question_text = Column(Text, nullable=False)
|
|
question_type = Column(String, nullable=False) # mcq, true_false, fill_blank
|
|
options = Column(JSON, nullable=True) # list of strings for mcq
|
|
correct_answer = Column(String, nullable=False)
|
|
explanation = Column(Text, nullable=True)
|
|
page_reference = Column(Integer, nullable=True)
|
|
image_path = Column(String, nullable=True)
|
|
embedding = Column(Vector(1024), nullable=True) # semantic search vector
|
|
|
|
question_category = relationship("QuestionCategory", back_populates="questions",
|
|
foreign_keys=[question_category_id])
|