Beispiel #1
0
def watch():
    assignment_list = request.values.get('assignments', '')
    assignments = [int(aid) for aid in assignment_list.split(',') if len(aid) > 0]
    course_id = request.values.get('course_id', g.course.id if 'course' in g else None)
    if course_id == None or course_id == "":
        return ajax_failure("No Course ID given!")
    if g.user is None or not g.user.is_instructor(int(course_id)):
        return ajax_failure("You are not an instructor in this assignments' course.")
    update = request.values.get('update', 'false') == "true"
    if update:
        data = []
        for aid in assignments:
            submissions = Submission.by_assignment(aid, int(course_id))
            completions = sum([int(sua[0].correct) for sua in submissions])
            workings = Submission.get_latest(aid, int(course_id))
            histories = [process_history([h['time'] for h in sua[0].get_history()])
                         for sua in submissions]
            touches = [int(sua[0].version) for sua in submissions]
            feedbacks = [l[0] for l in Log.calculate_feedbacks(aid, course_id)]
            data.append({'id': aid,
                         'Completions': completions,
                         'Workings': workings,
                         'Time': histories,
                         'Touches': touches,
                         'Feedbacks': feedbacks})
        return jsonify(success=True, data=data)
    else:
        assignments = [Assignment.by_id(aid) for aid in assignments]
        return render_template('blockpy/watch.html', course_id=course_id, assignments=assignments,
                               assignment_list=assignment_list)
Beispiel #2
0
def load_assignment(lti=lti):
    # Get arguments
    assignment_id = int(request.values.get('assignment_id'))
    assignment = Assignment.by_id(assignment_id)
    student_id = maybe_int(request.values.get('user_id'))
    course_id = get_course_id(True)
    user, user_id = get_user()
    force_download = maybe_bool(request.values.get('force_download', "false"))
    # Verify exists
    check_resource_exists(assignment, "Assignment", assignment_id)
    # Verify permissions
    if user_id != student_id and not user.is_grader(course_id):
        return ajax_failure(
            "Only graders can see submissions for other people.")
    if course_id is None:
        editor_information = assignment.for_read_only_editor(student_id)
    else:
        editor_information = assignment.for_editor(student_id, course_id)
        browser_info = json.dumps({
            'platform': request.user_agent.platform,
            'browser': request.user_agent.browser,
            'version': request.user_agent.version,
            'language': request.user_agent.language,
            'user_agent': request.user_agent.string
        })
        # Log the event
        if user is not None:
            if user_id != student_id:
                make_log_entry(assignment_id,
                               assignment.version,
                               course_id,
                               user_id,
                               'X-Submission.Get',
                               message=str(student_id))
            else:
                make_log_entry(assignment_id,
                               assignment.version,
                               course_id,
                               user_id,
                               'Session.Start',
                               message=browser_info)
    # Verify passcode, if necessary
    if assignment.passcode_fails(request.values.get('passcode')):
        return ajax_failure("Passcode {!r} rejected".format(
            request.values.get("passcode")))
    if force_download:
        student_filename = User.by_id(student_id).get_filename("")
        filename = assignment.get_filename(
            "") + "_" + student_filename + '_submission.json'
        return Response(json.dumps(editor_information),
                        mimetype='application/json',
                        headers={
                            'Content-Disposition':
                            'attachment;filename={}'.format(filename)
                        })
    else:
        return ajax_success(editor_information)
Beispiel #3
0
def save_file(lti=lti):
    filename = request.values.get("filename")
    course_id = get_course_id()
    user, user_id = get_user()
    if course_id is None:
        return ajax_failure("Course ID was not made available")
    if filename in Submission.STUDENT_FILENAMES:
        return save_student_file(filename, course_id, user)
    if filename in Assignment.INSTRUCTOR_FILENAMES:
        return save_instructor_file(course_id, user, filename)
    return ajax_failure("Unknown filename: " + str(filename))
