Repository: apluslms/a-plus

Issue:
Enrollment questionnaire is affected by the module opening time
Continued from https://github.com/apluslms/a-plus/issues/462

Enrollment questionnaires should not be affected by the module opening time. The course already has its own enrollment opening/closing time settings and they are enough. Enrollment questionnaires should not be affected by the module opening/closing times. Fixing this is difficult because the `CourseModulePermission` does not directly have access to the exercise and it can't check if the exercise is an enrollment exercise. Perhaps the exercise could be defined in the attributes of the `view` object and it would always be defined if the user is on an exercise page. If the exercise is defined and it is an enrollment exercise, then the course module opening time is ignored in the permission.

https://github.com/apluslms/a-plus/blob/cd10e04963557444efeb11b06b2834bf47ace903/course/permissions.py#L140


Discussion:
This commit changed enrollment questionnaires so that they are not affected by the module closing time:
https://github.com/apluslms/a-plus/commit/1472962a878cb673f7fa24432d2aa4aa1fc28df1

Module opening times affect `CourseModulePermission` and that's the problem in this issue.

Resolving patch:
diff --git a/course/permissions.py b/course/permissions.py
--- a/course/permissions.py
+++ b/course/permissions.py
@@ -15,7 +15,7 @@
     FilterBackend,
 )
 from exercise.cache.points import CachedPoints
-from exercise.models import BaseExercise
+from exercise.models import BaseExercise, LearningObject
 from exercise.submission_models import Submission
 from userprofile.models import GraderUser, UserProfile
 from .models import (
@@ -248,6 +248,13 @@ def has_object_permission(self, request: HttpRequest, view: Any, module: CourseM
         if module.status == CourseModule.STATUS.HIDDEN:
             return False
 
+        # Enrollment questionnaires are not affected by the module opening time.
+        if hasattr(view, 'exercise') and view.exercise.status in (
+                LearningObject.STATUS.ENROLLMENT,
+                LearningObject.STATUS.ENROLLMENT_EXTERNAL,
+                ):
+            return True
+
         if not module.is_after_open():
             # FIXME: use format from django settings
             self.error_msg(
diff --git a/exercise/permissions.py b/exercise/permissions.py
--- a/exercise/permissions.py
+++ b/exercise/permissions.py
@@ -39,7 +39,13 @@ def is_object_visible(self, request, view, exercise):
             self.error_msg(_('EXERCISE_VISIBILITY_ERROR_NOT_VISIBLE'))
             return False
 
-        if exercise.is_submittable and not exercise.course_module.have_exercises_been_opened():
+        if (
+            exercise.is_submittable
+            and not exercise.course_module.have_exercises_been_opened()
+            and not exercise.status in (
+                LearningObject.STATUS.ENROLLMENT,
+                LearningObject.STATUS.ENROLLMENT_EXTERNAL,
+                )):
             self.error_msg(
                 format_lazy(
                     _('EXERCISE_VISIBILITY_ERROR_NOT_OPEN_YET -- {}'),


Test patch:
diff --git a/exercise/tests.py b/exercise/tests.py
--- a/exercise/tests.py
+++ b/exercise/tests.py
@@ -1184,3 +1184,80 @@ def test_submission_draft(self):
         self.assertEqual(draft1.submission_data, [["key", "value4"]])
         draft2 = self.base_exercise.get_submission_draft(self.user2.userprofile)
         self.assertEqual(draft2.submission_data, [["key", "value2"]])
+
+    def test_enrollment_questionaire_opening_time(self):
+        course_module = self.enrollment_exercise.course_module
+        course_module.closing_time = self.two_days_from_now
+
+        # Course and enrollment are open.
+        self.course_instance.starting_time = self.yesterday
+        self.course_instance.enrollment_starting_time = self.yesterday
+        self.course_instance.save()
+        course_module.opening_time = self.yesterday
+        course_module.save()
+        self.assertEqual(
+            self.enrollment_exercise.check_submission_allowed(self.user.userprofile)[0],
+            BaseExercise.SUBMIT_STATUS.ALLOWED,
+        )
+
+        # Only enrollment isn't open.
+        self.course_instance.enrollment_starting_time = self.tomorrow
+        self.course_instance.save()
+        self.assertEqual(
+            self.enrollment_exercise.check_submission_allowed(self.user.userprofile)[0],
+            BaseExercise.SUBMIT_STATUS.CANNOT_ENROLL,
+        )
+
+        # Enrollment is open but the module is not.
+        self.course_instance.enrollment_starting_time = self.yesterday
+        self.course_instance.save()
+        course_module.starting_time = self.tomorrow
+        course_module.save()
+        self.assertEqual(
+            self.enrollment_exercise.check_submission_allowed(self.user.userprofile)[0],
+            BaseExercise.SUBMIT_STATUS.ALLOWED,
+        )
+
+        # Course isn't open but enrollment is. Module is open.
+        self.course_instance.enrollment_starting_time = self.yesterday
+        self.course_instance.starting_time = self.tomorrow
+        self.course_instance.save()
+        course_module.starting_time = self.yesterday
+        course_module.save()
+        self.assertEqual(
+            self.enrollment_exercise.check_submission_allowed(self.user.userprofile)[0],
+            BaseExercise.SUBMIT_STATUS.ALLOWED,
+        )
+
+        # Course isn't open but enrollment is. Module is closed.
+        self.course_instance.enrollment_starting_time = self.yesterday
+        self.course_instance.starting_time = self.tomorrow
+        self.course_instance.save()
+        course_module.starting_time = self.tomorrow
+        course_module.save()
+        self.assertEqual(
+            self.enrollment_exercise.check_submission_allowed(self.user.userprofile)[0],
+            BaseExercise.SUBMIT_STATUS.ALLOWED,
+        )
+
+        # Course, enrollment and module are closed.
+        self.course_instance.enrollment_starting_time = self.tomorrow
+        self.course_instance.starting_time = self.tomorrow
+        self.course_instance.save()
+        course_module.starting_time = self.tomorrow
+        course_module.save()
+        self.assertEqual(
+            self.enrollment_exercise.check_submission_allowed(self.user.userprofile)[0],
+            BaseExercise.SUBMIT_STATUS.CANNOT_ENROLL,
+        )
+
+        # Course and enrollment are closed. Module is open.
+        self.course_instance.enrollment_starting_time = self.tomorrow
+        self.course_instance.starting_time = self.tomorrow
+        self.course_instance.save()
+        course_module.starting_time = self.yesterday
+        course_module.save()
+        self.assertEqual(
+            self.enrollment_exercise.check_submission_allowed(self.user.userprofile)[0],
+            BaseExercise.SUBMIT_STATUS.CANNOT_ENROLL,
+        )