예제 #1
0
def inline_discussion(request, course_key, discussion_id):
    """
    Renders JSON for DiscussionModules
    """

    with function_trace('get_course_and_user_info'):
        course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
        cc_user = cc.User.from_django_user(request.user)
        user_info = cc_user.to_dict()

    try:
        with function_trace('get_threads'):
            threads, query_params = get_threads(
                request, course, user_info, discussion_id, per_page=INLINE_THREADS_PER_PAGE
            )
    except ValueError:
        return HttpResponseServerError('Invalid group_id')

    with function_trace('get_metadata_for_threads'):
        annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

    with function_trace('determine_group_permissions'):
        is_staff = has_permission(request.user, 'openclose_thread', course.id)
        course_discussion_settings = get_course_discussion_settings(course.id)
        group_names_by_id = get_group_names_by_id(course_discussion_settings)
        course_is_divided = course_discussion_settings.division_scheme is not CourseDiscussionSettings.NONE

    with function_trace('prepare_content'):
        threads = [
            utils.prepare_content(
                thread,
                course_key,
                is_staff,
                course_is_divided,
                group_names_by_id
            ) for thread in threads
        ]

    return utils.JsonResponse({
        'is_commentable_divided': is_commentable_divided(course_key, discussion_id),
        'discussion_data': threads,
        'user_info': user_info,
        'user_group_id': get_group_id_for_user(request.user, course_discussion_settings),
        'annotated_content_info': annotated_content_info,
        'page': query_params['page'],
        'num_pages': query_params['num_pages'],
        'roles': utils.get_role_ids(course_key),
        'course_settings': make_course_settings(course, request.user, False)
    })
예제 #2
0
    def get_schedules_with_target_date_by_bin_and_orgs(
        self, order_by='enrollment__user__id'
    ):
        """
        Returns Schedules with the target_date, related to Users whose id matches the bin_num, and filtered by org_list.

        Arguments:
        order_by -- string for field to sort the resulting Schedules by
        """
        target_day = _get_datetime_beginning_of_day(self.target_datetime)
        schedule_day_equals_target_day_filter = {
            'courseenrollment__schedule__{}__gte'.format(self.schedule_date_field): target_day,
            'courseenrollment__schedule__{}__lt'.format(self.schedule_date_field): target_day + datetime.timedelta(days=1),
        }
        users = User.objects.filter(
            courseenrollment__is_active=True,
            **schedule_day_equals_target_day_filter
        ).annotate(
            id_mod=F('id') % self.num_bins
        ).filter(
            id_mod=self.bin_num
        )

        schedule_day_equals_target_day_filter = {
            '{}__gte'.format(self.schedule_date_field): target_day,
            '{}__lt'.format(self.schedule_date_field): target_day + datetime.timedelta(days=1),
        }
        schedules = Schedule.objects.select_related(
            'enrollment__user__profile',
            'enrollment__course',
        ).filter(
            Q(enrollment__course__end__isnull=True) | Q(
                enrollment__course__end__gte=self.current_datetime
            ),
            self.experience_filter,
            enrollment__user__in=users,
            enrollment__is_active=True,
            active=True,
            **schedule_day_equals_target_day_filter
        ).order_by(order_by)

        schedules = self.filter_by_org(schedules)

        if "read_replica" in settings.DATABASES:
            schedules = schedules.using("read_replica")

        LOG.info(u'Query = %r', schedules.query.sql_with_params())

        with function_trace('schedule_query_set_evaluation'):
            # This will run the query and cache all of the results in memory.
            num_schedules = len(schedules)

        LOG.info(u'Number of schedules = %d', num_schedules)

        # This should give us a sense of the volume of data being processed by each task.
        set_custom_metric('num_schedules', num_schedules)

        return schedules
