edx-proctoring 5.2.0__tar.gz → 6.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. edx-proctoring-5.2.0/edx_proctoring.egg-info/PKG-INFO → edx_proctoring-6.1.2/CHANGELOG.rst +64 -130
  2. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/PKG-INFO +95 -4
  3. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/__init__.py +1 -1
  4. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/admin.py +2 -2
  5. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/api.py +123 -4
  6. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/apps.py +1 -0
  7. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/backend.py +6 -0
  8. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/rest.py +43 -14
  9. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/software_secure.py +4 -1
  10. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/tests/test_rest.py +111 -0
  11. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/tests/test_software_secure.py +10 -1
  12. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/exceptions.py +19 -0
  13. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/handlers.py +50 -49
  14. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/management/commands/reset_attempts.py +13 -0
  15. edx_proctoring-6.1.2/edx_proctoring/management/commands/tests/test_reset_attempts.py +153 -0
  16. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/management/commands/tests/test_update_attempt_status_from_review.py +9 -3
  17. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/models.py +12 -3
  18. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_api.py +143 -0
  19. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_handlers.py +0 -9
  20. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_models.py +1 -1
  21. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_reviews.py +247 -1
  22. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_views.py +8 -3
  23. edx-proctoring-5.2.0/CHANGELOG.rst → edx_proctoring-6.1.2/edx_proctoring.egg-info/PKG-INFO +221 -0
  24. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/base.txt +61 -64
  25. edx_proctoring-6.1.2/requirements/celery50.txt +9 -0
  26. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/ci.txt +17 -13
  27. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/common_constraints.txt +1 -8
  28. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/constraints.txt +0 -3
  29. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/dev.txt +59 -52
  30. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/pip-tools.txt +10 -8
  31. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/pip.txt +4 -2
  32. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/quality.txt +37 -35
  33. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/test.txt +78 -78
  34. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/setup.py +0 -2
  35. edx-proctoring-5.2.0/edx_proctoring/management/commands/tests/test_reset_attempts.py +0 -72
  36. edx-proctoring-5.2.0/requirements/celery50.txt +0 -9
  37. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/LICENSE.txt +0 -0
  38. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/MANIFEST.in +0 -0
  39. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/README.rst +0 -0
  40. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/LICENSE.txt +0 -0
  41. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/README.txt +0 -0
  42. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/__init__.py +0 -0
  43. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/mock.py +0 -0
  44. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/null.py +0 -0
  45. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/tests/__init__.py +0 -0
  46. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/tests/test_backend.py +0 -0
  47. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/backends/tests/test_review_payload.py +0 -0
  48. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/callbacks.py +0 -0
  49. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/constants.py +0 -0
  50. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/decorators.py +0 -0
  51. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/instructor_dashboard_exam_urls.py +0 -0
  52. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ar/LC_MESSAGES/django.mo +0 -0
  53. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ar/LC_MESSAGES/django.po +0 -0
  54. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ar/LC_MESSAGES/djangojs.mo +0 -0
  55. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ar/LC_MESSAGES/djangojs.po +0 -0
  56. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ca/LC_MESSAGES/django.mo +0 -0
  57. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ca/LC_MESSAGES/django.po +0 -0
  58. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ca/LC_MESSAGES/djangojs.mo +0 -0
  59. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ca/LC_MESSAGES/djangojs.po +0 -0
  60. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/config.yaml +0 -0
  61. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/cs/LC_MESSAGES/django.mo +0 -0
  62. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/cs/LC_MESSAGES/django.po +0 -0
  63. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/cs/LC_MESSAGES/djangojs.mo +0 -0
  64. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/cs/LC_MESSAGES/djangojs.po +0 -0
  65. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/de_DE/LC_MESSAGES/django.mo +0 -0
  66. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/de_DE/LC_MESSAGES/django.po +0 -0
  67. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/de_DE/LC_MESSAGES/djangojs.mo +0 -0
  68. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/de_DE/LC_MESSAGES/djangojs.po +0 -0
  69. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/el/LC_MESSAGES/django.mo +0 -0
  70. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/el/LC_MESSAGES/django.po +0 -0
  71. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/el/LC_MESSAGES/djangojs.mo +0 -0
  72. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/el/LC_MESSAGES/djangojs.po +0 -0
  73. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/en/LC_MESSAGES/django.mo +0 -0
  74. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/en/LC_MESSAGES/django.po +0 -0
  75. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/en/LC_MESSAGES/djangojs.mo +0 -0
  76. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/en/LC_MESSAGES/djangojs.po +0 -0
  77. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/eo/LC_MESSAGES/django.mo +0 -0
  78. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/eo/LC_MESSAGES/django.po +0 -0
  79. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/eo/LC_MESSAGES/djangojs.mo +0 -0
  80. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/eo/LC_MESSAGES/djangojs.po +0 -0
  81. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/es_419/LC_MESSAGES/django.mo +0 -0
  82. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/es_419/LC_MESSAGES/django.po +0 -0
  83. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/es_419/LC_MESSAGES/djangojs.mo +0 -0
  84. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/es_419/LC_MESSAGES/djangojs.po +0 -0
  85. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/es_AR/LC_MESSAGES/django.mo +0 -0
  86. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/es_AR/LC_MESSAGES/django.po +0 -0
  87. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/es_AR/LC_MESSAGES/djangojs.mo +0 -0
  88. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/es_AR/LC_MESSAGES/djangojs.po +0 -0
  89. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/es_ES/LC_MESSAGES/django.mo +0 -0
  90. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/es_ES/LC_MESSAGES/django.po +0 -0
  91. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/es_ES/LC_MESSAGES/djangojs.mo +0 -0
  92. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/es_ES/LC_MESSAGES/djangojs.po +0 -0
  93. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/eu_ES/LC_MESSAGES/django.mo +0 -0
  94. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/eu_ES/LC_MESSAGES/django.po +0 -0
  95. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/eu_ES/LC_MESSAGES/djangojs.mo +0 -0
  96. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/eu_ES/LC_MESSAGES/djangojs.po +0 -0
  97. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
  98. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/fa_IR/LC_MESSAGES/django.po +0 -0
  99. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/fa_IR/LC_MESSAGES/djangojs.mo +0 -0
  100. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/fa_IR/LC_MESSAGES/djangojs.po +0 -0
  101. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/fr/LC_MESSAGES/django.mo +0 -0
  102. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/fr/LC_MESSAGES/django.po +0 -0
  103. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/fr/LC_MESSAGES/djangojs.mo +0 -0
  104. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/fr/LC_MESSAGES/djangojs.po +0 -0
  105. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/fr_CA/LC_MESSAGES/django.mo +0 -0
  106. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/fr_CA/LC_MESSAGES/django.po +0 -0
  107. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/fr_CA/LC_MESSAGES/djangojs.mo +0 -0
  108. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/fr_CA/LC_MESSAGES/djangojs.po +0 -0
  109. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/he/LC_MESSAGES/django.mo +0 -0
  110. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/he/LC_MESSAGES/django.po +0 -0
  111. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/he/LC_MESSAGES/djangojs.mo +0 -0
  112. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/he/LC_MESSAGES/djangojs.po +0 -0
  113. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/id/LC_MESSAGES/django.mo +0 -0
  114. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/id/LC_MESSAGES/django.po +0 -0
  115. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/id/LC_MESSAGES/djangojs.mo +0 -0
  116. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/id/LC_MESSAGES/djangojs.po +0 -0
  117. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/it_IT/LC_MESSAGES/django.mo +0 -0
  118. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/it_IT/LC_MESSAGES/django.po +0 -0
  119. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/it_IT/LC_MESSAGES/djangojs.mo +0 -0
  120. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/it_IT/LC_MESSAGES/djangojs.po +0 -0
  121. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ja_JP/LC_MESSAGES/djangojs.mo +0 -0
  122. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ja_JP/LC_MESSAGES/djangojs.po +0 -0
  123. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ka/LC_MESSAGES/django.mo +0 -0
  124. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ka/LC_MESSAGES/django.po +0 -0
  125. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ka/LC_MESSAGES/djangojs.mo +0 -0
  126. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ka/LC_MESSAGES/djangojs.po +0 -0
  127. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/lv/LC_MESSAGES/django.mo +0 -0
  128. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/lv/LC_MESSAGES/django.po +0 -0
  129. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/lv/LC_MESSAGES/djangojs.mo +0 -0
  130. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/lv/LC_MESSAGES/djangojs.po +0 -0
  131. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/mn/LC_MESSAGES/django.mo +0 -0
  132. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/mn/LC_MESSAGES/django.po +0 -0
  133. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/mn/LC_MESSAGES/djangojs.mo +0 -0
  134. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/mn/LC_MESSAGES/djangojs.po +0 -0
  135. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/nb/LC_MESSAGES/django.mo +0 -0
  136. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/nb/LC_MESSAGES/django.po +0 -0
  137. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/nb/LC_MESSAGES/djangojs.mo +0 -0
  138. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/nb/LC_MESSAGES/djangojs.po +0 -0
  139. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/pl/LC_MESSAGES/django.mo +0 -0
  140. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/pl/LC_MESSAGES/django.po +0 -0
  141. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/pl/LC_MESSAGES/djangojs.mo +0 -0
  142. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/pl/LC_MESSAGES/djangojs.po +0 -0
  143. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  144. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/pt_PT/LC_MESSAGES/django.po +0 -0
  145. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/pt_PT/LC_MESSAGES/djangojs.mo +0 -0
  146. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/pt_PT/LC_MESSAGES/djangojs.po +0 -0
  147. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ro/LC_MESSAGES/django.mo +0 -0
  148. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ro/LC_MESSAGES/django.po +0 -0
  149. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ro/LC_MESSAGES/djangojs.mo +0 -0
  150. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ro/LC_MESSAGES/djangojs.po +0 -0
  151. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ru/LC_MESSAGES/django.mo +0 -0
  152. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ru/LC_MESSAGES/django.po +0 -0
  153. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ru/LC_MESSAGES/djangojs.mo +0 -0
  154. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/ru/LC_MESSAGES/djangojs.po +0 -0
  155. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/sw_KE/LC_MESSAGES/django.mo +0 -0
  156. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/sw_KE/LC_MESSAGES/django.po +0 -0
  157. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/sw_KE/LC_MESSAGES/djangojs.mo +0 -0
  158. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/sw_KE/LC_MESSAGES/djangojs.po +0 -0
  159. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/tr_TR/LC_MESSAGES/django.mo +0 -0
  160. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/tr_TR/LC_MESSAGES/django.po +0 -0
  161. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/tr_TR/LC_MESSAGES/djangojs.mo +0 -0
  162. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/tr_TR/LC_MESSAGES/djangojs.po +0 -0
  163. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/uk/LC_MESSAGES/django.mo +0 -0
  164. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/uk/LC_MESSAGES/django.po +0 -0
  165. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/uk/LC_MESSAGES/djangojs.mo +0 -0
  166. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/uk/LC_MESSAGES/djangojs.po +0 -0
  167. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/zh_CN/LC_MESSAGES/django.mo +0 -0
  168. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/zh_CN/LC_MESSAGES/django.po +0 -0
  169. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/zh_CN/LC_MESSAGES/djangojs.mo +0 -0
  170. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/locale/zh_CN/LC_MESSAGES/djangojs.po +0 -0
  171. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/management/__init__.py +0 -0
  172. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/management/commands/__init__.py +0 -0
  173. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/management/commands/set_attempt_status.py +0 -0
  174. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/management/commands/set_is_attempt_active.py +0 -0
  175. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/management/commands/tests/__init__.py +0 -0
  176. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/management/commands/tests/test_set_attempt_status.py +0 -0
  177. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/management/commands/tests/test_set_is_attempt_active.py +0 -0
  178. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/management/commands/tests/test_update_attempts_for_exam.py +0 -0
  179. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/management/commands/update_attempt_status_from_review.py +0 -0
  180. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/management/commands/update_attempts_for_exam.py +0 -0
  181. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0001_initial.py +0 -0
  182. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0002_proctoredexamstudentattempt_is_status_acknowledged.py +0 -0
  183. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0003_auto_20160101_0525.py +0 -0
  184. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0004_auto_20160201_0523.py +0 -0
  185. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0005_proctoredexam_hide_after_due.py +0 -0
  186. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0006_allowed_time_limit_mins.py +0 -0
  187. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0007_proctoredexam_backend.py +0 -0
  188. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0008_auto_20181116_1551.py +0 -0
  189. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0009_proctoredexamreviewpolicy_remove_rules.py +0 -0
  190. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0010_update_backend.py +0 -0
  191. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0011_allow_multiple_attempts.py +0 -0
  192. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0012_proctoredexamstudentattempt_time_remaining_seconds.py +0 -0
  193. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0013_proctoredexamsoftwaresecurereview_is_active_attempt.py +0 -0
  194. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0014_add_is_resumable_to_proctoredexamstudentattempt.py +0 -0
  195. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0015_rm_proctoredexamstudentattempt_ips.py +0 -0
  196. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0016_nullable_proctoredexamstudentattempt_name.py +0 -0
  197. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0017_rm_proctoredexamstudentattempt_name.py +0 -0
  198. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0018_historicalproctoredexamstudentattempt.py +0 -0
  199. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0019_proctoredexamsoftwaresecurereview_encrypted_video_url.py +0 -0
  200. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0020_auto_20211028_1915.py +0 -0
  201. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0021_auto_20211029_1353.py +0 -0
  202. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0022_proctoredexamstudentattempt_add_readytoresume_resumed.py +0 -0
  203. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0023_historicalproctoredexam.py +0 -0
  204. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0024_delete_proctoredexamstudentattempthistory.py +0 -0
  205. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/0025_auto_20230727_2112.py +0 -0
  206. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/migrations/__init__.py +0 -0
  207. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/rules.py +0 -0
  208. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/runtime.py +0 -0
  209. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/scripts/obscure_user.py +0 -0
  210. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/scripts/version_check.py +0 -0
  211. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/serializers.py +0 -0
  212. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/services.py +0 -0
  213. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/settings/__init__.py +0 -0
  214. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/settings/common.py +0 -0
  215. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/settings/production.py +0 -0
  216. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/settings/test.py +0 -0
  217. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/signals.py +0 -0
  218. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/index.js +0 -0
  219. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/clippy.swf +0 -0
  220. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/close-modal-hover.png +0 -0
  221. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/close-modal.png +0 -0
  222. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/collections/proctored_exam_allowance_collection.js +0 -0
  223. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/collections/proctored_exam_attempt_grouped_collection.js +0 -0
  224. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/collections/proctored_exam_collection.js +0 -0
  225. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/collections/proctored_exam_onboarding_collection.js +0 -0
  226. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/dropdown.js +0 -0
  227. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/exam_action_handler.js +0 -0
  228. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/models/learner_onboarding_model.js +0 -0
  229. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/models/proctored_exam_allowance_model.js +0 -0
  230. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/models/proctored_exam_attempt_model.js +0 -0
  231. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/models/proctored_exam_bulk_allowance_model.js +0 -0
  232. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/models/proctored_exam_model.js +0 -0
  233. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/proctored_app.js +0 -0
  234. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/vendor/backbone.js +0 -0
  235. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/vendor/date.js +0 -0
  236. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/vendor/i18n.js +0 -0
  237. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/vendor/jquery.js +0 -0
  238. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/vendor/underscore.js +0 -0
  239. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/views/Backbone.ModalDialog.js +0 -0
  240. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/views/proctored_exam_add_bulk_allowance_view.js +0 -0
  241. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/views/proctored_exam_allowance_view.js +0 -0
  242. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/views/proctored_exam_attempt_view.js +0 -0
  243. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/views/proctored_exam_edit_allowance_view.js +0 -0
  244. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/views/proctored_exam_info.js +0 -0
  245. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/views/proctored_exam_instructor_launch.js +0 -0
  246. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/views/proctored_exam_onboarding_view.js +0 -0
  247. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/js/views/proctored_exam_view.js +0 -0
  248. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/templates/add-new-bulk-allowance.underscore +0 -0
  249. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/templates/course_grouped_allowances.underscore +0 -0
  250. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/templates/dashboard.underscore +0 -0
  251. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/templates/edit-allowance.underscore +0 -0
  252. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/templates/proctored-exam-info.underscore +0 -0
  253. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/templates/student-onboarding-status.underscore +0 -0
  254. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/static/proctoring/templates/student-proctored-exam-attempts-grouped.underscore +0 -0
  255. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/statuses.py +0 -0
  256. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/common/waiting_banner.html +0 -0
  257. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/emails/proctoring_attempt_satisfactory_email.html +0 -0
  258. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/emails/proctoring_attempt_submitted_email.html +0 -0
  259. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/emails/proctoring_attempt_unsatisfactory_email.html +0 -0
  260. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/onboarding_exam/entrance.html +0 -0
  261. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/onboarding_exam/error.html +0 -0
  262. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/onboarding_exam/ready_to_submit.html +0 -0
  263. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/onboarding_exam/rejected.html +0 -0
  264. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/onboarding_exam/submitted.html +0 -0
  265. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/onboarding_exam/verified.html +0 -0
  266. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/practice_exam/entrance.html +0 -0
  267. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/practice_exam/error.html +0 -0
  268. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/practice_exam/submitted.html +0 -0
  269. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/confirm-decline.html +0 -0
  270. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/entrance.html +0 -0
  271. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/error.html +0 -0
  272. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/error_modal.html +0 -0
  273. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/error_wrong_browser.html +0 -0
  274. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/expired.html +0 -0
  275. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/failed-prerequisites.html +0 -0
  276. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/footer.html +0 -0
  277. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/inactive_account.html +0 -0
  278. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/instructions.html +0 -0
  279. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/onboarding_error.html +0 -0
  280. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/pending-prerequisites.html +0 -0
  281. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/proctoring_launch_callback.html +0 -0
  282. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/proctoring_opt_out_button.html +0 -0
  283. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/ready_to_resume.html +0 -0
  284. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/ready_to_start.html +0 -0
  285. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/ready_to_submit.html +0 -0
  286. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/rejected.html +0 -0
  287. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/submitted.html +0 -0
  288. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/verified.html +0 -0
  289. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/proctored_exam/visit_exam_content.html +0 -0
  290. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/timed_exam/entrance.html +0 -0
  291. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/timed_exam/expired.html +0 -0
  292. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/timed_exam/footer.html +0 -0
  293. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/timed_exam/ready_to_submit.html +0 -0
  294. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/templates/timed_exam/submitted.html +0 -0
  295. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/__init__.py +0 -0
  296. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_email.py +0 -0
  297. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_mfe_views.py +0 -0
  298. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_serializer.py +0 -0
  299. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_services.py +0 -0
  300. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_student_view.py +0 -0
  301. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_utils/factories.py +0 -0
  302. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_utils/test_utils.py +0 -0
  303. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_utils/utils.py +0 -0
  304. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/tests/test_workerconfig.py +0 -0
  305. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/urls.py +0 -0
  306. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/utils.py +0 -0
  307. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring/views.py +0 -0
  308. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring.egg-info/SOURCES.txt +0 -0
  309. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring.egg-info/dependency_links.txt +0 -0
  310. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring.egg-info/entry_points.txt +0 -0
  311. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring.egg-info/not-zip-safe +0 -0
  312. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring.egg-info/requires.txt +14 -14
  313. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/edx_proctoring.egg-info/top_level.txt +0 -0
  314. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/base.in +0 -0
  315. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/ci.in +0 -0
  316. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/dev.in +0 -0
  317. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/pip-tools.in +0 -0
  318. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/pip.in +0 -0
  319. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/quality.in +0 -0
  320. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/requirements/test.in +0 -0
  321. {edx-proctoring-5.2.0 → edx_proctoring-6.1.2}/setup.cfg +0 -0