Beispiel #4
0
def update_grading_status(lti, lti_exception=None):
    submission_id = maybe_int(request.values.get("submission_id"))
    # TODO: Pretty sure multiple assignments are broken for grading
    assignment_group_id = maybe_int(request.values.get('assignment_group_id'))
    new_grading_status = request.values.get("new_grading_status")
    user, user_id = get_user()
    submission = Submission.by_id(submission_id)
    # Check resource exists
    check_resource_exists(submission, "Submission", submission_id)
    # Verify permissions
    if not user.is_grader(submission.course_id):
        return ajax_failure(
            "This is not your submission and you are not a grader in its course."
        )
    submission.update_grading_status(new_grading_status)
    if submission.grading_status != GradingStatuses.FULLY_GRADED:
        return ajax_success({'new_status': new_grading_status})
    # Do action
    if assignment_group_id is None:
        assignment_group_id = submission.assignment_group_id
    error = "Generic LTI Failure - perhaps not logged into LTI session?"
    try:
        success, score = lti_post_grade(lti, submission, None,
                                        assignment_group_id,
                                        submission.user_id,
                                        submission.course_id)
    except LTIPostMessageException as e:
        success = False
        error = str(e)
    if success:
        make_log_entry(submission.assignment_id,
                       submission.assignment_version,
                       submission.course_id,
                       user_id,
                       "X-Submission.LMS",
                       "answer.py",
                       message=str(score))
        return ajax_success({"submitted": True, "new_status": "FullyGraded"})
    else:
        submission.update_grading_status(GradingStatuses.FAILED)
        make_log_entry(submission.assignment_id,
                       submission.assignment_version,
                       submission.course_id,
                       user_id,
                       "X-Submission.LMS.Failure",
                       "answer.py",
                       message=error)
        return ajax_failure({
            "submitted": False,
            "message": error,
            "new_status": "Failed"
        })
Beispiel #5
0
def save_student_file(filename, course_id, user):
    submission_id = request.values.get("submission_id")
    code = request.values.get("code")
    submission = Submission.query.get(submission_id)
    # Verify exists
    check_resource_exists(submission, "Submission", submission_id)
    # Verify permissions
    if submission.user_id != user.id:
        require_course_grader(user, submission.course_id)
    # Validate the maximum file size
    if app.config["MAXIMUM_CODE_SIZE"] < len(code):
        return ajax_failure(
            "Maximum size of code exceeded. Current limit is {}, you uploaded {} characters."
            .format(app.config["MAXIMUM_CODE_SIZE"], len(code)))
    # Perform update
    # TODO: What if submission's assignment version conflicts with current assignments' version?
    version_change = submission.assignment.version != submission.assignment_version
    submission.save_code(filename, code)
    make_log_entry(submission.assignment_id,
                   submission.assignment_version,
                   course_id,
                   user.id,
                   "File.Edit",
                   "answer.py",
                   message=code)
    return ajax_success({"version_change": version_change})
Beispiel #6
0
def view_submissions(course_id, user_id, assignment_group_id):
    embed = maybe_bool(request.values.get('embed'))
    viewer, viewer_id = get_user()
    group, assignments, submissions = get_groups_submissions(
        assignment_group_id, user_id, course_id)
    # Check permissions
    for submission in submissions:
        if not submission:
            return ajax_failure(
                "No submission for the given course, user, and group.")
        elif submission.user_id != viewer_id:
            require_course_grader(viewer, submission.course_id)
    # Do action
    points_total, points_possible = calculate_submissions_score(
        assignments, submissions)
    score = round(points_total / points_possible, 2)
    # TODO: Handle tags
    is_grader = viewer.is_grader(course_id)
    tags = []
    if is_grader:
        tags = [tag.encode_json() for tag in AssignmentTag.get_all()]
    return render_template("reports/group.html",
                           embed=embed,
                           points_total=points_total,
                           points_possible=points_possible,
                           score=score,
                           tags=tags,
                           is_grader=is_grader,
                           group=list(zip(assignments, submissions)),
                           user_id=user_id,
                           course_id=course_id)