예제 #3
0
def single_thread(request, course_key, discussion_id, thread_id):
    """
    Renders a response to display a single discussion thread.  This could either be a page refresh
    after navigating to a single thread, a direct link to a single thread, or an AJAX call from the
    discussions UI loading the responses/comments for a single thread.

    Depending on the HTTP headers, we'll adjust our response accordingly.
    """
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    request.user.is_community_ta = utils.is_user_community_ta(request.user, course.id)
    if request.is_ajax():
        cc_user = cc.User.from_django_user(request.user)
        user_info = cc_user.to_dict()
        is_staff = has_permission(request.user, 'openclose_thread', course.id)
        thread = _load_thread_for_viewing(
            request,
            course,
            discussion_id=discussion_id,
            thread_id=thread_id,
            raise_event=True,
        )

        with function_trace("get_annotated_content_infos"):
            annotated_content_info = utils.get_annotated_content_infos(
                course_key,
                thread,
                request.user,
                user_info=user_info
            )

        content = utils.prepare_content(thread.to_dict(), course_key, is_staff)
        with function_trace("add_courseware_context"):
            add_courseware_context([content], course, request.user)

        return utils.JsonResponse({
            'content': content,
            'annotated_content_info': annotated_content_info,
        })
    else:
        course_id = unicode(course.id)
        tab_view = CourseTabView()
        return tab_view.get(request, course_id, 'discussion', discussion_id=discussion_id, thread_id=thread_id)
예제 #4
0
 def send(self, msg_type):
     for (user, language, context) in self.schedules_for_bin():
         msg = msg_type.personalize(
             Recipient(
                 user.username,
                 self.override_recipient_email or user.email,
             ),
             language,
             context,
         )
         with function_trace('enqueue_send_task'):
             self.async_send_task.apply_async((self.site.id, str(msg)), retry=False)
예제 #5
0
def forum_form_discussion(request, course_key):
    """
    Renders the main Discussion page, potentially filtered by a search query
    """
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    request.user.is_community_ta = utils.is_user_community_ta(request.user, course.id)
    if request.is_ajax():
        user = cc.User.from_django_user(request.user)
        user_info = user.to_dict()

        try:
            unsafethreads, query_params = get_threads(request, course, user_info)  # This might process a search query
            is_staff = has_permission(request.user, 'openclose_thread', course.id)
            threads = [utils.prepare_content(thread, course_key, is_staff) for thread in unsafethreads]
        except cc.utils.CommentClientMaintenanceError:
            return HttpResponseServerError('Forum is in maintenance mode', status=status.HTTP_503_SERVICE_UNAVAILABLE)
        except ValueError:
            return HttpResponseServerError("Invalid group_id")

        with function_trace("get_metadata_for_threads"):
            annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

        with function_trace("add_courseware_context"):
            add_courseware_context(threads, course, request.user)

        return utils.JsonResponse({
            'discussion_data': threads,   # TODO: Standardize on 'discussion_data' vs 'threads'
            'annotated_content_info': annotated_content_info,
            'num_pages': query_params['num_pages'],
            'page': query_params['page'],
            'corrected_text': query_params['corrected_text'],
        })
    else:
        course_id = unicode(course.id)
        tab_view = CourseTabView()
        return tab_view.get(request, course_id, 'discussion')
