Example #1
0
def get_grading_status_list(request):
    """
    Get a list of locations where student has submitted open ended questions and the status of each.
    Input:
        Course id, student id
    Output:
        Dictionary containing success, and a list of problems in the course with student submission status.
        See grader_util for format details.
    """
    if request.method != 'GET':
        return util._error_response("Request type must be GET", _INTERFACE_VERSION)

    for tag in ['course_id', 'student_id']:
        if tag not in request.GET:
            return util._error_response("Missing required key {0}".format(tag), _INTERFACE_VERSION)

    course_id = request.GET.get('course_id')
    student_id = request.GET.get('student_id')

    success, sub_list = grader_util.get_problems_student_has_tried(student_id, course_id)

    if not success:
        return util._error_response("Could not generate a submission list. {0}".format(sub_list),_INTERFACE_VERSION)

    problem_list_dict={
        'success' : success,
        'problem_list' : sub_list,
        }

    util.log_connection_data()
    return util._success_response(problem_list_dict, _INTERFACE_VERSION)
Example #2
0
def get_grading_status_list(request):
    """
    Get a list of locations where student has submitted open ended questions and the status of each.
    Input:
        Course id, student id
    Output:
        Dictionary containing success, and a list of problems in the course with student submission status.
        See grader_util for format details.
    """
    if request.method != 'GET':
        return util._error_response("Request type must be GET", _INTERFACE_VERSION)

    for tag in ['course_id', 'student_id']:
        if tag not in request.GET:
            return util._error_response("Missing required key {0}".format(tag), _INTERFACE_VERSION)

    course_id = request.GET.get('course_id')
    student_id = request.GET.get('student_id')

    success, sub_list = grader_util.get_problems_student_has_tried(student_id, course_id)

    if not success:
        return util._error_response("Could not generate a submission list. {0}".format(sub_list),_INTERFACE_VERSION)

    problem_list_dict={
        'success' : success,
        'problem_list' : sub_list,
        }

    util.log_connection_data()
    return util._success_response(problem_list_dict, _INTERFACE_VERSION)
Example #3
0
def take_action_on_flags(request):
    if request.method != 'POST':
        return util._error_response("Request type must be POST", _INTERFACE_VERSION)

    for tag in ['course_id', 'student_id', 'submission_id', 'action_type']:
        if tag not in request.POST:
            return util._error_response("Missing required key {0}".format(tag), _INTERFACE_VERSION)

    course_id = request.POST.get('course_id')
    student_id = request.POST.get('student_id')
    submission_id = request.POST.get('submission_id')
    action_type = request.POST.get('action_type')

    success, data = peer_grading_util.take_action_on_flags(course_id, student_id, submission_id, action_type)

    if not success:
        return util._error_response(data,_INTERFACE_VERSION)

    submission_dict={
        'success' : success,
        'data' : data,
        }

    util.log_connection_data()
    return util._success_response(submission_dict, _INTERFACE_VERSION)
Example #4
0
def get_pending_count(request):
    """
    Returns the number of submissions pending grading
    """
    if request.method != 'GET':
        return util._error_response("'get_pending_count' must use HTTP GET",
                                    _INTERFACE_VERSION)

    grader_type = request.GET.get("grader_type")

    if not grader_type:
        return util._error_response("grader type is a needed key",
                                    _INTERFACE_VERSION)

    if grader_type not in [i[0] for i in GRADER_TYPE]:
        return util._error_response("invalid grader type", _INTERFACE_VERSION)

    to_be_graded_count = Submission.objects.filter(
        state=SubmissionState.waiting_to_be_graded,
        next_grader_type=grader_type,
    ).count()

    util.log_connection_data()
    return util._success_response({'to_be_graded_count': to_be_graded_count},
                                  _INTERFACE_VERSION)
Example #5
0
def check_for_notifications(request):
    """
    Check if a given problem name, location tuple is unique
    Input:
        A problem location and the problem name
    Output:
        Dictionary containing success, and and indicator of whether or not the name is unique
    """
    if request.method != 'GET':
        return util._error_response("Request type must be GET",
                                    _INTERFACE_VERSION)

    for tag in [
            'course_id', 'user_is_staff', 'last_time_viewed', 'student_id'
    ]:
        if tag not in request.GET:
            return util._error_response("Missing required key {0}".format(tag),
                                        _INTERFACE_VERSION)

    request_dict = request.GET.copy()
    success, combined_notifications = grader_util.check_for_combined_notifications(
        request_dict)

    if not success:
        return util._error_response(combined_notifications, _INTERFACE_VERSION)

    util.log_connection_data()
    return util._success_response(combined_notifications, _INTERFACE_VERSION)
Example #6
0
def get_pending_count(request):
    """
    Returns the number of submissions pending grading
    """
    if cache.get(NOTHING_TO_ML_GRADE_CACHE_KEY):
        # If get_submission_ml resulted in no ml grading being found, then return pending count as 0.
        # When cache timeout expires, it will check again.  This saves us from excessive calls to
        # get_submission_ml.
        to_be_graded_count = 0
    else:
        if request.method != 'GET':
            return util._error_response("'get_pending_count' must use HTTP GET", _INTERFACE_VERSION)

        grader_type = request.GET.get("grader_type")

        if not grader_type:
            return util._error_response("grader type is a needed key", _INTERFACE_VERSION)

        if grader_type not in [i[0] for i in GRADER_TYPE]:
            return util._error_response("invalid grader type", _INTERFACE_VERSION)

        to_be_graded_count = Submission.objects.filter(
            state=SubmissionState.waiting_to_be_graded,
            next_grader_type=grader_type,
            ).count()

    util.log_connection_data()
    return util._success_response({'to_be_graded_count' : to_be_graded_count}, _INTERFACE_VERSION)
Example #7
0
def verify_name_uniqueness(request):
    """
    Check if a given problem name, location tuple is unique
    Input:
        A problem location and the problem name
    Output:
        Dictionary containing success, and and indicator of whether or not the name is unique
    """
    if request.method != 'GET':
        return util._error_response("Request type must be GET", _INTERFACE_VERSION)

    for tag in ['location', 'problem_name', 'course_id']:
        if tag not in request.GET:
            return util._error_response("Missing required key {0}".format(tag), _INTERFACE_VERSION)

    location=request.GET.get("location")
    problem_name = request.GET.get("problem_name")
    course_id = request.GET.get('course_id')

    success, unique = grader_util.check_name_uniqueness(problem_name,location, course_id)

    if not success:
        return util._error_response(eta,_INTERFACE_VERSION)

    return util._success_response({
        'name_is_unique' : unique,
        }, _INTERFACE_VERSION)
Example #8
0
def take_action_on_flags(request):
    if request.method != 'POST':
        return util._error_response("Request type must be POST", _INTERFACE_VERSION)

    for tag in ['course_id', 'student_id', 'submission_id', 'action_type']:
        if tag not in request.POST:
            return util._error_response("Missing required key {0}".format(tag), _INTERFACE_VERSION)

    course_id = request.POST.get('course_id')
    student_id = request.POST.get('student_id')
    submission_id = request.POST.get('submission_id')
    action_type = request.POST.get('action_type')

    success, data = peer_grading_util.take_action_on_flags(course_id, student_id, submission_id, action_type)

    log.debug(data)
    if not success:
        return util._error_response(data,_INTERFACE_VERSION)

    submission_dict={
        'success' : success,
        'data' : data,
        }

    util.log_connection_data()
    return util._success_response(submission_dict, _INTERFACE_VERSION)
Example #9
0
def get_course_data(request):
    """
    Get the course data for a given course.
    """

    if request.method != "GET":
        return util._error_response("Request type must be GET",
                                    _INTERFACE_VERSION)

    course = request.GET.get('course')

    # Throw an error if user does not specify a course.
    if course is None:
        return util._error_response("You must specify a course.",
                                    _INTERFACE_VERSION)

    # Generate a data filename for the course.
    filename = get_course_data_filename(course)

    # Get the data file from S3.  There is a periodic task that puts the files here.
    s3 = S3Connection(settings.AWS_ACCESS_KEY_ID,
                      settings.AWS_SECRET_ACCESS_KEY,
                      is_secure=False)
    file_url = s3.generate_url(settings.S3_FILE_TIMEOUT,
                               'GET',
                               bucket=settings.S3_BUCKETNAME.lower(),
                               key=filename)

    # Return a temporary url.
    return util._success_response({'file_url': file_url}, _INTERFACE_VERSION)
Example #10
0
def request_eta_for_submission(request):
    """
    Gets the ETA (in seconds) for a student submission to be graded for a given location
    Input:
        A problem location
    Output:
        Dictionary containing success, and eta indicating time a new submission for given location will take to be graded
    """
    if request.method != 'GET':
        return util._error_response("Request type must be GET",
                                    _INTERFACE_VERSION)

    location = request.GET.get("location")
    if not location:
        return util._error_response("Missing required key location",
                                    _INTERFACE_VERSION)

    success, eta = grader_util.get_eta_for_submission(location)

    if not success:
        return util._error_response(eta, _INTERFACE_VERSION)

    return util._success_response({
        'eta': eta,
    }, _INTERFACE_VERSION)
Example #11
0
def verify_name_uniqueness(request):
    """
    Check if a given problem name, location tuple is unique
    Input:
        A problem location and the problem name
    Output:
        Dictionary containing success, and and indicator of whether or not the name is unique
    """
    if request.method != 'GET':
        return util._error_response("Request type must be GET", _INTERFACE_VERSION)

    for tag in ['location', 'problem_name', 'course_id']:
        if tag not in request.GET:
            return util._error_response("Missing required key {0}".format(tag), _INTERFACE_VERSION)

    location=request.GET.get("location")
    problem_name = request.GET.get("problem_name")
    course_id = request.GET.get('course_id')

    success, unique = grader_util.check_name_uniqueness(problem_name,location, course_id)

    if not success:
        return util._error_response(unique,_INTERFACE_VERSION)

    return util._success_response({
        'name_is_unique' : unique,
        }, _INTERFACE_VERSION)
Example #12
0
def get_pending_count(request):
    """
    Returns the number of submissions pending grading
    """
    if cache.get(NOTHING_TO_ML_GRADE_CACHE_KEY):
        # If get_submission_ml resulted in no ml grading being found, then return pending count as 0.
        # When cache timeout expires, it will check again.  This saves us from excessive calls to
        # get_submission_ml.
        to_be_graded_count = 0
    else:
        if request.method != 'GET':
            return util._error_response(
                "'get_pending_count' must use HTTP GET", _INTERFACE_VERSION)

        grader_type = request.GET.get("grader_type")

        if not grader_type:
            return util._error_response("grader type is a needed key",
                                        _INTERFACE_VERSION)

        if grader_type not in [i[0] for i in GRADER_TYPE]:
            return util._error_response("invalid grader type",
                                        _INTERFACE_VERSION)

        to_be_graded_count = Submission.objects.filter(
            state=SubmissionState.waiting_to_be_graded,
            next_grader_type=grader_type,
        ).count()

    util.log_connection_data()
    return util._success_response({'to_be_graded_count': to_be_graded_count},
                                  _INTERFACE_VERSION)
Example #13
0
def get_submission_ml(request):
    """
    Gets a submission for the ML grader
    Input:
        Get request with no parameters
    """
    unique_locations = [
        x['location']
        for x in list(Submission.objects.values('location').distinct())
    ]
    for location in unique_locations:
        sl = staff_grading_util.StaffLocation(location)
        subs_graded_by_instructor = sl.graded_count()
        success = ml_grading_util.check_for_all_model_and_rubric_success(
            location)
        if subs_graded_by_instructor >= settings.MIN_TO_USE_ML and success:
            to_be_graded = Submission.objects.filter(
                location=location,
                state=SubmissionState.waiting_to_be_graded,
                next_grader_type="ML",
            )
            if (to_be_graded.count() > 0):
                to_be_graded = to_be_graded[0]
                if to_be_graded is not None:
                    to_be_graded.state = SubmissionState.being_graded
                    to_be_graded.save()

                    return util._success_response(
                        {'submission_id': to_be_graded.id}, _INTERFACE_VERSION)

    util.log_connection_data()
    return util._error_response("Nothing to grade.", _INTERFACE_VERSION)
