Esempio n. 1
0
def get_course_info(course):
    course_info = {}
    course_info['course_id'] = course.course_id
    course_info['name'] = course.name
    course_info['thumbnail'] = course.thumbnail
    course_info['course_num'] = course.course_num
    course_info['modified'] = naturalFormatDate(course.modified)
    course_info['enrollment'] = course_stat.stat.enrollment_total_count(
        SlashSeparatedCourseKey.from_deprecated_string(course.course_id))
    course_info['comment'] = course_stat.stat.comment_total_count(
        SlashSeparatedCourseKey.from_deprecated_string(course.course_id))
    course_info['subtitle'] = course.subtitle
    course_info['about'] = reverse('about_course', args=[course.course_id])
    course_info['start_time'] = time_format(course.start)
    course_info['end_time'] = time_format(course.end)

    try:
        staff = course.staff.all()[0]
        course_info['staff_avatar'] = staff.avartar
        course_info['staff_name'] = staff.name
        course_info['staff_title'] = "%s %s %s" % (
            staff.company, staff.department, staff.position)
    except:
        course_info['staff_avatar'] = ''
        course_info['staff_name'] = ''
        course_info['staff_title'] = ''

    return course_info
Esempio n. 2
0
 def _decorated(request, course_id, *args, **kwargs):
     try:
         SlashSeparatedCourseKey.from_deprecated_string(course_id)
     except InvalidKeyError:
         raise Http404
     response = view_func(request, course_id, *args, **kwargs)
     return response
    def post(self, request, course_id, format=None):
        """ Enroll in Course """
        user = request.user
        err = {}
        try:
            course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

            course = course_from_key(course_key)
        except ItemNotFoundError:
            err['err_type'] = 'InvalidCourseId'
            err['err_msg'] = _("Course id is invalid")
            return Response(err, status=status.HTTP_400_BAD_REQUEST)

        if not has_access(user, 'enroll', course):
            err['err_type'] = 'InvalidEnrollment'
            err['err_msg'] = _("Enrollment is closed")
            return Response(err, status=status.HTTP_400_BAD_REQUEST)

        # see if we have already filled up all allowed enrollments
        is_course_full = CourseEnrollment.is_course_full(course)

        if is_course_full:
            err['err_type'] = 'InvalidEnrollment'
            err['err_msg'] = _("Course is full")
            return Response(err, status=status.HTTP_400_BAD_REQUEST)

        # If this course is available in multiple modes, redirect them to a page
        # where they can choose which mode they want.
        available_modes = CourseMode.modes_for_course(course_id)
        available_modes_dict = CourseMode.modes_for_course_dict(course_id, available_modes)
        if CourseMode.has_verified_mode(available_modes_dict):
            err['err_type'] = 'InvalidEnrollment'
            err['err_msg'] = _("Missing course mode")
            return Response(err, status=status.HTTP_400_BAD_REQUEST)

        current_mode = available_modes[0]
        course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
        dog_stats_api.increment(
            "common.student.enrollment",
            tags=[u"org:{0}".format(course_key.org),
                  u"course:{0}".format(course_key.course),
                  u"run:{0}".format(course_key.run)]
        )
        server_track(request, 'api.course.enrollment', {
            'username': user.username,
            'course_id': course_id,
        })

        CourseEnrollment.enroll(user, course.id, mode=current_mode.slug)
        return Response()
Esempio n. 4
0
    def handle(self, *args, **options):
        if not options['course']:
            raise CommandError(Command.course_option.help)

        try:
            course_key = CourseKey.from_string(options['course'])
        except InvalidKeyError:
            course_key = SlashSeparatedCourseKey.from_deprecated_string(options['course'])

        course = get_course_by_id(course_key)

        print 'Warning: this command directly edits the list of course tabs in mongo.'
        print 'Tabs before any changes:'
        print_course(course)

        try:
            if options['delete']:
                if len(args) != 1:
                    raise CommandError(Command.delete_option.help)
                num = int(args[0])
                if query_yes_no('Deleting tab {0} Confirm?'.format(num), default='no'):
                    primitive_delete(course, num - 1)  # -1 for 0-based indexing
            elif options['insert']:
                if len(args) != 3:
                    raise CommandError(Command.insert_option.help)
                num = int(args[0])
                tab_type = args[1]
                name = args[2]
                if query_yes_no('Inserting tab {0} "{1}" "{2}" Confirm?'.format(num, tab_type, name), default='no'):
                    primitive_insert(course, num - 1, tab_type, name)  # -1 as above
        except ValueError as e:
            # Cute: translate to CommandError so the CLI error prints nicely.
            raise CommandError(e)
Esempio n. 5
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,
        },
    )
Esempio n. 6
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
        }
    })
Esempio n. 7
0
def request_certificate(request):
    """Request the on-demand creation of a certificate for some user, course.

    A request doesn't imply a guarantee that such a creation will take place.
    We intentionally use the same machinery as is used for doing certification
    at the end of a course run, so that we can be sure users get graded and
    then if and only if they pass, do they get a certificate issued.
    """
    if request.method == "POST":
        if request.user.is_authenticated():
            xqci = XQueueCertInterface()
            username = request.user.username
            student = User.objects.get(username=username)
            course_key = SlashSeparatedCourseKey.from_deprecated_string(request.POST.get("course_id"))
            course = modulestore().get_course(course_key, depth=2)

            status = certificate_status_for_student(student, course_key)["status"]
            if status in [CertificateStatuses.unavailable, CertificateStatuses.notpassing, CertificateStatuses.error]:
                logger.info(
                    "Grading and certification requested for user {} in course {} via /request_certificate call".format(
                        username, course_key
                    )
                )
                status = xqci.add_cert(student, course_key, course=course)
            return HttpResponse(json.dumps({"add_status": status}), mimetype="application/json")
        return HttpResponse(json.dumps({"add_status": "ERRORANONYMOUSUSER"}), mimetype="application/json")
def set_course_mode_price(request, course_id):
    """
    set the new course price and add new entry in the CourseModesArchive Table
    """
    try:
        course_price = int(request.POST['course_price'])
    except ValueError:
        return JsonResponse(
            {'message': _("Please Enter the numeric value for the course price")},
            status=400)  # status code 400: Bad Request

    currency = request.POST['currency']
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    course_honor_mode = CourseMode.objects.filter(mode_slug='honor', course_id=course_key)
    if not course_honor_mode:
        return JsonResponse(
            {'message': _("CourseMode with the mode slug({mode_slug}) DoesNotExist").format(mode_slug='honor')},
            status=400)  # status code 400: Bad Request

    CourseModesArchive.objects.create(
        course_id=course_id, mode_slug='honor', mode_display_name='Honor Code Certificate',
        min_price=course_honor_mode[0].min_price, currency=course_honor_mode[0].currency,
        expiration_datetime=datetime.datetime.now(pytz.utc), expiration_date=datetime.date.today()
    )
    course_honor_mode.update(
        min_price=course_price,
        currency=currency
    )
    return JsonResponse({'message': _("CourseMode price updated successfully")})
Esempio n. 9
0
def get_anon_ids(request, course_id):  # pylint: disable=W0613
    """
    Respond with 2-column CSV output of user-id, anonymized-user-id
    """
    # TODO: the User.objects query and CSV generation here could be
    # centralized into instructor_analytics. Currently instructor_analytics 
    # has similar functionality but not quite what's needed.
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    def csv_response(filename, header, rows):
        """Returns a CSV http response for the given header and rows (excel/utf-8)."""
        response = HttpResponse(mimetype='text/csv')
        response['Content-Disposition'] = 'attachment; filename={0}'.format(unicode(filename).encode('utf-8'))
        writer = csv.writer(response, dialect='excel', quotechar='"', quoting=csv.QUOTE_ALL)
        # In practice, there should not be non-ascii data in this query,
        # but trying to do the right thing anyway.
        encoded = [unicode(s).encode('utf-8') for s in header]
        writer.writerow(encoded)
        for row in rows:
            encoded = [unicode(s).encode('utf-8') for s in row]
            writer.writerow(encoded)
        return response

    students = User.objects.filter(
        courseenrollment__course_id=course_id,
    ).order_by('id')
    header = ['User ID', 'Anonymized User ID', 'Course Specific Anonymized User ID']
    rows = [[s.id, unique_id_for_user(s, save=False), anonymous_id_for_user(s, course_id, save=False)] for s in students]
    return csv_response(course_id.to_deprecated_string().replace('/', '-') + '-anon-ids.csv', header, rows)
Esempio n. 10
0
    def get(self, request, course_id):
        """
        Handle the case where we have a get request
        """
        upgrade = request.GET.get('upgrade', False)
        course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
        if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == 'verified':
            return redirect(reverse('dashboard'))
        verify_mode = CourseMode.mode_for_course(course_id, "verified")

        if verify_mode is None:
            return redirect(reverse('dashboard'))

        chosen_price = request.session.get(
            "donation_for_course",
            {}
        ).get(
            course_id.to_deprecated_string(),
            verify_mode.min_price
        )

        course = modulestore().get_course(course_id)
        context = {
            "course_id": course_id.to_deprecated_string(),
            "course_modes_choose_url": reverse('course_modes_choose', kwargs={'course_id': course_id.to_deprecated_string()}),
            "course_name": course.display_name_with_default,
            "course_org": course.display_org_with_default,
            "course_num": course.display_number_with_default,
            "purchase_endpoint": get_purchase_endpoint(),
            "currency": verify_mode.currency.upper(),
            "chosen_price": chosen_price,
            "create_order_url": reverse("verify_student_create_order"),
            "upgrade": upgrade,
        }
        return render_to_response('verify_student/verified.html', context)
Esempio n. 11
0
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})
Esempio n. 12
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)
Esempio n. 13
0
def progress(request, course_id, student_id=None):
    """
    Wraps "_progress" with the manual_transaction context manager just in case
    there are unanticipated errors.
    """
    with grades.manual_transaction():
        return _progress(request, SlashSeparatedCourseKey.from_deprecated_string(course_id), student_id)
Esempio n. 14
0
def show_unit_extensions(request, course_id):
    """
    Shows all of the students which have due date extensions for the given unit.
    """
    course = get_course_by_id(SlashSeparatedCourseKey.from_deprecated_string(course_id))
    unit = find_unit(course, request.GET.get('url'))
    return JsonResponse(dump_module_extensions(course, unit))
Esempio n. 15
0
    def clean_course_id(self):
        """Validate the course id"""
        cleaned_id = self.cleaned_data["course_id"]
        try:
            course_key = CourseKey.from_string(cleaned_id)
        except InvalidKeyError:
            try:
                course_key = SlashSeparatedCourseKey.from_deprecated_string(cleaned_id)
            except InvalidKeyError:
                msg = u'Course id invalid.'
                msg += u' --- Entered course id was: "{0}". '.format(cleaned_id)
                msg += 'Please recheck that you have supplied a valid course id.'
                raise forms.ValidationError(msg)

        if not modulestore().has_course(course_key):
            msg = u'COURSE NOT FOUND'
            msg += u' --- Entered course id was: "{0}". '.format(course_key.to_deprecated_string())
            msg += 'Please recheck that you have supplied a valid course id.'
            raise forms.ValidationError(msg)

        # Now, try and discern if it is a Studio course - HTML editor doesn't work with XML courses
        is_studio_course = modulestore().get_modulestore_type(course_key) != ModuleStoreEnum.Type.xml
        if not is_studio_course:
            msg = "Course Email feature is only available for courses authored in Studio. "
            msg += '"{0}" appears to be an XML backed course.'.format(course_key.to_deprecated_string())
            raise forms.ValidationError(msg)

        return course_key
Esempio n. 16
0
def list_cohorts(request, course_key_string):
    """
    Return json dump of dict:

    {'success': True,
     'cohorts': [{'name': name, 'id': id}, ...]}
    """

    # this is a string when we get it here
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_key_string)

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

    all_cohorts = [
        {
            'name': c.name,
            'id': c.id,
            'user_count': c.users.count(),
            'assignment_type': cohorts.CohortAssignmentType.get(c, course)
        }
        for c in cohorts.get_course_cohorts(course)
    ]

    return json_http_response({'success': True,
                               'cohorts': all_cohorts})
Esempio n. 17
0
def all_sequential_open_distrib(request, course_id):
    """
    Creates a json with the open distribution for all the subsections in the course.

    `request` django request

    `course_id` the course ID for the course interested in

    Returns the format in dashboard_data.get_d3_sequential_open_distrib
    """

    data = {}

    # Only instructor for this particular course can request this information
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    if has_instructor_access_for_class(request.user, course_key):
        try:
            data = dashboard_data.get_d3_sequential_open_distrib(course_key)
        except Exception as ex:  # pylint: disable=broad-except
            log.error('Generating metrics failed with exception: %s', ex)
            data = {'error': "error"}
    else:
        data = {'error': "Access Denied: User does not have access to this course's data"}

    return HttpResponse(json.dumps(data), content_type="application/json")
Esempio n. 18
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'})

    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)})
Esempio n. 19
0
def get_visible_courses():
    """
    Return the set of CourseDescriptors that should be visible in this branded instance
    """

    filtered_by_org = microsite.get_value('course_org_filter')

    _courses = modulestore().get_courses(org=filtered_by_org)

    courses = [c for c in _courses
               if isinstance(c, CourseDescriptor)]
    courses = sorted(courses, key=lambda course: course.number)

    subdomain = microsite.get_value('subdomain', 'default')

    # See if we have filtered course listings in this domain
    filtered_visible_ids = None

    # this is legacy format which is outside of the microsite feature -- also handle dev case, which should not filter
    if hasattr(settings, 'COURSE_LISTINGS') and subdomain in settings.COURSE_LISTINGS and not settings.DEBUG:
        filtered_visible_ids = frozenset([SlashSeparatedCourseKey.from_deprecated_string(c) for c in settings.COURSE_LISTINGS[subdomain]])

    if filtered_by_org:
        return [course for course in courses if course.location.org == filtered_by_org]
    if filtered_visible_ids:
        return [course for course in courses if course.id in filtered_visible_ids]
    else:
        # Let's filter out any courses in an "org" that has been declared to be
        # in a Microsite
        org_filter_out_set = microsite.get_all_orgs()
        return [course for course in courses if course.location.org not in org_filter_out_set]
Esempio n. 20
0
def send_email(request, course_id):
    """
    Send an email to self, staff, or everyone involved in a course.
    Query Parameters:
    - 'send_to' specifies what group the email should be sent to
       Options are defined by the CourseEmail model in
       lms/djangoapps/bulk_email/models.py
    - 'subject' specifies email's subject
    - 'message' specifies email's content
    """
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    if not bulk_email_is_enabled_for_course(course_id):
        return HttpResponseForbidden("Email is not enabled for this course.")

    send_to = request.POST.get("send_to")
    subject = request.POST.get("subject")
    message = request.POST.get("message")

    # Create the CourseEmail object.  This is saved immediately, so that
    # any transaction that has been pending up to this point will also be
    # committed.
    email = CourseEmail.create(course_id, request.user, send_to, subject, message)

    # Submit the task, so that the correct InstructorTask object gets created (for monitoring purposes)
    instructor_task.api.submit_bulk_course_email(request, course_id, email.id)  # pylint: disable=E1101

    response_payload = {
        'course_id': course_id.to_deprecated_string(),
        'success': True,
    }
    return JsonResponse(response_payload)
Esempio n. 21
0
def jump_to(request, course_id, location):
    """
    Show the page that contains a specific location.

    If the location is invalid or not in any class, return a 404.

    Otherwise, delegates to the index view to figure out whether this user
    has access, and what they should see.
    """
    try:
        course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
        usage_key = course_key.make_usage_key_from_deprecated_string(location)
    except InvalidKeyError:
        raise Http404(u"Invalid course_key or usage_key")
    try:
        (course_key, chapter, section, position) = path_to_location(modulestore(), usage_key)
    except ItemNotFoundError:
        raise Http404(u"No data at this location: {0}".format(usage_key))
    except NoPathToItem:
        raise Http404(u"This location is not in any class: {0}".format(usage_key))

    # choose the appropriate view (and provide the necessary args) based on the
    # args provided by the redirect.
    # Rely on index to do all error handling and access control.
    if chapter is None:
        return redirect('courseware', course_id=course_key.to_deprecated_string())
    elif section is None:
        return redirect('courseware_chapter', course_id=course_key.to_deprecated_string(), chapter=chapter)
    elif position is None:
        return redirect('courseware_section', course_id=course_key.to_deprecated_string(), chapter=chapter, section=section)
    else:
        return redirect('courseware_position', course_id=course_key.to_deprecated_string(), chapter=chapter, section=section, position=position)
Esempio n. 22
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")

    # possible extension: implement pagination to show to large courses

    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),
    })
Esempio n. 23
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,
    })
Esempio n. 24
0
    def __init__(
            self,
            contentstore,
            mappings,
            stores,
            i18n_service=None,
            fs_service=None,
            user_service=None,
            create_modulestore_instance=None,
            signal_handler=None,
            **kwargs
    ):
        """
        Initialize a MixedModuleStore. Here we look into our passed in kwargs which should be a
        collection of other modulestore configuration information
        """
        super(MixedModuleStore, self).__init__(contentstore, **kwargs)

        if create_modulestore_instance is None:
            raise ValueError('MixedModuleStore constructor must be passed a create_modulestore_instance function')

        self.modulestores = []
        self.mappings = {}

        for course_id, store_name in mappings.iteritems():
            try:
                self.mappings[CourseKey.from_string(course_id)] = store_name
            except InvalidKeyError:
                try:
                    self.mappings[SlashSeparatedCourseKey.from_deprecated_string(course_id)] = store_name
                except InvalidKeyError:
                    log.exception("Invalid MixedModuleStore configuration. Unable to parse course_id %r", course_id)
                    continue

        for store_settings in stores:
            key = store_settings['NAME']
            is_xml = 'XMLModuleStore' in store_settings['ENGINE']
            if is_xml:
                # restrict xml to only load courses in mapping
                store_settings['OPTIONS']['course_ids'] = [
                    course_key.to_deprecated_string()
                    for course_key, store_key in self.mappings.iteritems()
                    if store_key == key
                ]

            store = create_modulestore_instance(
                store_settings['ENGINE'],
                self.contentstore,
                store_settings.get('DOC_STORE_CONFIG', {}),
                store_settings.get('OPTIONS', {}),
                i18n_service=i18n_service,
                fs_service=fs_service,
                user_service=user_service,
                signal_handler=signal_handler,
            )
            # replace all named pointers to the store into actual pointers
            for course_key, store_name in self.mappings.iteritems():
                if store_name == key:
                    self.mappings[course_key] = store
            self.modulestores.append(store)
    def setUp(self):
        self.course_name = 'edX/toy/2012_Fall'

        # Create student account
        student = UserFactory.create()
        CourseEnrollmentFactory.create(
            user=student,
            course_id=SlashSeparatedCourseKey.from_deprecated_string(self.course_name)
        )
        self.client.login(username=student.username, password="******")

        try:
            # URL for dashboard
            self.url = reverse('dashboard')
        except NoReverseMatch:
            raise SkipTest("Skip this test if url cannot be found (ie running from CMS tests)")

        # URL for email settings modal
        self.email_modal_link = (
            ('<a href="#email-settings-modal" class="email-settings" rel="leanModal" '
             'data-course-id="{0}/{1}/{2}" data-course-number="{1}" '
             'data-optout="False">Email Settings</a>').format(
                 'edX',
                 'toy',
                 '2012_Fall'
             )
        )
Esempio n. 26
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))
Esempio n. 27
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 = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    try:
        course = get_course_with_access(request.user, 'load_forum', course_key)
    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})
Esempio n. 28
0
    def __init__(
        self,
        data_dir,
        default_class=None,
        course_dirs=None,
        course_ids=None,
        load_error_modules=True,
        i18n_service=None,
        **kwargs
    ):
        """
        Initialize an XMLModuleStore from data_dir

        Args:
            data_dir (str): path to data directory containing the course directories

            default_class (str): dot-separated string defining the default descriptor
                class to use if none is specified in entry_points

            course_dirs or course_ids (list of str): If specified, the list of course_dirs or course_ids to load. Otherwise,
                load all courses. Note, providing both
        """
        super(XMLModuleStore, self).__init__(**kwargs)

        self.data_dir = path(data_dir)
        self.modules = defaultdict(dict)  # course_id -> dict(location -> XBlock)
        self.courses = {}  # course_dir -> XBlock for the course
        self.errored_courses = {}  # course_dir -> errorlog, for dirs that failed to load

        if course_ids is not None:
            course_ids = [SlashSeparatedCourseKey.from_deprecated_string(course_id) for course_id in course_ids]

        self.load_error_modules = load_error_modules

        if default_class is None:
            self.default_class = None
        else:
            module_path, _, class_name = default_class.rpartition(".")
            class_ = getattr(import_module(module_path), class_name)
            self.default_class = class_

        self.parent_trackers = defaultdict(ParentTracker)
        self.reference_type = Location

        # All field data will be stored in an inheriting field data.
        self.field_data = inheriting_field_data(kvs=DictKeyValueStore())

        self.i18n_service = i18n_service

        # If we are specifically asked for missing courses, that should
        # be an error.  If we are asked for "all" courses, find the ones
        # that have a course.xml. We sort the dirs in alpha order so we always
        # read things in the same order (OS differences in load order have
        # bitten us in the past.)
        if course_dirs is None:
            course_dirs = sorted(
                [d for d in os.listdir(self.data_dir) if os.path.exists(self.data_dir / d / "course.xml")]
            )
        for course_dir in course_dirs:
            self.try_load_course(course_dir, course_ids)
Esempio n. 29
0
def section_problem_grade_distrib(request, course_id, section):
    """
    Creates a json with the grade distribution for the problems in the specified section.

    `request` django request

    `course_id` the course ID for the course interested in

    `section` The zero-based index of the section for the course

    Returns the format in dashboard_data.get_d3_section_grade_distrib

    If this is requested multiple times quickly for the same course, it is better to call all_problem_grade_distribution
    and pick out the sections of interest.
    """
    data = {}

    # Only instructor for this particular course can request this information
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    if has_instructor_access_for_class(request.user, course_key):
        try:
            data = dashboard_data.get_d3_section_grade_distrib(course_key, section)
        except Exception as ex:  # pylint: disable=broad-except
            log.error('Generating metrics failed with exception: %s', ex)
            data = {'error': "error"}
    else:
        data = {'error': "Access Denied: User does not have access to this course's data"}

    return HttpResponse(json.dumps(data), content_type="application/json")
Esempio n. 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"]

    if "commentable_id" in request.POST:
        course = get_course_with_access(request.user, 'load', course_key)
        id_map = get_discussion_id_map(course)
        if request.POST.get("commentable_id") in id_map:
            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))
Esempio n. 31
0
    def get(self, request, course_id):
        """
        display this view
        """
        course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
        course = modulestore().get_course(course_id)
        course_enrollment = CourseEnrollment.get_or_create_enrollment(request.user, course_id)
        course_enrollment.update_enrollment(mode="verified")
        course_enrollment.emit_event(EVENT_NAME_USER_ENTERED_MIDCOURSE_REVERIFY_VIEW)
        context = {
            "user_full_name": request.user.profile.name,
            "error": False,
            "course_id": course_id.to_deprecated_string(),
            "course_name": course.display_name_with_default,
            "course_org": course.display_org_with_default,
            "course_num": course.display_number_with_default,
            "reverify": True,
        }

        return render_to_response("verify_student/midcourse_photo_reverification.html", context)
Esempio n. 32
0
def _check_rights(course_id, user, rolename):
    """Check if user has correct rights."""
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_by_id(course_id)
    has_instructor_access = has_access(user, 'instructor', course)
    has_forum_admin = has_forum_access(user, course_id, FORUM_ROLE_ADMINISTRATOR)

    # default roles require either (staff & forum admin) or (instructor)
    if not (has_forum_admin or has_instructor_access):
        raise UnauthorizedAccessError(
            "Operation requires staff & forum admin or instructor access"
        )

    # filter out unsupported for roles
    if rolename not in CUSTOM_ROLES:
        raise UnauthorizedAccessError(strip_tags(
            "Unrecognized FUN special rolename '{}'.".format(rolename)
        ))

    return course_id