예제 #6
0
def followed_threads(request, course_key, user_id):
    """
    Ajax-only endpoint retrieving the threads followed by a specific user.
    """
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    try:
        profiled_user = cc.User(id=user_id, course_id=course_key)

        query_params = {
            'page': 1,
            'per_page': THREADS_PER_PAGE,   # more than threads_per_page to show more activities
            'sort_key': 'date',
        }
        query_params.update(
            strip_none(
                extract(
                    request.GET,
                    [
                        'page',
                        'sort_key',
                        'flagged',
                        'unread',
                        'unanswered',
                    ]
                )
            )
        )

        try:
            group_id = get_group_id_for_comments_service(request, course_key)
        except ValueError:
            return HttpResponseServerError("Invalid group_id")
        if group_id is not None:
            query_params['group_id'] = group_id

        paginated_results = profiled_user.subscribed_threads(query_params)
        print("\n \n \n paginated results \n \n \n ")
        print(paginated_results)
        query_params['page'] = paginated_results.page
        query_params['num_pages'] = paginated_results.num_pages
        user_info = cc.User.from_django_user(request.user).to_dict()

        with function_trace("get_metadata_for_threads"):
            annotated_content_info = utils.get_metadata_for_threads(
                course_key,
                paginated_results.collection,
                request.user, user_info
            )
        if request.is_ajax():
            is_staff = has_permission(request.user, 'openclose_thread', course.id)
            return utils.JsonResponse({
                'annotated_content_info': annotated_content_info,
                'discussion_data': [
                    utils.prepare_content(thread, course_key, is_staff) for thread in paginated_results.collection
                ],
                'page': query_params['page'],
                'num_pages': query_params['num_pages'],
            })
        #TODO remove non-AJAX support, it does not appear to be used and does not appear to work.
        else:
            context = {
                'course': course,
                'user': request.user,
                'django_user': User.objects.get(id=user_id),
                'profiled_user': profiled_user.to_dict(),
                'threads': paginated_results.collection,
                'user_info': user_info,
                'annotated_content_info': annotated_content_info,
                #                'content': content,
            }

            return render_to_response('discussion/user_profile.html', context)
    except User.DoesNotExist:
        raise Http404
예제 #7
0
def create_user_profile_context(request, course_key, user_id):
    """ Generate a context dictionary for the user profile. """
    user = cc.User.from_django_user(request.user)
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)

    # If user is not enrolled in the course, do not proceed.
    django_user = User.objects.get(id=user_id)
    if not CourseEnrollment.is_enrolled(django_user, course.id):
        raise Http404

    query_params = {
        'page': request.GET.get('page', 1),
        'per_page': THREADS_PER_PAGE,   # more than threads_per_page to show more activities
    }

    group_id = get_group_id_for_comments_service(request, course_key)
    if group_id is not None:
        query_params['group_id'] = group_id
        profiled_user = cc.User(id=user_id, course_id=course_key, group_id=group_id)
    else:
        profiled_user = cc.User(id=user_id, course_id=course_key)

    threads, page, num_pages = profiled_user.active_threads(query_params)
    query_params['page'] = page
    query_params['num_pages'] = num_pages

    with function_trace("get_metadata_for_threads"):
        user_info = cc.User.from_django_user(request.user).to_dict()
        annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

    is_staff = has_permission(request.user, 'openclose_thread', course.id)
    threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]
    with function_trace("add_courseware_context"):
        add_courseware_context(threads, course, request.user)

        # TODO: LEARNER-3854: If we actually implement Learner Analytics code, this
        #   code was original protected to not run in user_profile() if is_ajax().
        #   Someone should determine if that is still necessary (i.e. was that ever
        #   called as is_ajax()) and clean this up as necessary.
        user_roles = django_user.roles.filter(
            course_id=course.id
        ).order_by("name").values_list("name", flat=True).distinct()

        with function_trace("get_cohort_info"):
            course_discussion_settings = get_course_discussion_settings(course_key)
            user_group_id = get_group_id_for_user(request.user, course_discussion_settings)

        context = _create_base_discussion_view_context(request, course_key)
        context.update({
            'django_user': django_user,
            'django_user_roles': user_roles,
            'profiled_user': profiled_user.to_dict(),
            'threads': threads,
            'user_group_id': user_group_id,
            'annotated_content_info': annotated_content_info,
            'page': query_params['page'],
            'num_pages': query_params['num_pages'],
            'sort_preference': user.default_sort_key,
            'learner_profile_page_url': reverse('learner_profile', kwargs={'username': django_user.username}),
        })
        return context