Example #14
0
def get_submission_ml(request):
    """
    Gets a submission for the ML grader
    Input:
        Get request with no parameters
    """
    unique_locations = [x["location"] for x in list(Submission.objects.values("location").distinct())]
    for location in unique_locations:
        subs_graded_by_instructor = staff_grading_util.finished_submissions_graded_by_instructor(location).count()
        success = ml_grading_util.check_for_all_model_and_rubric_success(location)
        if subs_graded_by_instructor >= settings.MIN_TO_USE_ML and success:
            to_be_graded = Submission.objects.filter(
                location=location, state=SubmissionState.waiting_to_be_graded, next_grader_type="ML"
            )
            if to_be_graded.count() > 0:
                to_be_graded = to_be_graded[0]
                if to_be_graded is not None:
                    to_be_graded.state = SubmissionState.being_graded
                    to_be_graded.save()

                    # Insert timing initialization code
                    initialize_timing(to_be_graded)

                    return util._success_response({"submission_id": to_be_graded.id}, _INTERFACE_VERSION)

    util.log_connection_data()
    return util._error_response("Nothing to grade.", _INTERFACE_VERSION)
Example #15
0
def put_result(request):
    """
    Used by external interfaces to post results back to controller
    """
    if request.method != 'POST':
        return util._error_response("'put_result' must use HTTP POST", _INTERFACE_VERSION)
    else:
        post_data = request.POST.dict().copy()

        for tag in ['feedback', 'submission_id', 'grader_type', 'status', 'confidence', 'grader_id', 'score', 'errors', 'rubric_scores_complete', 'rubric_scores']:
            if not post_data.has_key(tag):
                return util._error_response("Failed to find needed key {0}.".format(tag), _INTERFACE_VERSION)

        #list comprehension below just gets all available grader types ['ML','IN', etc
        if post_data['grader_type'] not in [i[0] for i in GRADER_TYPE]:
            return util._error_response("Invalid grader type {0}.".format(post_data['grader_type']),_INTERFACE_VERSION)

        #list comprehension below gets all available status codes ['F',"S']
        if post_data['status'] not in [i[0] for i in STATUS_CODES]:
            return util._error_response("Invalid grader status.".format(post_data['status']), _INTERFACE_VERSION)

        try:
            post_data['score'] = int(post_data['score'])
        except:
            return util._error_response("Can't parse score {0} into an int.".format(post_data['score']), _INTERFACE_VERSION)

        try:
            sub=Submission.objects.get(id=int(post_data['submission_id']))
        except:
            return util._error_response(
                "Submission id {0} is not valid.".format(post_data.get('submission_id', "NA")),
                _INTERFACE_VERSION,
            )

        rubric_scores_complete = request.POST.get('rubric_scores_complete', False)
        rubric_scores = request.POST.get('rubric_scores', [])

        try:
            rubric_scores=json.loads(rubric_scores)
        except:
            pass

        success, error_message = grader_util.validate_rubric_scores(rubric_scores, rubric_scores_complete, sub)
        if not success:
            return util._error_response(
                error_message,
                _INTERFACE_VERSION,
            )

        post_data['rubric_scores']=rubric_scores
        post_data['rubric_scores_complete'] = rubric_scores_complete
        success, header = grader_util.create_and_handle_grader_object(post_data)
        if not success:
            return util._error_response("Could not save grader.", _INTERFACE_VERSION)

        util.log_connection_data()
        return util._success_response({'message' : "Saved successfully."}, _INTERFACE_VERSION)
Example #16
0
def put_result(request):
    """
    Used by external interfaces to post results back to controller
    """
    if request.method != 'POST':
        return util._error_response("'put_result' must use HTTP POST", _INTERFACE_VERSION)
    else:
        post_data = request.POST.dict().copy()

        for tag in ['feedback', 'submission_id', 'grader_type', 'status', 'confidence', 'grader_id', 'score', 'errors', 'rubric_scores_complete', 'rubric_scores']:
            if not post_data.has_key(tag):
                return util._error_response("Failed to find needed key {0}.".format(tag), _INTERFACE_VERSION)

        #list comprehension below just gets all available grader types ['ML','IN', etc
        if post_data['grader_type'] not in [i[0] for i in GRADER_TYPE]:
            return util._error_response("Invalid grader type {0}.".format(post_data['grader_type']),_INTERFACE_VERSION)

        #list comprehension below gets all available status codes ['F',"S']
        if post_data['status'] not in [i[0] for i in STATUS_CODES]:
            return util._error_response("Invalid grader status.".format(post_data['status']), _INTERFACE_VERSION)

        try:
            post_data['score'] = int(post_data['score'])
        except Exception:
            return util._error_response("Can't parse score {0} into an int.".format(post_data['score']), _INTERFACE_VERSION)

        try:
            sub=Submission.objects.get(id=int(post_data['submission_id']))
        except Exception:
            return util._error_response(
                "Submission id {0} is not valid.".format(post_data.get('submission_id', "NA")),
                _INTERFACE_VERSION,
            )

        rubric_scores_complete = request.POST.get('rubric_scores_complete', False)
        rubric_scores = request.POST.get('rubric_scores', [])

        try:
            rubric_scores=json.loads(rubric_scores)
        except Exception:
            pass

        success, error_message = grader_util.validate_rubric_scores(rubric_scores, rubric_scores_complete, sub)
        if not success:
            return util._error_response(
                error_message,
                _INTERFACE_VERSION,
            )

        post_data['rubric_scores']=rubric_scores
        post_data['rubric_scores_complete'] = rubric_scores_complete
        success, header = grader_util.create_and_handle_grader_object(post_data)
        if not success:
            return util._error_response("Could not save grader.", _INTERFACE_VERSION)

        util.log_connection_data()
        return util._success_response({'message' : "Saved successfully."}, _INTERFACE_VERSION)
Example #17
0
def get_submission_instructor(request):
    """
    Gets a submission for the Instructor grading view
    """
    try:
        course_id = util._value_or_default(request.GET['course_id'], None)
    except Exception:
        return util._error_response("'get_submission' requires parameter 'course_id'", _INTERFACE_VERSION)

    sc = staff_grading_util.StaffCourse(course_id)
    found, sub_id = sc.next_item()

    if not found:
        return util._error_response("Nothing to grade.", _INTERFACE_VERSION)

    util.log_connection_data()
    return util._success_response({'submission_id' : sub_id}, _INTERFACE_VERSION)