@@ -1,133 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: edx-proctoring
3
- Version: 5.2.0
4
- Summary: Proctoring subsystem for Open edX
5
- Home-page: https://github.com/openedx/edx-proctoring
6
- Author: edX
7
- Author-email: oscm@edx.org
8
- License: AGPL 3.0
9
- Keywords: Django edx
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Framework :: Django
12
- Classifier: Framework :: Django :: 4.2
13
- Classifier: Framework :: Django :: 5.2
14
- Classifier: Intended Audience :: Developers
15
- Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
16
- Classifier: Natural Language :: English
17
- Classifier: Programming Language :: Python :: 3.8
18
- Classifier: Programming Language :: Python :: 3.11
19
- Classifier: Programming Language :: Python :: 3.12
20
- License-File: LICENSE.txt
21
-
22
- edx-proctoring
23
- ==============
24
-
25
- .. image:: https://img.shields.io/pypi/v/edx-proctoring.svg
26
- :target: https://pypi.python.org/pypi/edx-proctoring/
27
- :alt: PyPI
28
-
29
- .. image:: https://github.com/openedx/edx-proctoring/workflows/Python%20CI/badge.svg
30
- :target: https://github.com/openedx/edx-proctoring/actions?query=workflow%3A%22Python+CI%22
31
- :alt: Python CI
32
-
33
- .. image:: https://codecov.io/gh/edx/edx-proctoring/branch/master/graph/badge.svg
34
- :target: https://codecov.io/gh/edx/edx-proctoring
35
- :alt: Codecov
36
-
37
- .. image:: https://img.shields.io/pypi/pyversions/edx-proctoring.svg
38
- :target: https://pypi.python.org/pypi/edx-proctoring/
39
- :alt: Supported Python versions
40
-
41
- .. image:: https://img.shields.io/github/license/edx/django-component-views.svg
42
- :target: https://github.com/openedx/edx-proctoring/blob/master/LICENSE.txt
43
- :alt: License
44
-
45
- This is the exam proctoring subsystem for the Open edX platform.
46
-
47
- Overview
48
- --------
49
-
50
- Proctored exams are exams with time limits that learners complete while online
51
- proctoring software monitors their computers and behavior for activity that
52
- might be evidence of cheating. This Python library provides the proctoring
53
- implementation used by Open edX.
54
-
55
- Documentation
56
- -------------
57
-
58
- For authoring documentation, see `Including Proctored Exams In Your Course`_.
59
-
60
- Installation
61
- ------------
62
-
63
- To install edx-proctoring:
64
-
65
- mkvirtualenv edx-proctoring
66
-
67
- make install
68
-
69
- If you want to run tests, first setup requirements. Note that JS tests
70
- require Node 10, nvm install v10 if you are on a different version so
71
- that gulp is installed for v10:
72
-
73
- nvm install v10
74
-
75
- make requirements-test
76
-
77
- Run tests:
78
-
79
- make test
80
-
81
- Run just python tests:
82
-
83
- make test-python
84
-
85
- Run just JS tests:
86
-
87
- make test-js
88
-
89
- For a full list of Make targets:
90
-
91
- make help
92
-
93
- Developing
94
- -------------
95
-
96
- See the `developer guide`_ for configuration, devstack and sandbox setup, and other developer concerns.
97
-
98
- .. _developer guide: ./docs/developing.rst
99
-
100
- License
101
- -------
102
-
103
- The code in this repository is licensed under the AGPL 3.0 unless
104
- otherwise noted.
105
-
106
- Please see ``LICENSE.txt`` for details.
107
-
108
- How To Contribute
109
- -----------------
110
-
111
- Contributions are very welcome.
112
-
113
- Please read `How To Contribute <https://github.com/openedx/.github/blob/master/CONTRIBUTING.md>`_ for details.
114
-
115
-
116
- Reporting Security Issues
117
- -------------------------
118
-
119
- Please do not report security issues in public. Please email security@openedx.org.
120
-
121
- Getting Help
122
- ------------
123
-
124
- Have a question about this repository, or about Open edX in general? Please
125
- refer to this `list of resources`_ if you need any assistance.
126
-
127
- .. _list of resources: https://open.edx.org/getting-help
128
- .. _Including Proctored Exams In Your Course: https://docs.openedx.org/en/latest/educators/concepts/proctored_exams/proctored_exams_overview.html
129
-
130
-
131
1
  Change Log