Beispiel #7
0
def update_submission_status(lti, lti_exception=None):
    # Get parameters
    submission_id = maybe_int(request.values.get("submission_id"))
    status = request.values.get('status')
    course_id = get_course_id()
    user, user_id = get_user()
    submission = Submission.by_id(submission_id)
    # Check resource exists
    check_resource_exists(submission, "Submission", submission_id)
    # Verify permissions
    if submission.user_id != user_id and not user.is_grader(
            submission.course_id):
        return ajax_failure(
            "This is not your submission and you are not a grader in its course."
        )
    # Do action
    success = submission.update_submission_status(status)
    make_log_entry(submission.assignment_id,
                   submission.assignment_version,
                   course_id,
                   user_id,
                   "Submit",
                   "answer.py",
                   category=status,
                   message=str(success))
    return ajax_success({"success": success})
Beispiel #8
0
def load_assignment(lti=lti):
    # Get arguments
    assignment_id = int(request.values.get('assignment_id'))
    assignment = Assignment.by_id(assignment_id)
    course_id = get_course_id(True)
    user, user_id = get_user()
    # Verify exists
    check_resource_exists(assignment, "Assignment", assignment_id)
    # Verify permissions
    if course_id is None:
        editor_information = assignment.for_read_only_editor(user_id)
    else:
        editor_information = assignment.for_editor(user_id, course_id)
        browser_info = repr({
            'platform': request.user_agent.platform,
            'browser': request.user_agent.browser,
            'version': request.user_agent.version,
            'language': request.user_agent.language,
            'user_agent': request.user_agent.string
        })
        # Log the event
        if user is not None:
            make_log_entry(assignment_id,
                           assignment.version,
                           course_id,
                           user_id,
                           'Session.Start',
                           message=browser_info)
    # Verify passcode, if necessary
    if assignment.passcode_fails(request.values.get('passcode')):
        return ajax_failure("Passcode {!r} rejected".format(
            request.values.get("passcode")))
    return ajax_success(editor_information)
Beispiel #9
0
def update_submission(lti, lti_exception=None):
    # Get parameters
    submission_id = maybe_int(request.values.get("submission_id"))
    lis_result_sourcedid = request.values.get('lis_result_sourcedid')
    assignment_group_id = maybe_int(request.values.get('assignment_group_id'))
    score = float(request.values.get('score', '0'))
    correct = maybe_bool(request.values.get("correct"))
    # TODO: Only send image if the assignment settings starts as Block or Split
    image = request.values.get('image', "")
    hidden_override = maybe_bool(request.values.get('hidden_override'))
    force_update = maybe_bool(request.values.get('force_update'))
    course_id = get_course_id()
    user, user_id = get_user()
    submission = Submission.by_id(submission_id)
    # Check resource exists
    check_resource_exists(submission, "Submission", submission_id)
    # Verify permissions
    if submission.user_id != user_id and not user.is_grader(submission.course_id):
        return ajax_failure("This is not your submission and you are not a grader in its course.")
    # Do action
    was_changed = submission.update_submission(score, correct)
    if assignment_group_id is None:
        assignment_group_id = submission.assignment_group_id
    # TODO: Document that we currently only pass back grade if it changed
    # TODO: If failure on previous submission grading, then retry
    if was_changed or force_update:
        submission.save_block_image(image)
        error = "Generic LTI Failure - perhaps not logged into LTI session?"
        try:
            success, score = lti_post_grade(lti, submission, lis_result_sourcedid, assignment_group_id,
                                     submission.user_id, submission.course_id)
        except LTIPostMessageException as e:
            success = False
            error = str(e)
        if success:
            make_log_entry(submission.assignment_id, submission.assignment_version,
                           course_id, user_id, "X-Submission.LMS", "answer.py", message=str(score))
        else:
            submission.update_grading_status(GradingStatuses.FAILED)
            make_log_entry(submission.assignment_id, submission.assignment_version,
                           course_id, user_id, "X-Submission.LMS.Failure", "answer.py", message=error)
            return ajax_failure({"submitted": False, "changed": was_changed, "message": error})
    return ajax_success({"submitted": was_changed or force_update, "changed": was_changed})
Beispiel #10
0
def browse_submissions():
    assignment_id = request.values.get('assignment_id', None)
    if assignment_id is None:
        return ajax_failure("No Assignment ID given!")
    assignment_id = int(assignment_id)
    course_id = request.values.get('course_id', g.course.id if 'course' in g else None)
    if course_id == None or course_id == "":
        return ajax_failure("No Course ID given!")
    if g.user is None or not g.user.is_instructor(int(course_id)):
        return ajax_failure("You are not an instructor in this assignments' course.")
    submissions = Submission.by_assignment(assignment_id, int(course_id))
    for submission, user, assignment in submissions:
        submission.highlighted_code = highlight_python_code(submission.code)
        submission.history = process_history([h['time']
                                              for h in reversed(submission.get_history())])
    return render_template('blockpy/browse_submissions.html',
                           course_id=course_id,
                           assignment_id=assignment_id,
                           submissions=submissions,
                           ip=request.remote_addr)
Beispiel #11
0
def get_image():
    submission_id = int(request.values.get('submission_id'))
    directory = request.values.get('directory')
    relative_image_path = 'uploads/{}/{}.png'.format(directory, submission_id)
    submission = Submission.query.get(submission_id)
    user, user_id = get_user()
    # Check exists
    check_resource_exists(submission, "Submission", submission_id)
    # Check permissions
    if submission.user_id != user_id and not user.is_grader(submission.course_id):
        return ajax_failure("This is not your submission and you are not a grader in its course.")
    # Do action
    return app.send_static_file(relative_image_path)
Beispiel #12
0
def update_submission_status():
    submission_id = maybe_int(request.values.get("submission_id"))
    new_submission_status = request.values.get("new_submission_status")
    user, user_id = get_user()
    submission = Submission.by_id(submission_id)
    # Check resource exists
    check_resource_exists(submission, "Submission", submission_id)
    # Verify permissions
    if not user.is_grader(submission.course_id):
        return ajax_failure(
            "You are not a grader in this submission's course.")
    submission.update_submission_status(new_submission_status)
    return ajax_success({'new_status': new_submission_status})
def users():
    user_ids = request.values.get('user_ids', "")
    course_id = get_course_id()
    user, user_id = get_user()
    if course_id is None:
        return ajax_failure("You are not in a course context.")
    is_grader = user.is_grader(course_id)
    if not is_grader and user_ids != str(user_id):
        return ajax_failure("You do not have permissions to see those users.")
    users = []
    errors = []
    # If blank, then get all the available users
    if not user_ids:
        course = Course.by_id(course_id)
        check_resource_exists(course, "Course", course_id)
        user_roles = course.get_users()
        user_data = {}
        for role, user in user_roles:
            if user not in user_data:
                user_data[user] = user.encode_json()
                user_data[user]['roles'] = []
            user_data[user]['roles'].append(role.encode_json())
        users.extend(user_data.values())
    # Otherwise, get the subset suggested
    else:
        for user_id in user_ids.split(","):
            if not user_id.isdigit():
                errors.append(f"Unknown User ID: {user_id!r}")
                continue
            user_id = int(user_id)
            # With Course Role Information
            user = User.by_id(user_id)
            check_resource_exists(user, "User", user_id)
            user_data = user.encode_json()
            user_data['roles'] = [
                r.encode_json() for r in user.get_course_roles(course_id)
            ]
            users.append(user_data)
    return ajax_success(dict(users=users, errors=errors))
Beispiel #14
0
def load_assignment_give_feedback():
    '''
    Very random function necessary for syncing with JN - we need to expose the 'on_run'
    field from assignments in public courses.

    TODO: Do this for public courses only, not just private ones

    :return:
    '''
    assignment_id = request.values.get('assignment_id', None)
    if assignment_id is None:
        return ajax_failure("No Assignment ID given!")
    assignment = Assignment.by_id(assignment_id)
    return jsonify(success=True, give_feedback=assignment.on_run)
