def _wrapper(self, request, *args, **kwargs): """ Expects kwargs to contain 'course_id'. Passes the course descriptor to the given decorated function. Raises 404 if access to course is disallowed. """ course_id = CourseKey.from_string(kwargs.pop('course_id')) with modulestore().bulk_operations(course_id): try: course = get_course_with_access( request.user, 'load_mobile', course_id, depth=depth, check_if_enrolled=True, ) # Record user activity for tracking progress towards a user's course goals (for mobile app) UserActivity.record_user_activity( request.user, course_id, request=request, only_if_mobile_app=True ) except CoursewareAccessException as error: return Response(data=error.to_json(), status=status.HTTP_404_NOT_FOUND) except CourseAccessRedirect as error: # If the redirect contains information about the triggering AccessError, # return the information contained in the AccessError. if error.access_error is not None: return Response(data=error.access_error.to_json(), status=status.HTTP_404_NOT_FOUND) # Raise a 404 if the user does not have course access raise Http404 # lint-amnesty, pylint: disable=raise-missing-from return func(self, request, course=course, *args, **kwargs)
def list(self, request): """ Implements the GET method for the list endpoint as described in the class docstring. """ form = ThreadListGetForm(request.GET) if not form.is_valid(): raise ValidationError(form.errors) # Record user activity for tracking progress towards a user's course goals (for mobile app) UserActivity.record_user_activity( request.user, form.cleaned_data["course_id"], request=request, only_if_mobile_app=True ) return get_thread_list( request, form.cleaned_data["course_id"], form.cleaned_data["page"], form.cleaned_data["page_size"], form.cleaned_data["topic_id"], form.cleaned_data["text_search"], form.cleaned_data["following"], form.cleaned_data["author"], form.cleaned_data["thread_type"], form.cleaned_data["flagged"], form.cleaned_data["view"], form.cleaned_data["order_by"], form.cleaned_data["order_direction"], form.cleaned_data["requested_fields"], form.cleaned_data["count_flagged"], )
def post(self, request, *args, **kwargs): """ Handle the POST request Populate the user activity table. """ user_id = request.data.get('user_id') course_key = request.data.get('course_key') if not user_id or not course_key: return Response( 'User id and course key are required', status=status.HTTP_400_BAD_REQUEST, ) try: user_id = int(user_id) user = User.objects.get(id=user_id) except User.DoesNotExist: return Response( 'Provided user id does not correspond to an existing user', status=status.HTTP_400_BAD_REQUEST, ) try: course_key = CourseKey.from_string(course_key) except InvalidKeyError: return Response( 'Provided course key is not valid', status=status.HTTP_400_BAD_REQUEST, ) # Populate user activity for tracking progress towards a user's course goals UserActivity.record_user_activity(user, course_key) return Response(status=(200))
def list(self, request, hide_access_denials=False): # pylint: disable=arguments-differ """ Retrieves the usage_key for the requested course, and then returns the same information that would be returned by BlocksView.list, called with that usage key Arguments: request - Django request object """ # convert the requested course_key to the course's root block's usage_key course_key_string = request.query_params.get('course_id', None) if not course_key_string: raise ValidationError('course_id is required.') try: course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) except InvalidKeyError: raise ValidationError( f"'{str(course_key_string)}' is not a valid course key.") # lint-amnesty, pylint: disable=raise-missing-from response = super().list(request, course_usage_key, hide_access_denials=hide_access_denials) if RECORD_USER_ACTIVITY_FLAG.is_enabled(): # Record user activity for tracking progress towards a user's course goals (for mobile app) UserActivity.record_user_activity(request.user, course_key, request=request, only_if_mobile_app=True) calculate_completion = any( 'completion' in param for param in request.query_params.getlist('requested_fields', [])) if not calculate_completion: return response course_blocks = {} root = None if request.query_params.get('return_type') == 'list': for course_block in response.data: course_blocks[course_block['id']] = course_block if course_block.get('type') == 'course': root = course_block['id'] else: root = response.data['root'] course_blocks = response.data['blocks'] if not root: raise ValueError( f"Unable to find course block in {course_key_string}") recurse_mark_complete(root, course_blocks) return response
def get(self, request, course_id): """Implements the GET method as described in the class docstring.""" course_key = CourseKey.from_string( course_id) # TODO: which class is right? if RECORD_USER_ACTIVITY_FLAG.is_enabled(): # Record user activity for tracking progress towards a user's course goals (for mobile app) UserActivity.record_user_activity(request.user, course_key, request=request, only_if_mobile_app=True) return Response(get_course(request, course_key))
def get(self, request, course_id): """ Retrieve general discussion metadata for a course. **Example Requests**: GET /api/discussion/v1/courses/course-v1:ExampleX+Subject101+2015 """ course_key = CourseKey.from_string(course_id) # TODO: which class is right? # Record user activity for tracking progress towards a user's course goals (for mobile app) UserActivity.record_user_activity(request.user, course_key, request=request, only_if_mobile_app=True) return Response(get_course(request, course_key))
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) # Although this course data is not used this method will return 404 if course does not exist get_course_with_access(request.user, 'load', course_key) # Record user activity for tracking progress towards a user's course goals (for mobile app) UserActivity.record_user_activity(request.user, course_key, request=request, only_if_mobile_app=True) serializer = self.get_serializer({}) return Response(serializer.data)
def test_when_record_user_activity_does_not_perform_updates(self): ''' Ensure that record user activity is not called when: 1. user or course are not defined 2. we have already recorded user activity for this user/course on this date and have a record in the cache ''' with patch.object( TieredCache, 'set_all_tiers', wraps=TieredCache.set_all_tiers) as activity_cache_set: UserActivity.record_user_activity(self.user, None) activity_cache_set.assert_not_called() UserActivity.record_user_activity(None, self.course.id) activity_cache_set.assert_not_called() cache_key = 'goals_user_activity_{}_{}_{}'.format( str(self.user.id), str(self.course.id), str(datetime.now().date())) TieredCache.set_all_tiers(cache_key, 'test', 3600) with patch.object( TieredCache, 'set_all_tiers', wraps=TieredCache.set_all_tiers) as activity_cache_set: UserActivity.record_user_activity(self.user, self.course.id) activity_cache_set.assert_not_called() # Test that the happy path works to ensure that the measurement in this test isn't broken user2 = UserFactory() UserActivity.record_user_activity(user2, self.course.id) activity_cache_set.assert_called_once()
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) if course_home_legacy_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) is_staff = bool(has_access(request.user, 'staff', course_key)) _, request.user = setup_masquerade( request, course_key, staff_access=is_staff, reset_masquerade_data=True, ) # Record user activity for tracking progress towards a user's course goals (for mobile app) UserActivity.record_user_activity(request.user, course.id, request=request, only_if_mobile_app=True) if not CourseEnrollment.is_enrolled(request.user, course_key) and not is_staff: return Response('User not enrolled.', status=401) blocks = get_course_date_blocks(course, request.user, request, include_access=True, include_past_dates=True) learner_is_full_access = not ContentTypeGatingConfig.enabled_for_enrollment( user=request.user, course_key=course_key, ) # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] data = { 'has_ended': course.has_ended(), 'course_date_blocks': [block for block in blocks if not isinstance(block, TodaysDate)], 'learner_is_full_access': learner_is_full_access, 'user_timezone': user_timezone, } context = self.get_serializer_context() context['learner_is_full_access'] = learner_is_full_access serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get(self, request, course_id): """ Implements the GET method as described in the class docstring. """ course_key = CourseKey.from_string(course_id) topic_ids = self.request.GET.get('topic_id') with modulestore().bulk_operations(course_key): response = get_course_topics( request, course_key, set(topic_ids.strip(',').split(',')) if topic_ids else None, ) # Record user activity for tracking progress towards a user's course goals (for mobile app) UserActivity.record_user_activity(request.user, course_key, request=request, only_if_mobile_app=True) return Response(response)
def test_masquerading(self): ''' Method only records activity if the user is not masquerading ''' with patch.object( TieredCache, 'set_all_tiers', wraps=TieredCache.set_all_tiers) as activity_cache_set: UserActivity.record_user_activity(self.user, self.course.id) activity_cache_set.assert_called_once() with patch.object( TieredCache, 'set_all_tiers', wraps=TieredCache.set_all_tiers) as activity_cache_set: with patch('lms.djangoapps.course_goals.models.is_masquerading', return_value=True): UserActivity.record_user_activity(self.user, self.course.id) activity_cache_set.assert_not_called()
def get_object(self): """ Return the requested course object, if the user has appropriate permissions. """ if self.request.user.is_staff: username = self.request.GET.get('username', '') or self.request.user.username else: username = self.request.user.username course_key = CourseKey.from_string(self.kwargs['course_key_string']) overview = CoursewareMeta( course_key, self.request, username=username, ) # Record course goals user activity for learning mfe courseware on web if RECORD_USER_ACTIVITY_FLAG.is_enabled(): UserActivity.record_user_activity(self.request.user, course_key) return overview
def test_mobile_argument(self): ''' Method only records activity if the request is coming from the mobile app when the only_if_mobile_app argument is true ''' with patch.object( TieredCache, 'set_all_tiers', wraps=TieredCache.set_all_tiers) as activity_cache_set: UserActivity.record_user_activity(self.user, self.course.id, request=self.request, only_if_mobile_app=True) activity_cache_set.assert_not_called() with patch( 'lms.djangoapps.course_goals.models.is_request_from_mobile_app', return_value=True): UserActivity.record_user_activity(self.user, self.course.id, request=self.request, only_if_mobile_app=True) activity_cache_set.assert_called_once()
def get_object(self): """ Return the requested course object, if the user has appropriate permissions. """ original_user = self.request.user if self.request.user.is_staff: username = self.request.GET.get('username', '') or self.request.user.username else: username = self.request.user.username course_key = CourseKey.from_string(self.kwargs['course_key_string']) overview = CoursewareMeta( course_key, self.request, username=username, ) # Record course goals user activity for learning mfe courseware on web UserActivity.record_user_activity(self.request.user, course_key) # Record a user's browser timezone self.set_last_seen_courseware_timezone(original_user) return overview
def test_that_user_activity_cache_works_properly(self): ''' Ensure that the cache for user activity works properly 1. user or course are not defined 2. we have already recorded user activity for this user/course on this date and have a record in the cache ''' with patch.object( TieredCache, 'set_all_tiers', wraps=TieredCache.set_all_tiers) as activity_cache_set: UserActivity.record_user_activity(self.user, self.course.id) activity_cache_set.assert_called_once() with patch.object( TieredCache, 'set_all_tiers', wraps=TieredCache.set_all_tiers) as activity_cache_set: UserActivity.record_user_activity(self.user, self.course.id) activity_cache_set.assert_not_called() now_plus_1_day = datetime.now() + timedelta(days=1) with freeze_time(now_plus_1_day): UserActivity.record_user_activity(self.user, self.course.id) activity_cache_set.assert_called_once()
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) original_user_is_global_staff = self.request.user.is_staff original_user_is_staff = has_access(request.user, 'staff', course_key).has_access course = course_detail(request, request.user.username, course_key) # We must compute course load access *before* setting up masquerading, # else course staff (who are not enrolled) will not be able view # their course from the perspective of a learner. load_access = check_course_access( course, request.user, 'load', check_if_enrolled=True, check_if_authenticated=True, ) _, request.user = setup_masquerade( request, course_key, staff_access=original_user_is_staff, reset_masquerade_data=True, ) username = request.user.username if request.user.username else None enrollment = CourseEnrollment.get_enrollment(request.user, course_key_string) user_is_enrolled = bool(enrollment and enrollment.is_active) can_load_courseware = courseware_mfe_is_visible( course_key=course_key, is_global_staff=original_user_is_global_staff, is_course_staff=original_user_is_staff) # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] browser_timezone = self.request.query_params.get( 'browser_timezone', None) celebrations = get_celebrations_dict( request.user, enrollment, course, user_timezone if not None else browser_timezone) # Record course goals user activity for (web) learning mfe course tabs UserActivity.record_user_activity(request.user, course_key) data = { 'course_id': course.id, 'username': username, 'is_staff': has_access(request.user, 'staff', course_key).has_access, 'original_user_is_staff': original_user_is_staff, 'number': course.display_number_with_default, 'org': course.display_org_with_default, 'start': course.start, 'tabs': get_course_tab_list(request.user, course), 'title': course.display_name_with_default, 'is_self_paced': getattr(course, 'self_paced', False), 'is_enrolled': user_is_enrolled, 'course_access': load_access.to_json(), 'can_load_courseware': can_load_courseware, 'celebrations': celebrations, 'user_timezone': user_timezone, } context = self.get_serializer_context() context['course'] = course context['course_overview'] = course context['enrollment'] = enrollment serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)