132
2
  ----------
133
3
 
@@ -143,6 +13,70 @@ Change Log
143
13
 
144
14
  Unreleased
145
15
  ~~~~~~~~~~
16
+
17
+ [6.1.2] - 2026-10-16
18
+ ~~~~~~~~~~~~~~~~~~~~
19
+
20
+ * A passing review from the proctoring provider no longer marks an attempt ``verified``
21
+ when the learner never submitted it, for example because the proctoring session ended
22
+ while the attempt was still ``ready_to_start``. Such attempts are put into ``error``
23
+ instead, so the learner is told to retry and staff can see the exam was not completed.
24
+ * A review that arrives for an attempt that has since been reset (``onboarding_reset``)
25
+ no longer changes that attempt's status.
26
+ * A timeout treated as a submission (``ALLOW_TIMED_OUT_STATE`` off) now always records
27
+ ``completed_at``, falling back to the current time when no timeout timestamp is passed,
28
+ as ``mark_exam_attempt_timeout`` does. As with any other submission, this also marks
29
+ the exam complete in the completion service.
30
+ * **Existing data:** attempts that are already ``verified`` but have no ``completed_at``
31
+ (they were verified without being submitted) move to ``error`` the next time their
32
+ review is processed, whether by a re-delivered passing review or by the
33
+ ``update_attempt_status_from_review`` management command, and their credit
34
+ requirement changes to ``failed``. Run that command over a review date range to apply
35
+ this to existing attempts on purpose.
36
+
37
+ [6.1.1] - 2026-10-14
38
+ ~~~~~~~~~~~~~~~~~~~~
39
+
40
+ * Fix PyPI publish workflow: use ``python -m build`` instead of ``setup.py sdist
41
+ bdist_wheel``, which broke on Python 3.12 because ``setuptools`` is no longer
42
+ bundled and was not installed by the workflow.
43
+
44
+ [6.1.0] - 2026-09-01
45
+ ~~~~~~~~~~~~~~~~~~~~
46
+
47
+ * **Behaviour change for API consumers:** the instructor reset endpoint now responds with
48
+ **502** when the proctoring provider cannot remove the attempt, where it previously
49
+ returned a 500 or appeared to succeed. Anything driving the instructor dashboard reset
50
+ should handle that status.
51
+ * Make resetting a proctored exam attempt fail gracefully when the proctoring provider
52
+ is unavailable, instead of an unhandled 500 or a stuck reset:
53
+
54
+ * Add an explicit request timeout to the REST proctoring backend (default 30s,
55
+ overridable per backend via a ``timeout`` key in ``PROCTORING_BACKENDS``), applied to
56
+ every outbound provider request, so a slow provider cannot hang the request
57
+ indefinitely.
58
+ * Call the provider from ``remove_exam_attempt`` **before** the local delete (rather
59
+ than from the ``pre_delete`` signal). If the provider is unreachable/errors, a typed
60
+ ``BackendProviderCannotRemoveAttempt`` (HTTP 502) is raised before anything is
61
+ deleted, so the caller can show the instructor a descriptive message and the local
62
+ attempt is preserved. Because the failure no longer originates inside ``delete()``'s
63
+ ``pre_delete`` signal, it does not leave the DB connection in a needs-rollback state,
64
+ so a partially-failed multi-attempt reset can be retried in the same request.
65
+ * On removal, a provider ``404`` (the attempt is already gone upstream) is treated as
66
+ success so a retried, partially-failed reset converges; any other HTTP error is raised
67
+ so the local attempt is kept for a later retry instead of being silently dropped.
68
+ * Route the ``reset_attempts`` management command through the same provider-removal
69
+ path, since provider cleanup no longer happens in the ``pre_delete`` signal.
70
+ * Learner-initiated practice/onboarding resets stay best-effort, so a provider problem
71
+ does not block a learner from retrying their own onboarding exam.
72
+ * Deleting a ``ProctoredExam`` no longer notifies the proctoring provider about the
73
+ attempts that are cascade-deleted with it. Provider removal now happens at the three
74
+ call sites that reset attempts rather than in a ``pre_delete`` signal, so attempts
75
+ removed this way (a staff delete from Django admin) are left on the provider.
76
+
77
+ [5.2.1] - 2025-12-05
78
+ * Remove all references to Proctortrack
79
+
146
80
  [5.2.0] - 2025-04-22
