Repository navigation
fix: ignore a repeated submit of an already submitted attempt - #1365
Conversation
update_attempt_status treated submitted -> submitted like a first submission: it reset completed_at, set the credit requirement again, called the provider's stop_exam_attempt and sent another "Proctoring attempt submitted" email. A browser that repeats the submit request (frontend-lib-special-exams v5 can loop on it) sent learners hundreds or thousands of identical emails. Return early instead.
|
Thanks for the pull request, @Anas12091101! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
…t id Also return the attempt's own id from the early return, so callers get the same integer as for any other transition.
There was a problem hiding this comment.
🟡 Changes recommended
A provider timeout after the attempt is saved as submitted leaves submission side effects incomplete, while the new guard reports success on retries without reattempting the failed provider notification.
1 open issue
What changed in this PR
This PR makes repeated submissions of an already-submitted exam attempt return early, preventing duplicate learner emails and repeated submission side effects.
Changes:
- Logs and ignores repeated submissions in
update_attempt_status. - Adds a regression test checking that repeated submissions send only one email and preserve the completion timestamp.
| File | Description |
|---|---|
src/edx_proctoring/tests/test_email.py |
Tests email deduplication and completion-time preservation. |
src/edx_proctoring/api.py |
Adds an early return for already-submitted attempts. |
Review effort: Balanced

Description
Related: https://github.com/mitodl/hq/issues/14274
update_attempt_statustreatssubmitted→submittedlike a first submission: it movescompleted_atto now, sets the credit requirement again, calls the backend'sstop_exam_attemptand sends another "Proctoring attempt submitted" email. So every repeated submit request means another email to the learner.Repeated submits happen in normal use: when the exam timer runs out, the server has usually already timed the attempt out (which submits it) by the time the browser's
submitarrives, and every other tab showing the exam submits once more. Since frontend-lib-special-exams v5, a learner with a page of another course open when their time runs out sends that request several times a second until they close the tab (fixed separately in openedx/frontend-lib-special-exams#181). In production some learners got thousands of emails.This PR makes
update_attempt_statusreturn early, logging it, when an attempt that is alreadysubmittedis submitted again. The request still answers with the attempt id. Nothing changes for any other transition. The one visible difference outside the bug: re-saving an already submitted attempt in Django admin no longer re-sends the email or movescompleted_at.hq-14274-expire-loop.mp4
Testing
Automated:
pytest src/edx_proctoring/tests/test_email.py src/edx_proctoring/tests/test_views.py -k repeated_submit. Both fail onmaster(a second email, a movedcompleted_at) and pass here.Manual:
completed_at.Attempt attempt_id=<ATTEMPT_ID> for exam_id=... is already submitted. Ignoring the repeated submission.andcompleted_atis unchanged. Onmasterthe same call movescompleted_atand, for a proctored exam, sends a second "Proctoring attempt submitted" email.submit(277 times in about 35 seconds locally), and each one now logs "Ignoring the repeated submission".set_credit_requirement_statusis logged only once, for the real submission.Pre-Merge Checklist: