Revert commits for AA-4

This commit is contained in:
Dillon Dumesnil
2020-02-10 12:10:59 -05:00
parent 7b68869a14
commit 00fe1495b4
16 changed files with 143 additions and 421 deletions

View File

@@ -10,7 +10,7 @@ from crum import get_current_request
from django.utils.translation import ugettext as _
from course_modes.models import CourseMode
from lms.djangoapps.courseware.utils import verified_upgrade_deadline_link
from lms.djangoapps.courseware.date_summary import verified_upgrade_deadline_link
from openedx.features.course_experience.course_tools import CourseTool
from student.models import CourseEnrollment

View File

@@ -16,7 +16,6 @@ from django.db.models import Prefetch
from django.http import Http404, QueryDict
from django.urls import reverse
from edx_django_utils.monitoring import function_trace
from edx_when.api import get_dates_for_course
from fs.errors import ResourceNotFound
from opaque_keys.edx.keys import UsageKey
from path import Path as path
@@ -28,9 +27,7 @@ from lms.djangoapps.courseware.access import has_access
from lms.djangoapps.courseware.access_response import MilestoneAccessError, StartDateError
from lms.djangoapps.courseware.date_summary import (
CertificateAvailableDate,
CourseAssignmentDate,
CourseEndDate,
CourseExpiredDate,
CourseStartDate,
TodaysDate,
VerificationDeadlineDate,
@@ -393,14 +390,13 @@ def get_course_info_section(request, user, course, section_key):
return html
def get_course_date_blocks(course, user, request=None, include_past_dates=False, num_assignments=None):
def get_course_date_blocks(course, user):
"""
Return the list of blocks to display on the course info page,
sorted by date.
"""
block_classes = [
CourseEndDate,
CourseExpiredDate,
CourseStartDate,
TodaysDate,
VerificationDeadlineDate,
@@ -409,53 +405,17 @@ def get_course_date_blocks(course, user, request=None, include_past_dates=False,
if certs_api.get_active_web_certificate(course):
block_classes.insert(0, CertificateAvailableDate)
blocks = [cls(course, user) for cls in block_classes]
blocks.extend(get_course_assignment_due_dates(
course, user, request, num_return=num_assignments, include_past_dates=include_past_dates))
blocks = (cls(course, user) for cls in block_classes)
return sorted((b for b in blocks if b.date and (b.is_enabled or include_past_dates)), key=date_block_key_fn)
def date_block_key_fn(block):
"""
If the block's date is None, return the maximum datetime in order
to force it to the end of the list of displayed blocks.
"""
return block.date or datetime.max.replace(tzinfo=pytz.UTC)
def get_course_assignment_due_dates(course, user, request, num_return=None, include_past_dates=False):
"""
Returns a list of assignment (at the subsection/sequential level) due date
blocks for the given course. Will return num_return results or all results
if num_return is None in date increasing order.
"""
store = modulestore()
all_course_dates = get_dates_for_course(course.id, user)
date_blocks = []
for (block_key, date_type), date in all_course_dates.items():
if date_type == 'due' and block_key.block_type == 'sequential':
try:
item = store.get_item(block_key)
except ItemNotFoundError:
continue
if item.graded:
date_block = CourseAssignmentDate(course, user)
date_block.date = date
block_url = None
now = datetime.now().replace(tzinfo=pytz.UTC)
assignment_released = item.start < now
if assignment_released:
block_url = reverse('jump_to', args=[course.id, block_key])
block_url = request.build_absolute_uri(block_url) if request else None
date_block.set_title(item.display_name, block_url)
date_blocks.append(date_block)
date_blocks = sorted((b for b in date_blocks if b.is_enabled or include_past_dates), key=date_block_key_fn)
if num_return:
return date_blocks[:num_return]
return date_blocks
def block_key_fn(block):
"""
If the block's date is None, return the maximum datetime in order
to force it to the end of the list of displayed blocks.
"""
if block.date is None:
return datetime.max.replace(tzinfo=pytz.UTC)
return block.date
return sorted((b for b in blocks if b.is_enabled), key=block_key_fn)
# TODO: Fix this such that these are pulled in as extra course-specific tabs.

View File

@@ -20,14 +20,11 @@ from lazy import lazy
from pytz import utc
from course_modes.models import CourseMode, get_cosmetic_verified_display_price
from lms.djangoapps.courseware.utils import verified_upgrade_deadline_link, verified_upgrade_link_is_valid
from lms.djangoapps.commerce.utils import EcommerceService
from lms.djangoapps.verify_student.models import VerificationDeadline
from lms.djangoapps.verify_student.services import IDVerificationService
from openedx.core.djangoapps.catalog.utils import get_course_run_details
from openedx.core.djangoapps.certificates.api import can_show_certificate_available_date_field
from openedx.core.djangolib.markup import HTML, Text
from openedx.features.course_duration_limits.access import get_user_course_expiration_date
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
from openedx.features.course_experience import UPGRADE_DEADLINE_MESSAGE, CourseHomeMessages
from student.models import CourseEnrollment
@@ -290,14 +287,6 @@ class CourseEndDate(DateSummary):
@property
def date(self):
if self.course.self_paced:
weeks_to_complete = get_course_run_details(self.course.id, ['weeks_to_complete']).get('weeks_to_complete')
if weeks_to_complete:
course_duration = datetime.timedelta(weeks=weeks_to_complete)
if self.course.end < (self.current_time + course_duration):
return self.course.end
return None
return self.course.end
def register_alerts(self, request, course):
@@ -329,60 +318,6 @@ class CourseEndDate(DateSummary):
)
class CourseAssignmentDate(DateSummary):
"""
Displays due dates for homework assignments with a link to the homework
assignment if the link is provided.
"""
css_class = 'assignment'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.assignment_date = None
self.assignment_title = None
@property
def date(self):
return self.assignment_date
@date.setter
def date(self, date):
self.assignment_date = date
@property
def title(self):
return self.assignment_title
def set_title(self, title, link=None):
if link:
self.assignment_title = HTML(
'<a href="{assignment_link}">{assignment_title}</a>'
).format(assignment_link=link, assignment_title=title)
else:
self.assignment_title = title
class CourseExpiredDate(DateSummary):
"""
Displays the course expiration date for Audit learners (if enabled)
"""
css_class = 'course-expired'
@property
def date(self):
if not CourseDurationLimitConfig.enabled_for_enrollment(user=self.user, course_key=self.course_id):
return
return get_user_course_expiration_date(self.user, self.course)
@property
def description(self):
return _('You lose all access to this course, including your progress.')
@property
def title(self):
return _('Audit Access Expires')
class CertificateAvailableDate(DateSummary):
"""
Displays the certificate available date of the course.
@@ -449,6 +384,53 @@ class CertificateAvailableDate(DateSummary):
)
def verified_upgrade_deadline_link(user, course=None, course_id=None):
"""
Format the correct verified upgrade link for the specified ``user``
in a course.
One of ``course`` or ``course_id`` must be supplied. If both are specified,
``course`` will take priority.
Arguments:
user (:class:`~django.contrib.auth.models.User`): The user to display
the link for.
course (:class:`.CourseOverview`): The course to render a link for.
course_id (:class:`.CourseKey`): The course_id of the course to render for.
Returns:
The formatted link that will allow the user to upgrade to verified
in this course.
"""
if course is not None:
course_id = course.id
return EcommerceService().upgrade_url(user, course_id)
def verified_upgrade_link_is_valid(enrollment=None):
"""
Return whether this enrollment can be upgraded.
Arguments:
enrollment (:class:`.CourseEnrollment`): The enrollment under consideration.
If None, then the enrollment is considered to be upgradeable.
"""
# Return `true` if user is not enrolled in course
if enrollment is None:
return False
upgrade_deadline = enrollment.upgrade_deadline
if upgrade_deadline is None:
return False
if datetime.datetime.now(utc).date() > upgrade_deadline.date():
return False
# Show the summary if user enrollment is in which allow user to upsell
return enrollment.is_active and enrollment.mode in CourseMode.UPSELL_TO_VERIFIED_MODES
class VerifiedUpgradeDeadlineDate(DateSummary):
"""
Displays the date before which learners must upgrade to the

View File

@@ -21,7 +21,7 @@ from xblock.field_data import DictFieldData
from edxmako.shortcuts import render_to_string
from lms.djangoapps.courseware.access import has_access
from lms.djangoapps.courseware.utils import verified_upgrade_deadline_link
from lms.djangoapps.courseware.date_summary import verified_upgrade_deadline_link
from lms.djangoapps.courseware.masquerade import handle_ajax, setup_masquerade
from lms.djangoapps.lms_xblock.field_data import LmsFieldData
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview

View File

@@ -18,9 +18,7 @@ from course_modes.tests.factories import CourseModeFactory
from lms.djangoapps.courseware.courses import get_course_date_blocks
from lms.djangoapps.courseware.date_summary import (
CertificateAvailableDate,
CourseAssignmentDate,
CourseEndDate,
CourseExpiredDate,
CourseStartDate,
TodaysDate,
VerificationDeadlineDate,
@@ -40,11 +38,10 @@ from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG, UPGRADE_DEADLINE_MESSAGE, CourseHomeMessages
from student.tests.factories import TEST_PASSWORD, CourseEnrollmentFactory, UserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from xmodule.modulestore.tests.factories import CourseFactory
@ddt.ddt
@@ -132,138 +129,6 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED)
self.assert_block_types(course, user, expected_blocks)
def test_enabled_block_types_with_assignments(self):
"""
Creates a course with multiple subsections to test all of the different
cases for assignment dates showing up. Mocks out calling the edx-when
service and then validates the correct data is set and returned.
"""
course = create_course_run(days_till_start=-100)
user = create_user()
request = RequestFactory().request()
request.user = user
CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED)
now = datetime.now(utc)
assignment_title_html = ['<a href=', '</a>']
with self.store.bulk_operations(course.id):
section = ItemFactory.create(category='chapter', parent_location=course.location)
subsection_1 = ItemFactory.create(
category='sequential',
display_name='Released',
parent_location=section.location,
start=now - timedelta(days=1),
due=now + timedelta(days=6),
graded=True,
)
subsection_2 = ItemFactory.create(
category='sequential',
display_name='Not released',
parent_location=section.location,
start=now + timedelta(days=1),
due=now + timedelta(days=7),
graded=True,
)
subsection_3 = ItemFactory.create(
category='sequential',
display_name='Third nearest assignment',
parent_location=section.location,
start=now + timedelta(days=1),
due=now + timedelta(days=8),
graded=True,
)
subsection_4 = ItemFactory.create(
category='sequential',
display_name='Past due date',
parent_location=section.location,
start=now - timedelta(days=14),
due=now - timedelta(days=7),
graded=True,
)
subsection_5 = ItemFactory.create(
category='sequential',
display_name='Not returned since we do not get non-graded subsections',
parent_location=section.location,
start=now + timedelta(days=1),
due=now - timedelta(days=7),
graded=False,
)
with patch('lms.djangoapps.courseware.courses.get_dates_for_course') as mock_get_dates:
mock_get_dates.return_value = {
(subsection_1.location, 'due'): subsection_1.due,
(subsection_1.location, 'start'): subsection_1.start,
(subsection_2.location, 'due'): subsection_2.due,
(subsection_2.location, 'start'): subsection_2.start,
(subsection_3.location, 'due'): subsection_3.due,
(subsection_3.location, 'start'): subsection_3.start,
(subsection_4.location, 'due'): subsection_4.due,
(subsection_4.location, 'start'): subsection_4.start,
(subsection_5.location, 'due'): subsection_5.due,
(subsection_5.location, 'start'): subsection_5.start,
}
# Standard widget case where we restrict the number of assignments.
expected_blocks = (
TodaysDate, CourseAssignmentDate, CourseAssignmentDate, CourseEndDate, VerificationDeadlineDate
)
blocks = get_course_date_blocks(course, user, request, num_assignments=2)
self.assertEqual(len(blocks), len(expected_blocks))
self.assertEqual(set(type(b) for b in blocks), set(expected_blocks))
assignment_blocks = filter(lambda b: isinstance(b, CourseAssignmentDate), blocks)
for assignment in assignment_blocks:
assignment_title = str(assignment.title)
self.assertNotEqual(assignment_title, 'Third nearest assignment')
self.assertNotEqual(assignment_title, 'Past due date')
self.assertNotEqual(assignment_title, 'Not returned since we do not get non-graded subsections')
# checking if it is _in_ the title instead of being the title since released assignments
# are actually links. Unreleased assignments are just the string of the title.
if 'Released' in assignment_title:
for html_tag in assignment_title_html:
self.assertIn(html_tag, assignment_title)
elif assignment_title == 'Not released':
for html_tag in assignment_title_html:
self.assertNotIn(html_tag, assignment_title)
# No restrictions on number of assignments to return
expected_blocks = (
CourseStartDate, TodaysDate, CourseAssignmentDate, CourseAssignmentDate, CourseAssignmentDate,
CourseAssignmentDate, CourseEndDate, VerificationDeadlineDate
)
blocks = get_course_date_blocks(course, user, request, include_past_dates=True)
self.assertEqual(len(blocks), len(expected_blocks))
self.assertEqual(set(type(b) for b in blocks), set(expected_blocks))
assignment_blocks = filter(lambda b: isinstance(b, CourseAssignmentDate), blocks)
for assignment in assignment_blocks:
assignment_title = str(assignment.title)
self.assertNotEqual(assignment_title, 'Not returned since we do not get non-graded subsections')
# checking if it is _in_ the title instead of being the title since released assignments
# are actually links. Unreleased assignments are just the string of the title.
if 'Released' in assignment_title:
for html_tag in assignment_title_html:
self.assertIn(html_tag, assignment_title)
elif assignment_title == 'Not released':
for html_tag in assignment_title_html:
self.assertNotIn(html_tag, assignment_title)
elif assignment_title == 'Third nearest assignment':
# It's still not released
for html_tag in assignment_title_html:
self.assertNotIn(html_tag, assignment_title)
elif 'Past due date' in assignment_title:
self.assertGreater(now, assignment.date)
for html_tag in assignment_title_html:
self.assertIn(html_tag, assignment_title)
def test_enabled_block_types_with_expired_course(self):
course = create_course_run(days_till_start=-100)
user = create_user()
# These two lines are to trigger the course expired block to be rendered
CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.AUDIT)
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=utc))
expected_blocks = (
TodaysDate, CourseEndDate, CourseExpiredDate, VerifiedUpgradeDeadlineDate
)
self.assert_block_types(course, user, expected_blocks)
@ddt.data(
# Course not started
({}, (CourseStartDate, TodaysDate, CourseEndDate)),
@@ -312,7 +177,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
html_elements = [
'<h3 class="hd hd-6 handouts-header">Upcoming Dates</h3>',
'<div class="date-summary',
'<div class="date-summary-container">',
'<p class="hd hd-6 date localized-datetime"',
'data-timezone="None"'
]
@@ -337,7 +202,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
html_elements = [
'<h3 class="hd hd-6 handouts-header">Upcoming Dates</h3>',
'<div class="date-summary',
'<div class="date-summary-container">',
'<p class="hd hd-6 date localized-datetime"',
'data-timezone="America/Los_Angeles"'
]
@@ -422,32 +287,6 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
)
self.assertEqual(block.title, 'Course End')
@ddt.data(
{'weeks_to_complete': 7}, # Weeks to complete > time til end (end date shown)
{'weeks_to_complete': 4}, # Weeks to complete < time til end (end date not shown)
)
def test_course_end_date_self_paced(self, cr_details):
"""
In self-paced courses, the end date will now only show up if the learner
views the course within the course's weeks to complete (as defined in
the course-discovery service). E.g. if the weeks to complete is 5 weeks
and the course doesn't end for 10 weeks, there will be no end date, but
if the course ends in 3 weeks, the end date will appear.
"""
now = datetime.now(utc)
end_timedelta_number = 5
course = CourseFactory.create(
start=now + timedelta(days=-7), end=now + timedelta(weeks=end_timedelta_number), self_paced=True)
user = create_user()
with patch('lms.djangoapps.courseware.date_summary.get_course_run_details') as mock_get_cr_details:
mock_get_cr_details.return_value = cr_details
block = CourseEndDate(course, user)
self.assertEqual(block.title, 'Course End')
if cr_details['weeks_to_complete'] > end_timedelta_number:
self.assertEqual(block.date, course.end)
else:
self.assertIsNone(block.date)
def test_ecommerce_checkout_redirect(self):
"""Verify the block link redirects to ecommerce checkout if it's enabled."""
sku = 'TESTSKU'

View File

@@ -1,56 +0,0 @@
"""Utility functions that have to do with the courseware."""
import datetime
from lms.djangoapps.commerce.utils import EcommerceService
from pytz import utc
from course_modes.models import CourseMode
def verified_upgrade_deadline_link(user, course=None, course_id=None):
"""
Format the correct verified upgrade link for the specified ``user``
in a course.
One of ``course`` or ``course_id`` must be supplied. If both are specified,
``course`` will take priority.
Arguments:
user (:class:`~django.contrib.auth.models.User`): The user to display
the link for.
course (:class:`.CourseOverview`): The course to render a link for.
course_id (:class:`.CourseKey`): The course_id of the course to render for.
Returns:
The formatted link that will allow the user to upgrade to verified
in this course.
"""
if course is not None:
course_id = course.id
return EcommerceService().upgrade_url(user, course_id)
def verified_upgrade_link_is_valid(enrollment=None):
"""
Return whether this enrollment can be upgraded.
Arguments:
enrollment (:class:`.CourseEnrollment`): The enrollment under consideration.
If None, then the enrollment is considered to be upgradeable.
"""
# Return `true` if user is not enrolled in course
if enrollment is None:
return False
upgrade_deadline = enrollment.upgrade_deadline
if upgrade_deadline is None:
return False
if datetime.datetime.now(utc).date() > upgrade_deadline.date():
return False
# Show the summary if user enrollment is in which allow user to upsell
return enrollment.is_active and enrollment.mode in CourseMode.UPSELL_TO_VERIFIED_MODES