Example #18
0
def get_submission_peer(request):
    """
    Gets a submission for the Peer grading view
    """
    try:
        location = util._value_or_default(request.GET['location'], None)
        grader_id = util._value_or_default(request.GET['grader_id'], None)
    except KeyError:
        return util._error_response("'get_submission' requires parameters 'location', 'grader_id'", _INTERFACE_VERSION)

    pl = peer_grading_util.PeerLocation(location, grader_id)
    found, sub_id = pl.next_item()

    if not found:
        return util._error_response("Nothing to grade.", _INTERFACE_VERSION)

    util.log_connection_data()
    return util._success_response({'submission_id' : sub_id}, _INTERFACE_VERSION)
Example #19
0
def log_in(request):
    """
    Handles external login request.
    """
    if request.method == 'POST':
        p = request.POST.copy()
        if p.has_key('username') and p.has_key('password'):
            log.debug("Username: {0} Password: {1}".format(p['username'], p['password']))
            user = authenticate(username=p['username'], password=p['password'])
            if user is not None:
                login(request, user)
                log.debug("Successful login!")
                return util._success_response({'message' : 'logged in'} , _INTERFACE_VERSION)
            else:
                return util._error_response('Incorrect login credentials', _INTERFACE_VERSION)
        else:
            return util._error_response('Insufficient login info', _INTERFACE_VERSION)
    else:
        return util._error_response('login_required', _INTERFACE_VERSION)
Example #20
0
def get_submission_instructor(request):
    """
    Gets a submission for the Instructor grading view
    """
    try:
        course_id = util._value_or_default(request.GET['course_id'], None)
    except:
        return util._error_response("'get_submission' requires parameter 'course_id'", _INTERFACE_VERSION)

    found, sub_id = staff_grading_util.get_single_instructor_grading_item(course_id)

    if not found:
        return util._error_response("Nothing to grade.", _INTERFACE_VERSION)

    #Insert timing initialization code
    initialize_timing(sub_id)

    util.log_connection_data()
    return util._success_response({'submission_id' : sub_id}, _INTERFACE_VERSION)
Example #21
0
def get_submission_instructor(request):
    """
    Gets a submission for the Instructor grading view
    """
    try:
        course_id = util._value_or_default(request.GET["course_id"], None)
    except:
        return util._error_response("'get_submission' requires parameter 'course_id'", _INTERFACE_VERSION)

    found, sub_id = staff_grading_util.get_single_instructor_grading_item(course_id)

    if not found:
        return util._error_response("Nothing to grade.", _INTERFACE_VERSION)

    # Insert timing initialization code
    initialize_timing(sub_id)

    util.log_connection_data()
    return util._success_response({"submission_id": sub_id}, _INTERFACE_VERSION)
Example #22
0
def log_in(request):
    """
    Handles external login request.
    """
    if request.method == 'POST':
        p = request.POST.copy()
        if p.has_key('username') and p.has_key('password'):
            log.debug("Username: {0} Password: {1}".format(p['username'], p['password']))
            user = authenticate(username=p['username'], password=p['password'])
            if user is not None:
                login(request, user)
                log.debug("Successful login!")
                return util._success_response({'message' : 'logged in'} , _INTERFACE_VERSION)
            else:
                return util._error_response('Incorrect login credentials', _INTERFACE_VERSION)
        else:
            return util._error_response('Insufficient login info', _INTERFACE_VERSION)
    else:
        return util._error_response('login_required', _INTERFACE_VERSION)
Example #23
0
def get_flagged_problem_list(request):
    if request.method != 'GET':
        return util._error_response("Request type must be GET", _INTERFACE_VERSION)

    for tag in ['course_id']:
        if tag not in request.GET:
            return util._error_response("Missing required key {0}".format(tag), _INTERFACE_VERSION)

    success, flagged_submissions = peer_grading_util.get_flagged_submissions(request.GET.get('course_id'))

    if not success:
        return util._error_response(flagged_submissions,_INTERFACE_VERSION)

    flagged_submission_dict={
        'success' : success,
        'flagged_submissions' : flagged_submissions,
        }

    util.log_connection_data()
    return util._success_response(flagged_submission_dict, _INTERFACE_VERSION)
Example #24
0
def get_submission_instructor(request):
    """
    Gets a submission for the Instructor grading view
    """
    try:
        course_id = util._value_or_default(request.GET['course_id'], None)
    except Exception:
        return util._error_response(
            "'get_submission' requires parameter 'course_id'",
            _INTERFACE_VERSION)

    sc = staff_grading_util.StaffCourse(course_id)
    found, sub_id = sc.next_item()

    if not found:
        return util._error_response("Nothing to grade.", _INTERFACE_VERSION)

    util.log_connection_data()
    return util._success_response({'submission_id': sub_id},
                                  _INTERFACE_VERSION)
Example #25
0
def get_flagged_problem_list(request):
    if request.method != 'GET':
        return util._error_response("Request type must be GET", _INTERFACE_VERSION)

    for tag in ['course_id']:
        if tag not in request.GET:
            return util._error_response("Missing required key {0}".format(tag), _INTERFACE_VERSION)

    success, flagged_submissions = peer_grading_util.get_flagged_submissions(request.GET.get('course_id'))

    if not success:
        return util._error_response(flagged_submissions,_INTERFACE_VERSION)

    flagged_submission_dict={
        'success' : success,
        'flagged_submissions' : flagged_submissions,
        }

    util.log_connection_data()
    return util._success_response(flagged_submission_dict, _INTERFACE_VERSION)
Example #26
0
def get_submission_peer(request):
    """
    Gets a submission for the Peer grading view
    """
    try:
        location = util._value_or_default(request.GET["location"], None)
        grader_id = util._value_or_default(request.GET["grader_id"], None)
    except KeyError:
        return util._error_response("'get_submission' requires parameters 'location', 'grader_id'", _INTERFACE_VERSION)

    found, sub_id = peer_grading_util.get_single_peer_grading_item(location, grader_id)

    if not found:
        return util._error_response("Nothing to grade.", _INTERFACE_VERSION)

    # Insert timing initialization code
    initialize_timing(sub_id)

    util.log_connection_data()
    return util._success_response({"submission_id": sub_id}, _INTERFACE_VERSION)
Example #27
0
def get_submission_peer(request):
    """
    Gets a submission for the Peer grading view
    """
    try:
        location = util._value_or_default(request.GET['location'], None)
        grader_id = util._value_or_default(request.GET['grader_id'], None)
    except KeyError:
        return util._error_response("'get_submission' requires parameters 'location', 'grader_id'", _INTERFACE_VERSION)

    found, sub_id = peer_grading_util.get_single_peer_grading_item(location, grader_id)

    if not found:
        return util._error_response("Nothing to grade.", _INTERFACE_VERSION)

    #Insert timing initialization code
    initialize_timing(sub_id)

    util.log_connection_data()
    return util._success_response({'submission_id' : sub_id}, _INTERFACE_VERSION)
Example #28
0
def get_pending_count(request):
    """
    Returns the number of submissions pending grading
    """
    if request.method != "GET":
        return util._error_response("'get_pending_count' must use HTTP GET", _INTERFACE_VERSION)

    grader_type = request.GET.get("grader_type")

    if not grader_type:
        return util._error_response("grader type is a needed key", _INTERFACE_VERSION)

    if grader_type not in [i[0] for i in GRADER_TYPE]:
        return util._error_response("invalid grader type", _INTERFACE_VERSION)

    to_be_graded_count = Submission.objects.filter(
        state=SubmissionState.waiting_to_be_graded, next_grader_type=grader_type
    ).count()

    util.log_connection_data()
    return util._success_response({"to_be_graded_count": to_be_graded_count}, _INTERFACE_VERSION)
Example #29
0
def request_eta_for_submission(request):
    """
    Gets the ETA (in seconds) for a student submission to be graded for a given location
    Input:
        A problem location
    Output:
        Dictionary containing success, and eta indicating time a new submission for given location will take to be graded
    """
    if request.method != 'GET':
        return util._error_response("Request type must be GET", _INTERFACE_VERSION)

    location=request.GET.get("location")
    if not location:
        return util._error_response("Missing required key location", _INTERFACE_VERSION)

    success, eta = grader_util.get_eta_for_submission(location)

    if not success:
        return util._error_response(eta,_INTERFACE_VERSION)

    return util._success_response({
        'eta' : eta,
    }, _INTERFACE_VERSION)
Example #30
0
def get_course_data(request):
    """
    Get the course data for a given course.
    """

    if request.method != "GET":
        return util._error_response("Request type must be GET", _INTERFACE_VERSION)

    course = request.GET.get('course')

    # Throw an error if user does not specify a course.
    if course is None:
        return util._error_response("You must specify a course.", _INTERFACE_VERSION)

    # Generate a data filename for the course.
    filename = get_course_data_filename(course)

    # Get the data file from S3.  There is a periodic task that puts the files here.
    s3 = S3Connection(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY, is_secure=False)
    file_url = s3.generate_url(settings.S3_FILE_TIMEOUT, 'GET', bucket=settings.S3_BUCKETNAME.lower(), key=filename)

    # Return a temporary url.
    return util._success_response({'file_url': file_url}, _INTERFACE_VERSION)
Example #31
0
def check_for_notifications(request):
    """
    Check if a given problem name, location tuple is unique
    Input:
        A problem location and the problem name
    Output:
        Dictionary containing success, and and indicator of whether or not the name is unique
    """
    if request.method != 'GET':
        return util._error_response("Request type must be GET", _INTERFACE_VERSION)

    for tag in ['course_id', 'user_is_staff', 'last_time_viewed', 'student_id']:
        if tag not in request.GET:
            return util._error_response("Missing required key {0}".format(tag), _INTERFACE_VERSION)

    request_dict = request.GET.copy()
    success, combined_notifications = grader_util.check_for_combined_notifications(request_dict)

    if not success:
        return util._error_response(combined_notifications,_INTERFACE_VERSION)

    util.log_connection_data()
    return util._success_response(combined_notifications, _INTERFACE_VERSION)
Example #32
0
def get_submission_ml(request):
    """
    Gets a submission for the ML grader
    Input:
        Get request with no parameters
    """
    unique_locations = [
        x['location']
        for x in list(Submission.objects.values('location').distinct())
    ]
    for location in unique_locations:
        nothing_to_ml_grade_for_location_key = NOTHING_TO_ML_GRADE_LOCATION_CACHE_KEY.format(
            location=location)
        # Go to the next location if we have recently determined that a location
        # has no ML grading ready.
        if cache.get(nothing_to_ml_grade_for_location_key):
            continue

        sl = staff_grading_util.StaffLocation(location)
        control = SubmissionControl(sl.latest_submission())

        subs_graded_by_instructor = sl.graded_count()
        success = ml_grading_util.check_for_all_model_and_rubric_success(
            location)
        if subs_graded_by_instructor >= control.minimum_to_use_ai and success:
            to_be_graded = Submission.objects.filter(
                location=location,
                state=SubmissionState.waiting_to_be_graded,
                next_grader_type="ML",
            )
            if (to_be_graded.count() > 0):
                to_be_graded = to_be_graded[0]
                if to_be_graded is not None:
                    to_be_graded.state = SubmissionState.being_graded
                    to_be_graded.save()

                    return util._success_response(
                        {'submission_id': to_be_graded.id}, _INTERFACE_VERSION)
        # If we don't get a submission to return, then there is no ML grading for this location.
        # Cache this boolean to avoid an expensive loop iteration.
        cache.set(nothing_to_ml_grade_for_location_key, True,
                  settings.RECHECK_EMPTY_ML_GRADE_QUEUE_DELAY)

    util.log_connection_data()

    # Set this cache key to ensure that this expensive function isn't repeatedly called when not needed.
    cache.set(NOTHING_TO_ML_GRADE_CACHE_KEY, True,
              settings.RECHECK_EMPTY_ML_GRADE_QUEUE_DELAY)
    return util._error_response("Nothing to grade.", _INTERFACE_VERSION)
Example #33
0
def get_submission_ml(request):
    """
    Gets a submission for the ML grader
    Input:
        Get request with no parameters
    """
    unique_locations = [x['location'] for x in list(Submission.objects.values('location').distinct())]
    for location in unique_locations:
        nothing_to_ml_grade_for_location_key = NOTHING_TO_ML_GRADE_LOCATION_CACHE_KEY.format(location=location)
        # Go to the next location if we have recently determined that a location
        # has no ML grading ready.
        if cache.get(nothing_to_ml_grade_for_location_key):
            continue

        sl = staff_grading_util.StaffLocation(location)
        subs_graded_by_instructor = sl.graded_count()
        success = ml_grading_util.check_for_all_model_and_rubric_success(location)
        if subs_graded_by_instructor >= settings.MIN_TO_USE_ML and success:
            to_be_graded = Submission.objects.filter(
                location=location,
                state=SubmissionState.waiting_to_be_graded,
                next_grader_type="ML",
            )
            if(to_be_graded.count() > 0):
                to_be_graded = to_be_graded[0]
                if to_be_graded is not None:
                    to_be_graded.state = SubmissionState.being_graded
                    to_be_graded.save()

                    return util._success_response({'submission_id' : to_be_graded.id}, _INTERFACE_VERSION)
        # If we don't get a submission to return, then there is no ML grading for this location.
        # Cache this boolean to avoid an expensive loop iteration.
        cache.set(nothing_to_ml_grade_for_location_key, True, settings.RECHECK_EMPTY_ML_GRADE_QUEUE_DELAY)

    util.log_connection_data()

    # Set this cache key to ensure that this expensive function isn't repeatedly called when not needed.
    cache.set(NOTHING_TO_ML_GRADE_CACHE_KEY, True, settings.RECHECK_EMPTY_ML_GRADE_QUEUE_DELAY)
    return util._error_response("Nothing to grade.", _INTERFACE_VERSION)
Example #34
0
def submit(request):
    '''
    Xqueue pull script posts objects here.
    Input:

    request - dict with keys xqueue_header and xqueue_body
    xqueue_header needs submission_id,submission_key,queue_name
    xqueue_body needs grader_payload, student_info, student_response, max_score
    grader_payload needs location, course_id,problem_id,grader
    student_info needs anonymous_student_id, submission_time

    Output:
    Returns status code indicating success (0) or failure (1) and message
    '''
    transaction.commit_unless_managed()
    if request.method != 'POST':
        return util._error_response("'submit' must use HTTP POST", _INTERFACE_VERSION)
    else:
        #Minimal parsing of reply
        reply_is_valid, header, body = _is_valid_reply(request.POST.copy())

        if not reply_is_valid:
            log.error("Invalid xqueue object added: request_ip: {0} request.POST: {1}".format(
                util.get_request_ip(request),
                request.POST,
            ))
            statsd.increment("open_ended_assessment.grading_controller.controller.xqueue_interface.submit",
                tags=["success:Exception"])
            return util._error_response('Incorrect format', _INTERFACE_VERSION)
        else:
            try:
                #Retrieve individual values from xqueue body and header.
                prompt = util._value_or_default(body['grader_payload']['prompt'], "")
                rubric = util._value_or_default(body['grader_payload']['rubric'], "")
                student_id = util._value_or_default(body['student_info']['anonymous_student_id'])
                location = util._value_or_default(body['grader_payload']['location'])
                course_id = util._value_or_default(body['grader_payload']['course_id'])
                problem_id = util._value_or_default(body['grader_payload']['problem_id'], location)
                grader_settings = util._value_or_default(body['grader_payload']['grader_settings'], "")
                student_response = util._value_or_default(body['student_response'])
                student_response = util.sanitize_html(student_response)
                xqueue_submission_id = util._value_or_default(header['submission_id'])
                xqueue_submission_key = util._value_or_default(header['submission_key'])
                state_code = SubmissionState.waiting_to_be_graded
                xqueue_queue_name = util._value_or_default(header["queue_name"])
                max_score = util._value_or_default(body['max_score'])

                submission_time_string = util._value_or_default(body['student_info']['submission_time'])
                student_submission_time = datetime.strptime(submission_time_string, "%Y%m%d%H%M%S")

                skip_basic_checks = util._value_or_default(body['grader_payload']['skip_basic_checks'], False)
                if isinstance(skip_basic_checks, basestring):
                    skip_basic_checks = (skip_basic_checks.lower() == "true")

                #TODO: find a better way to do this
                #Need to set rubric to whatever the first submission for this location had
                #as its rubric.  If the rubric is changed in the course XML, it will break things.
                try:
                    first_sub_for_location=Submission.objects.filter(location=location).order_by('date_created')[0]
                    rubric= first_sub_for_location.rubric
                except:
                    error_message="Could not find an existing submission in location.  rubric is original."
                    log.info(error_message)

                initial_display=""
                if 'initial_display' in body['grader_payload'].keys():
                    initial_display = util._value_or_default(body['grader_payload']['initial_display'], "")
                answer=""
                if 'answer' in body['grader_payload'].keys():
                    answer = util._value_or_default(body['grader_payload']['answer'], "")

                #Sleep for some time to allow other pull_from_xqueue processes to get behind/ahead
                time_sleep_value = random.uniform(0, .1)
                time.sleep(time_sleep_value)

                transaction.commit_unless_managed()
                #Without this, sometimes a race condition creates duplicate submissions
                sub_count = Submission.objects.filter(
                    prompt=prompt,
                    rubric=rubric,
                    student_id=student_id,
                    problem_id=problem_id,
                    student_submission_time=student_submission_time,
                    xqueue_submission_id=xqueue_submission_id,
                    xqueue_submission_key=xqueue_submission_key,
                    xqueue_queue_name=xqueue_queue_name,
                    location=location,
                    course_id=course_id,
                    grader_settings=grader_settings,
                    ).count()

                if sub_count>0:
                    log.error("Exact submission already exists.")
                    return util._error_response('Submission already exists.', _INTERFACE_VERSION)

                transaction.commit_unless_managed()
                #Create submission object
                sub, created = Submission.objects.get_or_create(
                    prompt=prompt,
                    rubric=rubric,
                    student_id=student_id,
                    problem_id=problem_id,
                    state=state_code,
                    student_response=student_response,
                    student_submission_time=student_submission_time,
                    xqueue_submission_id=xqueue_submission_id,
                    xqueue_submission_key=xqueue_submission_key,
                    xqueue_queue_name=xqueue_queue_name,
                    location=location,
                    course_id=course_id,
                    max_score=max_score,
                    grader_settings=grader_settings,
                    initial_display=initial_display,
                    answer=answer,
                    skip_basic_checks = skip_basic_checks,
                )
                transaction.commit_unless_managed()

                if created==False:
                    log.error("Exact submission already exists.")
                    return util._error_response('Submission already exists.', _INTERFACE_VERSION)

            except Exception as err:
                xqueue_submission_id = util._value_or_default(header['submission_id'])
                xqueue_submission_key = util._value_or_default(header['submission_key'])
                log.exception(
                    "Error creating submission and adding to database: sender: {0}, submission_id: {1}, submission_key: {2}".format(
                        util.get_request_ip(request),
                        xqueue_submission_id,
                        xqueue_submission_key,
                    ))

                statsd.increment("open_ended_assessment.grading_controller.controller.xqueue_interface.submit",
                    tags=["success:Exception"])

                return util._error_response('Unable to create submission.', _INTERFACE_VERSION)

            #Handle submission and write to db
            success = handle_submission(sub)
            statsd.increment("open_ended_assessment.grading_controller.controller.xqueue_interface.submit",
                tags=[
                    "success:{0}".format(success),
                    "location:{0}".format(sub.location),
                    "course_id:{0}".format(course_id),
                ])

            transaction.commit_unless_managed()
            if not success:
                return util._error_response("Failed to handle submission.", _INTERFACE_VERSION)

            util.log_connection_data()
            transaction.commit_unless_managed()
            return util._success_response({'message': "Saved successfully."}, _INTERFACE_VERSION)
Example #35
0
def submit(request):
    '''
    Xqueue pull script posts objects here.
    Input:

    request - dict with keys xqueue_header and xqueue_body
    xqueue_header needs submission_id,submission_key,queue_name
    xqueue_body needs grader_payload, student_info, student_response, max_score
    grader_payload needs location, course_id,problem_id,grader
    student_info needs anonymous_student_id, submission_time

    Output:
    Returns status code indicating success (0) or failure (1) and message
    '''
    transaction.commit_unless_managed()
    if request.method != 'POST':
        return util._error_response("'submit' must use HTTP POST",
                                    _INTERFACE_VERSION)
    else:
        #Minimal parsing of reply
        reply_is_valid, header, body = _is_valid_reply(request.POST.copy())

        if not reply_is_valid:
            log.error(
                "Invalid xqueue object added: request_ip: {0} request.POST: {1}"
                .format(
                    util.get_request_ip(request),
                    request.POST,
                ))
            statsd.increment(
                "open_ended_assessment.grading_controller.controller.xqueue_interface.submit",
                tags=["success:Exception"])
            return util._error_response('Incorrect format', _INTERFACE_VERSION)
        else:
            try:
                #Retrieve individual values from xqueue body and header.
                prompt = util._value_or_default(
                    body['grader_payload']['prompt'], "")
                rubric = util._value_or_default(
                    body['grader_payload']['rubric'], "")
                student_id = util._value_or_default(
                    body['student_info']['anonymous_student_id'])
                location = util._value_or_default(
                    body['grader_payload']['location'])
                course_id = util._value_or_default(
                    body['grader_payload']['course_id'])
                problem_id = util._value_or_default(
                    body['grader_payload']['problem_id'], location)
                grader_settings = util._value_or_default(
                    body['grader_payload']['grader_settings'], "")
                student_response = util._value_or_default(
                    body['student_response'])
                student_response = util.sanitize_html(student_response)
                xqueue_submission_id = util._value_or_default(
                    header['submission_id'])
                xqueue_submission_key = util._value_or_default(
                    header['submission_key'])
                state_code = SubmissionState.waiting_to_be_graded
                xqueue_queue_name = util._value_or_default(
                    header["queue_name"])
                max_score = util._value_or_default(body['max_score'])

                submission_time_string = util._value_or_default(
                    body['student_info']['submission_time'])
                student_submission_time = datetime.strptime(
                    submission_time_string, "%Y%m%d%H%M%S")

                control_fields = body['grader_payload'].get('control', {})
                try:
                    control_fields = json.loads(control_fields)
                except Exception:
                    pass

                skip_basic_checks = util._value_or_default(
                    body['grader_payload']['skip_basic_checks'], False)
                if isinstance(skip_basic_checks, basestring):
                    skip_basic_checks = (skip_basic_checks.lower() == "true")

                #TODO: find a better way to do this
                #Need to set rubric to whatever the first submission for this location had
                #as its rubric.  If the rubric is changed in the course XML, it will break things.
                try:
                    first_sub_for_location = Submission.objects.filter(
                        location=location).order_by('date_created')[0]
                    rubric = first_sub_for_location.rubric
                except Exception:
                    error_message = "Could not find an existing submission in location.  rubric is original."
                    log.info(error_message)

                initial_display = ""
                if 'initial_display' in body['grader_payload'].keys():
                    initial_display = util._value_or_default(
                        body['grader_payload']['initial_display'], "")
                answer = ""
                if 'answer' in body['grader_payload'].keys():
                    answer = util._value_or_default(
                        body['grader_payload']['answer'], "")

                #Sleep for some time to allow other pull_from_xqueue processes to get behind/ahead
                time_sleep_value = random.uniform(0, .1)
                time.sleep(time_sleep_value)

                transaction.commit_unless_managed()
                #Without this, sometimes a race condition creates duplicate submissions
                sub_count = Submission.objects.filter(
                    prompt=prompt,
                    rubric=rubric,
                    student_id=student_id,
                    problem_id=problem_id,
                    student_submission_time=student_submission_time,
                    xqueue_submission_id=xqueue_submission_id,
                    xqueue_submission_key=xqueue_submission_key,
                    xqueue_queue_name=xqueue_queue_name,
                    location=location,
                    course_id=course_id,
                    grader_settings=grader_settings,
                ).count()

                if sub_count > 0:
                    return util._error_response('Submission already exists.',
                                                _INTERFACE_VERSION)

                transaction.commit_unless_managed()
                #Create submission object
                sub, created = Submission.objects.get_or_create(
                    prompt=prompt,
                    rubric=rubric,
                    student_id=student_id,
                    problem_id=problem_id,
                    state=state_code,
                    student_response=student_response,
                    student_submission_time=student_submission_time,
                    xqueue_submission_id=xqueue_submission_id,
                    xqueue_submission_key=xqueue_submission_key,
                    xqueue_queue_name=xqueue_queue_name,
                    location=location,
                    course_id=course_id,
                    max_score=max_score,
                    grader_settings=grader_settings,
                    initial_display=initial_display,
                    answer=answer,
                    skip_basic_checks=skip_basic_checks,
                    control_fields=json.dumps(control_fields))
                transaction.commit_unless_managed()

                if created == False:
                    return util._error_response('Submission already exists.',
                                                _INTERFACE_VERSION)

            except Exception as err:
                xqueue_submission_id = util._value_or_default(
                    header['submission_id'])
                xqueue_submission_key = util._value_or_default(
                    header['submission_key'])
                log.exception(
                    "Error creating submission and adding to database: sender: {0}, submission_id: {1}, submission_key: {2}"
                    .format(
                        util.get_request_ip(request),
                        xqueue_submission_id,
                        xqueue_submission_key,
                    ))

                statsd.increment(
                    "open_ended_assessment.grading_controller.controller.xqueue_interface.submit",
                    tags=["success:Exception"])

                return util._error_response('Unable to create submission.',
                                            _INTERFACE_VERSION)

            #Handle submission and write to db
            success = handle_submission(sub)
            statsd.increment(
                "open_ended_assessment.grading_controller.controller.xqueue_interface.submit",
                tags=[
                    "success:{0}".format(success),
                    "location:{0}".format(sub.location),
                    "course_id:{0}".format(course_id),
                ])

            transaction.commit_unless_managed()
            if not success:
                return util._error_response("Failed to handle submission.",
                                            _INTERFACE_VERSION)

            util.log_connection_data()
            transaction.commit_unless_managed()
            return util._success_response({'message': "Saved successfully."},
                                          _INTERFACE_VERSION)
Example #36
0
def submit_message(request):
    """
    Submits a message to the grading controller.

    """
    if request.method != 'POST':
        return util._error_response("'submit_message' must use HTTP POST",
                                    _INTERFACE_VERSION)

    reply_is_valid, header, body = _is_valid_reply_message(request.POST.copy())

    if not reply_is_valid:
        log.error(
            "Invalid xqueue object added: request_ip: {0} request.POST: {1}".
            format(
                util.get_request_ip(request),
                request.POST,
            ))
        statsd.increment(
            "open_ended_assessment.grading_controller.controller.xqueue_interface.submit_message",
            tags=["success:Exception"])
        return util._error_response('Incorrect format', _INTERFACE_VERSION)

    message = body['feedback']
    message = util.sanitize_html(message)
    grader_id = body['grader_id']
    submission_id = body['submission_id']
    originator = body['student_info']['anonymous_student_id']

    try:
        if 'score' in body:
            score = int(body['score'])
        else:
            score = None
    except Exception:
        error_message = "Score was not an integer, received \"{0}\" instead.".format(
            score)
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    try:
        grade = Grader.objects.get(id=grader_id)
    except Exception:
        error_message = "Could not find a grader object for message from xqueue"
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    try:
        submission = Submission.objects.get(id=submission_id)
    except Exception:
        error_message = "Could not find a submission object for message from xqueue"
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    if grade.submission.id != submission.id:
        error_message = "Grader id does not match submission id that was passed in"
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    if originator not in [submission.student_id, grade.grader_id]:
        error_message = "Message originator is not the grader, or the person being graded"
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    if grade.grader_type in ["ML", "IN"]:
        recipient_type = "controller"
        recipient = "controller"
    else:
        recipient_type = "human"

    if recipient_type != 'controller':
        if originator == submission.student_id:
            recipient = grade.grader_id
        elif originator == grade.grader_id:
            recipient = submission.student_id

    if recipient not in [submission.student_id, grade.grader_id, 'controller']:
        error_message = "Message recipient is not the grader, the person being graded, or the controller"
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    if originator == recipient:
        error_message = "Message recipient is the same as originator"
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    message_dict = {
        'grader_id': grader_id,
        'originator': originator,
        'submission_id': submission_id,
        'message': message,
        'recipient': recipient,
        'message_type': "feedback",
        'score': score
    }

    success, error = message_util.create_message(message_dict)

    if not success:
        return util._error_response(error, _INTERFACE_VERSION)

    return util._success_response({'message_id': error}, _INTERFACE_VERSION)
Example #37
0
def submit_message(request):
    """
    Submits a message to the grading controller.

    """
    if request.method != 'POST':
        return util._error_response("'submit_message' must use HTTP POST", _INTERFACE_VERSION)

    reply_is_valid, header, body = _is_valid_reply_message(request.POST.copy())

    if not reply_is_valid:
        log.error("Invalid xqueue object added: request_ip: {0} request.POST: {1}".format(
            util.get_request_ip(request),
            request.POST,
        ))
        statsd.increment("open_ended_assessment.grading_controller.controller.xqueue_interface.submit_message",
            tags=["success:Exception"])
        return util._error_response('Incorrect format', _INTERFACE_VERSION)

    message = body['feedback']
    message = util.sanitize_html(message)
    grader_id = body['grader_id']
    submission_id = body['submission_id']
    originator = body['student_info']['anonymous_student_id']

    try:
        if 'score' in body:
            score = int(body['score'])
        else:
            score = None
    except:
        error_message = "Score was not an integer, received \"{0}\" instead.".format(score)
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    try:
        grade = Grader.objects.get(id=grader_id)
    except:
        error_message = "Could not find a grader object for message from xqueue"
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    try:
        submission = Submission.objects.get(id=submission_id)
    except:
        error_message = "Could not find a submission object for message from xqueue"
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    if grade.submission.id != submission.id:
        error_message = "Grader id does not match submission id that was passed in"
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    if originator not in [submission.student_id, grade.grader_id]:
        error_message = "Message originator is not the grader, or the person being graded"
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    if grade.grader_type in ["ML", "IN"]:
        recipient_type = "controller"
        recipient = "controller"
    else:
        recipient_type = "human"

    if recipient_type != 'controller':
        if originator == submission.student_id:
            recipient = grade.grader_id
        elif originator == grade.grader_id:
            recipient = submission.student_id

    if recipient not in [submission.student_id, grade.grader_id, 'controller']:
        error_message = "Message recipient is not the grader, the person being graded, or the controller"
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    if originator == recipient:
        error_message = "Message recipient is the same as originator"
        log.exception(error_message)
        return util._error_response(error_message, _INTERFACE_VERSION)

    message_dict = {
        'grader_id': grader_id,
        'originator': originator,
        'submission_id': submission_id,
        'message': message,
        'recipient': recipient,
        'message_type': "feedback",
        'score': score
    }
        

    success, error = message_util.create_message(message_dict)

    if not success:
        return util._error_response(error, _INTERFACE_VERSION)

    return util._success_response({'message_id': error}, _INTERFACE_VERSION)