예제 #8
0
def _create_discussion_board_context(request, base_context, thread=None):
    """
    Returns the template context for rendering the discussion board.
    """
    context = base_context.copy()
    course = context['course']
    course_key = course.id
    thread_id = thread.id if thread else None
    discussion_id = thread.commentable_id if thread else None
    course_settings = context['course_settings']
    user = context['user']
    cc_user = cc.User.from_django_user(user)
    user_info = context['user_info']
    if thread:

        # Since we're in page render mode, and the discussions UI will request the thread list itself,
        # we need only return the thread information for this one.
        threads = [thread.to_dict()]

        for thread in threads:
            # patch for backward compatibility with comments service
            if "pinned" not in thread:
                thread["pinned"] = False
        thread_pages = 1
        root_url = reverse('forum_form_discussion', args=[unicode(course.id)])
    else:
        threads, query_params = get_threads(request, course, user_info)   # This might process a search query
        thread_pages = query_params['num_pages']
        root_url = request.path
    is_staff = has_permission(user, 'openclose_thread', course.id)
    threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]

    with function_trace("get_metadata_for_threads"):
        annotated_content_info = utils.get_metadata_for_threads(course_key, threads, user, user_info)

    with function_trace("add_courseware_context"):
        add_courseware_context(threads, course, user)

    with function_trace("get_cohort_info"):
        course_discussion_settings = get_course_discussion_settings(course_key)
        user_group_id = get_group_id_for_user(user, course_discussion_settings)

    context.update({
        'root_url': root_url,
        'discussion_id': discussion_id,
        'thread_id': thread_id,
        'threads': threads,
        'thread_pages': thread_pages,
        'annotated_content_info': annotated_content_info,
        'is_moderator': has_permission(user, "see_all_cohorts", course_key),
        'groups': course_settings["groups"],  # still needed to render _thread_list_template
        'user_group_id': user_group_id,  # read from container in NewPostView
        'sort_preference': cc_user.default_sort_key,
        'category_map': course_settings["category_map"],
        'course_settings': course_settings,
        'is_commentable_divided': is_commentable_divided(course_key, discussion_id, course_discussion_settings),
        # If the default topic id is None the front-end code will look for a topic that contains "General"
        'discussion_default_topic_id': _get_discussion_default_topic_id(course),
    })
    context.update(
        get_experiment_user_metadata_context(
            course,
            user,
        )
    )
    return context
예제 #9
0
def _create_discussion_board_context(request, base_context, thread=None):
    """
    Returns the template context for rendering the discussion board.
    """
    context = base_context.copy()
    course = context['course']
    course_key = course.id
    thread_id = thread.id if thread else None
    discussion_id = thread.commentable_id if thread else None
    course_settings = context['course_settings']
    user = context['user']
    cc_user = cc.User.from_django_user(user)
    user_info = context['user_info']
    if thread:
        _check_team_discussion_access(request, course, discussion_id)
        # Since we're in page render mode, and the discussions UI will request the thread list itself,
        # we need only return the thread information for this one.
        threads = [thread.to_dict()]

        for thread in threads:
            # patch for backward compatibility with comments service
            if "pinned" not in thread:
                thread["pinned"] = False
        thread_pages = 1
        root_url = reverse('forum_form_discussion',
                           args=[six.text_type(course.id)])
    else:
        threads, query_params = get_threads(
            request, course, user_info)  # This might process a search query
        thread_pages = query_params['num_pages']
        root_url = request.path
    is_staff = has_permission(user, 'openclose_thread', course.id)
    threads = [
        utils.prepare_content(thread, course_key, is_staff)
        for thread in threads
    ]

    with function_trace("get_metadata_for_threads"):
        annotated_content_info = utils.get_metadata_for_threads(
            course_key, threads, user, user_info)

    with function_trace("add_courseware_context"):
        add_courseware_context(threads, course, user)

    with function_trace("get_cohort_info"):
        course_discussion_settings = get_course_discussion_settings(course_key)
        user_group_id = get_group_id_for_user(user, course_discussion_settings)

    context.update({
        'root_url':
        root_url,
        'discussion_id':
        discussion_id,
        'thread_id':
        thread_id,
        'threads':
        threads,
        'thread_pages':
        thread_pages,
        'annotated_content_info':
        annotated_content_info,
        'is_moderator':
        has_permission(user, "see_all_cohorts", course_key),
        'groups':
        course_settings[
            "groups"],  # still needed to render _thread_list_template
        'user_group_id':
        user_group_id,  # read from container in NewPostView
        'sort_preference':
        cc_user.default_sort_key,
        'category_map':
        course_settings["category_map"],
        'course_settings':
        course_settings,
        'is_commentable_divided':
        is_commentable_divided(course_key, discussion_id,
                               course_discussion_settings),
        # If the default topic id is None the front-end code will look for a topic that contains "General"
        'discussion_default_topic_id':
        _get_discussion_default_topic_id(course),
        'enable_daily_digest':
        is_forum_daily_digest_enabled()
    })
    context.update(get_experiment_user_metadata_context(
        course,
        user,
    ))
    return context
