pdf-quiz-generator/backend/app/routers/share.py
Daniel 3279e14bb2 refactor: remove the LMS
There will be no courses. What was there: one draft called "jk" with two empty
lessons, and 4,000 lines of code around it — courses, modules, lessons,
enrolments, per-lesson progress, SCORM, BigBlueButton, completion certificates,
three React pages, a router, two models.

Its real cost was everywhere else. Every query that measured practice had to
remember `Quiz.course_id.is_(None)`, and forgetting it in one place would have
silently mixed course attempts into a learner's analytics; the bank predicate
carried a subquery to exclude a course's own questions from every search,
recommendation and share; quiz access had a second, parallel rule about
enrolment. All of that is gone, so the remaining rules say what they mean.

`quizzes.allow_review` goes with it. It was only ever enforced for a course
quiz, so it had become a promise nothing keeps — the public session page was
still offering "no answer review" about sessions that review fine.

The fixtures' question 5 lived in a course quiz and stood for "a question that
exists but is not in your bank". There is no such thing now — a question is in
the bank unless it is deleted — so the counts it kept out of the numbers are
back in, and the tests that turned on it now turn on deletion or on the
attempt that actually holds a question.

Files the LMS uploaded stay on disk and stay protected: LEGACY_LMS_PREFIXES in
app/utils/upload_access.py is what keeps them unreachable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-12 23:27:51 +02:00

40 lines
1.5 KiB
Python

"""Public share links — viewing quiz info does not require a login."""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.database import get_db
from app.models.quiz import Quiz
from app.models.user import User
from app.services.quiz_builder import category_breadcrumbs
from app.models.question_category import QuestionCategory
from app.utils.quiz_access import quiz_shareable_predicate
router = APIRouter()
@router.get("/{token}")
def get_shared_quiz(
token: str,
db: Session = Depends(get_db),
):
"""Public quiz landing data; never exposes questions or answers."""
quiz = db.query(Quiz).filter(
Quiz.share_token == token,
Quiz.deleted_at.is_(None),
Quiz.is_shared == 1,
).first()
if not quiz or not db.query(Quiz.id).filter(
Quiz.id == quiz.id, quiz_shareable_predicate()).first():
raise HTTPException(404, "Shared quiz not found")
owner = db.get(User, quiz.user_id) if quiz.user_id else None
categories = db.query(QuestionCategory).all()
return {
"quiz_id": quiz.id,
"title": quiz.title,
"mode": quiz.mode,
"questions_count": quiz.questions_count,
"time_limit_minutes": quiz.time_limit_minutes,
"owner_name": owner.name if owner else None,
"category": quiz.category.name if quiz.category else None,
"category_breadcrumbs": category_breadcrumbs(categories, quiz.category_id) if quiz.category_id else [],
}