# retoor <retoor@molodetz.nl>
import json
import time
import requests
from tests.conftest import BASE_URL
JSON = {"Accept": "application/json"}
_counter = [0]
CHOICE = {
"kind": "single_choice",
"prompt": "Which journal mode allows concurrent readers?",
"points": 2,
"options": "DELETE\nWAL\nMEMORY",
"correct_indexes": "1",
}
NUMERIC = {
"kind": "numeric",
"prompt": "How many bytes are in a kibibyte?",
"points": 1,
"numeric_value": "1024",
"numeric_tolerance": "0",
}
TRUE_FALSE = {
"kind": "true_false",
"prompt": "A partial index can carry a WHERE clause.",
"points": 1,
"correct_boolean": "1",
}
FREE_TEXT = {
"kind": "free_text",
"prompt": "Why does a partial index help here?",
"points": 3,
"expected_answer": "It keeps the live query off a one bucket index.",
"grading_criteria": "Accept any answer that mentions the planner.",
}
DOCUMENT = {
"title": "Imported quiz",
"description": "Created from a document.",
"settings": {"reveal_answers": True, "pass_percent": 60, "allow_review": True},
"questions": [
{
"kind": "single_choice",
"prompt": "Which journal mode allows concurrent readers?",
"points": 2,
"options": [{"label": "DELETE"}, {"label": "WAL", "is_correct": True}],
},
{
"kind": "numeric",
"prompt": "How many bytes are in a kibibyte?",
"points": 1,
"numeric_value": 1024,
},
],
}
def signup(prefix="qz"):
_counter[0] += 1
name = f"{prefix}{int(time.time() * 1000)}{_counter[0]}"
session = requests.Session()
session.post(
f"{BASE_URL}/auth/signup",
data={
"username": name,
"email": f"{name}@t.dev",
"password": "secret123",
"confirm_password": "secret123",
},
allow_redirects=True,
)
return session, name
def create_quiz(session, title="Test quiz", **settings):
payload = {"title": title, "description": "A quiz."}
payload.update(settings)
response = session.post(f"{BASE_URL}/quizzes/create", data=payload, headers=JSON)
return response.json()["data"]["slug"]
def add_question(session, slug, payload):
return session.post(f"{BASE_URL}/quizzes/{slug}/questions", data=payload, headers=JSON)
def publish(session, slug):
return session.post(f"{BASE_URL}/quizzes/{slug}/publish", headers=JSON)
def build_published(session, questions=(CHOICE, NUMERIC), title="Playable quiz", **settings):
slug = create_quiz(session, title, **settings)
for question in questions:
add_question(session, slug, question)
publish(session, slug)
return slug
def import_quiz(session, document=None):
payload = {"document": json.dumps(document or DOCUMENT)}
response = session.post(f"{BASE_URL}/quizzes/import", data=payload, headers=JSON)
return response
def start_attempt(session, slug):
response = session.post(f"{BASE_URL}/quizzes/{slug}/attempts", headers=JSON)
return response.json()["data"]["uid"]
def attempt_state(session, slug, attempt_uid):
return session.get(
f"{BASE_URL}/quizzes/{slug}/attempts/{attempt_uid}", headers=JSON
).json()
def answer_correctly(session, slug, attempt_uid, question):
payload = {"question_uid": question["uid"]}
if question["kind"] == "single_choice":
correct = next(
(option["uid"] for option in question["options"] if option.get("is_correct")), None
)
payload["option_uids"] = correct or question["options"][1]["uid"]
elif question["kind"] == "numeric":
payload["answer_text"] = "1024"
elif question["kind"] == "true_false":
payload["answer_text"] = "true"
else:
payload["answer_text"] = "the planner picks a bad index for live rows"
return session.post(
f"{BASE_URL}/quizzes/{slug}/attempts/{attempt_uid}/answer", data=payload, headers=JSON
)