147
81
  * adds support for django 5.2
148
82
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: edx-proctoring
3
- Version: 5.2.0
3
+ Version: 6.1.2
4
4
  Summary: Proctoring subsystem for Open edX
5
5
  Home-page: https://github.com/openedx/edx-proctoring
6
6
  Author: edX
@@ -14,10 +14,37 @@ Classifier: Framework :: Django :: 5.2
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
16
16
  Classifier: Natural Language :: English
17
- Classifier: Programming Language :: Python :: 3.8
18
- Classifier: Programming Language :: Python :: 3.11
19
17
  Classifier: Programming Language :: Python :: 3.12
20
18
  License-File: LICENSE.txt
19
+ Requires-Dist: event-tracking>=0.2.5
20
+ Requires-Dist: jsonfield
21
+ Requires-Dist: edx-opaque-keys>=0.4
22
+ Requires-Dist: edx-when>=0.1.3
23
+ Requires-Dist: Django>=2.2
24
+ Requires-Dist: django-webpack-loader>=0.6.0
25
+ Requires-Dist: rules
26
+ Requires-Dist: django-ipware>=1.1.0
27
+ Requires-Dist: djangorestframework
28
+ Requires-Dist: django-simple-history
29
+ Requires-Dist: pycryptodomex>=3.4.7
30
+ Requires-Dist: pyjwt[crypto]>=2.1.0
31
+ Requires-Dist: edx-drf-extensions
32
+ Requires-Dist: django-crum
33
+ Requires-Dist: python-dateutil>=2.1
34
+ Requires-Dist: pytz>=2018
35
+ Requires-Dist: django-model-utils
36
+ Requires-Dist: django-waffle
37
+ Requires-Dist: edx-rest-api-client>=1.9.2
38
+ Dynamic: author
39
+ Dynamic: author-email
40
+ Dynamic: classifier
41
+ Dynamic: description
42
+ Dynamic: home-page
43
+ Dynamic: keywords
44
+ Dynamic: license
45
+ Dynamic: license-file
46
+ Dynamic: requires-dist
47
+ Dynamic: summary
21
48
 
