Merge pull request #23550 from edx/ndalfonso/AA-85-reset-dates-mobile

AA-85 mobile reset dates
This commit is contained in:
Nick
2020-04-02 09:53:50 -04:00
committed by GitHub
16 changed files with 245 additions and 69 deletions

View File

@@ -4,6 +4,9 @@
<%namespace name="static" file="../../static_content.html"/>
% if display_reset_dates_banner:
<%include file="/reset_deadlines_banner.html" />
% endif
% for course_date in course_date_blocks:
<%include file="../dates-summary.html" args="course_date=course_date"/>
% endfor

View File

@@ -24,6 +24,7 @@ from waffle.models import Switch
from waffle.testutils import override_switch
from lms.djangoapps.courseware.tests.factories import StaffFactory
from lms.urls import RESET_COURSE_DEADLINES_NAME
from gating import api as lms_gating_api
from lms.djangoapps.course_api.blocks.transformers.milestones import MilestonesAndSpecialExamsTransformer
from openedx.core.djangoapps.schedules.models import Schedule
@@ -170,8 +171,8 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
start_date=timezone.now() - datetime.timedelta(1),
enrollment=enrollment
)
url = '{}{}'.format(course_home_url(course), 'reset_deadlines')
self.client.post(url)
post_dict = {'reset_deadlines_redirect_url_id_dict': json.dumps({'course_id': str(course.id)})}
self.client.post(reverse(RESET_COURSE_DEADLINES_NAME), post_dict)
updated_schedule = Schedule.objects.get(enrollment=enrollment)
self.assertEqual(updated_schedule.start_date.date(), datetime.datetime.today().date())
@@ -204,8 +205,8 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
assert response.status_code == 200
url = '{}{}'.format(course_home_url(course), 'reset_deadlines')
self.client.post(url)
post_dict = {'reset_deadlines_redirect_url_id_dict': json.dumps({'course_id': str(course.id)})}
self.client.post(reverse(RESET_COURSE_DEADLINES_NAME), post_dict)
updated_schedule = Schedule.objects.get(id=student_schedule.id)
self.assertEqual(updated_schedule.start_date.date(), datetime.datetime.today().date())
updated_staff_schedule = Schedule.objects.get(id=staff_schedule.id)
@@ -240,8 +241,8 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
assert response.status_code == 200
url = '{}{}'.format(course_home_url(course), 'reset_deadlines')
self.client.post(url)
post_dict = {'reset_deadlines_redirect_url_id_dict': json.dumps({'course_id': str(course.id)})}
self.client.post(reverse(RESET_COURSE_DEADLINES_NAME), post_dict)
updated_student_schedule = Schedule.objects.get(id=student_schedule.id)
self.assertEqual(updated_student_schedule.start_date, student_schedule.start_date)
updated_staff_schedule = Schedule.objects.get(id=staff_schedule.id)

View File

