Commit 259242f0 by Chris Dodge Committed by Jonathan Piacenti

tie into the xblock publish API point for progress and grading events

parent db0e36eb
...@@ -444,7 +444,7 @@ def _has_access_descriptor(user, action, descriptor, course_key=None): ...@@ -444,7 +444,7 @@ def _has_access_descriptor(user, action, descriptor, course_key=None):
checkers = { checkers = {
'load': can_load, 'load': can_load,
'staff': lambda: _has_staff_access_to_descriptor(user, descriptor, course_key), 'staff': lambda: _has_staff_access_to_descriptor(user, descriptor, course_key),
'instructor': lambda: _has_instructor_access_to_descriptor(user, descriptor, course_key) 'instructor': lambda: _has_instructor_access_to_descriptor(user, descriptor, course_key),
} }
return _dispatch(checkers, action, user, descriptor) return _dispatch(checkers, action, user, descriptor)
......
...@@ -10,6 +10,9 @@ import mimetypes ...@@ -10,6 +10,9 @@ import mimetypes
import static_replace import static_replace
from datetime import datetime
from django.utils.timezone import UTC
from collections import OrderedDict from collections import OrderedDict
from functools import partial from functools import partial
from requests.auth import HTTPBasicAuth from requests.auth import HTTPBasicAuth
...@@ -430,6 +433,14 @@ def get_module_system_for_user(user, field_data_cache, # TODO # pylint: disabl ...@@ -430,6 +433,14 @@ def get_module_system_for_user(user, field_data_cache, # TODO # pylint: disabl
""" """
Manages the workflow for recording and updating of student module grade state Manages the workflow for recording and updating of student module grade state
""" """
if not settings.FEATURES.get("ALLOW_STUDENT_STATE_UPDATES_ON_CLOSED_COURSE", True):
# if a course has ended, don't register grading events
course = modulestore().get_course(course_id, depth=0)
now = datetime.now(UTC())
if course.end is not None and now > course.end:
return
user_id = event.get('user_id', user.id) user_id = event.get('user_id', user.id)
grade = event.get('value') grade = event.get('value')
...@@ -472,6 +483,14 @@ def get_module_system_for_user(user, field_data_cache, # TODO # pylint: disabl ...@@ -472,6 +483,14 @@ def get_module_system_for_user(user, field_data_cache, # TODO # pylint: disabl
""" """
tie into the CourseCompletions datamodels that are exposed in the api_manager djangoapp tie into the CourseCompletions datamodels that are exposed in the api_manager djangoapp
""" """
if not settings.FEATURES.get("ALLOW_STUDENT_STATE_UPDATES_ON_CLOSED_COURSE", True):
# if a course has ended, don't register progress events
course = modulestore().get_course(course_id, depth=0)
now = datetime.now(UTC())
if course.end is not None and now > course.end:
return
user_id = event.get('user_id', user.id) user_id = event.get('user_id', user.id)
if not user_id: if not user_id:
return return
......
...@@ -3,16 +3,21 @@ ...@@ -3,16 +3,21 @@
Run these tests @ Devstack: Run these tests @ Devstack:
paver test_system -s lms --test_id=lms/djangoapps/gradebook/tests.py paver test_system -s lms --test_id=lms/djangoapps/gradebook/tests.py
""" """
from mock import MagicMock from mock import MagicMock, patch
import uuid import uuid
from datetime import datetime
from django.utils.timezone import UTC
from django.conf import settings
from django.test.utils import override_settings from django.test.utils import override_settings
from capa.tests.response_xml_factory import StringResponseXMLFactory from capa.tests.response_xml_factory import StringResponseXMLFactory
from courseware import module_render from courseware import module_render
from courseware.model_data import FieldDataCache from courseware.model_data import FieldDataCache
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from student.tests.factories import UserFactory, AdminFactory
from courseware.tests.factories import StaffFactory
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from gradebook.models import StudentGradebook, StudentGradebookHistory from gradebook.models import StudentGradebook, StudentGradebookHistory
...@@ -42,7 +47,11 @@ class GradebookTests(ModuleStoreTestCase): ...@@ -42,7 +47,11 @@ class GradebookTests(ModuleStoreTestCase):
self.user = UserFactory() self.user = UserFactory()
self.score = 0.75 self.score = 0.75
self.course = CourseFactory.create() def _create_course(self, start=None, end=None):
self.course = CourseFactory.create(
start=start,
end=end
)
self.course.always_recalculate_grades = True self.course.always_recalculate_grades = True
test_data = '<html>{}</html>'.format(str(uuid.uuid4())) test_data = '<html>{}</html>'.format(str(uuid.uuid4()))
chapter1 = ItemFactory.create( chapter1 = ItemFactory.create(
...@@ -114,6 +123,7 @@ class GradebookTests(ModuleStoreTestCase): ...@@ -114,6 +123,7 @@ class GradebookTests(ModuleStoreTestCase):
) )
def test_receiver_on_score_changed(self): def test_receiver_on_score_changed(self):
self._create_course()
module = self.get_module_for_user(self.user, self.course, self.problem) module = self.get_module_for_user(self.user, self.course, self.problem)
grade_dict = {'value': 0.75, 'max_value': 1, 'user_id': self.user.id} grade_dict = {'value': 0.75, 'max_value': 1, 'user_id': self.user.id}
module.system.publish(module, 'grade', grade_dict) module.system.publish(module, 'grade', grade_dict)
...@@ -139,3 +149,101 @@ class GradebookTests(ModuleStoreTestCase): ...@@ -139,3 +149,101 @@ class GradebookTests(ModuleStoreTestCase):
history = StudentGradebookHistory.objects.all() history = StudentGradebookHistory.objects.all()
self.assertEqual(len(history), 5) self.assertEqual(len(history), 5)
@patch.dict(settings.FEATURES, {'ALLOW_STUDENT_STATE_UPDATES_ON_CLOSED_COURSE': False})
def test_open_course(self):
self._create_course(start=datetime(2010,1,1, tzinfo=UTC()), end=datetime(3000, 1, 1, tzinfo=UTC()))
module = self.get_module_for_user(self.user, self.course, self.problem)
grade_dict = {'value': 0.75, 'max_value': 1, 'user_id': self.user.id}
module.system.publish(module, 'grade', grade_dict)
module = self.get_module_for_user(self.user, self.course, self.problem2)
grade_dict = {'value': 0.95, 'max_value': 1, 'user_id': self.user.id}
module.system.publish(module, 'grade', grade_dict)
gradebook = StudentGradebook.objects.all()
self.assertEqual(len(gradebook), 1)
history = StudentGradebookHistory.objects.all()
self.assertEqual(len(history), 2)
@patch.dict(settings.FEATURES, {'ALLOW_STUDENT_STATE_UPDATES_ON_CLOSED_COURSE': False})
def test_not_yet_started_course(self):
self._create_course(start=datetime(3000,1,1, tzinfo=UTC()), end=datetime(3000, 1, 1, tzinfo=UTC()))
module = self.get_module_for_user(self.user, self.course, self.problem)
grade_dict = {'value': 0.75, 'max_value': 1, 'user_id': self.user.id}
module.system.publish(module, 'grade', grade_dict)
module = self.get_module_for_user(self.user, self.course, self.problem2)
grade_dict = {'value': 0.95, 'max_value': 1, 'user_id': self.user.id}
module.system.publish(module, 'grade', grade_dict)
gradebook = StudentGradebook.objects.all()
self.assertEqual(len(gradebook), 1)
history = StudentGradebookHistory.objects.all()
self.assertEqual(len(history), 2)
@patch.dict(settings.FEATURES, {'ALLOW_STUDENT_STATE_UPDATES_ON_CLOSED_COURSE': False})
def test_closed_course_student(self):
self._create_course(start=datetime(2010,1,1, tzinfo=UTC()), end=datetime(2011, 1, 1, tzinfo=UTC()))
module = self.get_module_for_user(self.user, self.course, self.problem)
grade_dict = {'value': 0.75, 'max_value': 1, 'user_id': self.user.id}
module.system.publish(module, 'grade', grade_dict)
module = self.get_module_for_user(self.user, self.course, self.problem2)
grade_dict = {'value': 0.95, 'max_value': 1, 'user_id': self.user.id}
module.system.publish(module, 'grade', grade_dict)
gradebook = StudentGradebook.objects.all()
self.assertEqual(len(gradebook), 0)
history = StudentGradebookHistory.objects.all()
self.assertEqual(len(history), 0)
@patch.dict(settings.FEATURES, {'ALLOW_STUDENT_STATE_UPDATES_ON_CLOSED_COURSE': False})
def test_closed_course_admin(self):
"""
Users marked as Admin should be able to submit grade events to a closed course
"""
self.user = AdminFactory()
self._create_course(start=datetime(2010,1,1, tzinfo=UTC()), end=datetime(2011, 1, 1, tzinfo=UTC()))
module = self.get_module_for_user(self.user, self.course, self.problem)
grade_dict = {'value': 0.75, 'max_value': 1, 'user_id': self.user.id}
module.system.publish(module, 'grade', grade_dict)
module = self.get_module_for_user(self.user, self.course, self.problem2)
grade_dict = {'value': 0.95, 'max_value': 1, 'user_id': self.user.id}
module.system.publish(module, 'grade', grade_dict)
gradebook = StudentGradebook.objects.all()
self.assertEqual(len(gradebook), 0)
history = StudentGradebookHistory.objects.all()
self.assertEqual(len(history), 0)
@patch.dict(settings.FEATURES, {'ALLOW_STUDENT_STATE_UPDATES_ON_CLOSED_COURSE': False})
def test_closed_course_staff(self):
"""
Users marked as course staff should be able to submit grade events to a closed course
"""
self._create_course(start=datetime(2010,1,1, tzinfo=UTC()), end=datetime(2011, 1, 1, tzinfo=UTC()))
self.user = StaffFactory(course_key=self.course.id)
module = self.get_module_for_user(self.user, self.course, self.problem)
grade_dict = {'value': 0.75, 'max_value': 1, 'user_id': self.user.id}
module.system.publish(module, 'grade', grade_dict)
module = self.get_module_for_user(self.user, self.course, self.problem2)
grade_dict = {'value': 0.95, 'max_value': 1, 'user_id': self.user.id}
module.system.publish(module, 'grade', grade_dict)
gradebook = StudentGradebook.objects.all()
self.assertEqual(len(gradebook), 0)
history = StudentGradebookHistory.objects.all()
self.assertEqual(len(history), 0)
...@@ -2,13 +2,19 @@ ...@@ -2,13 +2,19 @@
Django database models supporting the progress app Django database models supporting the progress app
""" """
from datetime import datetime
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models from django.db import models
from django.db.models import Sum, Q from django.db.models import Sum, Q
from django.utils.timezone import UTC
from model_utils.models import TimeStampedModel from model_utils.models import TimeStampedModel
from xmodule_django.models import CourseKeyField from xmodule_django.models import CourseKeyField
from xmodule.modulestore.django import modulestore
from opaque_keys.edx.keys import CourseKey
class StudentProgress(TimeStampedModel): class StudentProgress(TimeStampedModel):
......
...@@ -74,6 +74,7 @@ FEATURES['EMBARGO'] = True ...@@ -74,6 +74,7 @@ FEATURES['EMBARGO'] = True
# Toggles API on for testing # Toggles API on for testing
FEATURES['API'] = True FEATURES['API'] = True
FEATURES['ALLOW_STUDENT_STATE_UPDATES_ON_CLOSED_COURSE'] = False
FEATURES['ENABLE_COMBINED_LOGIN_REGISTRATION'] = True FEATURES['ENABLE_COMBINED_LOGIN_REGISTRATION'] = True
......
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