22
49
  edx-proctoring
23
50
  ==============
@@ -143,6 +170,70 @@ Change Log
143
170
 
144
171
  Unreleased
145
172
  ~~~~~~~~~~
173
+
174
+ [6.1.2] - 2026-10-16
175
+ ~~~~~~~~~~~~~~~~~~~~
176
+
177
+ * A passing review from the proctoring provider no longer marks an attempt ``verified``
178
+ when the learner never submitted it, for example because the proctoring session ended
179
+ while the attempt was still ``ready_to_start``. Such attempts are put into ``error``
180
+ instead, so the learner is told to retry and staff can see the exam was not completed.
181
+ * A review that arrives for an attempt that has since been reset (``onboarding_reset``)
182
+ no longer changes that attempt's status.
183
+ * A timeout treated as a submission (``ALLOW_TIMED_OUT_STATE`` off) now always records
184
+ ``completed_at``, falling back to the current time when no timeout timestamp is passed,
185
+ as ``mark_exam_attempt_timeout`` does. As with any other submission, this also marks
186
+ the exam complete in the completion service.
187
+ * **Existing data:** attempts that are already ``verified`` but have no ``completed_at``
188
+ (they were verified without being submitted) move to ``error`` the next time their
189
+ review is processed, whether by a re-delivered passing review or by the
190
+ ``update_attempt_status_from_review`` management command, and their credit
191
+ requirement changes to ``failed``. Run that command over a review date range to apply
192
+ this to existing attempts on purpose.
193
+
194
+ [6.1.1] - 2026-10-14
195
+ ~~~~~~~~~~~~~~~~~~~~
196
+
197
+ * Fix PyPI publish workflow: use ``python -m build`` instead of ``setup.py sdist
198
+ bdist_wheel``, which broke on Python 3.12 because ``setuptools`` is no longer
199
+ bundled and was not installed by the workflow.
200
+
201
+ [6.1.0] - 2026-09-01
202
+ ~~~~~~~~~~~~~~~~~~~~
203
+
204
+ * **Behaviour change for API consumers:** the instructor reset endpoint now responds with
205
+ **502** when the proctoring provider cannot remove the attempt, where it previously
206
+ returned a 500 or appeared to succeed. Anything driving the instructor dashboard reset
207
+ should handle that status.
208
+ * Make resetting a proctored exam attempt fail gracefully when the proctoring provider
209
+ is unavailable, instead of an unhandled 500 or a stuck reset:
210
+
211
+ * Add an explicit request timeout to the REST proctoring backend (default 30s,
212
+ overridable per backend via a ``timeout`` key in ``PROCTORING_BACKENDS``), applied to
213
+ every outbound provider request, so a slow provider cannot hang the request
214
+ indefinitely.
215
+ * Call the provider from ``remove_exam_attempt`` **before** the local delete (rather
216
+ than from the ``pre_delete`` signal). If the provider is unreachable/errors, a typed
217
+ ``BackendProviderCannotRemoveAttempt`` (HTTP 502) is raised before anything is
218
+ deleted, so the caller can show the instructor a descriptive message and the local
219
+ attempt is preserved. Because the failure no longer originates inside ``delete()``'s
220
+ ``pre_delete`` signal, it does not leave the DB connection in a needs-rollback state,
221
+ so a partially-failed multi-attempt reset can be retried in the same request.
222
+ * On removal, a provider ``404`` (the attempt is already gone upstream) is treated as
223
+ success so a retried, partially-failed reset converges; any other HTTP error is raised
224
+ so the local attempt is kept for a later retry instead of being silently dropped.
225
+ * Route the ``reset_attempts`` management command through the same provider-removal
226
+ path, since provider cleanup no longer happens in the ``pre_delete`` signal.
227
+ * Learner-initiated practice/onboarding resets stay best-effort, so a provider problem
228
+ does not block a learner from retrying their own onboarding exam.
229
+ * Deleting a ``ProctoredExam`` no longer notifies the proctoring provider about the
230
+ attempts that are cascade-deleted with it. Provider removal now happens at the three
231
+ call sites that reset attempts rather than in a ``pre_delete`` signal, so attempts
232
+ removed this way (a staff delete from Django admin) are left on the provider.
233
+
234
+ [5.2.1] - 2025-12-05
235
+ * Remove all references to Proctortrack
236
+
146
237
  [5.2.0] - 2025-04-22