@@ -7,18 +7,21 @@ from django.conf.urls import url
from .views.course_dates import CourseDatesFragmentMobileView
from .views.course_home import CourseHomeFragmentView, CourseHomeView
from .views.course_outline import CourseOutlineFragmentView, reset_course_deadlines
from .views.course_outline import CourseOutlineFragmentView
from .views.course_reviews import CourseReviewsView
from .views.course_sock import CourseSockFragmentView
from .views.course_updates import CourseUpdatesFragmentView, CourseUpdatesView
from .views.latest_update import LatestUpdateFragmentView
from .views.welcome_message import WelcomeMessageFragmentView, dismiss_welcome_message
COURSE_HOME_VIEW_NAME = 'openedx.course_experience.course_home'
COURSE_DATES_FRAGMENT_VIEW_NAME = 'openedx.course_experience.mobile_dates_fragment_view'
urlpatterns = [
url(
r'^$',
CourseHomeView.as_view(),
name='openedx.course_experience.course_home',
name=COURSE_HOME_VIEW_NAME,
),
url(
r'^updates$',
@@ -68,11 +71,6 @@ urlpatterns = [
url(
r'^mobile_dates_fragment',
CourseDatesFragmentMobileView.as_view(),
name='openedx.course_experience.mobile_dates_fragment_view',
),
url(
r'^reset_deadlines$',
reset_course_deadlines,
name='openedx.course_experience.reset_course_deadlines',
name=COURSE_DATES_FRAGMENT_VIEW_NAME,
),
]

View File

@@ -3,13 +3,20 @@ Common utilities for the course experience, including course outline.
"""
from datetime import timedelta
from completion.models import BlockCompletion
from django.utils import timezone
from opaque_keys.edx.keys import CourseKey
from six.moves import range
from course_modes.models import CourseMode
from lms.djangoapps.course_api.blocks.api import get_blocks
from lms.djangoapps.course_blocks.utils import get_student_module_as_dict
from lms.djangoapps.courseware.access import has_access
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.lib.cache_utils import request_cached
from student.models import CourseEnrollment
from xmodule.modulestore.django import modulestore
@@ -232,3 +239,36 @@ def get_resume_block(block):
if resume_block:
return resume_block
return block
def reset_deadlines_banner_should_display(course_key, request):
"""
Return whether or not the reset banner should display,
determined by whether or not a course has any past-due,
incomplete sequentials
"""
display_reset_dates_banner = False
course_overview = CourseOverview.objects.get(id=str(course_key))
course_end_date = getattr(course_overview, 'end_date', None)
is_self_paced = getattr(course_overview, 'self_paced', False)
is_course_staff = bool(
request.user and course_overview and has_access(request.user, 'staff', course_overview, course_overview.id)
)
if is_self_paced and (not is_course_staff) and (not course_end_date or timezone.now() < course_end_date):
if (CourseEnrollment.objects.filter(
course=course_overview, user=request.user, mode=CourseMode.VERIFIED
).exists()):
course_block_tree = get_course_outline_block_tree(
request, str(course_key), request.user
)
course_sections = course_block_tree.get('children', [])
for section in course_sections:
if display_reset_dates_banner:
break
for subsection in section.get('children', []):
if (not subsection.get('complete', True)
and subsection.get('due', timezone.now() + timedelta(1)) < timezone.now()):
display_reset_dates_banner = True
break
return display_reset_dates_banner

View File

@@ -5,9 +5,14 @@ Fragment for rendering the course dates sidebar.
from django.http import Http404
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie
from django.utils.translation import get_language_bidi
from opaque_keys.edx.keys import CourseKey
from web_fragments.fragment import Fragment
from openedx.features.course_experience import RELATIVE_DATES_FLAG
from openedx.features.course_experience.utils import reset_deadlines_banner_should_display
from lms.djangoapps.courseware.courses import get_course_date_blocks, get_course_with_access
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
@@ -49,10 +54,10 @@ class CourseDatesFragmentMobileView(CourseDatesFragmentView):
"""
template_name = 'course_experience/mobile/course-dates-fragment.html'
@method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs):
if not request.user.is_authenticated:
raise Http404
print('****************', CourseDatesFragmentMobileView.__dict__)
return super(CourseDatesFragmentMobileView, self).get(request, *args, **kwargs)
def css_dependencies(self):
@@ -67,3 +72,37 @@ class CourseDatesFragmentMobileView(CourseDatesFragmentView):
return self.get_css_dependencies('style-mobile-rtl')
else:
return self.get_css_dependencies('style-mobile')
def render_to_fragment(self, request, course_id=None, **kwargs):
"""
Render the course dates fragment.
"""
from lms.urls import RESET_COURSE_DEADLINES_NAME
from openedx.features.course_experience.urls import COURSE_DATES_FRAGMENT_VIEW_NAME
course_key = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False)
course_date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=2)
display_reset_dates_banner = False
if RELATIVE_DATES_FLAG.is_enabled(course.id):
display_reset_dates_banner = reset_deadlines_banner_should_display(course_key, request)
reset_deadlines_url = reverse(RESET_COURSE_DEADLINES_NAME) if display_reset_dates_banner else None
reset_deadlines_redirect_url_base = COURSE_DATES_FRAGMENT_VIEW_NAME if (
reset_deadlines_url) else None
context = {
'course_date_blocks': [block for block in course_date_blocks if block.title != 'current_datetime'],
'display_reset_dates_banner': display_reset_dates_banner,
'reset_deadlines_url': reset_deadlines_url,
'reset_deadlines_redirect_url_base': reset_deadlines_redirect_url_base,
'reset_deadlines_redirect_url_id_dict': {'course_id': course_id}
}
html = render_to_string(self.template_name, context)
dates_fragment = Fragment(html)
self.add_fragment_resource_urls(dates_fragment)
return dates_fragment

View File

@@ -160,24 +160,3 @@ class CourseOutlineFragmentView(EdxFragmentView):
if children:
children[0]['resume_block'] = True
self.mark_first_unit_to_resume(children[0])
@ensure_csrf_cookie
def reset_course_deadlines(request, course_id):
"""
Set the start_date of a schedule to today, which in turn will adjust due dates for
sequentials belonging to a self paced course
"""
course_key = CourseKey.from_string(course_id)
masquerade_details, masquerade_user = setup_masquerade(
request,
course_key,
has_access(request.user, 'staff', course_key)
)
if masquerade_details and masquerade_details.role == 'student' and masquerade_details.user_name:
# Masquerading as a specific student, so reset that student's schedule
user = masquerade_user
else:
user = request.user
reset_self_paced_schedule(user, course_key)
return redirect(reverse('openedx.course_experience.course_home', args=[six.text_type(course_key)]))