Exemple #1
0
def reset_student_attempts(request, course_id):
    """

    Resets a students attempts counter or starts a task to reset all students
    attempts counters. Optionally deletes student state for a problem. Limited
    to staff access. Some sub-methods limited to instructor access.

    Takes some of the following query paremeters
        - problem_to_reset is a urlname of a problem
        - student_email is an email
        - all_students is a boolean
            requires instructor access
            mutually exclusive with delete_module
            mutually exclusive with delete_module
        - delete_module is a boolean
            requires instructor access
            mutually exclusive with all_students
    """
    course = get_course_with_access(
        request.user, course_id, 'staff', depth=None
    )

    problem_to_reset = strip_if_string(request.GET.get('problem_to_reset'))
    student_email = strip_if_string(request.GET.get('student_email'))
    all_students = request.GET.get('all_students', False) in ['true', 'True', True]
    delete_module = request.GET.get('delete_module', False) in ['true', 'True', True]

    # parameter combinations
    if all_students and student_email:
        return HttpResponseBadRequest(
            "all_students and student_email are mutually exclusive."
        )
    if all_students and delete_module:
        return HttpResponseBadRequest(
            "all_students and delete_module are mutually exclusive."
        )

    # instructor authorization
    if all_students or delete_module:
        if not has_access(request.user, course, 'instructor'):
            return HttpResponseForbidden("Requires instructor access.")

    module_state_key = _msk_from_problem_urlname(course_id, problem_to_reset)

    response_payload = {}
    response_payload['problem_to_reset'] = problem_to_reset

    if student_email:
        try:
            student = User.objects.get(email=student_email)
            enrollment.reset_student_attempts(course_id, student, module_state_key, delete_module=delete_module)
        except StudentModule.DoesNotExist:
            return HttpResponseBadRequest("Module does not exist.")
    elif all_students:
        instructor_task.api.submit_reset_problem_attempts_for_all_students(request, course_id, module_state_key)
        response_payload['task'] = 'created'
    else:
        return HttpResponseBadRequest()

    return JsonResponse(response_payload)
def remove_user_from_role(request, username_or_email, role, group_title, event_name):
    """
    Look up the given user by username (if no '@') or email (otherwise), and remove them from the supplied role.

    Arguments:
       request: django request--used for tracking log
       username_or_email: who to remove.  Decide if it's an email by presense of an '@'
       role: A student.roles.AccessRole
       group_title: what to call this group in messages to user--e.g. "beta-testers".
       event_name: what to call this event when logging to tracking logs.

    Returns:
       html to insert in the message field
    """

    username_or_email = strip_if_string(username_or_email)
    try:
        user = _user_from_name_or_email(username_or_email)
    except User.DoesNotExist:
        return u'<font color="red">Error: unknown username or email "{0}"</font>'.format(username_or_email)

    role.remove_users(user)

    # Deal with historical event names
    if event_name in ("staff", "beta-tester"):
        track.views.server_track(
            request,
            "add-or-remove-user-group",
            {"event_name": event_name, "user": unicode(user), "event": "remove"},
            page="idashboard",
        )
    else:
        track.views.server_track(request, "remove-instructor", {"instructor": unicode(user)}, page="idashboard")

    return '<font color="green">Removed {0} from {1}</font>'.format(user, group_title)
Exemple #3
0
def list_instructor_tasks(request, course_id):
    """
    List instructor tasks.
    Limited to instructor access.

    Takes optional query paremeters.
        - With no arguments, lists running tasks.
        - `problem_urlname` lists task history for problem
        - `problem_urlname` and `student_email` lists task
            history for problem AND student (intersection)
    """
    problem_urlname = strip_if_string(request.GET.get('problem_urlname',
                                                      False))
    student_email = strip_if_string(request.GET.get('student_email', False))

    if student_email and not problem_urlname:
        return HttpResponseBadRequest(
            "student_email must accompany problem_urlname")

    if problem_urlname:
        module_state_key = _msk_from_problem_urlname(course_id,
                                                     problem_urlname)
        if student_email:
            student = User.objects.get(email=student_email)
            tasks = instructor_task.api.get_instructor_task_history(
                course_id, module_state_key, student)
        else:
            tasks = instructor_task.api.get_instructor_task_history(
                course_id, module_state_key)
    else:
        tasks = instructor_task.api.get_running_instructor_tasks(course_id)

    def extract_task_features(task):
        """ Convert task to dict for json rendering """
        features = [
            'task_type', 'task_input', 'task_id', 'requester', 'created',
            'task_state'
        ]
        return dict(
            (feature, str(getattr(task, feature))) for feature in features)

    response_payload = {
        'tasks': map(extract_task_features, tasks),
    }
    return JsonResponse(response_payload)
Exemple #4
0
def update_forum_role_membership(request, course_id):
    """
    Modify user's forum role.

    The requesting user must be at least staff.
    Staff forum admins can access all roles EXCEPT for FORUM_ROLE_ADMINISTRATOR
        which is limited to instructors.
    No one can revoke an instructors FORUM_ROLE_ADMINISTRATOR status.

    Query parameters:
    - `email` is the target users email
    - `rolename` is one of [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]
    - `action` is one of ['allow', 'revoke']
    """
    course = get_course_by_id(course_id)
    has_instructor_access = has_access(request.user, course, 'instructor')
    has_forum_admin = has_forum_access(request.user, course_id,
                                       FORUM_ROLE_ADMINISTRATOR)

    email = strip_if_string(request.GET.get('email'))
    rolename = request.GET.get('rolename')
    action = request.GET.get('action')

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

    # EXCEPT FORUM_ROLE_ADMINISTRATOR requires (instructor)
    if rolename == FORUM_ROLE_ADMINISTRATOR and not has_instructor_access:
        return HttpResponseBadRequest("Operation requires instructor access.")

    if not rolename in [
            FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR,
            FORUM_ROLE_COMMUNITY_TA
    ]:
        return HttpResponseBadRequest(
            "Unrecognized rolename '{}'.".format(rolename))

    user = User.objects.get(email=email)
    target_is_instructor = has_access(user, course, 'instructor')
    # cannot revoke instructor
    if target_is_instructor and action == 'revoke' and rolename == FORUM_ROLE_ADMINISTRATOR:
        return HttpResponseBadRequest(
            "Cannot revoke instructor forum admin privelages.")

    try:
        update_forum_role(course_id, user, rolename, action)
    except Role.DoesNotExist:
        return HttpResponseBadRequest("Role does not exist.")

    response_payload = {
        'course_id': course_id,
        'action': action,
    }
    return JsonResponse(response_payload)
Exemple #5
0
def rescore_problem(request, course_id):
    """
    Starts a background process a students attempts counter. Optionally deletes student state for a problem.
    Limited to instructor access.

    Takes either of the following query paremeters
        - problem_to_reset is a urlname of a problem
        - student_email is an email
        - all_students is a boolean

    all_students and student_email cannot both be present.
    """
    problem_to_reset = strip_if_string(request.GET.get('problem_to_reset'))
    student_email = strip_if_string(request.GET.get('student_email', False))
    all_students = request.GET.get('all_students') in ['true', 'True', True]

    if not (problem_to_reset and (all_students or student_email)):
        return HttpResponseBadRequest("Missing query parameters.")

    if all_students and student_email:
        return HttpResponseBadRequest(
            "Cannot rescore with all_students and student_email.")

    module_state_key = _msk_from_problem_urlname(course_id, problem_to_reset)

    response_payload = {}
    response_payload['problem_to_reset'] = problem_to_reset

    if student_email:
        response_payload['student_email'] = student_email
        student = User.objects.get(email=student_email)
        instructor_task.api.submit_rescore_problem_for_student(
            request, course_id, module_state_key, student)
        response_payload['task'] = 'created'
    elif all_students:
        instructor_task.api.submit_rescore_problem_for_all_students(
            request, course_id, module_state_key)
        response_payload['task'] = 'created'
    else:
        return HttpResponseBadRequest()

    return JsonResponse(response_payload)
Exemple #6
0
def rescore_problem(request, course_id):
    """
    Starts a background process a students attempts counter. Optionally deletes student state for a problem.
    Limited to instructor access.

    Takes either of the following query paremeters
        - problem_to_reset is a urlname of a problem
        - student_email is an email
        - all_students is a boolean

    all_students and student_email cannot both be present.
    """
    problem_to_reset = strip_if_string(request.GET.get('problem_to_reset'))
    student_email = strip_if_string(request.GET.get('student_email', False))
    all_students = request.GET.get('all_students') in ['true', 'True', True]

    if not (problem_to_reset and (all_students or student_email)):
        return HttpResponseBadRequest("Missing query parameters.")

    if all_students and student_email:
        return HttpResponseBadRequest(
            "Cannot rescore with all_students and student_email."
        )

    module_state_key = _msk_from_problem_urlname(course_id, problem_to_reset)

    response_payload = {}
    response_payload['problem_to_reset'] = problem_to_reset

    if student_email:
        response_payload['student_email'] = student_email
        student = User.objects.get(email=student_email)
        instructor_task.api.submit_rescore_problem_for_student(request, course_id, module_state_key, student)
        response_payload['task'] = 'created'
    elif all_students:
        instructor_task.api.submit_rescore_problem_for_all_students(request, course_id, module_state_key)
        response_payload['task'] = 'created'
    else:
        return HttpResponseBadRequest()

    return JsonResponse(response_payload)
Exemple #7
0
def update_forum_role_membership(request, course_id):
    """
    Modify user's forum role.

    The requesting user must be at least staff.
    Staff forum admins can access all roles EXCEPT for FORUM_ROLE_ADMINISTRATOR
        which is limited to instructors.
    No one can revoke an instructors FORUM_ROLE_ADMINISTRATOR status.

    Query parameters:
    - `email` is the target users email
    - `rolename` is one of [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]
    - `action` is one of ['allow', 'revoke']
    """
    course = get_course_by_id(course_id)
    has_instructor_access = has_access(request.user, course, 'instructor')
    has_forum_admin = has_forum_access(
        request.user, course_id, FORUM_ROLE_ADMINISTRATOR
    )

    email = strip_if_string(request.GET.get('email'))
    rolename = request.GET.get('rolename')
    action = request.GET.get('action')

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

    # EXCEPT FORUM_ROLE_ADMINISTRATOR requires (instructor)
    if rolename == FORUM_ROLE_ADMINISTRATOR and not has_instructor_access:
        return HttpResponseBadRequest("Operation requires instructor access.")

    if not rolename in [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]:
        return HttpResponseBadRequest("Unrecognized rolename '{}'.".format(rolename))

    user = User.objects.get(email=email)
    target_is_instructor = has_access(user, course, 'instructor')
    # cannot revoke instructor
    if target_is_instructor and action == 'revoke' and rolename == FORUM_ROLE_ADMINISTRATOR:
        return HttpResponseBadRequest("Cannot revoke instructor forum admin privelages.")

    try:
        access.update_forum_role_membership(course_id, user, rolename, action)
    except Role.DoesNotExist:
        return HttpResponseBadRequest("Role does not exist.")

    response_payload = {
        'course_id': course_id,
        'action': action,
    }
    return JsonResponse(response_payload)
Exemple #8
0
def _user_from_name_or_email(username_or_email):
    """
    Return the `django.contrib.auth.User` with the supplied username or email.

    If `username_or_email` contains an `@` it is treated as an email, otherwise
    it is treated as the username
    """
    username_or_email = strip_if_string(username_or_email)

    if '@' in username_or_email:
        return User.objects.get(email=username_or_email)
    else:
        return User.objects.get(username=username_or_email)
Exemple #9
0
def _user_from_name_or_email(username_or_email):
    """
    Return the `django.contrib.auth.User` with the supplied username or email.

    If `username_or_email` contains an `@` it is treated as an email, otherwise
    it is treated as the username
    """
    username_or_email = strip_if_string(username_or_email)

    if '@' in username_or_email:
        return User.objects.get(email=username_or_email)
    else:
        return User.objects.get(username=username_or_email)
Exemple #10
0
def list_instructor_tasks(request, course_id):
    """
    List instructor tasks.
    Limited to instructor access.

    Takes optional query paremeters.
        - With no arguments, lists running tasks.
        - `problem_urlname` lists task history for problem
        - `problem_urlname` and `student_email` lists task
            history for problem AND student (intersection)
    """
    problem_urlname = strip_if_string(request.GET.get('problem_urlname', False))
    student_email = strip_if_string(request.GET.get('student_email', False))

    if student_email and not problem_urlname:
        return HttpResponseBadRequest(
            "student_email must accompany problem_urlname"
        )

    if problem_urlname:
        module_state_key = _msk_from_problem_urlname(course_id, problem_urlname)
        if student_email:
            student = User.objects.get(email=student_email)
            tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key, student)
        else:
            tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key)
    else:
        tasks = instructor_task.api.get_running_instructor_tasks(course_id)

    def extract_task_features(task):
        """ Convert task to dict for json rendering """
        features = ['task_type', 'task_input', 'task_id', 'requester', 'created', 'task_state']
        return dict((feature, str(getattr(task, feature))) for feature in features)

    response_payload = {
        'tasks': map(extract_task_features, tasks),
    }
    return JsonResponse(response_payload)
Exemple #11
0
def modify_access(request, course_id):
    """
    Modify staff/instructor access of other user.
    Requires instructor access.

    NOTE: instructors cannot remove their own instructor access.

    Query parameters:
    email is the target users email
    rolename is one of ['instructor', 'staff', 'beta']
    action is one of ['allow', 'revoke']
    """
    course = get_course_with_access(
        request.user, course_id, 'instructor', depth=None
    )

    email = strip_if_string(request.GET.get('email'))
    rolename = request.GET.get('rolename')
    action = request.GET.get('action')

    if not rolename in ['instructor', 'staff', 'beta']:
        return HttpResponseBadRequest(
            "unknown rolename '{}'".format(rolename)
        )

    user = User.objects.get(email=email)

    # disallow instructors from removing their own instructor access.
    if rolename == 'instructor' and user == request.user and action != 'allow':
        return HttpResponseBadRequest(
            "An instructor cannot remove their own instructor access."
        )

    if action == 'allow':
        access.allow_access(course, user, rolename)
    elif action == 'revoke':
        access.revoke_access(course, user, rolename)
    else:
        return HttpResponseBadRequest("unrecognized action '{}'".format(action))

    response_payload = {
        'email': email,
        'rolename': rolename,
        'action': action,
        'success': 'yes',
    }
    return JsonResponse(response_payload)
Exemple #12
0
def modify_access(request, course_id):
    """
    Modify staff/instructor access of other user.
    Requires instructor access.

    NOTE: instructors cannot remove their own instructor access.

    Query parameters:
    email is the target users email
    rolename is one of ['instructor', 'staff', 'beta']
    action is one of ['allow', 'revoke']
    """
    course = get_course_with_access(
        request.user, course_id, 'instructor', depth=None
    )

    email = strip_if_string(request.GET.get('email'))
    rolename = request.GET.get('rolename')
    action = request.GET.get('action')

    if not rolename in ['instructor', 'staff', 'beta']:
        return HttpResponseBadRequest(
            "unknown rolename '{}'".format(rolename)
        )

    user = User.objects.get(email=email)

    # disallow instructors from removing their own instructor access.
    if rolename == 'instructor' and user == request.user and action != 'allow':
        return HttpResponseBadRequest(
            "An instructor cannot remove their own instructor access."
        )

    if action == 'allow':
        access.allow_access(course, user, rolename)
    elif action == 'revoke':
        access.revoke_access(course, user, rolename)
    else:
        return HttpResponseBadRequest("unrecognized action '{}'".format(action))

    response_payload = {
        'email': email,
        'rolename': rolename,
        'action': action,
        'success': 'yes',
    }
    return JsonResponse(response_payload)
Exemple #13
0
def get_student_progress_url(request, course_id):
    """
    Get the progress url of a student.
    Limited to staff access.

    Takes query paremeter student_email and if the student exists
    returns e.g. {
        'progress_url': '/../...'
    }
    """
    student_email = strip_if_string(request.GET.get('student_email'))
    user = User.objects.get(email=student_email)

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

    response_payload = {
        'course_id': course_id,
        'progress_url': progress_url,
    }
    return JsonResponse(response_payload)
Exemple #14
0
def list_instructor_tasks(request, course_id):
    """
    List instructor tasks.

    Takes optional query paremeters.
        - With no arguments, lists running tasks.
        - `problem_urlname` lists task history for problem
        - `problem_urlname` and `unique_student_identifier` lists task
            history for problem AND student (intersection)
    """
    problem_urlname = strip_if_string(request.GET.get('problem_urlname',
                                                      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_urlname:
        return HttpResponseBadRequest(
            "unique_student_identifier must accompany problem_urlname")

    if problem_urlname:
        module_state_key = _msk_from_problem_urlname(course_id,
                                                     problem_urlname)
        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)
Exemple #15
0
def remove_user_from_role(request, username_or_email, role, group_title, event_name):
    """
    Look up the given user by username (if no '@') or email (otherwise), and remove them from the supplied role.

    Arguments:
       request: django request--used for tracking log
       username_or_email: who to remove.  Decide if it's an email by presense of an '@'
       role: A student.roles.AccessRole
       group_title: what to call this group in messages to user--e.g. "beta-testers".
       event_name: what to call this event when logging to tracking logs.

    Returns:
       html to insert in the message field
    """

    username_or_email = strip_if_string(username_or_email)
    try:
        user = _user_from_name_or_email(username_or_email)
    except User.DoesNotExist:
        return u'<font color="red">Error: unknown username or email "{0}"</font>'.format(username_or_email)

    role.remove_users(user)

    # Deal with historical event names
    if event_name in ('staff', 'beta-tester'):
        track.views.server_track(
            request,
            "add-or-remove-user-group",
            {
                "event_name": event_name,
                "user": unicode(user),
                "event": "remove"
            },
            page="idashboard"
        )
    else:
        track.views.server_track(request, "remove-instructor", {"instructor": unicode(user)}, page="idashboard")

    return '<font color="green">Removed {0} from {1}</font>'.format(user, group_title)
Exemple #16
0
def get_student_progress_url(request, course_id):
    """
    Get the progress url of a student.
    Limited to staff access.

    Takes query paremeter student_email and if the student exists
    returns e.g. {
        'progress_url': '/../...'
    }
    """
    student_email = strip_if_string(request.GET.get('student_email'))
    user = User.objects.get(email=student_email)

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

    response_payload = {
        'course_id': course_id,
        'progress_url': progress_url,
    }
    return JsonResponse(response_payload)
Exemple #17
0
def list_instructor_tasks(request, course_id):
    """
    List instructor tasks.

    Takes optional query paremeters.
        - With no arguments, lists running tasks.
        - `problem_urlname` lists task history for problem
        - `problem_urlname` and `unique_student_identifier` lists task
            history for problem AND student (intersection)
    """
    problem_urlname = strip_if_string(request.GET.get('problem_urlname', 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_urlname:
        return HttpResponseBadRequest(
            "unique_student_identifier must accompany problem_urlname"
        )

    if problem_urlname:
        module_state_key = _msk_from_problem_urlname(course_id, problem_urlname)
        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)
Exemple #18
0
def list_instructor_tasks(request, course_id):
    """
    List instructor tasks.
    Limited to instructor access.

    Takes optional query paremeters.
        - With no arguments, lists running tasks.
        - `problem_urlname` lists task history for problem
        - `problem_urlname` and `unique_student_identifier` lists task
            history for problem AND student (intersection)
    """
    problem_urlname = strip_if_string(request.GET.get('problem_urlname', 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_urlname:
        return HttpResponseBadRequest(
            "unique_student_identifier must accompany problem_urlname"
        )

    if problem_urlname:
        module_state_key = _msk_from_problem_urlname(course_id, problem_urlname)
        if student:
            tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key, student)
        else:
            tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key)
    else:
        tasks = instructor_task.api.get_running_instructor_tasks(course_id)

    def extract_task_features(task):
        """
        Convert task to dict for json rendering.
        Expects tasks have the following features:
        * task_type (str, type of task)
        * task_input (dict, input(s) to the task)
        * task_id (str, celery id of the task)
        * requester (str, username who submitted the task)
        * task_state (str, state of task eg PROGRESS, COMPLETED)
        * created (datetime, when the task was completed)
        * task_output (optional)
        """
        # Pull out information from the task
        features = ['task_type', 'task_input', 'task_id', 'requester', 'task_state']
        task_feature_dict = {feature: str(getattr(task, feature)) for feature in features}
        # Some information (created, duration, status, task message) require additional formatting
        task_feature_dict['created'] = task.created.isoformat()

        # Get duration info, if known
        duration_sec = 'unknown'
        if hasattr(task, 'task_output') and task.task_output is not None:
            try:
                task_output = json.loads(task.task_output)
            except ValueError:
                log.error("Could not parse task output as valid json; task output: %s", task.task_output)
            else:
                if 'duration_ms' in task_output:
                    duration_sec = int(task_output['duration_ms'] / 1000.0)
        task_feature_dict['duration_sec'] = duration_sec

        # Get progress status message & success information
        success, task_message = get_task_completion_info(task)
        status = _("Complete") if success else _("Incomplete")
        task_feature_dict['status'] = status
        task_feature_dict['task_message'] = task_message

        return task_feature_dict

    response_payload = {
        'tasks': map(extract_task_features, tasks),
    }
    return JsonResponse(response_payload)
Exemple #19
0
def list_instructor_tasks(request, course_id):
    """
    List instructor tasks.
    Limited to instructor access.

    Takes optional query paremeters.
        - With no arguments, lists running tasks.
        - `problem_urlname` lists task history for problem
        - `problem_urlname` and `unique_student_identifier` lists task
            history for problem AND student (intersection)
    """
    problem_urlname = strip_if_string(request.GET.get('problem_urlname',
                                                      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_urlname:
        return HttpResponseBadRequest(
            "unique_student_identifier must accompany problem_urlname")

    if problem_urlname:
        module_state_key = _msk_from_problem_urlname(course_id,
                                                     problem_urlname)
        if student:
            tasks = instructor_task.api.get_instructor_task_history(
                course_id, module_state_key, student)
        else:
            tasks = instructor_task.api.get_instructor_task_history(
                course_id, module_state_key)
    else:
        tasks = instructor_task.api.get_running_instructor_tasks(course_id)

    def extract_task_features(task):
        """
        Convert task to dict for json rendering.
        Expects tasks have the following features:
        * task_type (str, type of task)
        * task_input (dict, input(s) to the task)
        * task_id (str, celery id of the task)
        * requester (str, username who submitted the task)
        * task_state (str, state of task eg PROGRESS, COMPLETED)
        * created (datetime, when the task was completed)
        * task_output (optional)
        """
        # Pull out information from the task
        features = [
            'task_type', 'task_input', 'task_id', 'requester', 'task_state'
        ]
        task_feature_dict = {
            feature: str(getattr(task, feature))
            for feature in features
        }
        # Some information (created, duration, status, task message) require additional formatting
        task_feature_dict['created'] = task.created.isoformat()

        # Get duration info, if known
        duration_sec = 'unknown'
        if hasattr(task, 'task_output') and task.task_output is not None:
            try:
                task_output = json.loads(task.task_output)
            except ValueError:
                log.error(
                    "Could not parse task output as valid json; task output: %s",
                    task.task_output)
            else:
                if 'duration_ms' in task_output:
                    duration_sec = int(task_output['duration_ms'] / 1000.0)
        task_feature_dict['duration_sec'] = duration_sec

        # Get progress status message & success information
        success, task_message = get_task_completion_info(task)
        status = _("Complete") if success else _("Incomplete")
        task_feature_dict['status'] = status
        task_feature_dict['task_message'] = task_message

        return task_feature_dict

    response_payload = {
        'tasks': map(extract_task_features, tasks),
    }
    return JsonResponse(response_payload)
Exemple #20
0
def reset_student_attempts(request, course_id):
    """

    Resets a students attempts counter or starts a task to reset all students
    attempts counters. Optionally deletes student state for a problem. Limited
    to staff access. Some sub-methods limited to instructor access.

    Takes some of the following query paremeters
        - problem_to_reset is a urlname of a problem
        - unique_student_identifier is an email or username
        - all_students is a boolean
            requires instructor access
            mutually exclusive with delete_module
            mutually exclusive with delete_module
        - delete_module is a boolean
            requires instructor access
            mutually exclusive with all_students
    """
    course = get_course_with_access(request.user,
                                    course_id,
                                    'staff',
                                    depth=None)

    problem_to_reset = strip_if_string(request.GET.get('problem_to_reset'))
    student_identifier = request.GET.get('unique_student_identifier', None)
    student = None
    if student_identifier is not None:
        student = get_student_from_identifier(student_identifier)
    all_students = request.GET.get('all_students',
                                   False) in ['true', 'True', True]
    delete_module = request.GET.get('delete_module',
                                    False) in ['true', 'True', True]

    # parameter combinations
    if all_students and student:
        return HttpResponseBadRequest(
            "all_students and unique_student_identifier are mutually exclusive."
        )
    if all_students and delete_module:
        return HttpResponseBadRequest(
            "all_students and delete_module are mutually exclusive.")

    # instructor authorization
    if all_students or delete_module:
        if not has_access(request.user, course, 'instructor'):
            return HttpResponseForbidden("Requires instructor access.")

    module_state_key = _msk_from_problem_urlname(course_id, problem_to_reset)

    response_payload = {}
    response_payload['problem_to_reset'] = problem_to_reset

    if student:
        try:
            enrollment.reset_student_attempts(course_id,
                                              student,
                                              module_state_key,
                                              delete_module=delete_module)
        except StudentModule.DoesNotExist:
            return HttpResponseBadRequest("Module does not exist.")
        response_payload['student'] = student_identifier
    elif all_students:
        instructor_task.api.submit_reset_problem_attempts_for_all_students(
            request, course_id, module_state_key)
        response_payload['task'] = 'created'
        response_payload['student'] = 'All Students'
    else:
        return HttpResponseBadRequest()

    return JsonResponse(response_payload)