147
238
  * adds support for django 5.2
148
239
 
@@ -3,4 +3,4 @@ The exam proctoring subsystem for the Open edX platform.
3
3
  """
4
4
 
5
5
  # Be sure to update the version number in edx_proctoring/package.json
6
- __version__ = '5.2.0'
6
+ __version__ = '6.1.2'
@@ -316,11 +316,11 @@ class ProctoredExamSoftwareSecureReviewAdmin(admin.ModelAdmin):
316
316
  obj.reviewed_by = request.user
317
317
  obj.save()
318
318
 
319
- def lookup_allowed(self, lookup, value):
319
+ def lookup_allowed(self, lookup, value, request=None):
320
320
  """ Checks if lookup allowed or not """
321
321
  if lookup == 'exam__course_id':
322
322
  return True
323
- return super().lookup_allowed(lookup, value)
323
+ return super().lookup_allowed(lookup, value, request)
324
324
 
325
325
 
326
326
  @admin.register(ProctoredExamSoftwareSecureReviewHistory)
@@ -26,8 +26,10 @@ from edx_proctoring.backends import get_backend_provider
26
26
  from edx_proctoring.exceptions import (
27
27
  AllowanceValueNotAllowedException,
28
28
  BackendProviderCannotRegisterAttempt,
29
+ BackendProviderCannotRemoveAttempt,
29
30
  BackendProviderNotConfigured,
30
31
  BackendProviderOnboardingException,
32
+ BackendProviderRemovalNotConfirmed,
31
33
  BackendProviderSentNoAttemptID,
32
34
  ProctoredBaseException,
33
35
  ProctoredExamAlreadyExists,
@@ -1472,7 +1474,7 @@ def update_attempt_status(attempt_id, to_status,
1472
1474
  exam_attempt_obj.allowed_time_limit_mins = _calculate_allowed_mins(exam, exam_attempt_obj.user_id)
1473
1475
 
1474
1476
  elif treat_timeout_as_submitted:
1475
- exam_attempt_obj.completed_at = timeout_timestamp
1477
+ exam_attempt_obj.completed_at = timeout_timestamp or datetime.now(pytz.UTC)
1476
1478
  elif to_status == ProctoredExamStudentAttemptStatus.submitted:
1477
1479
  # likewise, when we transition to submitted mark
1478
1480
  # when the exam has been completed
@@ -1771,7 +1773,7 @@ def create_proctoring_attempt_status_email(exam_attempt_obj, course_name, course
1771
1773
  default_contact_url = f'{scheme}://{constants.SITE_NAME}/support/contact_us'
1772
1774
 
1773
1775
  # If the course has a proctoring escalation email set, use that rather than edX Support.
1774
- # Currently only courses using Proctortrack will have this set.
1776
+ # This will be set if 'requires_escalation_email' is 'True' in proctoring backend.
1775
1777
  proctoring_escalation_email = _get_proctoring_escalation_email(course_id)
1776
1778
  if proctoring_escalation_email:
1777
1779
  contact_url = f'mailto:{proctoring_escalation_email}'
@@ -1880,7 +1882,9 @@ def reset_practice_exam(exam_id, user_id, requesting_user):
1880
1882
 
1881
1883
  # resetting a submitted attempt that has not been reviewed will entirely remove that submission.
1882
1884
  if exam_attempt_obj.status == ProctoredExamStudentAttemptStatus.submitted:
1883
- remove_exam_attempt(exam_attempt_obj.id, requesting_user)
1885
+ # This is the learner retrying their own onboarding exam, so a provider problem
1886
+ # must not block them: remove what we can upstream and reset locally regardless.
1887
+ remove_exam_attempt(exam_attempt_obj.id, requesting_user, require_backend_removal=False)
1884
1888
  else:
1885
1889
  exam_attempt_obj.status = ProctoredExamStudentAttemptStatus.onboarding_reset
1886
1890
  exam_attempt_obj.save()
@@ -1890,9 +1894,115 @@ def reset_practice_exam(exam_id, user_id, requesting_user):
1890
1894
  return create_exam_attempt(exam_id, user_id, taking_as_proctored=exam_attempt_obj.taking_as_proctored)
1891
1895
 
1892
1896
 
1893
- def remove_exam_attempt(attempt_id, requesting_user):
1897
+ def _remove_exam_attempt_from_backend(attempt):
1898
+ """
1899
+ Ask the proctoring backend to remove ``attempt`` on the provider's side, raising
1900
+ ``BackendProviderCannotRemoveAttempt`` (HTTP 502) if it cannot.
1901
+
1902
+ This is called BEFORE the local delete (see ``remove_exam_attempt``). Doing the
1903
+ provider call outside the ``pre_delete`` signal means a provider outage can raise a
1904
+ descriptive error without the local delete being half-applied: raising from inside
1905
+ ``delete()``'s ``pre_delete`` marks the DB connection needs-rollback, which then makes
1906
+ the surrounding request (and any retry) fail with ``TransactionManagementError``.
1907
+
1908
+ An attempt already gone upstream (provider 404) counts as success, so the local delete
1909
+ proceeds and a retry converges. Bulk, best-effort callers (onboarding cleanup, the
1910
+ reset_attempts command) should use ``_remove_exam_attempts_from_backend`` instead, which
1911
+ swallows these errors and skips a failing backend for the rest of the pass.
1912
+ """
1913
+ exam = attempt.proctored_exam
1914
+ if not exam.is_proctored:
1915
+ return
1916
+ if not attempt.external_id:
1917
+ # The attempt was never registered with the provider; nothing to remove upstream.
1918
+ return
1919
+ # Single instructor-facing message for any provider-side removal failure. The specific
1920
+ # cause is logged (below), never surfaced to the user.
1921
+ # Translators: shown to an instructor when an exam attempt could not be reset because
1922
+ # the external proctoring provider is temporarily unavailable.
1923
+ unavailable_message = _(
1924
+ 'The proctoring provider is temporarily unavailable, so this attempt could not '
1925
+ 'be fully reset. Please try again in a few minutes.'
1926
+ )
1927
+ try:
1928
+ # Resolving the backend can itself raise (e.g. NotImplementedError for a
1929
+ # stale/unconfigured backend), so keep it inside the error-handling block.
1930
+ backend = get_backend_provider(name=exam.backend)
1931
+ if not backend:
1932
+ return
1933
+ result = backend.remove_exam_attempt(exam.external_id, attempt.external_id)
1934
+ except Exception as exc:
1935
+ log.exception(
1936
+ 'Failed to remove attempt_id=%(attempt_id)s (external_id=%(external_id)s) for '
1937
+ 'exam_id=%(exam_id)s user_id=%(user_id)s from backend=%(backend)s due to a provider error.',
1938
+ {
1939
+ 'attempt_id': attempt.id,
1940
+ 'external_id': attempt.external_id,
1941
+ 'exam_id': exam.id,
1942
+ 'user_id': attempt.user_id,
1943
+ 'backend': exam.backend,
1944
+ }
1945
+ )
1946
+ raise BackendProviderCannotRemoveAttempt(unavailable_message) from exc
1947
+ if not result:
1948
+ # The provider responded but did not confirm removal, so keep the local attempt: a
1949
+ # retry can converge once the provider is healthy instead of silently dropping it.
1950
+ # This is attempt-specific -- the provider answered -- so it is raised as the more
1951
+ # specific subclass, letting bulk callers keep using the same backend.
1952
+ log.warning('Backend %s did not confirm removal of attempt_id=%s.', exam.backend, attempt.id)
1953
+ raise BackendProviderRemovalNotConfirmed(unavailable_message)
1954
+
1955
+
1956
+ def _remove_exam_attempts_from_backend(attempts, failed_backends=frozenset()):
1957
+ """
1958
+ Best-effort provider-side removal for a collection of attempts.
1959
+
1960
+ Provider failures are swallowed (the caller is expected to delete the attempts locally
1961
+ regardless). Once a backend proves *unreachable* we stop calling it for the rest of this
1962
+ pass -- while still trying other, independently configured backends -- so a provider
1963
+ outage does not cost a full request timeout for every attempt. A reachable provider that
1964
+ merely fails to confirm one removal is not skipped, since the remaining attempts on it
1965
+ are still worth attempting.
1966
+
1967
+ Returns the set of backends that failed, so a caller working through batches can thread
1968
+ it back in and keep skipping them across calls.
1969
+
1970
+ Pass a queryset with ``select_related('proctored_exam')`` to avoid a query per attempt.
1971
+ """
1972
+ # Fresh mutable set per call; the shared default is only ever copied from, never mutated.
1973
+ failed = set(failed_backends)
1974
+ for attempt in attempts:
1975
+ backend = attempt.proctored_exam.backend
1976
+ if backend in failed:
1977
+ continue
1978
+ try:
1979
+ _remove_exam_attempt_from_backend(attempt)
1980
+ except BackendProviderRemovalNotConfirmed:
1981
+ # The provider answered, it just did not confirm this one. It is still healthy,
1982
+ # so keep going -- skipping the rest would orphan them upstream for nothing.
1983
+ log.warning(
1984
+ 'Backend %r did not confirm removal of attempt_id=%s; removing locally only.',
1985
+ backend, attempt.id,
1986
+ )
1987
+ except BackendProviderCannotRemoveAttempt:
1988
+ # The provider could not be reached at all, so stop paying its timeout for
1989
+ # every remaining attempt on that backend.
1990
+ log.warning(
1991
+ 'Provider removal failed for backend %r; skipping further provider calls '
1992
+ 'for it during this cleanup and removing locally only.', backend
1993
+ )
1994
+ failed.add(backend)
1995
+ return failed
1996
+
1997
+
1998
+ def remove_exam_attempt(attempt_id, requesting_user, require_backend_removal=True):
1894
1999
  """