예제 #10
0
def create_user_profile_context(request, course_key, user_id):
    """ Generate a context dictionary for the user profile. """
    user = cc.User.from_django_user(request.user)
    course = get_course_with_access(request.user,
                                    'load',
                                    course_key,
                                    check_if_enrolled=True)

    # If user is not enrolled in the course, do not proceed.
    django_user = User.objects.get(id=user_id)
    if not CourseEnrollment.is_enrolled(django_user, course.id):
        raise Http404

    query_params = {
        'page': request.GET.get('page', 1),
        'per_page':
        THREADS_PER_PAGE,  # more than threads_per_page to show more activities
    }

    group_id = get_group_id_for_comments_service(request, course_key)
    if group_id is not None:
        query_params['group_id'] = group_id
        profiled_user = cc.User(id=user_id,
                                course_id=course_key,
                                group_id=group_id)
    else:
        profiled_user = cc.User(id=user_id, course_id=course_key)

    threads, page, num_pages = profiled_user.active_threads(query_params)
    query_params['page'] = page
    query_params['num_pages'] = num_pages

    with function_trace("get_metadata_for_threads"):
        user_info = cc.User.from_django_user(request.user).to_dict()
        annotated_content_info = utils.get_metadata_for_threads(
            course_key, threads, request.user, user_info)

    is_staff = has_permission(request.user, 'openclose_thread', course.id)
    threads = [
        utils.prepare_content(thread, course_key, is_staff)
        for thread in threads
    ]
    with function_trace("add_courseware_context"):
        add_courseware_context(threads, course, request.user)

        # TODO: LEARNER-3854: If we actually implement Learner Analytics code, this
        #   code was original protected to not run in user_profile() if is_ajax().
        #   Someone should determine if that is still necessary (i.e. was that ever
        #   called as is_ajax()) and clean this up as necessary.
        user_roles = django_user.roles.filter(
            course_id=course.id).order_by("name").values_list(
                "name", flat=True).distinct()

        with function_trace("get_cohort_info"):
            course_discussion_settings = get_course_discussion_settings(
                course_key)
            user_group_id = get_group_id_for_user(request.user,
                                                  course_discussion_settings)

        context = _create_base_discussion_view_context(request, course_key)
        context.update({
            'django_user':
            django_user,
            'django_user_roles':
            user_roles,
            'profiled_user':
            profiled_user.to_dict(),
            'threads':
            threads,
            'user_group_id':
            user_group_id,
            'annotated_content_info':
            annotated_content_info,
            'page':
            query_params['page'],
            'num_pages':
            query_params['num_pages'],
            'sort_preference':
            user.default_sort_key,
            'learner_profile_page_url':
            reverse('learner_profile',
                    kwargs={'username': django_user.username}),
        })
        return context
