示例#1
0
文件: views.py 项目: JudyFox/edXMOOC
def add_users_to_cohort(request, course_key_string, cohort_id):
    """
    Return json dict of:

    {'success': True,
     'added': [{'username': ...,
                'name': ...,
                'email': ...}, ...],
     'changed': [{'username': ...,
                  'name': ...,
                  'email': ...,
                  'previous_cohort': ...}, ...],
     'present': [str1, str2, ...],    # already there
     'unknown': [str1, str2, ...]}

     Raises Http404 if the cohort cannot be found for the given course.
    """
    # this is a string when we get it here
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_key_string)
    get_course_with_access(request.user, 'staff', course_key)

    try:
        cohort = cohorts.get_cohort_by_id(course_key, cohort_id)
    except CourseUserGroup.DoesNotExist:
        raise Http404("Cohort (ID {cohort_id}) not found for {course_key_string}".format(
            cohort_id=cohort_id,
            course_key_string=course_key_string
        ))

    users = request.POST.get('users', '')
    added = []
    changed = []
    present = []
    unknown = []
    for username_or_email in split_by_comma_and_whitespace(users):
        if not username_or_email:
            continue

        try:
            (user, previous_cohort) = cohorts.add_user_to_cohort(cohort, username_or_email)
            info = {
                'username': user.username,
                'name': user.profile.name,
                'email': user.email,
            }
            if previous_cohort:
                info['previous_cohort'] = previous_cohort
                changed.append(info)
            else:
                added.append(info)
        except ValueError:
            present.append(username_or_email)
        except User.DoesNotExist:
            unknown.append(username_or_email)

    return json_http_response({'success': True,
                               'added': added,
                               'changed': changed,
                               'present': present,
                               'unknown': unknown})
示例#2
0
def add_cohort(request, course_key_string):
    """
    Return json of dict:
    {'success': True,
     'cohort': {'id': id,
                'name': name}}

                or

    {'success': False,
     'msg': error_msg} if there's an error
    """
    # this is a string when we get it here
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_key_string)

    get_course_with_access(request.user, 'staff', course_key)

    name = request.POST.get("name")
    if not name:
        return json_http_response({'success': False,
                                   'msg': "No name specified"})

    try:
        cohort = cohorts.add_cohort(course_key, name)
    except ValueError as err:
        return json_http_response({'success': False,
                                   'msg': str(err)})

    return json_http_response({
        'success': 'True',
        'cohort': {
            'id': cohort.id,
            'name': cohort.name
        }
    })
示例#3
0
文件: views.py 项目: JudyFox/edXMOOC
def remove_user_from_cohort(request, course_key_string, cohort_id):
    """
    Expects 'username': username in POST data.

    Return json dict of:

    {'success': True} or
    {'success': False,
     'msg': error_msg}
    """
    # this is a string when we get it here
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_key_string)
    get_course_with_access(request.user, 'staff', course_key)

    username = request.POST.get('username')
    if username is None:
        return json_http_response({'success': False,
                                   'msg': 'No username specified'})

    cohort = cohorts.get_cohort_by_id(course_key, cohort_id)
    try:
        user = User.objects.get(username=username)
        cohort.users.remove(user)
        return json_http_response({'success': True})
    except User.DoesNotExist:
        log.debug('no user')
        return json_http_response({'success': False,
                                   'msg': "No user '{0}'".format(username)})
示例#4
0
def remove_user_from_cohort(request, course_key_string, cohort_id):
    """
    Expects 'username': username in POST data.

    Return json dict of:

    {'success': True} or
    {'success': False,
     'msg': error_msg}
    """
    # this is a string when we get it here
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_key_string)
    get_course_with_access(request.user, "staff", course_key)

    username = request.POST.get("username")
    if username is None:
        return json_http_response({"success": False, "msg": "No username specified"})

    try:
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        log.debug("no user")
        return json_http_response({"success": False, "msg": "No user '{0}'".format(username)})

    try:
        membership = CohortMembership.objects.get(user=user, course_id=course_key)
        membership.delete()

    except CohortMembership.DoesNotExist:
        pass

    return json_http_response({"success": True})
示例#5
0
def course_cohort_settings_handler(request, course_key_string):
    """
    The restful handler for cohort setting requests. Requires JSON.
    This will raise 404 if user is not staff.
    GET
        Returns the JSON representation of cohort settings for the course.
    PATCH
        Updates the cohort settings for the course. Returns the JSON representation of updated settings.
    """
    course_key = CourseKey.from_string(course_key_string)
    # Although this course data is not used this method will return 404 is user is not staff
    get_course_with_access(request.user, 'staff', course_key)

    if request.method == 'PATCH':
        if 'is_cohorted' not in request.json:
            return JsonResponse({"error": unicode("Bad Request")}, 400)

        is_cohorted = request.json.get('is_cohorted')
        try:
            cohorts.set_course_cohorted(course_key, is_cohorted)
        except ValueError as err:
            # Note: error message not translated because it is not exposed to the user (UI prevents this state).
            return JsonResponse({"error": unicode(err)}, 400)

    return JsonResponse(_get_course_cohort_settings_representation(
        cohorts.get_course_cohort_id(course_key),
        cohorts.is_course_cohorted(course_key)
    ))
示例#6
0
def hint_manager(request, course_id):
    """
    The URL landing function for all calls to the hint manager, both POST and GET.
    """
    try:
        get_course_with_access(request.user, course_id, 'staff', depth=None)
    except Http404:
        out = 'Sorry, but students are not allowed to access the hint manager!'
        return HttpResponse(out)
    if request.method == 'GET':
        out = get_hints(request, course_id, 'mod_queue')
        out.update({'error': ''})
        return render_to_response('instructor/hint_manager.html', out)
    field = request.POST['field']
    if not (field == 'mod_queue' or field == 'hints'):
        # Invalid field.  (Don't let users continue - they may overwrite other db's)
        out = 'Error in hint manager - an invalid field was accessed.'
        return HttpResponse(out)

    switch_dict = {
        'delete hints': delete_hints,
        'switch fields': lambda *args: None,    # Takes any number of arguments, returns None.
        'change votes': change_votes,
        'add hint': add_hint,
        'approve': approve,
    }

    # Do the operation requested, and collect any error messages.
    error_text = switch_dict[request.POST['op']](request, course_id, field)
    if error_text is None:
        error_text = ''
    render_dict = get_hints(request, course_id, field)
    render_dict.update({'error': error_text})
    rendered_html = render_to_string('instructor/hint_manager_inner.html', render_dict)
    return HttpResponse(json.dumps({'success': True, 'contents': rendered_html}))
示例#7
0
文件: views.py 项目: 2bj/edx-platform
def remove_user_from_cohort(request, course_id, cohort_id):
    """
    Expects 'username': username in POST data.

    Return json dict of:

    {'success': True} or
    {'success': False,
     'msg': error_msg}
    """
    get_course_with_access(request.user, course_id, 'staff')

    username = request.POST.get('username')
    if username is None:
        return json_http_response({'success': False,
                                'msg': 'No username specified'})

    cohort = cohorts.get_cohort_by_id(course_id, cohort_id)
    try:
        user = User.objects.get(username=username)
        cohort.users.remove(user)
        return json_http_response({'success': True})
    except User.DoesNotExist:
        log.debug('no user')
        return json_http_response({'success': False,
                                'msg': "No user '{0}'".format(username)})
def hint_manager(request, course_id):
    """
    The URL landing function for all calls to the hint manager, both POST and GET.
    """
    try:
        get_course_with_access(request.user, course_id, "staff", depth=None)
    except Http404:
        out = "Sorry, but students are not allowed to access the hint manager!"
        return HttpResponse(out)
    if request.method == "GET":
        out = get_hints(request, course_id, "mod_queue")
        out.update({"error": ""})
        return render_to_response("instructor/hint_manager.html", out)
    field = request.POST["field"]
    if not (field == "mod_queue" or field == "hints"):
        # Invalid field.  (Don't let users continue - they may overwrite other db's)
        out = "Error in hint manager - an invalid field was accessed."
        return HttpResponse(out)

    switch_dict = {
        "delete hints": delete_hints,
        "switch fields": lambda *args: None,  # Takes any number of arguments, returns None.
        "change votes": change_votes,
        "add hint": add_hint,
        "approve": approve,
    }

    # Do the operation requested, and collect any error messages.
    error_text = switch_dict[request.POST["op"]](request, course_id, field)
    if error_text is None:
        error_text = ""
    render_dict = get_hints(request, course_id, field)
    render_dict.update({"error": error_text})
    rendered_html = render_to_string("instructor/hint_manager_inner.html", render_dict)
    return HttpResponse(json.dumps({"success": True, "contents": rendered_html}))
