You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a new 'invalidated' status for submissions. Teachers can manually set this status, and invalidated submissions do not count towards point totals.
Why?
This was requested by teachers, as it is useful for e.g. invalidating AI submissions.
How?
Updated the submission database schema to include the new status. This includes a Django migration.
Also created two new API endpoints to invalidate the submission from status Ready and re-validate the submission back to status Ready. These endpoints are only accessible by teachers and only work if the submission status is initially Ready or Invalidated (respectively.
Also created buttons in the Inspect submission view where this endpoint can be accessed. The buttons also only show if the user is a teacher and the submission has correct status.
Added a couple new unit tests for the invalidations. Also tested in the UI that invalidation and re-validation works and affects the points totals correctly.
Did you test the changes in
Chrome
Firefox
This pull request cannot be tested in the browser.
Think of what is affected by these changes and could become broken
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Adds a new “invalidated” submission status that course teachers can toggle to exclude selected submissions from point totals and related reporting, addressing the “invalidate graded submission” request in #1479.
Changes:
Extends Submission status with INVALIDATED and updates relevant queryset/export/reporting filters to exclude invalidated submissions.
Adds teacher-only staff endpoints to invalidate/revalidate a submission and exposes corresponding buttons in the staff submission inspection UI.
Adds migration and UI/status translations (en/fi) for the new status and buttons.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
File
Description
locale/fi/LC_MESSAGES/django.po
Adds Finnish translations for new status + UI strings (needs additional staff flash message translations).
locale/en/LC_MESSAGES/django.po
Adds English translations for new status + UI strings (needs additional staff flash message translations).
exercise/urls.py
Registers new invalidate/revalidate staff endpoints for submissions.
exercise/tests.py
Adds unit test coverage for invalidate/revalidate permissions and status transitions.
We should figure out if we want to remove the invalidated status from exclude_errors() and make a separate exclude_invalidated() method. The main concern is if invalidating a submission should refund a submission attempt to the student.
The following explains the ramifications of keeping the new invalidated status in exclude_errors() (from Copilot):
INVALIDATED should be included only if an invalidated submission is meant to be treated as a non-counting attempt. It is not technically an error, and adding it to exclude_errors() has wider effects than just hiding it from results.
Submission limits in one_has_submissions() and no_submissions_left(): an invalidated attempt does not consume a submission slot, so the student may submit again.
ordinal_number(): invalidated submissions are not counted when generating the attempt number sent to the grader.
Batch approval: invalidated submissions are excluded from the submissions eligible for approval.
Any future caller of exclude_errors(): it will silently treat invalidation like a grader error or rejected submission.
It does not affect callers that use the default get_submissions_for_student() or query Submission directly. The CSV/API result queries already explicitly exclude INVALIDATED independently.
I would avoid putting it in exclude_errors() because the name is now misleading. Prefer separate semantics:
Then use both filters where appropriate. If the intended rule is that invalidation voids the attempt and permits a replacement submission, keep INVALIDATED in a separately named “non-counting submissions” filter. If invalidation is disciplinary and must still consume the attempt limit, it should not be part of the filter used by submission-limit calculations. The relevant shared filter is in submission_models.py, and the limit logic is in exercise_models.py.
I changed the filtering logic as above - now exclude_errors() doesn't include the invalidated status and there's a separate method exclude_invalidated(). Invalidated submissions also now consume an attempt. I also changed the form loading logic so that erroneous or invalidated submissions aren't loaded on questionnaire forms.
There's now a bunch of commits on this PR, maybe it's best for you to review this first @ihalaij1 and then I'll merge the commits?
Added new commit to also remove explicit final markings upon submission invalidation. This way the invalidated submissions will never count towards point totals. Also fixed the typo suggested by Copilot.
The question whether to count invalidated submissions towards statistics remains. Currently invalidated submissions, as they consume a submission, count towards submission counts in exercise points data, submission limit calculations, exercise satatistics endpoints and numbers of submitters. In my view these probably should count invalidated submissions as well, since they must consume a submission? What do you think @ihalaij1 or should for example the statistics endpoints exclude invalidated submissions?
Invalidated submissons don't currently count towards points, and csv:s/other aggregate submission exports.
Added new commit to also remove explicit final markings upon submission invalidation. This way the invalidated submissions will never count towards point totals. Also fixed the typo suggested by Copilot.
The question whether to count invalidated statistics towards submissions remains. Currently invalidated submissions, as they consume a submission, count towards submission counts in exercise points data, submission limit calculations, exercise satatistics endpoints and numbers of submitters. In my view these probably should count invalidated submissions as well, since they must consume a submission? What do you think @ihalaij1 or should for example the statistics endpoints exclude invalidated submissions?
Invalidated submissons don't currently count towards points, and csv:s/other aggregate submission exports.
That sounds fine. Did you check the regrade_exercises(..., 'all') function that Copilot mentions also regrades invalidated submissions and changes the status back to READY?
Whoops, didn't notice that one :) made new commit so that mass re-grading will always exclude invalidated submissions (whether re-grade type is incomplete or not) and also disabled regrading for single invalidated submissions. The button is also disabled in the inspect submission UI when inspecting an invalidated submission. Also added amention of this in the UI tooltip.
Filter invalidated submissions from confirmation subquery
exercise/api/csv/views.py:462
This excludes invalidated rows from the result query, but the confirmed subquery above still calls .passes() without any status filter. An invalidated passing submission in a confirm_the_level category can therefore continue to confirm sibling submissions and cause their points to appear in this export. Apply the same eligible-status filtering to that confirmation subquery.
Added one more commit to disable also manual regrading for invalidated submissions. This also prevents assistants from regrading invalidated submissions. Also noticed incorrect tooltip behavior, as previously if the best submission was invalidated, the second-best (which then determined the grade) had a tooltip in the inspect view saying incorrectly that another submission determines the grade. Fixed this and also added a tooltip saying this to invalidated submissions.
The second issue raised by Copilot doesn't make sense to me. If a submission is marked as best and manually graded, a teacher probably won't invalidate it in the first place. If for some reason the teacher wants to, though, (if there is suspicion of AI use etc.) the best flagging should also be removed, since invalidated submissions can't count towards the grade. If the submission is then revalidated (which is already 3 steps of a quite weird user flow that probably wouldn't happen under normal circumstances), the teacher should in my opinion manually regrade the submission again instead of the system carrying the flags somehow over. What do you think @ihalaij1
Added new commit to address the above issue. I also went through (with AI assistance) all possible API endpoints that could accidentally re-validate an invalidated submission and prevented that from happening by either filtering invalidated submissions out from batch API access points or causing an error with API access points that target only single submission instances.
Further, I added sanity checks to submission status-changing functions in submission_models.py to throw an error if an invalidated submission is tried to change into something else. This shouldn't happen now given the above changes but it further safeguards that the submissions won't be accidentally re-validated. Do you think this was a good idea @ihalaij1
Register the outer commit hook when merging savepoint cache changes.
This prevents stale point totals when the first cache write occurs
inside a nested transaction.
Add a regression test covering nested-only cache writes.
The reason will be displayed to describe this comment to others. Learn more.
This has been reviewed, fixed and re-reviewed multiple times by Copilot and me. It seems to work fine now. Possible remaining bugs should be minor and can be addressed later if found.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
What?
Add a new 'invalidated' status for submissions. Teachers can manually set this status, and invalidated submissions do not count towards point totals.
Why?
This was requested by teachers, as it is useful for e.g. invalidating AI submissions.
How?
Updated the submission database schema to include the new status. This includes a Django migration.
Also created two new API endpoints to invalidate the submission from status Ready and re-validate the submission back to status Ready. These endpoints are only accessible by teachers and only work if the submission status is initially Ready or Invalidated (respectively.
Also created buttons in the Inspect submission view where this endpoint can be accessed. The buttons also only show if the user is a teacher and the submission has correct status.
Fixes #1479
Testing
Remember to add or update unit tests for new features and changes.
What type of test did you run?
Added a couple new unit tests for the invalidations. Also tested in the UI that invalidation and re-validation works and affects the points totals correctly.
Did you test the changes in
Think of what is affected by these changes and could become broken
Translation
Programming style
Have you updated the README or other relevant documentation?
Is it Done?
Clean up your git commit history before submitting the pull request!