Esempio n. 33
0
def course_context_from_url(url):
    """
    Extracts the course_context from the given `url` and passes it on to
    `course_context_from_course_id()`.
    """
    url = url or ''

    match = COURSE_REGEX.match(url)
    course_id = None
    if match:
        course_id_string = match.group('course_id')
        try:
            course_id = SlashSeparatedCourseKey.from_deprecated_string(
                course_id_string)
        except InvalidKeyError:
            log.warning('unable to parse course_id "{course_id}"'.format(
                course_id=course_id_string),
                        exc_info=True)

    return course_context_from_course_id(course_id)
Esempio n. 34
0
def find_target_student_module(request, user_id, course_id, mod_id):
    """
    Retrieve target StudentModule
    """
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    usage_key = course_id.make_usage_key_from_deprecated_string(mod_id)
    user = User.objects.get(id=user_id)
    field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
        course_id,
        user,
        modulestore().get_item(usage_key),
        depth=0,
        select_for_update=True
    )
    instance = get_module(user, request, usage_key, field_data_cache, grade_bucket_type='xqueue')
    if instance is None:
        msg = "No module {0} for user {1}--access denied?".format(mod_id, user)
        log.debug(msg)
        raise Http404
    return instance
Esempio n. 35
0
    def __init__(self, xml_import_data):
        self.course_id = SlashSeparatedCourseKey.from_deprecated_string(xml_import_data.course_id)
        self.default_class = xml_import_data.default_class
        self._descriptors = {}

        def get_policy(usage_id):
            """Return the policy data for the specified usage"""
            return xml_import_data.policy.get(policy_key(usage_id), {})

        super(InMemorySystem, self).__init__(
            get_policy=get_policy,
            process_xml=self.process_xml,
            load_item=self.load_item,
            error_tracker=Mock(),
            resources_fs=xml_import_data.filesystem,
            mixins=xml_import_data.xblock_mixins,
            select=xml_import_data.xblock_select,
            render_template=lambda template, context: pprint.pformat((template, context)),
            field_data=KvsFieldData(DictKeyValueStore()),
        )
Esempio n. 36
0
def jump_to_id(request, course_id, module_id):
    """
    This entry point allows for a shorter version of a jump to where just the id of the element is
    passed in. This assumes that id is unique within the course_id namespace
    """
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    items = modulestore().get_items(course_key, name=module_id)

    if len(items) == 0:
        raise Http404(
            u"Could not find id: {0} in course_id: {1}. Referer: {2}".format(
                module_id, course_id, request.META.get("HTTP_REFERER", "")))
    if len(items) > 1:
        log.warning(
            u"Multiple items found with id: {0} in course_id: {1}. Referer: {2}. Using first: {3}"
            .format(module_id, course_id, request.META.get("HTTP_REFERER", ""),
                    items[0].location.to_deprecated_string()))

    return jump_to(request, course_id,
                   items[0].location.to_deprecated_string())