예제 #11
0
def inline_discussion(request, course_key, discussion_id):
    """
    Renders JSON for DiscussionModules
    """
    with function_trace('get_course_and_user_info'):
        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=True)
        cc_user = cc.User.from_django_user(request.user)
        user_info = cc_user.to_dict()

    try:
        with function_trace('get_threads'):
            threads, query_params = get_threads(
                request,
                course,
                user_info,
                discussion_id,
                per_page=INLINE_THREADS_PER_PAGE)
    except ValueError:
        return HttpResponseServerError('Invalid group_id')
    except TeamDiscussionHiddenFromUserException:
        return HttpResponseForbidden(TEAM_PERMISSION_MESSAGE)

    with function_trace('get_metadata_for_threads'):
        annotated_content_info = utils.get_metadata_for_threads(
            course_key, threads, request.user, user_info)

    with function_trace('determine_group_permissions'):
        is_staff = has_permission(request.user, 'openclose_thread', course.id)
        course_discussion_settings = get_course_discussion_settings(course.id)
        group_names_by_id = get_group_names_by_id(course_discussion_settings)
        course_is_divided = course_discussion_settings.division_scheme is not CourseDiscussionSettings.NONE

    with function_trace('prepare_content'):
        threads = [
            utils.prepare_content(thread, course_key, is_staff,
                                  course_is_divided, group_names_by_id)
            for thread in threads
        ]

    return utils.JsonResponse({
        'is_commentable_divided':
        is_commentable_divided(course_key, discussion_id),
        'discussion_data':
        threads,
        'user_info':
        user_info,
        'user_group_id':
        get_group_id_for_user(request.user, course_discussion_settings),
        'annotated_content_info':
        annotated_content_info,
        'page':
        query_params['page'],
        'num_pages':
        query_params['num_pages'],
        'roles':
        utils.get_role_ids(course_key),
        'course_settings':
        make_course_settings(course, request.user, False)
    })
예제 #12
0
    def get_schedules_with_target_date_by_bin_and_orgs(
            self, order_by='enrollment__user__id'):
        """
        Returns Schedules with the target_date, related to Users whose id matches the bin_num, and filtered by org_list.

        Arguments:
        order_by -- string for field to sort the resulting Schedules by
        """
        target_day = _get_datetime_beginning_of_day(self.target_datetime)
        schedule_day_equals_target_day_filter = {
            f'courseenrollment__schedule__{self.schedule_date_field}__gte':
            target_day,
            f'courseenrollment__schedule__{self.schedule_date_field}__lt':
            target_day + datetime.timedelta(days=1),  # lint-amnesty, pylint: disable=line-too-long
        }
        users = User.objects.filter(
            courseenrollment__is_active=True,
            is_active=True,
            **schedule_day_equals_target_day_filter).annotate(
                id_mod=self.bin_num_for_user_id(F('id'))).filter(
                    id_mod=self.bin_num)

        schedule_day_equals_target_day_filter = {
            f'{self.schedule_date_field}__gte':
            target_day,
            f'{self.schedule_date_field}__lt':
            target_day + datetime.timedelta(days=1),
        }
        schedules = Schedule.objects.select_related(
            'enrollment__user__profile',
            'enrollment__course',
            'enrollment__fbeenrollmentexclusion',
        ).filter(
            Q(enrollment__course__end__isnull=True)
            | Q(enrollment__course__end__gte=self.current_datetime),
            self.experience_filter,
            enrollment__user__in=users,
            enrollment__is_active=True,
            **schedule_day_equals_target_day_filter).annotate(
                external_updates_enabled=Exists(
                    query_external_updates(
                        OuterRef('enrollment__user_id'),
                        OuterRef('enrollment__course_id'))), ).exclude(
                            external_updates_enabled=True, ).order_by(order_by)

        schedules = self.filter_by_org(schedules)

        if "read_replica" in settings.DATABASES:
            schedules = schedules.using("read_replica")

        LOG.info('Query = %r', schedules.query.sql_with_params())

        with function_trace('schedule_query_set_evaluation'):
            # This will run the query and cache all of the results in memory.
            num_schedules = len(schedules)

        LOG.info('Number of schedules = %d', num_schedules)

        # This should give us a sense of the volume of data being processed by each task.
        set_custom_attribute('num_schedules', num_schedules)

        return schedules
