Comments are gone. A thread under every question was a discussion nobody
moderated, and what it was used for was telling an educator something was
wrong. That is now feedback: a private report, carrying the question id,
that someone is expected to act on.
* Give feedback sits in the question bar's new "more" menu, beside Save
and Share — occasional actions, folded away rather than each taking a
slot in a bar read on every question.
* An educator gets a badge of what is outstanding. Each row names the
question and opens its editor, where the report sits beside the field
it is about; reply, resolve, reopen or delete from there.
* Resolving keeps the report. A question with a history of the same
complaint should visibly have one; deleting is for the ones that were
never about the question.
* A granted educator sees only their own branch. The badge answers
quietly with zero for someone with no access, so the header can ask
without first working out who is asking.
The question bank is now the Qbank: create a session, and the last three
with Resume. Its facets, tag tree and create-a-quiz were a second copy of
the custom-session page; marking and folders belong in the player while
you are sitting a question. Import and export moved to the question
manager, which is the one place questions are managed, and which now has
a Preview that opens over the list instead of a page you have to come
back from.
Fixed while there: a session in progress analysed as 0/0 with an empty
table, because the analysis read attempt_answers — written on submit —
while the session list counted the saved progress. They read the same
thing now. The category trail is gone from the player: it named the
answer's own topic and led out of a session part-way through. An option's
reasoning opens on click and closes on the next one.
Backend 253/253, frontend 323/323.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
44 lines
1.8 KiB
Python
44 lines
1.8 KiB
Python
"""Feedback on a question, replacing the comment thread
|
|
|
|
A comment thread under every question is a discussion that needs moderating.
|
|
What people used it for was telling an educator something was wrong, which is a
|
|
private report someone is meant to act on. This is that.
|
|
|
|
Revision ID: c3d4e5f6a7b8
|
|
Revises: b2c3d4e5f6a7
|
|
"""
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision = "c3d4e5f6a7b8"
|
|
down_revision = "b2c3d4e5f6a7"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
# Guarded: create_all() may have built it already on a fresh deploy.
|
|
if "question_feedback" in sa.inspect(op.get_bind()).get_table_names():
|
|
return
|
|
op.create_table(
|
|
"question_feedback",
|
|
sa.Column("id", sa.Integer(), primary_key=True, index=True),
|
|
sa.Column("question_id", sa.Integer(),
|
|
sa.ForeignKey("questions.id", ondelete="CASCADE"), nullable=False),
|
|
sa.Column("user_id", sa.Integer(),
|
|
sa.ForeignKey("users.id", ondelete="SET NULL"), nullable=True),
|
|
sa.Column("message", sa.Text(), nullable=False),
|
|
sa.Column("status", sa.String(length=20), nullable=False, server_default="open"),
|
|
sa.Column("reply", sa.Text(), nullable=True),
|
|
sa.Column("replied_by", sa.Integer(),
|
|
sa.ForeignKey("users.id", ondelete="SET NULL"), nullable=True),
|
|
sa.Column("replied_at", sa.DateTime(), nullable=True),
|
|
sa.Column("created_at", sa.DateTime(), server_default=sa.func.now()),
|
|
)
|
|
op.create_index("ix_question_feedback_question_id", "question_feedback", ["question_id"])
|
|
op.create_index("ix_question_feedback_status", "question_feedback", ["status"])
|
|
|
|
|
|
def downgrade() -> None:
|
|
if "question_feedback" in sa.inspect(op.get_bind()).get_table_names():
|
|
op.drop_table("question_feedback")
|