Esempio n. 37
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.
    '''
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    #Get the current course
    course = get_course_with_access(request.user, 'load', course_key)

    found_module, problem_url = find_peer_grading_module(course)
    if not found_module:
        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.
        """)
        log.exception(error_message + u"Current course is: {0}".format(course_id))
        return HttpResponse(error_message)

    return HttpResponseRedirect(problem_url)
Esempio n. 38
0
def course_specific_register(request, course_id):
    """
        Dispatcher function for selecting the specific registration method
        required by the course
    """
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = modulestore().get_course(course_key)

    if not course:
        # couldn't find the course, will just return vanilla registration page
        return redirect_with_get('register_user', request.GET)

    # now the dispatching conditionals.  Only shib for now
    if (settings.FEATURES.get('AUTH_USE_SHIB') and course.enrollment_domain
            and course.enrollment_domain.startswith(SHIBBOLETH_DOMAIN_PREFIX)):
        # shib-login takes care of both registration and login flows
        return redirect_with_get('shib-login', request.GET)

    # Default fallthrough to normal registration page
    return redirect_with_get('register_user', request.GET)
Esempio n. 39
0
def get_student_progress_url(request, course_id):
    """
    Get the progress url of a student.
    Limited to staff access.

    Takes query paremeter unique_student_identifier and if the student exists
    returns e.g. {
        'progress_url': '/../...'
    }
    """
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    user = get_student_from_identifier(request.GET.get('unique_student_identifier'))

    progress_url = reverse('student_progress', kwargs={'course_id': course_id.to_deprecated_string(), 'student_id': user.id})

    response_payload = {
        'course_id': course_id.to_deprecated_string(),
        'progress_url': progress_url,
    }
    return JsonResponse(response_payload)
Esempio n. 40
0
def has_passed(request, course_id, section_url_name):
    """
    Returns True if the student has higher or equeal grades in
    asssignment type.
    """
    student = request.user

    # Get the course by ID
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(student, 'load', course_key, depth=None)

    # Get the grade summary
    with outer_atomic():
        field_data_cache = grades.field_data_cache_for_grading(course, student)
        scores_client = ScoresClient.from_field_data_cache(field_data_cache)

    grade_summary = grades.grade(student,
                                 request,
                                 course,
                                 field_data_cache=field_data_cache,
                                 scores_client=scores_client)

    # Get assignment type wise percent
    assignments = {}
    for section in grade_summary['section_breakdown']:
        if section.get('prominent', False):
            assignments.update({section['category']: section['percent']})

    # Get the section assignment type
    section_assignment_type = ''
    for chapter in course.get_children():
        for sequenctial in chapter.get_children():
            if sequenctial.url_name == section_url_name:
                section_assignment_type = sequenctial.format
                break

    # Get section assignment percent
    percentage = assignments.get(section_assignment_type, 0.0)

    # Return passing status
    return percentage * 100 == 100
Esempio n. 41
0
def api_request(request, course_id, **kwargs):
    '''
    Routes API requests to the appropriate action method and returns JSON.
    Raises a 404 if the requested resource does not exist or notes are
        disabled for the course.
    '''
    assert isinstance(course_id, basestring)
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    # Verify that the api should be accessible to this course
    if not api_enabled(request, course_key):
        log.debug('Notes are disabled for course: {0}'.format(course_id))
        raise Http404

    # Locate the requested resource
    resource_map = API_SETTINGS.get('RESOURCE_MAP', {})
    resource_name = kwargs.pop('resource')
    resource_method = request.method
    resource = resource_map.get(resource_name)

    if resource is None:
        log.debug('Resource "{0}" does not exist'.format(resource_name))
        raise Http404

    if resource_method not in resource.keys():
        log.debug('Resource "{0}" does not support method "{1}"'.format(resource_name, resource_method))
        raise Http404

    # Execute the action associated with the resource
    func = resource.get(resource_method)
    module = globals()
    if func not in module:
        log.debug('Function "{0}" does not exist for request {1} {2}'.format(func, resource_method, resource_name))
        raise Http404

    log.debug('API request: {0} {1}'.format(resource_method, resource_name))

    api_response = module[func](request, course_key, **kwargs)
    http_response = api_format(api_response)

    return http_response
Esempio n. 42
0
def look_up_registration_code(request, course_id):
    """
    Look for the registration_code in the database.
    and check if it is still valid, allowed to redeem or not.
    """
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    code = request.GET.get('registration_code')
    course = get_course_by_id(course_key, depth=0)
    try:
        registration_code = CourseRegistrationCode.objects.get(code=code)
    except CourseRegistrationCode.DoesNotExist:
        return JsonResponse(
            {
                'is_registration_code_exists':
                False,
                'is_registration_code_valid':
                False,
                'is_registration_code_redeemed':
                False,
                'message':
                _('The enrollment code ({code}) was not found for the {course_name} course.'
                  ).format(code=code, course_name=course.display_name)
            },
            status=400)  # status code 200: OK by default

    reg_code_already_redeemed = RegistrationCodeRedemption.is_registration_code_redeemed(
        code)

    registration_code_detail_url = reverse(
        'registration_code_details', kwargs={'course_id': unicode(course_id)})

    return JsonResponse({
        'is_registration_code_exists':
        True,
        'is_registration_code_valid':
        registration_code.is_valid,
        'is_registration_code_redeemed':
        reg_code_already_redeemed,
        'registration_code_detail_url':
        registration_code_detail_url
    })  # status code 200: OK by default
Esempio n. 43
0
def xblock_view(request, course_id, usage_id, view_name):
    """
    Returns the rendered view of a given XBlock, with related resources

    Returns a json object containing two keys:
        html: The rendered html of the view
        resources: A list of tuples where the first element is the resource hash, and
            the second is the resource description
    """
    if not settings.FEATURES.get('ENABLE_XBLOCK_VIEW_ENDPOINT', False):
        log.warn("Attempt to use deactivated XBlock view endpoint -"
                 " see FEATURES['ENABLE_XBLOCK_VIEW_ENDPOINT']")
        raise Http404

    if not request.user.is_authenticated():
        raise PermissionDenied

    try:
        course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    except InvalidKeyError:
        raise Http404("Invalid location")

    with modulestore().bulk_operations(course_key):
        course = modulestore().get_course(course_key)
        instance, _ = get_module_by_usage_id(request, course_id, usage_id, course=course)

        try:
            fragment = instance.render(view_name, context=request.GET)
        except NoSuchViewError:
            log.exception("Attempt to render missing view on %s: %s", instance, view_name)
            raise Http404

        hashed_resources = OrderedDict()
        for resource in fragment.resources:
            hashed_resources[hash_resource(resource)] = resource

        return JsonResponse({
            'html': fragment.content,
            'resources': hashed_resources.items(),
            'csrf_token': unicode(csrf(request)['csrf_token']),
        })
Esempio n. 44
0
def get_section_list(request,course_id,student_id):
    section_id_dict = {}
    commentable_id_list = []
    try:
        course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    except Exception as e:
        return Resposne(
            data={
            "Error": e.message
        })

    course = get_course_by_id(course_key)
    with modulestore().bulk_operations(course.id):
        course_module = get_module_for_descriptor(
            request.user, request, course, None, course.id, course=course
        )
        if course_module is None:
            return Response(
                data={
                "Error":"User is not allowed to do the operation"
            })

    student = User.objects.get(id=student_id)
    query_params = {
    "course_id":course_id,
    "user_id": student.id,
    }
    threads = Thread.search(query_params).collection

    for chapter in course_module.get_children():
        for section in chapter.get_children():
            for unit in section.get_children():
                for vertical in unit.get_children():
                    if vertical.category == 'discussion':
                        participated = False
                        for thread in threads:
                            if thread['commentable_id'] == vertical.discussion_id:
                                participated = thread['username'] == student.username
                        section_id_dict.update({chapter.url_name: participated})

    return section_id_dict
Esempio n. 45
0
def inline_discussion(request, course_id, discussion_id):
    """
    Renders JSON for DiscussionModules
    """
    nr_transaction = newrelic.agent.current_transaction()
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    course = get_course_with_access(request.user, 'load_forum', course_key)
    cc_user = cc.User.from_django_user(request.user)
    user_info = cc_user.to_dict()

    try:
        threads, query_params = get_threads(request,
                                            course_key,
                                            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 = cached_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)
    return utils.JsonResponse({
        '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)
    })
Esempio n. 46
0
    def handle(self, *args, **options):
        course_id = options['course_id']
        if not course_id:
            raise CommandError("You must specify a course-id")

        def update_user_whitelist(username, add=True):
            """
            Update the status of whitelist user(s)
            """
            user = get_user_from_identifier(username)
            cert_whitelist, _created = CertificateWhitelist.objects.get_or_create(
                user=user, course_id=course)
            cert_whitelist.whitelist = add
            cert_whitelist.save()

        # try to parse the serialized course key into a CourseKey
        try:
            course = CourseKey.from_string(course_id)
        except InvalidKeyError:
            print(("Course id {} could not be parsed as a CourseKey; "
                   "falling back to SSCK.from_dep_str").format(course_id))
            course = SlashSeparatedCourseKey.from_deprecated_string(course_id)

        if options['add'] and options['del']:
            raise CommandError("Either remove or add a user, not both")

        if options['add'] or options['del']:
            user_str = options['add'] or options['del']
            add_to_whitelist = True if options['add'] else False
            users_list = user_str.split(",")
            for username in users_list:
                if username.strip():
                    update_user_whitelist(username, add=add_to_whitelist)

        whitelist = CertificateWhitelist.objects.filter(course_id=course)
        print(u"User whitelist for course {0}:".format(course_id))
        for whitelisted in whitelist:
            username = whitelisted.user.username
            email = whitelisted.user.email
            is_whitelisted = whitelisted.whitelist
            print(username, email, is_whitelisted)
Esempio n. 47
0
    def handle(self, *args, **options):
        if len(args) == 0:
            raise CommandError("Arguments missing: 'org/number/run commit'")

        if len(args) == 1:
            if args[0] == 'commit':
                raise CommandError(
                    "Delete_course requires a course_key <org/number/run> argument."
                )
            else:
                raise CommandError(
                    "Delete_course requires a commit argument at the end")
        elif len(args) == 2:
            try:
                course_key = CourseKey.from_string(args[0])
            except InvalidKeyError:
                try:
                    course_key = SlashSeparatedCourseKey.from_deprecated_string(
                        args[0])
                except InvalidKeyError:
                    raise CommandError(
                        "Invalid course_key: '%s'. Proper syntax: 'org/number/run commit' "
                        % args[0])
            if args[1] != 'commit':
                raise CommandError(
                    "Delete_course requires a commit argument at the end")
        elif len(args) > 2:
            raise CommandError(
                "Too many arguments! Expected <course_key> <commit>")

        if not modulestore().get_course(course_key):
            raise CommandError("Course with '%s' key not found." % args[0])

        print 'Actually going to delete the %s course from DB....' % args[0]
        if query_yes_no("Deleting course {0}. Confirm?".format(course_key),
                        default="no"):
            if query_yes_no("Are you sure. This action cannot be undone!",
                            default="no"):
                delete_course_and_groups(course_key,
                                         ModuleStoreEnum.UserID.mgmt_command)
                print "Deleted course {}".format(course_key)
Esempio n. 48
0
def course_id_from_url(url):
    """
    Extracts the course_id from the given `url`.
    """
    if not url:
        return None

    match = COURSE_REGEX.match(url)

    if match is None:
        return None

    course_id = match.group('course_id')

    if course_id is None:
        return None

    try:
        return SlashSeparatedCourseKey.from_deprecated_string(course_id)
    except InvalidKeyError:
        return None
Esempio n. 49
0
def ensure_certif(request, course_id):
    user_id = request.user.id
    username = request.user.username
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course_tma = get_course_by_id(course_key)
    is_graded = course_tma.is_graded
    grade_cutoffs = modulestore().get_course(
        course_key, depth=0).grade_cutoffs['Pass'] * 100
    grading_note = CourseGradeFactory().create(request.user, course_tma)
    passed = grading_note.passed
    percent = float(int(grading_note.percent * 1000) / 10)
    overall_progress = get_overall_progress(request.user.id, course_key)
    context = {
        'passed': passed,
        'percent': percent,
        'is_graded': is_graded,
        'grade_cutoffs': grade_cutoffs,
        'overall_progress': overall_progress
    }

    return JsonResponse(context)
Esempio n. 50
0
def notes(request, course_id):
    ''' Displays the student's notes. '''
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(request.user, 'load', course_key)
    if not notes_enabled_for_course(course):
        raise Http404

    notes = Note.objects.filter(course_id=course_key, user=request.user).order_by('-created', 'uri')

    student = request.user
    storage = course.annotation_storage_url
    context = {
        'course': course,
        'notes': notes,
        'student': student,
        'storage': storage,
        'token': retrieve_token(student.email, course.annotation_token_secret),
        'default_tab': 'myNotes',
    }

    return render_to_response('notes.html', context)
