Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Add assignment_checkpoint table."""

import sqlalchemy as sa
from alembic import op

revision = "af090ca7e73f"
down_revision = "b91594c0e379"


def upgrade() -> None:
op.create_table(
"assignment_checkpoint",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("assignment_id", sa.Integer(), nullable=False),
sa.Column("reveal_date", sa.DateTime(), nullable=True),
sa.Column(
"created", sa.DateTime(), server_default=sa.func.now(), nullable=False
),
sa.Column(
"updated", sa.DateTime(), server_default=sa.func.now(), nullable=False
),
sa.ForeignKeyConstraint(
["assignment_id"],
["assignment.id"],
name=op.f("fk__assignment_checkpoint__assignment_id__assignment"),
ondelete="cascade",
),
sa.PrimaryKeyConstraint("id", name=op.f("pk__assignment_checkpoint")),
)
op.create_index(
op.f("ix__assignment_checkpoint_assignment_id"),
"assignment_checkpoint",
["assignment_id"],
unique=False,
)


def downgrade() -> None:
op.drop_index(
op.f("ix__assignment_checkpoint_assignment_id"),
table_name="assignment_checkpoint",
)
op.drop_table("assignment_checkpoint")
1 change: 1 addition & 0 deletions lms/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
AutoGradingConfig,
AutoGradingType,
)
from lms.models.assignment_checkpoint import AssignmentCheckpoint
from lms.models.assignment_grouping import AssignmentGrouping
from lms.models.assignment_membership import (
AssignmentMembership,
Expand Down
35 changes: 35 additions & 0 deletions lms/models/assignment_checkpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from datetime import datetime

import sqlalchemy as sa
from sqlalchemy.orm import Mapped, mapped_column, relationship

from lms.db import Base
from lms.models._mixins import CreatedUpdatedMixin


class AssignmentCheckpoint(CreatedUpdatedMixin, Base):
"""Hide & Reveal configuration for an assignment.

The existence of a row marks the assignment as a Hide & Reveal assignment
(mirroring how a NULL auto_grading_config_id marks a non-auto-graded one).

Reveal is whole-assignment: a single reveal_date applies to every group the
assignment is launched into. This LMS-side row is the source of truth for
that reveal state; on each launch the LMS fans it out into one h checkpoint
per (group, document) for server-side authorization. The LMS must persist
reveal_date here because lti_h.sync blindly re-sends current data on every
launch, so without it a re-sync would overwrite h's reveal_date back to NULL
and un-reveal an already-revealed assignment.
"""

__tablename__ = "assignment_checkpoint"

id: Mapped[int] = mapped_column(autoincrement=True, primary_key=True)

assignment_id: Mapped[int] = mapped_column(
sa.ForeignKey("assignment.id", ondelete="cascade"), index=True
)
assignment = relationship("Assignment")

reveal_date: Mapped[datetime | None] = mapped_column()
"""When the instructor revealed the assignment; NULL until revealed."""
1 change: 1 addition & 0 deletions tests/factories/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from tests.factories import requests_ as requests
from tests.factories.application_instance import ApplicationInstance
from tests.factories.assignment import Assignment, AutoGradingConfig
from tests.factories.assignment_checkpoint import AssignmentCheckpoint
from tests.factories.assignment_grouping import AssignmentGrouping
from tests.factories.assignment_membership import (
AssignmentMembership,
Expand Down
12 changes: 12 additions & 0 deletions tests/factories/assignment_checkpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from factory import SubFactory
from factory.alchemy import SQLAlchemyModelFactory

from lms import models
from tests.factories.assignment import Assignment


class AssignmentCheckpoint(SQLAlchemyModelFactory):
class Meta:
model = models.AssignmentCheckpoint

assignment = SubFactory(Assignment)
28 changes: 28 additions & 0 deletions tests/unit/lms/models/assignment_checkpoint_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import pytest

from lms.models import AssignmentCheckpoint
from tests import factories


class TestAssignmentCheckpoint:
def test_it(self, db_session, assignment):
db_session.commit() # Ensure our objects have ids

checkpoint = AssignmentCheckpoint(assignment=assignment)
db_session.add(checkpoint)

db_session.flush()
assert checkpoint.assignment == assignment
assert checkpoint.assignment_id == assignment.id
# reveal_date is NULL until the instructor reveals the assignment.
assert checkpoint.reveal_date is None

def test_factory(self, db_session):
checkpoint = factories.AssignmentCheckpoint()

db_session.flush()
assert checkpoint.assignment_id is not None

@pytest.fixture
def assignment(self):
return factories.Assignment.create()
Loading