1895
2000
  Removes an exam attempt given the attempt id. requesting_user is passed through to the instructor_service.
2001
+
2002
+ The attempt is removed on the proctoring provider first. By default a provider failure
2003
+ raises ``BackendProviderCannotRemoveAttempt`` and the local attempt is left untouched so
2004
+ it can be retried. Pass ``require_backend_removal=False`` for learner-initiated resets,
2005
+ where the provider removal is best-effort and must not block the learner.
1896
2006
  """
1897
2007
 
1898
2008
  log.info(
@@ -1916,6 +2026,15 @@ def remove_exam_attempt(attempt_id, requesting_user):
1916
2026
  content_id = existing_attempt.proctored_exam.content_id
1917
2027
  to_status = existing_attempt.status
1918
2028
 
2029
+ # Remove the attempt on the proctoring provider BEFORE deleting it locally, so a
2030
+ # provider outage surfaces a descriptive error instead of a rolled-back, half-applied
2031
+ # delete. See _remove_exam_attempt_from_backend for why this must not happen in the
2032
+ # pre_delete signal.
2033
+ if require_backend_removal:
2034
+ _remove_exam_attempt_from_backend(existing_attempt)
2035
+ else:
2036
+ _remove_exam_attempts_from_backend([existing_attempt])
2037
+
1919
2038
  existing_attempt.delete_exam_attempt()
1920
2039
  instructor_service = get_runtime_service('instructor')
1921
2040
  grades_service = get_runtime_service('grades')
@@ -37,6 +37,7 @@ BACKEND_CONFIGURATION_ALLOW_LIST = [
37
37
  'supports_onboarding',
38
38
  'tech_support_email',
39
39
  'tech_support_phone',
40
+ 'timeout',
40
41
  'token_expiration_time',
41
42
  'verbose_name',
42
43
  'video_review_aes_key',
@@ -61,6 +61,12 @@ class ProctoringBackendProvider(metaclass=abc.ABCMeta):
61
61
  def remove_exam_attempt(self, exam, attempt):
62
62
  """
63
63
  Method that removes the exam attempt from the backend's system