示例#9
0
def users(request, course_id):
    """
    Given a `username` query parameter, find matches for users in the forum for this course.

    Only exact matches are supported here, so the length of the result set will either be 0 or 1.
    """

    course_key = CourseKey.from_string(course_id)
    try:
        get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    except Http404:
        # course didn't exist, or requesting user does not have access to it.
        return JsonError(status=404)

    try:
        username = request.GET['username']
    except KeyError:
        # 400 is default status for JsonError
        return JsonError(["username parameter is required"])

    user_objs = []
    try:
        matched_user = User.objects.get(username=username)
        cc_user = cc.User.from_django_user(matched_user)
        cc_user.course_id = course_key
        cc_user.retrieve(complete=False)
        if (cc_user['threads_count'] + cc_user['comments_count']) > 0:
            user_objs.append({
                'id': matched_user.id,
                'username': matched_user.username,
            })
    except User.DoesNotExist:
        pass
    return JsonResponse({"users": user_objs})
示例#10
0
文件: views.py 项目: 2bj/edx-platform
def add_cohort(request, course_id):
    """
    Return json of dict:
    {'success': True,
     'cohort': {'id': id,
                'name': name}}

                or

    {'success': False,
     'msg': error_msg} if there's an error
    """
    get_course_with_access(request.user, course_id, 'staff')

    name = request.POST.get("name")
    if not name:
        return json_http_response({'success': False,
                                'msg': "No name specified"})

    try:
        cohort = cohorts.add_cohort(course_id, name)
    except ValueError as err:
        return json_http_response({'success': False,
                                'msg': str(err)})

    return json_http_response({'success': 'True',
                            'cohort': {
                                'id': cohort.id,
                                'name': cohort.name
                                }})
示例#11
0
def hint_manager(request, course_id):
    try:
        get_course_with_access(request.user, course_id, 'staff', depth=None)
    except Http404:
        out = 'Sorry, but students are not allowed to access the hint manager!'
        return HttpResponse(out)
    if request.method == 'GET':
        out = get_hints(request, course_id, 'mod_queue')
        return render_to_response('courseware/hint_manager.html', out)
    field = request.POST['field']
    if not (field == 'mod_queue' or field == 'hints'):
        # Invalid field.  (Don't let users continue - they may overwrite other db's)
        out = 'Error in hint manager - an invalid field was accessed.'
        return HttpResponse(out)

    if request.POST['op'] == 'delete hints':
        delete_hints(request, course_id, field)
    if request.POST['op'] == 'switch fields':
        pass
    if request.POST['op'] == 'change votes':
        change_votes(request, course_id, field)
    if request.POST['op'] == 'add hint':
        add_hint(request, course_id, field)
    if request.POST['op'] == 'approve':
        approve(request, course_id, field)
    rendered_html = render_to_string('courseware/hint_manager_inner.html', get_hints(request, course_id, field))
    return HttpResponse(json.dumps({'success': True, 'contents': rendered_html}))
示例#12
0
def mobi_course_action(request, course_id, action):
    try:
        course_id_bak = course_id.replace('.', '/')
        if action in ["updates", "handouts", "structure"]:
            course = get_course_with_access(request.user, course_id_bak, 'see_exists')
            user = request.user
            if not user:
                user = AnonymousUser()

            registered = registered_for_course(course, user)

            if action == "updates" and registered:
                course_updates = get_course_info_section(request, course, action)
                return JsonResponse(parse_updates_html_str(course_updates))
            elif action == "handouts" and registered:
                course_handouts = get_course_info_section(request, course, action)
                return JsonResponse({"handouts": course_handouts})
            elif action == "structure":
                return JsonResponse(_course_json(course, course.location.course_id))
            else:
                raise Exception
        else:
            course = get_course_with_access(request.user, course_id_bak, 'see_exists')
            return JsonResponse(mobi_course_info(request, course))
    except:
        return JsonResponse({"success": False, "errmsg": "access denied!"})
示例#13
0
    def test_get_course_with_access(self):
        user = UserFactory.create()
        course = CourseFactory.create(visible_to_staff_only=True)

        with self.assertRaises(CoursewareAccessException) as error:
            get_course_with_access(user, 'load', course.id)
        self.assertEqual(error.exception.message, "Course not found.")
        self.assertEqual(error.exception.access_response.error_code, "not_visible_to_user")
        self.assertFalse(error.exception.access_response.has_access)
示例#14
0
文件: views.py 项目: 2bj/edx-platform
def debug_cohort_mgmt(request, course_id):
    """
    Debugging view for dev.
    """
    # add staff check to make sure it's safe if it's accidentally deployed.
    get_course_with_access(request.user, course_id, 'staff')

    context = {'cohorts_ajax_url': reverse('cohorts',
                                           kwargs={'course_id': course_id})}
    return render_to_response('/course_groups/debug.html', context)
示例#15
0
def debug_cohort_mgmt(request, course_key_string):
    """
    Debugging view for dev.
    """
    # this is a string when we get it here
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_key_string)
    # add staff check to make sure it's safe if it's accidentally deployed.
    get_course_with_access(request.user, "staff", course_key)

    context = {"cohorts_url": reverse("cohorts", kwargs={"course_key": course_key.to_deprecated_string()})}
    return render_to_response("/course_groups/debug.html", context)
示例#16
0
def debug_cohort_mgmt(request, course_key_string):
    """
    Debugging view for dev.
    """
    # this is a string when we get it here
    course_key = CourseKey.from_string(course_key_string)
    # add staff check to make sure it's safe if it's accidentally deployed.
    get_course_with_access(request.user, 'staff', course_key)

    context = {'cohorts_url': reverse(
        'cohorts',
        kwargs={'course_key': text_type(course_key)}
    )}
    return render_to_response('/course_groups/debug.html', context)
示例#17
0
def add_users_to_cohort(request, course_id, cohort_id):
    """
    Return json dict of:

    {'success': True,
     'added': [{'username': ...,
                'name': ...,
                'email': ...}, ...],
     'changed': [{'username': ...,
                  'name': ...,
                  'email': ...,
                  'previous_cohort': ...}, ...],
     'present': [str1, str2, ...],    # already there
     'unknown': [str1, str2, ...]}
    """
    get_course_with_access(request.user, course_id, 'staff')

    cohort = cohorts.get_cohort_by_id(course_id, cohort_id)

    users = request.POST.get('users', '')
    added = []
    changed = []
    present = []
    unknown = []
    for username_or_email in split_by_comma_and_whitespace(users):
        if not username_or_email:
            continue

        try:
            (user, previous_cohort) = cohorts.add_user_to_cohort(cohort, username_or_email)
            info = {
                'username': user.username,
                'name': user.profile.name,
                'email': user.email,
            }
            if previous_cohort:
                info['previous_cohort'] = previous_cohort
                changed.append(info)
            else:
                added.append(info)
        except ValueError:
            present.append(username_or_email)
        except User.DoesNotExist:
            unknown.append(username_or_email)

    return json_http_response({'success': True,
                            'added': added,
                            'changed': changed,
                            'present': present,
                            'unknown': unknown})
    def get_course_time(self, user_id, course_id, type='', add_time_out=False):
        count_time = self.get_adjustment_time(user_id, type, course_id)
        if type == 'courseware':
            if course_id is None:
                course_time = {}
                results = self.collection_page.find({'user_id': user_id})
                for data in results:
                    try:
                        course_time[data['course_id']]['time'] += int(data['time'])
                    except:
                        max_time = int(get_course_with_access(user_id, data['course_id'], 'load').maximum_units_time)
                        course_time[data['course_id']] = {'time': int(data['time']), 'max_time': max_time}

                for cid in course_time:
                    if course_time[cid]['time'] > course_time[cid]['max_time']:
                        course_time[cid]['time'] = course_time[cid]['max_time']
                    count_time += course_time[cid]['time']
            else:
                results = self.collection_page.find(
                    {
                        'user_id': user_id,
                        'course_id': course_id
                    }
                )
                for data in results:
                    count_time += int(data['time'])
                max_time = int(get_course_with_access(user_id, course_id, 'load').maximum_units_time)
                if add_time_out:
                    fix_time = count_time - settings.PEPPER_SESSION_EXPIRY
                    if fix_time >= 0:
                        count_time = fix_time
                if count_time > max_time:
                    count_time = max_time
            return count_time

        elif type == 'discussion':
            if course_id is None:
                results = self.collection_discussion.find({'user_id': user_id})
            else:
                results = self.collection_discussion.find(
                    {
                        'user_id': user_id,
                        'course_id': course_id
                    }
                )
        elif type == 'portfolio':
            results = self.collection_portfolio.find({'user_id': user_id})
        for data in results:
            count_time += int(data['time'])
        return count_time
