Add "last completed block" to dropdown

This commit is contained in:
Gregory Martin
2018-02-12 13:45:16 -05:00
committed by Sanford Student
parent 335bf4b035
commit a5d8cbb8e9
7 changed files with 202 additions and 6 deletions

View File

@@ -1,11 +1,22 @@
""" Unit tests for custom UserProfile properties. """
from __future__ import absolute_import, division, print_function, unicode_literals
import ddt
from django.test import TestCase
from openedx.core.djangolib.testing.utils import skip_unless_lms
from django.test.utils import override_settings
from mock import patch
from ..utils import validate_social_link, format_social_link
from completion import models
from completion.test_utils import CompletionWaffleTestMixin
from openedx.core.djangoapps.user_api.accounts.utils import retrieve_last_sitewide_block_completed
from openedx.core.djangolib.testing.utils import skip_unless_lms
from student.models import CourseEnrollment
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from ..utils import format_social_link, validate_social_link
@ddt.ddt
@@ -51,3 +62,77 @@ class UserAccountSettingsTest(TestCase):
self.assertEqual(is_valid_expected, self.validate_social_link(platform_name, link_input))
self.assertEqual(formatted_link_expected, format_social_link(platform_name, link_input))
@ddt.ddt
class CompletionUtilsTestCase(SharedModuleStoreTestCase, CompletionWaffleTestMixin, TestCase):
"""
Test completion utility functions
"""
def setUp(self):
"""
Creates a test course that can be used for non-destructive tests
"""
super(CompletionUtilsTestCase, self).setUp()
self.override_waffle_switch(True)
self.engaged_user = UserFactory.create()
self.cruft_user = UserFactory.create()
self.course = self.create_test_course()
self.submit_faux_completions()
def create_test_course(self):
"""
Create, populate test course.
"""
course = CourseFactory.create()
with self.store.bulk_operations(course.id):
self.chapter = ItemFactory.create(category='chapter', parent_location=course.location)
self.sequential = ItemFactory.create(category='sequential', parent_location=self.chapter.location)
self.vertical1 = ItemFactory.create(category='vertical', parent_location=self.sequential.location)
self.vertical2 = ItemFactory.create(category='vertical', parent_location=self.sequential.location)
course.children = [self.chapter]
self.chapter.children = [self.sequential]
self.sequential.children = [self.vertical1, self.vertical2]
if hasattr(self, 'user_one'):
CourseEnrollment.enroll(self.engaged_user, course.id)
if hasattr(self, 'user_two'):
CourseEnrollment.enroll(self.cruft_user, course.id)
return course
def submit_faux_completions(self):
"""
Submit completions (only for user_one)g
"""
for block in self.course.children[0].children[0].children:
models.BlockCompletion.objects.submit_completion(
user=self.engaged_user,
course_key=self.course.id,
block_key=block.location,
completion=1.0
)
@override_settings(LMS_ROOT_URL='test_url:9999')
@patch('completion.waffle.get_current_site')
@ddt.data(True, False)
def test_retrieve_last_sitewide_block_completed(self, use_username, get_patched_current_site): # pylint: disable=unused-argument
"""
Test that the method returns a URL for the "last completed" block
when sending a user object
"""
block_url = retrieve_last_sitewide_block_completed(
self.engaged_user.username if use_username else self.engaged_user
)
empty_block_url = retrieve_last_sitewide_block_completed(
self.cruft_user.username if use_username else self.cruft_user
)
self.assertEqual(
block_url,
u'test_url:9999/courses/{org}/{course}/{run}/jump_to/i4x://{org}/{course}/vertical/{vertical_id}'.format(
org=self.course.location.course_key.org,
course=self.course.location.course_key.course,
run=self.course.location.course_key.run,
vertical_id=self.vertical2.location.block_id,
)
)
self.assertEqual(empty_block_url, None)

View File

@@ -5,7 +5,15 @@ import re
from urlparse import urlparse
from django.conf import settings
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from six import text_type
from completion import waffle as completion_waffle
from completion.models import BlockCompletion
from openedx.core.djangoapps.site_configuration.models import SiteConfiguration
from openedx.core.djangoapps.theming.helpers import get_config_value_from_site_or_settings, get_current_site
from xmodule.modulestore.django import modulestore
def validate_social_link(platform_name, new_social_link):
@@ -89,3 +97,77 @@ def _is_valid_social_username(value):
in the username.
"""
return '/' not in value
def retrieve_last_sitewide_block_completed(username):
"""
Completion utility
From a string 'username' or object User retrieve
the last course block marked as 'completed' and construct a URL
:param username: str(username) or obj(User)
:return: block_lms_url
"""
if not completion_waffle.waffle().is_enabled(completion_waffle.ENABLE_COMPLETION_TRACKING):
return
if not isinstance(username, User):
userobj = User.objects.get(username=username)
else:
userobj = username
latest_completions_by_course = BlockCompletion.latest_blocks_completed_all_courses(userobj)
known_site_configs = [
other_site_config.get_value('course_org_filter') for other_site_config in SiteConfiguration.objects.all()
if other_site_config.get_value('course_org_filter')
]
current_site_configuration = get_config_value_from_site_or_settings(
name='course_org_filter',
site=get_current_site()
)
# courses.edx.org has no 'course_org_filter'
# however the courses within DO, but those entries are not found in
# known_site_configs, which are White Label sites
# This is necessary because the WL sites and courses.edx.org
# have the same AWS RDS mySQL instance
candidate_course = None
candidate_block_key = None
latest_date = None
# Go through dict, find latest
for course, [modified_date, block_key] in latest_completions_by_course.items():
if not current_site_configuration:
# This is a edx.org
if course.org in known_site_configs:
continue
if not latest_date or modified_date > latest_date:
candidate_course = course
candidate_block_key = block_key
latest_date = modified_date
else:
# This is a White Label site, and we should find candidates from the same site
if course.org not in current_site_configuration:
# Not the same White Label, or a edx.org course
continue
if not latest_date or modified_date > latest_date:
candidate_course = course
candidate_block_key = block_key
latest_date = modified_date
if not candidate_course:
return
lms_root = SiteConfiguration.get_value_for_org(candidate_course.org, "LMS_ROOT_URL", settings.LMS_ROOT_URL)
item = modulestore().get_item(candidate_block_key, depth=1)
if not lms_root:
return
return u"{lms_root}/courses/{course_key}/jump_to/{location}".format(
lms_root=lms_root,
course_key=text_type(item.location.course_key),
location=text_type(item.location),
)

View File

@@ -10,6 +10,7 @@ from completion.models import BlockCompletion
from completion.test_utils import CompletionWaffleTestMixin
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.test import override_settings
from mock import Mock, patch
from six import text_type
@@ -402,6 +403,7 @@ class TestCourseOutlineResumeCourse(SharedModuleStoreTestCase, CompletionWaffleT
),
active=True
)
@override_settings(LMS_BASE='test_url:9999')
@patch('completion.waffle.get_current_site')
def test_resume_course_with_completion_api(self, get_patched_current_site):
"""