Beispiel #15
0
def mass_close_assignment():
    assignment_id = maybe_int(request.values.get("assignment_id"))
    course_id = maybe_int(request.values.get("course_id"))
    new_submission_status = request.values.get("new_submission_status")
    user, user_id = get_user()
    submissions = Submission.by_assignment(assignment_id=assignment_id,
                                           course_id=course_id)
    # Verify permissions
    if not user.is_grader(course_id):
        return ajax_failure("You are not a grader in this course.")
    # Do action
    for submission in submissions:
        submission.update_submission_status(new_submission_status)
    return ajax_success({'new_status': new_submission_status})
Beispiel #16
0
def users():
    user_ids = request.values.get('user_ids', "")
    course_id = get_course_id()
    user, user_id = get_user()
    if course_id is None:
        return ajax_failure("You are not in a course context.")
    is_grader = user.is_grader(course_id)
    if not is_grader and user_ids != str(user_id):
        return ajax_failure("You do not have permissions to see those users.")
    users = []
    for user_id in user_ids.split(","):
        if not user_id.isdigit():
            return ajax_failure(f"Unknown User ID: {user_id!r}")
        user_id = int(user_id)
        # With Course Role Information
        user = User.by_id(user_id)
        check_resource_exists(user, "User", user_id)
        user_data = user.encode_json()
        user_data['roles'] = [
            r.encode_json() for r in user.get_course_roles(course_id)
        ]
        users.append(user_data)
    return ajax_success(dict(users=users))
Beispiel #17
0
def get_assignments():
    assignment_ids = request.values.get('assignment_ids', "")
    course_id = get_course_id()
    user, user_id = get_user()
    # TODO: verify that they have the permissions to see this file
    assignments = []
    for assignment_id in assignment_ids.split(","):
        if not assignment_id.isdigit():
            return ajax_failure(f"Unknown Assignment ID: {assignment_id!r}")
        assignment_id = int(assignment_id)
        # With Course Role Information
        assignment = Assignment.by_id(assignment_id)
        check_resource_exists(assignment, "Assignment", assignment_id)
        assignments.append(assignment.encode_json())
    return ajax_success(dict(assignments=assignments))
Beispiel #18
0
def save_image():
    # Get parameters
    submission_id = maybe_int(request.values.get("submission_id"))
    directory = request.values.get('directory')
    image = request.values.get('image')
    course_id = get_course_id()
    user, user_id = get_user()
    submission = Submission.by_id(submission_id)
    # Check resource exists
    check_resource_exists(submission, "Submission", submission_id)
    # Verify permissions
    if submission.user_id != user_id and not user.is_grader(submission.course_id):
        return ajax_failure("This is not your submission and you are not a grader in its course.")
    # Do action
    success = submission.save_image(directory, image)
    make_log_entry(submission.assignment_id, submission.assignment_version,
                   course_id, user_id, "X-Image.Save", directory)
    return ajax_success({"success": success})
Beispiel #19
0
def load_history():
    # Get parameters
    course_id = maybe_int(request.values.get('course_id'))
    assignment_id = maybe_int(request.values.get('assignment_id'))
    student_id = maybe_int(request.values.get('user_id'))
    page_limit = maybe_int(request.values.get('page_limit'))
    page_offset = maybe_int(request.values.get('page_offset'))
    user, user_id = get_user()
    # Verify user can see the submission
    if user_id != student_id and not user.is_grader(course_id):
        return ajax_failure("Only graders can see logs for other people.")
    history = list(
        reversed(
            Log.get_history(course_id,
                            assignment_id,
                            student_id,
                            page_offset=page_offset,
                            page_limit=page_limit)))
    return ajax_success({"history": history})
