Commit 0fd06e80 by Renzo Lucioni

Merge pull request #12040 from edx/patch/2016-04-05

Patch/2016 04 05
parents 69305ce8 cc7aa349
......@@ -50,7 +50,9 @@ class CourseMetadata(object):
'is_time_limited',
'is_practice_exam',
'exam_review_rules',
'self_paced'
'self_paced',
'chrome',
'default_tab',
]
@classmethod
......
......@@ -752,6 +752,17 @@ class CourseFields(object):
scope=Scope.settings
)
bypass_home = Boolean(
display_name=_("Bypass Course Home"),
help=_(
"Bypass the course home tab when students arrive from the dashboard, "
"sending them directly to course content."
),
default=False,
scope=Scope.settings,
deprecated=True
)
enable_subsection_gating = Boolean(
display_name=_("Enable Subsection Prerequisites"),
help=_(
......
......@@ -365,6 +365,16 @@ class SelfPacedTestCase(unittest.TestCase):
self.assertFalse(self.course.self_paced)
class BypassHomeTestCase(unittest.TestCase):
"""Tests for setting which allows course home to be bypassed."""
def setUp(self):
super(BypassHomeTestCase, self).setUp()
self.course = get_dummy_course('2012-12-02T12:00')
def test_default(self):
self.assertFalse(self.course.bypass_home)
class CourseDescriptorTestCase(unittest.TestCase):
"""
Tests for a select few functions from CourseDescriptor.
......
......@@ -184,9 +184,7 @@ class AdvancedSettingsPage(CoursePage):
'display_coursenumber',
'display_organization',
'catalog_visibility',
'chrome',
'days_early_for_beta',
'default_tab',
'disable_progress_graph',
'discussion_blackouts',
'discussion_sort_alpha',
......
......@@ -182,7 +182,11 @@ class CourseEndDate(DateSummary):
@property
def description(self):
if datetime.now(pytz.UTC) <= self.date:
mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
if is_active and CourseMode.is_eligible_for_certificate(mode):
return _('To earn a certificate, you must complete all requirements before this date.')
else:
return _('After this date, course content will be archived.')
return _('This course is archived, which means you can review course content but it is no longer active.')
@property
......
......@@ -173,7 +173,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
## CourseEndDate
def test_course_end_date_during_course(self):
def test_course_end_date_for_certificate_eligible_mode(self):
self.setup_course_and_user(days_till_start=-1)
block = CourseEndDate(self.course, self.user)
self.assertEqual(
......@@ -181,6 +181,14 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
'To earn a certificate, you must complete all requirements before this date.'
)
def test_course_end_date_for_non_certificate_eligible_mode(self):
self.setup_course_and_user(days_till_start=-1, enrollment_mode=CourseMode.AUDIT)
block = CourseEndDate(self.course, self.user)
self.assertEqual(
block.description,
'After this date, course content will be archived.'
)
def test_course_end_date_after_course(self):
self.setup_course_and_user(days_till_start=-2, days_till_end=-1)
block = CourseEndDate(self.course, self.user)
......
......@@ -652,6 +652,44 @@ class ViewsTestCase(ModuleStoreTestCase):
response = self.client.get(url)
self.assertRedirects(response, reverse('signin_user') + '?next=' + url)
def test_bypass_course_info(self):
course_id = unicode(self.course_key)
request = self.request_factory.get(
reverse('info', args=[course_id])
)
# Middleware is not supported by the request factory. Simulate a
# logged-in user by setting request.user manually.
request.user = self.user
mako_middleware_process_request(request)
self.assertFalse(self.course.bypass_home)
self.assertIsNone(request.META.get('HTTP_REFERER')) # pylint: disable=no-member
response = views.course_info(request, course_id)
self.assertEqual(response.status_code, 200)
request.META['HTTP_REFERER'] = reverse('dashboard') # pylint: disable=no-member
response = views.course_info(request, course_id)
self.assertEqual(response.status_code, 200)
self.course.bypass_home = True
self.store.update_item(self.course, self.user.id) # pylint: disable=no-member
self.assertTrue(self.course.bypass_home)
response = views.course_info(request, course_id)
# assertRedirects would be great here, but it forces redirections to be absolute URLs.
self.assertEqual(response.status_code, 302)
self.assertEqual(
response.url,
reverse('courseware', args=[course_id])
)
request.META['HTTP_REFERER'] = 'foo' # pylint: disable=no-member
response = views.course_info(request, course_id)
self.assertEqual(response.status_code, 200)
@attr('shard_1')
# setting TIME_ZONE_DISPLAYED_FOR_DEADLINES explicitly
......
......@@ -694,6 +694,10 @@ def course_info(request, course_id):
if request.user.is_authenticated() and survey.utils.must_answer_survey(course, user):
return redirect(reverse('course_survey', args=[unicode(course.id)]))
is_from_dashboard = reverse('dashboard') in request.META.get('HTTP_REFERER', [])
if course.bypass_home and is_from_dashboard:
return redirect(reverse('courseware', args=[course_id]))
studio_url = get_studio_url(course, 'course_info')
# link to where the student should go to enroll in the course:
......
......@@ -666,6 +666,7 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
self.create_discussion("Chapter 2 / Section 1 / Subsection 2", "Discussion", start=later)
self.create_discussion("Chapter 3 / Section 1", "Discussion", start=later)
self.assertFalse(self.course.self_paced)
self.assert_category_map_equals(
{
"entries": {},
......@@ -696,7 +697,102 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
"children": ["Chapter 1", "Chapter 2"]
}
)
self.maxDiff = None
def test_self_paced_start_date_filter(self):
self.course.self_paced = True
self.course.save()
now = datetime.datetime.now()
later = datetime.datetime.max
self.create_discussion("Chapter 1", "Discussion 1", start=now)
self.create_discussion("Chapter 1", "Discussion 2", start=later)
self.create_discussion("Chapter 2", "Discussion", start=now)
self.create_discussion("Chapter 2 / Section 1 / Subsection 1", "Discussion", start=later)
self.create_discussion("Chapter 2 / Section 1 / Subsection 2", "Discussion", start=later)
self.create_discussion("Chapter 3 / Section 1", "Discussion", start=later)
self.assertTrue(self.course.self_paced)
self.assert_category_map_equals(
{
"entries": {},
"subcategories": {
"Chapter 1": {
"entries": {
"Discussion 1": {
"id": "discussion1",
"sort_key": None,
"is_cohorted": False,
},
"Discussion 2": {
"id": "discussion2",
"sort_key": None,
"is_cohorted": False,
}
},
"subcategories": {},
"children": ["Discussion 1", "Discussion 2"]
},
"Chapter 2": {
"entries": {
"Discussion": {
"id": "discussion3",
"sort_key": None,
"is_cohorted": False,
}
},
"subcategories": {
"Section 1": {
"entries": {},
"subcategories": {
"Subsection 1": {
"entries": {
"Discussion": {
"id": "discussion4",
"sort_key": None,
"is_cohorted": False,
}
},
"subcategories": {},
"children": ["Discussion"]
},
"Subsection 2": {
"entries": {
"Discussion": {
"id": "discussion5",
"sort_key": None,
"is_cohorted": False,
}
},
"subcategories": {},
"children": ["Discussion"]
}
},
"children": ["Subsection 1", "Subsection 2"]
}
},
"children": ["Discussion", "Section 1"]
},
"Chapter 3": {
"entries": {},
"subcategories": {
"Section 1": {
"entries": {
"Discussion": {
"id": "discussion6",
"sort_key": None,
"is_cohorted": False,
}
},
"subcategories": {},
"children": ["Discussion"]
}
},
"children": ["Section 1"]
}
},
"children": ["Chapter 1", "Chapter 2", "Chapter 3"]
}
)
def test_sort_inline_explicit(self):
self.create_discussion("Chapter", "Discussion 1", sort_key="D")
......
......@@ -198,7 +198,7 @@ def get_discussion_id_map(course, user):
return dict(map(get_discussion_id_map_entry, get_accessible_discussion_modules(course, user)))
def _filter_unstarted_categories(category_map):
def _filter_unstarted_categories(category_map, course):
"""
Returns a subset of categories from the provided map which have not yet met the start date
Includes information about category children, subcategories (different), and entries
......@@ -221,7 +221,7 @@ def _filter_unstarted_categories(category_map):
for child in unfiltered_map["children"]:
if child in unfiltered_map["entries"]:
if unfiltered_map["entries"][child]["start_date"] <= now:
if course.self_paced or unfiltered_map["entries"][child]["start_date"] <= now:
filtered_map["children"].append(child)
filtered_map["entries"][child] = {}
for key in unfiltered_map["entries"][child]:
......@@ -230,7 +230,7 @@ def _filter_unstarted_categories(category_map):
else:
log.debug(u"Filtering out:%s with start_date: %s", child, unfiltered_map["entries"][child]["start_date"])
else:
if unfiltered_map["subcategories"][child]["start_date"] < now:
if course.self_paced or unfiltered_map["subcategories"][child]["start_date"] < now:
filtered_map["children"].append(child)
filtered_map["subcategories"][child] = {}
unfiltered_queue.append(unfiltered_map["subcategories"][child])
......@@ -382,7 +382,7 @@ def get_discussion_category_map(course, user, cohorted_if_in_list=False, exclude
_sort_map_entries(category_map, course.discussion_sort_alpha)
return _filter_unstarted_categories(category_map) if exclude_unstarted else category_map
return _filter_unstarted_categories(category_map, course) if exclude_unstarted else category_map
def discussion_category_id_access(course, user, discussion_id):
......
......@@ -190,7 +190,6 @@ if ENV_TOKENS.get('SESSION_COOKIE_NAME', None):
SESSION_COOKIE_NAME = str(ENV_TOKENS.get('SESSION_COOKIE_NAME'))
BOOK_URL = ENV_TOKENS['BOOK_URL']
MEDIA_URL = ENV_TOKENS['MEDIA_URL']
LOG_DIR = ENV_TOKENS['LOG_DIR']
CACHES = ENV_TOKENS['CACHES']
......@@ -619,6 +618,7 @@ if FEATURES.get('ENABLE_OAUTH2_PROVIDER'):
OAUTH_EXPIRE_DELTA_PUBLIC = datetime.timedelta(
days=ENV_TOKENS.get('OAUTH_EXPIRE_PUBLIC_CLIENT_DAYS', OAUTH_EXPIRE_PUBLIC_CLIENT_DAYS)
)
OAUTH_ID_TOKEN_EXPIRATION = ENV_TOKENS.get('OAUTH_ID_TOKEN_EXPIRATION', OAUTH_ID_TOKEN_EXPIRATION)
##### ADVANCED_SECURITY_CONFIG #####
......
......@@ -2551,9 +2551,8 @@ COURSE_ABOUT_VISIBILITY_PERMISSION = 'see_exists'
# Enrollment API Cache Timeout
ENROLLMENT_COURSE_DETAILS_CACHE_TIMEOUT = 60
# for Student Notes we would like to avoid too frequent token refreshes (default is 30 seconds)
if FEATURES['ENABLE_EDXNOTES']:
OAUTH_ID_TOKEN_EXPIRATION = 60 * 60
OAUTH_ID_TOKEN_EXPIRATION = 60 * 60
# These tabs are currently disabled
NOTES_DISABLED_TABS = ['course_structure', 'tags']
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment