Merge pull request #37748 from asajjad2/areeb/last-publish-grade-calc-update

fix: calculate last_grade_publish_date for all unreleased subsections
This commit is contained in:
Peter Pinch
2026-02-24 10:27:06 -05:00
committed by GitHub
2 changed files with 25 additions and 9 deletions

View File

@@ -40,7 +40,7 @@ class _AssignmentBucket:
"""
assignment_type: str
num_total: int
last_grade_publish_date: datetime
last_grade_publish_date: datetime | None
scores: list[float] = field(default_factory=list)
visibilities: list[bool | None] = field(default_factory=list)
included: list[bool | None] = field(default_factory=list)
@@ -52,7 +52,7 @@ class _AssignmentBucket:
return cls(
assignment_type=assignment_type,
num_total=num_total,
last_grade_publish_date=now,
last_grade_publish_date=None,
scores=[0] * num_total,
visibilities=[None] * num_total,
included=[None] * num_total,
@@ -168,8 +168,13 @@ class _AssignmentTypeGradeAggregator:
bucket.add_subsection(score, is_visible, is_included)
visibilities_with_due_dates = [ShowCorrectness.PAST_DUE, ShowCorrectness.NEVER_BUT_INCLUDE_GRADE]
if subsection_grade.show_correctness in visibilities_with_due_dates:
if subsection_grade.due and subsection_grade.due > bucket.last_grade_publish_date:
bucket.last_grade_publish_date = subsection_grade.due
if subsection_grade.due:
should_update = (
bucket.last_grade_publish_date is None or
subsection_grade.due > bucket.last_grade_publish_date
)
if should_update:
bucket.last_grade_publish_date = subsection_grade.due
def build_results(self) -> dict:
"""Apply drops, compute averages, and return aggregated results and total grade."""

View File

@@ -15,7 +15,7 @@ from datetime import datetime, timedelta, timezone
from types import SimpleNamespace
def _make_subsection(fmt, earned, possible, show_corr, *, due_delta_days=None):
def _make_subsection(fmt, earned, possible, show_corr, *, due_delta_days=None, is_included=True):
"""Build a lightweight subsection object for testing aggregation scenarios."""
graded_total = SimpleNamespace(earned=earned, possible=possible)
due = None
@@ -27,7 +27,7 @@ def _make_subsection(fmt, earned, possible, show_corr, *, due_delta_days=None):
graded_total=graded_total,
show_correctness=show_corr,
due=due,
show_grades=lambda staff: True,
show_grades=lambda staff: is_included,
)
@@ -63,10 +63,10 @@ _AGGREGATION_SCENARIOS = [
'past_due_mixed_visibility',
{'type': 'Lab', 'weight': 1.0, 'drop_count': 0, 'min_count': 2, 'short_label': 'LB'},
[
_make_subsection('Lab', 0.8, 1, ShowCorrectness.PAST_DUE, due_delta_days=-1),
_make_subsection('Lab', 0.2, 1, ShowCorrectness.PAST_DUE, due_delta_days=+3),
_make_subsection('Lab', 0.8, 1, ShowCorrectness.PAST_DUE, due_delta_days=-1, is_included=True),
_make_subsection('Lab', 0.2, 1, ShowCorrectness.PAST_DUE, due_delta_days=+3, is_included=True),
],
{'avg': 0.4, 'weighted': 0.4, 'hidden': 'some', 'final': 0.5},
{'avg': 0.4, 'weighted': 0.4, 'hidden': 'some', 'final': 0.5, 'last_grade_publish_date_days': 3},
),
(
'drop_lowest_keeps_high_scores',
@@ -79,6 +79,14 @@ _AGGREGATION_SCENARIOS = [
],
{'avg': 1.0, 'weighted': 1.0, 'hidden': 'none', 'final': 1.0},
),
(
'unreleased_with_future_due_date',
{'type': 'Midterm', 'weight': 1.0, 'drop_count': 0, 'min_count': 1, 'short_label': 'MT'},
[
_make_subsection('Midterm', 0.5, 1, ShowCorrectness.PAST_DUE, due_delta_days=7, is_included=False),
],
{'avg': 0.0, 'weighted': 0.0, 'hidden': 'all', 'final': 0.0, 'last_grade_publish_date_days': 7},
),
]
@@ -180,3 +188,6 @@ class ProgressApiTests(TestCase):
assert row['weighted_grade'] == expected['weighted']
assert row['has_hidden_contribution'] == expected['hidden']
assert row['num_droppable'] == policy['drop_count']
assert (row['last_grade_publish_date'] is not None) == (
'last_grade_publish_date_days' in expected
)