示例#19
0
文件: views.py 项目: 2bj/edx-platform
def list_cohorts(request, course_id):
    """
    Return json dump of dict:

    {'success': True,
     'cohorts': [{'name': name, 'id': id}, ...]}
    """
    get_course_with_access(request.user, course_id, 'staff')

    all_cohorts = [{'name': c.name, 'id': c.id}
               for c in cohorts.get_course_cohorts(course_id)]

    return json_http_response({'success': True,
                            'cohorts': all_cohorts})
示例#20
0
def users_in_cohort(request, course_key_string, cohort_id):
    """
    Return users in the cohort.  Show up to 100 per page, and page
    using the 'page' GET attribute in the call.  Format:

    Returns:
        Json dump of dictionary in the following format:
        {'success': True,
         'page': page,
         'num_pages': paginator.num_pages,
         'users': [{'username': ..., 'email': ..., 'name': ...}]
    }
    """
    # this is a string when we get it here
    course_key = SlashSeparatedCourseKey.from_deprecated_string(
        course_key_string)

    get_course_with_access(request.user, 'staff', course_key)

    # this will error if called with a non-int cohort_id.  That's ok--it
    # shoudn't happen for valid clients.
    cohort = cohorts.get_cohort_by_id(course_key, int(cohort_id))

    paginator = Paginator(cohort.users.all(), 100)
    page = request.GET.get('page')
    try:
        users = paginator.page(page)
    except PageNotAnInteger:
        # return the first page
        page = 1
        users = paginator.page(page)
    except EmptyPage:
        # Page is out of range.  Return last page
        page = paginator.num_pages
        contacts = paginator.page(page)

    user_info = [{
        'username': u.username,
        'email': u.email,
        'name': '{0} {1}'.format(u.first_name, u.last_name)
    } for u in users]

    return json_http_response({
        'success': True,
        'page': page,
        'num_pages': paginator.num_pages,
        'users': user_info
    })
示例#21
0
def static_tab(request, course_id, tab_slug):
    """
    Display the courses tab with the given name.

    Assumes the course_id is in a valid format.
    """
    try:
        course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    except InvalidKeyError:
        raise Http404

    course = get_course_with_access(request.user, 'load', course_key)

    tab = CourseTabList.get_tab_by_slug(course.tabs, tab_slug)
    if tab is None:
        raise Http404

    contents = get_static_tab_contents(
        request,
        course,
        tab
    )
    if contents is None:
        raise Http404

    return render_to_response('courseware/static_tab.html', {
        'course': course,
        'tab': tab,
        'tab_contents': contents,
    })
示例#22
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)
    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')
示例#23
0
def course_info(request, course_id):
    """
    Display the course's info.html, or 404 if there is no such course.

    Assumes the course_id is in a valid format.
    """
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(request.user, 'load', course_key)
    staff_access = has_access(request.user, 'staff', course)
    masq = setup_masquerade(request, staff_access)    # allow staff to toggle masquerade on info page
    reverifications = fetch_reverify_banner_info(request, course_key)
    studio_url = get_studio_url(course_key, 'course_info')

    context = {
        'request': request,
        'course_id': course_key.to_deprecated_string(),
        'cache': None,
        'course': course,
        'staff_access': staff_access,
        'masquerade': masq,
        'studio_url': studio_url,
        'reverifications': reverifications,
    }

    return render_to_response('courseware/info.html', context)
示例#24
0
def inline_discussion(request, course_key, discussion_id):
    """
    Renders JSON for DiscussionModules
    """
    nr_transaction = newrelic.agent.current_transaction()

    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:
        threads, query_params = get_threads(request, course, discussion_id, per_page=INLINE_THREADS_PER_PAGE)
    except ValueError:
        return HttpResponseBadRequest("Invalid group_id")

    with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"):
        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 newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
        add_courseware_context(threads, course, request.user)
    return utils.JsonResponse({
        'is_commentable_cohorted': is_commentable_cohorted(course_key, discussion_id),
        'discussion_data': threads,
        'user_info': user_info,
        '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)
    })
示例#25
0
def mktg_course_about(request, course_id):
    """
    This is the button that gets put into an iframe on the Drupal site
    """

    try:
        course = get_course_with_access(request.user, course_id, "see_exists")
    except (ValueError, Http404) as e:
        # if a course does not exist yet, display a coming
        # soon button
        return render_to_response("courseware/mktg_coming_soon.html", {"course_id": course_id})

    registered = registered_for_course(course, request.user)

    if has_access(request.user, course, "load"):
        course_target = reverse("info", args=[course.id])
    else:
        course_target = reverse("about_course", args=[course.id])

    allow_registration = has_access(request.user, course, "enroll")

    show_courseware_link = has_access(request.user, course, "load") or settings.FEATURES.get("ENABLE_LMS_MIGRATION")
    course_modes = CourseMode.modes_for_course(course.id)

    return render_to_response(
        "courseware/mktg_course_about.html",
        {
            "course": course,
            "registered": registered,
            "allow_registration": allow_registration,
            "course_target": course_target,
            "show_courseware_link": show_courseware_link,
            "course_modes": course_modes,
        },
    )
    def test_module_render_with_jump_to_id(self):
        """
        This test validates that the /jump_to_id/<id> shorthand for intracourse linking works assertIn
        expected. Note there's a HTML element in the 'toy' course with the url_name 'toyjumpto' which
        defines this linkage
        """
        mock_request = MagicMock()
        mock_request.user = self.mock_user

        course = get_course_with_access(self.mock_user, self.course_id, 'load')

        field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
            self.course_id, self.mock_user, course, depth=2)

        module = render.get_module(
            self.mock_user,
            mock_request,
            Location('i4x', 'edX', 'toy', 'html', 'toyjumpto'),
            field_data_cache,
            self.course_id
        )

        # get the rendered HTML output which should have the rewritten link
        html = module.render('student_view').content

        # See if the url got rewritten to the target link
        # note if the URL mapping changes then this assertion will break
        self.assertIn('/courses/' + self.course_id + '/jump_to_id/vertical_test', html)
示例#27
0
def index(request, course_id, book_index, page=None):
    """
    Serve static image-based textbooks.
    """
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(request.user, 'load', course_key)
    staff_access = bool(has_access(request.user, 'staff', course))

    book_index = int(book_index)
    if book_index < 0 or book_index >= len(course.textbooks):
        raise Http404("Invalid book index value: {0}".format(book_index))
    textbook = course.textbooks[book_index]
    table_of_contents = textbook.table_of_contents

    if page is None:
        page = textbook.start_page

    return render_to_response(
        'staticbook.html',
        {
            'book_index': book_index, 'page': int(page),
            'course': course,
            'book_url': textbook.book_url,
            'table_of_contents': table_of_contents,
            'start_page': textbook.start_page,
            'end_page': textbook.end_page,
            'staff_access': staff_access,
        },
    )
示例#28
0
def _create_base_discussion_view_context(request, course_key):
    """
    Returns the default template context for rendering any discussion view.
    """
    user = request.user
    cc_user = cc.User.from_django_user(user)
    user_info = cc_user.to_dict()
    course = get_course_with_access(user, 'load', course_key, check_if_enrolled=True)
    course_settings = make_course_settings(course, user)
    uses_bootstrap = USE_BOOTSTRAP_FLAG.is_enabled()
    return {
        'csrf': csrf(request)['csrf_token'],
        'course': course,
        'user': user,
        'user_info': user_info,
        'staff_access': bool(has_access(user, 'staff', course)),
        'roles': utils.get_role_ids(course_key),
        'can_create_comment': has_permission(user, "create_comment", course.id),
        'can_create_subcomment': has_permission(user, "create_sub_comment", course.id),
        'can_create_thread': has_permission(user, "create_thread", course.id),
        'flag_moderator': bool(
            has_permission(user, 'openclose_thread', course.id) or
            has_access(user, 'staff', course)
        ),
        'course_settings': course_settings,
        'disable_courseware_js': True,
        'uses_bootstrap': uses_bootstrap,
        'uses_pattern_library': not uses_bootstrap,
    }
示例#29
0
    def test_module_render_with_jump_to_id(self):
        """
        This test validates that the /jump_to_id/<id> shorthand for intracourse linking works assertIn
        expected. Note there's a HTML element in the 'toy' course with the url_name 'toyjumpto' which
        defines this linkage
        """
        mock_request = MagicMock()
        mock_request.user = self.mock_user

        course = get_course_with_access(self.mock_user, "load", self.course_key)

        field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
            self.course_key, self.mock_user, course, depth=2
        )

        module = render.get_module(
            self.mock_user, mock_request, self.course_key.make_usage_key("html", "toyjumpto"), field_data_cache
        )

        # get the rendered HTML output which should have the rewritten link
        html = module.render(STUDENT_VIEW).content

        # See if the url got rewritten to the target link
        # note if the URL mapping changes then this assertion will break
        self.assertIn("/courses/" + self.course_key.to_deprecated_string() + "/jump_to_id/vertical_test", html)
示例#30
0
def update_thread(request, course_id, thread_id):
    """
    Given a course id and thread id, update a existing thread, used for both static and ajax submissions
    """
    if 'title' not in request.POST or not request.POST['title'].strip():
        return JsonError(_("Title can't be empty"))
    if 'body' not in request.POST or not request.POST['body'].strip():
        return JsonError(_("Body can't be empty"))

    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    thread = cc.Thread.find(thread_id)
    thread.body = request.POST["body"]
    thread.title = request.POST["title"]
    # The following checks should avoid issues we've seen during deploys, where end users are hitting an updated server
    # while their browser still has the old client code. This will avoid erasing present values in those cases.
    if "thread_type" in request.POST:
        thread.thread_type = request.POST["thread_type"]
    if "commentable_id" in request.POST:
        course = get_course_with_access(request.user, 'load', course_key)
        commentable_ids = get_discussion_categories_ids(course)
        if request.POST.get("commentable_id") in commentable_ids:
            thread.commentable_id = request.POST["commentable_id"]
        else:
            return JsonError(_("Topic doesn't exist"))

    thread.save()
    if request.is_ajax():
        return ajax_content_response(request, course_key, thread.to_dict())
    else:
        return JsonResponse(prepare_content(thread.to_dict(), course_key))
示例#31
0
def peer_grading(request, course_id):
    '''
    When a student clicks on the "peer grading" button in the open ended interface, link them to a peer grading
    xmodule in the course.
    '''

    #Get the current course
    course = get_course_with_access(request.user, course_id, 'load')

    found_module, problem_url = find_peer_grading_module(course)
    if not found_module:
        #This is a student_facing_error
        error_message = """
        Error with initializing peer grading.
        There has not been a peer grading module created in the courseware that would allow you to grade others.
        Please check back later for this.
        """
        #This is a dev_facing_error
        log.exception(error_message +
                      "Current course is: {0}".format(course_id))
        return HttpResponse(error_message)

    return HttpResponseRedirect(problem_url)
    def render_to_fragment(self, request, course_id=None, **kwargs):
        """
        Renders the latest update message fragment for the specified course.

        Returns: A fragment, or None if there is no latest update message.
        """
        course_key = CourseKey.from_string(course_id)
        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=True)

        ordered_updates = get_ordered_updates(request, course)

        if not ordered_updates:
            return None

        context = {
            'ordered_updates': ordered_updates,
        }
        html = render_to_string(
            'course_experience/course-home-updates-fragment.html', context)
        return Fragment(html)
示例#33
0
def forum_form_discussion(request, course_key):
    """
    Renders the main Discussion page, potentially filtered by a search query
    """
    nr_transaction = newrelic.agent.current_transaction()

    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    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 newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"):
            annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

        with newrelic.agent.FunctionTrace(nr_transaction, "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')
示例#34
0
def _create_comment(request, course_id, thread_id=None, parent_id=None):
    """
    given a course_id, thread_id, and parent_id, create a comment,
    called from create_comment to do the actual creation
    """
    post = request.POST

    if 'body' not in post or not post['body'].strip():
        return JsonError(_("Body can't be empty"))

    course = get_course_with_access(request.user, course_id, 'load')
    if course.allow_anonymous:
        anonymous = post.get('anonymous', 'false').lower() == 'true'
    else:
        anonymous = False

    if course.allow_anonymous_to_peers:
        anonymous_to_peers = post.get('anonymous_to_peers',
                                      'false').lower() == 'true'
    else:
        anonymous_to_peers = False

    comment = cc.Comment(anonymous=anonymous,
                         anonymous_to_peers=anonymous_to_peers,
                         user_id=request.user.id,
                         course_id=course_id,
                         thread_id=thread_id,
                         parent_id=parent_id,
                         body=post["body"])
    comment.save()
    if post.get('auto_subscribe', 'false').lower() == 'true':
        user = cc.User.from_django_user(request.user)
        user.follow(comment.thread)
    if request.is_ajax():
        return ajax_content_response(request, course_id, comment.to_dict())
    else:
        return JsonResponse(utils.safe_content(comment.to_dict()))
示例#35
0
def _create_comment(request, course_id, thread_id=None, parent_id=None):
    """
    given a course_id, thread_id, and parent_id, create a comment,
    called from create_comment to do the actual creation
    """
    post = request.POST
    comment = cc.Comment(**extract(post, ['body']))

    course = get_course_with_access(request.user, course_id, 'load')
    if course.allow_anonymous:
        anonymous = post.get('anonymous', 'false').lower() == 'true'
    else:
        anonymous = False

    if course.allow_anonymous_to_peers:
        anonymous_to_peers = post.get('anonymous_to_peers',
                                      'false').lower() == 'true'
    else:
        anonymous_to_peers = False

    comment.update_attributes(
        **{
            'anonymous': anonymous,
            'anonymous_to_peers': anonymous_to_peers,
            'user_id': request.user.id,
            'course_id': course_id,
            'thread_id': thread_id,
            'parent_id': parent_id,
        })
    comment.save()
    if post.get('auto_subscribe', 'false').lower() == 'true':
        user = cc.User.from_django_user(request.user)
        user.follow(comment.thread)
    if request.is_ajax():
        return ajax_content_response(request, course_id, comment.to_dict())
    else:
        return JsonResponse(utils.safe_content(comment.to_dict()))
示例#36
0
def spoc_gradebook(request, course_id):
    """
    Show the gradebook for this course:
    - Only shown for courses with enrollment < settings.FEATURES.get("MAX_ENROLLMENT_INSTR_BUTTONS")
    - Only displayed to course staff
    """
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(request.user, 'staff', course_key, depth=None)

    enrolled_students = User.objects.filter(
        courseenrollment__course_id=course_key,
        courseenrollment__is_active=1
    ).order_by('username').select_related("profile")

    # TODO (vshnayder): implement pagination to show to large courses
    max_num_students = settings.FEATURES.get("MAX_ENROLLMENT_INSTR_BUTTONS")
    enrolled_students = enrolled_students[:max_num_students]   # HACK!

    student_info = [
        {
            'username': student.username,
            'id': student.id,
            'email': student.email,
            'grade_summary': student_grades(student, request, course),
            'realname': student.profile.name,
        }
        for student in enrolled_students
    ]

    return render_to_response('courseware/gradebook.html', {
        'students': student_info,
        'course': course,
        'course_id': course_key,
        # Checked above
        'staff_access': True,
        'ordered_grades': sorted(course.grade_cutoffs.items(), key=lambda i: i[1], reverse=True),
    })
示例#37
0
def update_thread(request, course_id, thread_id):
    """
    Given a course id and thread id, update a existing thread, used for both static and ajax submissions
    """
    if 'title' not in request.POST or not request.POST['title'].strip():
        return JsonError(_("Title can't be empty"))
    if 'body' not in request.POST or not request.POST['body'].strip():
        return JsonError(_("Body can't be empty"))

    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    thread = cc.Thread.find(thread_id)
    # Get thread context first in order to be safe from reseting the values of thread object later
    thread_context = getattr(thread, "context", "course")
    thread.body = request.POST["body"]
    thread.title = request.POST["title"]
    user = request.user
    # The following checks should avoid issues we've seen during deploys, where end users are hitting an updated server
    # while their browser still has the old client code. This will avoid erasing present values in those cases.
    if "thread_type" in request.POST:
        thread.thread_type = request.POST["thread_type"]
    if "commentable_id" in request.POST:
        commentable_id = request.POST["commentable_id"]
        course = get_course_with_access(user, 'load', course_key)
        if thread_context == "course" and not discussion_category_id_access(
                course, user, commentable_id):
            return JsonError(_("Topic doesn't exist"))
        else:
            thread.commentable_id = commentable_id

    thread.save()

    thread_edited.send(sender=None, user=user, post=thread)

    if request.is_ajax():
        return ajax_content_response(request, course_key, thread.to_dict())
    else:
        return JsonResponse(prepare_content(thread.to_dict(), course_key))
    def render_to_fragment(self, request, course_id, user_access, **kwargs):
        """
        Renders a course message fragment for the specified course.
        """
        course_key = CourseKey.from_string(course_id)
        course = get_course_with_access(request.user, 'load', course_key)

        # Get time until the start date, if already started, or no start date, value will be zero or negative
        now = datetime.now(UTC())
        already_started = course.start and now > course.start
        days_until_start_string = "started" if already_started else format_timedelta(course.start - now, locale=to_locale(get_language()))
        course_start_data = {
            'course_start_date': format_date(course.start, locale=to_locale(get_language())),
            'already_started': already_started,
            'days_until_start_string': days_until_start_string
        }

        # Register the course home messages to be loaded on the page
        self.register_course_home_messages(request, course, user_access, course_start_data)

        # Grab the relevant messages
        course_home_messages = list(CourseHomeMessages.user_messages(request))

        # Return None if user is enrolled and course has begun
        if user_access['is_enrolled'] and already_started:
            return None

        # Grab the logo
        image_src = "course_experience/images/home_message_author.png"

        context = {
            'course_home_messages': course_home_messages,
            'image_src': image_src,
        }

        html = render_to_string('course_experience/course-messages-fragment.html', context)
        return Fragment(html)
示例#39
0
def context_processor(request):
    """
    This is a context processor which looks at the URL while we are
    in the wiki. If the url is in the form
    /courses/(course_id)/wiki/...
    then we add 'course' to the context. This allows the course nav
    bar to be shown.
    """

    match = re.match(IN_COURSE_WIKI_REGEX, request.path)
    if match:
        course_id = match.group('course_id')

        try:
            course = get_course_with_access(request.user, course_id, 'load')
            staff_access = has_access(request.user, course, 'staff')
            return {'course': course, 'staff_access': staff_access}
        except Http404:
            # We couldn't access the course for whatever reason. It is too late to change
            # the URL here, so we just leave the course context. The middleware shouldn't
            # let this happen
            pass

    return {}
示例#40
0
def index(request, course_id, book_index, page=None):
    course = get_course_with_access(request.user, course_id, 'load')
    staff_access = has_access(request.user, course, 'staff')

    book_index = int(book_index)
    if book_index < 0 or book_index >= len(course.textbooks):
        raise Http404("Invalid book index value: {0}".format(book_index))
    textbook = course.textbooks[book_index]
    table_of_contents = textbook.table_of_contents

    if page is None:
        page = textbook.start_page

    return render_to_response(
        'staticbook.html', {
            'book_index': book_index,
            'page': int(page),
            'course': course,
            'book_url': textbook.book_url,
            'table_of_contents': table_of_contents,
            'start_page': textbook.start_page,
            'end_page': textbook.end_page,
            'staff_access': staff_access
        })
示例#41
0
def get_all_last_access(request, course_id):
    """
    Show the last_access for the users enrolled in this course:
    Only displayed to course staff
    """
    course_key = CourseKey.from_string(course_id)
    course = get_course_with_access(request.user,
                                    'staff',
                                    course_key,
                                    depth=None)
    enrollments_info, page = get_last_access_user_page(request, course_key)

    # student_info, page = get_grade_book_page(request, course, course_key)
    return render_to_response(
        'courseware/last_access.html', {
            'page':
            page,
            'page_url':
            reverse('last_access', kwargs={'course_id': unicode(course_key)}),
            'course':
            course,
            'enrollments':
            enrollments_info
        })
示例#42
0
def _get_course(course_key, user):
    """
    Get the course descriptor, raising CourseNotFoundError if the course is not found or
    the user cannot access forums for the course, and DiscussionDisabledError if the
    discussion tab is disabled for the course.
    """
    try:
        course = get_course_with_access(user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=True)
    except Http404:
        # Convert 404s into CourseNotFoundErrors.
        raise CourseNotFoundError("Course not found.")
    except CourseAccessRedirect:
        # Raise course not found if the user cannot access the course
        # since it doesn't make sense to redirect an API.
        raise CourseNotFoundError("Course not found.")
    if not any([
            tab.type == 'discussion' and tab.is_enabled(course, user)
            for tab in course.tabs
    ]):
        raise DiscussionDisabledError("Discussion is disabled for the course.")
    return course
示例#43
0
def static_tab(request, course_id, tab_slug):
    """
    Display the courses tab with the given name.

    Assumes the course_id is in a valid format.
    """
    course = get_course_with_access(request.user, course_id, 'load')

    tab = tabs.get_static_tab_by_slug(course, tab_slug)
    if tab is None:
        raise Http404

    contents = tabs.get_static_tab_contents(request, course, tab)
    if contents is None:
        raise Http404

    staff_access = has_access(request.user, course, 'staff')
    return render_to_response(
        'courseware/static_tab.html', {
            'course': course,
            'tab': tab,
            'tab_contents': contents,
            'staff_access': staff_access,
        })
示例#44
0
def mktg_course_about(request, course_id):
    """
    This is the button that gets put into an iframe on the Drupal site
    """

    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    try:
        course = get_course_with_access(request.user, 'see_exists', course_key)
    except (ValueError, Http404) as e:
        # if a course does not exist yet, display a coming
        # soon button
        return render_to_response(
            'courseware/mktg_coming_soon.html', {'course_id': course_key.to_deprecated_string()}
        )

    registered = registered_for_course(course, request.user)

    if has_access(request.user, 'load', course):
        course_target = reverse('info', args=[course.id.to_deprecated_string()])
    else:
        course_target = reverse('about_course', args=[course.id.to_deprecated_string()])

    allow_registration = has_access(request.user, 'enroll', course)

    show_courseware_link = (has_access(request.user, 'load', course) or
                            settings.FEATURES.get('ENABLE_LMS_MIGRATION'))
    course_modes = CourseMode.modes_for_course(course.id)

    return render_to_response('courseware/mktg_course_about.html', {
        'course': course,
        'registered': registered,
        'allow_registration': allow_registration,
        'course_target': course_target,
        'show_courseware_link': show_courseware_link,
        'course_modes': course_modes,
    })
示例#45
0
def static_tab(request, course_id, tab_slug):
    """
    Display the courses tab with the given name.

    Assumes the course_id is in a valid format.
    """

    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    course = get_course_with_access(request.user, 'load', course_key)

    tab = CourseTabList.get_tab_by_slug(course.tabs, tab_slug)
    if tab is None:
        raise Http404

    contents = get_static_tab_contents(request, course, tab)
    if contents is None:
        raise Http404

    return render_to_response('courseware/static_tab.html', {
        'course': course,
        'tab': tab,
        'tab_contents': contents,
    })
示例#46
0
def edxnotes_visibility(request, course_id):
    """
    Handle ajax call from "Show notes" checkbox.
    """
    course_key = CourseKey.from_string(course_id)
    course = get_course_with_access(request.user, "load", course_key)
    field_data_cache = FieldDataCache([course], course_key, request.user)
    course_module = get_module_for_descriptor(
        request.user, request, course, field_data_cache, course_key, course=course
    )

    if not is_feature_enabled(course):
        raise Http404

    try:
        visibility = json.loads(request.body)["visibility"]
        course_module.edxnotes_visibility = visibility
        course_module.save()
        return JsonResponse(status=200)
    except (ValueError, KeyError):
        log.warning(
            "Could not decode request body as JSON and find a boolean visibility field: '%s'", request.body
        )
        return JsonResponseBadRequest()
def calendar_dashboard(request, course_id):
    course_key = CourseKey.from_string(course_id)
    course = get_course_with_access(request.user, "load", course_key)
    add_lookup(
        'main',
        os.path.join(os.path.dirname(os.path.dirname(__file__)),
                     'calendar_tab/templates'))
    csrf_token = csrf(request)['csrf_token']
    tab = CourseTabList.get_tab_by_id(course.tabs, "calendar_tab")
    is_staff = bool(has_access(request.user, 'staff', course))

    try:
        data = json.loads(tab.data)
    except (TypeError, ValueError):
        data = {}

    context = {
        "course": course,
        "csrf_token": csrf_token,
        'url': data.get('url', '#'),
        'message': data.get('message', _('Open calendar')),
        'is_staff': is_staff
    }
    return render_to_response("calendar_tab/calendar_tab.html", context)
示例#48
0
    def render_to_fragment(self, request, course_id=None, **kwargs):
        """
        Renders the user's course bookmarks as a fragment.
        """
        course_key = CourseKey.from_string(course_id)
        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=True)

        context = {
            'csrf': csrf(request)['csrf_token'],
            'course': course,
            'bookmarks_api_url': reverse('bookmarks'),
            'language_preference': 'en',  # TODO:
        }
        html = render_to_string(
            'course_bookmarks/course-bookmarks-fragment.html', context)
        inline_js = render_to_string(
            'course_bookmarks/course_bookmarks_js.template', context)
        fragment = Fragment(html)
        self.add_fragment_resource_urls(fragment)
        fragment.add_javascript(inline_js)
        return fragment
示例#49
0
def course_info(request, course_id):
    """
    Display the course's info.html, or 404 if there is no such course.

    Assumes the course_id is in a valid format.
    """
    course = get_course_with_access(request.user, course_id, 'load')
    staff_access = has_access(request.user, course, 'staff')
    masq = setup_masquerade(request, staff_access)    # allow staff to toggle masquerade on info page
    studio_url = get_studio_url(course_id, 'course_info')
    reverifications = fetch_reverify_banner_info(request, course_id)

    context = {
        'request': request,
        'course_id': course_id,
        'cache': None,
        'course': course,
        'staff_access': staff_access,
        'masquerade': masq,
        'studio_url': studio_url,
        'reverifications': reverifications,
    }

    return render_to_response('courseware/info.html', context)
示例#50
0
    def render_to_fragment(self, request, course_id=None, **kwargs):
        """
        Renders the course's home page as a fragment.
        """
        course_key = CourseKey.from_string(course_id)
        course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)

        # Render the outline as a fragment
        outline_fragment = CourseOutlineFragmentView().render_to_fragment(request, course_id=course_id, **kwargs)

        # Get the last accessed courseware
        last_accessed_url, __ = get_last_accessed_courseware(course, request, request.user)

        # Render the course home fragment
        context = {
            'csrf': csrf(request)['csrf_token'],
            'course': course,
            'outline_fragment': outline_fragment,
            'has_visited_course': last_accessed_url is not None,
            'disable_courseware_js': True,
            'uses_pattern_library': True,
        }
        html = render_to_string('course_experience/course-home-fragment.html', context)
        return Fragment(html)
示例#51
0
def course_survey(request, course_id):
    """
    URL endpoint to present a survey that is associated with a course_id
    Note that the actual implementation of course survey is handled in the
    views.py file in the Survey Djangoapp
    """

    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(request.user, 'load', course_key)

    redirect_url = reverse('info', args=[course_id])

    # if there is no Survey associated with this course,
    # then redirect to the course instead
    if not course.course_survey_name:
        return redirect(redirect_url)

    return survey.views.view_student_survey(
        request.user,
        course.course_survey_name,
        course=course,
        redirect_url=redirect_url,
        is_required=course.course_survey_required,
    )
示例#52
0
def mktg_course_about(request, course_id):
    """This is the button that gets put into an iframe on the Drupal site."""
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    try:
        permission_name = microsite.get_value(
            'COURSE_ABOUT_VISIBILITY_PERMISSION',
            settings.COURSE_ABOUT_VISIBILITY_PERMISSION
        )
        course = get_course_with_access(request.user, permission_name, course_key)
    except (ValueError, Http404):
        # If a course does not exist yet, display a "Coming Soon" button
        return render_to_response(
            'courseware/mktg_coming_soon.html', {'course_id': course_key.to_deprecated_string()}
        )

    registered = registered_for_course(course, request.user)

    if has_access(request.user, 'load', course):
        course_target = reverse('info', args=[course.id.to_deprecated_string()])
    else:
        course_target = reverse('about_course', args=[course.id.to_deprecated_string()])

    allow_registration = has_access(request.user, 'enroll', course)

    show_courseware_link = (has_access(request.user, 'load', course) or
                            settings.FEATURES.get('ENABLE_LMS_MIGRATION'))
    course_modes = CourseMode.modes_for_course_dict(course.id)

    context = {
        'course': course,
        'registered': registered,
        'allow_registration': allow_registration,
        'course_target': course_target,
        'show_courseware_link': show_courseware_link,
        'course_modes': course_modes,
    }

    if settings.FEATURES.get('ENABLE_MKTG_EMAIL_OPT_IN'):
        # Drupal will pass organization names using a GET parameter, as follows:
        #     ?org=Harvard
        #     ?org=Harvard,MIT
        # If no full names are provided, the marketing iframe won't show the
        # email opt-in checkbox.
        org = request.GET.get('org')
        if org:
            org_list = org.split(',')
            # HTML-escape the provided organization names
            org_list = [cgi.escape(org) for org in org_list]
            if len(org_list) > 1:
                if len(org_list) > 2:
                    # Translators: The join of three or more institution names (e.g., Harvard, MIT, and Dartmouth).
                    org_name_string = _("{first_institutions}, and {last_institution}").format(
                        first_institutions=u", ".join(org_list[:-1]),
                        last_institution=org_list[-1]
                    )
                else:
                    # Translators: The join of two institution names (e.g., Harvard and MIT).
                    org_name_string = _("{first_institution} and {second_institution}").format(
                        first_institution=org_list[0],
                        second_institution=org_list[1]
                    )
            else:
                org_name_string = org_list[0]

            context['checkbox_label'] = ungettext(
                "I would like to receive email from {institution_series} and learn about its other programs.",
                "I would like to receive email from {institution_series} and learn about their other programs.",
                len(org_list)
            ).format(institution_series=org_name_string)

    # The edx.org marketing site currently displays only in English.
    # To avoid displaying a different language in the register / access button,
    # we force the language to English.
    # However, OpenEdX installations with a different marketing front-end
    # may want to respect the language specified by the user or the site settings.
    force_english = settings.FEATURES.get('IS_EDX_DOMAIN', False)
    if force_english:
        translation.activate('en-us')

    try:
        return render_to_response('courseware/mktg_course_about.html', context)
    finally:
        # Just to be safe, reset the language if we forced it to be English.
        if force_english:
            translation.deactivate()
示例#53
0
def course_about(request, course_id):
    """
    Display the course's about page.

    Assumes the course_id is in a valid format.
    """

    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    with modulestore().bulk_operations(course_key):
        permission_name = microsite.get_value(
            'COURSE_ABOUT_VISIBILITY_PERMISSION',
            settings.COURSE_ABOUT_VISIBILITY_PERMISSION
        )
        course = get_course_with_access(request.user, permission_name, course_key)

        if microsite.get_value('ENABLE_MKTG_SITE', settings.FEATURES.get('ENABLE_MKTG_SITE', False)):
            return redirect(reverse('info', args=[course.id.to_deprecated_string()]))

        registered = registered_for_course(course, request.user)

        staff_access = has_access(request.user, 'staff', course)
        studio_url = get_studio_url(course, 'settings/details')

        if has_access(request.user, 'load', course):
            course_target = reverse('info', args=[course.id.to_deprecated_string()])
        else:
            course_target = reverse('about_course', args=[course.id.to_deprecated_string()])

        show_courseware_link = (
            (
                has_access(request.user, 'load', course)
                and has_access(request.user, 'view_courseware_with_prerequisites', course)
            )
            or settings.FEATURES.get('ENABLE_LMS_MIGRATION')
        )

        # Note: this is a flow for payment for course registration, not the Verified Certificate flow.
        registration_price = 0
        in_cart = False
        reg_then_add_to_cart_link = ""

        _is_shopping_cart_enabled = is_shopping_cart_enabled()
        if _is_shopping_cart_enabled:
            registration_price = CourseMode.min_course_price_for_currency(course_key,
                                                                          settings.PAID_COURSE_REGISTRATION_CURRENCY[0])
            if request.user.is_authenticated():
                cart = shoppingcart.models.Order.get_cart_for_user(request.user)
                in_cart = shoppingcart.models.PaidCourseRegistration.contained_in_order(cart, course_key) or \
                    shoppingcart.models.CourseRegCodeItem.contained_in_order(cart, course_key)

            reg_then_add_to_cart_link = "{reg_url}?course_id={course_id}&enrollment_action=add_to_cart".format(
                reg_url=reverse('register_user'), course_id=course.id.to_deprecated_string())

        course_price = get_cosmetic_display_price(course, registration_price)
        can_add_course_to_cart = _is_shopping_cart_enabled and registration_price

        # Used to provide context to message to student if enrollment not allowed
        can_enroll = has_access(request.user, 'enroll', course)
        invitation_only = course.invitation_only
        is_course_full = CourseEnrollment.is_course_full(course)

        # Register button should be disabled if one of the following is true:
        # - Student is already registered for course
        # - Course is already full
        # - Student cannot enroll in course
        active_reg_button = not(registered or is_course_full or not can_enroll)

        is_shib_course = uses_shib(course)

        # get prerequisite courses display names
        pre_requisite_courses = get_prerequisite_courses_display(course)

        return render_to_response('courseware/course_about.html', {
            'course': course,
            'staff_access': staff_access,
            'studio_url': studio_url,
            'registered': registered,
            'course_target': course_target,
            'is_cosmetic_price_enabled': settings.FEATURES.get('ENABLE_COSMETIC_DISPLAY_PRICE'),
            'course_price': course_price,
            'in_cart': in_cart,
            'reg_then_add_to_cart_link': reg_then_add_to_cart_link,
            'show_courseware_link': show_courseware_link,
            'is_course_full': is_course_full,
            'can_enroll': can_enroll,
            'invitation_only': invitation_only,
            'active_reg_button': active_reg_button,
            'is_shib_course': is_shib_course,
            # We do not want to display the internal courseware header, which is used when the course is found in the
            # context. This value is therefor explicitly set to render the appropriate header.
            'disable_courseware_header': True,
            'can_add_course_to_cart': can_add_course_to_cart,
            'cart_link': reverse('shoppingcart.views.show_cart'),
            'pre_requisite_courses': pre_requisite_courses
        })
示例#54
0
def _index_bulk_op(request, course_key, chapter, section, position):
    """
    Render the index page for the specified course.
    """
    user = request.user
    course = get_course_with_access(user, 'load', course_key, depth=2)

    staff_access = has_access(user, 'staff', course)
    registered = registered_for_course(course, user)
    if not registered:
        # TODO (vshnayder): do course instructors need to be registered to see course?
        log.debug(u'User %s tried to view course %s but is not enrolled', user, course.location.to_deprecated_string())
        return redirect(reverse('about_course', args=[course_key.to_deprecated_string()]))

    # see if all pre-requisites (as per the milestones app feature) have been fulfilled
    # Note that if the pre-requisite feature flag has been turned off (default) then this check will
    # always pass
    if not has_access(user, 'view_courseware_with_prerequisites', course):
        # prerequisites have not been fulfilled therefore redirect to the Dashboard
        log.info(
            u'User %d tried to view course %s '
            u'without fulfilling prerequisites',
            user.id, unicode(course.id))
        return redirect(reverse('dashboard'))

    # check to see if there is a required survey that must be taken before
    # the user can access the course.
    if survey.utils.must_answer_survey(course, user):
        return redirect(reverse('course_survey', args=[unicode(course.id)]))

    masquerade = setup_masquerade(request, course_key, staff_access)

    try:
        field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
            course_key, user, course, depth=2)

        course_module = get_module_for_descriptor(user, request, course, field_data_cache, course_key)
        if course_module is None:
            log.warning(u'If you see this, something went wrong: if we got this'
                        u' far, should have gotten a course module for this user')
            return redirect(reverse('about_course', args=[course_key.to_deprecated_string()]))

        studio_url = get_studio_url(course, 'course')

        context = {
            'csrf': csrf(request)['csrf_token'],
            'accordion': render_accordion(request, course, chapter, section, field_data_cache),
            'COURSE_TITLE': course.display_name_with_default,
            'course': course,
            'init': '',
            'fragment': Fragment(),
            'staff_access': staff_access,
            'studio_url': studio_url,
            'masquerade': masquerade,
            'xqa_server': settings.FEATURES.get('USE_XQA_SERVER', 'http://*****:*****@content-qa.mitx.mit.edu/xqa'),
            'reverifications': fetch_reverify_banner_info(request, course_key),
        }

        now = datetime.now(UTC())
        effective_start = _adjust_start_date_for_beta_testers(user, course, course_key)
        if staff_access and now < effective_start:
            # Disable student view button if user is staff and
            # course is not yet visible to students.
            context['disable_student_access'] = True

        has_content = course.has_children_at_depth(CONTENT_DEPTH)
        if not has_content:
            # Show empty courseware for a course with no units
            return render_to_response('courseware/courseware.html', context)
        elif chapter is None:
            # passing CONTENT_DEPTH avoids returning 404 for a course with an
            # empty first section and a second section with content
            return redirect_to_course_position(course_module, CONTENT_DEPTH)

        # Only show the chat if it's enabled by the course and in the
        # settings.
        show_chat = course.show_chat and settings.FEATURES['ENABLE_CHAT']
        if show_chat:
            context['chat'] = chat_settings(course, user)
            # If we couldn't load the chat settings, then don't show
            # the widget in the courseware.
            if context['chat'] is None:
                show_chat = False

        context['show_chat'] = show_chat

        chapter_descriptor = course.get_child_by(lambda m: m.location.name == chapter)
        if chapter_descriptor is not None:
            save_child_position(course_module, chapter)
        else:
            raise Http404('No chapter descriptor found with name {}'.format(chapter))

        chapter_module = course_module.get_child_by(lambda m: m.location.name == chapter)
        if chapter_module is None:
            # User may be trying to access a chapter that isn't live yet
            if masquerade and masquerade.role == 'student':  # if staff is masquerading as student be kinder, don't 404
                log.debug('staff masquerading as student: no chapter %s', chapter)
                return redirect(reverse('courseware', args=[course.id.to_deprecated_string()]))
            raise Http404

        if section is not None:
            section_descriptor = chapter_descriptor.get_child_by(lambda m: m.location.name == section)

            if section_descriptor is None:
                # Specifically asked-for section doesn't exist
                if masquerade and masquerade.role == 'student':  # don't 404 if staff is masquerading as student
                    log.debug('staff masquerading as student: no section %s', section)
                    return redirect(reverse('courseware', args=[course.id.to_deprecated_string()]))
                raise Http404

            ## Allow chromeless operation
            if section_descriptor.chrome:
                chrome = [s.strip() for s in section_descriptor.chrome.lower().split(",")]
                if 'accordion' not in chrome:
                    context['disable_accordion'] = True
                if 'tabs' not in chrome:
                    context['disable_tabs'] = True

            if section_descriptor.default_tab:
                context['default_tab'] = section_descriptor.default_tab

            # cdodge: this looks silly, but let's refetch the section_descriptor with depth=None
            # which will prefetch the children more efficiently than doing a recursive load
            section_descriptor = modulestore().get_item(section_descriptor.location, depth=None)

            # Load all descendants of the section, because we're going to display its
            # html, which in general will need all of its children
            section_field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
                course_key, user, section_descriptor, depth=None, asides=XBlockAsidesConfig.possible_asides()
            )

            # Verify that position a string is in fact an int
            if position is not None:
                try:
                    int(position)
                except ValueError:
                    raise Http404("Position {} is not an integer!".format(position))

            section_module = get_module_for_descriptor(
                request.user,
                request,
                section_descriptor,
                section_field_data_cache,
                course_key,
                position
            )

            if section_module is None:
                # User may be trying to be clever and access something
                # they don't have access to.
                raise Http404

            # Save where we are in the chapter
            save_child_position(chapter_module, section)
            context['fragment'] = section_module.render(STUDENT_VIEW)
            context['section_title'] = section_descriptor.display_name_with_default
        else:
            # section is none, so display a message
            studio_url = get_studio_url(course, 'course')
            prev_section = get_current_child(chapter_module)
            if prev_section is None:
                # Something went wrong -- perhaps this chapter has no sections visible to the user.
                # Clearing out the last-visited state and showing "first-time" view by redirecting
                # to courseware.
                course_module.position = None
                course_module.save()
                return redirect(reverse('courseware', args=[course.id.to_deprecated_string()]))
            prev_section_url = reverse('courseware_section', kwargs={
                'course_id': course_key.to_deprecated_string(),
                'chapter': chapter_descriptor.url_name,
                'section': prev_section.url_name
            })
            context['fragment'] = Fragment(content=render_to_string(
                'courseware/welcome-back.html',
                {
                    'course': course,
                    'studio_url': studio_url,
                    'chapter_module': chapter_module,
                    'prev_section': prev_section,
                    'prev_section_url': prev_section_url
                }
            ))

        result = render_to_response('courseware/courseware.html', context)
    except Exception as e:

        # Doesn't bar Unicode characters from URL, but if Unicode characters do
        # cause an error it is a graceful failure.
        if isinstance(e, UnicodeEncodeError):
            raise Http404("URL contains Unicode characters")

        if isinstance(e, Http404):
            # let it propagate
            raise

        # In production, don't want to let a 500 out for any reason
        if settings.DEBUG:
            raise
        else:
            log.exception(
                u"Error in index view: user={user}, course={course}, chapter={chapter}"
                u" section={section} position={position}".format(
                    user=user,
                    course=course,
                    chapter=chapter,
                    section=section,
                    position=position
                ))
            try:
                result = render_to_response('courseware/courseware-error.html', {
                    'staff_access': staff_access,
                    'course': course
                })
            except:
                # Let the exception propagate, relying on global config to at
                # at least return a nice error message
                log.exception("Error while rendering courseware-error page")
                raise

    return result
示例#55
0
def course_discussions_settings_handler(request, course_key_string):
    """
    The restful handler for divided discussion setting requests. Requires JSON.
    This will raise 404 if user is not staff.
    GET
        Returns the JSON representation of divided discussion settings for the course.
    PATCH
        Updates the divided discussion settings for the course. Returns the JSON representation of updated settings.
    """
    course_key = CourseKey.from_string(course_key_string)
    course = get_course_with_access(request.user, 'staff', course_key)
    discussion_settings = get_course_discussion_settings(course_key)

    if request.method == 'PATCH':
        divided_course_wide_discussions, divided_inline_discussions = get_divided_discussions(
            course, discussion_settings)

        settings_to_change = {}

        if 'divided_course_wide_discussions' in request.json or 'divided_inline_discussions' in request.json:
            divided_course_wide_discussions = request.json.get(
                'divided_course_wide_discussions',
                divided_course_wide_discussions)
            divided_inline_discussions = request.json.get(
                'divided_inline_discussions', divided_inline_discussions)
            settings_to_change[
                'divided_discussions'] = divided_course_wide_discussions + divided_inline_discussions

        if 'always_divide_inline_discussions' in request.json:
            settings_to_change[
                'always_divide_inline_discussions'] = request.json.get(
                    'always_divide_inline_discussions')
        if 'division_scheme' in request.json:
            settings_to_change['division_scheme'] = request.json.get(
                'division_scheme')

        if not settings_to_change:
            return JsonResponse({"error": unicode("Bad Request")}, 400)

        try:
            if settings_to_change:
                discussion_settings = set_course_discussion_settings(
                    course_key, **settings_to_change)

        except ValueError as err:
            # Note: error message not translated because it is not exposed to the user (UI prevents this state).
            return JsonResponse({"error": unicode(err)}, 400)

    divided_course_wide_discussions, divided_inline_discussions = get_divided_discussions(
        course, discussion_settings)

    return JsonResponse({
        'id':
        discussion_settings.id,
        'divided_inline_discussions':
        divided_inline_discussions,
        'divided_course_wide_discussions':
        divided_course_wide_discussions,
        'always_divide_inline_discussions':
        discussion_settings.always_divide_inline_discussions,
        'division_scheme':
        discussion_settings.division_scheme,
        'available_division_schemes':
        available_division_schemes(course_key)
    })
示例#56
0
def discussion_topics(request, course_key_string):
    """
    The handler for divided discussion categories requests.
    This will raise 404 if user is not staff.

    Returns the JSON representation of discussion topics w.r.t categories for the course.

    Example:
        >>> example = {
        >>>               "course_wide_discussions": {
        >>>                   "entries": {
        >>>                       "General": {
        >>>                           "sort_key": "General",
        >>>                           "is_divided": True,
        >>>                           "id": "i4x-edx-eiorguegnru-course-foobarbaz"
        >>>                       }
        >>>                   }
        >>>                   "children": ["General", "entry"]
        >>>               },
        >>>               "inline_discussions" : {
        >>>                   "subcategories": {
        >>>                       "Getting Started": {
        >>>                           "subcategories": {},
        >>>                           "children": [
        >>>                               ["Working with Videos", "entry"],
        >>>                               ["Videos on edX", "entry"]
        >>>                           ],
        >>>                           "entries": {
        >>>                               "Working with Videos": {
        >>>                                   "sort_key": None,
        >>>                                   "is_divided": False,
        >>>                                   "id": "d9f970a42067413cbb633f81cfb12604"
        >>>                               },
        >>>                               "Videos on edX": {
        >>>                                   "sort_key": None,
        >>>                                   "is_divided": False,
        >>>                                   "id": "98d8feb5971041a085512ae22b398613"
        >>>                               }
        >>>                           }
        >>>                       },
        >>>                       "children": ["Getting Started", "subcategory"]
        >>>                   },
        >>>               }
        >>>          }
    """
    course_key = CourseKey.from_string(course_key_string)
    course = get_course_with_access(request.user, 'staff', course_key)

    discussion_topics = {}
    discussion_category_map = utils.get_discussion_category_map(
        course,
        request.user,
        divided_only_if_explicit=True,
        exclude_unstarted=False)

    # We extract the data for the course wide discussions from the category map.
    course_wide_entries = discussion_category_map.pop('entries')

    course_wide_children = []
    inline_children = []

    for name, c_type in discussion_category_map['children']:
        if name in course_wide_entries and c_type == TYPE_ENTRY:
            course_wide_children.append([name, c_type])
        else:
            inline_children.append([name, c_type])

    discussion_topics['course_wide_discussions'] = {
        'entries': course_wide_entries,
        'children': course_wide_children
    }

    discussion_category_map['children'] = inline_children
    discussion_topics['inline_discussions'] = discussion_category_map

    return JsonResponse(discussion_topics)
示例#57
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_forum',
                                    course_key,
                                    check_if_enrolled=True)
    try:
        profiled_user = cc.User(id=user_id, course_id=course_key)

        default_query_params = {
            'page': 1,
            'per_page':
            THREADS_PER_PAGE,  # more than threads_per_page to show more activities
            'sort_key': 'date',
        }

        query_params = merge_dict(
            default_query_params,
            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 newrelic_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
示例#58
0
def user_profile(request, course_key, user_id):
    """
    Renders a response to display the user profile page (shown after clicking
    on a post author's username).
    """
    user = cc.User.from_django_user(request.user)
    course = get_course_with_access(request.user,
                                    'load_forum',
                                    course_key,
                                    check_if_enrolled=True)

    try:
        # 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
        }

        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
            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 newrelic_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 newrelic_function_trace("add_courseware_context"):
            add_courseware_context(threads, course, request.user)
        if request.is_ajax():
            return utils.JsonResponse({
                'discussion_data':
                threads,
                'page':
                query_params['page'],
                'num_pages':
                query_params['num_pages'],
                'annotated_content_info':
                annotated_content_info,
            })
        else:
            user_roles = django_user.roles.filter(
                course_id=course.id).order_by("name").values_list(
                    "name", flat=True).distinct()

            with newrelic_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 render_to_response(
                'discussion/discussion_profile_page.html', context)
    except User.DoesNotExist:
        raise Http404
示例#59
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
示例#60
0
def create_thread(request, course_id, commentable_id):
    """
    Given a course and commentble ID, create the thread
    """

    log.debug("Creating new thread in %r, id %r", course_id, commentable_id)
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(request.user, 'load', course_key)
    post = request.POST
    user = request.user

    if course.allow_anonymous:
        anonymous = post.get('anonymous', 'false').lower() == 'true'
    else:
        anonymous = False

    if course.allow_anonymous_to_peers:
        anonymous_to_peers = post.get('anonymous_to_peers',
                                      'false').lower() == 'true'
    else:
        anonymous_to_peers = False

    if 'title' not in post or not post['title'].strip():
        return JsonError(_("Title can't be empty"))
    if 'body' not in post or not post['body'].strip():
        return JsonError(_("Body can't be empty"))

    params = {
        'anonymous': anonymous,
        'anonymous_to_peers': anonymous_to_peers,
        'commentable_id': commentable_id,
        'course_id': course_key.to_deprecated_string(),
        'user_id': user.id,
        'thread_type': post["thread_type"],
        'body': post["body"],
        'title': post["title"],
    }

    # Check for whether this commentable belongs to a team, and add the right context
    if get_team(commentable_id) is not None:
        params['context'] = ThreadContext.STANDALONE
    else:
        params['context'] = ThreadContext.COURSE

    thread = cc.Thread(**params)

    # Cohort the thread if required
    try:
        group_id = get_group_id_for_comments_service(request, course_key,
                                                     commentable_id)
    except ValueError:
        return HttpResponseBadRequest("Invalid cohort id")
    if group_id is not None:
        thread.group_id = group_id

    thread.save()

    thread_created.send(sender=None, user=user, post=thread)

    # patch for backward compatibility to comments service
    if 'pinned' not in thread.attributes:
        thread['pinned'] = False

    follow = post.get('auto_subscribe', 'false').lower() == 'true'

    if follow:
        cc_user = cc.User.from_django_user(user)
        cc_user.follow(thread)

    event_data = get_thread_created_event_data(thread, follow)
    data = thread.to_dict()

    add_courseware_context([data], course, user)

    track_forum_event(request, THREAD_CREATED_EVENT_NAME, course, thread,
                      event_data)

    if request.is_ajax():
        return ajax_content_response(request, course_key, data)
    else:
        return JsonResponse(prepare_content(data, course_key))