Beispiel #20
0
def load_submission(lti=lti):
    submission_id = int(request.args.get('submission_id'))
    embed = maybe_bool(request.values.get('embed'))
    course_id = get_course_id(True)
    user, user_id = get_user()
    submission = Submission.by_id(submission_id)
    read_only = maybe_bool(request.values.get('read_only', "true"))
    # Check that the resource exists
    check_resource_exists(submission, "Submission", submission_id)
    # If it is this user's submission, redirect to load the assignment
    if submission.user_id == user_id:
        if course_id is None:
            course_id = submission.course_id
        return redirect(
            url_for('blockpy.load',
                    assignment_id=submission.assignment.id,
                    course_id=course_id))
    # Check that it is public or you are a grader
    elif user.is_grader(submission.course_id):
        role = 'grader'
    elif not submission.assignment.public:
        # TODO: Handle this more gracefully
        return ajax_failure(
            "Cannot view submission. This is not a public submission, and you do not own the submission, and you are "
            "not an instructor in its course.")
    else:
        role = 'anonymous'
    # Get the assignment
    assignment_data = submission.assignment.for_editor(submission.user_id,
                                                       submission.course_id)
    return load_editor(
        lti, {
            "user": user,
            "user_id": user_id,
            "embed": embed,
            "read_only": read_only,
            "current_submission_id": submission_id,
            "course_id": course_id,
            "role": role,
            "assignment_group_id": None,
            "assignment_data": assignment_data
        })
Beispiel #21
0
def load_history():
    # Get parameters
    course_id = maybe_int(request.values.get('course_id'))
    assignment_id = (request.values.get('assignment_id'))
    student_id = (request.values.get('user_id'))
    page_limit = maybe_int(request.values.get('page_limit'))
    page_offset = maybe_int(request.values.get('page_offset'))
    with_submission = maybe_bool(request.values.get('with_submission'))
    user, user_id = get_user()
    # Verify user can see the submission
    if str(user_id) != str(student_id) and not user.is_grader(course_id):
        return ajax_failure("Only graders can see logs for other people.")
    history = Log.get_history(course_id, assignment_id, student_id,
                                            page_offset=page_offset,
                                            page_limit=page_limit)
    history = list(reversed(history))
    submissions = []
    if with_submission:
        submissions = Submission.get_submissions(course_id, assignment_id, student_id)
    return ajax_success({"history": history, "submissions": submissions})
Beispiel #22
0
def browse_history():
    # Get parameters
    course_id = maybe_int(request.values.get('course_id'))
    assignment_id = maybe_int(request.values.get('assignment_id'))
    student_id = maybe_int(request.values.get('user_id'))
    page_offset = maybe_int(request.values.get('page_offset', 0))
    embed = maybe_bool(request.values.get('embed'))
    user, user_id = get_user()
    # Get resources
    assignment = Assignment.by_id(assignment_id)
    student = User.by_id(student_id)
    course = Course.by_id(course_id)
    # Verify user can see the submission
    if user_id != student_id and not user.is_grader(course_id):
        return ajax_failure("Only graders can see logs for other people.")
    history = Log.get_history(course_id, assignment_id, student_id,
                              page_offset, HISTORY_PAGE_LIMIT)
    return render_template('blockpy/browse_history.html', assignment=assignment,
                           student=student, course=course, history=history,
                           embed=embed)
Beispiel #23
0
def load_submission(lti=lti):
    submission_id = int(request.args.get('submission_id'))
    embed = maybe_bool(request.values.get('embed'))
    user = g.get('user', None)
    user_id = user.id if user else None
    course_id = maybe_int(request.args.get('course_id', None))
    if course_id is None:
        course_id = int(g.course.id) if g.course else None
    submission = Submission.query.get(submission_id)
    # Check that the resource exists
    check_resource_exists(submission, "Submission", submission_id)
    # If it is this user's submission, redirect to load the assignment
    if submission.user_id == user_id:
        return redirect(
            url_for(
                'blockpy.load',
                assignment_id=submission.assignment.id,
            ))
    # Check that it is public or you are a grader
    elif user.is_grader(submission.course_id):
        pass
    elif not submission.assignment.public:
        # TODO: Handle this more gracefully
        return ajax_failure(
            "Cannot view submission. This is not a public submission, and you do not own the submission, and you are "
            "not an instructor in its course.")
    # Get the assignment
    assignment_data = submission.assignment.for_editor(submission.user_id,
                                                       submission.course_id)
    return load_editor(
        lti, {
            "user": user,
            "user_id": user_id,
            "embed": embed,
            "course_id": course_id,
            "role": 'anonymous',
            "assignment_group_id": None,
            "assignment_data": assignment_data
        })