Esempio n. 51
0
def show_requirements(request, course_id):
    """
    Show the requirements necessary for the verification flow.
    """
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == ('verified', True):
        return redirect(reverse('dashboard'))

    upgrade = request.GET.get('upgrade', False)
    course = modulestore().get_course(course_id)
    context = {
        "course_id": course_id.to_deprecated_string(),
        "course_modes_choose_url": reverse("course_modes_choose", kwargs={'course_id': course_id.to_deprecated_string()}),
        "verify_student_url": reverse('verify_student_verify', kwargs={'course_id': course_id.to_deprecated_string()}),
        "course_name": course.display_name_with_default,
        "course_org": course.display_org_with_default,
        "course_num": course.display_number_with_default,
        "is_not_active": not request.user.is_active,
        "upgrade": upgrade == u'True',
    }
    return render_to_response("verify_student/show_requirements.html", context)
Esempio n. 52
0
def get_overall_course_progress(request):
    """
    Description: This view kept for fetching the overall course progress.

    Request Parameters:
        course_id: course ID for which progress needs to be calculated.
        student_id: Student for which progress needs to be calculated.

    Returns:
        json response

    Assumes the course_id is in a valid format.

    Author: Naresh Makwana
    """
    course_id = request.GET.get('course_id')
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    overall_progress = get_overall_progress(request.user.id, course_key)

    return JsonResponse({'overall_progress': overall_progress})
Esempio n. 53
0
def list_instructor_tasks(request, course_id):
    """
    List instructor tasks.

    Takes optional query paremeters.
        - With no arguments, lists running tasks.
        - `problem_location_str` lists task history for problem
        - `problem_location_str` and `unique_student_identifier` lists task
            history for problem AND student (intersection)
    """
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    problem_location_str = strip_if_string(request.GET.get('problem_location_str', False))
    student = request.GET.get('unique_student_identifier', None)
    if student is not None:
        student = get_student_from_identifier(student)

    if student and not problem_location_str:
        return HttpResponseBadRequest(
            "unique_student_identifier must accompany problem_location_str"
        )

    if problem_location_str:
        try:
            module_state_key = course_id.make_usage_key_from_deprecated_string(problem_location_str)
        except InvalidKeyError:
            return HttpResponseBadRequest()
        if student:
            # Specifying for a single student's history on this problem
            tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key, student)
        else:
            # Specifying for single problem's history
            tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key)
    else:
        # If no problem or student, just get currently running tasks
        tasks = instructor_task.api.get_running_instructor_tasks(course_id)

    response_payload = {
        'tasks': map(extract_task_features, tasks),
    }
    return JsonResponse(response_payload)