64
+
65
+ Return ``True`` when the provider confirms the attempt was removed, or that it is
66
+ already gone upstream. Return ``False`` when the provider responded without
67
+ confirming the removal; the caller then keeps the local attempt so the removal can
68
+ be retried instead of being silently dropped. Raise to signal that the provider
69
+ could not be reached at all.
64
70
  """
65
71
  raise NotImplementedError()
66
72
 
@@ -41,6 +41,14 @@ class BaseRestProctoringProvider(ProctoringBackendProvider):
41
41
  has_dashboard = True
42
42
  supports_onboarding = True
43
43
  passing_statuses = (SoftwareSecureReviewStatus.clean,)
44
+ # Timeout (in seconds) for every outbound request to the provider, so a slow or
45
+ # unavailable provider cannot hang the request indefinitely. ``requests`` applies this
46
+ # as two separate inactivity limits -- how long to wait to connect, and how long to wait
47
+ # for more data -- not as a cap on total request time, so a response that keeps
48
+ # trickling can outlast it. A ``(connect, read)`` tuple sets those two phases
49
+ # independently. Overridable per backend via a ``timeout`` key in
50
+ # ``PROCTORING_BACKENDS``.
51
+ timeout = 30
44
52
 
45
53
  @property
46
54
  def exam_attempt_url(self):
@@ -148,7 +156,7 @@ class BaseRestProctoringProvider(ProctoringBackendProvider):
148
156
  """
149
157
  url = self.config_url
150
158
  log.debug('Requesting config from %r', url)
151
- response = self.session.get(url, headers=self._get_language_headers()).json()
159
+ response = self.session.get(url, headers=self._get_language_headers(), timeout=self.timeout).json()
152
160
  return response
153
161
 
154
162
  def get_exam(self, exam):
@@ -157,7 +165,7 @@ class BaseRestProctoringProvider(ProctoringBackendProvider):
157
165
  """
158
166
  url = self.exam_url.format(exam_id=exam['id'])
159
167
  log.debug('Requesting exam from %r', url)
160
- response = self.session.get(url).json()
168
+ response = self.session.get(url, timeout=self.timeout).json()
161
169
  return response
162
170
 
163
171
  def get_attempt(self, attempt):
@@ -187,7 +195,7 @@ class BaseRestProctoringProvider(ProctoringBackendProvider):
187
195
  'Creating exam attempt for exam_id=%(exam_id)i (external_id=%(external_id)s) at %(url)s',
188
196
  {'exam_id': exam['id'], 'external_id': exam['external_id'], 'url': url}
189
197
  )
190
- response = self.session.post(url, json=payload)
198
+ response = self.session.post(url, json=payload, timeout=self.timeout)
191
199
  if response.status_code != 200:
192
200
  raise BackendProviderCannotRegisterAttempt(response.content, response.status_code)
193
201
  status_code = response.status_code
@@ -227,13 +235,29 @@ class BaseRestProctoringProvider(ProctoringBackendProvider):
227
235
 
228
236
  def remove_exam_attempt(self, exam, attempt):
229
237
  """
230
- Removes the exam attempt on the backend provider's server
238
+ Removes the exam attempt on the backend provider's server.
239
+
240
+ Returns ``True`` when the provider confirms removal with the documented
241
+ ``{"status": "deleted"}`` payload, or on a ``404`` (the attempt is already gone
242
+ upstream, so a retried reset converges). Returns ``False`` when the provider responds
243
+ but does not confirm deletion (undecodable body or an unexpected status); the API
244
+ layer decides whether that is fatal. Any other HTTP error is raised so a transient
245
+ failure keeps the local attempt available for a later retry.
231
246
  """
232
- response = self._make_attempt_request(
233
- exam,
234
- attempt,
235
- method='DELETE')
236
- return response.get('status', None) == 'deleted'
247
+ if not attempt:
248
+ return False
249
+ url = self.exam_attempt_url.format(exam_id=exam, attempt_id=attempt)
250
+ log.debug('Removing attempt at %r', url)
251
+ response = self.session.request('DELETE', url, timeout=self.timeout)
252
+ if response.status_code == 404:
253
+ return True
254
+ response.raise_for_status()
255
+ try:
256
+ data = response.json()
257
+ except ValueError:
258
+ log.exception('Decoding attempt removal %r -> %r', attempt, response.content)
259
+ return False
260
+ return data.get('status', None) == 'deleted'
237
261
 
238
262
  def mark_erroneous_exam_attempt(self, exam, attempt):
239
263
  """
@@ -272,7 +296,7 @@ class BaseRestProctoringProvider(ProctoringBackendProvider):
272
296
  )
273
297
  response = None
274
298
  try:
275
- response = self.session.post(url, json=exam)
299
+ response = self.session.post(url, json=exam, timeout=self.timeout)
276
300
  data = response.json()
277
301
  except Exception as exc: # pylint: disable=broad-exception-caught
278
302
  if response:
@@ -327,16 +351,21 @@ class BaseRestProctoringProvider(ProctoringBackendProvider):
327
351
 
328
352
  def retire_user(self, user_id):
329
353
  url = self.user_info_url.format(user_id=user_id)
354
+ response = None
330
355
  try:
331
- response = self.session.delete(url)
356
+ response = self.session.delete(url, timeout=self.timeout)
332
357
  data = response.json()
333
358
  assert data in (True, False)
334
359
  except Exception as exc:
335
360
  # pylint: disable=no-member
336
361
  if hasattr(exc, 'response') and exc.response is not None:
337
362
  content = exc.response.content
338
- else:
363
+ elif response is not None:
339
364
  content = response.content
365
+ else:
366
+ # The request failed before a response was received (e.g. a timeout), so
367
+ # there is no body to include.
368
+ content = None
340
369
  raise BackendProviderCannotRetireUser(content) from exc
341
370
  return data
342
371
 
@@ -346,7 +375,7 @@ class BaseRestProctoringProvider(ProctoringBackendProvider):
346
375
  query_string = urlencode(kwargs)
347
376
  url += '?' + query_string
348
377
 
349
- response = self.session.get(url)
378
+ response = self.session.get(url, timeout=self.timeout)
350
379
 
351
380
  if response.status_code != 200:
352
381
  raise BackendProviderOnboardingProfilesException(response.content, response.status_code)
@@ -384,7 +413,7 @@ class BaseRestProctoringProvider(ProctoringBackendProvider):
384
413
  if method == 'GET':
385
414
  headers.update(self._get_language_headers())
386
415
  log.debug('Making %r attempt request at %r', method, url)
387
- response = self.session.request(method, url, json=payload, headers=headers)
416
+ response = self.session.request(method, url, json=payload, headers=headers, timeout=self.timeout)
388
417
  try:
389
418
  data = response.json()
390
419
  except ValueError: