Skip to content

Commit

Permalink
Fixed progress page update on save ccx
Browse files Browse the repository at this point in the history
  • Loading branch information
amir-qayyum-khan committed Jul 29, 2016
1 parent cad10c2 commit d5fcb60
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 14 deletions.
17 changes: 17 additions & 0 deletions lms/djangoapps/ccx/api/v0/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from ccx_keys.locator import CCXLocator
from courseware import courses
from xmodule.modulestore.django import SignalHandler
from edx_rest_framework_extensions.authentication import JwtAuthentication
from instructor.enrollment import (
enroll_email,
Expand Down Expand Up @@ -517,6 +518,14 @@ def post(self, request):
)

serializer = self.get_serializer(ccx_course_object)

# using CCX object as sender here.
responses = SignalHandler.course_published.send(
sender=ccx_course_object,
course_key=ccx_course_key
)
for rec, response in responses:
log.info('Signal fired when course is published. Receiver: %s. Response: %s', rec, response)
return Response(
status=status.HTTP_201_CREATED,
data=serializer.data
Expand Down Expand Up @@ -760,6 +769,14 @@ def patch(self, request, ccx_course_id=None):
# enroll the coach to the newly created ccx
assign_coach_role_to_ccx(ccx_course_key, coach, master_course_object.id)

# using CCX object as sender here.
responses = SignalHandler.course_published.send(
sender=ccx_course_object,
course_key=ccx_course_key
)
for rec, response in responses:
log.info('Signal fired when course is published. Receiver: %s. Response: %s', rec, response)

return Response(
status=status.HTTP_204_NO_CONTENT,
)
146 changes: 135 additions & 11 deletions lms/djangoapps/ccx/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pytz
import ddt
import urlparse
from dateutil.tz import tzutc
from mock import patch, MagicMock
from nose.plugins.attrib import attr

Expand Down Expand Up @@ -67,7 +68,7 @@
)
from lms.djangoapps.ccx.utils import (
ccx_course,
is_email
is_email,
)
from lms.djangoapps.ccx.views import get_date

Expand Down Expand Up @@ -133,6 +134,16 @@ def setup_students_and_grades(context):
)


def unhide(unit):
"""
Recursively unhide a unit and all of its children in the CCX
schedule.
"""
unit['hidden'] = False
for child in unit.get('children', ()):
unhide(child)


class TestAdminAccessCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
"""
Tests for Custom Courses views.
Expand Down Expand Up @@ -177,6 +188,121 @@ def test_forbidden_user_access_coach_dashboard(self):
self.assertEqual(response.status_code, 403)


@attr('shard_1')
@override_settings(
XBLOCK_FIELD_DATA_WRAPPERS=['lms.djangoapps.courseware.field_overrides:OverrideModulestoreFieldData.wrap'],
MODULESTORE_FIELD_OVERRIDE_PROVIDERS=['ccx.overrides.CustomCoursesForEdxOverrideProvider'],
)
class TestCCXProgressChanges(CcxTestCase, LoginEnrollmentTestCase):
"""
Tests ccx schedule changes in progress page
"""
@classmethod
def setUpClass(cls):
"""
Set up tests
"""
super(TestCCXProgressChanges, cls).setUpClass()
start = datetime.datetime(2016, 7, 1, 0, 0, tzinfo=tzutc())
due = datetime.datetime(2016, 7, 8, 0, 0, tzinfo=tzutc())

cls.course = course = CourseFactory.create(enable_ccx=True, start=start)
chapter = ItemFactory.create(start=start, parent=course, category=u'chapter')
sequential = ItemFactory.create(
parent=chapter,
start=start,
due=due,
category=u'sequential',
metadata={'graded': True, 'format': 'Homework'}
)
vertical = ItemFactory.create(
parent=sequential,
start=start,
due=due,
category=u'vertical',
metadata={'graded': True, 'format': 'Homework'}
)

# Trying to wrap the whole thing in a bulk operation fails because it
# doesn't find the parents. But we can at least wrap this part...
with cls.store.bulk_operations(course.id, emit_signals=False):
flatten([ItemFactory.create(
parent=vertical,
start=start,
due=due,
category="problem",
data=StringResponseXMLFactory().build_xml(answer='foo'),
metadata={'rerandomize': 'always'}
)] for _ in xrange(2))

def assert_progress_summary(self, ccx_course_key, due):
"""
assert signal and schedule update.
"""
student = UserFactory.create(is_staff=False, password="test")
CourseEnrollment.enroll(student, ccx_course_key)
self.assertTrue(
CourseEnrollment.objects.filter(course_id=ccx_course_key, user=student).exists()
)

# login as student
self.client.login(username=student.username, password="test")
progress_page_response = self.client.get(
reverse('progress', kwargs={'course_id': ccx_course_key})
)
grade_summary = progress_page_response.mako_context['courseware_summary'] # pylint: disable=no-member
chapter = grade_summary[0]
section = chapter['sections'][0]
progress_page_due_date = section['due'].strftime("%Y-%m-%d %H:%M")
self.assertEqual(progress_page_due_date, due)

@patch('ccx.views.render_to_response', intercept_renderer)
@patch('courseware.views.views.render_to_response', intercept_renderer)
@patch.dict('django.conf.settings.FEATURES', {'CUSTOM_COURSES_EDX': True})
def test_edit_schedule(self):
"""
Get CCX schedule, modify it, save it.
"""
self.make_coach()
ccx = self.make_ccx()
ccx_course_key = CCXLocator.from_course_locator(self.course.id, unicode(ccx.id))
self.client.login(username=self.coach.username, password="test")

url = reverse('ccx_coach_dashboard', kwargs={'course_id': ccx_course_key})
response = self.client.get(url)

schedule = json.loads(response.mako_context['schedule']) # pylint: disable=no-member
self.assertEqual(len(schedule), 1)

unhide(schedule[0])

# edit schedule
date = datetime.datetime.now() - datetime.timedelta(days=5)
start = date.strftime("%Y-%m-%d %H:%M")
due = (date + datetime.timedelta(days=3)).strftime("%Y-%m-%d %H:%M")

schedule[0]['start'] = start
schedule[0]['children'][0]['start'] = start
schedule[0]['children'][0]['due'] = due
schedule[0]['children'][0]['children'][0]['start'] = start
schedule[0]['children'][0]['children'][0]['due'] = due

url = reverse('save_ccx', kwargs={'course_id': ccx_course_key})
response = self.client.post(url, json.dumps(schedule), content_type='application/json')

self.assertEqual(response.status_code, 200)

schedule = json.loads(response.content)['schedule']
self.assertEqual(schedule[0]['hidden'], False)
self.assertEqual(schedule[0]['start'], start)
self.assertEqual(schedule[0]['children'][0]['start'], start)
self.assertEqual(schedule[0]['children'][0]['due'], due)
self.assertEqual(schedule[0]['children'][0]['children'][0]['due'], due)
self.assertEqual(schedule[0]['children'][0]['children'][0]['start'], start)

self.assert_progress_summary(ccx_course_key, due)


@attr('shard_1')
@ddt.ddt
class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
Expand Down Expand Up @@ -384,15 +510,6 @@ def test_edit_schedule(self, today):
'save_ccx',
kwargs={'course_id': CCXLocator.from_course_locator(self.course.id, ccx.id)})

def unhide(unit):
"""
Recursively unhide a unit and all of its children in the CCX
schedule.
"""
unit['hidden'] = False
for child in unit.get('children', ()):
unhide(child)

unhide(schedule[0])
schedule[0]['start'] = u'2014-11-20 00:00'
schedule[0]['children'][0]['due'] = u'2014-12-25 00:00' # what a jerk!
Expand Down Expand Up @@ -1017,11 +1134,18 @@ def setUp(self):

# create a ccx locator and retrieve the course structure using that key
# which emulates how a student would get access.
self.ccx_key = CCXLocator.from_course_locator(self._course.id, ccx.id)
self.ccx_key = CCXLocator.from_course_locator(self._course.id, unicode(ccx.id))
self.course = get_course_by_id(self.ccx_key, depth=None)
setup_students_and_grades(self)
self.client.login(username=coach.username, password="test")
self.addCleanup(RequestCache.clear_request_cache)
from xmodule.modulestore.django import SignalHandler

# using CCX object as sender here.
SignalHandler.course_published.send(
sender=ccx,
course_key=self.ccx_key
)

@patch('ccx.views.render_to_response', intercept_renderer)
@patch('instructor.views.gradebook_api.MAX_STUDENTS_PER_PAGE_GRADE_BOOK', 1)
Expand Down
2 changes: 1 addition & 1 deletion lms/djangoapps/ccx/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def setUp(self):
"""
super(CcxTestCase, self).setUp()
# Create instructor account
self.coach = UserFactory.create()
self.coach = UserFactory.create(password="test")
# create an instance of modulestore
self.mstore = modulestore()

Expand Down
30 changes: 28 additions & 2 deletions lms/djangoapps/ccx/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from ccx_keys.locator import CCXLocator
from student.roles import CourseCcxCoachRole
from student.models import CourseEnrollment
from xmodule.modulestore.django import SignalHandler

from instructor.views.api import _split_input_list
from instructor.views.gradebook_api import get_grade_book_page
Expand Down Expand Up @@ -233,6 +234,15 @@ def create_ccx(request, course, ccx=None):

assign_coach_role_to_ccx(ccx_id, request.user, course.id)
add_master_course_staff_to_ccx(course, ccx_id, ccx.display_name)

# using CCX object as sender here.
responses = SignalHandler.course_published.send(
sender=ccx,
course_key=CCXLocator.from_course_locator(course.id, unicode(ccx.id))
)
for rec, response in responses:
log.info('Signal fired when course is published. Receiver: %s. Response: %s', rec, response)

return redirect(url)


Expand Down Expand Up @@ -324,6 +334,14 @@ def override_fields(parent, data, graded, earliest=None, ccx_ids_to_delete=None)
if changed:
override_field_for_ccx(ccx, course, 'grading_policy', policy)

# using CCX object as sender here.
responses = SignalHandler.course_published.send(
sender=ccx,
course_key=CCXLocator.from_course_locator(course.id, unicode(ccx.id))
)
for rec, response in responses:
log.info('Signal fired when course is published. Receiver: %s. Response: %s', rec, response)

return HttpResponse(
json.dumps({
'schedule': get_ccx_schedule(course, ccx),
Expand All @@ -345,6 +363,14 @@ def set_grading_policy(request, course, ccx=None):
override_field_for_ccx(
ccx, course, 'grading_policy', json.loads(request.POST['policy']))

# using CCX object as sender here.
responses = SignalHandler.course_published.send(
sender=ccx,
course_key=CCXLocator.from_course_locator(course.id, unicode(ccx.id))
)
for rec, response in responses:
log.info('Signal fired when course is published. Receiver: %s. Response: %s', rec, response)

url = reverse(
'ccx_coach_dashboard',
kwargs={'course_id': CCXLocator.from_course_locator(course.id, ccx.id)}
Expand Down Expand Up @@ -494,7 +520,7 @@ def ccx_gradebook(request, course, ccx=None):
if not ccx:
raise Http404

ccx_key = CCXLocator.from_course_locator(course.id, ccx.id)
ccx_key = CCXLocator.from_course_locator(course.id, unicode(ccx.id))
with ccx_course(ccx_key) as course:
prep_course_for_grading(course, request)
student_info, page = get_grade_book_page(request, course, course_key=ccx_key)
Expand Down Expand Up @@ -522,7 +548,7 @@ def ccx_grades_csv(request, course, ccx=None):
if not ccx:
raise Http404

ccx_key = CCXLocator.from_course_locator(course.id, ccx.id)
ccx_key = CCXLocator.from_course_locator(course.id, unicode(ccx.id))
with ccx_course(ccx_key) as course:
prep_course_for_grading(course, request)

Expand Down

0 comments on commit d5fcb60

Please sign in to comment.