Esempio n. 54
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})
Esempio n. 55
0
def get_completion_status(request):
    """
    Description: To check completion status of the section/chapter.

    Request Parameters:
        course_id: course ID string.

    Returns:
        json response

    Assumes the course_id is in a valid format.

    Author: Naresh Makwana
    """
    # Set initial value to progress
    progress = {}
    completion_status = {}

    # Get course id and convert it to course key
    course_id = request.GET.get('course_id')
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    # Get the student course completion progress
    try:
        student_course_progress = StudentCourseProgress.objects.get(
            student=request.user.id, course_id=course_key)
        progress = student_course_progress.progress
    except StudentCourseProgress.DoesNotExist:
        pass

    # Prepare completion status dictionary
    sequential_id = request.GET.get('sequential_id')
    sequential_progress = progress.get(sequential_id, [])['progress']
    log.info('sequential progress {}'.format(sequential_progress))
    for block_id in progress.get(sequential_id, [])['children']:
        completion_status.update(
            {block_id: progress.get(block_id, [])['progress']})

    # Return the JSON resposne
    return JsonResponse({'completion_status': completion_status})
Esempio n. 56
0
    def handle(self, *args, **options):
        if not options['course']:
            raise CommandError(Command.course_option.help)

        try:
            course_key = CourseKey.from_string(options['course'])
        except InvalidKeyError:
            course_key = SlashSeparatedCourseKey.from_deprecated_string(
                options['course'])

        course = get_course_by_id(course_key)

        print 'Warning: this command directly edits the list of course tabs in mongo.'
        print 'Tabs before any changes:'
        print_course(course)

        try:
            if options['delete']:
                if len(args) != 1:
                    raise CommandError(Command.delete_option.help)
                num = int(args[0])
                if query_yes_no('Deleting tab {0} Confirm?'.format(num),
                                default='no'):
                    tabs.primitive_delete(course,
                                          num - 1)  # -1 for 0-based indexing
            elif options['insert']:
                if len(args) != 3:
                    raise CommandError(Command.insert_option.help)
                num = int(args[0])
                tab_type = args[1]
                name = args[2]
                if query_yes_no(
                        'Inserting tab {0} "{1}" "{2}" Confirm?'.format(
                            num, tab_type, name),
                        default='no'):
                    tabs.primitive_insert(course, num - 1, tab_type,
                                          name)  # -1 as above
        except ValueError as e:
            # Cute: translate to CommandError so the CLI error prints nicely.
            raise CommandError(e)
Esempio n. 57
0
def user_software_license(request):
    if request.method != 'POST' or not request.is_ajax():
        raise Http404

    # get the course id from the referer
    url_path = urlparse(request.META.get('HTTP_REFERER', '')).path
    pattern = re.compile('^/courses/(?P<id>[^/]+/[^/]+/[^/]+)/.*/?$')
    match = re.match(pattern, url_path)

    if not match:
        raise Http404
    course_id = match.groupdict().get('id', '')
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    user_id = request.session.get('_auth_user_id')
    software_name = request.POST.get('software')
    generate = request.POST.get('generate', False) == 'true'

    try:
        software = CourseSoftware.objects.get(name=software_name,
                                              course_id=course_key)
    except CourseSoftware.DoesNotExist:
        raise Http404

    try:
        user = User.objects.get(id=user_id)
    except User.DoesNotExist:
        raise Http404

    if generate:
        software_license = get_or_create_license(user, software)
    else:
        software_license = get_license(user, software)

    if software_license:
        response = {'serial': software_license.serial}
    else:
        response = {'error': 'No serial number found'}

    return HttpResponse(json.dumps(response), content_type='application/json')
    def test_branching(self):
        """
        Exercise branching code of import
        """
        repo_dir = self.GIT_REPO_DIR
        # Test successful import from command
        if not os.path.isdir(repo_dir):
            os.mkdir(repo_dir)
        self.addCleanup(shutil.rmtree, repo_dir)

        # Checkout non existent branch
        with self.assertRaisesRegexp(GitImportError,
                                     GitImportError.REMOTE_BRANCH_MISSING):
            git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite',
                                'asdfasdfasdf')

        # Checkout new branch
        git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite',
                            self.TEST_BRANCH)
        def_ms = modulestore()
        # Validate that it is different than master
        self.assertIsNotNone(def_ms.get_course(self.TEST_BRANCH_COURSE))

        # Attempt to check out the same branch again to validate branch choosing
        # works
        git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite',
                            self.TEST_BRANCH)

        # Delete to test branching back to master
        def_ms.delete_course(self.TEST_BRANCH_COURSE,
                             ModuleStoreEnum.UserID.test)
        self.assertIsNone(def_ms.get_course(self.TEST_BRANCH_COURSE))
        git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite',
                            'master')
        self.assertIsNone(def_ms.get_course(self.TEST_BRANCH_COURSE))
        self.assertIsNotNone(
            def_ms.get_course(
                SlashSeparatedCourseKey.from_deprecated_string(
                    self.TEST_COURSE)))
Esempio n. 59
0
def set_course_mode_price(request, course_id):
    """
    set the new course price and add new entry in the CourseModesArchive Table
    """
    try:
        course_price = int(request.POST['course_price'])
    except ValueError:
        return JsonResponse(
            {
                'message':
                _("Please Enter the numeric value for the course price")
            },
            status=400)  # status code 400: Bad Request

    currency = request.POST['currency']
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    course_honor_mode = CourseMode.objects.filter(mode_slug='honor',
                                                  course_id=course_key)
    if not course_honor_mode:
        return JsonResponse(
            {
                'message':
                _("CourseMode with the mode slug({mode_slug}) DoesNotExist").
                format(mode_slug='honor')
            },
            status=400)  # status code 400: Bad Request

    CourseModesArchive.objects.create(
        course_id=course_id,
        mode_slug='honor',
        mode_display_name='Honor Code Certificate',
        min_price=course_honor_mode[0].min_price,
        currency=course_honor_mode[0].currency,
        expiration_datetime=datetime.datetime.now(pytz.utc),
        expiration_date=datetime.date.today())
    course_honor_mode.update(min_price=course_price, currency=currency)
    return JsonResponse(
        {'message': _("CourseMode price updated successfully")})
Esempio n. 60
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_dict(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,
        })