Repository: apluslms/a-plus

Issue:
Fix the current course instance redirection when the newest instance is not open yet
The current course instance redirection (URL contains course without any course instance identifier): https://plus.cs.aalto.fi/o1

https://github.com/apluslms/a-plus/blob/6a7704d22469cb0e2c0f1f52ba420cb3a4cf3c7c/course/views.py#L110

From @jsorva:

In a meeting some time ago, I promised to return to the issue of the 
“current instance link” that points to the most recent version of a 
course. So, here’s how I’d prefer like it to work:

1) While a course is running, the link points to the active instance. 
[For O1, this is in during the fall term.]

2) After a course is over [in December], the link continues to points to 
the most recent instance. This continues even after next year’s instance 
has been created in A+ but isn’t yet open to the public [typically early 
in the next calendar year]. And it even continues after the next 
instance is technically visible in some form but there’s only the front 
page and maybe some other materials, but the actual course contents 
aren’t visible yet [this is often the case during the summer].

3) Only when the actual course contents are visible [ideally some time 
in August, but might be September] does the link start pointing at the 
new instance.

To summarize, I’d like the link to always point to the newest instance 
whose actual contents are actually visible; the mere existence of the 
instance isn’t enough. (I’m not sure how close this wish is to the 
current behavior.)

Internal ticket: https://rt.cs.aalto.fi/Ticket/Display.html?id=20168

-----------------------------------

My first hunch is that this should be easy to accomplish by tweaking the latest course instance selection in the LastInstanceView.


Discussion:

Resolving patch:
diff --git a/course/views.py b/course/views.py
--- a/course/views.py
+++ b/course/views.py
@@ -113,19 +113,36 @@ class LastInstanceView(CourseMixin, BaseRedirectView):
 
     def get_resource_objects(self):
         super().get_resource_objects()
-        course = get_object_or_404(Course, url=self._get_kwarg(self.course_kw))
-        course_instances = CourseInstance.objects.filter(course=course).order_by('-starting_time')
+        now = timezone.now()
+        course_instances = CourseInstance.objects.filter(course=self.course).order_by('-starting_time')
+        instance_ids_with_open_course_modules = set(
+            CourseModule.objects.filter(
+                course_instance__course=self.course,
+                status__in=(CourseModule.STATUS.READY, CourseModule.STATUS.UNLISTED),
+                opening_time__lte=now,
+            )
+            .values_list('course_instance_id', flat=True)
+            .order_by()
+            .distinct()
+        )
 
+        latest_open = None
         latest_visible = None
         latest_hidden = None
         for instance in course_instances:
             if instance.visible_to_students:
-                latest_visible = instance
-                break
+                if (instance.id in instance_ids_with_open_course_modules
+                        and instance.starting_time <= now
+                        ):
+                    latest_open = instance
+                    break
+                latest_visible = latest_visible or instance
             else:
                 latest_hidden = latest_hidden or instance
 
-        if latest_visible:
+        if latest_open:
+            self.course_instance = latest_open
+        elif latest_visible:
             self.course_instance = latest_visible
         elif latest_hidden and latest_hidden.is_course_staff(self.request.user):
             self.course_instance = latest_hidden


Test patch:
diff --git a/course/tests.py b/course/tests.py
--- a/course/tests.py
+++ b/course/tests.py
@@ -402,3 +402,650 @@ def test_student_enroll_from_sis(self):
         )
         self.assertTrue(self.current_course_instance.is_student(self.user1))
         self.assertTrue(self.current_course_instance.is_student(self.user2))
