# retoor <retoor@molodetz.nl>
from __future__ import annotations
import json
from datetime import datetime, timezone
from devplacepy.database import conditional_update_row, get_table
from devplacepy.utils import is_admin
class QuizError(Exception):
pass
def _now() -> datetime:
return datetime.now(timezone.utc)
def _iso(value: datetime | None = None) -> str:
return (value or _now()).isoformat()
def _quizzes():
return get_table("quizzes")
def _questions():
return get_table("quiz_questions")
def _options():
return get_table("quiz_options")
def _attempts():
return get_table("quiz_attempts")
def _answers():
return get_table("quiz_answers")
def born_live(fields: dict) -> dict:
stamp = _iso()
return {
**fields,
"created_at": fields.get("created_at") or stamp,
"updated_at": stamp,
"deleted_at": None,
"deleted_by": None,
}
def load_json(raw, fallback):
if isinstance(raw, (list, dict)):
return raw
try:
parsed = json.loads(raw or "")
except (TypeError, ValueError):
return fallback
return parsed if isinstance(parsed, type(fallback)) else fallback
def dump_json(value) -> str:
return json.dumps(value, ensure_ascii=False)
def get_quiz(quiz_uid: str) -> dict | None:
if not quiz_uid:
return None
return _quizzes().find_one(uid=quiz_uid, deleted_at=None)
def is_quiz_owner(quiz: dict | None, user: dict | None) -> bool:
return bool(quiz and user and quiz.get("user_uid") == user.get("uid"))
def can_view_quiz(quiz: dict | None, user: dict | None) -> bool:
if not quiz:
return False
if quiz.get("status") == "published":
return True
return is_quiz_owner(quiz, user) or is_admin(user)
def guard_editable(quiz_uid: str) -> dict:
rows = conditional_update_row(
"quizzes",
quiz_uid,
"content_version = COALESCE(content_version, 0) + 1",
"status = 'draft' AND deleted_at IS NULL",
{},
)
if rows:
return get_quiz(quiz_uid) or {}
quiz = get_quiz(quiz_uid)
if not quiz:
raise QuizError("Quiz not found")
raise QuizError("A published quiz can no longer be edited")
__all__ = [
"QuizError",
"born_live",
"can_view_quiz",
"conditional_update_row",
"dump_json",
"get_quiz",
"guard_editable",
"is_quiz_owner",
"load_json",
"_answers",
"_attempts",
"_iso",
"_now",
"_options",
"_questions",
"_quizzes",
]