예제 #13
0
def _get_user_course_outline_and_processors(
        course_key: CourseKey,  # lint-amnesty, pylint: disable=missing-function-docstring
        user: User,
        at_time: datetime):
    full_course_outline = get_course_outline(course_key)
    user_can_see_all_content = can_see_all_content(user, course_key)

    # These are processors that alter which sequences are visible to students.
    # For instance, certain sequences that are intentionally hidden or not yet
    # released. These do not need to be run for staff users. This is where we
    # would add in pluggability for OutlineProcessors down the road.
    processor_classes = [
        ('content_gating', ContentGatingOutlineProcessor),
        ('milestones', MilestonesOutlineProcessor),
        ('schedule', ScheduleOutlineProcessor),
        ('special_exams', SpecialExamsOutlineProcessor),
        ('visibility', VisibilityOutlineProcessor),
        ('enrollment', EnrollmentOutlineProcessor),
        # Future:
        # ('user_partitions', UserPartitionsOutlineProcessor),
    ]

    # Run each OutlineProcessor in order to figure out what items we have to
    # remove from the CourseOutline.
    processors = dict()
    usage_keys_to_remove = set()
    inaccessible_sequences = set()
    for name, processor_cls in processor_classes:
        # Future optimization: This should be parallelizable (don't rely on a
        # particular ordering).
        processor = processor_cls(course_key, user, at_time)
        processors[name] = processor
        processor.load_data()
        if not user_can_see_all_content:
            # function_trace lets us see how expensive each processor is being.
            with function_trace('processor:{}'.format(name)):
                processor_usage_keys_removed = processor.usage_keys_to_remove(
                    full_course_outline)
                processor_inaccessible_sequences = processor.inaccessible_sequences(
                    full_course_outline)
                usage_keys_to_remove |= processor_usage_keys_removed
                inaccessible_sequences |= processor_inaccessible_sequences

    # Open question: Does it make sense to remove a Section if it has no Sequences in it?
    trimmed_course_outline = full_course_outline.remove(usage_keys_to_remove)
    accessible_sequences = set(
        trimmed_course_outline.sequences) - inaccessible_sequences

    user_course_outline = UserCourseOutlineData(
        base_outline=full_course_outline,
        user=user,
        at_time=at_time,
        accessible_sequences=accessible_sequences,
        **{
            name: getattr(trimmed_course_outline, name)
            for name in [
                'course_key',
                'title',
                'published_at',
                'published_version',
                'entrance_exam_id',
                'sections',
                'self_paced',
                'course_visibility',
                'days_early_for_beta',
            ]
        })

    return user_course_outline, processors