+
+    def test_last_instance_view_hidden_module(self):
+        course = Course.objects.create(
+            name="Last instance view course",
+            code="111111",
+            url="last_instance_view",
+        )
+
+        older_instance = CourseInstance.objects.create(
+            instance_name="older instance",
+            starting_time=self.today,
+            ending_time=self.tomorrow,
+            course=course,
+            url="expected",
+        )
+        # Add a ready module to the older course instance.
+        older_module = CourseModule.objects.create(
+            name="older module",
+            url="older-module",
+            points_to_pass=10,
+            course_instance=older_instance,
+            opening_time=self.today,
+            closing_time=self.tomorrow,
+            status=CourseModule.STATUS.READY,
+        )
+
+        newer_instance = CourseInstance.objects.create(
+            instance_name="newer instance",
+            starting_time=self.yesterday,
+            ending_time=self.tomorrow,
+            course=course,
+            url="invalid",
+        )
+        # Add a hidden module to the newer course instance.
+        newer_module = CourseModule.objects.create(
+            name="newer module",
+            url="newer-module",
+            points_to_pass=10,
+            course_instance=newer_instance,
+            opening_time=self.yesterday,
+            closing_time=self.tomorrow,
+            status=CourseModule.STATUS.HIDDEN,
+        )
+
+
+        response = self.client.get('/last_instance_view/', follow=True)
+        self.assertEqual(response.status_code, 200)
+
+        # The redirect should point to the older instance with a visible module.
+        self.assertEqual(response.context['next'] , '/last_instance_view/expected/')
+
+    def test_last_instance_view_not_yet_open_module(self):
+        self.current_course_instance.enroll_student(self.user1)
+        self.client.login(username='testUser1', password='testPassword')
+        response = self.client.get('/Course-Url/', follow=True)
+
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        self.assertEqual(response.redirect_chain[0][0], '/Course-Url/T-00.1000_d1/')
+
+    def test_last_instance_view_open_modules(self):
+        course = Course.objects.create(
+            name="Last instance view course",
+            code="CS-111111",
+            url="cs-111111",
+        )
+
+        # Two years ago.
+        instance1 = CourseInstance.objects.create(
+            instance_name="Instance 1",
+            starting_time=self.today - timedelta(weeks=105),
+            ending_time=self.today - timedelta(weeks=91),
+            course=course,
+            url="instance1",
+        )
+        module1 = CourseModule.objects.create(
+            name="Module 1",
+            url="module1",
+            points_to_pass=10,
+            course_instance=instance1,
+            opening_time=self.today - timedelta(weeks=104),
+            closing_time=self.today - timedelta(weeks=102),
+            status=CourseModule.STATUS.READY,
+        )
+
+        # One year ago.
+        instance2 = CourseInstance.objects.create(
+            instance_name="Instance 2",
+            starting_time=self.today - timedelta(weeks=53),
+            ending_time=self.today - timedelta(weeks=39),
+            course=course,
+            url="instance2",
+        )
+        module2 = CourseModule.objects.create(
+            name="Module 2",
+            url="module2",
+            points_to_pass=10,
+            course_instance=instance2,
+            opening_time=self.today - timedelta(weeks=52),
+            closing_time=self.today - timedelta(weeks=50),
+            status=CourseModule.STATUS.READY,
+        )
+
+        # This course instance has just recently started.
+        instance3 = CourseInstance.objects.create(
+            instance_name="Instance 3",
+            starting_time=self.today - timedelta(weeks=1),
+            ending_time=self.today + timedelta(weeks=14),
+            course=course,
+            url="instance3",
+        )
+        module3 = CourseModule.objects.create(
+            name="Module 3",
+            url="module3",
+            points_to_pass=10,
+            course_instance=instance3,
+            opening_time=self.today - timedelta(days=6),
+            closing_time=self.today + timedelta(weeks=1),
+            status=CourseModule.STATUS.READY,
+        )
+
+        instance1.enroll_student(self.user1)
+        instance2.enroll_student(self.user1)
+        instance3.enroll_student(self.user1)
+        self.client.login(username='testUser1', password='testPassword')
+        response = self.client.get('/cs-111111/', follow=True)
+
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        self.assertEqual(response.redirect_chain[0][0], '/cs-111111/instance3/')
+
+    def test_last_instance_view_almost_concurrent_instances(self):
+        course = Course.objects.create(
+            name="Last instance view course",
+            code="CS-111111",
+            url="cs-111111",
+        )
+
+        # One year ago.
+        instance1 = CourseInstance.objects.create(
+            instance_name="Instance 1",
+            starting_time=self.today - timedelta(weeks=54),
+            ending_time=self.today - timedelta(weeks=47),
+            course=course,
+            url="instance1",
+        )
+        module1 = CourseModule.objects.create(
+            name="Module 1",
+            url="module1",
+            points_to_pass=10,
+            course_instance=instance1,
+            opening_time=self.today - timedelta(weeks=54),
+            closing_time=self.today - timedelta(weeks=51),
+            status=CourseModule.STATUS.READY,
+        )
+
+        # Two course instances this year.
+        instance2 = CourseInstance.objects.create(
+            instance_name="Instance 2",
+            starting_time=self.today - timedelta(weeks=3),
+            ending_time=self.today + timedelta(weeks=4),
+            course=course,
+            url="instance2",
+        )
+        module2 = CourseModule.objects.create(
+            name="Module 2",
+            url="module2",
+            points_to_pass=10,
+            course_instance=instance2,
+            opening_time=self.today - timedelta(weeks=2),
+            closing_time=self.today - timedelta(days=1),
+            status=CourseModule.STATUS.READY,
+        )
+        module2b = CourseModule.objects.create(
+            name="Module 2b",
+            url="module2b",
+            points_to_pass=20,
+            course_instance=instance2,
+            opening_time=self.today + timedelta(days=1),
+            closing_time=self.today + timedelta(weeks=1),
+            status=CourseModule.STATUS.READY,
+        )
+
+        instance3 = CourseInstance.objects.create(
+            instance_name="Instance 3",
+            starting_time=self.today - timedelta(weeks=10),
+            ending_time=self.today,
+            course=course,
+            url="instance3",
+        )
+        module3 = CourseModule.objects.create(
+            name="Module 3",
+            url="module3",
+            points_to_pass=10,
+            course_instance=instance3,
+            opening_time=self.today - timedelta(weeks=10),
+            closing_time=self.today - timedelta(weeks=1),
+            status=CourseModule.STATUS.READY,
+        )
+
+        instance1.enroll_student(self.user1)
+        instance2.enroll_student(self.user1)
+        instance3.enroll_student(self.user1)
+        self.client.login(username='testUser1', password='testPassword')
+        response = self.client.get('/cs-111111/', follow=True)
+
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        self.assertEqual(response.redirect_chain[0][0], '/cs-111111/instance2/')
+
+    def test_last_instance_view_only_future_instances(self):
+        course = Course.objects.create(
+            name="Last instance view course",
+            code="CS-111111",
+            url="cs-111111",
+        )
+
+        # Starting in a month.
+        instance1 = CourseInstance.objects.create(
+            instance_name="Instance 1",
+            starting_time=self.today + timedelta(weeks=4),
+            ending_time=self.today + timedelta(weeks=19),
+            course=course,
+            url="instance1",
+        )
+        module1 = CourseModule.objects.create(
+            name="Module 1",
+            url="module1",
+            points_to_pass=10,
+            course_instance=instance1,
+            opening_time=self.today + timedelta(weeks=4),
+            closing_time=self.today + timedelta(weeks=18),
+            status=CourseModule.STATUS.READY,
+        )
+
+        # Starts in a year.
+        instance2 = CourseInstance.objects.create(
+            instance_name="Instance 2",
+            starting_time=self.today + timedelta(weeks=53),
+            ending_time=self.today + timedelta(weeks=60),
+            course=course,
+            url="instance2",
+        )
+        module2 = CourseModule.objects.create(
+            name="Module 2",
+            url="module2",
+            points_to_pass=10,
+            course_instance=instance2,
+            opening_time=self.today + timedelta(weeks=53),
+            closing_time=self.today + timedelta(weeks=56),
+            status=CourseModule.STATUS.UNLISTED,
+        )
+        module2b = CourseModule.objects.create(
+            name="Module 2b",
+            url="module2b",
+            points_to_pass=20,
+            course_instance=instance2,
+            opening_time=self.today + timedelta(weeks=56),
+            closing_time=self.today + timedelta(weeks=59),
+            status=CourseModule.STATUS.HIDDEN,
+        )
+
+        # Starts after instance2.
+        instance3 = CourseInstance.objects.create(
+            instance_name="Instance 3",
+            starting_time=self.today + timedelta(weeks=61),
+            ending_time=self.today + timedelta(weeks=70),
+            course=course,
+            url="instance3",
+        )
+        module3 = CourseModule.objects.create(
+            name="Module 3",
+            url="module3",
+            points_to_pass=10,
+            course_instance=instance3,
+            opening_time=self.today + timedelta(weeks=61),
+            closing_time=self.today + timedelta(weeks=70),
+            status=CourseModule.STATUS.HIDDEN,
+        )
+
+        instance1.enroll_student(self.user1)
+        instance2.enroll_student(self.user1)
+        instance3.enroll_student(self.user1)
+        self.client.login(username='testUser1', password='testPassword')
+        response = self.client.get('/cs-111111/', follow=True)
+
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        # This is not ideal, but this is how it currently works.
+        # All instances start in the future, thus the latest instance is
+        # the one that has the very latest starting time even though
+        # there are earlier course instances that would start sooner.
+        # It could make sense if the redirect targeted the next course instance
+        # that is about to start soon.
+        self.assertEqual(response.redirect_chain[0][0], '/cs-111111/instance3/')
+
+        # Make the future course instances hidden from students except
+        # the one that starts soon.
+        instance2.visible_to_students = False
+        instance2.save()
+        instance3.visible_to_students = False
+        instance3.save()
+
+        response = self.client.get('/cs-111111/', follow=True)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        self.assertEqual(response.redirect_chain[0][0], '/cs-111111/instance1/')
+
+    def test_last_instance_view_after_instance_ending(self):
+        course = Course.objects.create(
+            name="Last instance view course",
+            code="CS-111111",
+            url="cs-111111",
+        )
+
+        # One year ago.
+        instance1 = CourseInstance.objects.create(
+            instance_name="Instance 1",
+            starting_time=self.today - timedelta(weeks=52),
+            ending_time=self.today - timedelta(weeks=40),
+            course=course,
+            url="instance1",
+        )
+        module1 = CourseModule.objects.create(
+            name="Module 1",
+            url="module1",
+            points_to_pass=10,
+            course_instance=instance1,
+            opening_time=self.today - timedelta(weeks=51),
+            closing_time=self.today - timedelta(weeks=41),
+            status=CourseModule.STATUS.READY,
+        )
+
+        # Ended recently.
+        instance2 = CourseInstance.objects.create(
+            instance_name="Instance 2",
+            starting_time=self.today - timedelta(weeks=15),
+            ending_time=self.today - timedelta(weeks=1),
+            course=course,
+            url="instance2",
+        )
+        module2 = CourseModule.objects.create(
+            name="Module 2",
+            url="module2",
+            points_to_pass=10,
+            course_instance=instance2,
+            opening_time=self.today - timedelta(weeks=15),
+            closing_time=self.today - timedelta(weeks=12),
+            status=CourseModule.STATUS.READY,
+        )
+        module2b = CourseModule.objects.create(
+            name="Module 2b",
+            url="module2b",
+            points_to_pass=20,
+            course_instance=instance2,
+            opening_time=self.today - timedelta(weeks=12),
+            closing_time=self.today - timedelta(weeks=1),
+            status=CourseModule.STATUS.UNLISTED,
+        )
+
+        # Starts in 6 months.
+        instance3 = CourseInstance.objects.create(
+            instance_name="Instance 3",
+            starting_time=self.today + timedelta(weeks=25),
+            ending_time=self.today + timedelta(weeks=40),
+            course=course,
+            url="instance3",
+        )
+        module3 = CourseModule.objects.create(
+            name="Module 3",
+            url="module3",
+            points_to_pass=10,
+            course_instance=instance3,
+            opening_time=self.today + timedelta(weeks=25),
+            closing_time=self.today + timedelta(weeks=30),
+            status=CourseModule.STATUS.READY,
+        )
+
+        instance1.enroll_student(self.user1)
+        instance2.enroll_student(self.user1)
+        self.client.login(username='testUser1', password='testPassword')
+        response = self.client.get('/cs-111111/', follow=True)
+
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        self.assertEqual(response.redirect_chain[0][0], '/cs-111111/instance2/')
+
+        # Hide instance2 from students.
+        instance2.visible_to_students = False
+        instance2.save()
+
+        response = self.client.get('/cs-111111/', follow=True)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        self.assertEqual(response.redirect_chain[0][0], '/cs-111111/instance1/')
+
+    def test_last_instance_view_all_instances_hidden(self):
+        course = Course.objects.create(
+            name="Last instance view course",
+            code="CS-111111",
+            url="cs-111111",
+        )
+
+        # One year ago.
+        instance1 = CourseInstance.objects.create(
+            visible_to_students=False,
+            instance_name="Instance 1",
+            starting_time=self.today - timedelta(weeks=52),
+            ending_time=self.today - timedelta(weeks=40),
+            course=course,
+            url="instance1",
+        )
+        module1 = CourseModule.objects.create(
+            name="Module 1",
+            url="module1",
+            points_to_pass=10,
+            course_instance=instance1,
+            opening_time=self.today - timedelta(weeks=51),
+            closing_time=self.today - timedelta(weeks=41),
+            status=CourseModule.STATUS.READY,
+        )
+
+        # Ended recently.
+        instance2 = CourseInstance.objects.create(
+            visible_to_students=False,
+            instance_name="Instance 2",
+            starting_time=self.today - timedelta(weeks=15),
+            ending_time=self.today - timedelta(weeks=1),
+            course=course,
+            url="instance2",
+        )
+        module2 = CourseModule.objects.create(
+            name="Module 2",
+            url="module2",
+            points_to_pass=10,
+            course_instance=instance2,
+            opening_time=self.today - timedelta(weeks=15),
+            closing_time=self.today - timedelta(weeks=12),
+            status=CourseModule.STATUS.READY,
+        )
+        module2b = CourseModule.objects.create(
+            name="Module 2b",
+            url="module2b",
+            points_to_pass=20,
+            course_instance=instance2,
+            opening_time=self.today - timedelta(weeks=12),
+            closing_time=self.today - timedelta(weeks=1),
+            status=CourseModule.STATUS.READY,
+        )
+
+        # Starts in 6 months.
+        instance3 = CourseInstance.objects.create(
+            visible_to_students=False,
+            instance_name="Instance 3",
+            starting_time=self.today + timedelta(weeks=25),
+            ending_time=self.today + timedelta(weeks=40),
+            course=course,
+            url="instance3",
+        )
+        module3 = CourseModule.objects.create(
+            name="Module 3",
+            url="module3",
+            points_to_pass=10,
+            course_instance=instance3,
+            opening_time=self.today + timedelta(weeks=25),
+            closing_time=self.today + timedelta(weeks=30),
+            status=CourseModule.STATUS.READY,
+        )
+
+        instance1.enroll_student(self.user1)
+        instance2.enroll_student(self.user1)
+        self.client.login(username='testUser1', password='testPassword')
+        response = self.client.get('/cs-111111/', follow=True)
+
+        self.assertEqual(response.status_code, 404)
+        self.assertEqual(len(response.redirect_chain), 0)
+
+        # Show all course instances from students.
+        instance1.visible_to_students = True
+        instance1.save()
+        instance2.visible_to_students = True
+        instance2.save()
+        instance3.visible_to_students = True
+        instance3.save()
+
+        response = self.client.get('/cs-111111/', follow=True)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        self.assertEqual(response.redirect_chain[0][0], '/cs-111111/instance2/')
+
+        instance1.visible_to_students = False
+        instance1.save()
+        instance2.visible_to_students = False
+        instance2.save()
+        instance3.visible_to_students = False
+        instance3.save()
+
+        # Log in as a teacher.
+        self.client.logout()
+        instance1.add_teacher(self.user.userprofile)
+        instance2.add_teacher(self.user.userprofile)
+        instance3.add_teacher(self.user.userprofile)
+        self.client.login(username='testUser', password='testPassword')
+
+        response = self.client.get('/cs-111111/', follow=True)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        self.assertEqual(response.redirect_chain[0][0], '/cs-111111/instance3/')
+
+        instance1.visible_to_students = True
+        instance1.save()
+        instance2.visible_to_students = True
+        instance2.save()
+        instance3.visible_to_students = True
+        instance3.save()
+
+        response = self.client.get('/cs-111111/', follow=True)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        self.assertEqual(response.redirect_chain[0][0], '/cs-111111/instance2/')
+
+    def test_last_instance_view_no_instances(self):
+        course = Course.objects.create(
+            name="Last instance view course",
+            code="CS-111111",
+            url="cs-111111",
+        )
+
+        response = self.client.get('/cs-111111/', follow=True)
+        self.assertEqual(response.status_code, 404)
+        self.assertEqual(len(response.redirect_chain), 0)
+
+        self.client.login(username='testUser1', password='testPassword')
+        response = self.client.get('/cs-111111/', follow=True)
+        self.assertEqual(response.status_code, 404)
+        self.assertEqual(len(response.redirect_chain), 0)
+
+        instance1 = CourseInstance.objects.create(
+            visible_to_students=False,
+            instance_name="Instance 1",
+            starting_time=self.today - timedelta(weeks=15),
+            ending_time=self.today + timedelta(weeks=1),
+            course=course,
+            url="instance1",
+        )
+
+        response = self.client.get('/cs-111111/', follow=True)
+        self.assertEqual(response.status_code, 404)
+        self.assertEqual(len(response.redirect_chain), 0)
+
+        # Show the course instance to students and the user should be redirected there.
+        instance1.visible_to_students = True
+        instance1.save()
+        instance1.enroll_student(self.user1)
+
+        response = self.client.get('/cs-111111/', follow=True)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        self.assertEqual(response.redirect_chain[0][0], '/cs-111111/instance1/')
+
+    def test_last_instance_view_new_instance_has_closed_module(self):
+        course = Course.objects.create(
+            name="Last instance view course",
+            code="CS-111111",
+            url="cs-111111",
+        )
+
+        # One year ago.
+        instance1 = CourseInstance.objects.create(
+            instance_name="Instance 1",
+            starting_time=self.today - timedelta(weeks=52),
+            ending_time=self.today - timedelta(weeks=40),
+            course=course,
+            url="instance1",
+        )
+        module1 = CourseModule.objects.create(
+            name="Module 1",
+            url="module1",
+            points_to_pass=10,
+            course_instance=instance1,
+            opening_time=self.today - timedelta(weeks=51),
+            closing_time=self.today - timedelta(weeks=41),
+            status=CourseModule.STATUS.READY,
+        )
+
+        # Course instance has opened, but the modules have not yet opened.
+        instance2 = CourseInstance.objects.create(
+            instance_name="Instance 2",
+            starting_time=self.today - timedelta(days=3),
+            ending_time=self.today + timedelta(weeks=7),
+            course=course,
+            url="instance2",
+        )
+        module2 = CourseModule.objects.create(
+            name="Module 2",
+            url="module2",
+            points_to_pass=10,
+            course_instance=instance2,
+            opening_time=self.today + timedelta(days=2),
+            closing_time=self.today + timedelta(weeks=3),
+            status=CourseModule.STATUS.READY,
+        )
+        module2b = CourseModule.objects.create(
+            name="Module 2b",
+            url="module2b",
+            points_to_pass=20,
+            course_instance=instance2,
+            opening_time=self.today + timedelta(weeks=2),
+            closing_time=self.today + timedelta(weeks=6),
+            status=CourseModule.STATUS.READY,
+        )
+
+        # Starts in 6 months.
+        instance3 = CourseInstance.objects.create(
+            instance_name="Instance 3",
+            starting_time=self.today + timedelta(weeks=25),
+            ending_time=self.today + timedelta(weeks=40),
+            course=course,
+            url="instance3",
+        )
+        module3 = CourseModule.objects.create(
+            name="Module 3",
+            url="module3",
+            points_to_pass=10,
+            course_instance=instance3,
+            opening_time=self.today + timedelta(weeks=25),
+            closing_time=self.today + timedelta(weeks=30),
+            status=CourseModule.STATUS.READY,
+        )
+
+        instance1.enroll_student(self.user1)
+        instance2.enroll_student(self.user1)
+        self.client.login(username='testUser1', password='testPassword')
+        response = self.client.get('/cs-111111/', follow=True)
+
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        self.assertEqual(response.redirect_chain[0][0], '/cs-111111/instance1/')
+
+        # Move the module opening time in instance2 so that it has already opened.
+        module2.opening_time = self.today - timedelta(days=1)
+        module2.save()
+
+        response = self.client.get('/cs-111111/', follow=True)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.redirect_chain), 1)
+        self.assertEqual(response.redirect_chain[0][0], '/cs-111111/instance2/')