예제 #14
0
def _get_user_course_outline_and_processors(
        course_key: CourseKey,  # lint-amnesty, pylint: disable=missing-function-docstring
        user: types.User,
        at_time: datetime):
    """
    Helper function that runs the outline processors.

    This function returns a UserCourseOutlineData and a dict of outline
    processors that have executed their data loading and returned which
    sequences to remove and which to mark as inaccessible.
    """
    # Record the user separately from the standard user_id that views record,
    # because it's possible to ask for views as other users if you're global
    # staff. Performance is going to vary based on the user we're asking the
    # outline for, not the user who is initiating the request.
    set_custom_attribute('learning_sequences.api.user_id', user.id)

    full_course_outline = get_course_outline(course_key)
    user_can_see_all_content = can_see_all_content(user, course_key)

    # These are processors that alter which sequences are visible to students.
    # For instance, certain sequences that are intentionally hidden or not yet
    # released. These do not need to be run for staff users. This is where we
    # would add in pluggability for OutlineProcessors down the road.
    processor_classes = [
        ('content_gating', ContentGatingOutlineProcessor),
        ('milestones', MilestonesOutlineProcessor),
        ('schedule', ScheduleOutlineProcessor),
        ('special_exams', SpecialExamsOutlineProcessor),
        ('visibility', VisibilityOutlineProcessor),
        ('enrollment', EnrollmentOutlineProcessor),
        ('enrollment_track_partitions',
         EnrollmentTrackPartitionGroupsOutlineProcessor),
    ]

    # Run each OutlineProcessor in order to figure out what items we have to
    # remove from the CourseOutline.
    processors = {}
    usage_keys_to_remove = set()
    inaccessible_sequences = set()
    for name, processor_cls in processor_classes:
        # Future optimization: This should be parallelizable (don't rely on a
        # particular ordering).
        processor = processor_cls(course_key, user, at_time)
        processors[name] = processor
        processor.load_data(full_course_outline)
        if not user_can_see_all_content:
            # function_trace lets us see how expensive each processor is being.
            with function_trace(
                    f'learning_sequences.api.outline_processors.{name}'):
                processor_usage_keys_removed = processor.usage_keys_to_remove(
                    full_course_outline)
                processor_inaccessible_sequences = processor.inaccessible_sequences(
                    full_course_outline)
                usage_keys_to_remove |= processor_usage_keys_removed
                inaccessible_sequences |= processor_inaccessible_sequences

    # Open question: Does it make sense to remove a Section if it has no Sequences in it?
    trimmed_course_outline = full_course_outline.remove(usage_keys_to_remove)
    accessible_sequences = frozenset(
        set(trimmed_course_outline.sequences) - inaccessible_sequences)

    user_course_outline = UserCourseOutlineData(
        base_outline=full_course_outline,
        user=user,
        at_time=at_time,
        accessible_sequences=accessible_sequences,
        **{
            name: getattr(trimmed_course_outline, name)
            for name in [
                'course_key',
                'title',
                'published_at',
                'published_version',
                'entrance_exam_id',
                'sections',
                'self_paced',
                'course_visibility',
                'days_early_for_beta',
            ]
        })

    return user_course_outline, processors
예제 #15
0
파일: views.py 프로젝트: saadow123/1
def followed_threads(request, course_key, user_id):
    """
    Ajax-only endpoint retrieving the threads followed by a specific user.
    """
    course = get_course_with_access(request.user,
                                    'load',
                                    course_key,
                                    check_if_enrolled=True)
    try:
        profiled_user = cc.User(id=user_id, course_id=course_key)

        query_params = {
            'page': 1,
            'per_page':
            THREADS_PER_PAGE,  # more than threads_per_page to show more activities
            'sort_key': 'date',
        }
        query_params.update(
            strip_none(
                extract(request.GET, [
                    'page',
                    'sort_key',
                    'flagged',
                    'unread',
                    'unanswered',
                ])))

        try:
            group_id = get_group_id_for_comments_service(request, course_key)
        except ValueError:
            return HttpResponseServerError("Invalid group_id")
        if group_id is not None:
            query_params['group_id'] = group_id

        paginated_results = profiled_user.subscribed_threads(query_params)
        print("\n \n \n paginated results \n \n \n ")
        print(paginated_results)
        query_params['page'] = paginated_results.page
        query_params['num_pages'] = paginated_results.num_pages
        user_info = cc.User.from_django_user(request.user).to_dict()

        with function_trace("get_metadata_for_threads"):
            annotated_content_info = utils.get_metadata_for_threads(
                course_key, paginated_results.collection, request.user,
                user_info)
        if request.is_ajax():
            is_staff = has_permission(request.user, 'openclose_thread',
                                      course.id)
            return utils.JsonResponse({
                'annotated_content_info':
                annotated_content_info,
                'discussion_data': [
                    utils.prepare_content(thread, course_key, is_staff)
                    for thread in paginated_results.collection
                ],
                'page':
                query_params['page'],
                'num_pages':
                query_params['num_pages'],
            })
        #TODO remove non-AJAX support, it does not appear to be used and does not appear to work.
        else:
            context = {
                'course': course,
                'user': request.user,
                'django_user': User.objects.get(id=user_id),
                'profiled_user': profiled_user.to_dict(),
                'threads': paginated_results.collection,
                'user_info': user_info,
                'annotated_content_info': annotated_content_info,
                #                'content': content,
            }

            return render_to_response('discussion/user_profile.html', context)
    except User.DoesNotExist:
        raise Http404